// MainFrm.cpp : implementation of the CMainFrame class
//
#include "stdafx.h"
#include "AgileReader.h"
#include "MainFrm.h"
#include "TreeViewEx.h"
#include "AgileReaderView.h"
#include "FeedDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMainFrame
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_SIZE()
ON_COMMAND(ID_FILE_SUBSCRIBERFEED, OnFileSubscriberfeed)
ON_COMMAND(ID_EDIT_DELETE, OnEditDelete)
ON_WM_CLOSE()
ON_COMMAND(ID_TOOLS_REFRESHNOW, OnToolsRefreshnow)
ON_UPDATE_COMMAND_UI(ID_TOOLS_REFRESHNOW, OnUpdateToolsRefreshnow)
ON_WM_TIMER()
ON_COMMAND(ID_TOOLS_REFRESHRATE_5MINUTES, OnToolsRefreshrate5minutes)
ON_COMMAND(ID_TOOLS_REFRESHRATE_15MINUTES, OnToolsRefreshrate15minutes)
ON_COMMAND(ID_TOOLS_REFRESHRATE_1HOUR, OnToolsRefreshrate1hour)
ON_COMMAND(ID_TOOLS_REFRESHRATE_8HOUR, OnToolsRefreshrate8hour)
ON_UPDATE_COMMAND_UI(ID_TOOLS_REFRESHRATE_15MINUTES, OnUpdateToolsRefreshrate15minutes)
ON_UPDATE_COMMAND_UI(ID_TOOLS_REFRESHRATE_8HOUR, OnUpdateToolsRefreshrate8hour)
ON_UPDATE_COMMAND_UI(ID_TOOLS_REFRESHRATE_5MINUTES, OnUpdateToolsRefreshrate5minutes)
ON_UPDATE_COMMAND_UI(ID_TOOLS_REFRESHRATE_1HOUR, OnUpdateToolsRefreshrate1hour)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_TREEDCLICK, OnTreeDblClick)
ON_MESSAGE(WM_LISTDCLICK, OnListDblClick)
ON_MESSAGE(WM_TREEITEMCHANGE, OnTreeItemChange)
END_MESSAGE_MAP()
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
// TODO: add member initialization code here
m_bCreate = FALSE;
m_image.Create( IDB_IMAGE, 16, 0, RGB(255, 255, 255) );
m_pThread = NULL;
m_bRunning = FALSE;
m_nTimer = 0;
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
// By default we refresh every 5 minutes
OnToolsRefreshrate5minutes();
m_nTimer = SetTimer(1, m_nRate, 0);
return 0;
}
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
CRect rect;
GetClientRect( &rect );
int nWidth1 = (int)0.25 * rect.Width();
int nWidth2 = (int)0.75 * rect.Width();
int nHeight1 = (int)0.25 * rect.Height();
int nHeight2 = (int)0.75 * rect.Height();
if ( !m_wndSplitter.CreateStatic(this, 1, 2) )
{
return false;
}
if(!m_wndSplitter.CreateView(0, 0,
RUNTIME_CLASS(CTreeViewEx),
CSize(nWidth1, rect.Height() ),
pContext))
{
TRACE(_T("Failed to create top view\n"));
return FALSE;
}
if (!m_wndSplitterRight.CreateStatic(
&m_wndSplitter, // our parent window is the first splitter
2, 1, // the new splitter is 2 rows, 1 column
WS_CHILD | WS_VISIBLE | WS_BORDER, // style, WS_BORDER is needed
m_wndSplitter.IdFromRowCol(0, 1)
// new splitter is in the first row, 2nd column of first splitter
))
{
TRACE0("Failed to create nested splitter\n");
return FALSE;
}
// create top view
if(!m_wndSplitterRight.CreateView(0, 0,
RUNTIME_CLASS(CAgileReaderView),
CSize(nWidth2, nHeight1),
pContext))
{
TRACE(_T("Failed to create top view\n"));
return FALSE;
}
// create bottom view
if(!m_wndSplitterRight.CreateView(1, 0,
RUNTIME_CLASS(CHtmlView),
CSize(nWidth2, nHeight2), pContext))
{
TRACE(_T("Failed to create bottom view\n"));
return FALSE;
}
m_wndSplitterRight.RecalcLayout();
m_bCreate = TRUE;
InitTree();
InitList();
return TRUE;
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
| WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_MAXIMIZE;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CFrameWnd::OnSize(nType, cx, cy);
if ( m_bCreate )
{
CRect r;
GetClientRect(&r);
int w1 = (int)(r.Width()*0.25);
int w2 = (int)(r.Width()*0.75);
m_wndSplitter.SetColumnInfo( 0, w1, 0 );
m_wndSplitter.SetColumnInfo( 1, w2, 0 );
m_wndSplitter.RecalcLayout();
w1 = (int)(r.Height()*0.25);
w2 = (int)(r.Height()*0.75);
m_wndSplitterRight.SetRowInfo( 0, w1, 0 );
m_wndSplitterRight.SetRowInfo( 1, w2, 0 );
m_wndSplitterRight.RecalcLayout();
}
}
/////////////////////////////////////////////////////////////////////////////
// Get Left TreeCtrl
//
CTreeCtrl* CMainFrame::GetTreeCtrl()
{
CTreeViewEx* pTree = (CTreeViewEx*)m_wndSplitter.GetPane(0,0);
return &(pTree->GetTreeCtrl());
}
/////////////////////////////////////////////////////////////////////////////
// Get Right Top ListCtrl
//
CListCtrl* CMainFrame::GetListCtrl()
{
CListView* pView = (CListView*)m_wndSplitterRight.GetPane(0,0);
return &(pView->GetListCtrl());
}
/////////////////////////////////////////////////////////////////////////////
// Get Right Bottom HtmlView
//
CHtmlView* CMainFrame::GetHtmlView()
{
CHtmlView* pView = (CHtmlView*)m_wndSplitterRight.GetPane(1,0);
return pView;
}
/////////////////////////////////////////////////////////////////////////////
// Initialize Tree Ctrl
//
void CMainFrame::InitTree()
{
SetWindowLong( GetTreeCtrl()->m_hWnd, GWL_STYLE, GetWindowLong( GetTreeCtrl()->m_hWnd, GWL_STYLE ) | TVS_HASLINES | TVS_HASBUTTONS | TVS_SHOWSELALWAYS );
GetTreeCtrl()->SetImageList( &m_image, TVSIL_NORMAL );
// Need to create Image List and attach image list to tree
AddTreeNode( _T("My Feed"), _T("") );
// Need to read congfig file to get a list of subscribered RSS Feeds and populated to the tree structure
CStringArray strTitleArray;
CStringArray strLinkArray;
CFeed::GetFeedSourceList( strTitleArray, strLinkArray );
for( int nIndex = 0; nIndex < strTitleArray.GetSize(); nIndex++ )
{
AddTreeNode( strTitleArray[nIndex], strLinkArray[nIndex] );
}
// Need to determine which one was the last visited and then load that entry into right side
}
/////////////////////////////////////////////////////////////////////////////
// Initialize List
//
void CMainFrame::InitList()
{
// Step 1. Set the list style
SetWindowLong( GetListCtrl()->m_hWnd, GWL_STYLE, WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_SHOWSELALWAYS | LVS_REPORT );
GetListCtrl()->SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT );
GetListCtrl()->SetImageList( &m_image, LVSIL_SMALL );
// Step 2. Set the column header
GetListCtrl()->InsertColumn( 0, _T("Title"), LVCFMT_LEFT, 400 );
GetListCtrl()->InsertColumn( 1, _T("Description"), LVCFMT_LEFT, 300 );
GetListCtrl()->InsertColumn( 2, _T("Link"), LVCFMT_LEFT, 200 );
GetListCtrl()->InsertColumn( 3, _T("Author"), LVCFMT_LEFT, 200 );
GetListCtrl()->InsertColumn( 4, _T("Category"), LVCFMT_LEFT, 200 );
GetListCtrl()->InsertColumn( 5, _T("Publish Date"), LVCFMT_LEFT, 200 );
GetListCtrl()->InsertColumn( 6, _T("Subject"), LVCFMT_LEFT, 200 );
}
/////////////////////////////////////////////////////////////////////////////
// Subscribe a feed
//
void CMainFrame::OnFileSubscriberfeed()
{
CString strRSSFile;
// Step 1. Popup a dialog to ask user to specify RSS Feeds URL
CFeedDlg dlg;
if ( dlg.DoModal() == IDOK )
{
strRSSFile = dlg.m_strUrl;
}
else
{
return;
}
// Step 2. If this feed already exists, then switch to that one
// then return.
// Step 4. Open XML Document and Parse it there
CFeed feed( strRSSFile );
feed.m_source.m_strLink = dlg.m_strUrl;
feed.Save();
// Step 5. Add this entry into tree structure
// Need to check whether it already exists in the tree structure
if ( feed.m_bAdded && feed.m_item.GetSize() > 0 )
{
AddTreeNode( feed.m_source.m_strTitle, feed.m_source.m_strLink );
// Step 6. Load all the items into list view
DisplayFeed( feed );
}
}
/////////////////////////////////////////////////////////////////////////////
// Get current user selection, then load feed into list
//
LONG CMainFrame::OnTreeDblClick(UINT wParam, LONG lParam)
{
CWaitCursor wait;
HTREEITEM hItem = GetTreeCtrl()->GetSelectedItem();
CNode* pNode = NULL;
if ( hItem )
{
pNode = (CNode*)GetTreeCtrl()->GetItemData( hItem );
if ( !pNode->m_strLink.IsEmpty() )
{
// Step 4. Open XML Document and Parse it there
CFeed feed( pNode->m_strLink );
feed.m_source.m_strLink = pNode->m_strLink;
feed.Save(FALSE);
// Step 5. Load all the items into list view
CFeed feed1;
feed1.LoadLocal(pNode->m_strLink);
// Step 5. Load all the items into list view
DisplayFeed( feed1 );
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// When Tree Item Change
//
LONG CMainFrame::OnTreeItemChange(UINT wParam, LONG lParam)
{
HTREEITEM hItem = GetTreeCtrl()->GetSelectedItem();
CNode* pNode = NULL;
if ( hItem )
{
pNode = (CNode*)GetTreeCtrl()->GetItemData( hItem );
if ( !pNode->m_strLink.IsEmpty() )
{
// Step 4. Open XML Document and Parse it there
CFeed feed;
feed.LoadLocal(pNode->m_strLink);
// Step 5. Load all the items into list view
DisplayFeed( feed );
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// Get Current Selection and start this page
//
LONG CMainFrame::OnListDblClick(UINT wParam, LONG lParam)
{
POSITION pos = GetListCtrl()->GetFirstSelectedItemPosition();
CFeedItem item;
int nIndex;
if ( pos != NULL )
{
nIndex = GetListCtrl()->GetNextSelectedItem(pos);
GetHtmlView()->Navigate( GetListCtrl()->GetItemText(nIndex,2) );
// Get item data
item.m_strTitle = GetListCtrl()->GetItemText( nIndex, 0 );
item.m_strDescription = GetListCtrl()->GetItemText( nIndex, 1 );
item.m_strLink = GetListCtrl()->GetItemText( nIndex, 2 );
item.m_strAuthor = GetListCtrl()->GetItemText( nIndex, 3 );
item.m_strCategory = GetListCtrl()->GetItemText( nIndex, 4 );
item.m_strPubDate = GetListCtrl()->GetItemText( nIndex, 5 );
item.m_strSubject = GetListCtrl()->GetItemText( nIndex, 6 );
CFeed::MarkItemRead( item.m_strLink );
GetListCtrl()->LockWindowUpdate();
GetListCtrl()->InsertItem( nIndex+1, item.m_strTitle, 2 );
GetListCtrl()->SetItemText( nIndex+1, 1, item.m_strDescription );
GetListCtrl()->SetItemText( nIndex+1, 2, item.m_strLink );
GetListCtrl()->SetItemText( nIndex+1, 3, item.m_strAuthor );
GetListCtrl()->SetItemText( nIndex+1, 4, item.m_strCategory );
GetListCtrl()->SetItemText( nIndex+1, 5, item.m_strPubDate );
GetListCtrl()->SetItemText( nIndex+1, 6, item.m_strSubject );
GetListCtrl()->DeleteItem( nIndex );
GetListCtrl()->SetItemState( nIndex, LVIS_SELECTED, LVIS_SELECTED );
GetListCtrl()->UnlockWindowUpdate();
GetListCtrl()->UpdateWindow();
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// Add Tree Node
//
void CMainFrame::AddTreeNode(CString strTitle, CString strLink)
{
CNode* pNode = NULL;
HTREEITEM hItem;
if ( strLink.IsEmpty() )
{
hItem = GetTreeCtrl()->InsertItem( strTitle, 0, 0 );
}
else
{
hItem = GetTreeCtrl()->InsertItem( strTitle, 3, 3, GetTreeCtrl()->GetRootItem() );
}
pNode = new CNode( strLink );
GetTreeCtrl()->SetItemData( hItem, (DWORD)pNode );
GetTreeCtrl()->Expand( GetTreeCtrl()->GetRootItem(), TVE_EXPAND );
}
/////////////////////////////////////////////////////////////////////////////
// Release Data Attached to each node
//
void CMainFrame::Release( HTREEITEM hParent )
{
CNode* pNode = NULL;
HTREEITEM hItem = NULL;
// Step 1. Release current node
if ( hParent != NULL )
{
pNode = (CNode*)GetTreeCtrl()->GetItemData( hParent );
if ( pNode )
{
delete pNode;
pNode = NULL;
}
}
// Step 2. Release child node
hItem = GetTreeCtrl()->GetChildItem( hParent );
while( hItem )
{
Release( hItem );
hItem = GetTreeCtrl()->GetNextSiblingItem( hItem );
}
}
/////////////////////////////////////////////////////////////////////////////
// Display Feed Information into List
void CMainFrame::DisplayFeed(CFeed& feed)
{
int nIndex;
GetListCtrl()->DeleteAllItems();
for( nIndex = 0; nIndex < feed.m_item.GetSize(); nIndex++ )
{
if ( feed.m_item[nIndex].m_strReadStatus == _T("1") )
{
GetListCtrl()->InsertItem( nIndex, feed.m_item[nIndex].m_strTitle, 2 );
}
else
{
GetListCtrl()->InsertItem( nIndex, feed.m_item[nIndex].m_strTitle, 1 );
}
GetListCtrl()->SetItemText( nIndex, 1, feed.m_item[nIndex].m_strDescription );
GetListCtrl()->SetItemText( nIndex, 2, feed.m_item[nIndex].m_strLink );
GetListCtrl()->SetItemText( nIndex, 3, feed.m_item[nIndex].m_strAuthor );
GetListCtrl()->SetItemText( nIndex, 4, feed.m_item[nIndex].m_strCategory );
GetListCtrl()->SetItemText( nIndex, 5, NormalizeDate( feed.m_item[nIndex].m_strPubDate ) );
GetListCtrl()->SetItemText( nIndex, 6, feed.m_item[nIndex].m_strSubject );
}
}
/////////////////////////////////////////////////////////////////////////////
// Delete Item from Database
//
void CMainFrame::OnEditDelete()
{
int nIndex;
CArray<int,int> nIndexArray;
CStringArray strLinkArray;
CString strMsg;
CView* pView = GetActiveView();
if ( pView->IsKindOf(RUNTIME_CLASS(CTreeViewEx) ) )
{
// Delete from Tree
HTREEITEM hItem = GetTreeCtrl()->GetSelectedItem();
if ( hItem == NULL )
{
return;
}
CNode* pNode = (CNode*)GetTreeCtrl()->GetItemData( hItem );
if ( pNode->m_strLink.IsEmpty() )
{
return;
}
strMsg.Format( _T("Are you sure you want to unsubscribe '%s'?"), GetTreeCtrl()->GetItemText( hItem ) );
if( AfxMessageBox( strMsg, MB_ICONQUESTION | MB_YESNO ) == IDYES )
{
CFeed::DeleteFeedSource( pNode->m_strLink );
Release( hItem );
GetTreeCtrl()->DeleteItem( hItem );
}
}
else if ( pView->IsKindOf(RUNTIME_CLASS(CAgileReaderView) ) )
{
POSITION pos = GetListCtrl()->GetFirstSelectedItemPosition();
while( pos )
{
nIndex = GetListCtrl()->GetNextSelectedItem( pos );
nIndexArray.Add( nIndex );
strLinkArray.Add( GetListCtrl()->GetItemText(nIndex,2) );
}
// Delete it from Database
CFeed::DeleteListArray( strLinkArray );
// Delete it from ListView
for( nIndex = nIndexArray.GetSize()-1; nIndex>= 0; nIndex-- )
{
GetListCtrl()->DeleteItem( nIndexArray[nIndex] );
}
}
}
void CMainFrame::OnClose()
{
Release( GetTreeCtrl()->GetRootItem() );
KillTimer(m_nTimer);
CFrameWnd::OnClose();
}
/////////////////////////////////////////////////////////////////////////////
// Normalize a GMT Date Time to Date
//
CString CMainFrame::NormalizeDate(CString strValue)
{
int nPos;
strValue.MakeUpper();
strValue.Replace( _T("GMT"), _T("") );
strValue.Replace( _T("PST"), _T("") );
strValue.Replace( _T("EST"), _T("") );
strValue.Replace( _T("MON,"), _T("") );
strValue.Replace( _T("TUE,"), _T("") );
strValue.Replace( _T("WED,"), _T("") );
strValue.Replace( _T("THU,"), _T("") );
strValue.Replace( _T("FRI,"), _T("") );
strValue.Replace( _T("SAT,"), _T("") );
strValue.Replace( _T("SUN,"), _T("") );
strValue.TrimLeft();
strValue.TrimRight();
nPos = strValue.ReverseFind( '-' );
if ( strValue.GetLength() - nPos == 5 )
{
strValue = strValue.Left( nPos-1 );
strValue.TrimRight();
}
return strValue;
}
/////////////////////////////////////////////////////////////////////////////
// Start a thread to refresh all subscriptions now
//
void CMainFrame::OnToolsRefreshnow()
{
// Step 1. Check whether there is any refreshing thread running
if ( m_bRunning )
{
// There is a thread running, skip this
return;
}
// Step 2. If there is no thread running, then start a thread to refresh it
m_bRunning = TRUE;
m_pThread = AfxBeginThread( CMainFrame::ProcRefreshAll, this );
}
/////////////////////////////////////////////////////////////////////////////
// Refresh Subscription Thread Function
//
UINT CMainFrame::ProcRefreshAll( void* pParam )
{
CMainFrame* pMain = (CMainFrame*)pParam;
CoInitialize( NULL );
// Real Refresh function is a static function
CFeed::RefreshAll();
CoUninitialize();
// Set status back to ready
pMain->m_bRunning = FALSE;
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// Enable or disable refresh depending on status
//
void CMainFrame::OnUpdateToolsRefreshnow(CCmdUI* pCmdUI)
{
pCmdUI->Enable( !m_bRunning );
}
/////////////////////////////////////////////////////////////////////////////
// When it is timer, then start the thread to refresh
//
void CMainFrame::OnTimer(UINT nIDEvent)
{
// Refresh it now
OnToolsRefreshnow();
CFrameWnd::OnTimer(nIDEvent);
}
void CMainFrame::OnToolsRefreshrate5minutes()
{
m_nRate = 5*60*1000;
}
void CMainFrame::OnToolsRefreshrate15minutes()
{
m_nRate = 15*60*1000;
}
void CMainFrame::OnToolsRefreshrate1hour()
{
m_nRate = 60*60*1000;
}
void CMainFrame::OnToolsRefreshrate8hour()
{
m_nRate = 8*60*60*1000;
}
void CMainFrame::OnUpdateToolsRefreshrate15minutes(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck( m_nRate == 15*60*1000 );
}
void CMainFrame::OnUpdateToolsRefreshrate8hour(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck( m_nRate == 8*60*60*1000 );
}
void CMainFrame::OnUpdateToolsRefreshrate5minutes(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck( m_nRate == 5*60*1000 );
}
void CMainFrame::OnUpdateToolsRefreshrate1hour(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck( m_nRate == 60*60*1000 );
}