ToDoList

只谈情不闲聊 提交于 2020-04-05 23:40:15
// ToDoListWnd.cpp : implementation file
//

#include "stdafx.h"
#include "ToDoList.h"
#include "ToDoListWnd.h"
#include "ToolsCmdlineParser.h"
#include "ToolsUserInputDlg.h"
#include "Toolshelper.h"
#include "tdlexportDlg.h"
#include "tasklisthtmlexporter.h"
#include "tasklisttxtexporter.h"
#include "tdcmsg.h"
#include "tdlschemadef.h"
#include "tdlprintdialog.h"
#include "tdltransformdialog.h"
#include "tdstringres.h"
#include "tdlcolumnselectiondlg.h"
#include "tdlfilterdlg.h"
#include "OffsetDatesDlg.h"
#include "KeyboardShortcutDisplayDlg.h"
#include "tdlimportdialog.h"
#include "tdlsetreminderdlg.h"
#include "tdlshowreminderdlg.h"
#include "tdlmultisortdlg.h"
#include "tdladdloggedtimedlg.h"
#include "multitaskfile.h"
#include "tdcstatic.h"
#include "tdlcustomattributedlg.h"
#include "tdccustomattributehelper.h"
#include "welcomewizard.h"

#include "..\shared\aboutdlg.h"
#include "..\shared\holdredraw.h"
#include "..\shared\autoflag.h"
#include "..\shared\enbitmap.h"
#include "..\shared\spellcheckdlg.h"
#include "..\shared\encolordialog.h"
#include "..\shared\winclasses.h"
#include "..\shared\wclassdefines.h"
#include "..\shared\datehelper.h"
#include "..\shared\osversion.h"
#include "..\shared\enfiledialog.h"
#include "..\shared\misc.h"
#include "..\shared\graphicsmisc.h"
#include "..\shared\filemisc.h"
#include "..\shared\themed.h"
#include "..\shared\enstring.h"
#include "..\shared\fileregister.h"
#include "..\shared\mousewheelMgr.h"
#include "..\shared\editshortcutMgr.h"
#include "..\shared\dlgunits.h"
#include "..\shared\passworddialog.h"
#include "..\shared\sysimagelist.h"
#include "..\shared\regkey.h"
#include "..\shared\uiextensionuihelper.h"
#include "..\shared\lightbox.h"
#include "..\shared\remotefile.h"
#include "..\shared\serverdlg.h"
#include "..\shared\focuswatcher.h"
#include "..\shared\localizer.h"
#include "..\shared\outlookhelper.h"

#include "..\3rdparty\gui.h"
#include "..\3rdparty\sendfileto.h"

#include <shlwapi.h>
#include <windowsx.h>
#include <direct.h>
#include <math.h>

#include <afxpriv.h>        // for WM_KICKIDLE

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CToDoListWnd dialog

// popup menus
enum { TRAYICON, TASKCONTEXT, TABCTRLCONTEXT, HEADERCONTEXT };
enum { TB_TOOLBARHIDDEN, TB_DUMMY, TB_TOOLBARANDMENU };
enum { FILEALL, NEWTASK, EDITTASK, VIEW, MOVE, SORTTASK, TOOLS, HELP };

const int TD_VERSION = 31000;
const int BEVEL = 2; // pixels
const int BORDER = 3; // pixels
const int TB_VOFFSET = 4;
const int ONE_MINUTE = 60000; // milliseconds

const LPCTSTR UPDATE_SCRIPT_PATH = _T("http://www.abstractspoon.com/todolist_update.txt");
const LPCTSTR UPDATE_SCRIPT_PATH_MANUAL = _T("http://www.abstractspoon.com/todolist_update_manual.txt");
const LPCTSTR UPDATE_SCRIPT_PATH_STAGING = _T("http://www.abstractspoon.com/todolist_update_manual_staging.txt");
const LPCTSTR PREF_KEY = _T("Preferences");

#define DOPROGRESS(stringID) \
	CWaitCursor cursor; \
	CStatusBarProgressProxy prog(&m_sbProgress, m_statusBar, CEnString(stringID));

enum
{
	WM_POSTONCREATE = (WM_APP+1),
	WM_WEBUPDATEWIZARD,
	WM_ADDTOOLBARTOOLS,
	WM_APPRESTOREFOCUS,
};

enum 
{
	TIMER_READONLYSTATUS = 1,
	TIMER_TIMESTAMPCHANGE,
	TIMER_AUTOSAVE,
	TIMER_CHECKOUTSTATUS,
	TIMER_DUEITEMS,
	TIMER_TIMETRACKING,
	TIMER_AUTOMINIMIZE,
};

enum 
{
	INTERVAL_READONLYSTATUS = 1000,
	INTERVAL_TIMESTAMPCHANGE = 10000,
	//	INTERVAL_AUTOSAVE, // dynamically determined
	INTERVAL_CHECKOUTSTATUS = 5000,
	INTERVAL_DUEITEMS = ONE_MINUTE,
	INTERVAL_TIMETRACKING = 5000,
	//	INTERVAL_AUTOMINIMIZE, // dynamically determined
};

/////////////////////////////////////////////////////////////////////////////

CToDoListWnd::CToDoListWnd() : CFrameWnd(), 
		m_bVisible(-1), 
		m_mruList(0, _T("MRU"), _T("TaskList%d"), 16, AFX_ABBREV_FILENAME_LEN, CEnString(IDS_RECENTFILES)),
		m_nLastSelItem(-1), 
		m_nMaxState(TDCMS_NORMAL), 
		m_nPrevMaxState(TDCMS_NORMAL),
		m_bShowFilterBar(TRUE),
		m_bShowStatusBar(TRUE),
		m_bInNewTask(FALSE),
		m_bSaving(FALSE),
		m_mgrShortcuts(FALSE),
		m_pPrefs(NULL),
		m_bClosing(FALSE),
		m_mgrToDoCtrls(m_tabCtrl),
		m_bFindShowing(FALSE),
		m_bShowProjectName(TRUE),
		m_bQueryOpenAllow(FALSE),
		m_bPasswordPrompting(TRUE),
		m_bShowToolbar(TRUE),
		m_bReloading(FALSE),
		m_hIcon(NULL),
		m_hwndLastFocus(NULL),
		m_bStartHidden(FALSE),
		m_cbQuickFind(ACBS_ALLOWDELETE | ACBS_ADDTOSTART),
		m_bShowTasklistBar(TRUE), 
		m_bShowTreeListBar(TRUE),
		m_bEndingSession(FALSE),
		m_nContextColumnID(TDCC_NONE)
{
	// must do this before initializing any controls
	SetupUIStrings();

	// delete temporary web-update-wizard
	CString sWuwPathTemp = FileMisc::GetAppFolder() + _T("\\WebUpdateSvc2.exe");
	::DeleteFile(sWuwPathTemp);

	// init preferences
	ResetPrefs();

	CFilteredToDoCtrl::EnableExtendedSelection(FALSE, TRUE);

	m_bAutoMenuEnable = FALSE;
}

CToDoListWnd::~CToDoListWnd()
{
	delete m_pPrefs;

	if (m_hIcon)
		DestroyIcon(m_hIcon);

	// cleanup temp files
	// Note: Due task notifications are removed by CToDoCtrlMgr
	::DeleteFile(FileMisc::GetTempFileName(_T("ToDoList.print"), _T("html")));
	::DeleteFile(FileMisc::GetTempFileName(_T("ToDoList.import"), _T("txt")));
	::DeleteFile(FileMisc::GetTempFileName(_T("tdt")));
}

BEGIN_MESSAGE_MAP(CToDoListWnd, CFrameWnd)
//{{AFX_MSG_MAP(CToDoListWnd)
	ON_WM_QUERYOPEN()
	ON_COMMAND(ID_ADDTIMETOLOGFILE, OnAddtimetologfile)
	ON_COMMAND(ID_ARCHIVE_SELECTEDTASKS, OnArchiveSelectedTasks)
	ON_COMMAND(ID_CLOSEALLBUTTHIS, OnCloseallbutthis)
	ON_COMMAND(ID_EDIT_COPYAS_DEPEND, OnCopyTaskasDependency)
	ON_COMMAND(ID_EDIT_COPYAS_DEPENDFULL, OnCopyTaskasDependencyFull)
	ON_COMMAND(ID_EDIT_COPYAS_PATH, OnCopyTaskasPath)
	ON_COMMAND(ID_EDIT_COPYAS_LINK, OnCopyTaskasLink)
	ON_COMMAND(ID_EDIT_COPYAS_LINKFULL, OnCopyTaskasLinkFull)
	ON_COMMAND(ID_EDIT_CLEARREMINDER, OnEditClearReminder)
	ON_COMMAND(ID_EDIT_CLEARTASKCOLOR, OnEditCleartaskcolor)
	ON_COMMAND(ID_EDIT_CLEARTASKICON, OnEditCleartaskicon)
	ON_COMMAND(ID_EDIT_DECTASKPERCENTDONE, OnEditDectaskpercentdone)
	ON_COMMAND(ID_EDIT_DECTASKPRIORITY, OnEditDectaskpriority)
	ON_COMMAND(ID_EDIT_FLAGTASK, OnEditFlagtask)
	ON_COMMAND(ID_EDIT_INCTASKPERCENTDONE, OnEditInctaskpercentdone)
	ON_COMMAND(ID_EDIT_INCTASKPRIORITY, OnEditInctaskpriority)
	ON_COMMAND(ID_EDIT_INSERTDATE, OnEditInsertdate)
	ON_COMMAND(ID_EDIT_INSERTDATETIME, OnEditInsertdatetime)
	ON_COMMAND(ID_EDIT_INSERTTIME, OnEditInserttime)
	ON_COMMAND(ID_EDIT_OFFSETDATES, OnEditOffsetdates)
	ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
	ON_COMMAND(ID_EDIT_SELECTALL, OnEditSelectall)
	ON_COMMAND(ID_EDIT_SETREMINDER, OnEditSetReminder)
	ON_COMMAND(ID_EDIT_SETTASKICON, OnEditSettaskicon)
	ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
	ON_WM_ENABLE()
	ON_COMMAND(ID_FILE_CHANGEPASSWORD, OnFileChangePassword)
	ON_COMMAND(ID_FILE_OPENARCHIVE, OnFileOpenarchive)
	ON_COMMAND(ID_NEXTTASK, OnGotoNexttask)
	ON_COMMAND(ID_PREVTASK, OnGotoPrevtask)
	ON_WM_MEASUREITEM()
	ON_COMMAND(ID_NEWSUBTASK, OnNewsubtask)
	ON_COMMAND(ID_NEWTASK, OnNewtask)
	ON_COMMAND(ID_PRINTPREVIEW, OnPrintpreview)
	ON_COMMAND(ID_EDIT_QUICKFIND, OnQuickFind)
	ON_COMMAND(ID_EDIT_QUICKFINDNEXT, OnQuickFindNext)
	ON_COMMAND(ID_EDIT_QUICKFINDPREV, OnQuickFindPrev)
	ON_COMMAND(ID_SENDTASKS, OnSendTasks)
	ON_COMMAND(ID_SEND_SELTASKS, OnSendSelectedTasks)
	ON_COMMAND(ID_HELP_KEYBOARDSHORTCUTS, OnShowKeyboardshortcuts)
	ON_COMMAND(ID_SHOWTIMELOGFILE, OnShowTimelogfile)
	ON_COMMAND(ID_SORT_MULTI, OnSortMulti)
	ON_WM_SYSCOLORCHANGE()
	ON_COMMAND(ID_TABCTRL_PREFERENCES, OnTabctrlPreferences)
	ON_COMMAND(ID_TASKLIST_SELECTCOLUMNS, OnTasklistSelectColumns)
	ON_COMMAND(ID_TOOLS_CHECKFORUPDATES, OnToolsCheckforupdates)
	ON_COMMAND(ID_TOOLS_TRANSFORM, OnToolsTransformactivetasklist)
	ON_UPDATE_COMMAND_UI(ID_ADDTIMETOLOGFILE, OnUpdateAddtimetologfile)
	ON_UPDATE_COMMAND_UI(ID_ARCHIVE_SELECTEDTASKS, OnUpdateArchiveSelectedCompletedTasks)
	ON_UPDATE_COMMAND_UI(ID_CLOSEALLBUTTHIS, OnUpdateCloseallbutthis)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_DEPEND, OnUpdateCopyTaskasDependency)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_DEPENDFULL, OnUpdateCopyTaskasDependencyFull)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_PATH, OnUpdateCopyTaskasPath)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_LINK, OnUpdateCopyTaskasLink)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_LINKFULL, OnUpdateCopyTaskasLinkFull)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEARREMINDER, OnUpdateEditClearReminder)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEARTASKCOLOR, OnUpdateEditCleartaskcolor)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEARTASKICON, OnUpdateEditCleartaskicon)
	ON_UPDATE_COMMAND_UI(ID_EDIT_DECTASKPERCENTDONE, OnUpdateEditDectaskpercentdone)
	ON_UPDATE_COMMAND_UI(ID_EDIT_DECTASKPRIORITY, OnUpdateEditDectaskpriority)
	ON_UPDATE_COMMAND_UI(ID_EDIT_FLAGTASK, OnUpdateEditFlagtask)
	ON_UPDATE_COMMAND_UI(ID_EDIT_INCTASKPERCENTDONE, OnUpdateEditInctaskpercentdone)
	ON_UPDATE_COMMAND_UI(ID_EDIT_INCTASKPRIORITY, OnUpdateEditInctaskpriority)
	ON_UPDATE_COMMAND_UI(ID_EDIT_INSERTDATE, OnUpdateEditInsertdate)
	ON_UPDATE_COMMAND_UI(ID_EDIT_INSERTDATETIME, OnUpdateEditInsertdatetime)
	ON_UPDATE_COMMAND_UI(ID_EDIT_INSERTTIME, OnUpdateEditInserttime)
	ON_UPDATE_COMMAND_UI(ID_EDIT_OFFSETDATES, OnUpdateEditOffsetdates)
	ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SELECTALL, OnUpdateEditSelectall)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SETREMINDER, OnUpdateEditSetReminder)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SETTASKICON, OnUpdateEditSettaskicon)
	ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
	ON_UPDATE_COMMAND_UI(ID_FILE_CHANGEPASSWORD, OnUpdateFileChangePassword)
	ON_UPDATE_COMMAND_UI(ID_FILE_OPENARCHIVE, OnUpdateFileOpenarchive)
	ON_UPDATE_COMMAND_UI(ID_NEXTTASK, OnUpdateGotoNexttask)
	ON_UPDATE_COMMAND_UI(ID_PREVTASK, OnUpdateGotoPrevtask)
	ON_UPDATE_COMMAND_UI(ID_NEWSUBTASK, OnUpdateNewsubtask)
	ON_UPDATE_COMMAND_UI(ID_EDIT_QUICKFIND, OnUpdateQuickFind)
	ON_UPDATE_COMMAND_UI(ID_EDIT_QUICKFINDNEXT, OnUpdateQuickFindNext)
	ON_UPDATE_COMMAND_UI(ID_EDIT_QUICKFINDPREV, OnUpdateQuickFindPrev)
	ON_UPDATE_COMMAND_UI(ID_SHOWTIMELOGFILE, OnUpdateShowTimelogfile)
	ON_UPDATE_COMMAND_UI(ID_SENDTASKS, OnUpdateSendTasks)
	ON_UPDATE_COMMAND_UI(ID_SEND_SELTASKS, OnUpdateSendSelectedTasks)
	ON_UPDATE_COMMAND_UI(ID_SORT_MULTI, OnUpdateSortMulti)
	ON_UPDATE_COMMAND_UI(ID_VIEW_CLEARFILTER, OnUpdateViewClearfilter)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSEDUE, OnUpdateViewCollapseDuetasks)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSESTARTED, OnUpdateViewCollapseStartedtasks)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSEALL, OnUpdateViewCollapseall)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSETASK, OnUpdateViewCollapsetask)
	ON_UPDATE_COMMAND_UI(ID_VIEW_EXPANDDUE, OnUpdateViewExpandDuetasks)
	ON_UPDATE_COMMAND_UI(ID_VIEW_EXPANDSTARTED, OnUpdateViewExpandStartedtasks)
	ON_UPDATE_COMMAND_UI(ID_VIEW_EXPANDALL, OnUpdateViewExpandall)
	ON_UPDATE_COMMAND_UI(ID_VIEW_EXPANDTASK, OnUpdateViewExpandtask)
	ON_UPDATE_COMMAND_UI(ID_VIEW_FILTER, OnUpdateViewFilter)
	ON_UPDATE_COMMAND_UI(ID_VIEW_PROJECTNAME, OnUpdateViewProjectname)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWTASKLISTTABBAR, OnUpdateViewShowTasklistTabbar)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWTREELISTTABBAR, OnUpdateViewShowTreeListTabbar)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWFILTERBAR, OnUpdateViewShowfilterbar)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SORTTASKLISTTABS, OnUpdateViewSorttasklisttabs)
	ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, OnUpdateViewStatusBar)
	ON_UPDATE_COMMAND_UI(ID_VIEW_CYCLETASKVIEWS, OnUpdateViewCycleTaskViews)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TOGGLETREEANDLIST, OnUpdateViewToggleTreeandList)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TOGGLEFILTER, OnUpdateViewTogglefilter)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TOGGLETASKEXPANDED, OnUpdateViewToggletaskexpanded)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TOGGLETASKSANDCOMMENTS, OnUpdateViewToggletasksandcomments)
	ON_UPDATE_COMMAND_UI(ID_WINDOW1, OnUpdateWindow)
	ON_COMMAND(ID_VIEW_CLEARFILTER, OnViewClearfilter)
	ON_COMMAND(ID_VIEW_COLLAPSEDUE, OnViewCollapseDuetasks)
	ON_COMMAND(ID_VIEW_COLLAPSESTARTED, OnViewCollapseStartedtasks)
	ON_COMMAND(ID_VIEW_COLLAPSEALL, OnViewCollapseall)
	ON_COMMAND(ID_VIEW_COLLAPSETASK, OnViewCollapsetask)
	ON_COMMAND(ID_VIEW_EXPANDDUE, OnViewExpandDuetasks)
	ON_COMMAND(ID_VIEW_EXPANDSTARTED, OnViewExpandStartedtasks)
	ON_COMMAND(ID_VIEW_EXPANDALL, OnViewExpandall)
	ON_COMMAND(ID_VIEW_EXPANDTASK, OnViewExpandtask)
	ON_COMMAND(ID_VIEW_FILTER, OnViewFilter)
	ON_COMMAND(ID_VIEW_PROJECTNAME, OnViewProjectname)
	ON_COMMAND(ID_VIEW_SHOWTASKLISTTABBAR, OnViewShowTasklistTabbar) 
	ON_COMMAND(ID_VIEW_SHOWTREELISTTABBAR, OnViewShowTreeListTabbar)
	ON_COMMAND(ID_VIEW_SHOWFILTERBAR, OnViewShowfilterbar)
	ON_COMMAND(ID_VIEW_SORTTASKLISTTABS, OnViewSorttasklisttabs)
	ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
	ON_COMMAND(ID_VIEW_CYCLETASKVIEWS, OnViewCycleTaskViews)
	ON_COMMAND(ID_VIEW_TOGGLETREEANDLIST, OnViewToggleTreeandList)
	ON_COMMAND(ID_VIEW_TOGGLEFILTER, OnViewTogglefilter)
	ON_COMMAND(ID_VIEW_TOGGLETASKEXPANDED, OnViewToggletaskexpanded)
	ON_COMMAND(ID_VIEW_TOGGLETASKSANDCOMMENTS, OnViewToggletasksandcomments)
	ON_WM_WINDOWPOSCHANGED()
	ON_WM_WINDOWPOSCHANGING()
	ON_COMMAND(ID_TASKLIST_CUSTOMCOLUMNS, OnTasklistCustomColumns)
	ON_COMMAND(ID_EDIT_GOTODEPENDENCY, OnEditGotoDependency)
	ON_UPDATE_COMMAND_UI(ID_EDIT_GOTODEPENDENCY, OnUpdateEditGotoDependency)
	ON_COMMAND(ID_EDIT_RECURRENCE, OnEditRecurrence)
	ON_UPDATE_COMMAND_UI(ID_EDIT_RECURRENCE, OnUpdateEditRecurrence)
	ON_WM_QUERYENDSESSION()
	ON_COMMAND(ID_EDIT_CLEARFIELD, OnEditClearAttribute)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEARFIELD, OnUpdateEditClearAttribute)
	ON_COMMAND(ID_EDIT_CLEARFOCUSEDFIELD, OnEditClearFocusedAttribute)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEARFOCUSEDFIELD, OnUpdateEditClearFocusedAttribute)
	ON_UPDATE_COMMAND_UI(ID_TASKLIST_CUSTOMCOLUMNS, OnUpdateTasklistCustomcolumns)
	ON_COMMAND(ID_ADDTIMETOLOGFILE, OnAddtimetologfile)
	ON_UPDATE_COMMAND_UI(ID_ADDTIMETOLOGFILE, OnUpdateAddtimetologfile)
	ON_WM_ACTIVATEAPP()
	ON_WM_ERASEBKGND()
	ON_WM_INITMENUPOPUP()
	ON_WM_MOVE()
	ON_WM_SYSCOMMAND()
	ON_COMMAND(ID_EDIT_PASTEASREF, OnEditPasteAsRef)
	ON_UPDATE_COMMAND_UI(ID_EDIT_PASTEASREF, OnUpdateEditPasteAsRef)
	//}}AFX_MSG_MAP
	ON_CBN_EDITCHANGE(IDC_QUICKFIND, OnEditChangeQuickFind)
	ON_CBN_SELCHANGE(IDC_QUICKFIND, OnSelChangeQuickFind)
	ON_COMMAND(ID_ABOUT, OnAbout)
	ON_COMMAND(ID_ARCHIVE_COMPLETEDTASKS, OnArchiveCompletedtasks)
	ON_COMMAND(ID_CLOSE, OnCloseTasklist)
	ON_COMMAND(ID_CLOSEALL, OnCloseall)
	ON_COMMAND(ID_DELETEALLTASKS, OnDeleteAllTasks)
	ON_COMMAND(ID_DELETETASK, OnDeleteTask)
	ON_COMMAND(ID_EDIT_CLOCK_TASK, OnEditTimeTrackTask)
	ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
	ON_COMMAND(ID_EDIT_COPYAS_HTML, OnEditCopyashtml)
	ON_COMMAND(ID_EDIT_COPYAS_TEXT, OnEditCopyastext)
	ON_COMMAND(ID_EDIT_CUT, OnEditCut)
	ON_COMMAND(ID_EDIT_FINDTASKS, OnFindTasks)
	ON_COMMAND(ID_EDIT_OPENFILEREF, OnEditOpenfileref)
	ON_COMMAND(ID_EDIT_PASTEAFTER, OnEditPasteAfter)
	ON_COMMAND(ID_EDIT_PASTESUB, OnEditPasteSub)
	ON_COMMAND(ID_EDIT_SETFILEREF, OnEditSetfileref)
	ON_COMMAND(ID_EDIT_SPELLCHECKCOMMENTS, OnSpellcheckcomments)
	ON_COMMAND(ID_EDIT_SPELLCHECKTITLE, OnSpellchecktitle)
	ON_COMMAND(ID_EDIT_TASKCOLOR, OnEditTaskcolor)
	ON_COMMAND(ID_EDIT_TASKDONE, OnEditTaskdone)
	ON_COMMAND(ID_EDIT_TASKTEXT, OnEditTasktext)
	ON_COMMAND(ID_EXIT, OnExit)
	ON_COMMAND(ID_FILE_ENCRYPT, OnFileEncrypt)
	ON_COMMAND(ID_FILE_RESETVERSION, OnFileResetversion)
	ON_COMMAND(ID_LOAD_NORMAL, OnLoad)
	ON_COMMAND(ID_MAXCOMMENTS, OnMaximizeComments)
	ON_COMMAND(ID_MAXTASKLIST, OnMaximizeTasklist)
	ON_COMMAND(ID_MINIMIZETOTRAY, OnMinimizeToTray)
	ON_COMMAND(ID_MOVETASKDOWN, OnMovetaskdown)
	ON_COMMAND(ID_MOVETASKLEFT, OnMovetaskleft)
	ON_COMMAND(ID_MOVETASKRIGHT, OnMovetaskright)
	ON_COMMAND(ID_MOVETASKUP, OnMovetaskup)
	ON_COMMAND(ID_NEW, OnNew)
	ON_COMMAND(ID_NEWSUBTASK_ATBOTTOM, OnNewsubtaskAtbottom)
	ON_COMMAND(ID_NEWSUBTASK_ATTOP, OnNewsubtaskAttop)
	ON_COMMAND(ID_NEWTASK_AFTERSELECTEDTASK, OnNewtaskAfterselectedtask)
	ON_COMMAND(ID_NEWTASK_ATBOTTOM, OnNewtaskAtbottom)
	ON_COMMAND(ID_NEWTASK_ATBOTTOMSELECTED, OnNewtaskAtbottomSelected)
	ON_COMMAND(ID_NEWTASK_ATTOP, OnNewtaskAttop)
	ON_COMMAND(ID_NEWTASK_ATTOPSELECTED, OnNewtaskAttopSelected)
	ON_COMMAND(ID_NEWTASK_BEFORESELECTEDTASK, OnNewtaskBeforeselectedtask)
	ON_COMMAND(ID_NEXTTOPLEVELTASK, OnNexttopleveltask)
	ON_COMMAND(ID_OPEN_RELOAD, OnReload)
	ON_COMMAND(ID_PREFERENCES, OnPreferences)
	ON_COMMAND(ID_PREVTOPLEVELTASK, OnPrevtopleveltask)
	ON_COMMAND(ID_PRINT, OnPrint)
	ON_COMMAND(ID_SAVEALL, OnSaveall)
	ON_COMMAND(ID_SAVEAS, OnSaveas)
	ON_COMMAND(ID_SAVE_NORMAL, OnSave)
	ON_COMMAND(ID_SORT, OnSort)
	ON_COMMAND(ID_TOOLS_CHECKIN, OnToolsCheckin)
	ON_COMMAND(ID_TOOLS_CHECKOUT, OnToolsCheckout)
	ON_COMMAND(ID_TOOLS_EXPORT, OnExport)
	ON_COMMAND(ID_TOOLS_IMPORT, OnImportTasklist)
	ON_COMMAND(ID_TOOLS_SPELLCHECKTASKLIST, OnSpellcheckTasklist)
 	ON_COMMAND(ID_TOOLS_REMOVEFROMSOURCECONTROL, OnToolsRemovefromsourcecontrol)
	ON_COMMAND(ID_TOOLS_TOGGLECHECKIN, OnToolsToggleCheckin)
	ON_COMMAND(ID_TRAYICON_CLOSE, OnTrayiconClose)
	ON_COMMAND(ID_TRAYICON_CREATETASK, OnTrayiconCreatetask)
	ON_COMMAND(ID_TRAYICON_SHOW, OnTrayiconShow)
	ON_COMMAND(ID_VIEW_MOVETASKLISTLEFT, OnViewMovetasklistleft)
	ON_COMMAND(ID_VIEW_MOVETASKLISTRIGHT, OnViewMovetasklistright)
	ON_COMMAND(ID_VIEW_NEXT, OnViewNext)
	ON_COMMAND(ID_VIEW_NEXT_SEL, OnViewNextSel)
	ON_COMMAND(ID_VIEW_PREV, OnViewPrev)
	ON_COMMAND(ID_VIEW_PREV_SEL, OnViewPrevSel)
	ON_COMMAND(ID_VIEW_REFRESHFILTER, OnViewRefreshfilter)
	ON_COMMAND(ID_VIEW_TOOLBAR, OnViewToolbar)
	ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE16, OnOpenRecentFile)
	ON_COMMAND_RANGE(ID_EDIT_SETPRIORITYNONE, ID_EDIT_SETPRIORITY10, OnSetPriority)
	ON_COMMAND_RANGE(ID_NEWTASK_SPLITTASKINTO_TWO, ID_NEWTASK_SPLITTASKINTO_FIVE, OnSplitTaskIntoPieces)
	ON_COMMAND_RANGE(ID_SORT_BYFIRST, ID_SORT_BYLAST, OnSortBy)
	ON_COMMAND_RANGE(ID_TOOLS_SHOWTASKS_DUETODAY, ID_TOOLS_SHOWTASKS_DUEENDNEXTMONTH, OnToolsShowtasksDue)
//	ON_COMMAND_RANGE(ID_TOOLS_USEREXT1, ID_TOOLS_USEREXT16, OnUserUIExtension)
	ON_COMMAND_RANGE(ID_TOOLS_USERTOOL1, ID_TOOLS_USERTOOL16, OnUserTool)
	ON_COMMAND_RANGE(ID_FILE_OPEN_USERSTORAGE1, ID_FILE_OPEN_USERSTORAGE16, OnFileOpenFromUserStorage)
	ON_COMMAND_RANGE(ID_FILE_SAVE_USERSTORAGE1, ID_FILE_SAVE_USERSTORAGE16, OnFileSaveToUserStorage)
	ON_COMMAND_RANGE(ID_TRAYICON_SHOWDUETASKS1, ID_TRAYICON_SHOWDUETASKS20, OnTrayiconShowDueTasks)
	ON_COMMAND_RANGE(ID_WINDOW1, ID_WINDOW16, OnWindow)
	ON_MESSAGE(WM_ADDTOOLBARTOOLS, OnAddToolbarTools)
	ON_MESSAGE(WM_APPRESTOREFOCUS, OnAppRestoreFocus)
	ON_MESSAGE(WM_GETFONT, OnGetFont)
	ON_MESSAGE(WM_GETICON, OnGetIcon)
	ON_MESSAGE(WM_HOTKEY, OnHotkey)
	ON_MESSAGE(WM_POSTONCREATE, OnPostOnCreate)
	ON_MESSAGE(WM_POWERBROADCAST, OnPowerBroadcast)
	ON_MESSAGE(WM_WEBUPDATEWIZARD, OnWebUpdateWizard)
	ON_NOTIFY(NM_CLICK, IDC_TRAYICON, OnTrayIconClick) 
	ON_NOTIFY(NM_DBLCLK, IDC_TRAYICON, OnTrayIconDblClk)
	ON_NOTIFY(NM_MCLICK, IDC_TABCONTROL, OnMBtnClickTabcontrol)
	ON_NOTIFY(NM_RCLICK, IDC_TRAYICON, OnTrayIconRClick)
	ON_NOTIFY(TCN_SELCHANGE, IDC_TABCONTROL, OnSelchangeTabcontrol)
	ON_NOTIFY(TCN_SELCHANGING, IDC_TABCONTROL, OnSelchangingTabcontrol)
//	ON_NOTIFY(TTN_NEEDTEXTA, 0, OnNeedTooltipText)
	ON_NOTIFY(TTN_NEEDTEXTW, 0, OnNeedTooltipText)
	ON_REGISTERED_MESSAGE(WM_ACB_ITEMADDED, OnQuickFindItemAdded)
	ON_REGISTERED_MESSAGE(WM_FBN_FILTERCHNG, OnSelchangeFilter)
	ON_REGISTERED_MESSAGE(WM_FTD_APPLYASFILTER, OnFindApplyAsFilter)
	ON_REGISTERED_MESSAGE(WM_FTD_CLOSE, OnFindDlgClose)
	ON_REGISTERED_MESSAGE(WM_FTD_ADDSEARCH, OnFindAddSearch)
	ON_REGISTERED_MESSAGE(WM_FTD_DELETESEARCH, OnFindDeleteSearch)
	ON_REGISTERED_MESSAGE(WM_FTD_FIND, OnFindDlgFind)
	ON_REGISTERED_MESSAGE(WM_FTD_SELECTALL, OnFindSelectAll)
	ON_REGISTERED_MESSAGE(WM_FTD_SELECTRESULT, OnFindSelectResult)
	ON_REGISTERED_MESSAGE(WM_FW_FOCUSCHANGE, OnFocusChange)
	ON_REGISTERED_MESSAGE(WM_PGP_CLEARMRU, OnPreferencesClearMRU)
	ON_REGISTERED_MESSAGE(WM_PGP_CLEANUPDICTIONARY, OnPreferencesCleanupDictionary)
	ON_REGISTERED_MESSAGE(WM_PTDP_LISTCHANGE, OnPreferencesDefaultListChange)
	ON_REGISTERED_MESSAGE(WM_PTP_TESTTOOL, OnPreferencesTestTool)
	ON_REGISTERED_MESSAGE(WM_TDCM_TASKHASREMINDER, OnToDoCtrlTaskHasReminder)
	ON_REGISTERED_MESSAGE(WM_TDCM_TASKISDONE, OnToDoCtrlTaskIsDone)
	ON_REGISTERED_MESSAGE(WM_TDCM_LENGTHYOPERATION, OnToDoCtrlDoLengthyOperation)
	ON_REGISTERED_MESSAGE(WM_TDCM_TASKLINK, OnToDoCtrlDoTaskLink)
	ON_REGISTERED_MESSAGE(WM_TDCM_FAILEDLINK, OnTodoCtrlFailedLink)
	ON_REGISTERED_MESSAGE(WM_TDCN_DOUBLECLKREMINDERCOL, OnDoubleClkReminderCol)
	ON_REGISTERED_MESSAGE(WM_TDCN_LISTCHANGE, OnToDoCtrlNotifyListChange)
	ON_REGISTERED_MESSAGE(WM_TDCN_MODIFY, OnToDoCtrlNotifyMod)
	ON_REGISTERED_MESSAGE(WM_TDCN_RECREATERECURRINGTASK, OnToDoCtrlNotifyRecreateRecurringTask)
	ON_REGISTERED_MESSAGE(WM_TDCN_TIMETRACK, OnToDoCtrlNotifyTimeTrack)
	ON_REGISTERED_MESSAGE(WM_TDCN_VIEWPOSTCHANGE, OnToDoCtrlNotifyViewChange)
	ON_REGISTERED_MESSAGE(WM_TDL_GETVERSION , OnToDoListGetVersion)
	ON_REGISTERED_MESSAGE(WM_TDL_ISCLOSING , OnToDoListIsClosing)
	ON_REGISTERED_MESSAGE(WM_TDL_REFRESHPREFS , OnToDoListRefreshPrefs)
	ON_REGISTERED_MESSAGE(WM_TDL_RESTORE , OnToDoListRestore)
	ON_REGISTERED_MESSAGE(WM_TDL_SHOWWINDOW , OnToDoListShowWindow)
	ON_REGISTERED_MESSAGE(WM_TD_REMINDER, OnToDoCtrlReminder)
	ON_REGISTERED_MESSAGE(WM_TLDT_DROP, OnDropFile)
	ON_UPDATE_COMMAND_UI(ID_ARCHIVE_COMPLETEDTASKS, OnUpdateArchiveCompletedtasks)
	ON_UPDATE_COMMAND_UI(ID_CLOSEALL, OnUpdateCloseall)
	ON_UPDATE_COMMAND_UI(ID_DELETEALLTASKS, OnUpdateDeletealltasks) 
	ON_UPDATE_COMMAND_UI(ID_DELETETASK, OnUpdateDeletetask)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLOCK_TASK, OnUpdateEditTimeTrackTask)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_HTML, OnUpdateEditCopy)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPYAS_TEXT, OnUpdateEditCopy)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
	ON_UPDATE_COMMAND_UI(ID_EDIT_OPENFILEREF, OnUpdateEditOpenfileref)
	ON_UPDATE_COMMAND_UI(ID_EDIT_PASTEAFTER, OnUpdateEditPasteAfter)
	ON_UPDATE_COMMAND_UI(ID_EDIT_PASTESUB, OnUpdateEditPasteSub)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SETFILEREF, OnUpdateEditSetfileref)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SPELLCHECKCOMMENTS, OnUpdateSpellcheckcomments)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SPELLCHECKTITLE, OnUpdateSpellchecktitle)
	ON_UPDATE_COMMAND_UI(ID_EDIT_TASKCOLOR, OnUpdateTaskcolor)
	ON_UPDATE_COMMAND_UI(ID_EDIT_TASKDONE, OnUpdateTaskdone)
	ON_UPDATE_COMMAND_UI(ID_EDIT_TASKTEXT, OnUpdateEditTasktext)
	ON_UPDATE_COMMAND_UI(ID_FILE_ENCRYPT, OnUpdateFileEncrypt)
	ON_UPDATE_COMMAND_UI(ID_FILE_MRU_FILE1, OnUpdateRecentFileMenu)
	ON_UPDATE_COMMAND_UI(ID_FILE_RESETVERSION, OnUpdateFileResetversion)
	ON_UPDATE_COMMAND_UI(ID_MAXCOMMENTS, OnUpdateMaximizeComments)
	ON_UPDATE_COMMAND_UI(ID_MAXTASKLIST, OnUpdateMaximizeTasklist)
	ON_UPDATE_COMMAND_UI(ID_MOVETASKDOWN, OnUpdateMovetaskdown)
	ON_UPDATE_COMMAND_UI(ID_MOVETASKLEFT, OnUpdateMovetaskleft)
	ON_UPDATE_COMMAND_UI(ID_MOVETASKRIGHT, OnUpdateMovetaskright)
	ON_UPDATE_COMMAND_UI(ID_MOVETASKUP, OnUpdateMovetaskup)
	ON_UPDATE_COMMAND_UI(ID_NEW, OnUpdateNew)
	ON_UPDATE_COMMAND_UI(ID_NEWSUBTASK_ATBOTTOM, OnUpdateNewsubtaskAtBottom)
	ON_UPDATE_COMMAND_UI(ID_NEWSUBTASK_ATTOP, OnUpdateNewsubtaskAttop)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK, OnUpdateNewtask)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK_AFTERSELECTEDTASK, OnUpdateNewtaskAfterselectedtask)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK_ATBOTTOM, OnUpdateNewtaskAtbottom)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK_ATBOTTOMSELECTED, OnUpdateNewtaskAtbottomSelected)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK_ATTOP, OnUpdateNewtaskAttop)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK_ATTOPSELECTED, OnUpdateNewtaskAttopSelected)
	ON_UPDATE_COMMAND_UI(ID_NEWTASK_BEFORESELECTEDTASK, OnUpdateNewtaskBeforeselectedtask)
	ON_UPDATE_COMMAND_UI(ID_NEXTTOPLEVELTASK, OnUpdateNexttopleveltask)
	ON_UPDATE_COMMAND_UI(ID_OPEN_RELOAD, OnUpdateReload)
	ON_UPDATE_COMMAND_UI(ID_PREVTOPLEVELTASK, OnUpdatePrevtopleveltask)
	ON_UPDATE_COMMAND_UI(ID_PRINT, OnUpdatePrint)
	ON_UPDATE_COMMAND_UI(ID_SAVEALL, OnUpdateSaveall)
	ON_UPDATE_COMMAND_UI(ID_SAVEAS, OnUpdateSaveas)
	ON_UPDATE_COMMAND_UI(ID_SAVE_NORMAL, OnUpdateSave)
	ON_UPDATE_COMMAND_UI(ID_SB_SELCOUNT, OnUpdateSBSelectionCount)
	ON_UPDATE_COMMAND_UI(ID_SB_TASKCOUNT, OnUpdateSBTaskCount)
	ON_UPDATE_COMMAND_UI(ID_SORT, OnUpdateSort)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_CHECKIN, OnUpdateToolsCheckin)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_CHECKOUT, OnUpdateToolsCheckout)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_EXPORT, OnUpdateExport)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_IMPORT, OnUpdateImport)
 	ON_UPDATE_COMMAND_UI(ID_TOOLS_REMOVEFROMSOURCECONTROL, OnUpdateToolsRemovefromsourcecontrol)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_SPELLCHECKTASKLIST, OnUpdateSpellcheckTasklist)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_TOGGLECHECKIN, OnUpdateToolsToggleCheckin)
	ON_UPDATE_COMMAND_UI(ID_TOOLS_TRANSFORM, OnUpdateExport) // use same text as export
	ON_UPDATE_COMMAND_UI(ID_VIEW_MOVETASKLISTLEFT, OnUpdateViewMovetasklistleft)
	ON_UPDATE_COMMAND_UI(ID_VIEW_MOVETASKLISTRIGHT, OnUpdateViewMovetasklistright)
	ON_UPDATE_COMMAND_UI(ID_VIEW_NEXT, OnUpdateViewNext)
	ON_UPDATE_COMMAND_UI(ID_VIEW_NEXT_SEL, OnUpdateViewNextSel)
	ON_UPDATE_COMMAND_UI(ID_VIEW_PREV, OnUpdateViewPrev)
	ON_UPDATE_COMMAND_UI(ID_VIEW_PREV_SEL, OnUpdateViewPrevSel)
	ON_UPDATE_COMMAND_UI(ID_VIEW_REFRESHFILTER, OnUpdateViewRefreshfilter)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, OnUpdateViewToolbar)
	ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_SETPRIORITYNONE, ID_EDIT_SETPRIORITY10, OnUpdateSetPriority)	
	ON_UPDATE_COMMAND_UI_RANGE(ID_NEWTASK_SPLITTASKINTO_TWO, ID_NEWTASK_SPLITTASKINTO_FIVE, OnUpdateSplitTaskIntoPieces)
	ON_UPDATE_COMMAND_UI_RANGE(ID_SORT_BYFIRST, ID_SORT_BYLAST, OnUpdateSortBy)
//	ON_UPDATE_COMMAND_UI_RANGE(ID_TOOLS_USEREXT1, ID_TOOLS_USEREXT16, OnUpdateUserUIExtension)
	ON_UPDATE_COMMAND_UI_RANGE(ID_TOOLS_USERTOOL1, ID_TOOLS_USERTOOL16, OnUpdateUserTool)
	ON_WM_CLOSE()
	ON_WM_CONTEXTMENU()
	ON_WM_COPYDATA()
	ON_WM_CREATE()
	ON_WM_DRAWITEM()
	ON_WM_ERASEBKGND()
	ON_WM_HELPINFO()
	ON_WM_INITMENUPOPUP()
	ON_WM_ENDSESSION()
	ON_WM_SIZE()
	ON_WM_SYSCOMMAND()
	ON_WM_TIMER()
	
#ifdef _DEBUG
	ON_COMMAND(ID_DEBUGENDSESSION, OnDebugEndSession)
	ON_COMMAND(ID_DEBUGSHOWSETUPDLG, OnDebugShowSetupDlg)
#endif

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CToDoListWnd message handlers

void CToDoListWnd::SetupUIStrings()
{
	// set up UI strings for helper classes
	CTimeEdit::SetUnits(THU_MINS, CEnString(IDS_TE_MINS), CEnString(IDS_MINS_ABBREV));
	CTimeEdit::SetUnits(THU_HOURS, CEnString(IDS_TE_HOURS), CEnString(IDS_HOURS_ABBREV));
	CTimeEdit::SetUnits(THU_DAYS, CEnString(IDS_TE_DAYS), CEnString(IDS_DAYS_ABBREV));
	CTimeEdit::SetUnits(THU_WEEKS, CEnString(IDS_TE_WEEKS), CEnString(IDS_WEEKS_ABBREV));
	CTimeEdit::SetUnits(THU_MONTHS, CEnString(IDS_TE_MONTHS), CEnString(IDS_MONTHS_ABBREV));
	CTimeEdit::SetUnits(THU_YEARS, CEnString(IDS_TE_YEARS), CEnString(IDS_YEARS_ABBREV));
	CTimeEdit::SetDefaultButtonTip(CEnString(IDS_TIMEUNITS));

	CFileEdit::SetDefaultButtonTips(CEnString(IDS_BROWSE), CEnString(IDS_VIEW));
	CFileEdit::SetDefaultBrowseTitles(CEnString(IDS_BROWSEFILE_TITLE), CEnString(IDS_BROWSEFOLDER_TITLE));

	CTDLRecurringTaskEdit::SetDefaultButtonTip(CEnString(IDS_OPTIONS));

	CXmlFileEx::SetUIStrings(CEnString(IDS_ENCRYPTEDFILE), CEnString(IDS_DECRYPTFAILED));

/*
	CServerDlg::SetItemText(SD_TITLE, IDS_SCD_TITLE);
	CServerDlg::SetItemText(IDC_SD_SERVERLABEL, IDS_SD_SERVERLABEL);
	CServerDlg::SetItemText(IDC_SD_USERNAMELABEL, IDS_SD_USERNAMELABEL);
	CServerDlg::SetItemText(IDC_SD_PASSWORDLABEL, IDS_SD_PASSWORDLABEL);
	CServerDlg::SetItemText(IDC_SD_ANONLOGIN, IDS_SD_ANONLOGIN);
	CServerDlg::SetItemText(IDOK, IDS_OK);
	CServerDlg::SetItemText(IDCANCEL, IDS_CANCEL);


	CPasswordDialog::SetItemText(PD_TITLE, IDS_PD_TITLE);
	CPasswordDialog::SetItemText(IDC_PD_PASSWORDLABEL, IDS_PD_PASSWORDLABEL);
	CPasswordDialog::SetItemText(IDC_PD_CONFIRMLABEL, IDS_PD_CONFIRMLABEL);
	CPasswordDialog::SetItemText(DLG_PD_CONFIRMFAILED, IDS_PD_CONFIRMFAILED);
	CPasswordDialog::SetItemText(IDOK, IDS_OK);
	CPasswordDialog::SetItemText(IDCANCEL, IDS_CANCEL);

	CSpellCheckDlg::SetItemText(IDC_SCD_DICTLABEL, IDS_SCD_DICTLABEL);
	CSpellCheckDlg::SetItemText(IDC_SCD_BROWSE, IDS_SCD_BROWSE);
	CSpellCheckDlg::SetItemText(IDC_SCD_URL, IDS_SCD_URL);
	CSpellCheckDlg::SetItemText(IDC_SCD_CHECKINGLABEL, IDS_SCD_CHECKINGLABEL);
	CSpellCheckDlg::SetItemText(IDC_SCD_RESTART, IDS_SCD_RESTART);
	CSpellCheckDlg::SetItemText(IDC_SCD_REPLACELABEL, IDS_SCD_REPLACELABEL);
	CSpellCheckDlg::SetItemText(IDC_SCD_WITHLABEL, IDS_SCD_WITHLABEL);
	CSpellCheckDlg::SetItemText(IDC_SCD_REPLACE, IDS_SCD_REPLACE);
	CSpellCheckDlg::SetItemText(IDC_SCD_NEXT, IDS_SCD_NEXT);
	CSpellCheckDlg::SetItemText(IDOK, IDS_OK);
	CSpellCheckDlg::SetItemText(IDCANCEL, IDS_CANCEL);
	CSpellCheckDlg::SetItemText(SCD_TITLE, IDS_SCD_TITLE);
	CSpellCheckDlg::SetItemText(DLG_SCD_SETUPMSG, IDS_SCD_SETUPMSG);
	CSpellCheckDlg::SetItemText(DLG_SCD_DICTFILTER, IDS_SCD_DICTFILTER);
	CSpellCheckDlg::SetItemText(DLG_SCD_ENGINEFILTER, IDS_SCD_ENGINEFILTER);
	CSpellCheckDlg::SetItemText(DLG_SCD_ENGINETITLE, IDS_SCD_ENGINETITLE);
*/
	CSpellCheckDlg::SetItemText(DLG_SCD_BROWSETITLE, IDS_SCD_BROWSETITLE);
}

BOOL CToDoListWnd::OnHelpInfo(HELPINFO* /*pHelpInfo*/)
{
	// always eat this because we handle the F1 ourselves
	return FALSE;
}

void CToDoListWnd::SetUITheme(const CString& sThemeFile)
{
	if (COSVersion() < OSV_XP)
		return;

	// cache existing theme
	CUIThemeFile themeCur = m_theme;

	if (CThemed::IsThemeActive() && m_theme.LoadThemeFile(sThemeFile)) 
		m_sThemeFile = sThemeFile;
	else
	{
		m_sThemeFile.Empty();
		m_theme.Reset();
	}
	
	// update the UI
	if (themeCur != m_theme)
	{
		m_cbQuickFind.DestroyWindow();
		m_toolbar.DestroyWindow();
		m_tbHelper.Release();

		InitToolbar();

		// reinitialize the menu icon manager
		m_mgrMenuIcons.Release();
		InitMenuIconManager();
	}
	else
		m_toolbar.SetBackgroundColors(m_theme.crToolbarLight, 
										m_theme.crToolbarDark, 
										m_theme.HasGradient(), 
										m_theme.HasGlass());

	m_statusBar.SetUIColors(m_theme.crStatusBarLight, 
							m_theme.crStatusBarDark, 
							m_theme.crStatusBarText, 
							m_theme.HasGradient(), 
							m_theme.HasGlass());

	m_menubar.SetBackgroundColor(m_theme.crMenuBack);
	m_filterBar.SetUIColors(m_theme.crAppBackLight, m_theme.crAppText);
	m_tabCtrl.SetBackgroundColor(m_theme.crAppBackDark);

	for (int nCtl = 0; nCtl < GetTDCCount(); nCtl++)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtl);
		tdc.SetUITheme(m_theme);
	}

	if (m_findDlg.GetSafeHwnd())
		m_findDlg.SetUITheme(m_theme);

	DrawMenuBar();
	Invalidate();
}

BOOL CToDoListWnd::Create(const TDCSTARTUP& startup)
{
	m_startupOptions = startup;
	m_bVisible = startup.HasFlag(TLD_FORCEVISIBLE) ? 1 : -1;
	m_bUseStagingScript = startup.HasFlag(TLD_STAGING);

#ifdef _DEBUG
	m_bPasswordPrompting = FALSE;
#else
	m_bPasswordPrompting = startup.HasFlag(TLD_PASSWORDPROMPTING);
#endif

	FileMisc::EnableLogging(startup.HasFlag(TLD_LOGGING), TRUE, FALSE);
	
	return CFrameWnd::LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, NULL, NULL);
}

int CToDoListWnd::GetVersion()
{
	return TD_VERSION;
}

int CToDoListWnd::MessageBox(UINT nIDText, UINT nIDCaption, UINT nType, LPCTSTR szData)
{
	if (szData && *szData)
		return MessageBox(CEnString(nIDText, szData), nIDCaption, nType);
	else
		return MessageBox(CEnString(nIDText), nIDCaption, nType);
}

int CToDoListWnd::MessageBox(const CString& sText, UINT nIDCaption, UINT nType)
{
	return MessageBox(sText, CEnString(nIDCaption), nType);
}

int CToDoListWnd::MessageBox(const CString& sText, const CString& sCaption, UINT nType)
{
	CString sMessage;

	if (!sCaption.IsEmpty())
		sMessage.Format(_T("%s|%s"), sCaption, sText);
	else
		sMessage = sText;

	return AfxMessageBox(sMessage, nType);
}

int CToDoListWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// set frame icon
	HICON hIcon = GraphicsMisc::LoadIcon(IDR_MAINFRAME);
	SetIcon(hIcon, FALSE);
		
	// set taskbar icon
	hIcon = GraphicsMisc::LoadIcon(IDR_MAINFRAME, 32);
	SetIcon(hIcon, TRUE);

	SetWindowPos(NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE);

	// menu
	if (!LoadMenubar())
		return -1;

	// drop target
	if (!m_dropTarget.Register(this, this))
		return -1;

	// trayicon
	// we always create the trayicon (for simplicity) but we only
	// show it if required
	BOOL bUseSysTray = Prefs().GetUseSysTray();
	
	m_trayIcon.Create(WS_CHILD | (bUseSysTray ? WS_VISIBLE : 0), 
						this, 
						IDC_TRAYICON, 
						IDI_TRAY_STD, 
						CString(IDS_COPYRIGHT));
	
	// toolbar
	if (!InitToolbar())
		return -1;

	// statusbar
	if (!InitStatusbar())
		return -1;

	// filterbar
	if (!InitFilterbar())
		return -1;
	
	// tabctrl
	if (!m_tabCtrl.Create(WS_CHILD | WS_VISIBLE | TCS_HOTTRACK | TCS_TABS | TCS_SINGLELINE | TCS_RIGHTJUSTIFY | TCS_TOOLTIPS, 
							CRect(0, 0, 10, 10), this, IDC_TABCONTROL))
		return -1;

	m_tabCtrl.GetToolTips()->ModifyStyle(0, TTS_ALWAYSTIP);
	CLocalizer::EnableTranslation(m_tabCtrl, FALSE);

	BOOL bStackTabbar = Prefs().GetStackTabbarItems();
	
	m_tabCtrl.ModifyStyle(bStackTabbar ? 0 : TCS_MULTILINE, bStackTabbar ? TCS_MULTILINE : 0);
	UpdateTabSwitchTooltip();
	
	if (m_ilTabCtrl.Create(16, 16, ILC_COLOR32 | ILC_MASK, 4, 1))
	{
		CBitmap bm;
		bm.LoadBitmap(IDB_SOURCECONTROL_STD);
		m_ilTabCtrl.Add(&bm, RGB(255, 0, 255));
		m_tabCtrl.SetImageList(&m_ilTabCtrl);
	}
	else
		return -1;
	
	// UI Font
	InitUIFont();

	LoadSettings();

	// add a barebones tasklist while we're still hidden
	if (!CreateNewTaskList(FALSE))
		return -1;

	// timers
	SetTimer(TIMER_DUEITEMS, TRUE);
	SetTimer(TIMER_TIMETRACKING, TRUE);

	// notify users about dodgy content plugins
	if (m_mgrContent.SomePluginsHaveBadversions())
	{
		if (MessageBox(IDS_BADPLUGINVERSIONS, IDS_BADPLUGINTITLE, MB_OKCANCEL) == IDCANCEL)
			return -1;
	}

	// inherited parent task attributes for new tasks
	CTDCAttributeArray aParentAttrib;
	BOOL bUpdateAttrib;

	Prefs().GetParentAttribsUsed(aParentAttrib, bUpdateAttrib);
	CFilteredToDoCtrl::SetInheritedParentAttributes(aParentAttrib, bUpdateAttrib);

	// theme
	SetUITheme(Prefs().GetUITheme());
				
	// late initialization
	PostMessage(WM_POSTONCREATE);
	
	return 0; // success
}

void CToDoListWnd::InitUIFont()
{
	GraphicsMisc::VerifyDeleteObject(m_fontMain);

	HFONT hFontUI = GraphicsMisc::CreateFont(_T("Tahoma"), 8);

	if (m_fontMain.Attach(hFontUI))
		CDialogHelper::SetFont(this, m_fontMain); // will update all child controls
}

void CToDoListWnd::InitShortcutManager()
{
	// setup defaults first
	m_mgrShortcuts.AddShortcut(ID_LOAD_NORMAL, 'O', HOTKEYF_CONTROL); 
	m_mgrShortcuts.AddShortcut(ID_OPEN_RELOAD, 'R', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_SAVE_NORMAL, 'S', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_NEWTASK_BEFORESELECTEDTASK, 'N', HOTKEYF_CONTROL | HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_NEWTASK_AFTERSELECTEDTASK, 'N', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_NEWSUBTASK_ATBOTTOM, 'N', HOTKEYF_CONTROL | HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_MAXTASKLIST, 'M', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_MAXCOMMENTS, 'M', HOTKEYF_CONTROL | HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_PRINT, 'P', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_VIEW_NEXT, VK_TAB, HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_VIEW_PREV, VK_TAB, HOTKEYF_CONTROL | HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_EDIT_CUT, 'X', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_COPY, 'C', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_PASTEAFTER, 'V', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_PASTESUB, 'V', HOTKEYF_CONTROL | HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_EDIT_INSERTDATETIME, 'D', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_OPENFILEREF, 'G', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EXIT, VK_F4, HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_CLOSE, VK_F4, HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_MOVETASKDOWN, VK_DOWN, HOTKEYF_CONTROL | HOTKEYF_EXT);
	m_mgrShortcuts.AddShortcut(ID_MOVETASKUP, VK_UP, HOTKEYF_CONTROL | HOTKEYF_EXT);
	m_mgrShortcuts.AddShortcut(ID_MOVETASKLEFT, VK_LEFT, HOTKEYF_CONTROL | HOTKEYF_EXT);
	m_mgrShortcuts.AddShortcut(ID_MOVETASKRIGHT, VK_RIGHT, HOTKEYF_CONTROL | HOTKEYF_EXT);
	m_mgrShortcuts.AddShortcut(ID_DELETETASK, VK_DELETE, HOTKEYF_EXT);
	m_mgrShortcuts.AddShortcut(ID_EDIT_TASKTEXT, VK_F2, 0);
	m_mgrShortcuts.AddShortcut(ID_EDIT_TASKDONE, VK_SPACE, HOTKEYF_CONTROL | HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_TOOLS_IMPORT, 'I', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_TOOLS_EXPORT, 'E', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_HELP, VK_F1, 0);
	m_mgrShortcuts.AddShortcut(ID_WINDOW1, '1', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW2, '2', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW3, '3', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW4, '4', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW5, '5', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW6, '6', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW7, '7', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW8, '8', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_WINDOW9, '9', HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_TOOLS_TRANSFORM, 'T', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_FINDTASKS, 'F', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_QUICKFIND, 'Q', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_QUICKFINDNEXT, VK_F3, 0);
	m_mgrShortcuts.AddShortcut(ID_EDIT_QUICKFINDPREV, VK_F3, HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_VIEW_REFRESHFILTER, VK_F5, HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_VIEW_TOGGLEFILTER, VK_F12, 0);
	m_mgrShortcuts.AddShortcut(ID_EDIT_SELECTALL, 'A', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_PREVTASK, VK_F7, 0);
	m_mgrShortcuts.AddShortcut(ID_NEXTTASK, VK_F8, 0);
	m_mgrShortcuts.AddShortcut(ID_EDIT_UNDO, 'Z', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_REDO, 'Y', HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_VIEW_TOGGLETREEANDLIST, VK_F10, 0);
	m_mgrShortcuts.AddShortcut(ID_VIEW_CYCLETASKVIEWS, VK_F10, HOTKEYF_SHIFT);
	m_mgrShortcuts.AddShortcut(ID_VIEW_TOGGLETASKSANDCOMMENTS, VK_F11, 0);
	m_mgrShortcuts.AddShortcut(ID_VIEW_TOGGLETASKEXPANDED, VK_SPACE, HOTKEYF_CONTROL | HOTKEYF_ALT);
	m_mgrShortcuts.AddShortcut(ID_EDIT_INCTASKPERCENTDONE, VK_ADD, HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_EDIT_DECTASKPERCENTDONE, VK_SUBTRACT, HOTKEYF_CONTROL);
	m_mgrShortcuts.AddShortcut(ID_VIEW_PREV_SEL, VK_LEFT, HOTKEYF_ALT | HOTKEYF_EXT);
	m_mgrShortcuts.AddShortcut(ID_VIEW_NEXT_SEL, VK_RIGHT, HOTKEYF_ALT | HOTKEYF_EXT);
	
	// now init shortcut mgr which will override the defaults
	// with the users actual settings
	CPreferences prefs;

	if (m_mgrShortcuts.Initialize(this, &prefs))
	{
		// fix for previously adding escape key as a shortcut for IDCLOSE 
		// (big mistake)
		if (m_mgrShortcuts.GetShortcut(IDCLOSE) == VK_ESCAPE)
			m_mgrShortcuts.DeleteShortcut(IDCLOSE);

		// fix for paste being wrongly set up
		if (m_mgrShortcuts.GetShortcut(ID_EDIT_PASTE))
		{
			// delete existing
			m_mgrShortcuts.DeleteShortcut(ID_EDIT_PASTE);

			// if nothing already assigned use Ctrl+V
			if (!m_mgrShortcuts.GetShortcut(ID_EDIT_PASTESUB))
				m_mgrShortcuts.AddShortcut(ID_EDIT_PASTESUB, 'V', HOTKEYF_CONTROL);
		}
	}
}

void CToDoListWnd::InitMenuIconManager()
{
	if (!m_mgrMenuIcons.Initialize(this))
		return;
	
	m_mgrMenuIcons.ClearImages();
	
	// images
	UINT nToolbarImageID = IDB_APP_TOOLBAR_STD;
	UINT nExtraImageID = IDB_APP_EXTRA_STD;
				
	CUIntArray aCmdIDs;
	
	// toolbar
	aCmdIDs.Add(ID_LOAD_NORMAL);
	aCmdIDs.Add(ID_SAVE_NORMAL);
	aCmdIDs.Add(ID_SAVEALL);
	
	// new tasks
	aCmdIDs.Add(GetNewTaskCmdID());
	aCmdIDs.Add(GetNewSubtaskCmdID());
	
	aCmdIDs.Add(ID_EDIT_TASKTEXT);
	aCmdIDs.Add(ID_EDIT_SETTASKICON);
	aCmdIDs.Add(ID_EDIT_SETREMINDER);
	aCmdIDs.Add(ID_EDIT_UNDO);
	aCmdIDs.Add(ID_EDIT_REDO);
	aCmdIDs.Add(ID_MAXTASKLIST);
	aCmdIDs.Add(ID_VIEW_EXPANDTASK);
	aCmdIDs.Add(ID_VIEW_COLLAPSETASK);
	aCmdIDs.Add(ID_VIEW_PREV_SEL);
	aCmdIDs.Add(ID_VIEW_NEXT_SEL);
	aCmdIDs.Add(ID_EDIT_FINDTASKS);
	aCmdIDs.Add(ID_SORT);
	aCmdIDs.Add(ID_DELETETASK);
	
	// source control
	if (GetTDCCount() && m_mgrToDoCtrls.PathSupportsSourceControl(GetSelToDoCtrl()))
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl();

		if (tdc.IsCheckedOut())
			aCmdIDs.Add(ID_TOOLS_CHECKIN);
		else
			aCmdIDs.Add(ID_TOOLS_CHECKOUT);
	}
	else
		aCmdIDs.Add(ID_TOOLS_TOGGLECHECKIN);
		
	aCmdIDs.Add(ID_PREFERENCES);

	if (m_theme.HasToolbarImageFile(_T("TODOLIST")))
	{
		COLORREF crMask = CLR_NONE;
		CString sImagePath = m_theme.GetToolbarImageFile(_T("TODOLIST"), crMask);

		VERIFY(m_mgrMenuIcons.AddImages(aCmdIDs, sImagePath, 16, crMask));
	}
	else
		m_mgrMenuIcons.AddImages(aCmdIDs, nToolbarImageID, 16, RGB(255, 0, 255));

	// extra
	aCmdIDs.RemoveAll();

  	aCmdIDs.Add(ID_HELP_WIKI);
  	aCmdIDs.Add(ID_HELP_DONATE);
  	aCmdIDs.Add(ID_HELP);

	if (m_theme.HasToolbarImageFile(_T("TODOLIST_EXTRA")))
	{
		COLORREF crMask = CLR_NONE;
		CString sImagePath = m_theme.GetToolbarImageFile(_T("TODOLIST_EXTRA"), crMask);

		VERIFY(m_mgrMenuIcons.AddImages(aCmdIDs, sImagePath, 16, crMask));
	}
	else
		m_mgrMenuIcons.AddImages(aCmdIDs, nExtraImageID, 16, RGB(255, 0, 255));
}

void CToDoListWnd::OnShowKeyboardshortcuts() 
{
	CStringArray aMapping;

	if (m_mgrShortcuts.BuildMapping(IDR_MAINFRAME, aMapping, '|'))
	{
		// add a few misc items that don't appear in the menus
		CString sMisc;

		for (int nItem = 0; nItem < NUM_MISCSHORTCUTS; nItem++)
		{
			if (MISC_SHORTCUTS[nItem].dwShortcut)
				sMisc.Format(_T("%s|%s"), m_mgrShortcuts.GetShortcutText(MISC_SHORTCUTS[nItem].dwShortcut), 
									CEnString(MISC_SHORTCUTS[nItem].nIDShortcut));
			else
				sMisc.Empty();

			aMapping.Add(sMisc);
		}
	
		CKeyboardShortcutDisplayDlg dialog(aMapping, '|');

		dialog.DoModal();
	}
}

LRESULT CToDoListWnd::OnFocusChange(WPARAM wp, LPARAM /*lp*/)
{
	if (m_statusBar.GetSafeHwnd() && IsWindowEnabled() && GetTDCCount() && wp)
	{
		// grab the previous window in the z-order and if its
		// static text then use that as the focus hint
		CWnd* pFocus = CWnd::FromHandle((HWND)wp);
		const CFilteredToDoCtrl& tdc = GetToDoCtrl();
		m_sCurrentFocus.Empty();

		if (CDialogHelper::IsChildOrSame(tdc.GetSafeHwnd(), (HWND)wp))
		{
			m_sCurrentFocus.LoadString(IDS_FOCUS_TASKS);
			m_sCurrentFocus += ": ";
			m_sCurrentFocus += tdc.GetControlDescription(pFocus);
		}
		else if (pFocus == m_cbQuickFind.GetWindow(GW_CHILD))
		{
			m_sCurrentFocus.LoadString(IDS_QUICKFIND);
		}
		else
		{
			if (m_findDlg.GetSafeHwnd() && m_findDlg.IsChild(pFocus))
			{
				m_sCurrentFocus.LoadString(IDS_FINDTASKS);
			}
			else if (m_filterBar.GetSafeHwnd() && m_filterBar.IsChild(pFocus))
			{
				m_sCurrentFocus.LoadString(IDS_FOCUS_FILTERBAR);
			}
			
			if (!m_sCurrentFocus.IsEmpty())
				m_sCurrentFocus += ": ";
			
			m_sCurrentFocus += GetControlLabel(pFocus);
		}		

		// limit length of string
		if (m_sCurrentFocus.GetLength() > 22)
			m_sCurrentFocus = m_sCurrentFocus.Left(20) + _T("...");

		m_statusBar.SetPaneText(m_statusBar.CommandToIndex(ID_SB_FOCUS), m_sCurrentFocus);
		
		// if the status bar is hidden then add text to title bar
		if (!m_bShowStatusBar)
			UpdateCaption();
	}

	return 0L;
}

LRESULT CToDoListWnd::OnGetIcon(WPARAM bLargeIcon, LPARAM /*not used*/)
{
	if (!bLargeIcon)
	{
		// cache small icon for reuse
		if (!m_hIcon)
			m_hIcon = CSysImageList(FALSE).ExtractAppIcon();
		
		return (LRESULT)m_hIcon;
	}
	else
		return Default();
}

BOOL CToDoListWnd::InitStatusbar()
{
	static SBACTPANEINFO SB_PANES[] = 
	{
	  { ID_SB_FILEPATH,		MAKEINTRESOURCE(IDS_SB_FILEPATH_TIP), SBACTF_STRETCHY | SBACTF_RESOURCETIP }, 
	  { ID_SB_FILEVERSION,	MAKEINTRESOURCE(IDS_SB_FILEVERSION_TIP), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	  { ID_SB_TASKCOUNT,	MAKEINTRESOURCE(IDS_SB_TASKCOUNT_TIP), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	  //{ ID_SB_SPACER }, 
	  { ID_SB_SELCOUNT,		MAKEINTRESOURCE(0), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	  { ID_SB_SELTIMEEST,	MAKEINTRESOURCE(IDS_SB_SELTIMEEST_TIP), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	  { ID_SB_SELTIMESPENT,	MAKEINTRESOURCE(IDS_SB_SELTIMESPENT_TIP), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	  { ID_SB_SELCOST,		MAKEINTRESOURCE(IDS_SB_SELCOST_TIP), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	  //{ ID_SB_SPACER }, 
	  { ID_SB_FOCUS,		MAKEINTRESOURCE(IDS_SB_FOCUS_TIP), SBACTF_AUTOFIT | SBACTF_RESOURCETIP }, 
	};

	static int SB_PANECOUNT = sizeof(SB_PANES) / sizeof(SBACTPANEINFO);

	if (!m_statusBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, IDC_FILENAME))
		return FALSE;

	// prevent translation because we handle it manually
	CLocalizer::EnableTranslation(m_statusBar, FALSE);

	if (!m_statusBar.SetPanes(SB_PANES, SB_PANECOUNT))
		return FALSE;

	return TRUE;
}

BOOL CToDoListWnd::InitFilterbar()
{
	if (!m_filterBar.Create(this))
		return FALSE;

	m_filterBar.EnableMultiSelection(Prefs().GetMultiSelFilters());
	m_filterBar.ShowDefaultFilters(Prefs().GetShowDefaultFilters());

	RefreshFilterBarCustomFilters();

	return TRUE;
}

BOOL CToDoListWnd::InitToolbar()
{
	if (m_toolbar.GetSafeHwnd())
		return TRUE;

	UINT nStyle = WS_CHILD | CBRS_ALIGN_TOP | WS_CLIPCHILDREN | CBRS_TOOLTIPS;

	if (m_bShowToolbar)
		nStyle |= WS_VISIBLE;

	if (!m_toolbar.CreateEx(this, TBSTYLE_FLAT | TBSTYLE_WRAPABLE, nStyle))
		return FALSE;

	if (!m_toolbar.LoadToolBar(IDR_APP_TOOLBAR))
		return FALSE;

	// colors
	if (CThemed::IsThemeActive())
	{
		m_toolbar.SetBackgroundColors(m_theme.crToolbarLight, 
												m_theme.crToolbarDark, 
												m_theme.HasGradient(), 
												m_theme.HasGlass());
	}
	
	// toolbar images
	if (m_theme.HasToolbarImageFile(_T("TODOLIST")))
	{
		COLORREF crMask = CLR_NONE;
		CString sImagePath = m_theme.GetToolbarImageFile(_T("TODOLIST"), crMask);

		VERIFY(m_toolbar.SetImage(sImagePath, crMask));
	}
	else 
	{
		const COLORREF MAGENTA = RGB(255, 0, 255);
		m_toolbar.SetImage(IDB_APP_TOOLBAR_STD, MAGENTA);
	}
	
	// resize the toolbar in one row so that our subsequent calculations work
	m_toolbar.GetToolBarCtrl().HideButton(ID_TOOLS_TOGGLECHECKIN, !Prefs().GetEnableSourceControl());
	m_toolbar.MoveWindow(0, 0, 1000, 32); 
	
	// insert combobox for quick Find after Find Tasks button
	int nPos = m_toolbar.CommandToIndex(ID_EDIT_FINDTASKS) + 1;
	
	TBBUTTON tbbQuickFind = { 0, nPos, 0, TBSTYLE_SEP, 0, NULL };
	TBBUTTON tbbSep = { 0, nPos + 1, 0, TBSTYLE_SEP, 0, NULL };
	
	m_toolbar.GetToolBarCtrl().InsertButton(nPos, &tbbQuickFind);
	m_toolbar.GetToolBarCtrl().InsertButton(nPos + 1, &tbbSep);
	
	TBBUTTONINFO tbi;
	tbi.cbSize = sizeof( TBBUTTONINFO );
	tbi.cx = 150;
	tbi.dwMask = TBIF_SIZE;  // By index
	
	m_toolbar.GetToolBarCtrl().SetButtonInfo(nPos + 1, &tbi);
	
	CRect rect;
	m_toolbar.GetToolBarCtrl().GetItemRect(nPos + 1, &rect);
	rect.bottom += 200;
	
	if (!m_cbQuickFind.Create(WS_CHILD | WS_VSCROLL | WS_VISIBLE | CBS_AUTOHSCROLL | 
		CBS_DROPDOWN, rect, &m_toolbar, IDC_QUICKFIND))
		return FALSE;
	
	m_cbQuickFind.SetFont(&m_fontMain);
	m_mgrPrompts.SetComboEditPrompt(m_cbQuickFind, IDS_QUICKFIND);
	
	m_tbHelper.Initialize(&m_toolbar, this);
	
	return TRUE;
}

void CToDoListWnd::OnEditChangeQuickFind()
{
	m_cbQuickFind.GetWindowText(m_sQuickFind);
	
	if (!GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTNEXTINCLCURRENT))
		GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTFIRST);
}

void CToDoListWnd::OnSelChangeQuickFind()
{
	int nSel = m_cbQuickFind.GetCurSel();

	if (nSel != CB_ERR)
	{
		m_sQuickFind = CDialogHelper::GetSelectedItem(m_cbQuickFind);
		
		if (!GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTNEXT))
			GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTFIRST);
	}
}

BOOL CToDoListWnd::PreTranslateMessage(MSG* pMsg)
{
	// the only way we get a WM_CLOSE here is if it was sent from an external app
	// so we shut down as gracefully as possible
	if (pMsg->message == WM_CLOSE && IsWindowEnabled())
	{
		DoExit();
		return TRUE;
	}
	
	if (ProcessDialogControlShortcut(pMsg))
		return TRUE;

	if (IsDroppedComboBox(pMsg->hwnd))
		return FALSE;
	
	// process for app level shortcuts first so we can handle
	// reserved shortcuts
	DWORD dwShortcut = 0;
	UINT nCmdID = m_mgrShortcuts.ProcessMessage(pMsg, &dwShortcut);
	
	// if it's a reserved shortcut let's notify the user to change it
	if (CFilteredToDoCtrl::IsReservedShortcut(dwShortcut))
	{
		int nRet = MessageBox(IDS_RESERVEDSHORTCUT_MSG, IDS_RESERVEDSHORTCUT_TITLE, MB_YESNOCANCEL);
		
		if (nRet == IDYES)
			DoPreferences(PREFPAGE_SHORTCUT);
		
		// and keep eating it until the user changes it
		return TRUE;
	}
	
	// also we handle undo/redo
	if (nCmdID != ID_EDIT_UNDO && nCmdID != ID_EDIT_REDO)
	{
		// now try active task list
		if (GetTDCCount() && GetToDoCtrl().PreTranslateMessage(pMsg))
			return TRUE;
	}
	
	if (nCmdID)
	{
		BOOL bSendMessage = TRUE; // default
		
		// focus checks
		switch (nCmdID)
		{
		case ID_EDIT_CUT:
		case ID_EDIT_COPY:
			// tree must have the focus
			if (!GetToDoCtrl().TasksHaveFocus())
			{
				bSendMessage = FALSE;
				GetToDoCtrl().ClearCopiedItem();
			}
			break;
			
			// tree must have the focus
		case ID_EDIT_SELECT_ALL: 
		case ID_EDIT_PASTE: 
		case ID_DELETEALLTASKS:
		case ID_DELETETASK:
			bSendMessage = GetToDoCtrl().TasksHaveFocus();
			break;
		}
		
		// send off
		if (bSendMessage)
		{
			SendMessage(WM_COMMAND, nCmdID);
			return TRUE;
		}
	}
	
	// we need to check for <escape>, <tab> and <return>
	switch (pMsg->message)
	{
	case WM_KEYDOWN:
		{
			switch (pMsg->wParam)
			{
			case VK_ESCAPE:
				if (Prefs().GetEscapeMinimizes() && GetCapture() == NULL)
				{
					// if the window with the target is either a combobox or
					// the child edit of a combobox and the combo is
					// dropped down then let it thru else if the target is
					// a child of ours then treat as a cancel
					BOOL bHandle = TRUE;
					
					if (CWinClasses::IsClass(pMsg->hwnd, WC_COMBOBOX))
						bHandle = !ComboBox_GetDroppedState(pMsg->hwnd);
					
					else if (CWinClasses::IsClass(::GetParent(pMsg->hwnd), WC_COMBOBOX))
						bHandle = !ComboBox_GetDroppedState(::GetParent(pMsg->hwnd));
					
					else if (GetTDCCount() && GetToDoCtrl().IsTaskLabelEditing())
						bHandle = FALSE;
					
					if (bHandle && ::IsChild(*this, pMsg->hwnd))
					{
						OnCancel();
						return TRUE;
					}
				}
				break;
				
			case VK_TAB: // tabbing away from Quick Find -> tasks
				if (::IsChild(m_cbQuickFind, pMsg->hwnd))
				{
					GetToDoCtrl().SetFocusToTasks();
					return TRUE;
				}
				break;
				
			case VK_RETURN: // hitting return in filter bar and quick find
				if (Prefs().GetFocusTreeOnEnter())
				{
					CWnd* pFocus = GetFocus();
					
					if (pFocus && (m_filterBar.IsChild(pFocus) || m_cbQuickFind.IsChild(pFocus)))
					{
						if (!ControlWantsEnter(*pFocus))
							GetToDoCtrl().SetFocusToTasks();

						return FALSE; // continue routing
					}
				}
				break;
			}
		}
		break;
	}
	
	return CFrameWnd::PreTranslateMessage(pMsg);
}

void CToDoListWnd::OnCancel()
{
	ASSERT (Prefs().GetEscapeMinimizes());

	// if the close button has been configured to Minimize to tray
	// then do that here else normal minimize 
	int nOption = Prefs().GetSysTrayOption();
	
	if (nOption == STO_ONMINCLOSE || nOption == STO_ONCLOSE)
		MinimizeToTray();
	else
		SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);
}

void CToDoListWnd::OnDeleteTask() 
{
	if (GetToDoCtrl().GetSelectedItem())
		GetToDoCtrl().DeleteSelectedTask();
}

void CToDoListWnd::OnDeleteAllTasks() 
{
	if (GetToDoCtrl().DeleteAllTasks())
	{
//		m_mgrToDoCtrls.ClearFilePath(GetSelToDoCtrl()); // this will ensure that the user must explicitly overwrite the original file
		UpdateStatusbar();
	}
}

void CToDoListWnd::OnSave() 
{
	if (SaveTaskList(GetSelToDoCtrl()) == TDCO_SUCCESS)
		UpdateCaption();
}

BOOL CToDoListWnd::DoBackup(int nIndex)
{
	if (!Prefs().GetBackupOnSave())
		return TRUE;

	CString sTDLPath = m_mgrToDoCtrls.GetFilePath(nIndex);

	if (sTDLPath.IsEmpty())
		return TRUE; // not yet saved

	// get backup path
	CString sBackupFolder = Prefs().GetBackupLocation(sTDLPath);
	sBackupFolder.TrimRight();

	// cull old backups
	int nKeepBackups = Prefs().GetKeepBackupCount();

	if (nKeepBackups)
	{
		CStringArray aFiles;
		CString sPath = CFileBackup::BuildBackupPath(sTDLPath, sBackupFolder, FBS_APPVERSION, _T(""));

		CString sDrive, sFolder, sFName, sExt, sPattern;

		FileMisc::SplitPath(sPath, &sDrive, &sFolder, &sFName, &sExt);
		FileMisc::MakePath(sPath, sDrive, sFolder);
		
		int nFiles = FileMisc::FindFiles(sPath, aFiles, FALSE, sFName + _T("*") + sExt);

		if (nFiles >= nKeepBackups)
		{
			Misc::SortArray(aFiles); // sorts oldest backups first

			// cull as required
			while (aFiles.GetSize() >= nKeepBackups)
			{
				DeleteFile(aFiles[0]);
				aFiles.RemoveAt(0);
			}
		}
	}

	CFileBackup backup;
	return backup.MakeBackup(sTDLPath, sBackupFolder, FBS_APPVERSION | FBS_TIMESTAMP, _T(""));
}

TDC_FILE CToDoListWnd::SaveTaskList(int nIndex, LPCTSTR szFilePath, BOOL bAuto)
{
	CAutoFlag af(m_bSaving, TRUE);
	CString sFilePath = szFilePath ? szFilePath : m_mgrToDoCtrls.GetFilePath(nIndex);

	CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);
	tdc.Flush();

	// build dialog title, incorporating tasklist name
	CString sName = m_mgrToDoCtrls.GetFriendlyProjectName(nIndex);
	CEnString sTitle(IDS_SAVETASKLIST_TITLE, sName);
	
	// conditions for saving
	// 1. Save As... ie szFilePath != NULL and not empty
	// 2. tasklist has been modified
	if ((szFilePath && !sFilePath.IsEmpty()) || tdc.IsModified())
	{
		CPreferences prefs;
		
		// do this in a loop in case the save fails for _any_ reason
		while (TRUE)
		{
			if (sFilePath.IsEmpty()) // means first time save
			{
				// activate tasklist
				if (!SelectToDoCtrl(nIndex, (nIndex != GetSelToDoCtrl())))
					return TDCO_CANCELLED;
				
				// use tab text as hint to user
				sFilePath = m_mgrToDoCtrls.GetFilePath(nIndex, FALSE);

				CFileSaveDialog dialog(sTitle, 
										GetDefaultFileExt(), 
										sFilePath, 
										EOFN_DEFAULTSAVE, 
										GetFileFilter(FALSE), 
										this);
				
				// get the user's last choice of saving new tasklists
				// as the best hint for this time
				BOOL bUnicode = prefs.GetProfileInt(PREF_KEY, _T("UnicodeNewTasklists"), TRUE);
				dialog.m_ofn.nFilterIndex = (bUnicode ? 2 : 1);
				
				if (dialog.DoModal(&prefs) != IDOK)
					return TDCO_CANCELLED; // user elected not to proceed
				
				// else make sure the file is not readonly
				sFilePath = dialog.GetPathName();

				// check for format change
				bUnicode = (dialog.m_ofn.nFilterIndex == 2);
				tdc.SetUnicode(bUnicode);
		
				// save this choice as the best hint for the next new tasklist
				prefs.WriteProfileInt(PREF_KEY, _T("UnicodeNewTasklists"), bUnicode);
				
				// else make sure the file is not readonly
				if (CDriveInfo::IsReadonlyPath(sFilePath) > 0)
				{
					CEnString sMessage(IDS_SAVEREADONLY, sFilePath);
					
					if (MessageBox(sMessage, sTitle, MB_OKCANCEL) == IDCANCEL)
						return TDCO_CANCELLED; // user elected not to proceed
					else
					{
						sFilePath.Empty(); // try again
						continue;
					}
				}
			}

			// back file up
			DoBackup(nIndex);
			
			// update source control status
			const CPreferencesDlg& userPrefs = Prefs();
			BOOL bSrcControl = m_mgrToDoCtrls.PathSupportsSourceControl(sFilePath);
			
			tdc.SetStyle(TDCS_ENABLESOURCECONTROL, bSrcControl);
			tdc.SetStyle(TDCS_CHECKOUTONLOAD, bSrcControl ? userPrefs.GetAutoCheckOut() : FALSE);
			
			TDC_FILE nSave = TDCO_SUCCESS;
			CTaskFile tasks;

			// scoped to end status bar progress
			// before calling UpdateStatusbar
			{
				DOPROGRESS(IDS_SAVINGPROGRESS)
				nSave = tdc.Save(tasks, sFilePath);
				
				// send to storage as appropriate
				TSM_TASKLISTINFO storageInfo;
				
				if (nSave == TDCO_SUCCESS && m_mgrToDoCtrls.GetStorageDetails(nIndex, storageInfo))
				{
					m_mgrStorage.StoreTasklist(&storageInfo, &tasks, -1, &prefs);
				}
			}

			if (nSave == TDCO_SUCCESS)
			{
				m_mgrToDoCtrls.SetModifiedStatus(nIndex, FALSE);
				m_mgrToDoCtrls.RefreshLastModified(nIndex);
				m_mgrToDoCtrls.RefreshReadOnlyStatus(nIndex);
				m_mgrToDoCtrls.RefreshPathType(nIndex);
				
				if (userPrefs.GetAddFilesToMRU() && !m_mgrToDoCtrls.UsesStorage(nIndex))
					m_mruList.Add(sFilePath);
				
				UpdateCaption();
				UpdateStatusbar();
				
				// auto-export after saving
				CString sExt;
				
				if (userPrefs.GetAutoExport() && GetAutoExportExtension(sExt))
				{
					DOPROGRESS(IDS_EXPORTPROGRESS)

					// construct output path
					CString sExportPath = userPrefs.GetAutoExportFolderPath();
					CString sDrive, sFolder, sFName;
					
					FileMisc::SplitPath(sFilePath, &sDrive, &sFolder, &sFName);
					
					if (!sExportPath.IsEmpty() && FileMisc::CreateFolder(sExportPath))
						FileMisc::MakePath(sFilePath, NULL, sExportPath, sFName, sExt);
					else
						FileMisc::MakePath(sFilePath, sDrive, sFolder, sFName, sExt);
					
					// The current contents of 'tasks' is 'All Tasks' and 
					// 'All Columns' but NOT 'Html Comments'.
					// So if user either wants 'Filtered Tasks' or 'Html Comments' or
					// only 'Visible Columns' we need to grab the tasks again.
					BOOL bFiltered = (userPrefs.GetExportFilteredOnly() && (tdc.HasCustomFilter() || tdc.HasFilter()));
					
					if (bFiltered || userPrefs.GetExportToHTML() || !userPrefs.GetExportAllAttributes())
					{
						TSD_TASKS nWhatTasks = bFiltered ? TSDT_FILTERED : TSDT_ALL;
						TDCGETTASKS filter;
						
						if (!userPrefs.GetExportAllAttributes())
						{
							CTDCColumnIDArray aCols;
							tdc.GetVisibleColumns(aCols);
							
							MapColumnsToAttributes(aCols, filter.aAttribs);
							
							// add comments always
							filter.aAttribs.Add(TDCA_COMMENTS);
						}
						
						BOOL bHtmlComments = userPrefs.GetExportToHTML();
						BOOL bTransform = FileMisc::FileExists(userPrefs.GetSaveExportStylesheet());
						
						// set the html image folder to be the output path with
						// an different extension
						CString sImgFolder;
						
						if (bHtmlComments)
						{
							sImgFolder = sFilePath;
							FileMisc::ReplaceExtension(sImgFolder, _T("html_images"));
						}
						
						tasks.Reset();
						GetTasks(tdc, bHtmlComments, bTransform, nWhatTasks, filter, 0, tasks, sImgFolder); 
					}
					
					// save intermediate tasklist to file as required
					LogIntermediateTaskList(tasks, tdc.GetFilePath());

					// want HTML 
					if (userPrefs.GetExportToHTML())
					{
						Export2Html(tasks, sFilePath, userPrefs.GetSaveExportStylesheet());
					}
					else if (userPrefs.GetOtherExporter() != -1)
					{
						int nExporter = userPrefs.GetOtherExporter();
						m_mgrImportExport.ExportTaskList(&tasks, sFilePath, nExporter, TRUE);
					}
				}
				
				// we're done
				break;
			}
			else if (!bAuto)
			{
				// error handling if this is not an auto-save
				if (nSave == TDCO_NOTALLOWED)
				{
					CEnString sMessage(IDS_SAVEACCESSDENIED, sFilePath);
					
					if (IDYES == MessageBox(sMessage, sTitle, MB_YESNOCANCEL | MB_ICONEXCLAMATION))
					{
						sFilePath.Empty(); // try again
						continue;
					}
					else // clear modified status
					{
						tdc.SetModified(FALSE);
						m_mgrToDoCtrls.SetModifiedStatus(nIndex, FALSE);
						break;
					}
				}
				else
				{
					CString sMessage;
					
					switch (nSave)
					{
					case TDCO_CANCELLED:
						break;
						
					case TDCO_BADMSXML:
						sMessage.Format(IDS_SAVEBADXML, sFilePath);
						break;
						
					case TDCO_INUSE:
						sMessage.Format(IDS_SAVESHARINGVIOLATION, sFilePath);
						break;
						
					case TDCO_NOTCHECKEDOUT:
						sMessage.Format(IDS_SAVENOTCHECKEDOUT, sFilePath);
						break;
						
					default:
						sMessage.Format(IDS_UNKNOWNSAVEERROR2, sFilePath, (nSave - (int)TDCO_OTHER));
						break;
					}
					
					if (!sMessage.IsEmpty())
						MessageBox(sMessage, sTitle, MB_OK);
				}
				break; // we're done
			}
			else // bAuto == TRUE
			{
				break; // we're done
			}
		}
	}

	return TDCO_SUCCESS;
}

BOOL CToDoListWnd::GetAutoExportExtension(CString& sExt) const
{
	sExt.Empty();

	if (Prefs().GetExportToHTML())
		sExt = ".html";
	else
	{
		int nExporter = Prefs().GetOtherExporter();

		if (nExporter != -1)
			sExt = m_mgrImportExport.GetExporterFileExtension(nExporter);
	}

	return !sExt.IsEmpty();
}

LPCTSTR CToDoListWnd::GetFileFilter(BOOL bOpen)
{
	static CEnString TDLFILEOPENFILTER(IDS_TDLFILEOPENFILTER);
	static CEnString XMLFILEOPENFILTER(IDS_XMLFILEOPENFILTER);
	static CEnString TDLFILESAVEFILTER(IDS_TDLFILESAVEFILTER);
	static CEnString XMLFILESAVEFILTER(IDS_XMLFILESAVEFILTER);
	
	if (Prefs().GetEnableTDLExtension())
		return bOpen ? TDLFILEOPENFILTER : TDLFILESAVEFILTER;
	
	// else
	return bOpen ? XMLFILEOPENFILTER : XMLFILESAVEFILTER;
}

LPCTSTR CToDoListWnd::GetDefaultFileExt()
{
	static LPCTSTR TDLEXT = _T("tdl");
	static LPCTSTR XMLEXT = _T("xml");
	
	if (Prefs().GetEnableTDLExtension())
		return TDLEXT;
	else
		return XMLEXT;
}

void CToDoListWnd::UpdateStatusbar()
{
	if (!m_sbProgress.IsActive() && GetTDCCount())
	{
		// get display path
		int nTasklist = GetSelToDoCtrl();
		const CFilteredToDoCtrl& tdc = GetToDoCtrl(nTasklist);

		CEnString sText = m_mgrToDoCtrls.GetDisplayPath(nTasklist);

		if (sText.IsEmpty())
			sText.LoadString(ID_SB_FILEPATH);
		
		else if (tdc.IsUnicode())
			sText += _T(" (Unicode)");
		
		m_statusBar.SetPaneText(m_statusBar.CommandToIndex(ID_SB_FILEPATH), sText);

		// get file version
		sText.Format(ID_SB_FILEVERSION, tdc.GetFileVersion());
		m_statusBar.SetPaneText(m_statusBar.CommandToIndex(ID_SB_FILEVERSION), sText);
	}
}

void CToDoListWnd::OnLoad() 
{
	CPreferences prefs;
	CFileOpenDialog dialog(IDS_OPENTASKLIST_TITLE, 
							GetDefaultFileExt(), 
							NULL, 
							EOFN_DEFAULTOPEN | OFN_ALLOWMULTISELECT,
							GetFileFilter(TRUE), 
							this);
	
	const UINT BUFSIZE = 1024 * 5;
	static TCHAR FILEBUF[BUFSIZE] = { 0 };
	
	dialog.m_ofn.lpstrFile = FILEBUF;
	dialog.m_ofn.nMaxFile = BUFSIZE;
	
	if (dialog.DoModal(&prefs) == IDOK)
	{
		CWaitCursor cursor;
		POSITION pos = dialog.GetStartPosition();
		
		while (pos)
		{
			CString sTaskList = dialog.GetNextPathName(pos);
			TDC_FILE nOpen = OpenTaskList(sTaskList);
			
			if (nOpen == TDCO_SUCCESS)
			{
				Resize();
				UpdateWindow();
			}
			else
				HandleLoadTasklistError(nOpen, sTaskList);
		}
		
		RefreshTabOrder();
	}
}

void CToDoListWnd::HandleLoadTasklistError(TDC_FILE nErr, LPCTSTR szTaskList)
{
	CEnString sMessage, sTitle(IDS_OPENTASKLIST_TITLE);
	
	switch (nErr)
	{
	case TDCO_SUCCESS:
		break; // not an error!
		
	case TDCO_CANCELLED:
		break; 
		
	case TDCO_NOTEXIST:
		sMessage.Format(IDS_TASKLISTNOTFOUND, szTaskList);
		break;
		
	case TDCO_NOTTASKLIST:
		sMessage.Format(IDS_INVALIDTASKLIST, szTaskList);
		break;
		
	case TDCO_NOTALLOWED:
		sMessage.Format(IDS_OPENACCESSDENIED, szTaskList);
		break;

	case TDCO_INUSE:
		sMessage.Format(IDS_OPENSHARINGVIOLATION, szTaskList);
		break;
		
	case TDCO_BADMSXML:
		sMessage.Format(IDS_BADXML, szTaskList);
		break;
		
	case TDCO_NOENCRYPTIONDLL:
		sMessage.Format(IDS_NOENCRYPTIONDLL, szTaskList);
		break;
		
	case TDCO_UNKNOWNENCRYPTION:
		sMessage.Format(IDS_UNKNOWNENCRYPTION, szTaskList);
		break;
		
	default: // all the other errors
		sMessage.Format(IDS_UNKNOWNOPENERROR, szTaskList, nErr - (int)TDCO_OTHER);
		break;
	}
	
	if (!sMessage.IsEmpty())
		MessageBox(sMessage, sTitle, MB_OK);
}

void CToDoListWnd::SaveSettings()
{
	CPreferences prefs;

	// pos
	WINDOWPLACEMENT wp;
	GetWindowPlacement(&wp);
	
	prefs.WriteProfileInt(_T("Pos"), _T("Left"), wp.rcNormalPosition.left);
	prefs.WriteProfileInt(_T("Pos"), _T("Top"), wp.rcNormalPosition.top);
	prefs.WriteProfileInt(_T("Pos"), _T("Right"), wp.rcNormalPosition.right);
	prefs.WriteProfileInt(_T("Pos"), _T("Bottom"), wp.rcNormalPosition.bottom);

// 	FileMisc::LogText(_T("SavePosition: TopLeft=(%d,%d) BotRight=(%d,%d) MinPos=(%d,%d) MaxPos=(%d,%d)"), 
// 						wp.rcNormalPosition.left, wp.rcNormalPosition.top,
// 						wp.rcNormalPosition.right, wp.rcNormalPosition.bottom,
// 						wp.ptMaxPosition.x, wp.ptMaxPosition.y,
// 						wp.ptMinPosition.x, wp.ptMinPosition.y);

	prefs.WriteProfileInt(_T("Pos"), _T("Hidden"), !m_bVisible);
	prefs.WriteProfileInt(_T("Pos"), _T("Maximized"), IsZoomed());
	
	// version
	prefs.WriteProfileInt(_T("Version"), _T("Version"), GetVersion());
	
	// last open files
	int nCount = GetTDCCount();
	int nSel = GetSelToDoCtrl(); // and last active file
	
	if (nCount) // but don't overwrite files saved in OnQueryEndSession() or OnClose()
	{
		for (int nTDC = 0, nItem = 0; nTDC < nCount; nTDC++)
		{
			CString sFilePath = m_mgrToDoCtrls.GetFilePath(nTDC);

			TSM_TASKLISTINFO storageInfo;

			if (m_mgrToDoCtrls.GetStorageDetails(nTDC, storageInfo))
			{
				sFilePath = storageInfo.EncodeInfo(Prefs().GetSaveStoragePasswords());

#ifdef _DEBUG
				ASSERT(storageInfo.DecodeInfo(sFilePath));
				ASSERT(storageInfo.EncodeInfo(TRUE) == sFilePath);
#endif
			}
			else // make file paths relative
			{
				FileMisc::MakeRelativePath(sFilePath, FileMisc::GetAppFolder(), FALSE);
			}

			CString sKey = Misc::MakeKey(_T("LastFile%d"), nItem);
			prefs.WriteProfileString(_T("Settings"), sKey, sFilePath);
			
			if (nSel == nTDC)
				prefs.WriteProfileString(_T("Settings"), _T("LastActiveFile"), sFilePath);

			nItem++;
		}
		
		prefs.WriteProfileInt(_T("Settings"), _T("NumLastFiles"), nCount);
	}

	// other settings
	prefs.WriteProfileInt(_T("Settings"), _T("ViewState"), m_nMaxState);
	prefs.WriteProfileInt(_T("Settings"), _T("ShowFilterBar"), m_bShowFilterBar);
	prefs.WriteProfileInt(_T("Settings"), _T("ToolbarOption"), m_bShowToolbar ? TB_TOOLBARANDMENU : TB_TOOLBARHIDDEN);
	prefs.WriteProfileInt(_T("Settings"), _T("ShowProjectName"), m_bShowProjectName);
	prefs.WriteProfileInt(_T("Settings"), _T("ShowStatusBar"), m_bShowStatusBar);
	prefs.WriteProfileInt(_T("Settings"), _T("ShowTasklistBar"), m_bShowTasklistBar);
	prefs.WriteProfileInt(_T("Settings"), _T("ShowTreeListBar"), m_bShowTreeListBar);

	if (m_findDlg.GetSafeHwnd())
		prefs.WriteProfileInt(_T("Settings"), _T("FindTasksVisible"), m_bFindShowing && m_findDlg.IsWindowVisible());
	
	if (Prefs().GetAddFilesToMRU())
		m_mruList.WriteList(prefs, TRUE);

	// quick find items
	nCount = m_cbQuickFind.GetCount();
	prefs.WriteProfileInt(_T("QuickFind"), _T("Count"), nCount);

	for (int nItem = 0; nItem < nCount; nItem++)
	{
		CString sItem, sKey = Misc::MakeKey(_T("Item%d"), nItem);
		m_cbQuickFind.GetLBText(nItem, sItem);

		prefs.WriteProfileString(_T("QuickFind"), sKey, sItem);
	}

	// save to permanent storage
	prefs.Save();
}

LRESULT CToDoListWnd::OnWebUpdateWizard(WPARAM /*wp*/, LPARAM /*lp*/)
{
	ASSERT (Prefs().GetAutoCheckForUpdates());

	CheckForUpdates(FALSE);
	return 0L;
}

LRESULT CToDoListWnd::OnAddToolbarTools(WPARAM /*wp*/, LPARAM /*lp*/)
{
	Misc::ProcessMsgLoop();
	AppendTools2Toolbar();
	return 0L;
}

TDC_PREPAREPATH CToDoListWnd::PrepareFilePath(CString& sFilePath, TSM_TASKLISTINFO* pInfo)
{
	TDC_PREPAREPATH nType = TDCPP_NONE;
	TSM_TASKLISTINFO temp;

	if (pInfo == NULL)
		pInfo = &temp;

	// first test for storage
	if (pInfo->DecodeInfo(sFilePath, Prefs().GetSaveStoragePasswords()))
	{
		sFilePath = pInfo->szLocalFileName;

		// check for overflow and non-existence
		if (FileMisc::FileExists(sFilePath))
			nType = TDCPP_STORAGE;
		else
			sFilePath.Empty();
	}
	// else it's a file path.
	// if it starts with a colon then we need to find the removable drive it's stored on
	else if (!sFilePath.IsEmpty())
	{
		if (sFilePath[0] == ':')
		{
			for (int nDrive = 4; nDrive <= 26; nDrive++) // from D: upwards
			{
				if (CDriveInfo::GetType(nDrive) == DRIVE_REMOVABLE)
				{
					CString sTryPath = CDriveInfo::GetLetter(nDrive) + sFilePath;

					if (FileMisc::FileExists(sTryPath))
					{
						sFilePath = sTryPath;
						break; // finished
					}
				}
			}
		}
		else
			FileMisc::MakeFullPath(sFilePath, FileMisc::GetAppFolder()); // handle relative paths

		// check for existence
		if (FileMisc::FileExists(sFilePath))
			nType = TDCPP_FILE;
		else
			sFilePath.Empty();
	}
	
	return nType;
}

LRESULT CToDoListWnd::OnPostOnCreate(WPARAM /*wp*/, LPARAM /*lp*/)
{
	// late initialization
	CMouseWheelMgr::Initialize();
	CEditShortcutMgr::Initialize();
	CFocusWatcher::Initialize(this);

	InitShortcutManager();
	InitMenuIconManager();

	// reminders
	m_reminders.Initialize(this);

	// with or without Stickies Support
	const CPreferencesDlg& userPrefs = Prefs();
	CString sStickiesPath;
	
	if (userPrefs.GetUseStickies(sStickiesPath))
		VERIFY(m_reminders.UseStickies(TRUE, sStickiesPath));
	
	// light boxing
 	if (Prefs().GetEnableLightboxMgr())
		CLightBoxMgr::Initialize(this, m_theme.crAppBackDark);
	
	// add outstanding translated items to dictionary
	if (CLocalizer::GetTranslationOption() == ITTTO_ADD2DICTIONARY)
	{
		CUIntArray aDictVersion, aAppVersion;
		VERIFY (FileMisc::GetAppVersion(aAppVersion));

		BOOL bUpdateDict = !CLocalizer::GetDictionaryVersion(aDictVersion) ||
							aDictVersion.GetSize() < 2;

		if (!bUpdateDict)
		{
			// check if the major or minor version has increased
			bUpdateDict = (FileMisc::CompareVersions(aAppVersion, aDictVersion, 2) > 0);

			// check for pre-release build then update
			if (!bUpdateDict && aAppVersion[2] >= 297)
			{
				// compare entire version string
				bUpdateDict = (FileMisc::CompareVersions(aAppVersion, aDictVersion) > 0);
			}
		}

		if (bUpdateDict)
			TranslateUIElements();
	}

	RestoreVisibility();
	
	// load last open tasklists
	CAutoFlag af(m_bReloading, TRUE);
	CPreferences prefs;

	// initialize Progress first time
	m_sbProgress.BeginProgress(m_statusBar, CEnString(IDS_STARTUPPROGRESS));

	// open cmdline tasklist
	int nTDCCount = prefs.GetProfileInt(_T("Settings"), _T("NumLastFiles"), 0);

	if (!m_startupOptions.HasFilePath() || nTDCCount)
	{
		// if we have a file on the commandline or any previous tasklists
		// set the prompt of the initial tasklist to something appropriate
		//	TODO
	}
	
	// theme
	SetUITheme(userPrefs.GetUITheme());

	// cache empty flag for later
	BOOL bStartupEmpty = m_startupOptions.HasFlag(TLD_STARTEMPTY);

	// what to (re)load?
	BOOL bReloadTasklists = (!bStartupEmpty && userPrefs.GetReloadTasklists());
	
	// filepath overrides
	if (m_startupOptions.HasFilePath())
	{
		ProcessStartupOptions(m_startupOptions);

		// don't reload previous if a tasklist was actually loaded
		if (!m_mgrToDoCtrls.IsPristine(0))
			bReloadTasklists = FALSE;
	}
	
	m_startupOptions.Reset(); // always
	
	// load last files
	if (bReloadTasklists)
	{
		// get the last active tasklist
		CString sLastActiveFile = prefs.GetProfileString(_T("Settings"), _T("LastActiveFile")), sOrgLastActiveFile;
		BOOL bCanDelayLoad = userPrefs.GetEnableDelayedLoading();

		for (int nTDC = 0; nTDC < nTDCCount; nTDC++)
		{
			CString sKey = Misc::MakeKey(_T("LastFile%d"), nTDC);
			CString sLastFile = prefs.GetProfileString(_T("Settings"), sKey);

			if (!sLastFile.IsEmpty())
			{
				// delay-open all but the non-active tasklist
				// unless the tasklist has reminders
				BOOL bActiveTDC = (sLastFile == sLastActiveFile);

				if (!bActiveTDC && bCanDelayLoad && !m_reminders.ToDoCtrlHasReminders(sLastFile))
				{
					DelayOpenTaskList(sLastFile);
				}
				else
				{
					TDC_FILE nResult = OpenTaskList(sLastFile, FALSE);

					// if the last active tasklist was cancelled then
					// delay load it and mark the last active todoctrl as not found
					if (bActiveTDC && nResult != TDCO_SUCCESS)
					{
						sOrgLastActiveFile = sLastActiveFile;
						sLastActiveFile.Empty();

						if (nResult == TDCO_CANCELLED && bCanDelayLoad)
							DelayOpenTaskList(sLastFile);
					}
				}
			}
		}
		
		// process all pending messages
		Misc::ProcessMsgLoop();

		// if the last active tasklist could not be loaded then we need to find another
		if (GetTDCCount())
		{
			// make Last Active Files actual filepaths
			PrepareFilePath(sLastActiveFile);
			PrepareFilePath(sOrgLastActiveFile);

			if (sLastActiveFile.IsEmpty())
			{
				for (int nTDC = 0; nTDC < GetTDCCount() && sLastActiveFile.IsEmpty(); nTDC++)
				{
					CFilteredToDoCtrl& tdc = GetToDoCtrl(nTDC);

					// ignore original active tasklist
					if (tdc.GetFilePath() != sOrgLastActiveFile)
					{
						if (VerifyTaskListOpen(nTDC, FALSE))
							sLastActiveFile = tdc.GetFilePath();
					}
				}
			}

			// if nothing suitable found then create an empty tasklist
			if (sLastActiveFile.IsEmpty())
			{
				if (GetTDCCount() == 0)
					CreateNewTaskList(FALSE);
			}
			else if (!SelectToDoCtrl(sLastActiveFile, FALSE))
				SelectToDoCtrl(0, FALSE); // the first one

			Resize();
		}
	}

	// if there's only one tasklist open and it's pristine then 
	// it's the original one so add a sample task unless 
	// 'empty' flag is set
	if (GetTDCCount() == 1 && m_mgrToDoCtrls.IsPristine(0))
	{
		if (!bStartupEmpty)
		{
			CFilteredToDoCtrl& tdc = GetToDoCtrl();
			ASSERT (tdc.GetTaskCount() == 0);

			tdc.CreateNewTask(CEnString(IDS_SAMPLETASK), TDC_INSERTATTOP, FALSE);
			tdc.SetModified(FALSE);
			
			m_mgrToDoCtrls.SetModifiedStatus(0, FALSE);

			UpdateCaption();
		}
	}
	else // due task notifications
	{
		int nDueBy = userPrefs.GetNotifyDueByOnLoad();
		
		if (nDueBy != PFP_DONTNOTIFY)
		{
			UpdateWindow();
			m_tabCtrl.UpdateWindow();
			
			int nCtrls = GetTDCCount();
			
			for (int nCtrl = 0; nCtrl < nCtrls; nCtrl++)
			{
				if (m_mgrToDoCtrls.IsLoaded(nCtrl))
					DoDueTaskNotification(nCtrl, nDueBy);
			}
		}
	}

	// refresh toolbar 'tools' buttons unless minimized because
	// we must handle it when we're first shown
	if (m_bShowToolbar && AfxGetApp()->m_nCmdShow != SW_SHOWMINIMIZED)
		AppendTools2Toolbar();

	// web update
	if (Prefs().GetAutoCheckForUpdates())
		PostMessage(WM_WEBUPDATEWIZARD);

	// current focus
	PostMessage(WM_FW_FOCUSCHANGE, (WPARAM)::GetFocus(), 0L);

	RefreshTabOrder();

	// end progress before refreshing statusbar
	m_sbProgress.EndProgress();
	UpdateStatusbar();

	// find tasks dialog
	InitFindDialog();

	if (prefs.GetProfileInt(_T("Settings"), _T("FindTasksVisible"), 0))
	{
		OnFindTasks();
		
		if (userPrefs.GetRefreshFindOnLoad())
			m_findDlg.RefreshSearch();
	}

	// log the app and its dlls for debugging
	FileMisc::LogAppModuleState(FBM_SORTBY_HMODULE);

	return 0L;
}

void CToDoListWnd::CheckForUpdates(BOOL bManual)
{
	CPreferences prefs;

	// only check once a day
	int nLastUpdate = prefs.GetProfileInt(_T("Updates"), _T("LastUpdate"), 0);
	int nToday = (int)CDateHelper::GetDate(DHD_TODAY);

	if (!bManual && nLastUpdate >= nToday)
		return;

	prefs.WriteProfileInt(_T("Updates"), _T("LastUpdate"), nToday);

	// get the app wuw path
	CString sFolder, sDrive;
	CString sWuwPath = FileMisc::GetAppFolder() + _T("\\WebUpdateSvc.exe");

	// check for existence if manual
	if (bManual && !FileMisc::FileExists(sWuwPath))
	{
		LPCTSTR DOWNLOAD_WUW_PATH = _T("http://www.abstractspoon.com/todolist_wuw.zip");

		if (MessageBox(IDS_NOWUW, 0, MB_YESNO) == IDYES)
			::ShellExecute(NULL, _T("open"), DOWNLOAD_WUW_PATH, NULL, NULL, SW_HIDE);
		else
			return;
	}

	// because the download may include the WuW we copy it to a temp name
	// so that the original can be overwritten.
	CString sWuwPathTemp = FileMisc::GetAppFolder() + _T("\\WebUpdateSvc2.exe");

	if (::CopyFile(sWuwPath, sWuwPathTemp, FALSE))
		sWuwPath = sWuwPathTemp;

	if (bManual)
	{
		if (m_bUseStagingScript)
			::ShellExecute(NULL, _T("open"), sWuwPath, UPDATE_SCRIPT_PATH_STAGING, NULL, SW_HIDE);
		else
			::ShellExecute(NULL, _T("open"), sWuwPath, UPDATE_SCRIPT_PATH_MANUAL, NULL, SW_HIDE);
	}
	else
		::ShellExecute(NULL, _T("open"), sWuwPath, UPDATE_SCRIPT_PATH, NULL, SW_HIDE);
} 
	
void CToDoListWnd::LoadSettings()
{
	// settings
	CPreferences prefs;

	BOOL bMaxTasklists = prefs.GetProfileInt(_T("Settings"), _T("SimpleMode"), FALSE); // backward compatibility
	m_nMaxState = (TDC_MAXSTATE)prefs.GetProfileInt(_T("Settings"), _T("ViewState"), bMaxTasklists ? TDCMS_MAXTASKLIST : TDCMS_NORMAL);

	m_bShowFilterBar = prefs.GetProfileInt(_T("Settings"), _T("ShowFilterBar"), m_bShowFilterBar);
	m_bShowProjectName = prefs.GetProfileInt(_T("Settings"), _T("ShowProjectName"), m_bShowProjectName);
	
	m_bShowStatusBar = prefs.GetProfileInt(_T("Settings"), _T("ShowStatusBar"), m_bShowStatusBar);
	m_statusBar.ShowWindow(m_bShowStatusBar ? SW_SHOW : SW_HIDE);
	
	// toolbar
	m_bShowToolbar = (prefs.GetProfileInt(_T("Settings"), _T("ToolbarOption"), TB_TOOLBARANDMENU) != TB_TOOLBARHIDDEN);
	m_toolbar.ShowWindow(m_bShowToolbar ? SW_SHOW : SW_HIDE);
	m_toolbar.EnableWindow(m_bShowToolbar);

	// tabbars
	m_bShowTasklistBar = prefs.GetProfileInt(_T("Settings"), _T("ShowTasklistBar"), TRUE);
	m_bShowTreeListBar = prefs.GetProfileInt(_T("Settings"), _T("ShowTreeListBar"), TRUE);

	// pos
	RestorePosition();

	// user preferences
	const CPreferencesDlg& userPrefs = Prefs();
	
	// MRU
	if (userPrefs.GetAddFilesToMRU())
		m_mruList.ReadList(prefs);
	
	// note: we do not restore visibility until OnPostOnCreate

	// default attributes
	UpdateDefaultTaskAttributes(userPrefs);
	
	// hotkey
	UpdateGlobalHotkey();
	
	// time periods
	CTimeHelper::SetHoursInOneDay(userPrefs.GetHoursInOneDay());
	CTimeHelper::SetDaysInOneWeek(userPrefs.GetDaysInOneWeek());

	// support for .tdl
	CFileRegister filereg(_T("tdl"), _T("tdl_Tasklist"));
	
	if (userPrefs.GetEnableTDLExtension())
		filereg.RegisterFileType(_T("Tasklist"), 0);
	else
		filereg.UnRegisterFileType();

	// support for tdl protocol
	EnableTDLProtocol(userPrefs.GetEnableTDLProtocol());

	// previous quick find items
	int nCount = prefs.GetProfileInt(_T("QuickFind"), _T("Count"), 0);

	for (int nItem = 0; nItem < nCount; nItem++)
	{
		CString sKey = Misc::MakeKey(_T("Item%d"), nItem);
		m_cbQuickFind.AddUniqueItem(prefs.GetProfileString(_T("QuickFind"), sKey));
	}

	// Recently modified period
	CFilteredToDoCtrl::SetRecentlyModifiedPeriod(userPrefs.GetRecentlyModifiedPeriod());
}

void CToDoListWnd::EnableTDLProtocol(BOOL bEnable)
{
	if (bEnable)
	{
		CRegKey reg;

		if (reg.Open(HKEY_CLASSES_ROOT, _T("tdl")) == ERROR_SUCCESS)
		{
			reg.Write(_T(""), _T("URL: ToDoList protocol"));
			reg.Write(_T("URL Protocol"), _T(""));

			// write exe name out
			CString sAppPath = FileMisc::GetAppFileName() + _T(" -l \"%1\"");

			reg.Close();

			if (reg.Open(HKEY_CLASSES_ROOT, _T("tdl\\shell\\open\\command")) == ERROR_SUCCESS)
				reg.Write(_T(""), sAppPath);
		}
	}
	else
		CRegKey::DeleteKey(HKEY_CLASSES_ROOT, _T("tdl"));
}

void CToDoListWnd::RestoreVisibility()
{
	const CPreferencesDlg& userPrefs = Prefs();
	CPreferences prefs;

	int nDefShowState = AfxGetApp()->m_nCmdShow;
	BOOL bShowOnStartup = userPrefs.GetShowOnStartup();

	BOOL bMaximized = prefs.GetProfileInt(_T("Pos"), _T("Maximized"), FALSE) || (nDefShowState == SW_SHOWMAXIMIZED);
	BOOL bMinimized = !bShowOnStartup && (nDefShowState == SW_SHOWMINIMIZED || nDefShowState == SW_SHOWMINNOACTIVE);
	
	if (bMinimized)
	{
		bMaximized = FALSE; // can't be max-ed and min-ed
		m_bStartHidden = TRUE;
	}
	
	if (m_bVisible == -1) // not yet set
	{
		m_bVisible = TRUE;
		
		// the only reason it can be hidden is if it uses the systray
		// and the user has elected to not have it show at startup
		// and it was hidden the last time it closed or its set to run
		// minimized and that is the trigger to hide it
		if (!bShowOnStartup && userPrefs.GetUseSysTray())
		{
			if (prefs.GetProfileInt(_T("Pos"), _T("Hidden"), FALSE))
				m_bVisible = FALSE;
			
			// also if wp.showCmd == minimized and we would hide to sys
			// tray when minimized then hide here too
			else if (nDefShowState == SW_SHOWMINIMIZED || nDefShowState == SW_SHOWMINNOACTIVE)
			{
				int nSysTrayOption = Prefs().GetSysTrayOption();
				
				if (nSysTrayOption == STO_ONMINIMIZE || nSysTrayOption == STO_ONMINCLOSE)
					m_bVisible = FALSE;
			}
		}
	}
	
	if (m_bVisible)
	{
		int nShowCmd = (bMaximized ? SW_SHOWMAXIMIZED : 
						(bMinimized ? SW_SHOWMINIMIZED : SW_SHOW));
		
 		ShowWindow(nShowCmd);
 		Invalidate();
 		UpdateWindow();
	}
	else
		m_bStartHidden = TRUE;

	// don't set topmost if maximized
	if (userPrefs.GetAlwaysOnTop() && !bMaximized)
		SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}

void CToDoListWnd::RestorePosition()
{
	CPreferences prefs;

	int nLeft = prefs.GetProfileInt(_T("Pos"), _T("Left"), -1);
	int nTop = prefs.GetProfileInt(_T("Pos"), _T("Top"), -1);
	int nRight = prefs.GetProfileInt(_T("Pos"), _T("Right"), -1);
	int nBottom = prefs.GetProfileInt(_T("Pos"), _T("Bottom"), -1);
	
	CRect rect(nLeft, nTop, nRight, nBottom);
	
	if (rect.Width() > 0 && rect.Height() > 0)
	{
		// ensure this intersects with the desktop by a decent amount
		int BORDER = 200;
		rect.DeflateRect(BORDER, BORDER);

		CRect rScreen;

		if (GraphicsMisc::GetAvailableScreenSpace(rect, rScreen))
		{
			rect.InflateRect(BORDER, BORDER);

			// because the position was saved using the results of 
			// GetWindowPlacement we must use SetWindowPlacement
			// to restore the window
			WINDOWPLACEMENT wp = { 0 };

			wp.rcNormalPosition = rect;
			wp.ptMaxPosition.x = -1;
			wp.ptMaxPosition.y = -1;
			wp.ptMinPosition.x = -1;
			wp.ptMinPosition.y = -1;

// 			FileMisc::LogText(_T("RestorePosition: TopLeft=(%d,%d) BotRight=(%d,%d) MinPos=(%d,%d) MaxPos=(%d,%d)"), 
// 								wp.rcNormalPosition.left, wp.rcNormalPosition.top,
// 								wp.rcNormalPosition.right, wp.rcNormalPosition.bottom,
// 								wp.ptMaxPosition.x, wp.ptMaxPosition.y,
// 								wp.ptMinPosition.x, wp.ptMinPosition.y);

			SetWindowPlacement(&wp);
		}
		else
			rect.SetRectEmpty();
	}
	
	// first time or monitors changed?
	if (rect.IsRectEmpty())
	{
		rect.SetRect(0, 0, 1024, 730); // default

		// make sure it fits the screen
		CRect rScreen;
		GraphicsMisc::GetAvailableScreenSpace(rScreen);

		if (rect.Height() > rScreen.Height())
			rect.bottom = rScreen.Height();

		MoveWindow(rect);
		CenterWindow();
	}
}

void CToDoListWnd::OnNew() 
{
	CreateNewTaskList(FALSE);
	RefreshTabOrder();
}

BOOL CToDoListWnd::CreateNewTaskList(BOOL bAddDefTask)
{
	CFilteredToDoCtrl* pNew = NewToDoCtrl();
	
	if (pNew)
	{
		int nNew = AddToDoCtrl(pNew);
		
		// insert a default task
		if (bAddDefTask)
		{
			if (pNew->GetTaskCount() == 0)
				VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATTOP, FALSE));
		}
		else // ensure it is empty
			pNew->DeleteAllTasks();
		
		// clear modified flag
		pNew->SetModified(FALSE);
		m_mgrToDoCtrls.SetModifiedStatus(nNew, FALSE);
	}

	return (pNew != NULL);
}

void CToDoListWnd::OnUpdateDeletetask(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.HasSelection());	
}

void CToDoListWnd::OnUpdateEditTasktext(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount == 1 && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnUpdateTaskcolor(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.HasSelection() && 
		Prefs().GetTextColorOption() == COLOROPT_DEFAULT);	
}

void CToDoListWnd::OnUpdateTaskdone(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	if (nSelCount == 1)
		pCmdUI->SetCheck(tdc.IsSelectedTaskDone() ? 1 : 0);
	
	pCmdUI->Enable(!tdc.IsReadOnly() && GetToDoCtrl().GetSelectedItem());	
}

void CToDoListWnd::OnUpdateDeletealltasks(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && GetToDoCtrl().GetTaskCount());	
}

void CToDoListWnd::OnUpdateSave(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(GetTDCCount() && tdc.IsModified() && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnUpdateNew(CCmdUI* pCmdUI)  
{
	pCmdUI->Enable(TRUE);	
}

BOOL CToDoListWnd::OnEraseBkgnd(CDC* pDC) 
{
	if (!GetTDCCount())
		return CFrameWnd::OnEraseBkgnd(pDC);

	CDialogHelper::ExcludeChild(this, pDC, &m_toolbar);
	CDialogHelper::ExcludeChild(this, pDC, &m_statusBar);
	CDialogHelper::ExcludeChild(this, pDC, &m_tabCtrl);
	CDialogHelper::ExcludeChild(this, pDC, &m_filterBar);
	CDialogHelper::ExcludeChild(this, pDC, &GetToDoCtrl());

	CRect rClient;
	GetClientRect(rClient);

	if (CThemed::IsThemeActive())
	{
		// paint between the window top and the top of the toolbar
		// in toolbar color
		if (m_bShowToolbar)
		{
			CRect rToolbar = CDialogHelper::OffsetCtrl(this, &m_toolbar);

			pDC->FillSolidRect(rClient.left, rClient.top, rClient.Width(), rToolbar.top, m_theme.crToolbarLight);
			rClient.top += rToolbar.bottom;// + 2;
		}

		// we need to paint a smidgen between the base of the toolbar
		// and the top of the tab bar in crAppBackDark 
		if (WantTasklistTabbarVisible())
		{
			pDC->FillSolidRect(rClient.left, rClient.top, rClient.Width(), 5, m_theme.crAppBackDark);
			rClient.top += 5;
		}

		// and then the rest in crAppBackLight
		pDC->FillSolidRect(rClient, m_theme.crAppBackLight);
	}
	else
		pDC->FillSolidRect(rClient, GetSysColor(COLOR_3DFACE));

	// we must draw out own bevel below the toolbar (or menu if the toolbar is not visible)
	int nVPos = 0;
	
	if (m_bShowToolbar)
	{
		if (COSVersion() <= OSV_XP)
			pDC->FillSolidRect(rClient.left, nVPos, rClient.Width(), 1, m_theme.crAppLinesDark);
		
		nVPos = m_toolbar.GetHeight() + TB_VOFFSET;
	}	

	pDC->FillSolidRect(rClient.left, nVPos, rClient.Width(), 1, m_theme.crAppLinesDark);
	pDC->FillSolidRect(rClient.left, nVPos + 1, rClient.Width(), 1, m_theme.crAppLinesLight);
	
	// bevel below filter bar
	if (m_bShowFilterBar)
	{
		CRect rFilter;
		m_filterBar.GetWindowRect(rFilter);
		ScreenToClient(rFilter);

		int nVPos = rFilter.bottom;

		pDC->FillSolidRect(rClient.left, nVPos, rClient.Width(), 1, m_theme.crAppLinesDark);
		pDC->FillSolidRect(rClient.left, nVPos + 1, rClient.Width(), 1, m_theme.crAppLinesLight);
	}

	// bevel above the statusbar if themed
	if (m_bShowStatusBar && CThemed::IsThemeActive())
	{
		CRect rStatBar;
		m_statusBar.GetWindowRect(rStatBar);
		ScreenToClient(rStatBar);

		pDC->FillSolidRect(0, rStatBar.top - 2, rClient.Width(), 1, m_theme.crAppLinesDark);
		pDC->FillSolidRect(0, rStatBar.top - 1, rClient.Width(), 1, m_theme.crAppLinesLight);
	}

	// this is definitely amongst the worst hacks I've ever had to implement.
	// It occurs because the CSysImageList class seems to not initialize 
	// properly unless the main window is visible. so in the case of starting hidden
	// or starting minimized we must wait until we become visible before
	// adding the tools to the toolbar.
	if (m_bStartHidden)
	{
		m_bStartHidden = FALSE;
		PostMessage(WM_ADDTOOLBARTOOLS);
	}

	return TRUE;
}

void CToDoListWnd::OnSortBy(UINT nCmdID) 
{
	if (nCmdID == ID_SORT_MULTI)
		return;

	TDC_COLUMN nSortBy = GetSortBy(nCmdID);
	
	// update todoctrl
	GetToDoCtrl().Sort(nSortBy);
}

void CToDoListWnd::OnUpdateSortBy(CCmdUI* pCmdUI)
{
	if (pCmdUI->m_nID == ID_SORT_MULTI)
		return;

	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	// disable if column not visible
	TDC_COLUMN nSortBy = GetSortBy(pCmdUI->m_nID);
	pCmdUI->Enable(tdc.IsColumnShowing(nSortBy));

	// only set the radio button if we're not multisorting
	BOOL bChecked = !tdc.IsMultiSorting() && (tdc.GetSortBy() == nSortBy);
	pCmdUI->SetRadio(bChecked);

	// let menu icon manager handle setting selected state
	switch (pCmdUI->m_nID)
	{
	case ID_SORT_NONE:
		pCmdUI->Enable(TRUE);
		break;

	case ID_SORT_BYCOLOR:
		pCmdUI->Enable(Prefs().GetTextColorOption() == COLOROPT_DEFAULT);
		break;

	case ID_SORT_BYPATH:
		pCmdUI->Enable(tdc.GetView() != FTCV_TASKTREE);
		break;
	}
}

TDC_COLUMN CToDoListWnd::GetSortBy(UINT nSortID) 
{
	switch (nSortID)
	{
	case ID_SORT_BYNAME:			return TDCC_CLIENT;
	case ID_SORT_BYID:				return TDCC_ID;
	case ID_SORT_BYALLOCTO:			return TDCC_ALLOCTO;
	case ID_SORT_BYALLOCBY:			return TDCC_ALLOCBY;
	case ID_SORT_BYSTATUS:			return TDCC_STATUS;
	case ID_SORT_BYCATEGORY:		return TDCC_CATEGORY;
	case ID_SORT_BYTAG:				return TDCC_TAGS;
	case ID_SORT_BYPERCENT:			return TDCC_PERCENT;
	case ID_SORT_BYTIMEEST:			return TDCC_TIMEEST;
	case ID_SORT_BYTIMESPENT:		return TDCC_TIMESPENT;
	case ID_SORT_BYSTARTDATE:		return TDCC_STARTDATE;
	case ID_SORT_BYDUEDATE:			return TDCC_DUEDATE;
	case ID_SORT_BYDONEDATE:		return TDCC_DONEDATE; 
	case ID_SORT_BYDONE:			return TDCC_DONE;
	case ID_SORT_BYPRIORITY:		return TDCC_PRIORITY;
	case ID_SORT_BYCREATEDBY:		return TDCC_CREATEDBY;
	case ID_SORT_BYCREATIONDATE:	return TDCC_CREATIONDATE;
	case ID_SORT_BYMODIFYDATE:		return TDCC_LASTMOD;
	case ID_SORT_BYRISK:			return TDCC_RISK;
	case ID_SORT_BYEXTERNALID:		return TDCC_EXTERNALID;
	case ID_SORT_BYCOST:			return TDCC_COST;
	case ID_SORT_BYVERSION:			return TDCC_VERSION;
	case ID_SORT_BYRECURRENCE:		return TDCC_RECURRENCE;
	case ID_SORT_NONE:				return TDCC_NONE;
	case ID_SORT_BYFLAG:			return TDCC_FLAG;
	case ID_SORT_BYREMAINING:		return TDCC_REMAINING;
	case ID_SORT_BYRECENTEDIT:		return TDCC_RECENTEDIT;
	case ID_SORT_BYICON:			return TDCC_ICON;
	case ID_SORT_BYFILEREF:			return TDCC_FILEREF;
	case ID_SORT_BYTIMETRACKING:	return TDCC_TRACKTIME;
	case ID_SORT_BYPATH:			return TDCC_PATH;
	case ID_SORT_BYCOLOR:			return TDCC_COLOR;
	case ID_SORT_BYDEPENDENCY:		return TDCC_DEPENDENCY;
	case ID_SORT_BYPOSITION:		return TDCC_POSITION;
	}
	
	// handle custom columns
	if (nSortID >= ID_SORT_BYCUSTOMCOLUMN_FIRST && nSortID <= ID_SORT_BYCUSTOMCOLUMN_LAST)
		return (TDC_COLUMN)(TDCC_CUSTOMCOLUMN_FIRST + (nSortID - ID_SORT_BYCUSTOMCOLUMN_FIRST));

	// all else
	ASSERT (0);
	return TDCC_NONE;
}

UINT CToDoListWnd::GetSortID(TDC_COLUMN nSortBy) 
{
	switch (nSortBy)
	{
	case TDCC_CLIENT:		return ID_SORT_BYNAME;
	case TDCC_ID:			return ID_SORT_BYID;
	case TDCC_ALLOCTO:		return ID_SORT_BYALLOCTO;
	case TDCC_ALLOCBY:		return ID_SORT_BYALLOCBY;
	case TDCC_STATUS:		return ID_SORT_BYSTATUS;
	case TDCC_CATEGORY:		return ID_SORT_BYCATEGORY;
	case TDCC_TAGS:			return ID_SORT_BYTAG;
	case TDCC_PERCENT:		return ID_SORT_BYPERCENT;
	case TDCC_TIMEEST:		return ID_SORT_BYTIMEEST;
	case TDCC_TIMESPENT:	return ID_SORT_BYTIMESPENT;
	case TDCC_STARTDATE:	return ID_SORT_BYSTARTDATE;
	case TDCC_DUEDATE:		return ID_SORT_BYDUEDATE;
	case TDCC_DONEDATE:		return ID_SORT_BYDONEDATE;
	case TDCC_DONE:			return ID_SORT_BYDONE;
	case TDCC_PRIORITY:		return ID_SORT_BYPRIORITY;
	case TDCC_FLAG:			return ID_SORT_BYFLAG;
	case TDCC_CREATEDBY:	return ID_SORT_BYCREATEDBY;
	case TDCC_CREATIONDATE:	return ID_SORT_BYCREATIONDATE;
	case TDCC_LASTMOD:		return ID_SORT_BYMODIFYDATE;
	case TDCC_RISK:			return ID_SORT_BYRISK;
	case TDCC_EXTERNALID:	return ID_SORT_BYEXTERNALID;
	case TDCC_COST:			return ID_SORT_BYCOST;
	case TDCC_VERSION:		return ID_SORT_BYVERSION;
	case TDCC_RECURRENCE:	return ID_SORT_BYRECURRENCE;
	case TDCC_REMAINING:	return ID_SORT_BYREMAINING;
	case TDCC_RECENTEDIT:	return ID_SORT_BYRECENTEDIT;
	case TDCC_NONE:			return ID_SORT_NONE;
	case TDCC_ICON:			return ID_SORT_BYICON;
	case TDCC_FILEREF:		return ID_SORT_BYFILEREF;
	case TDCC_TRACKTIME:	return ID_SORT_BYTIMETRACKING;
	case TDCC_PATH:			return ID_SORT_BYPATH;
	case TDCC_COLOR:		return ID_SORT_BYCOLOR;
	case TDCC_POSITION:		return ID_SORT_BYPOSITION;
	}
	
	// handle custom columns
	if (nSortBy >= TDCC_CUSTOMCOLUMN_FIRST && nSortBy < TDCC_CUSTOMCOLUMN_LAST)
		return (ID_SORT_BYCUSTOMCOLUMN_FIRST + (nSortBy - TDCC_CUSTOMCOLUMN_FIRST));

	// all else
	ASSERT (0);
	return 0;
}

void CToDoListWnd::OnNewtaskAttopSelected() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATTOPOFSELTASKPARENT));
}

void CToDoListWnd::OnNewtaskAtbottomSelected() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATBOTTOMOFSELTASKPARENT));
}

void CToDoListWnd::OnNewtaskAfterselectedtask() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTAFTERSELTASK));
}

void CToDoListWnd::OnNewtaskBeforeselectedtask() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTBEFORESELTASK));
}

void CToDoListWnd::OnNewsubtaskAtbottom() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATBOTTOMOFSELTASK));
}

void CToDoListWnd::OnNewsubtaskAttop() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATTOPOFSELTASK));
}

void CToDoListWnd::OnNewtaskAttop() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATTOP));
}

void CToDoListWnd::OnNewtaskAtbottom() 
{
	VERIFY (CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATBOTTOM));
}

BOOL CToDoListWnd::CreateNewTask(LPCTSTR szTitle, TDC_INSERTWHERE nInsertWhere, BOOL bEdit)
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	return (tdc.CreateNewTask(szTitle, nInsertWhere, bEdit) != NULL);
}

void CToDoListWnd::OnUpdateSort(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.IsSortable() && tdc.GetTaskCount());	
}

void CToDoListWnd::OnEditTaskcolor() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (!tdc.IsReadOnly() && tdc.HasSelection())
	{
		CEnColorDialog dialog(tdc.GetSelectedTaskColor(), CC_FULLOPEN | CC_RGBINIT);
		
		if (dialog.DoModal() == IDOK)
			tdc.SetSelectedTaskColor(dialog.GetColor());
	}
}


void CToDoListWnd::OnEditCleartaskcolor() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (!tdc.IsReadOnly() && tdc.HasSelection())
		tdc.ClearSelectedTaskColor();
}

void CToDoListWnd::OnUpdateEditCleartaskcolor(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.HasSelection() && 
					Prefs().GetTextColorOption() == COLOROPT_DEFAULT &&
					tdc.GetSelectedTaskColor() != 0);	
}

void CToDoListWnd::OnEditTaskdone() 
{
	GetToDoCtrl().SetSelectedTaskDone(!GetToDoCtrl().IsSelectedTaskDone());
}

void CToDoListWnd::OnEditTasktext() 
{
	GetToDoCtrl().EditSelectedTask();
}

void CToDoListWnd::OnTrayIconClick(NMHDR* /*pNMHDR*/, LRESULT* pResult)
{
	SetFocus();
	Show(Prefs().GetToggleTrayVisibility());
	*pResult = 0;
}

LRESULT CToDoListWnd::OnToDoListShowWindow(WPARAM /*wp*/, LPARAM /*lp*/)
{
	Show(FALSE);
	return 0;
}

LRESULT CToDoListWnd::OnToDoListGetVersion(WPARAM /*wp*/, LPARAM /*lp*/)
{
	return GetVersion();
}

LRESULT CToDoListWnd::OnToDoListRefreshPrefs(WPARAM /*wp*/, LPARAM /*lp*/)
{
	// sent by the app object if registry settings have changed
	ResetPrefs();

	// mark all tasklists as needing update
	m_mgrToDoCtrls.SetAllNeedPreferenceUpdate(TRUE);
	
	// then update active tasklist
	UpdateToDoCtrlPreferences();

	return 0;
}

void CToDoListWnd::OnTrayIconDblClk(NMHDR* /*pNMHDR*/, LRESULT* pResult)
{
	Show(FALSE);
	
	*pResult = 0;
}

void CToDoListWnd::OnTrayiconCreatetask() 
{
	Show(FALSE);
	
	// create a task at the top of the tree
	GetToDoCtrl().CreateNewTask(CEnString(IDS_TASK), TDC_INSERTATTOP);
}

void CToDoListWnd::OnTrayIconRClick(NMHDR* pNMHDR, LRESULT* pResult)
{
	SetForegroundWindow();
	
	// show context menu
	CEnMenu menu;
	
	if (menu.LoadMenu(IDR_MISC, m_trayIcon.GetSafeHwnd(), TRUE))
	{
		CMenu* pSubMenu = menu.GetSubMenu(TRAYICON);
		pSubMenu->SetDefaultItem(ID_TRAYICON_SHOW);
		
		if (pSubMenu)
		{
			m_mgrToDoCtrls.PreparePopupMenu(*pSubMenu, ID_TRAYICON_SHOWDUETASKS1);
			
			NM_TRAYICON* pNMTI = (NM_TRAYICON*)pNMHDR;

			// in order to ensure that multiple password dialogs cannot 
			// appear we must make sure that all the command handling is
			// done before we return from here
			UINT nCmdID = ::TrackPopupMenu(*pSubMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON, 
											pNMTI->ptAction.x, pNMTI->ptAction.y, 0, *this, NULL);

			PostMessage(WM_NULL);

			if (nCmdID != (UINT)-1)
				SendMessage(WM_COMMAND, nCmdID);
		}
	}
	
	*pResult = 0;
}

void CToDoListWnd::OnClose() 
{
	if (!m_bEndingSession)
	{
		int nSysTrayOption = Prefs().GetSysTrayOption();
		
		if (nSysTrayOption == STO_ONCLOSE || nSysTrayOption == STO_ONMINCLOSE)
			MinimizeToTray();
		
		else // shutdown but user can cancel
			DoExit();
	}
	// else we've already shutdown
}

void CToDoListWnd::MinimizeToTray()
{
	// end whatever the user is doing
	GetToDoCtrl().Flush();

	// save prev state so we can restore properly
	CPreferences().WriteProfileInt(_T("Pos"), _T("Maximized"), IsZoomed());
	
	if (Prefs().GetAutoSaveOnSwitchApp())
	{
		// save all
		SaveAll(TDLS_FLUSH | TDLS_AUTOSAVE);
	}
	
	// hide main window
	Gui::MinToTray(*this); // courtesy of floyd
	m_bVisible = FALSE;
	
	// hide find dialog
	ShowFindDialog(FALSE);
}

void CToDoListWnd::ShowFindDialog(BOOL bShow)
{
	if (bShow)
	{
		if (m_bVisible && m_findDlg.GetSafeHwnd() && IsWindowVisible())
			m_findDlg.Show(TRUE);
	}
	else // hide
	{
		if (m_findDlg.GetSafeHwnd())
		{
			m_bFindShowing = m_findDlg.IsWindowVisible();
			m_findDlg.Show(FALSE);
		}
		else
			m_bFindShowing = FALSE;
	}
}

void CToDoListWnd::OnTrayiconClose() 
{
	DoExit();
}

LRESULT CToDoListWnd::OnToDoCtrlNotifyListChange(WPARAM /*wp*/, LPARAM lp)
{
	// decide whether the filter controls need updating
	switch (lp)
	{
	case TDCA_ALLOCTO:
	case TDCA_ALLOCBY:
	case TDCA_STATUS:
	case TDCA_CATEGORY:
	case TDCA_VERSION:
	case TDCA_TAGS:
		RefreshFilterControls();
		break;
	}
	
	return 0L;
}

LRESULT CToDoListWnd::OnToDoCtrlNotifyViewChange(WPARAM wp, LPARAM lp)
{
	if (GetTDCCount())
	{
		if (lp != (LPARAM)wp)
		{
			CFocusWatcher::UpdateFocus();
			m_filterBar.RefreshFilterControls(GetToDoCtrl());
		}
		else
		{
			int breakpoint = 0;
		}
	}

	return 0L;
}

LRESULT CToDoListWnd::OnToDoCtrlNotifyTimeTrack(WPARAM /*wp*/, LPARAM lp)
{
	BOOL bTrack = lp;

	if (bTrack && Prefs().GetExclusiveTimeTracking())
	{
		// end time tracking on every other tasklist
		int nSel = GetSelToDoCtrl();
		ASSERT (nSel != -1);

		for (int nCtrl = 0; nCtrl < GetTDCCount(); nCtrl++)
		{
			if (nCtrl != nSel)
				GetToDoCtrl(nCtrl).EndTimeTracking();
		}
	}

	return 0L;
}
	
LRESULT CToDoListWnd::OnToDoCtrlNotifyRecreateRecurringTask(WPARAM wp, LPARAM lp)
{
	DWORD dwTaskID = wp, dwNewTaskID = lp;

	// is there a reminder that we need to copy for the new task
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	TDCREMINDER rem;

	int nRem = m_reminders.FindReminder(dwTaskID, &tdc);

	if (nRem != -1)
	{
		// get the existing reminder
		m_reminders.GetReminder(nRem, rem);

		// init for new task
		rem.bEnabled = TRUE;
		rem.dDaysSnooze = 0.0;
		rem.dwTaskID = dwNewTaskID;

		// add for the new task ID
		m_reminders.SetReminder(rem);

		// delete the original only if the task id has changed
		if (dwNewTaskID != dwTaskID)		
			m_reminders.RemoveReminder(dwTaskID, rem.pTDC);
	}

	return 0L;
}

LRESULT CToDoListWnd::OnToDoCtrlNotifyMod(WPARAM wp, LPARAM lp)
{
	int nTDC = m_mgrToDoCtrls.FindToDoCtrl((HWND)wp);

	if (nTDC == -1)
	{
		// could be a notification from a TDC not yet added
		return 0L;
	}

	BOOL bWasMod = m_mgrToDoCtrls.GetModifiedStatus(nTDC);
	m_mgrToDoCtrls.SetModifiedStatus(nTDC, TRUE);

	// update the caption only if the control was not previously modified
	// or the project name changed
	if (!bWasMod)
		UpdateCaption();

	TDC_ATTRIBUTE nAttrib = (TDC_ATTRIBUTE)lp;

	switch (nAttrib)
	{
	case TDCA_PROJNAME:
		{
			// update caption if not already done
			if (bWasMod)
				UpdateCaption();

			// update tab order
			if (Prefs().GetKeepTabsOrdered())
				RefreshTabOrder();
		}
		break;

	// update due items
	case TDCA_DONEDATE:
	case TDCA_DUEDATE:
		OnTimerDueItems(nTDC);
		break;

	// reminders
	case TDCA_DELETE:
		m_reminders.RemoveDeletedTaskReminders(&GetToDoCtrl(nTDC));
		break;
	}

	// status bar
	UpdateStatusbar();

	// refresh toolbar states
	PostMessage(WM_IDLEUPDATECMDUI, TRUE);

	// do we need to update the current todoctrl's
	// custom attributes on the find dialog?
	if (m_findDlg.GetSafeHwnd() && nAttrib == TDCA_CUSTOMATTRIBDEFS)
	{
		UpdateFindDialogCustomAttributes(&GetToDoCtrl());
	}

	return 0L;
}

void CToDoListWnd::UpdateCaption()
{
	int nSel = GetSelToDoCtrl();
	
	CString sProjectName = m_mgrToDoCtrls.UpdateTabItemText(nSel);
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	UINT nIDStatus = 0;
	
	if (m_mgrToDoCtrls.GetReadOnlyStatus(nSel) > 0)
		nIDStatus = IDS_STATUSREADONLY;
	
	else if (tdc.CompareFileFormat() == TDCFF_NEWER)
		nIDStatus = IDS_STATUSNEWERFORMAT;
	
	else if (m_mgrToDoCtrls.GetReadOnlyStatus(nSel) == 0 && 
		m_mgrToDoCtrls.PathSupportsSourceControl(nSel))
	{
		if (tdc.IsCheckedOut())
			nIDStatus = IDS_STATUSCHECKEDOUT;
		else
			nIDStatus = IDS_STATUSCHECKEDIN;
	}
	else if (!Prefs().GetEnableSourceControl() && m_mgrToDoCtrls.IsSourceControlled(nSel))
	{
		nIDStatus = IDS_STATUSSOURCECONTROLLED;
	}
	
	CString sCaption, sCopyright(MAKEINTRESOURCE(IDS_COPYRIGHT));
	CEnString sStatus(nIDStatus);
	
	// add current focus text as required
	if (nIDStatus)
	{
		if (m_bShowStatusBar || m_sCurrentFocus.IsEmpty())
			sCaption.Format(_T("%s [%s] - %s"), sProjectName, sStatus, sCopyright);
		else
			sCaption.Format(_T("%s [%s][%s] - %s"), sProjectName, m_sCurrentFocus, sStatus, sCopyright);
	}
	else
	{
		if (m_bShowStatusBar || m_sCurrentFocus.IsEmpty())
			sCaption.Format(_T("%s - %s"), sProjectName, sCopyright);
		else
			sCaption.Format(_T("%s [%s] - %s"), sProjectName, m_sCurrentFocus, sCopyright);
	}
	
	// prepend task pathname if tasklist not visible
	if (m_nMaxState == TDCMS_MAXCOMMENTS)
	{
		// quote the path to help it stand-out
		CString sTaskPath;
		sTaskPath.Format(_T("\"%s\""), tdc.GetSelectedTaskPath(TRUE, 100));
		
		if (!sTaskPath.IsEmpty())
			sCaption = sTaskPath + " - " + sCaption;
	}
	
	SetWindowText(sCaption);

	// set tray tip too
	UpdateTooltip();
}

void CToDoListWnd::UpdateTooltip()
{
    // base the tooltip on our current caption
    CString sTooltip;
    GetWindowText(sTooltip);

    // move copyright to next line and remove '-'
    sTooltip.Replace(_T(" - "), _T("\n"));

    // prefix with selected task as first line
    CFilteredToDoCtrl& tdc = GetToDoCtrl();
    DWORD dwSelID = tdc.GetSelectedTaskID();

    if (dwSelID)
    {
        CString sSelItem = tdc.GetTaskTitle(dwSelID);

        // maximum length of tooltip is 128 including null
        if (sSelItem.GetLength() > (128 - sTooltip.GetLength() - 6))
        {
            sSelItem = sSelItem.Left(128 - sTooltip.GetLength() - 6);
            sSelItem += _T("...");
        }
        else if (tdc.GetSelectedCount() > 1)
            sSelItem += _T(", ...");

        sTooltip = sSelItem + _T("\n") + sTooltip;
    }

    m_trayIcon.SetTip(sTooltip);
}

BOOL CToDoListWnd::Export2Html(const CTaskFile& tasks, LPCTSTR szFilePath, LPCTSTR szStylesheet) const
{
	CWaitCursor cursor;
	
	if (FileMisc::FileExists(szStylesheet))
	{
		return tasks.TransformToFile(szStylesheet, szFilePath, Prefs().GetHtmlCharSet());
	}
	
	// else default export
	return m_mgrImportExport.ExportTaskListToHtml(&tasks, szFilePath);
}

void CToDoListWnd::OnSaveas() 
{
	int nSel = GetSelToDoCtrl();
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nSel);

	// use tab text as hint to user
	CString sFilePath = m_mgrToDoCtrls.GetFilePath(nSel, FALSE);
	CPreferences prefs;

	// display the dialog
	CFileSaveDialog dialog(IDS_SAVETASKLISTAS_TITLE,
							GetDefaultFileExt(), 
							sFilePath, 
							EOFN_DEFAULTSAVE,
							GetFileFilter(FALSE), 
							this);
	
	// always use the tasklist's own format for initializing the file dialog
	dialog.m_ofn.nFilterIndex = tdc.IsUnicode() ? 2 : 1;
	
	int nRes = dialog.DoModal(&prefs);
	
	if (nRes == IDOK)
	{
		BOOL bWasUnicode = tdc.IsUnicode();
		BOOL bUnicode = (dialog.m_ofn.nFilterIndex == 2);
		tdc.SetUnicode(bUnicode);

		// save this choice as the best hint for the next new tasklist
		CPreferences().WriteProfileInt(PREF_KEY, _T("UnicodeNewTasklists"), bUnicode);

 		if (SaveTaskList(nSel, dialog.GetPathName()) == TDCO_SUCCESS)
		{
			//m_mgrToDoCtrls.ClearWebDetails(nSel); // because it's no longer a remote file
		}
		else // restore previous file format
			tdc.SetUnicode(bWasUnicode);

		UpdateStatusbar();
	}
}

void CToDoListWnd::OnUpdateSaveas(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(tdc.GetTaskCount() || tdc.IsModified());
}

void CToDoListWnd::OnContextMenu(CWnd* pWnd, CPoint point) 
{
	static UINT nActiveMenuID = 0; // prevent reentrancy
	UINT nMenuID = 0;
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (pWnd == &m_tabCtrl)
	{
		// if point.x,y are both -1 then we just use the cursor pos
		// which is what windows appears to do mostly/sometimes
		if (point.x == -1 && point.y == -1)
		{
			CRect rTab;
			
			if (m_tabCtrl.GetItemRect(m_tabCtrl.GetCurSel(), rTab))
			{
				point = rTab.CenterPoint();
				m_tabCtrl.ClientToScreen(&point);
				
				// load popup menu
				nMenuID = TABCTRLCONTEXT;
			}
		}
		else
		{
			// activate clicked tab
			TCHITTESTINFO tcht = { { point.x, point.y }, TCHT_NOWHERE  };
			m_tabCtrl.ScreenToClient(&tcht.pt);
			
			int nTab = m_tabCtrl.HitTest(&tcht);
			
			if (nTab != -1 && !(tcht.flags & TCHT_NOWHERE))
			{
				if (nTab != m_tabCtrl.GetCurSel())
				{
					if (!SelectToDoCtrl(nTab, TRUE))
						return; // user cancelled
				}
				
				m_tabCtrl.SetFocus(); // give user feedback
				
				// load popup menu
				nMenuID = TABCTRLCONTEXT;
			}
		}
	}
	else if (pWnd == (CWnd*)&tdc) // try active todoctrl
	{
		TDC_HITTEST nHit = tdc.HitTest(point);

		switch (nHit)
		{
		case TDCHT_NOWHERE:
			break;

		case TDCHT_TASKLIST:
		case TDCHT_TASK:
			if (tdc.WantTaskContextMenu())
			{
				nMenuID = TASKCONTEXT;

				// if point.x,y are both -1 then we request the current 
				// selection position
				if (point.x == -1 && point.y == -1)
				{
					CRect rSelection;

					if (tdc.GetSelectionBoundingRect(rSelection))
					{
						tdc.ClientToScreen(rSelection);

						point.x = min(rSelection.left + 50, rSelection.CenterPoint().x);
						point.y = rSelection.top + 8;
					}
					else
					{
						nMenuID = 0; // no context menu
					}
				}
			}
			break;

		case TDCHT_COLUMNHEADER:
			nMenuID = HEADERCONTEXT;
			break;
		}
	}
	
	// show the menu
	if (nMenuID && nMenuID != nActiveMenuID)
	{
		CEnMenu menu;
		
		if (menu.LoadMenu(IDR_MISC, NULL, TRUE))
		{
			CMenu* pPopup = menu.GetSubMenu(nMenuID);
			
			if (pPopup)
			{
				// some special handling
				switch (nMenuID)
				{
				case TASKCONTEXT:
					m_nContextColumnID = tdc.ColumnHitTest(point);
					PrepareEditMenu(pPopup);
					break;

				case TABCTRLCONTEXT:
					PrepareSourceControlMenu(pPopup);
					break;
				}
				
				CToolbarHelper::PrepareMenuItems(pPopup, this);
				
				nActiveMenuID = nMenuID;
				pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, this);
				nActiveMenuID = 0;
			}
		}
	}
	else
		CFrameWnd::OnContextMenu(pWnd, point);
}

void CToDoListWnd::OnTrayiconShow() 
{
	Show(FALSE);
}

void CToDoListWnd::OnTrayiconShowDueTasks(UINT nCmdID) 
{
	int nTDC = nCmdID - ID_TRAYICON_SHOWDUETASKS1;
	int nSelTDC = GetSelToDoCtrl();

	// verify password if encrypted tasklist is active
	// unless app is already visible
	if (!m_bVisible || IsIconic() || (nTDC != nSelTDC))
	{
		if (!VerifyToDoCtrlPassword(nTDC))
			return;
	}

	CFilteredToDoCtrl& tdc = GetToDoCtrl(nTDC);

	if (!DoDueTaskNotification(nTDC, PFP_DUETODAY))
	{
		CEnString sMessage(IDS_NODUETODAY, m_mgrToDoCtrls.GetFriendlyProjectName(nTDC));
		MessageBox(sMessage);//, IDS_DUETASKS_TITLE);
	}
}

LRESULT CToDoListWnd::OnHotkey(WPARAM /*wp*/, LPARAM /*lp*/)
{
	Show(TRUE);
	return 0L;
}

BOOL CToDoListWnd::VerifyToDoCtrlPassword() const
{
	return VerifyToDoCtrlPassword(GetSelToDoCtrl());
}

BOOL CToDoListWnd::VerifyToDoCtrlPassword(int nIndex) const
{
	if (m_bPasswordPrompting)
	{
		CEnString sExplanation(IDS_SELECTENCRYPTED, 
								m_mgrToDoCtrls.GetFriendlyProjectName(nIndex));

		return GetToDoCtrl(nIndex).VerifyPassword(sExplanation);
	}
	
	// else
	return TRUE;
}

void CToDoListWnd::Show(BOOL bAllowToggle)
{
	if (GetSelToDoCtrl() == -1)
		return;
	
	if (!m_bVisible || !IsWindowVisible()) // restore from the tray
	{
		SetForegroundWindow();
		
		if (!VerifyToDoCtrlPassword())
			return;

		m_bVisible = TRUE;
		Gui::RestoreFromTray(*this, CPreferences().GetProfileInt(_T("Pos"), _T("Maximized"), FALSE));

		// restore find dialog
		if (m_bFindShowing)
			m_findDlg.Show();
	}
	else if (IsIconic())
	{
		SetForegroundWindow();
		ShowWindow(SW_RESTORE); // this will force a password check
	}
	// if we're already visible then either bring to the foreground 
	// or hide if we're right at the top of the z-order
	else if (!bAllowToggle || Gui::IsObscured(*this) || !Gui::HasFocus(*this, TRUE))
		SetForegroundWindow();

	else if (Prefs().GetSysTrayOption() == STO_NONE)
		ShowWindow(SW_MINIMIZE);

	else // hide to system tray
		MinimizeToTray();
	
	// refresh all tasklists if we are visible
	if (m_bVisible && !IsIconic())
	{
		const CPreferencesDlg& userPrefs = Prefs();
		
		if (userPrefs.GetReadonlyReloadOption() != RO_NO)
			OnTimerReadOnlyStatus();
		
		if (userPrefs.GetTimestampReloadOption() != RO_NO)
			OnTimerTimestampChange();
		
		if (userPrefs.GetEnableSourceControl())
			OnTimerCheckoutStatus();
	}	

	GetToDoCtrl().SetFocusToTasks();
}

#ifdef _DEBUG
void CToDoListWnd::OnDebugEndSession() 
{ 
	SendMessage(WM_QUERYENDSESSION); 
	SendMessage(WM_ENDSESSION, 1, 0); 
}

void CToDoListWnd::OnDebugShowSetupDlg() 
{ 
	CTDLWelcomeWizard dialog;
	dialog.DoModal();
}
#endif

void CToDoListWnd::TranslateUIElements() 
{ 
	// show progress bar
	DOPROGRESS(IDS_UPDATINGDICTIONARY)

	// disable translation of top-level menu names in
	// IDR_MISC, IDR_PLACEHOLDERS, IDR_TREEDRAGDROP
	CLocalizer::IgnoreString(_T("TrayIcon"));
	CLocalizer::IgnoreString(_T("TaskContext"));
	CLocalizer::IgnoreString(_T("TabCtrl"));
	CLocalizer::IgnoreString(_T("TasklistHeader"));
	CLocalizer::IgnoreString(_T("CommentsPopup"));
	CLocalizer::IgnoreString(_T("ToolsDialog"));
	CLocalizer::IgnoreString(_T("TreeDragDrop"));

	// disable light box manager temporarily
	// to avoid the disabling effect whenever 
	// a dialog is created
	if (Prefs().GetEnableLightboxMgr())
		CLightBoxMgr::Release();

	CLocalizer::ForceTranslateAllUIElements(IDS_FIRSTSTRING, IDS_LASTSTRING);

	// force a redraw of whole UI
	if (IsWindowVisible())
	{
		ShowWindow(SW_HIDE);
		ShowWindow(SW_SHOW);
	}
	
	SetForegroundWindow();

	// restore light box manager
	if (Prefs().GetEnableLightboxMgr())
		CLightBoxMgr::Initialize(this, m_theme.crAppBackDark);
}

void CToDoListWnd::OnUpdateRecentFileMenu(CCmdUI* pCmdUI) 
{
	// check that this is not occurring because our CFrameWnd
	// base class is routing this to the first item in a submenu
	if (pCmdUI->m_pMenu && 
		pCmdUI->m_pMenu->GetMenuItemID(pCmdUI->m_nIndex) == (UINT)-1)
		return;

	m_mruList.UpdateMenu(pCmdUI);	
}

BOOL CToDoListWnd::OnOpenRecentFile(UINT nID)
{
	ASSERT(nID >= ID_FILE_MRU_FILE1);
	ASSERT(nID < ID_FILE_MRU_FILE1 + (UINT)m_mruList.GetSize());
	
	int nIndex = nID - ID_FILE_MRU_FILE1;
	
	CString sTaskList = m_mruList[nIndex];
	TDC_FILE nOpen = OpenTaskList(sTaskList);
	
	if (nOpen == TDCO_SUCCESS)
	{
		Resize();
		UpdateWindow();
	}
	else
	{
		HandleLoadTasklistError(nOpen, sTaskList);
		
		if (nOpen != TDCO_CANCELLED)
			m_mruList.Remove(nIndex);
	}

	RefreshTabOrder();
	
	// always return TRUE to say we handled it
	return TRUE;
}

void CToDoListWnd::RefreshTabOrder()
{
	if (Prefs().GetKeepTabsOrdered())
	{
		int nSelOrg = GetSelToDoCtrl();
		int nSel = m_mgrToDoCtrls.SortToDoCtrlsByName();
		
		if (nSel != nSelOrg)
			SelectToDoCtrl(nSel, FALSE);
	}
}

TDC_FILE CToDoListWnd::DelayOpenTaskList(LPCTSTR szFilePath)
{
	ASSERT (Prefs().GetEnableDelayedLoading()); // sanity check

	// decode/prepare filepath
	CString sFilePath(szFilePath);
	TSM_TASKLISTINFO storageInfo;

	TDC_PREPAREPATH nPathType = PrepareFilePath(sFilePath, &storageInfo);

	if (nPathType == TDCPP_NONE)
		return TDCO_NOTEXIST;

	// see if the tasklist is already open
	if (SelectToDoCtrl(sFilePath, TRUE))
		return TDCO_SUCCESS;

	// delay load the file, visible but disabled
	CFilteredToDoCtrl* pTDC = NewToDoCtrl(TRUE, FALSE);

	// if this is a 'special' temp file then assume TDL automatically
	// named it when handling WM_ENDSESSION. 
	BOOL bDelayLoad = !IsEndSessionFilePath(sFilePath);
	COleDateTime dtEarliest;

	if (bDelayLoad)
		bDelayLoad = pTDC->DelayLoad(sFilePath, dtEarliest);

	if (bDelayLoad)
	{
		// now we have to check for whether the tasklist has due tasks 
		// and the user wants notification
		int nNotifyDueBy = Prefs().GetNotifyDueByOnLoad();

		if (nNotifyDueBy != PFP_DONTNOTIFY && CDateHelper::IsDateSet(dtEarliest.m_dt))
		{
			// check the date against when the user wants notifying
			DH_DATE nDate = DHD_TODAY;
			
			switch (nNotifyDueBy)
			{
			case PFP_DUETODAY:		/*nDate = DHD_TODAY;*/		break;
			case PFP_DUETOMORROW:	nDate = DHD_TOMORROW;		break;
			case PFP_DUETHISWEEK:	nDate = DHD_ENDTHISWEEK;	break;
			case PFP_DUENEXTWEEK:	nDate = DHD_ENDNEXTWEEK;	break;
			case PFP_DUETHISMONTH:	nDate = DHD_ENDTHISMONTH;	break;
			case PFP_DUENEXTMONTH:	nDate = DHD_ENDNEXTMONTH;	break;
			default:				ASSERT (0);
			}
			
			COleDateTime dtDueWhen = CDateHelper::GetDate(nDate);
			
			bDelayLoad = (dtDueWhen < dtEarliest);
		}
	}

	// if the delay load failed for any reason we need to delete the tasklist
	// and fallback on the default load mechanism
	if (!bDelayLoad)
	{
		pTDC->DestroyWindow();
		delete pTDC;

		// note: we use the original filepath in case it is 
		// actually storage info
		return OpenTaskList(szFilePath, FALSE);
	}
	
	int nCtrl = m_mgrToDoCtrls.AddToDoCtrl(pTDC, &storageInfo, FALSE); // FALSE == not yet loaded
	
	// update due item status
	if (CDateHelper::IsDateSet(dtEarliest))
	{
		TDCM_DUESTATUS nStatus = TDCM_FUTURE;
		COleDateTime dtToday = COleDateTime::GetCurrentTime();

		if (floor(dtEarliest) < floor(dtToday))
			nStatus = TDCM_PAST;

		else if (floor(dtEarliest) == floor(dtToday))
			nStatus = TDCM_TODAY;

		m_mgrToDoCtrls.SetDueItemStatus(nCtrl, nStatus);
	}
		
	return TDCO_SUCCESS;
}

CString CToDoListWnd::GetEndSessionFilePath()
{
	return FileMisc::GetTempFileName(_T("tde"));
}

BOOL CToDoListWnd::IsEndSessionFilePath(const CString& sFilePath)
{
	if (!FileMisc::FileExists(sFilePath))
		return FALSE;

	if (!FileMisc::HasExtension(sFilePath, _T("tmp")))
		return FALSE;
	
	if (!FileMisc::IsTempFile(sFilePath))
		return FALSE;

	if (FileMisc::GetFileNameFromPath(sFilePath).Find(_T("tde")) != 0)
		return FALSE;

	// passed all the tests
	return TRUE;
}

TDC_ARCHIVE CToDoListWnd::GetAutoArchiveOptions(LPCTSTR szFilePath, CString& sArchivePath, BOOL& bRemoveFlagged) const
{
	TDC_ARCHIVE nRemove = TDC_REMOVENONE;
	bRemoveFlagged = FALSE;

	const CPreferencesDlg& userPrefs = Prefs();
	
	if (userPrefs.GetAutoArchive())
	{
		if (userPrefs.GetRemoveArchivedTasks())
		{
			if (userPrefs.GetRemoveOnlyOnAbsoluteCompletion())
				nRemove = TDC_REMOVEIFSIBLINGSANDSUBTASKSCOMPLETE;
			else
				nRemove = TDC_REMOVEALL;

			bRemoveFlagged = !userPrefs.GetDontRemoveFlagged();
		}
		
		sArchivePath = m_mgrToDoCtrls.GetArchivePath(szFilePath);
	}
	else
		sArchivePath.Empty();

	return nRemove;
}

TDC_FILE CToDoListWnd::OpenTaskList(LPCTSTR szFilePath, BOOL bNotifyDueTasks)
{
	CString sFilePath(szFilePath);
	TDC_PREPAREPATH nType = PrepareFilePath(sFilePath);
	
	if (nType == TDCPP_NONE)
		return TDCO_NOTEXIST;
	
	// see if the tasklist is already open
	int nExist = m_mgrToDoCtrls.FindToDoCtrl(sFilePath);
	
	if (nExist != -1)
	{
		// reload provided there are no existing changes
		// and the timestamp has changed
		if (!m_mgrToDoCtrls.GetModifiedStatus(nExist) &&
			m_mgrToDoCtrls.RefreshLastModified(nExist))
		{
			ReloadTaskList(nExist, bNotifyDueTasks);
		}
		
		// then select
		if (SelectToDoCtrl(nExist, TRUE))
			return TDCO_SUCCESS;
	}
	
	// create a new todoltrl for this tasklist 
	const CPreferencesDlg& userPrefs = Prefs();
	CFilteredToDoCtrl* pTDC = NewToDoCtrl();
	CHoldRedraw hr(pTDC->GetSafeHwnd());
	
	// handles simple and storage tasklists
	// we use szFilePath because it may be storage Info not a true path
	TSM_TASKLISTINFO storageInfo;
	TDC_FILE nOpen = OpenTaskList(pTDC, sFilePath, &storageInfo);
	
	if (nOpen == TDCO_SUCCESS)
	{
		int nTDC = AddToDoCtrl(pTDC, &storageInfo);

		// notify readonly
		CheckNotifyReadonly(nTDC);

		// reload any reminders
		m_reminders.AddToDoCtrl(*pTDC);
		
		// notify user of due tasks if req
		if (bNotifyDueTasks)
			DoDueTaskNotification(nTDC, userPrefs.GetNotifyDueByOnLoad());
		
		// save checkout status
		if (userPrefs.GetAutoCheckOut())
			m_mgrToDoCtrls.SetLastCheckoutStatus(nTDC, pTDC->IsCheckedOut());

		// check for automatic naming when handling WM_ENDSESSION. 
		// so we clear the filename and mark it as modified
		if (IsEndSessionFilePath(sFilePath))
		{
			pTDC->ClearFilePath();
			pTDC->SetModified();
		}
		
		UpdateCaption();
		UpdateStatusbar();
		OnTimerDueItems(nTDC);
		
		// update search
		if (userPrefs.GetRefreshFindOnLoad() && m_findDlg.GetSafeHwnd())
			m_findDlg.RefreshSearch();
	}
	else if (GetTDCCount() >= 1) // only delete if there's another ctrl existing
	{
		pTDC->DestroyWindow();
		delete pTDC;
	}
	else // re-add
	{
		AddToDoCtrl(pTDC);
	}
	
	return nOpen;
}

TDC_FILE CToDoListWnd::OpenTaskList(CFilteredToDoCtrl* pTDC, LPCTSTR szFilePath, TSM_TASKLISTINFO* pInfo)
{
	CString sFilePath(szFilePath);
	CTaskFile tasks;
	TDC_FILE nOpen = TDCO_UNSET;

	TSM_TASKLISTINFO storageInfo;
	TDC_PREPAREPATH nType = PrepareFilePath(sFilePath, &storageInfo);

	// handle bad path
	if ((szFilePath && *szFilePath) && sFilePath.IsEmpty())
		return TDCO_NOTEXIST;

	DOPROGRESS(IDS_LOADINGPROGRESS)

	if (sFilePath.IsEmpty())
	{
		sFilePath = pTDC->GetFilePath(); // ie. reload
	}
	else
	{
		switch (nType)
		{
		case TDCPP_STORAGE:
			{
				CPreferences prefs;

				// retrieve file from storage
				if (m_mgrStorage.RetrieveTasklist(&storageInfo, &tasks, -1, &prefs))
				{
					// handle returned tasks
					if (tasks.GetTaskCount())
					{
						// merge returned tasks with this tasklist
						// TODO

						nOpen = TDCO_SUCCESS;
					}

					// return update storage info
					if (pInfo)
						*pInfo = storageInfo;
				}
				else
					nOpen = TDCO_CANCELLED;
			}
			break;

		case TDCPP_FILE:
			{
				BOOL bSrcControl = m_mgrToDoCtrls.PathSupportsSourceControl(szFilePath);

				pTDC->SetStyle(TDCS_ENABLESOURCECONTROL, bSrcControl);
				pTDC->SetStyle(TDCS_CHECKOUTONLOAD, bSrcControl ? Prefs().GetAutoCheckOut() : FALSE);
			}
			break;

		case TDCPP_NONE:
		default:
			ASSERT(0);
			break;
		}
	}
	
	// has the load already been handled?
	if (nOpen == TDCO_UNSET)
		nOpen = pTDC->Load(sFilePath, tasks);

	if (nOpen == TDCO_SUCCESS)
	{
		// update readonly status
		m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(*pTDC);

		const CPreferencesDlg& userPrefs = Prefs();

		// certain operations cannot be performed on 'storage' tasklists
		if (nType == TDCPP_FILE)
		{
			// archive completed tasks?
			if (!pTDC->IsReadOnly())
			{
				CString sArchivePath;
				BOOL bRemoveFlagged;
				TDC_ARCHIVE nRemove = GetAutoArchiveOptions(szFilePath, sArchivePath, bRemoveFlagged);
			
				if (!sArchivePath.IsEmpty())
					pTDC->ArchiveDoneTasks(sArchivePath, nRemove, bRemoveFlagged);
			}

			if (userPrefs.GetAddFilesToMRU())
				m_mruList.Add(sFilePath);
		}

		if (userPrefs.GetExpandTasksOnLoad())
			pTDC->ExpandTasks(TDCEC_ALL);
		
		// update find dialog with this ToDoCtrl's custom attributes
		UpdateFindDialogCustomAttributes(pTDC);
	}
	else
		pTDC->SetModified(FALSE);

	return nOpen;
}

void CToDoListWnd::CheckNotifyReadonly(int nIndex) const
{
	ASSERT(nIndex != -1);

	const CPreferencesDlg& userPrefs = Prefs();

	if (nIndex >= 0 && userPrefs.GetNotifyReadOnly())
	{
		CEnString sMessage;
		CString sDisplayPath = m_mgrToDoCtrls.GetDisplayPath(nIndex);
		CString sFilePath = m_mgrToDoCtrls.GetFilePath(nIndex);
		const CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);
		
		if (CDriveInfo::IsReadonlyPath(sFilePath) > 0)
			sMessage.Format(IDS_OPENREADONLY, sDisplayPath);
		
		else if (!userPrefs.GetEnableSourceControl() && m_mgrToDoCtrls.IsSourceControlled(nIndex))
			sMessage.Format(IDS_OPENSOURCECONTROLLED, sDisplayPath);
		
		else if (tdc.CompareFileFormat() == TDCFF_NEWER)
			sMessage.Format(IDS_OPENNEWER, sDisplayPath);
		
		if (!sMessage.IsEmpty())
			MessageBox(sMessage, IDS_OPENTASKLIST_TITLE);
	}
}

void CToDoListWnd::UpdateFindDialogCustomAttributes(const CFilteredToDoCtrl* pTDC)
{
	if (pTDC == NULL && GetTDCCount() == 0)
		return; // nothing to do

	CTDCCustomAttribDefinitionArray aTDCAttribDefs, aAllAttribDefs;

	// all tasklists
	int nTDC = GetTDCCount();

	while (nTDC--)
	{
		const CFilteredToDoCtrl& tdc = GetToDoCtrl(nTDC);
		tdc.GetCustomAttributeDefs(aTDCAttribDefs);

		CTDCCustomAttributeHelper::AppendUniqueAttributes(aTDCAttribDefs, aAllAttribDefs);
	}
	
	// active tasklist
	if (pTDC == NULL)
	{
		ASSERT(GetTDCCount() > 0);
		pTDC = &GetToDoCtrl();
	}

	ASSERT (pTDC);
	pTDC->GetCustomAttributeDefs(aTDCAttribDefs);

	// do the update
	m_findDlg.SetCustomAttributes(aTDCAttribDefs, aAllAttribDefs);
}

BOOL CToDoListWnd::DoDueTaskNotification(int nDueBy)
{
	return DoDueTaskNotification(GetSelToDoCtrl(), nDueBy);
}

BOOL CToDoListWnd::DoDueTaskNotification(int nTDC, int nDueBy)
{
	// check userPrefs
	if (nDueBy == -1)
		return TRUE; // nothing to do
	
	if (nTDC != -1 && !VerifyTaskListOpen(nTDC, FALSE))
		return TRUE; // no error. user cancelled

	CFilteredToDoCtrl& tdc = m_mgrToDoCtrls.GetToDoCtrl(nTDC);

	const CPreferencesDlg& userPrefs = Prefs();
	
	// preferences
	BOOL bParentTitleCommentsOnly = userPrefs.GetExportParentTitleCommentsOnly();
	BOOL bDueTaskTitlesOnly = userPrefs.GetDueTaskTitlesOnly();
	CString sStylesheet = userPrefs.GetDueTaskStylesheet();
	BOOL bTransform = FileMisc::FileExists(sStylesheet);
	BOOL bHtmlNotify = userPrefs.GetDisplayDueTasksInHtml();
	
	DWORD dwFlags = TDCGTF_FILENAME;
	
	if (bHtmlNotify)
		dwFlags |= TDCGTF_HTMLCOMMENTS;

	if (bTransform)
		dwFlags |= TDCGTF_TRANSFORM;

	// due task notification preference overrides Export preference
	if (bDueTaskTitlesOnly)
		dwFlags |= TDCGTF_TITLESONLY;

	else if (bParentTitleCommentsOnly)
		dwFlags |= TDCGTF_PARENTTITLECOMMENTSONLY;

	TDC_GETTASKS nFilter = TDCGT_DUE;
	UINT nIDDueBy = IDS_DUETODAY;
	
	switch (nDueBy)
	{
	case PFP_DUETODAY:
		break; // done
		
	case PFP_DUETOMORROW:
		nIDDueBy = IDS_DUETOMORROW;
		nFilter = TDCGT_DUETOMORROW;
		break;
		
	case PFP_DUETHISWEEK:
		nIDDueBy = IDS_DUETHISWEEK;
		nFilter = TDCGT_DUETHISWEEK;
		break;
		
	case PFP_DUENEXTWEEK:
		nIDDueBy = IDS_DUENEXTWEEK;
		nFilter = TDCGT_DUENEXTWEEK;
		break;
		
	case PFP_DUETHISMONTH:
		nIDDueBy = IDS_DUETHISMONTH;
		nFilter = TDCGT_DUETHISMONTH;
		break;
		
	case PFP_DUENEXTMONTH:
		nIDDueBy = IDS_DUENEXTMONTH;
		nFilter = TDCGT_DUENEXTMONTH;
		break;
		
	default:
		ASSERT (0);
		return FALSE;
	}
	
	TDCGETTASKS filter(nFilter, dwFlags);
	filter.sAllocTo = userPrefs.GetDueTaskPerson();

	CTaskFile tasks;

	if (!tdc.GetTasks(tasks, filter))
		return FALSE;
	
	// set an appropriate title
	tasks.SetReportAttributes(CEnString(nIDDueBy));
	tasks.SetCharSet(userPrefs.GetHtmlCharSet());

	// save intermediate tasklist to file as required
	LogIntermediateTaskList(tasks, tdc.GetFilePath());

	// nasty hack to prevent exporters adding space for notes
	BOOL bSpaceForNotes = userPrefs.GetExportSpaceForNotes();

	if (bSpaceForNotes)
		CPreferences().WriteProfileInt(PREF_KEY, _T("ExportSpaceForNotes"), FALSE);
	
	// different file for each
	CString sTempFile;

	sTempFile.Format(_T("ToDoList.due.%d"), nTDC);
	sTempFile = FileMisc::GetTempFileName(sTempFile, bHtmlNotify ? _T("html") : _T("txt"));
			
	BOOL bRes = FALSE;
	
	if (bHtmlNotify) // display in browser?
	{
		if (bTransform)
			bRes = tasks.TransformToFile(sStylesheet, sTempFile, userPrefs.GetHtmlCharSet());
		else
			bRes = m_mgrImportExport.ExportTaskListToHtml(&tasks, sTempFile);
	}
	else
		bRes = m_mgrImportExport.ExportTaskListToText(&tasks, sTempFile);

	if (bRes)
	{
		Show(FALSE);
		m_mgrToDoCtrls.ShowDueTaskNotification(nTDC, sTempFile, bHtmlNotify);
	}

	// undo hack
	if (bSpaceForNotes)
		CPreferences().WriteProfileInt(PREF_KEY, _T("ExportSpaceForNotes"), TRUE);
	
	return TRUE;
}

CString CToDoListWnd::GetTitle() 
{
	static CString sTitle(_T("ToDoList 6.8.10 Feature Release"));
	CLocalizer::IgnoreString(sTitle);

	return sTitle;
}

void CToDoListWnd::OnAbout() 
{
	CString sVersion;
	sVersion.Format(_T("<b>%s</b> (c) AbstractSpoon 2003-14"), GetTitle());
	CLocalizer::IgnoreString(sVersion);

	CAboutDlg dialog(IDR_MAINFRAME, 
					ABS_LISTCOPYRIGHT, 
					sVersion,
					CEnString(IDS_ABOUTHEADING), 
					CEnString(IDS_ABOUTCONTRIBUTION), 
					// format the link into the license so that it is not translated
					CEnString(IDS_LICENSE, _T("\"http://www.opensource.org/licenses/eclipse-1.0.php\"")), 
					1, 1, 12, 1, 250);
	
	dialog.DoModal();
}

void CToDoListWnd::OnPreferences() 
{
	DoPreferences();
}

void CToDoListWnd::DoPreferences(int nInitPage) 
{
	// take a copy of current userPrefs to check changes against
	const CPreferencesDlg curPrefs; 
	
	// kill timers
	SetTimer(TIMER_READONLYSTATUS, FALSE);
	SetTimer(TIMER_TIMESTAMPCHANGE, FALSE);
	SetTimer(TIMER_CHECKOUTSTATUS, FALSE);
	SetTimer(TIMER_AUTOSAVE, FALSE);
	SetTimer(TIMER_TIMETRACKING, FALSE);
	SetTimer(TIMER_AUTOMINIMIZE, FALSE);

	// restore translation of dynamic menu items shortcut prefs
	EnableDynamicMenuTranslation(TRUE);
	
	ASSERT(m_pPrefs);
	UINT nRet = m_pPrefs->DoModal(nInitPage);
	
	// updates userPrefs
	RedrawWindow();
	ResetPrefs();

	const CPreferencesDlg& userPrefs = Prefs();
	
	if (nRet == IDOK)
	{
		SetUITheme(userPrefs.GetUITheme());

		// lightbox mgr
		if (curPrefs.GetEnableLightboxMgr() != userPrefs.GetEnableLightboxMgr())
		{
			if (userPrefs.GetEnableLightboxMgr())
				CLightBoxMgr::Initialize(this, m_theme.crAppBackDark);
			else
				CLightBoxMgr::Release();
		}

		// mark all todoctrls as needing refreshing
		m_mgrToDoCtrls.SetAllNeedPreferenceUpdate(TRUE); 
		
		// delete fonts if they appear to have changed
		// and recreate in UpdateToDoCtrlPrefs
		CString sFaceName;
		int nFontSize;
		
		if (!userPrefs.GetTreeFont(sFaceName, nFontSize) || !GraphicsMisc::SameFont(m_fontTree, sFaceName, nFontSize))
			GraphicsMisc::VerifyDeleteObject(m_fontTree);
		
		if (!userPrefs.GetCommentsFont(sFaceName, nFontSize) || !GraphicsMisc::SameFont(m_fontComments, sFaceName, nFontSize))
			GraphicsMisc::VerifyDeleteObject(m_fontComments);
		
		BOOL bResizeDlg = FALSE;
		
		// topmost
		BOOL bTopMost = (userPrefs.GetAlwaysOnTop() && !IsZoomed());
		
		SetWindowPos(bTopMost ? &wndTopMost : &wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
		
		// tray icon
		m_trayIcon.ShowTrayIcon(userPrefs.GetUseSysTray());
		
		// support for .tdl
		if (curPrefs.GetEnableTDLExtension() != userPrefs.GetEnableTDLExtension())
		{
			CFileRegister filereg(_T("tdl"), _T("tdl_Tasklist"));
			
			if (userPrefs.GetEnableTDLExtension())
				filereg.RegisterFileType(_T("Tasklist"), 0);
			else
				filereg.UnRegisterFileType();
		}

		// support for tdl web protocol
		if (curPrefs.GetEnableTDLProtocol() != userPrefs.GetEnableTDLProtocol())
			EnableTDLProtocol(userPrefs.GetEnableTDLProtocol());

		// language
		CString sLangFile = userPrefs.GetLanguageFile();
		BOOL bAdd2Dict = userPrefs.GetEnableAdd2Dictionary();

		if (UpdateLanguageTranslationAndRestart(sLangFile, bAdd2Dict, curPrefs))
		{
			DoExit(TRUE);
			return;
		}

		// default task attributes
		UpdateDefaultTaskAttributes(userPrefs);

		// source control
		BOOL bSourceControl = userPrefs.GetEnableSourceControl();
		
		if (curPrefs.GetEnableSourceControl() != bSourceControl ||
			curPrefs.GetSourceControlLanOnly() != userPrefs.GetSourceControlLanOnly())
		{
			// update all open files to ensure they're in the right state
			int nCtrl = GetTDCCount();
			
			while (nCtrl--)
			{
				CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
				
				// check files in if we're disabling sc and this file is
				// checked out. however although we 
				// are checking in, the file cannot be edited by the user
				// until they remove the file from under source control
				if (!bSourceControl && tdc.IsCheckedOut())
				{
					if (tdc.IsModified())
						tdc.Save();
					
					tdc.CheckIn();
				}
				// else checkout if we're enabling and auto-checkout is also enabled
				else if (bSourceControl)
				{
					// there can be two reasons for wanting to check out a file
					// either the autocheckout preference is set or its a local
					// file which is not checked out but has been modified and source
					// control now covers all files in which case we save it first
					BOOL bPathSupports = m_mgrToDoCtrls.PathSupportsSourceControl(nCtrl);
					BOOL bNeedsSave = bPathSupports && !tdc.IsCheckedOut() && tdc.IsModified();
					BOOL bWantCheckOut = bNeedsSave || (bPathSupports && userPrefs.GetAutoCheckOut());
					
					if (bNeedsSave)
						tdc.Save(); // save silently
					
					tdc.SetStyle(TDCS_ENABLESOURCECONTROL, bPathSupports);
					tdc.SetStyle(TDCS_CHECKOUTONLOAD, bPathSupports && userPrefs.GetAutoCheckOut());
					
					if (bWantCheckOut && !tdc.IsCheckedOut())
						tdc.CheckOut();
				}

				// re-sync
				m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nCtrl);				
				m_mgrToDoCtrls.RefreshModifiedStatus(nCtrl);
				m_mgrToDoCtrls.RefreshLastModified(nCtrl);
				m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
			}
		}
		
		m_toolbar.GetToolBarCtrl().HideButton(ID_TOOLS_TOGGLECHECKIN, !bSourceControl);

		// check box in front of task title.
		// this is tricky because the checkbox won't display if the completion
		// column is visible. ie. the completion column take precedence.
		// so if the user has just turned on the checkbox in front of
		// the task title then we need to turn off the completion column
		// on all those tasklists currently showing it. But we only need to do
		// this on those tasklists which are managing their own columns else
		// it will be handled when we update the preferences in due course.
		if (userPrefs.GetTreeCheckboxes() && !curPrefs.GetTreeCheckboxes())
		{
			int nCtrl = GetTDCCount();
				
			while (nCtrl--)
			{
				if (m_mgrToDoCtrls.HasOwnColumns(nCtrl))
				{
					CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);

					if (tdc.IsColumnShowing(TDCC_DONE))
					{
						CTDCColumnIDArray aColumns;
						int nCol = tdc.GetVisibleColumns(aColumns);

						while (nCol--)
						{
							if (aColumns[nCol] == TDCC_DONE)
							{
								aColumns.RemoveAt(nCol);
								break;
							}
						}

						tdc.SetVisibleColumns(aColumns);
					}
				}
			}	
		}

		// same again for task icons
		if (userPrefs.GetTreeTaskIcons() && !curPrefs.GetTreeTaskIcons())
		{
			int nCtrl = GetTDCCount();
				
			while (nCtrl--)
			{
				if (m_mgrToDoCtrls.HasOwnColumns(nCtrl))
				{
					CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);

					if (tdc.IsColumnShowing(TDCC_ICON))
					{
						CTDCColumnIDArray aColumns;
						int nCol = tdc.GetVisibleColumns(aColumns);

						while (nCol--)
						{
							if (aColumns[nCol] == TDCC_ICON)
							{
								aColumns.RemoveAt(nCol);
								break;
							}
						}

						tdc.SetVisibleColumns(aColumns);
					}
				}
			}	
		}

		// menu icons
		UINT nPrevID = MapNewTaskPos(curPrefs.GetNewTaskPos(), FALSE);
		m_mgrMenuIcons.ChangeImageID(nPrevID, GetNewTaskCmdID());

		nPrevID = MapNewTaskPos(curPrefs.GetNewSubtaskPos(), TRUE);
		m_mgrMenuIcons.ChangeImageID(nPrevID, GetNewSubtaskCmdID());
		
		// reload menu 
		LoadMenubar();
		
		// tab bar
		bResizeDlg |= (curPrefs.GetAutoHideTabbar() != userPrefs.GetAutoHideTabbar());
		
		if (curPrefs.GetStackTabbarItems() != userPrefs.GetStackTabbarItems())
		{
			BOOL bStackTabbar = userPrefs.GetStackTabbarItems();
			
			bResizeDlg = TRUE;
			m_tabCtrl.ModifyStyle(bStackTabbar ? 0 : TCS_MULTILINE, bStackTabbar ? TCS_MULTILINE : 0);
		}
		else
			m_tabCtrl.Invalidate(); // handle priority colour changes
			
		// visible filter controls
		if (m_bShowFilterBar)
			bResizeDlg = TRUE;

		BOOL bEnableMultiSel = userPrefs.GetMultiSelFilters();
		BOOL bPrevMultiSel = curPrefs.GetMultiSelFilters();

		if (bPrevMultiSel != bEnableMultiSel)
		{
			m_filterBar.EnableMultiSelection(bEnableMultiSel);

			// if it was was previously multisel (but not now) then
			// refresh the filter because we may have gone from
			// multiple selection down to only one
			OnViewRefreshfilter();
		}

		m_filterBar.ShowDefaultFilters(userPrefs.GetShowDefaultFilters());

		// title filter option
		PUIP_MATCHTITLE nTitleOption = userPrefs.GetTitleFilterOption();
		PUIP_MATCHTITLE nPrevTitleOption = curPrefs.GetTitleFilterOption();

		if (nPrevTitleOption != nTitleOption)
			OnViewRefreshfilter();

		// inherited parent task attributes for new tasks
		CTDCAttributeArray aParentAttrib;
		BOOL bUpdateAttrib;

		userPrefs.GetParentAttribsUsed(aParentAttrib, bUpdateAttrib);
		CFilteredToDoCtrl::SetInheritedParentAttributes(aParentAttrib, bUpdateAttrib);
				
		// hotkey
		UpdateGlobalHotkey();
		
		// time periods
		CTimeHelper::SetHoursInOneDay(userPrefs.GetHoursInOneDay());
		CTimeHelper::SetDaysInOneWeek(userPrefs.GetDaysInOneWeek());
		CDateHelper::SetWeekendDays(userPrefs.GetWeekendDays());
		
		RefreshTabOrder();
		
		// time tracking
		if (curPrefs.GetTrackNonActiveTasklists() != userPrefs.GetTrackNonActiveTasklists())
			RefreshPauseTimeTracking();
		
		UpdateCaption();
		UpdateTabSwitchTooltip();

		// colours
		if (m_findDlg.GetSafeHwnd())
			m_findDlg.RefreshUserPreferences();
		
		// active tasklist userPrefs
		UpdateToDoCtrlPreferences();

		// then refresh filter bar for any new default cats, statuses, etc
		m_filterBar.RefreshFilterControls(GetToDoCtrl());
		
		if (bResizeDlg)
			Resize();

		// Stickies Support
		CString sStickiesPath;

		if (userPrefs.GetUseStickies(sStickiesPath))
			VERIFY(m_reminders.UseStickies(TRUE, sStickiesPath));
		else
			m_reminders.UseStickies(FALSE);

		// Recently modified period
		CFilteredToDoCtrl::SetRecentlyModifiedPeriod(userPrefs.GetRecentlyModifiedPeriod());
		
		// don't ask me for the full details on this but it seems as
		// though the CSysImageList class is waiting to process a 
		// message before we can switch image sizes so we put it
		// right at the end after everything is done.
		Misc::ProcessMsgLoop();
		AppendTools2Toolbar(TRUE);
	}
	
	// finally set or terminate the various status check timers
	SetTimer(TIMER_READONLYSTATUS, (userPrefs.GetReadonlyReloadOption() != RO_NO));
	SetTimer(TIMER_TIMESTAMPCHANGE, (userPrefs.GetTimestampReloadOption() != RO_NO));
	SetTimer(TIMER_AUTOSAVE, userPrefs.GetAutoSaveFrequency());
	SetTimer(TIMER_CHECKOUTSTATUS, (userPrefs.GetCheckoutOnCheckin() || userPrefs.GetAutoCheckinFrequency()));
	SetTimer(TIMER_TIMETRACKING, TRUE);
	SetTimer(TIMER_AUTOMINIMIZE, userPrefs.GetAutoMinimizeFrequency());

	// re-disable dynamic menu translation
	EnableDynamicMenuTranslation(FALSE);
}

BOOL CToDoListWnd::UpdateLanguageTranslationAndRestart(const CString& sLangFile, BOOL bAdd2Dict, 
													   const CPreferencesDlg& curPrefs)
{
	BOOL bDefLang = (CTDLLanguageComboBox::GetDefaultLanguage() == sLangFile);
		
	if (curPrefs.GetLanguageFile() != sLangFile)
	{
		if (bDefLang || FileMisc::FileExists(sLangFile))
		{
			// if the language file exists and has changed then inform the user that they to restart
			// Note: restarting will also handle bAdd2Dict
			if (MessageBox(IDS_RESTARTTOCHANGELANGUAGE, 0, MB_YESNO) == IDYES)
			{
				return TRUE;
			}
		}
	}
	// else if the the user wants to enable/disable 'Add2Dictionary'
	else if (bAdd2Dict != curPrefs.GetEnableAdd2Dictionary())
	{
		if (bAdd2Dict && !bDefLang)
		{
			CLocalizer::SetTranslationOption(ITTTO_ADD2DICTIONARY);
			TranslateUIElements();
		}
		else // disable 'Add2Dictionary'
			CLocalizer::SetTranslationOption(ITTTO_TRANSLATEONLY);
	}

	// no need to restart
	return FALSE;
}

void CToDoListWnd::UpdateDefaultTaskAttributes(const CPreferencesDlg& prefs)
{
	m_tdiDefault.sTitle = CEnString(IDS_TASK);
	m_tdiDefault.color = prefs.GetDefaultColor();
	m_tdiDefault.dateStart.m_dt = prefs.GetAutoDefaultStartDate() ? -1 : 0;
	m_tdiDefault.sAllocBy = prefs.GetDefaultAllocBy();
	m_tdiDefault.sStatus = prefs.GetDefaultStatus();
	m_tdiDefault.dTimeEstimate = prefs.GetDefaultTimeEst(m_tdiDefault.nTimeEstUnits);
	m_tdiDefault.dTimeSpent = prefs.GetDefaultTimeSpent(m_tdiDefault.nTimeSpentUnits);
	m_tdiDefault.nTimeSpentUnits = m_tdiDefault.nTimeEstUnits; // to match
	m_tdiDefault.sCreatedBy = prefs.GetDefaultCreatedBy();
	m_tdiDefault.dCost = prefs.GetDefaultCost();
	m_tdiDefault.sCommentsTypeID = prefs.GetDefaultCommentsFormat();
	m_tdiDefault.nPriority = prefs.GetDefaultPriority();
	m_tdiDefault.nRisk = prefs.GetDefaultRisk();
	
	prefs.GetDefaultCategories(m_tdiDefault.aCategories);
	prefs.GetDefaultAllocTo(m_tdiDefault.aAllocTo);
	prefs.GetDefaultTags(m_tdiDefault.aTags);
	
	m_mgrImportExport.SetDefaultTaskAttributes(m_tdiDefault);
}

BOOL CToDoListWnd::LoadMenubar()
{
	m_menubar.DestroyMenu();
	
	if (!m_menubar.LoadMenu(IDR_MAINFRAME))
		return FALSE;

	SetMenu(&m_menubar);
	m_hMenuDefault = m_menubar;
	
#ifdef _DEBUG
	// add menu option to simulate WM_QUERYENDSESSION
	m_menubar.InsertMenu((UINT)-1, MFT_STRING, ID_DEBUGENDSESSION, _T("EndSession"));
	CLocalizer::EnableTranslation(ID_DEBUGENDSESSION, FALSE);

	// and option to display setup dialog on demand
	m_menubar.InsertMenu((UINT)-1, MFT_STRING, ID_DEBUGSHOWSETUPDLG, _T("Show Setup"));
	CLocalizer::EnableTranslation(ID_DEBUGSHOWSETUPDLG, FALSE);
#endif

	if (Prefs().GetShowTasklistCloseButton()) 
		m_menubar.AddMDIButton(MEB_CLOSE, ID_CLOSE);

	if (CThemed::IsThemeActive())
		m_menubar.SetBackgroundColor(m_theme.crMenuBack);

	DrawMenuBar();

	// disable translation of dynamic menus
	EnableDynamicMenuTranslation(FALSE);

	return TRUE;
}

void CToDoListWnd::EnableDynamicMenuTranslation(BOOL bEnable)
{
	CLocalizer::EnableTranslation(ID_FILE_MRU_FIRST, ID_FILE_MRU_LAST, bEnable);
	CLocalizer::EnableTranslation(ID_WINDOW1, ID_WINDOW16, bEnable);
	CLocalizer::EnableTranslation(ID_TOOLS_USERTOOL1, ID_TOOLS_USERTOOL16, bEnable);
	CLocalizer::EnableTranslation(ID_FILE_OPEN_USERSTORAGE1, ID_FILE_OPEN_USERSTORAGE16, bEnable);
	CLocalizer::EnableTranslation(ID_FILE_SAVE_USERSTORAGE1, ID_FILE_SAVE_USERSTORAGE16, bEnable);
	CLocalizer::EnableTranslation(ID_TRAYICON_SHOWDUETASKS1, ID_TRAYICON_SHOWDUETASKS20, bEnable);
}

void CToDoListWnd::UpdateGlobalHotkey()
{
	static DWORD dwPrevHotkey = 0;
	DWORD dwHotkey = Prefs().GetGlobalHotkey();
	
	if (dwPrevHotkey == dwHotkey)
		return;
	
	if (dwHotkey == 0) // disabled
		::UnregisterHotKey(*this, 1);
	else
	{
		// map modifiers to those wanted by RegisterHotKey
		DWORD dwPrefMods = HIWORD(dwHotkey);
		DWORD dwVKey = LOWORD(dwHotkey);
		
		DWORD dwMods = (dwPrefMods & HOTKEYF_ALT) ? MOD_ALT : 0;
		dwMods |= (dwPrefMods & HOTKEYF_CONTROL) ? MOD_CONTROL : 0;
		dwMods |= (dwPrefMods & HOTKEYF_SHIFT) ? MOD_SHIFT : 0;
		
		RegisterHotKey(*this, 1, dwMods, dwVKey);
	}

	dwPrevHotkey = dwHotkey;
}

void CToDoListWnd::RefreshPauseTimeTracking()
{
	// time tracking
	int nCtrl = GetTDCCount();
	int nSel = GetSelToDoCtrl();
	BOOL bTrackActiveOnly = !Prefs().GetTrackNonActiveTasklists();
	
	while (nCtrl--)
	{
		BOOL bSel = (nCtrl == nSel);
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
		
		tdc.PauseTimeTracking(bSel ? FALSE : bTrackActiveOnly);
	}
}

BOOL CToDoListWnd::ProcessStartupOptions(const TDCSTARTUP& startup)
{
	// 1. check if we can handle a task link
	if (startup.HasFlag(TLD_TASKLINK))
	{
		CStringArray aFiles;

		if (startup.GetFilePaths(aFiles))
		{
			CString sFile;
			DWORD dwTaskID = 0;

			CFilteredToDoCtrl::ParseTaskLink(aFiles[0], dwTaskID, sFile);

			return DoTaskLink(sFile, dwTaskID);
		}

		// else
		return FALSE;
	}

	// 2. execute a command
	if (startup.HasCommandID())
	{
		SendMessage(WM_COMMAND, MAKEWPARAM(startup.GetCommandID(), 0), 0);
		return TRUE;
	}

	// 3. try open/import file
	if (startup.HasFilePath())
	{
		int nFirstSel = -1;
		BOOL bSuccess = FALSE;

		CStringArray aFilePaths;
		int nNumFiles = startup.GetFilePaths(aFilePaths);

		for (int nFile = 0; nFile < nNumFiles; nFile++)
		{
			const CString& sFilePath = aFilePaths[nFile];
			
			if (startup.HasFlag(TLD_IMPORTFILE))
			{
				if (ImportFile(sFilePath, TRUE))
				{
					bSuccess = TRUE;
				}
			}
			else
			{
				BOOL bCanDelayLoad = Prefs().GetEnableDelayedLoading();

				// open the first tasklist fully and the rest delayed
				if (!bSuccess || !Prefs().GetEnableDelayedLoading())
				{
					if (OpenTaskList(sFilePath, FALSE) == TDCO_SUCCESS)
					{
						bSuccess = TRUE;
					}
				}
				else if (DelayOpenTaskList(sFilePath) == TDCO_SUCCESS)
				{
					bSuccess = TRUE;
				}
			}

			// snapshot the first success for subsequent selection
			if (bSuccess && (nFirstSel == -1))
				nFirstSel = GetSelToDoCtrl();
		}
		
		// exit on failure
		if (!bSuccess)
			return FALSE;

		// set selection to first tasklist loaded
		ASSERT((nFirstSel != -1) && (nFirstSel < GetTDCCount()));

		SelectToDoCtrl(nFirstSel, FALSE);
	}
	
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	BOOL bRes = FALSE;
	
	if (startup.HasFlag(TLD_NEWTASK))
	{
		CEnString sNewTask;
		BOOL bEditTask = FALSE;
		
		// we edit the task name if no name was supplied
		if (!startup.GetNewTask(sNewTask))
		{
			sNewTask.LoadString(IDS_TASK);
			bEditTask = TRUE;
		}
		
		// do we have a parent task ?
		if (tdc.SelectTask(startup.GetParentTaskID()))
		{
			bRes = CreateNewTask(sNewTask, TDC_INSERTATTOPOFSELTASK, FALSE);
		}
		// or a sibling task ?
		else if (tdc.SelectTask(startup.GetSiblingTaskID()))
		{
			bRes = CreateNewTask(sNewTask, TDC_INSERTAFTERSELTASK, FALSE);
		}	
		else
		{
			bRes = CreateNewTask(sNewTask, TDC_INSERTATTOP, FALSE);
		}
	
		// creation date
		double dDate;

		if (startup.GetCreationDate(dDate))
			tdc.SetSelectedTaskDate(TDCD_CREATE, dDate);
		
		// edit task title?
		if (bRes && bEditTask)
			PostMessage(WM_COMMAND, ID_EDIT_TASKTEXT);
	}
	else if (startup.GetTaskID())
	{
		bRes = tdc.SelectTask(startup.GetTaskID());
	}
	else // works on the currently selected item(s)
	{
		bRes = (tdc.GetSelectedCount() > 0);
	}

	// rest of task attributes
	if (bRes)
	{
		CStringArray aItems;
		CString sItem;
		int nItem;
		double dItem;
		
		if (startup.GetComments(sItem))
			tdc.SetSelectedTaskComments(sItem, sItem);
		
		if (startup.GetExternalID(sItem))
			tdc.SetSelectedTaskExtID(sItem);
		
		if (startup.GetVersion(sItem))
			tdc.SetSelectedTaskVersion(sItem);
		
		if (startup.GetAllocTo(aItems))
			tdc.SetSelectedTaskAllocTo(aItems);
		
		if (startup.GetAllocBy(sItem))
			tdc.SetSelectedTaskAllocBy(sItem);
		
		if (startup.GetCategories(aItems))
			tdc.SetSelectedTaskCategories(aItems);
		
		if (startup.GetTags(aItems))
			tdc.SetSelectedTaskTags(aItems);
		
		if (startup.GetStatus(sItem))
			tdc.SetSelectedTaskStatus(sItem);
		
		if (startup.GetFileRef(sItem))
			tdc.SetSelectedTaskFileRef(sItem);

		if (startup.GetPriority(nItem))
			tdc.SetSelectedTaskPriority(nItem);

		if (startup.GetRisk(nItem))
			tdc.SetSelectedTaskRisk(nItem);

		if (startup.GetPercentDone(nItem))
			tdc.SetSelectedTaskPercentDone(nItem);

		if (startup.GetCost(dItem))
			tdc.SetSelectedTaskCost(dItem);

		if (startup.GetTimeEst(dItem))
			tdc.SetSelectedTaskTimeEstimate(dItem); // in hours

		if (startup.GetTimeSpent(dItem))
			tdc.SetSelectedTaskTimeSpent(dItem); // in hours

		if (startup.GetStartDate(dItem))
			tdc.SetSelectedTaskDate(TDCD_START, dItem);

		if (startup.GetDueDate(dItem))
			tdc.SetSelectedTaskDate(TDCD_DUE, dItem);

		if (startup.GetDoneDate(dItem))
			tdc.SetSelectedTaskDate(TDCD_DONE, dItem);
	}

	return bRes;
}

BOOL CToDoListWnd::OnCopyData(CWnd* /*pWnd*/, COPYDATASTRUCT* pCopyDataStruct)
{
	BOOL bRes = FALSE;

	switch (pCopyDataStruct->dwData)
	{
	case TDL_STARTUP:
		{
			ASSERT(pCopyDataStruct->cbData == sizeof(TDCSTARTUP));

			const TDCSTARTUP* pStartup = (TDCSTARTUP*)(pCopyDataStruct->lpData);

			if (pStartup)
				bRes = ProcessStartupOptions(*pStartup);
		}
		break;
	}

	return bRes; 
}

BOOL CToDoListWnd::ImportFile(LPCTSTR szFilePath, BOOL bSilent)
{
	int nImporter = m_mgrImportExport.FindImporter(szFilePath);

	if (nImporter == -1)
		return FALSE;

	CTaskFile tasks;
		
	m_mgrImportExport.ImportTaskList(szFilePath, &tasks, nImporter, bSilent);
		
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
		
	if (tdc.InsertTasks(tasks, TDC_INSERTATTOP))
		UpdateCaption();

	return TRUE;
}

void CToDoListWnd::OnEditCopy() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	tdc.Flush();
	tdc.CopySelectedTask();
}

void CToDoListWnd::OnEditCut() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	tdc.Flush();
	tdc.CutSelectedTask();
}

void CToDoListWnd::OnUpdateEditCut(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && nSelCount);	
}

void CToDoListWnd::OnEditPasteSub() 
{
	CWaitCursor wait;
	GetToDoCtrl().PasteTasks(TDCP_ONSELTASK);
}

void CToDoListWnd::OnUpdateEditPasteSub(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.CanPaste() && nSelCount == 1);	
}

void CToDoListWnd::OnEditPasteAfter() 
{
	CWaitCursor wait;
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	if (nSelCount == 0)
		tdc.PasteTasks(TDCP_ATBOTTOM);
	else
		tdc.PasteTasks(TDCP_BELOWSELTASK);
}

void CToDoListWnd::OnUpdateEditPasteAfter(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	// modify the text appropriately if the tasklist is empty
	if (nSelCount == 0)
		pCmdUI->SetText(CEnString(IDS_PASTETOPLEVELTASK));
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.CanPaste());	
}

void CToDoListWnd::OnEditPasteAsRef() 
{
	CWaitCursor wait;
	GetToDoCtrl().PasteTasks(TDCP_ONSELTASK, TRUE);
}

void CToDoListWnd::OnUpdateEditPasteAsRef(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	//int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.CanPaste()/* && nSelCount == 1*/);	
}

void CToDoListWnd::OnEditCopyastext() 
{
	CopySelectedTasksToClipboard(TDCTC_ASTEXT);
}

void CToDoListWnd::OnEditCopyashtml() 
{
	CopySelectedTasksToClipboard(TDCTC_ASHTML);
}

void CToDoListWnd::CopySelectedTasksToClipboard(TDC_TASKS2CLIPBOARD nAsFormat)
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.Flush();
	
	BOOL bParentTitleCommentsOnly = Prefs().GetExportParentTitleCommentsOnly();
	DWORD dwFlags = (bParentTitleCommentsOnly ? TDCGTF_PARENTTITLECOMMENTSONLY : 0);
	
	CTaskFile tasks;
	CString sTasks;
	tdc.GetSelectedTasks(tasks, TDCGETTASKS(TDCGT_ALL, dwFlags));
	
	switch (nAsFormat)
	{	
	case TDCTC_ASHTML:
		sTasks = m_mgrImportExport.ExportTaskListToHtml(&tasks);
		break;
		
	case TDCTC_ASTEXT:
		sTasks = m_mgrImportExport.ExportTaskListToText(&tasks);
		break;
		
	case TDCTC_ASLINK:
		sTasks.Format(_T("tdl://%ld"), tdc.GetSelectedTaskID());
		break;
		
	case TDCTC_ASDEPENDS:
		sTasks.Format(_T("%ld"), tdc.GetSelectedTaskID());
		break;
		
	case TDCTC_ASLINKFULL:
		sTasks.Format(_T("tdl://%s?%ld"), 
						tdc.GetFilePath(),
						tdc.GetSelectedTaskID());
		sTasks.Replace(_T(" "), _T("%20"));
		break;
		
	case TDCTC_ASDEPENDSFULL:
		sTasks.Format(_T("%s?%ld"), 
						tdc.GetFilePath(),
						tdc.GetSelectedTaskID());
		break;

	case TDCTC_ASPATH:
		sTasks = tdc.GetSelectedTaskPath(TRUE);
		break;
	}

	Misc::CopyTexttoClipboard(sTasks, *this);
}

void CToDoListWnd::OnUpdateEditCopy(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetSelectedCount() > 0);
}

BOOL CToDoListWnd::CanCreateNewTask(TDC_INSERTWHERE nInsertWhere) const
{
	return GetToDoCtrl().CanCreateNewTask(nInsertWhere);
}

void CToDoListWnd::OnUpdateNewtaskAttopSelected(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTATTOPOFSELTASKPARENT));
}

void CToDoListWnd::OnUpdateNewtaskAtbottomSelected(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTATBOTTOMOFSELTASKPARENT));
}

void CToDoListWnd::OnUpdateNewtaskAfterselectedtask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTAFTERSELTASK));
}

void CToDoListWnd::OnUpdateNewtaskBeforeselectedtask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTBEFORESELTASK));
}

void CToDoListWnd::OnUpdateNewsubtaskAttop(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTATTOPOFSELTASK));
}

void CToDoListWnd::OnUpdateNewsubtaskAtBottom(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTATBOTTOMOFSELTASK));
}

void CToDoListWnd::OnUpdateNewtaskAttop(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTATTOP));
}

void CToDoListWnd::OnUpdateNewtaskAtbottom(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(CanCreateNewTask(TDC_INSERTATBOTTOM));
}

void CToDoListWnd::OnMaximizeTasklist() 
{
	// toggle max state on or off
	switch (m_nMaxState)
	{
	case TDCMS_MAXTASKLIST:
		// turn off maximize tasklist by restoring previous max state
		m_nMaxState = m_nPrevMaxState;
		m_nPrevMaxState = TDCMS_NORMAL; // reset
		break;

	case TDCMS_MAXCOMMENTS:
		// turn on maximize tasklist and save previous max state
		m_nMaxState = TDCMS_MAXTASKLIST;
		m_nPrevMaxState = TDCMS_MAXCOMMENTS;
		break;

	case TDCMS_NORMAL:
		// turn on maximize tasklist
		m_nMaxState = TDCMS_MAXTASKLIST;
		m_nPrevMaxState = TDCMS_NORMAL; // reset
		break;
	}
	
	// update active tasklist
	GetToDoCtrl().SetMaximizeState(m_nMaxState);
	Invalidate();

	// and caption
	UpdateCaption();
}

void CToDoListWnd::OnUpdateMaximizeTasklist(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nMaxState == TDCMS_MAXTASKLIST ? 1 : 0);
}

void CToDoListWnd::OnMaximizeComments() 
{
	// toggle max state on or off
	switch (m_nMaxState)
	{
	case TDCMS_MAXCOMMENTS:
		// toggle off maximize comments by restoring previous max state
		m_nMaxState = m_nPrevMaxState;
		m_nPrevMaxState = TDCMS_NORMAL; // reset
		break;

	case TDCMS_MAXTASKLIST:
		// turn on maximize comments and save previous max state
		m_nMaxState = TDCMS_MAXCOMMENTS;
		m_nPrevMaxState = TDCMS_MAXTASKLIST;
		break;

	case TDCMS_NORMAL:
		// turn on maximize comments
		m_nMaxState = TDCMS_MAXCOMMENTS;
		m_nPrevMaxState = TDCMS_NORMAL; // reset
		break;
	}
	
	// update active tasklist
	GetToDoCtrl().SetMaximizeState(m_nMaxState);
	Invalidate();

	// and caption
	UpdateCaption();
}

void CToDoListWnd::OnUpdateMaximizeComments(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nMaxState == TDCMS_MAXCOMMENTS ? 1 : 0);
}

void CToDoListWnd::OnReload() 
{
	int nSel = GetSelToDoCtrl();
	
	if (m_mgrToDoCtrls.GetModifiedStatus(nSel))
	{ 
		if (IDYES != MessageBox(IDS_CONFIRMRELOAD, IDS_CONFIRMRELOAD_TITLE, MB_YESNOCANCEL | MB_DEFBUTTON2))
		{
			return;
		}
	}
	
	// else reload
	ReloadTaskList(nSel);
	RefreshTabOrder();
}

BOOL CToDoListWnd::ReloadTaskList(int nIndex, BOOL bNotifyDueTasks, BOOL bNotifyError)
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);
	
	TDC_FILE nRes = OpenTaskList(&tdc);
	
	if (nRes == TDCO_SUCCESS)
	{
		const CPreferencesDlg& userPrefs = Prefs();
		
		// update file status
		if (userPrefs.GetAutoCheckOut())
			m_mgrToDoCtrls.SetLastCheckoutStatus(nIndex, tdc.IsCheckedOut());
		
		m_mgrToDoCtrls.RefreshLastModified(nIndex);
		m_mgrToDoCtrls.SetModifiedStatus(nIndex, FALSE);
		m_mgrToDoCtrls.UpdateTabItemText(nIndex);
		
		// notify user of due tasks if req
		if (bNotifyDueTasks)
			DoDueTaskNotification(nIndex, userPrefs.GetNotifyDueByOnLoad());
		
		UpdateCaption();
		UpdateStatusbar();
	}
	else if (bNotifyError)
	{
		HandleLoadTasklistError(nRes, tdc.GetFilePath());
	}

	return (nRes == TDCO_SUCCESS);
}

void CToDoListWnd::OnUpdateReload(CCmdUI* pCmdUI) 
{
	int nSel = GetSelToDoCtrl();
	
	pCmdUI->Enable(!m_mgrToDoCtrls.GetFilePath(nSel).IsEmpty());
}

void CToDoListWnd::OnSize(UINT nType, int cx, int cy) 
{
	CFrameWnd::OnSize(nType, cx, cy);
	
	// ensure m_cbQuickFind is positioned correctly
	int nPos = m_toolbar.CommandToIndex(ID_EDIT_FINDTASKS) + 2;

	CRect rNewPos, rOrgPos;
	m_toolbar.GetItemRect(nPos, rNewPos);
	m_toolbar.ClientToScreen(rNewPos);
	m_cbQuickFind.CWnd::GetWindowRect(rOrgPos);

	// check if it needs to be moved
	if (rNewPos.TopLeft() != rOrgPos.TopLeft())
	{
		m_toolbar.ScreenToClient(rNewPos);
		rNewPos.bottom = rNewPos.top + 200;
		m_cbQuickFind.MoveWindow(rNewPos);
	}

	// topmost?
	BOOL bMaximized = (nType == SIZE_MAXIMIZED);
	
	if (nType != SIZE_MINIMIZED)
		Resize(cx, cy, bMaximized);
	
	// if not maximized then set topmost if that's the preference
	BOOL bTopMost = (Prefs().GetAlwaysOnTop() && !bMaximized) ? 1 : 0;
	
	// do nothing if no change
	BOOL bIsTopMost = (GetExStyle() & WS_EX_TOPMOST) ? 1 : 0;
	
	if (bTopMost != bIsTopMost)
		SetWindowPos(bTopMost ? &wndTopMost : &wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}

BOOL CToDoListWnd::CalcToDoCtrlRect(CRect& rect, int cx, int cy, BOOL bMaximized)
{
	if (!cx && !cy)
	{
		CRect rClient;
		GetClientRect(rClient);
		
		cx = rClient.right;
		cy = rClient.bottom;
		bMaximized = IsZoomed();
		
		// check again 
		if (!cx && !cy)
			return FALSE;
	}
	
	CRect rTaskList(0, BEVEL, cx - BEVEL, cy);
	
	// toolbar
	if (m_bShowToolbar) 
 		rTaskList.top += m_toolbar.GetHeight() + TB_VOFFSET;
	
	// resize tabctrl
	CDeferWndMove dwm(0); // dummy
	
	CPoint ptOrg(0, rTaskList.top);
	int nTabHeight = ReposTabBar(dwm, ptOrg, cx, TRUE);
	
	if (nTabHeight)
		rTaskList.top += nTabHeight + 1; // hide the bottom of the tab ctrl
	
	// filter controls
	int nInset = (CThemed().IsNonClientThemed() ? BORDER : BEVEL);
	int nFilterWidth = cx - 2 * nInset;
	int nFilterHeight = m_bShowFilterBar ? m_filterBar.CalcHeight(nFilterWidth) : 0;
	
	if (nFilterHeight)
		rTaskList.top += nFilterHeight;// + 4;
	
	// statusbar
	if (m_bShowStatusBar)
	{
		CRect rStatus;
		m_statusBar.GetWindowRect(rStatus);
		ScreenToClient(rStatus);
		rTaskList.bottom = rStatus.top - BORDER;
	}
	else
		rTaskList.bottom = cy - BORDER;
	
	// shrink slightly so that edit controls do not merge with window border
	rTaskList.DeflateRect(nInset, nInset, nInset, nInset);
	rect = rTaskList;
	
	return TRUE;
}

void CToDoListWnd::Resize(int cx, int cy, BOOL bMaximized)
{
	static int nLastCx = 0, nLastCy = 0;

	if (!cx && !cy)
	{
		CRect rClient;
		GetClientRect(rClient);
		
		cx = rClient.right;
		cy = rClient.bottom;
		bMaximized = IsZoomed();
		
		// check again 
		if (!cx && !cy)
			return;
	}

	if (cx == nLastCx && cy == nLastCy && !GetTDCCount())
		return;

	nLastCx = cx;
	nLastCy = cy;
	
	// resize in one go
	CDlgUnits dlu(*this);
	CDeferWndMove dwm(6);
	CRect rTaskList(0, BEVEL, cx - BEVEL, cy);
	
	// toolbar
	if (m_bShowToolbar) // showing toolbar
		rTaskList.top += m_toolbar.Resize(cx, CPoint(0, TB_VOFFSET)) + TB_VOFFSET;
	
	// resize tabctrl
	CPoint ptOrg(0, rTaskList.top);
	int nTabHeight = ReposTabBar(dwm, ptOrg, cx);
	
	if (nTabHeight)
		rTaskList.top += nTabHeight + 1; // hide the bottom of the tab ctrl
	
	// filter controls
	int nInset = (CThemed().IsNonClientThemed() ? BORDER : BEVEL);
	int nFilterWidth = cx - 2 * nInset;
	int nFilterHeight = m_bShowFilterBar ? m_filterBar.CalcHeight(nFilterWidth) : 0;
	
	dwm.MoveWindow(&m_filterBar, nInset, rTaskList.top, nFilterWidth, nFilterHeight);
	
	if (nFilterHeight)
		rTaskList.top += nFilterHeight;// + 4;
	
	// statusbar has already been automatically resized unless it's invisible
	CRect rStatus(0, cy, cx, cy);

	if (m_bShowStatusBar)
	{
		m_statusBar.GetWindowRect(rStatus);
		ScreenToClient(rStatus);
	}
	else
		dwm.MoveWindow(&m_statusBar, rStatus, FALSE);
	
	// finally the active todoctrl
	if (GetTDCCount())
	{
		if (m_bShowStatusBar)
			rTaskList.bottom = rStatus.top - BORDER;
		else
			rTaskList.bottom = rStatus.bottom - BORDER;
		
		// shrink slightly so that edit controls do not merge with window border
		rTaskList.DeflateRect(nInset, nInset, nInset, nInset);

		dwm.MoveWindow(&GetToDoCtrl(), rTaskList);

#ifdef _DEBUG
		CRect rect;
		CalcToDoCtrlRect(rect, cx, cy, IsZoomed());
		ASSERT(rect == rTaskList);
#endif

	}
}

BOOL CToDoListWnd::WantTasklistTabbarVisible() const 
{ 
	BOOL bWantTabbar = (GetTDCCount() > 1 || !Prefs().GetAutoHideTabbar()); 
	bWantTabbar &= m_bShowTasklistBar;

	return bWantTabbar;
}

int CToDoListWnd::ReposTabBar(CDeferWndMove& dwm, const CPoint& ptOrg, int nWidth, BOOL bCalcOnly)
{
	CRect rTabs(0, 0, nWidth, 0);
	m_tabCtrl.AdjustRect(TRUE, rTabs);
	int nTabHeight = rTabs.Height() - 4;
	
	rTabs = dwm.OffsetCtrl(this, IDC_TABCONTROL); // not actually a move
	rTabs.right = nWidth + 1;
	rTabs.bottom = rTabs.top + nTabHeight;
	rTabs.OffsetRect(0, ptOrg.y - rTabs.top + 1); // add a pixel between tabbar and toolbar
	
	BOOL bNeedTabCtrl = WantTasklistTabbarVisible();
	
	if (!bCalcOnly)
	{
		dwm.MoveWindow(&m_tabCtrl, rTabs);
		
		// hide and disable tabctrl if not needed
		m_tabCtrl.ShowWindow(bNeedTabCtrl ? SW_SHOW : SW_HIDE);
		m_tabCtrl.EnableWindow(bNeedTabCtrl);

		if (bNeedTabCtrl)
			UpdateTabSwitchTooltip();
	}
	
	return bNeedTabCtrl ? rTabs.Height() : 0;
}

void CToDoListWnd::OnPrint() 
{
	DoPrint();
}

void CToDoListWnd::DoPrint(BOOL bPreview)
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelTDC = GetSelToDoCtrl();

	// pass the project name as the title field
	CString sTitle = m_mgrToDoCtrls.GetFriendlyProjectName(nSelTDC);

	// export to html and then print in IE
	CTDLPrintDialog dialog(sTitle, bPreview, tdc.GetView());
	
	if (dialog.DoModal() != IDOK)
		return;

	RedrawWindow();
	
	// always use the same file
	CString sTempFile = FileMisc::GetTempFileName(_T("ToDoList.print"), _T("html"));
	
	// stylesheets don't seem to like the way we do html comments
	CString sStylesheet = dialog.GetStylesheet();
	BOOL bTransform = FileMisc::FileExists(sStylesheet);

	sTitle = dialog.GetTitle();
	
	// export
	DOPROGRESS(bPreview ? IDS_PPREVIEWPROGRESS : IDS_PRINTPROGRESS)

	CTaskFile tasks;
	GetTasks(tdc, TRUE, bTransform, dialog.GetTaskSelection(), tasks, NULL);

	// add title and date, and style 
	COleDateTime date;

	if (dialog.GetWantDate())
		date = COleDateTime::GetCurrentTime();

	tasks.SetReportAttributes(sTitle, date);

	// add export style
	if (!bTransform)
	{
		TDLPD_STYLE nStyle = dialog.GetExportStyle();
		tasks.SetMetaData(TDL_EXPORTSTYLE, Misc::Format(nStyle));
	}
	
	// save intermediate tasklist to file as required
	LogIntermediateTaskList(tasks, tdc.GetFilePath());

	if (!Export2Html(tasks, sTempFile, sStylesheet))
		return;
	
	// print from browser
	CRect rHidden(-20, -20, -10, -10); // create IE off screen
	
	if (m_IE.GetSafeHwnd() || m_IE.Create(NULL, WS_CHILD | WS_VISIBLE, rHidden, this, (UINT)IDC_STATIC))
	{
		double dFileSize = FileMisc::GetFileSize(sTempFile);
		BOOL bPrintBkgnd = Prefs().GetColorTaskBackground();

		if (bPreview)
			m_IE.PrintPreview(sTempFile, bPrintBkgnd);
		else
			m_IE.Print(sTempFile, bPrintBkgnd);
	}
	else // try sending to browser
	{
		int nRes = (int)::ShellExecute(*this, bPreview ? _T("print") : NULL, sTempFile, NULL, NULL, SW_HIDE);
								
		if (nRes < 32)
			MessageBox(IDS_PRINTFAILED, IDS_PRINTFAILED_TITLE, MB_OK);
	}
}

void CToDoListWnd::OnUpdatePrint(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetTaskCount());
}

int CToDoListWnd::AddToDoCtrl(CFilteredToDoCtrl* pTDC, TSM_TASKLISTINFO* pInfo, BOOL bResizeDlg)
{
	// add tdc first to ensure tab controls has some
	// items before we query it for its size
	int nSel = m_mgrToDoCtrls.AddToDoCtrl(pTDC, pInfo);
	
	// make sure size is right
	CRect rTDC;
	
	if (CalcToDoCtrlRect(rTDC))
		pTDC->MoveWindow(rTDC);
	
	SelectToDoCtrl(nSel, FALSE);
	pTDC->SetFocusToTasks();
	
	// make sure the tab control is correctly sized
	if (bResizeDlg)
		Resize();
	
	// if this is the only control then set or terminate the various status 
	// check timers
	if (GetTDCCount() == 1)
	{
		const CPreferencesDlg& userPrefs = Prefs();
		
		SetTimer(TIMER_READONLYSTATUS, userPrefs.GetReadonlyReloadOption() != RO_NO);
		SetTimer(TIMER_TIMESTAMPCHANGE, userPrefs.GetTimestampReloadOption() != RO_NO);
		SetTimer(TIMER_AUTOSAVE, userPrefs.GetAutoSaveFrequency());
		SetTimer(TIMER_CHECKOUTSTATUS, 
				userPrefs.GetCheckoutOnCheckin() ||	userPrefs.GetAutoCheckinFrequency());
	}
	
	// make sure everything looks okay
	Invalidate();
	UpdateWindow();
	
	return nSel;
}

void CToDoListWnd::SetTimer(UINT nTimerID, BOOL bOn)
{
	if (bOn)
	{
		UINT nPeriod = 0;
		
		switch (nTimerID)
		{
		case TIMER_READONLYSTATUS:
			nPeriod = INTERVAL_READONLYSTATUS;
			break;
			
		case TIMER_TIMESTAMPCHANGE:
			nPeriod = INTERVAL_TIMESTAMPCHANGE;
			break;
			
		case TIMER_AUTOSAVE:
			nPeriod = (Prefs().GetAutoSaveFrequency() * ONE_MINUTE);
			break;
			
		case TIMER_CHECKOUTSTATUS:
			nPeriod = INTERVAL_CHECKOUTSTATUS;
			break;
			
		case TIMER_DUEITEMS:
			nPeriod = INTERVAL_DUEITEMS;
			break;
			
		case TIMER_TIMETRACKING:
			nPeriod = INTERVAL_TIMETRACKING;
			break;
			
		case TIMER_AUTOMINIMIZE:
			nPeriod = (Prefs().GetAutoMinimizeFrequency() * ONE_MINUTE);
			break;
		}
		
		if (nPeriod)
		{
			UINT nID = CFrameWnd::SetTimer(nTimerID, nPeriod, NULL);
			ASSERT (nID);
		}
	}
	else
		KillTimer(nTimerID);
}

void CToDoListWnd::OnTimer(UINT nIDEvent) 
{
	CFrameWnd::OnTimer(nIDEvent);
	
	// if we are disabled (== modal dialog visible) then do not respond
	if (!IsWindowEnabled())
		return;
	
	// don't check whilst in the middle of saving or closing
	if (m_bSaving || m_bClosing)
		return;
	
	// if no controls are active kill the timers
	if (!GetTDCCount())
	{
		SetTimer(TIMER_READONLYSTATUS, FALSE);
		SetTimer(TIMER_TIMESTAMPCHANGE, FALSE);
		SetTimer(TIMER_AUTOSAVE, FALSE);
		SetTimer(TIMER_CHECKOUTSTATUS, FALSE);
		SetTimer(TIMER_DUEITEMS, FALSE);
		SetTimer(TIMER_TIMETRACKING, FALSE);
		SetTimer(TIMER_AUTOMINIMIZE, FALSE);
		return;
	}
	
	switch (nIDEvent)
	{
	case TIMER_READONLYSTATUS:
		OnTimerReadOnlyStatus();
		break;
		
	case TIMER_TIMESTAMPCHANGE:
		OnTimerTimestampChange();
		break;
		
	case TIMER_AUTOSAVE:
		OnTimerAutoSave();
		break;
		
	case TIMER_CHECKOUTSTATUS:
		OnTimerCheckoutStatus();
		break;
		
	case TIMER_DUEITEMS:
		OnTimerDueItems();
		break;
		
	case TIMER_TIMETRACKING:
		OnTimerTimeTracking();
		break;
		
	case TIMER_AUTOMINIMIZE:
		OnTimerAutoMinimize();
		break;
	}
}

BOOL CToDoListWnd::IsActivelyTimeTracking() const
{
	// cycle thru tasklists until we find a time tracker
	int nCtrl = GetTDCCount();
	
	while (nCtrl--)
	{
		if (GetToDoCtrl(nCtrl).IsActivelyTimeTracking())
			return TRUE;
	}

	// else
	return FALSE;
}

void CToDoListWnd::OnTimerTimeTracking()
{
	AF_NOREENTRANT // macro helper
		
	static BOOL bWasTimeTracking = FALSE;
	BOOL bNowTimeTracking = IsActivelyTimeTracking();
		
	if (bWasTimeTracking != bNowTimeTracking)
	{
		UINT nIDTrayIcon = (bNowTimeTracking ? IDI_TRAYTRACK_STD : IDI_TRAY_STD);
		m_trayIcon.SetIcon(nIDTrayIcon);

		// set the main window icon also as this helps the user know what's going on
		HICON hIcon = GraphicsMisc::LoadIcon(nIDTrayIcon);
		SetIcon(hIcon, FALSE);
	}
	
	bWasTimeTracking = bNowTimeTracking;
}

void CToDoListWnd::OnTimerDueItems(int nCtrl)
{
	AF_NOREENTRANT // macro helper
		
	int nFrom = (nCtrl == -1) ? 0 : nCtrl;
	int nTo = (nCtrl == -1) ? GetTDCCount() - 1 : nCtrl;
	BOOL bRepaint = FALSE;
	
	for (nCtrl = nFrom; nCtrl <= nTo; nCtrl++)
	{
		// first we search for overdue items on each tasklist and if that
		// fails to find anything we then search for items due today
		// but only if the tasklist is fully loaded
		if (m_mgrToDoCtrls.IsLoaded(nCtrl))
		{
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
			TDCM_DUESTATUS nStatus = TDCM_NONE;
			
			if (tdc.HasOverdueTasks()) // takes priority
				nStatus = TDCM_PAST;

			else if (tdc.HasDueTodayTasks())
				nStatus = TDCM_TODAY;
			
			if (nStatus != m_mgrToDoCtrls.GetDueItemStatus(nCtrl))
			{
				m_mgrToDoCtrls.SetDueItemStatus(nCtrl, nStatus);
				bRepaint = TRUE;
			}
		}
	}

	if (bRepaint)
		m_tabCtrl.Invalidate(FALSE);
}

void CToDoListWnd::OnTimerReadOnlyStatus(int nCtrl)
{
	AF_NOREENTRANT // macro helper
		
	const CPreferencesDlg& userPrefs = Prefs();
	
	// work out whether we should check remote files or not
	BOOL bCheckRemoteFiles = (nCtrl != -1);
	
	if (!bCheckRemoteFiles)
	{
		static int nElapsed = 0;
		UINT nRemoteFileCheckInterval = userPrefs.GetRemoteFileCheckFrequency() * 1000; // in ms
		
		nElapsed %= nRemoteFileCheckInterval;
		bCheckRemoteFiles = !nElapsed;
		
		nElapsed += INTERVAL_READONLYSTATUS;
	}
	
	int nReloadOption = userPrefs.GetReadonlyReloadOption();
	
	ASSERT (nReloadOption != RO_NO);
	
	// process files
	CString sFileList;
	int nFrom = (nCtrl == -1) ? 0 : nCtrl;
	int nTo = (nCtrl == -1) ? GetTDCCount() - 1 : nCtrl;
	
	for (nCtrl = nFrom; nCtrl <= nTo; nCtrl++)
	{
		// don't check delay-loaded tasklists
		if (!m_mgrToDoCtrls.IsLoaded(nCtrl))
			continue;
		
		// don't check removeable drives
		int nType = m_mgrToDoCtrls.GetFilePathType(nCtrl);
		
        if (nType == TDCM_UNDEF || nType == TDCM_REMOVABLE)
			continue;
		
		// check remote files?
		if (!bCheckRemoteFiles && nType == TDCM_REMOTE)
			continue;
				
		if (m_mgrToDoCtrls.RefreshReadOnlyStatus(nCtrl))
		{
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
		
			BOOL bReadOnly = m_mgrToDoCtrls.GetReadOnlyStatus(nCtrl);
			BOOL bReload = FALSE;
			
			if (nReloadOption == RO_ASK)
			{
				CString sFilePath = tdc.GetFilePath();
				CEnString sMessage(bReadOnly ? IDS_WRITABLETOREADONLY : IDS_READONLYTOWRITABLE, sFilePath);
				
				if (!bReadOnly) // might been modified
					sMessage += CEnString(IDS_WANTRELOAD);

				UINT nRet = MessageBox(sMessage, IDS_STATUSCHANGE_TITLE, !bReadOnly ? MB_YESNOCANCEL : MB_OK);
				
				bReload = (nRet == IDYES || nRet == IDOK);
			}
			else
				bReload = !bReadOnly; // now writable
			
			if (bReload && ReloadTaskList(nCtrl, FALSE, (nReloadOption == RO_ASK)))
			{
				// notify the user if we haven't already
				if (nReloadOption == RO_NOTIFY)
				{
					sFileList += tdc.GetFriendlyProjectName();
					sFileList += "\n";
				}
			}
			else // update the UI
			{
				if (nCtrl == m_tabCtrl.GetCurSel())
					UpdateCaption();
				
				m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nCtrl);
				m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
			}
		}
	}
	
	// do we need to notify the user?
	if (!sFileList.IsEmpty())
	{
		CEnString sMessage(IDS_TASKLISTSRELOADED, sFileList);
		m_trayIcon.ShowBalloon(sMessage, CEnString(IDS_TIMESTAMPCHANGE_BALLOONTITLE), NIIF_INFO);
	}
}

void CToDoListWnd::OnTimerTimestampChange(int nCtrl)
{
	AF_NOREENTRANT // macro helper
		
	const CPreferencesDlg& userPrefs = Prefs();
	int nReloadOption = userPrefs.GetTimestampReloadOption();
	
	ASSERT (nReloadOption != RO_NO);
	
	// work out whether we should check remote files or not
	BOOL bCheckRemoteFiles = (nCtrl != -1);
	
	if (!bCheckRemoteFiles)
	{
		static int nElapsed = 0;
		UINT nRemoteFileCheckInterval = userPrefs.GetRemoteFileCheckFrequency() * 1000; // in ms
		
		nElapsed %= nRemoteFileCheckInterval;
		bCheckRemoteFiles = !nElapsed;
		
		nElapsed += INTERVAL_TIMESTAMPCHANGE;
	}
	
	// process files
	CString sFileList;
	int nFrom = (nCtrl == -1) ? 0 : nCtrl;
	int nTo = (nCtrl == -1) ? GetTDCCount() - 1 : nCtrl;
	
	for (nCtrl = nFrom; nCtrl <= nTo; nCtrl++)
	{
		// don't check delay-loaded tasklists
		if (!m_mgrToDoCtrls.IsLoaded(nCtrl))
			continue;

		// don't check removeable drives
		int nType = m_mgrToDoCtrls.GetFilePathType(nCtrl);
		
        if (nType == TDCM_UNDEF || nType == TDCM_REMOVABLE)
			continue;
		
		// check remote files?
		if (!bCheckRemoteFiles && nType == TDCM_REMOTE)
			continue;
		
		if (m_mgrToDoCtrls.RefreshLastModified(nCtrl))
		{
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);

			BOOL bReload = TRUE; // default
			
			if (nReloadOption == RO_ASK)
			{
				CString sFilePath = tdc.GetFilePath();
			
				CEnString sMessage(IDS_MODIFIEDELSEWHERE, sFilePath);
				sMessage += CEnString(IDS_WANTRELOAD);
				
				bReload = (MessageBox(sMessage, IDS_TIMESTAMPCHANGE_TITLE, MB_YESNOCANCEL) == IDYES);
			}
			
			if (bReload && ReloadTaskList(nCtrl, FALSE, (nReloadOption == RO_ASK)))
			{
				// notify the user if we haven't already
				if (nReloadOption == RO_NOTIFY)
				{
					sFileList += tdc.GetFriendlyProjectName();
					sFileList += "\n";
				}
			}
			else
			{
				// update UI
				if (nCtrl == m_tabCtrl.GetCurSel())
					UpdateCaption();
				
				m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nCtrl);
				m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
			}
		}
	}
	
	// do we need to notify the user?
	if (!sFileList.IsEmpty())
	{
		CEnString sMessage(IDS_TASKLISTSRELOADED, sFileList);
		m_trayIcon.ShowBalloon(sMessage, CEnString(IDS_TIMESTAMPCHANGE_BALLOONTITLE), NIIF_INFO);
	}
}

void CToDoListWnd::OnTimerAutoSave()
{
	AF_NOREENTRANT // macro helper
		
	// don't save if the user is editing a task label
	if (!GetToDoCtrl().IsTaskLabelEditing())
		SaveAll(TDLS_AUTOSAVE);
}

void CToDoListWnd::OnTimerAutoMinimize()
{
	AF_NOREENTRANT // macro helper

	if (!IsWindowVisible() || IsIconic())
		return;

	LASTINPUTINFO lii = { sizeof(LASTINPUTINFO), 0 };
	
	if (GetLastInputInfo(&lii))
	{
		double dElapsed = (GetTickCount() - lii.dwTime);
		dElapsed /= ONE_MINUTE; // convert to minutes
		
		// check
		if (dElapsed > (double)Prefs().GetAutoMinimizeFrequency())
			ShowWindow(SW_MINIMIZE);
	}
}

void CToDoListWnd::OnTimerCheckoutStatus(int nCtrl)
{
	AF_NOREENTRANT // macro helper
		
	const CPreferencesDlg& userPrefs = Prefs();
	
	// work out whether we should check remote files or not
	BOOL bCheckRemoteFiles = (nCtrl != -1);
	
	if (!bCheckRemoteFiles)
	{
		static int nElapsed = 0;
		UINT nRemoteFileCheckInterval = userPrefs.GetRemoteFileCheckFrequency() * 1000; // in ms
		
		nElapsed %= nRemoteFileCheckInterval;
		bCheckRemoteFiles = !nElapsed;
		
		nElapsed += INTERVAL_CHECKOUTSTATUS;
	}
	
	// process files
	CString sFileList;
	int nFrom = (nCtrl == -1) ? 0 : nCtrl;
	int nTo = (nCtrl == -1) ? GetTDCCount() - 1 : nCtrl;
	
	for (nCtrl = nFrom; nCtrl <= nTo; nCtrl++)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
		
		if (!m_mgrToDoCtrls.PathSupportsSourceControl(nCtrl))
            continue;
		
		// try to check out only if the previous attempt failed
		if (!tdc.IsCheckedOut() && userPrefs.GetCheckoutOnCheckin())
		{
			// we only try to check if the previous checkout failed
			if (!m_mgrToDoCtrls.GetLastCheckoutStatus(nCtrl))
			{
				if (tdc.CheckOut())
				{
					// update timestamp 
					m_mgrToDoCtrls.RefreshLastModified(nCtrl);
					m_mgrToDoCtrls.SetLastCheckoutStatus(nCtrl, TRUE);
					
					if (nCtrl == m_tabCtrl.GetCurSel())
						UpdateCaption();
					
					m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nCtrl);
					m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
					
					// notify the user if the trayicon is visible
					sFileList += tdc.GetFriendlyProjectName();
					sFileList += "\n";
				}
				else // make sure we try again later
					m_mgrToDoCtrls.SetLastCheckoutStatus(nCtrl, FALSE);
			}
		}
		// only checkin if sufficient time has elapsed since last mod
		// and there are no mods outstanding
		else if (tdc.IsCheckedOut() && userPrefs.GetAutoCheckinFrequency())
		{
			if (!tdc.IsModified())
			{
				double dElapsed = COleDateTime::GetCurrentTime() - tdc.GetLastTaskModified();
				dElapsed *= 24 * 60; // convert to minutes
				
				if (dElapsed > (double)userPrefs.GetAutoCheckinFrequency())
				{
					if (tdc.CheckIn())	
					{
						m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nCtrl);
						m_mgrToDoCtrls.RefreshLastModified(nCtrl);
						m_mgrToDoCtrls.SetLastCheckoutStatus(nCtrl, TRUE);
						m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
						
						UpdateCaption();
					}
				}
			}
		}
	}
	// do we need to notify the user?
	if (!sFileList.IsEmpty())
	{
		CEnString sMessage(IDS_TASKLISTSCHECKEDOUT, sFileList);
		m_trayIcon.ShowBalloon(sMessage, _T("Source Control Change(s)"), NIIF_INFO);
	}
}

void CToDoListWnd::OnNeedTooltipText(NMHDR* pNMHDR, LRESULT* pResult)
{
	static CString sTipText;
	sTipText.Empty();

	switch (pNMHDR->idFrom)
	{
	case ID_TOOLS_USERTOOL1:
	case ID_TOOLS_USERTOOL2:
	case ID_TOOLS_USERTOOL3:
	case ID_TOOLS_USERTOOL4:
	case ID_TOOLS_USERTOOL5:
	case ID_TOOLS_USERTOOL6:
	case ID_TOOLS_USERTOOL7:
	case ID_TOOLS_USERTOOL8:
	case ID_TOOLS_USERTOOL9:
	case ID_TOOLS_USERTOOL10:
	case ID_TOOLS_USERTOOL11:
	case ID_TOOLS_USERTOOL12:
	case ID_TOOLS_USERTOOL13:
	case ID_TOOLS_USERTOOL14:
	case ID_TOOLS_USERTOOL15:
	case ID_TOOLS_USERTOOL16:
		{
			USERTOOL ut;

			if (Prefs().GetUserTool(pNMHDR->idFrom - ID_TOOLS_USERTOOL1, ut))
				sTipText = ut.sToolName;
		}
		break;

	default:
		// tab control popups
		if (pNMHDR->idFrom >= 0 && pNMHDR->idFrom < (UINT)m_mgrToDoCtrls.GetCount())
		{
			sTipText = m_mgrToDoCtrls.GetTabItemTooltip(pNMHDR->idFrom);
		}
		break;
	}

	if (!sTipText.IsEmpty())
	{
		TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*)pNMHDR;
		pTTT->lpszText = (LPTSTR)(LPCTSTR)sTipText;
	}

	*pResult = 0;
}

void CToDoListWnd::OnUpdateUserTool(CCmdUI* pCmdUI) 
{
	if (pCmdUI->m_pMenu && pCmdUI->m_nID == ID_TOOLS_USERTOOL1) // only handle first item
	{
		CUserToolArray aTools;
		Prefs().GetUserTools(aTools);
		
		CToolsHelper th(Prefs().GetEnableTDLExtension(), ID_TOOLS_USERTOOL1);
		th.UpdateMenu(pCmdUI, aTools, m_mgrMenuIcons);
	}
	else if (m_bShowToolbar) 
	{
		int nTool = pCmdUI->m_nID - ID_TOOLS_USERTOOL1;
		ASSERT (nTool >= 0 && nTool < 16);

		USERTOOL ut;
		
		if (Prefs().GetUserTool(nTool, ut))
			pCmdUI->Enable(TRUE);
	}
}

void CToDoListWnd::OnUserTool(UINT nCmdID) 
{
	int nTool = nCmdID - ID_TOOLS_USERTOOL1;
	USERTOOL ut;
	
	ASSERT (nTool >= 0 && nTool < 16);

	const CPreferencesDlg& prefs = Prefs();
	
	if (prefs.GetUserTool(nTool, ut))
	{
		// Save all tasklists before executing the user tool
		if (prefs.GetAutoSaveOnRunTools())
		{
			if (SaveAll(TDLS_FLUSH) == TDCO_CANCELLED)
				return;
		}

		USERTOOLARGS args;
		PopulateToolArgs(args);

		CToolsHelper th(prefs.GetEnableTDLExtension(), ID_TOOLS_USERTOOL1);
		th.RunTool(ut, args, this);
	}
}

void CToDoListWnd::AddUserStorageToMenu(CMenu* pMenu) 
{
	if (pMenu)
	{
		const UINT MENUSTARTID = pMenu->GetMenuItemID(0);

		// delete existing entries
		int nStore = 16;

		while (nStore--)
		{
			pMenu->DeleteMenu(nStore, MF_BYPOSITION);
		}
		
		// if we have any tools to add we do it here
		int nNumStorage = min(m_mgrStorage.GetNumStorage(), 16);

		if (nNumStorage)
		{
			UINT nFlags = (MF_BYPOSITION | MF_STRING);

			for (int nStore = 0; nStore < nNumStorage; nStore++)
			{
				CString sMenuItem, sText = m_mgrStorage.GetStorageMenuText(nStore);
								
				if (nStore < 9)
					sMenuItem.Format(_T("&%d %s"), nStore + 1, sText);
				else
					sMenuItem = sText;
				
				// special case: if this is a 'Save' menu item
				// then the we disable it if it's the same as
				// the current tasklist's storage
				UINT nExtraFlags = 0;
/*
				if (MENUSTARTID == ID_FILE_SAVE_USERSTORAGE1)
				{
					int nTDC = GetSelToDoCtrl();
					TSM_TASKLISTINFO storageInfo;

					if (m_mgrToDoCtrls.GetStorageDetails(nTDC, storageInfo))
					{
						if (m_mgrStorage.FindStorage(storageInfo.sStorageID) == nStore)
							nExtraFlags = (MF_GRAYED | MF_CHECKED);
					}
				}
*/				
				pMenu->InsertMenu(nStore, nFlags | nExtraFlags, MENUSTARTID + nStore, sMenuItem);

				// add icon if available
				HICON hIcon = m_mgrStorage.GetStorageIcon(nStore);

				if (hIcon)
					m_mgrMenuIcons.AddImage(MENUSTARTID + nStore, hIcon);
			}
		}
		else // if nothing to add just re-add placeholder
		{
			pMenu->InsertMenu(0, MF_BYPOSITION | MF_STRING | MF_GRAYED, MENUSTARTID, _T("3rd Party Storage"));
		}
	}
}

void CToDoListWnd::OnFileOpenFromUserStorage(UINT nCmdID) 
{
	int nStorage = nCmdID - ID_FILE_OPEN_USERSTORAGE1;
	
	ASSERT (nStorage >= 0 && nStorage < 16);

	TSM_TASKLISTINFO storageInfo;
	CPreferences prefs;
	CTaskFile tasks;

	if (m_mgrStorage.RetrieveTasklist(&storageInfo, &tasks, nStorage, &prefs))
	{
		if (tasks.GetTaskCount())
		{
			VERIFY(CreateNewTaskList(FALSE));
			
			CFilteredToDoCtrl& tdc = GetToDoCtrl(); 
			VERIFY(tdc.InsertTasks(tasks, TDC_INSERTATTOP));

			// attach the returned storage info to this tasklist
			m_mgrToDoCtrls.SetStorageDetails(GetSelToDoCtrl(), storageInfo);
		}
		else if (storageInfo.szLocalFileName[0])
		{
			TDC_FILE nOpen = OpenTaskList(storageInfo.szLocalFileName, TRUE);
			
			if (nOpen == TDCO_SUCCESS)
			{
				// attach the returned storage info to this tasklist
				int nTDC = m_mgrToDoCtrls.FindToDoCtrl(storageInfo.szLocalFileName);
				ASSERT(nTDC == GetSelToDoCtrl());
				
				m_mgrToDoCtrls.SetStorageDetails(nTDC, storageInfo);
			}
			else
				HandleLoadTasklistError(nOpen, storageInfo.szDisplayName);
		}
		else
		{
			// TODO
		}
		
		UpdateCaption();
		UpdateStatusbar();
		Resize();
		UpdateWindow();
	}
}

void CToDoListWnd::OnFileSaveToUserStorage(UINT nCmdID) 
{
	int nStorage = (nCmdID - ID_FILE_SAVE_USERSTORAGE1);
	
	ASSERT (nStorage >= 0 && nStorage < 16);

	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	CString sLocalPath, sTdcPath = tdc.GetFilePath();

	// retrieve any existing storage info for this tasklist
	int nTDC = GetSelToDoCtrl();
	TSM_TASKLISTINFO storageInfo;

	if (m_mgrToDoCtrls.GetStorageDetails(nTDC, storageInfo))
	{
		sLocalPath = storageInfo.szLocalFileName;

		// clear the storage info ID if we are changing
		// the destination
		if (nStorage != m_mgrStorage.FindStorage(storageInfo.sStorageID))
			storageInfo.Reset();
	}

	if (sLocalPath.IsEmpty())
	{
		sLocalPath = sTdcPath = tdc.GetFilePath();

		if (sLocalPath.IsEmpty())
			sLocalPath = FileMisc::GetTempFileName(m_mgrToDoCtrls.GetFriendlyProjectName(nTDC), _T("tdl"));
	}

	CTaskFile tasks;
	VERIFY (tdc.Save(tasks, sLocalPath) == TDCO_SUCCESS);

	// restore previous task path
	if (sTdcPath != sLocalPath)
	{
		tdc.SetFilePath(sTdcPath);
	}
	else // prevent this save triggering a reload
	{
		m_mgrToDoCtrls.RefreshLastModified(nTDC);
	}

	_tcsncpy(storageInfo.szLocalFileName, sLocalPath, _MAX_PATH);
		
	CPreferences prefs;

	if (m_mgrStorage.StoreTasklist(&storageInfo, &tasks, nStorage, &prefs))
	{
		m_mgrToDoCtrls.SetStorageDetails(nTDC, storageInfo);
		
		UpdateCaption();
		UpdateStatusbar();
		Resize();
		UpdateWindow();
	}

	// else assume that the plugin handled any problems
}

void CToDoListWnd::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) 
{
	// test for top-level menus
	if (bSysMenu)
		return;

	if (m_menubar.GetSubMenu(FILEALL) == pPopupMenu)
	{
		// insert Min to sys tray if appropriate 
		BOOL bHasMinToTray = (::GetMenuString(*pPopupMenu, ID_MINIMIZETOTRAY, NULL, 0, MF_BYCOMMAND) != 0);
		
		if (Prefs().GetSysTrayOption() == STO_ONCLOSE || Prefs().GetSysTrayOption() == STO_ONMINCLOSE)
		{
			if (!bHasMinToTray)
				pPopupMenu->InsertMenu(ID_EXIT, MF_BYCOMMAND, ID_MINIMIZETOTRAY, CEnString(ID_MINIMIZETOTRAY));
		}
		else if (bHasMinToTray) // then remove
		{
			pPopupMenu->DeleteMenu(ID_MINIMIZETOTRAY, MF_BYCOMMAND);
		}
	}
	else if (m_menubar.GetSubMenu(EDITTASK) == pPopupMenu)
	{
		// remove relevant commands from the edit menu
		PrepareEditMenu(pPopupMenu);
	}
	else if (m_menubar.GetSubMenu(SORTTASK) == pPopupMenu)
	{
		// remove relevant commands from the sort menu
		PrepareSortMenu(pPopupMenu);
	}
	else if (m_menubar.GetSubMenu(TOOLS) == pPopupMenu)
	{
		// remove relevant commands from the sort menu
		PrepareSourceControlMenu(pPopupMenu);
	}
	else // all other sub-menus
	{
		// test for 'Open From...'
		if (pPopupMenu->GetMenuItemID(0) == ID_FILE_OPEN_USERSTORAGE1)
		{
			AddUserStorageToMenu(pPopupMenu);
		}
		// test for 'save To...'
		else if (pPopupMenu->GetMenuItemID(0) == ID_FILE_SAVE_USERSTORAGE1)
		{
			AddUserStorageToMenu(pPopupMenu);
		}
	}

	CFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

void CToDoListWnd::OnViewToolbar() 
{
	m_bShowToolbar = !m_bShowToolbar;
	m_toolbar.ShowWindow(m_bShowToolbar ? SW_SHOW : SW_HIDE);
	m_toolbar.EnableWindow(m_bShowToolbar);

	Resize();
	Invalidate(TRUE);
}

void CToDoListWnd::OnUpdateViewToolbar(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bShowToolbar);
}

void CToDoListWnd::AppendTools2Toolbar(BOOL bAppend)
{
	CToolsHelper th(Prefs().GetEnableTDLExtension(), ID_TOOLS_USERTOOL1);
	
	if (bAppend)
	{
		// then re-add
		CUserToolArray aTools;
		Prefs().GetUserTools(aTools);
		
		th.AppendToolsToToolbar(aTools, m_toolbar, ID_PREFERENCES);

		// refresh tooltips
		m_tbHelper.Release();
		m_tbHelper.Initialize(&m_toolbar, this);
	}
	else // remove
	{
		th.RemoveToolsFromToolbar(m_toolbar, ID_PREFERENCES);
	}

	// resize toolbar to accept the additional buttons
	Resize();
}

void CToDoListWnd::OnSort() 
{
	GetToDoCtrl().Resort(TRUE);
}

void CToDoListWnd::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) 
{
	CFrameWnd::OnWindowPosChanged(lpwndpos);
}

void CToDoListWnd::OnArchiveCompletedtasks() 
{
	CWaitCursor cursor;
	int nSelTDC = GetSelToDoCtrl();
	
	if (m_mgrToDoCtrls.ArchiveDoneTasks(nSelTDC))
	{
		// auto-reload archive if it's loaded
		CString sArchivePath = m_mgrToDoCtrls.GetArchivePath(nSelTDC);
		int nArchiveTDC = m_mgrToDoCtrls.FindToDoCtrl(sArchivePath);

		if (nArchiveTDC != -1 && m_mgrToDoCtrls.IsLoaded(nArchiveTDC))
			ReloadTaskList(nArchiveTDC, FALSE, FALSE);
	
		UpdateCaption();
	}
}

void CToDoListWnd::OnUpdateArchiveCompletedtasks(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && !m_mgrToDoCtrls.GetArchivePath(tdc.GetFilePath()).IsEmpty());
}

void CToDoListWnd::OnArchiveSelectedTasks() 
{
	CWaitCursor cursor;
	int nSelTDC = GetSelToDoCtrl();
	
	if (m_mgrToDoCtrls.ArchiveSelectedTasks(nSelTDC))
	{
		// auto-reload archive if it's loaded
		CString sArchivePath = m_mgrToDoCtrls.GetArchivePath(nSelTDC);
		int nArchiveTDC = m_mgrToDoCtrls.FindToDoCtrl(sArchivePath);

		if (nArchiveTDC != -1 && m_mgrToDoCtrls.IsLoaded(nArchiveTDC))
			ReloadTaskList(nArchiveTDC, FALSE, FALSE);
	
		UpdateCaption();
	}
}

void CToDoListWnd::OnUpdateArchiveSelectedCompletedTasks(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && !m_mgrToDoCtrls.GetArchivePath(tdc.GetFilePath()).IsEmpty());
}

void CToDoListWnd::OnMovetaskdown() 
{
	GetToDoCtrl().MoveSelectedTask(TDCM_DOWN);	
}

void CToDoListWnd::OnUpdateMovetaskdown(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanMoveSelectedTask(TDCM_DOWN));	
}

void CToDoListWnd::OnMovetaskup() 
{
	GetToDoCtrl().MoveSelectedTask(TDCM_UP);	
}

void CToDoListWnd::OnUpdateMovetaskup(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanMoveSelectedTask(TDCM_UP));	
}

void CToDoListWnd::OnMovetaskright() 
{
	GetToDoCtrl().MoveSelectedTask(TDCM_RIGHT);	
}

void CToDoListWnd::OnUpdateMovetaskright(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanMoveSelectedTask(TDCM_RIGHT));	
}

void CToDoListWnd::OnMovetaskleft() 
{
	GetToDoCtrl().MoveSelectedTask(TDCM_LEFT);	
}

void CToDoListWnd::OnUpdateMovetaskleft(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanMoveSelectedTask(TDCM_LEFT));	
}

CFilteredToDoCtrl& CToDoListWnd::GetToDoCtrl()
{
	return GetToDoCtrl(GetSelToDoCtrl());
}

CFilteredToDoCtrl& CToDoListWnd::GetToDoCtrl(int nIndex)
{
	return m_mgrToDoCtrls.GetToDoCtrl(nIndex);
}

const CFilteredToDoCtrl& CToDoListWnd::GetToDoCtrl() const
{
	return GetToDoCtrl(GetSelToDoCtrl());
}

const CFilteredToDoCtrl& CToDoListWnd::GetToDoCtrl(int nIndex) const
{
	return m_mgrToDoCtrls.GetToDoCtrl(nIndex);
}

CFilteredToDoCtrl* CToDoListWnd::NewToDoCtrl(BOOL bVisible, BOOL bEnabled)
{
	CFilteredToDoCtrl* pTDC = NULL;
	
	// if the active tasklist is untitled and unmodified then reuse it
	if (GetTDCCount())
	{
		int nSel = GetSelToDoCtrl();
		CFilteredToDoCtrl& tdc = GetToDoCtrl();
		
		// make sure that we don't accidently reuse a just edited tasklist
		tdc.Flush(); 
		
		if (m_mgrToDoCtrls.IsPristine(nSel))
		{
			pTDC = &tdc;
			m_mgrToDoCtrls.RemoveToDoCtrl(nSel, FALSE);
			
			// make sure we reset the selection to a valid index
			if (GetTDCCount())
			{
				// try leaving the selection as-is
				if (nSel >= GetTDCCount())
					nSel--; // try the preceding item
				
				SelectToDoCtrl(nSel, FALSE);
			}
			
			return pTDC;
		}
	}
	
	// else
	pTDC = new CFilteredToDoCtrl(m_mgrContent, Prefs().GetDefaultCommentsFormat());
	
	if (pTDC && CreateToDoCtrl(pTDC, bVisible, bEnabled))
		return pTDC;
	
	// else
	delete pTDC;
	return NULL;
}

BOOL CToDoListWnd::CreateToDoCtrl(CFilteredToDoCtrl* pTDC, BOOL bVisible, BOOL bEnabled)
{
	// create somewhere out in space
	CRect rCtrl(-32010, -32010, -32000, -32000);

	if (pTDC->Create(rCtrl, this, IDC_TODOLIST, bVisible, bEnabled))
	{
		// set font to our font
		CDialogHelper::SetFont(pTDC, m_fontMain, FALSE);
		
		if (!m_ilCheckboxes.GetSafeHandle())
			InitCheckboxImageList();
		
		UpdateToDoCtrlPreferences(pTDC);

		if (CThemed::IsThemeActive())
			pTDC->SetUITheme(m_theme);

		for (int nExt = 0; nExt < m_mgrUIExtensions.GetNumUIExtensions(); nExt++)
			pTDC->AddView(m_mgrUIExtensions.GetUIExtension(nExt));
		
		return TRUE;
	}
	
	return FALSE;
}

BOOL CToDoListWnd::InitCheckboxImageList()
{
	if (m_ilCheckboxes.GetSafeHandle())
		return TRUE;
	
	const int nStates[] = { -1, CBS_UNCHECKEDNORMAL, CBS_CHECKEDNORMAL };//, CBS_MIXEDNORMAL };
	const int nNumStates = sizeof(nStates) / sizeof(int);
	
	CThemed th;
	
	if (th.Open(this, _T("BUTTON")) && th.AreControlsThemed())
	{
		th.BuildImageList(m_ilCheckboxes, BP_CHECKBOX, nStates, nNumStates);
	}
	
	// unthemed + fallback
	if (!m_ilCheckboxes.GetSafeHandle())
	{
		CBitmap bitmap;
		bitmap.LoadBitmap(IDB_CHECKBOXES);
		
		BITMAP BM;
		bitmap.GetBitmap(&BM);
		
		if (m_ilCheckboxes.Create(BM.bmWidth / nNumStates, BM.bmHeight, ILC_COLOR32 | ILC_MASK, 0, 1))
			m_ilCheckboxes.Add(&bitmap, 255);
	}
	
	return (NULL != m_ilCheckboxes.GetSafeHandle());
}

void CToDoListWnd::OnMBtnClickTabcontrol(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NMTCMBTNCLK* pTCHDR = (NMTCMBTNCLK*)pNMHDR;
	
	// check valid tab
	if (pTCHDR->iTab >= 0)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(pTCHDR->iTab);
		tdc.Flush();
		
		CloseToDoCtrl(pTCHDR->iTab);
		
		if (!GetTDCCount())
			CreateNewTaskList(FALSE);
	}
	*pResult = 0;
}

void CToDoListWnd::OnSelchangeTabcontrol(NMHDR* /*pNMHDR*/, LRESULT* pResult) 
{
	// show the incoming selection
	int nCurSel = GetSelToDoCtrl();

	// check password if necessary
	if (m_nLastSelItem != -1 && !VerifyToDoCtrlPassword())
	{
		m_tabCtrl.SetCurSel(m_nLastSelItem);
		return;
	}

	int nDueBy = Prefs().GetNotifyDueByOnSwitch();
	
	if (nCurSel != -1)
	{
		// make sure it's loaded
		if (!VerifyTaskListOpen(nCurSel, (nDueBy == -1)))
		{
			// restore the previous tab
			m_tabCtrl.SetCurSel(m_nLastSelItem);
			return;
		}

		CFilteredToDoCtrl& tdc = GetToDoCtrl(nCurSel);
		UpdateToDoCtrlPreferences(&tdc);

		// update the filter selection
 		RefreshFilterControls();
 		
		// update status bar
		UpdateStatusbar();
		UpdateCaption();

		// make sure size is right
		CRect rTDC;
		
		if (CalcToDoCtrlRect(rTDC))
			tdc.MoveWindow(rTDC, FALSE);

		// refresh view state
		tdc.SetMaximizeState(m_nMaxState);

		tdc.EnableWindow(TRUE);
		tdc.ShowWindow(SW_SHOW);
		tdc.PauseTimeTracking(FALSE); // always
	}
	
	// hide the outgoing selection
	if (m_nLastSelItem != -1)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(m_nLastSelItem);

		tdc.ShowWindow(SW_HIDE);
		tdc.EnableWindow(FALSE);
		tdc.PauseTimeTracking(!Prefs().GetTrackNonActiveTasklists());

		m_nLastSelItem = -1; // reset
	}
	
	if (nCurSel != -1)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nCurSel);

		// update find dialog with this ToDoCtrl's custom attributes
		UpdateFindDialogCustomAttributes(&tdc);

		// leave focus setting till last else the 'old' tasklist flashes
		tdc.SetFocusToTasks();

		// notify user of due tasks if req
		DoDueTaskNotification(nCurSel, nDueBy);

		UpdateAeroFeatures();
	}

	InitMenuIconManager();
	
	*pResult = 0;
}

void CToDoListWnd::RefreshFilterControls()
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	// if this tasklist's filter is an unnamed custom one
	RefreshFilterBarCustomFilters();

	// get existing filter bar size so we can determine if a 
	// resize if required
	CRect rFilter;

	m_filterBar.GetClientRect(rFilter);
	m_filterBar.RefreshFilterControls(tdc);

	CRect rClient;
	GetClientRect(rClient);

	if (m_filterBar.CalcHeight(rClient.Width()) != rFilter.Height())
		Resize();
}

void CToDoListWnd::OnSelchangingTabcontrol(NMHDR* /*pNMHDR*/, LRESULT* pResult) 
{
	// cache the outgoing selection
	m_nLastSelItem = GetSelToDoCtrl();
	
	// and flush
	if (m_nLastSelItem != -1)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(m_nLastSelItem);
		tdc.Flush();

		// and save
		if (Prefs().GetAutoSaveOnSwitchTasklist() && !tdc.GetFilePath().IsEmpty() && tdc.IsModified())
		{
			tdc.Save();

			m_mgrToDoCtrls.SetModifiedStatus(m_nLastSelItem, FALSE); 
			m_mgrToDoCtrls.RefreshLastModified(m_nLastSelItem); 
			m_mgrToDoCtrls.RefreshReadOnlyStatus(m_nLastSelItem); 
			m_mgrToDoCtrls.RefreshPathType(m_nLastSelItem); 
		}
	}
	
	*pResult = 0;
}

TDC_FILE CToDoListWnd::ConfirmSaveTaskList(int nIndex, DWORD dwFlags)
{
	BOOL bClosingWindows = Misc::HasFlag(dwFlags, TDLS_CLOSINGWINDOWS);
	BOOL bClosingTaskList = Misc::HasFlag(dwFlags, TDLS_CLOSINGTASKLISTS) || bClosingWindows; // sanity check
	TDC_FILE nSave = TDCO_SUCCESS;
	
	// save changes
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);
	
	if (tdc.IsModified())
	{
		BOOL bFirstTimeSave = tdc.GetFilePath().IsEmpty();

		// if we are closing Windows, we don't bother asking
		// we just save and get out as fast as poss
		if (bClosingWindows)
		{
			// if it's a first time save we just save to a temp file
			if (bFirstTimeSave)
				tdc.Save(GetEndSessionFilePath());
			else
				tdc.Save();

			return TDCO_SUCCESS;
		}
		// else we obey the user's preferences
		else if (bClosingTaskList && (bFirstTimeSave || Prefs().GetConfirmSaveOnExit()))
		{
			// make sure app is visible
			Show(FALSE);

			// save tasklist
			CString sName = m_mgrToDoCtrls.GetFriendlyProjectName(nIndex);
			CEnString sMessage(IDS_SAVEBEFORECLOSE, sName);
			
			// don't allow user to cancel if closing down
			int nRet = MessageBox(sMessage, IDS_CONFIRMSAVE_TITLE, bClosingWindows ? MB_YESNO : MB_YESNOCANCEL);
			
			if (nRet == IDYES)
			{
				// note: we omit the auto save parameter here because we want the user to
				// be notified of any problems
				nSave = SaveTaskList(nIndex);

				// if the save failed (as opposed to cancelled) then we must
				// propagate this upwards
				if (nSave != TDCO_SUCCESS && nSave != TDCO_CANCELLED)
					return nSave;

				// user can still cancel save dialog even if closing down
				// but not if closing windows
				else if (nSave == TDCO_CANCELLED)
					nRet = bClosingWindows ? IDNO : IDCANCEL;
			}
			
			ASSERT (!(bClosingWindows && nRet == IDCANCEL)); // sanity check
			
			if (nRet == IDCANCEL)
				return TDCO_CANCELLED;
			else
			{
				tdc.SetModified(FALSE); // so we don't get prompted again
				m_mgrToDoCtrls.SetModifiedStatus(nIndex, FALSE);
			}
		}
		else
			nSave = SaveTaskList(nIndex, NULL, Misc::HasFlag(dwFlags, TDLS_AUTOSAVE));
	}
	
	return nSave; // user did not cancel
}

BOOL CToDoListWnd::CloseToDoCtrl(int nIndex)
{
	ASSERT (nIndex >= 0);
	ASSERT (nIndex < GetTDCCount());

	CFilteredToDoCtrl& tdcSel = GetToDoCtrl();
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);

	tdc.Flush(TRUE);
	
	if (ConfirmSaveTaskList(nIndex, TDLS_CLOSINGTASKLISTS) != TDCO_SUCCESS)
		return FALSE;
	
	// remove any find results associated with this tasklist
	if (m_findDlg.GetSafeHwnd())
		m_findDlg.DeleteResults(&tdc);
	
	CWaitCursor cursor;

	// save off current reminders
	m_reminders.CloseToDoCtrl(tdc);

	int nNewSel = m_mgrToDoCtrls.RemoveToDoCtrl(nIndex, TRUE);
	
	if (nNewSel != -1)
	{
		// if we're closing TDL then the main window will not
		// be visible at this point so we don't have to worry about
		// encrypted tasklists becoming visible. however if as a result
		// of this closure an encrypted tasklist would become visible
		// then we need to prompt for a password and if this fails
		// we must create another tasklist to hide the encrypted one.
		// unless the tasklist being closed was not active and the 
		// new selection hasn't actually changed
		BOOL bCheckPassword = !m_bClosing && (&GetToDoCtrl(nNewSel) != &tdcSel);

		if (!SelectToDoCtrl(nNewSel, bCheckPassword))
		{
			CreateNewTaskList(FALSE);
			RefreshTabOrder();
		}

		if (!m_bClosing)
			Resize();
	}
	
	return TRUE;
}

void CToDoListWnd::OnCloseTasklist() 
{
	int nSel = GetSelToDoCtrl();
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nSel);

	// make sure there are no edits pending
	tdc.Flush(TRUE); 
	
	// check if its a pristine tasklist and the last tasklist and 
	// if so only close it if the default comments type has changed
	if (m_mgrToDoCtrls.IsPristine(nSel) && GetTDCCount() == 1)
		return;
	
	CloseToDoCtrl(nSel);
	
	// if empty then create a new dummy item		
	if (!GetTDCCount())
		CreateNewTaskList(FALSE);
	else
		Resize();
}

BOOL CToDoListWnd::SelectToDoCtrl(LPCTSTR szFilePath, BOOL bCheckPassword, int nNotifyDueTasksBy)
{
	int nCtrl = m_mgrToDoCtrls.FindToDoCtrl(szFilePath);
	
	if (nCtrl != -1)
	{
		SelectToDoCtrl(nCtrl, bCheckPassword, nNotifyDueTasksBy);
		return TRUE;
	}
	
	return FALSE;
}

int CToDoListWnd::GetSelToDoCtrl() const 
{ 
	if (m_tabCtrl.GetSafeHwnd()) 
		return m_tabCtrl.GetCurSel(); 
	else
		return -1;
}

BOOL CToDoListWnd::VerifyTaskListOpen(int nIndex, BOOL bWantNotifyDueTasks)
{
	if (!m_mgrToDoCtrls.IsLoaded(nIndex))
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);
		TSM_TASKLISTINFO storageInfo;
		CString sFilePath = tdc.GetFilePath();

		if (m_mgrToDoCtrls.GetStorageDetails(nIndex, storageInfo))
			sFilePath = storageInfo.EncodeInfo();

		if (OpenTaskList(&tdc, sFilePath, &storageInfo) == TDCO_SUCCESS)
		{
			// make sure hidden windows stay hidden
			if (nIndex != GetSelToDoCtrl())
				tdc.ShowWindow(SW_HIDE);

			// notify readonly
			Resize();
			CheckNotifyReadonly(nIndex);

			m_mgrToDoCtrls.SetLoaded(nIndex);
			m_mgrToDoCtrls.UpdateTabItemText(nIndex);

			// update storage info
			m_mgrToDoCtrls.SetStorageDetails(nIndex, storageInfo);

			if (bWantNotifyDueTasks)
				DoDueTaskNotification(nIndex, Prefs().GetNotifyDueByOnLoad());

			return TRUE;
		}

		return FALSE;
	}

	return TRUE;
}

BOOL CToDoListWnd::SelectToDoCtrl(int nIndex, BOOL bCheckPassword, int nNotifyDueTasksBy)
{
	ASSERT (nIndex >= 0);
	ASSERT (nIndex < GetTDCCount());
	
	// load and show the chosen item
	// we don't need to do a 'open' due task notification if the caller
	// has asked for a notification anyway
	if (!m_bClosing)
	{
		// if the tasklist is not loaded and we verify its loading
		// then we know that the password (if there is one) has been 
		// verified and doesn't need checking again
		if (!m_mgrToDoCtrls.IsLoaded(nIndex) )
		{
			if (!VerifyTaskListOpen(nIndex, nNotifyDueTasksBy == -1))
			{
				// TODO
				return FALSE;
			}
			else
				bCheckPassword = FALSE;
		}
	}

	// validate password first if necessary
	if (bCheckPassword && !VerifyToDoCtrlPassword(nIndex))
		return FALSE;
	
	int nCurSel = GetSelToDoCtrl(); // cache this

	// resize tdc first
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nIndex);
	CRect rTDC;
	
	if (CalcToDoCtrlRect(rTDC))
		tdc.MoveWindow(rTDC);
	
	m_tabCtrl.SetCurSel(nIndex); // this changes the selected CToDoCtrl
	m_tabCtrl.UpdateWindow();
	
	if (!m_bClosing)
		UpdateToDoCtrlPreferences();
	
	const CPreferencesDlg& userPrefs = Prefs();

	tdc.EnableWindow(TRUE);
	tdc.SetFocusToTasks();
	tdc.ShowWindow(SW_SHOW);
	tdc.PauseTimeTracking(FALSE); // always
	tdc.SetMaximizeState(m_nMaxState);

	// if the tasklist is encrypted and todolist always prompts for password
	// then disable Flip3D and Aero Peek
	UpdateAeroFeatures();

	// before hiding the previous selection
	if (nCurSel != -1 && nCurSel != nIndex)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nCurSel);
		
		tdc.ShowWindow(SW_HIDE);
		tdc.EnableWindow(FALSE);
		tdc.PauseTimeTracking(!userPrefs.GetTrackNonActiveTasklists());
	}
	
	if (!m_bClosing)
	{
		if (userPrefs.GetReadonlyReloadOption() != RO_NO)
			OnTimerReadOnlyStatus(nIndex);
		
		if (userPrefs.GetTimestampReloadOption() != RO_NO)
			OnTimerTimestampChange(nIndex);
		
		if (userPrefs.GetEnableSourceControl())
			OnTimerCheckoutStatus(nIndex);
		
		UpdateCaption();
		UpdateStatusbar();
		
		// update the filter selection
		RefreshFilterControls();

		// and the menu icon manager
		InitMenuIconManager();

		// and current directory
		UpdateCwd();

		DoDueTaskNotification(nNotifyDueTasksBy);
	}

	return TRUE;
}

void CToDoListWnd::UpdateAeroFeatures()
{
#ifdef _DEBUG
	BOOL bEnable = !GetToDoCtrl().IsEncrypted();
#else
	BOOL bEnable = (!m_bPasswordPrompting || !GetToDoCtrl().IsEncrypted());
#endif

	// Disable peek and other dynamic views if the active tasklist is encrypted
	GraphicsMisc::EnableFlip3D(*this, bEnable);

	if (!GraphicsMisc::EnableAeroPeek(*this, bEnable))
		GraphicsMisc::ForceIconicRepresentation(*this, !bEnable);
}

void CToDoListWnd::UpdateToDoCtrlPreferences()
{
	// check if this has already been done since the last userPrefs change
	int nSel = GetSelToDoCtrl();
	
	if (m_mgrToDoCtrls.GetNeedsPreferenceUpdate(nSel))
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nSel);

		UpdateToDoCtrlPreferences(&tdc);

		// we do column visibility a bit different because 
		// the manager knows whether the columns have been fiddled 
		// with or not
		CTDCColumnIDArray aColumns;
		m_mgrToDoCtrls.RefreshColumns(nSel, aColumns);

		// and filter bar relies on this tdc's visible columns
		m_filterBar.SetVisibleFilters(aColumns);
		
		// reset flag
		m_mgrToDoCtrls.SetNeedsPreferenceUpdate(nSel, FALSE);
	}
}

void CToDoListWnd::UpdateToDoCtrlPreferences(CFilteredToDoCtrl* pTDC)
{
	const CPreferencesDlg& userPrefs = Prefs();

	CFilteredToDoCtrl& tdc = *pTDC;
	tdc.NotifyBeginPreferencesUpdate();
	
	CTDCStylesMap styles;
	styles.InitHashTable(TDCS_LAST);
	
	styles[TDCS_ALLOWPARENTTIMETRACKING] = userPrefs.GetAllowParentTimeTracking();
	styles[TDCS_ALLOWREFERENCEEDITING] = userPrefs.GetAllowReferenceEditing();
	styles[TDCS_ALWAYSHIDELISTPARENTS] = userPrefs.GetAlwaysHideListParents();
	styles[TDCS_AUTOADJUSTDEPENDENCYDATES] = userPrefs.GetAutoAdjustDependentsDates();
	styles[TDCS_AUTOCALCPERCENTDONE] = userPrefs.GetAutoCalcPercentDone();
	styles[TDCS_AUTOCALCTIMEESTIMATES] = userPrefs.GetAutoCalcTimeEstimates();
	styles[TDCS_AUTOREPOSCTRLS] = userPrefs.GetAutoReposCtrls();
	styles[TDCS_AVERAGEPERCENTSUBCOMPLETION] = userPrefs.GetAveragePercentSubCompletion();
	styles[TDCS_CALCREMAININGTIMEBYDUEDATE] = (userPrefs.GetTimeRemainingCalculation() == PTCP_REMAININGTTIMEISDUEDATE);
	styles[TDCS_CALCREMAININGTIMEBYPERCENT] = (userPrefs.GetTimeRemainingCalculation() == PTCP_REMAININGTTIMEISPERCENTAGE);
	styles[TDCS_CALCREMAININGTIMEBYSPENT] = (userPrefs.GetTimeRemainingCalculation() == PTCP_REMAININGTTIMEISSPENT);
	styles[TDCS_COLORTEXTBYATTRIBUTE] = (userPrefs.GetTextColorOption() == COLOROPT_ATTRIB);
	styles[TDCS_COLORTEXTBYNONE] = (userPrefs.GetTextColorOption() == COLOROPT_NONE);
	styles[TDCS_COLORTEXTBYPRIORITY] = (userPrefs.GetTextColorOption() == COLOROPT_PRIORITY);
	styles[TDCS_COLUMNHEADERSORTING] = userPrefs.GetEnableColumnHeaderSorting();
	styles[TDCS_COMMENTSUSETREEFONT] = userPrefs.GetCommentsUseTreeFont();
	styles[TDCS_CONFIRMDELETE] = userPrefs.GetConfirmDelete();
	styles[TDCS_DISPLAYHMSTIMEFORMAT] = userPrefs.GetUseHMSTimeFormat();
	styles[TDCS_DONEHAVELOWESTPRIORITY] = userPrefs.GetDoneTasksHaveLowestPriority();
	styles[TDCS_DONEHAVELOWESTRISK] = userPrefs.GetDoneTasksHaveLowestRisk();
	styles[TDCS_DUEHAVEHIGHESTPRIORITY] = userPrefs.GetDueTasksHaveHighestPriority();
	styles[TDCS_FOCUSTREEONENTER] = userPrefs.GetFocusTreeOnEnter();
	styles[TDCS_FULLROWSELECTION] = userPrefs.GetFullRowSelection();
	styles[TDCS_HIDEDONETIMEFIELD] = userPrefs.GetHideDoneTimeField();
	styles[TDCS_HIDEDUETIMEFIELD] = userPrefs.GetHideDueTimeField();
	styles[TDCS_HIDEPERCENTFORDONETASKS] = userPrefs.GetHidePercentForDoneTasks();
	styles[TDCS_HIDEPRIORITYNUMBER] = userPrefs.GetHidePriorityNumber();
	styles[TDCS_HIDESTARTDUEFORDONETASKS] = userPrefs.GetHideStartDueForDoneTasks();
	styles[TDCS_HIDESTARTTIMEFIELD] = userPrefs.GetHideStartTimeField();
	styles[TDCS_HIDEZEROPERCENTDONE] = userPrefs.GetHideZeroPercentDone();
	styles[TDCS_HIDEZEROTIMECOST] = userPrefs.GetHideZeroTimeCost();
	styles[TDCS_INCLUDEDONEINAVERAGECALC] = userPrefs.GetIncludeDoneInAverageCalc();
	styles[TDCS_INCLUDEDONEINPRIORITYCALC] = userPrefs.GetIncludeDoneInPriorityRiskCalc();
	styles[TDCS_INCLUDEDONEINRISKCALC] = userPrefs.GetIncludeDoneInPriorityRiskCalc();
	styles[TDCS_INCLUDEUSERINCHECKOUT] = userPrefs.GetIncludeUserNameInCheckout();
	styles[TDCS_LOGTASKTIMESEPARATELY] = userPrefs.GetLogTaskTimeSeparately();
	styles[TDCS_LOGTIMETRACKING] = userPrefs.GetLogTimeTracking();
	styles[TDCS_NODUEDATEISDUETODAY] = userPrefs.GetNoDueDateIsDueToday();
	styles[TDCS_PAUSETIMETRACKINGONSCRNSAVER] = !userPrefs.GetTrackOnScreenSaver();
	styles[TDCS_REFILTERONMODIFY] = userPrefs.GetReFilterOnModify();
	styles[TDCS_RESORTONMODIFY] = userPrefs.GetReSortOnModify();
	styles[TDCS_RESTOREFILTERS] = userPrefs.GetRestoreTasklistFilters();
	styles[TDCS_RIGHTSIDECOLUMNS] = userPrefs.GetShowColumnsOnRight();
	styles[TDCS_ROUNDTIMEFRACTIONS] = userPrefs.GetRoundTimeFractions();
	styles[TDCS_SHAREDCOMMENTSHEIGHT] = userPrefs.GetSharedCommentsHeight();
	styles[TDCS_SHOWCOMMENTSALWAYS] = userPrefs.GetShowCommentsAlways();
	styles[TDCS_SHOWCOMMENTSINLIST] = userPrefs.GetShowComments();
	styles[TDCS_SHOWCTRLSASCOLUMNS] = userPrefs.GetShowCtrlsAsColumns();
	styles[TDCS_SHOWDATESINISO] = userPrefs.GetDisplayDatesInISO();
	styles[TDCS_SHOWDEFAULTTASKICONS] = userPrefs.GetShowDefaultTaskIcons();
	styles[TDCS_SHOWFIRSTCOMMENTLINEINLIST] = userPrefs.GetDisplayFirstCommentLine();
	styles[TDCS_SHOWINFOTIPS] = userPrefs.GetShowInfoTips();
	styles[TDCS_SHOWNONFILEREFSASTEXT] = userPrefs.GetShowNonFilesAsText();
	styles[TDCS_SHOWPARENTSASFOLDERS] = userPrefs.GetShowParentsAsFolders();
	styles[TDCS_SHOWPATHINHEADER] = userPrefs.GetShowPathInHeader();
	styles[TDCS_SHOWPERCENTASPROGRESSBAR] = userPrefs.GetShowPercentAsProgressbar();
	styles[TDCS_SHOWPROJECTNAME] = m_bShowProjectName;
	styles[TDCS_SHOWSUBTASKCOMPLETION] = userPrefs.GetShowSubtaskCompletion();
	styles[TDCS_SHOWTREELISTBAR] = m_bShowTreeListBar;
	styles[TDCS_SHOWWEEKDAYINDATES] = userPrefs.GetShowWeekdayInDates();
	styles[TDCS_SORTDONETASKSATBOTTOM] = userPrefs.GetSortDoneTasksAtBottom();
	styles[TDCS_SORTVISIBLETASKSONLY] = FALSE;//userPrefs.GetSortVisibleOnly();
	styles[TDCS_STRIKETHOUGHDONETASKS] = userPrefs.GetStrikethroughDone();
	styles[TDCS_TASKCOLORISBACKGROUND] = userPrefs.GetColorTaskBackground();
	styles[TDCS_TRACKSELECTEDTASKONLY] = !userPrefs.GetTrackNonSelectedTasks();
	styles[TDCS_TREATSUBCOMPLETEDASDONE] = userPrefs.GetTreatSubCompletedAsDone();
	styles[TDCS_TREECHECKBOXES] = userPrefs.GetTreeCheckboxes();
	styles[TDCS_TREETASKICONS] = userPrefs.GetTreeTaskIcons();
	styles[TDCS_USEEARLIESTDUEDATE] = (userPrefs.GetDueDateCalculation() == PTCP_EARLIESTDUEDATE);
	styles[TDCS_USEEARLIESTSTARTDATE] = (userPrefs.GetStartDateCalculation() == PTCP_EARLIESTSTARTDATE);
	styles[TDCS_USEHIGHESTPRIORITY] = userPrefs.GetUseHighestPriority();
	styles[TDCS_USEHIGHESTRISK] = userPrefs.GetUseHighestRisk();
	styles[TDCS_USELATESTDUEDATE] = (userPrefs.GetDueDateCalculation() == PTCP_LATESTDUEDATE);
	styles[TDCS_USELATESTSTARTDATE] = (userPrefs.GetStartDateCalculation() == PTCP_LATESTSTARTDATE);
	styles[TDCS_USEPERCENTDONEINTIMEEST] = userPrefs.GetUsePercentDoneInTimeEst();
	styles[TDCS_USES3RDPARTYSOURCECONTROL] = userPrefs.GetUsing3rdPartySourceControl();
	styles[TDCS_WARNADDDELETEARCHIVE] = userPrefs.GetWarnAddDeleteArchive();
	styles[TDCS_WEIGHTPERCENTCALCBYNUMSUB] = userPrefs.GetWeightPercentCompletionByNumSubtasks();

	// source control
	BOOL bSrcControl = m_mgrToDoCtrls.PathSupportsSourceControl(tdc.GetFilePath());
	
	styles[TDCS_ENABLESOURCECONTROL] = bSrcControl;
	styles[TDCS_CHECKOUTONLOAD] = bSrcControl && userPrefs.GetAutoCheckOut();
	
	// set the styles in one hit
	tdc.SetStyles(styles);

	// layout
	tdc.SetLayoutPositions((TDC_UILOCATION)userPrefs.GetControlsPos(), 
							(TDC_UILOCATION)userPrefs.GetCommentsPos(), 
							TRUE);
	
	// info tips
	tdc.SetMaxInfotipCommentsLength(userPrefs.GetMaxInfoTipCommentsLength());
	
	// update default task preferences
	tdc.SetDefaultTaskAttributes(m_tdiDefault);

	// default string lists
	CStringArray aItems;
	
	if (userPrefs.GetDefaultListItems(PTDP_CATEGORY, aItems))
		aItems.Append(m_tdiDefault.aCategories);

	tdc.SetDefaultCategoryNames(aItems, userPrefs.GetCategoryListIsReadonly());

	if (userPrefs.GetDefaultListItems(PTDP_ALLOCTO, aItems))
		aItems.Append(m_tdiDefault.aAllocTo);
	
	tdc.SetDefaultAllocToNames(aItems, userPrefs.GetAllocToListIsReadonly());
	
	userPrefs.GetDefaultListItems(PTDP_STATUS, aItems);
	tdc.SetDefaultStatusNames(aItems, userPrefs.GetStatusListIsReadonly());
	
	userPrefs.GetDefaultListItems(PTDP_ALLOCBY, aItems);
	tdc.SetDefaultAllocByNames(aItems, userPrefs.GetAllocByListIsReadonly());
		
	// fonts
	if (!m_fontTree.GetSafeHandle() || !m_fontComments.GetSafeHandle())
	{
		CString sFaceName;
		int nFontSize;
		
		if (!m_fontTree.GetSafeHandle() && userPrefs.GetTreeFont(sFaceName, nFontSize))
			m_fontTree.Attach(GraphicsMisc::CreateFont(sFaceName, nFontSize));
		
		if (!m_fontComments.GetSafeHandle() && userPrefs.GetCommentsFont(sFaceName, nFontSize))
			m_fontComments.Attach(GraphicsMisc::CreateFont(sFaceName, nFontSize));
	}
	
	tdc.SetTreeFont(m_fontTree);
	tdc.SetCommentsFont(m_fontComments);
	
	// colours
	tdc.SetGridlineColor(userPrefs.GetGridlineColor());
	tdc.SetCompletedTaskColor(userPrefs.GetDoneTaskColor());
	tdc.SetAlternateLineColor(userPrefs.GetAlternateLineColor());
	tdc.SetFlaggedTaskColor(userPrefs.GetFlaggedTaskColor());
	tdc.SetReferenceTaskColor(userPrefs.GetReferenceTaskColor());
	tdc.SetPriorityColors(m_aPriorityColors);

	COLORREF color, crToday;
	userPrefs.GetStartedTaskColors(color, crToday);
	tdc.SetStartedTaskColors(color, crToday);

	userPrefs.GetDueTaskColors(color, crToday);
	tdc.SetDueTaskColors(color, crToday);
	
	CTDCColorMap mapColors;
	CAttribColorArray aColors;

	TDC_ATTRIBUTE nAttrib = TDCA_NONE;
	int nColor = userPrefs.GetAttributeColors(nAttrib, aColors);
	
	while (nColor--)
	{
		ATTRIBCOLOR& ac = aColors[nColor];
		mapColors[ac.sAttrib] = ac.color;
	}
	tdc.SetAttributeColors(nAttrib, mapColors);

	// drag drop
	tdc.SetSubtaskDragDropPos(userPrefs.GetNewSubtaskPos() == PUIP_TOP);
	
	// misc
	tdc.SetMaxColumnWidth(userPrefs.GetMaxColumnWidth());
	tdc.SetCheckImageList(m_ilCheckboxes);
	tdc.SetPercentDoneIncrement(userPrefs.GetPercentDoneIncrement());

	CString sStatus;
	userPrefs.GetCompletionStatus(sStatus);
	tdc.SetCompletionStatus(sStatus);
	
	tdc.Flush(); // clear any outstanding issues

	// we're done
	tdc.NotifyEndPreferencesUpdate();
}

void CToDoListWnd::UpdateTabSwitchTooltip()
{
	CToolTipCtrl* pTT = m_tabCtrl.GetToolTips();
	
	if (pTT)
	{
		// get the string for tab switching
		CString sShortcut = m_mgrShortcuts.GetShortcutTextByCmd(ID_VIEW_NEXT);
		
		if (sShortcut.IsEmpty())
			sShortcut = m_mgrShortcuts.GetShortcutTextByCmd(ID_VIEW_PREV);
		
		pTT->DelTool(&m_tabCtrl); // always
		
		if (!sShortcut.IsEmpty())
		{
			CEnString sTip(IDS_TABSWITCHTOOLTIP, sShortcut);
			pTT->AddTool(&m_tabCtrl, sTip);
		}
	}
}

void CToDoListWnd::OnSaveall() 
{
	SaveAll(TDLS_INCLUDEUNSAVED | TDLS_FLUSH);
}

void CToDoListWnd::OnUpdateSaveall(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_mgrToDoCtrls.AnyIsModified());
}

void CToDoListWnd::OnCloseall() 
{
	// save first
	TDC_FILE nSaveAll = SaveAll(TDLS_INCLUDEUNSAVED | TDLS_CLOSINGTASKLISTS | TDLS_FLUSH);

	if (nSaveAll != TDCO_SUCCESS)
		return;

	// remove tasklists
	int nCtrl = GetTDCCount();
	
	while (nCtrl--)
		m_mgrToDoCtrls.RemoveToDoCtrl(nCtrl, TRUE);

	// if empty then create a new dummy item		
	if (!GetTDCCount())
		CreateNewTaskList(FALSE);
	else
		Resize();
}

void CToDoListWnd::OnUpdateCloseall(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetTDCCount());
}

BOOL CToDoListWnd::DoQueryEndSession(BOOL bQuery, BOOL bEnding)
{
	HWND hWnd = GetSafeHwnd();

	// what we do here depends on whether we're on Vista or not
	// we test for this by trying to load the new API functions
	if (bQuery)
	{
		CEnString sReason(IDS_SHUTDOWNBLOCKREASON);

		// if Vista and handling WM_QUERYENDSESSION
		// we register our reason and return TRUE to
		// get more time to clean up in WM_ENDSESSION
		if (Misc::ShutdownBlockReasonCreate(hWnd, sReason))
			return TRUE;

		// else we're XP so we return TRUE to let shutdown continue
		return TRUE;
	}

	// else do a proper shutdown
	m_bEndingSession = TRUE;

	DoExit(FALSE, bEnding);
		
	// cleanup our shutdown reason if not handled in DoExit
	Misc::ShutdownBlockReasonDestroy(hWnd);

	// and return anything because it's ignored
	return TRUE;
}

BOOL CToDoListWnd::OnQueryEndSession() 
{
	if (!CFrameWnd::OnQueryEndSession())
		return FALSE;
	
	return DoQueryEndSession(TRUE, FALSE);
}

void CToDoListWnd::OnEndSession(BOOL bEnding) 
{
	CFrameWnd::OnEndSession(bEnding);

	DoQueryEndSession(FALSE, bEnding);
}

void CToDoListWnd::OnExit()
{
	DoExit();
}

void CToDoListWnd::OnMinimizeToTray()
{
	MinimizeToTray();
}

BOOL CToDoListWnd::DoExit(BOOL bRestart, BOOL bClosingWindows) 
{
	ASSERT (!(bClosingWindows && bRestart));

    // save all first to ensure new tasklists get reloaded on startup
	DWORD dwSaveFlags = TDLS_INCLUDEUNSAVED | TDLS_CLOSINGTASKLISTS | TDLS_FLUSH;

	if (bClosingWindows)
		dwSaveFlags |= TDLS_CLOSINGWINDOWS;

	TDC_FILE nSaveAll = SaveAll(dwSaveFlags);

	if (nSaveAll != TDCO_SUCCESS)
        return FALSE; // user cancelled
	
	// save settings before we close the tasklists
	// to snapshot the currently open tasklists
	SaveSettings(); 

	m_bClosing = TRUE;
	
	// hide the window as soon as possible so users do not
	// see the machinations of closing down
	if (m_bFindShowing)
		m_findDlg.ShowWindow(SW_HIDE);

	ShowWindow(SW_HIDE);
	
	// remove tasklists
	int nCtrl = GetTDCCount();
		
	while (nCtrl--)
		VERIFY(CloseToDoCtrl(nCtrl)); // shouldn't fail now
	
	// if there are any still open then the user must have cancelled else destroy the window
	ASSERT (GetTDCCount() == 0);
	
	if (GetTDCCount() == 0)
	{
		// this will save any left over settings 
		// when it goes out of scope
		{
			CPreferences prefs; 

			m_mgrImportExport.Release();
			m_tbHelper.Release();
			m_mgrShortcuts.Release(&prefs);
			m_mgrImportExport.Release();
			m_mgrUIExtensions.Release();
			m_mgrStorage.Release();
			
			CFocusWatcher::Release();
			CMouseWheelMgr::Release();
			CEditShortcutMgr::Release();
		}

		// cleanup our shutdown reason
		Misc::ShutdownBlockReasonDestroy(*this);

		DestroyWindow();
		
		if (bRestart)
		{
			CString sParams = AfxGetApp()->m_lpCmdLine;
			::ShellExecute(NULL, NULL, FileMisc::GetModuleFileName(), sParams, NULL, SW_SHOW);
		}

		return TRUE;
	}

	// cancel
	m_bClosing = FALSE;
	return FALSE;
}

void CToDoListWnd::OnImportTasklist() 
{
	CString sImportPath;
	TDLID_IMPORTTO nImportTo = TDIT_NEWTASKLIST;
	int nImporter = -1;
	
	do
	{
		CTDLImportDialog dialog(m_mgrImportExport);

		if (dialog.DoModal() == IDOK)
		{
			// check file can be opened
			nImportTo = dialog.GetImportTo();
			nImporter = dialog.GetImporterIndex();

			if (dialog.GetImportFromClipboard())
			{
				sImportPath = FileMisc::GetTempFileName(_T("ToDoList.import"), _T("txt"));
				FileMisc::SaveFile(sImportPath, dialog.GetImportClipboardText());
			}
			else
				sImportPath = dialog.GetImportFilePath();

			// check file accessibility
			if (sImportPath.IsEmpty() || FileMisc::CanOpenFile(sImportPath, TRUE))
				break;

			// else
			MessageBox(CEnString(IDS_IMPORTFILE_CANTOPEN, sImportPath), IDS_IMPORTTASKLIST_TITLE);
			sImportPath.Empty();
		}
		else // cancel
			return;
	}
	while (sImportPath.IsEmpty());

	// load/import tasks
	DOPROGRESS(IDS_IMPORTPROGRESS)

	// do the import
	CTaskFile tasks;
	BOOL bCancel = !m_mgrImportExport.ImportTaskList(sImportPath, &tasks, nImporter);

	if (bCancel)
		return;

	if (!tasks.GetTaskCount())
	{
		// notify user
		MessageBox(IDS_NOTASKSIMPORTED);
	}
	else
	{
		if (nImportTo == TDIT_NEWTASKLIST)
			VERIFY(CreateNewTaskList(FALSE));
		
		CFilteredToDoCtrl& tdc = GetToDoCtrl(); 
		TDC_INSERTWHERE nWhere = (nImportTo == TDIT_SELECTEDTASK) ? TDC_INSERTATTOPOFSELTASK : TDC_INSERTATTOP;
		
		VERIFY(tdc.InsertTasks(tasks, nWhere));

		// if a new tasklist then set the project name
		if (nImportTo == TDIT_NEWTASKLIST)
			tdc.SetProjectName(tasks.GetProjectName());

		m_mgrToDoCtrls.RefreshModifiedStatus(GetSelToDoCtrl());
		UpdateCaption();
	}
}

void CToDoListWnd::OnSetPriority(UINT nCmdID) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (!tdc.IsReadOnly() && tdc.HasSelection())
	{
		if (nCmdID == ID_EDIT_SETPRIORITYNONE) 
			tdc.SetSelectedTaskPriority(-2);
		else
			tdc.SetSelectedTaskPriority(nCmdID - ID_EDIT_SETPRIORITY0);
	}
}

void CToDoListWnd::OnUpdateSetPriority(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && nSelCount);
	
	int nPriority = pCmdUI->m_nID - ID_EDIT_SETPRIORITY0;
	
	if (pCmdUI->m_nID == ID_EDIT_SETPRIORITYNONE)
		nPriority = -2;
	
	pCmdUI->SetCheck(nPriority == tdc.GetSelectedTaskPriority());
}

void CToDoListWnd::OnEditSetfileref() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (!tdc.IsReadOnly() && tdc.HasSelection())
	{
		CPreferences prefs;
		CFileOpenDialog dialog(IDS_SETFILEREF_TITLE, 
								NULL, 
								tdc.GetSelectedTaskFileRef(), 
								EOFN_DEFAULTOPEN, 
								CEnString(IDS_ALLFILEFILTER));
		
		if (dialog.DoModal(&prefs) == IDOK)
			tdc.SetSelectedTaskFileRef(dialog.GetPathName());
	}
}

void CToDoListWnd::OnUpdateEditSetfileref(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.HasSelection());
}

void CToDoListWnd::OnEditOpenfileref() 
{
	GetToDoCtrl().GotoSelectedTaskFileRef();
}

void CToDoListWnd::OnUpdateEditOpenfileref(CCmdUI* pCmdUI) 
{
	CEnString sFileRef = GetToDoCtrl().GetSelectedTaskFileRef();
	
	if (sFileRef.IsEmpty())
		pCmdUI->Enable(FALSE);
	else
	{
		pCmdUI->Enable(TRUE);

		// restrict file length to 250 pixels
		CClientDC dc(this);
		sFileRef.FormatDC(&dc, 250, ES_PATH);
		pCmdUI->SetText(sFileRef);
	}
}

LRESULT CToDoListWnd::OnPreferencesDefaultListChange(WPARAM wp, LPARAM lp)
{
	// decode params
	int nList = LOWORD(wp);
	BOOL bAdd = HIWORD(wp);
	LPCTSTR szItem = (LPCTSTR)lp;

	switch (nList)
	{
	case PTDP_ALLOCBY:
		break;

	case PTDP_ALLOCTO:
		break;

	case PTDP_STATUS:
		break;

	case PTDP_CATEGORY:
		break;
	}

	return 0L;
}

void CToDoListWnd::PopulateToolArgs(USERTOOLARGS& args) const
{
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();
		
	args.sTasklist = tdc.GetFilePath();
	args.sTaskTitle = tdc.GetSelectedTaskTitle();
	args.sTaskExtID = tdc.GetSelectedTaskExtID();
	args.sTaskComments = tdc.GetSelectedTaskComments();
	args.sTaskFileLink = tdc.GetSelectedTaskFileRef();
	args.sTaskAllocBy = tdc.GetSelectedTaskAllocBy();
	
	CDWordArray aIDs;
	DWORD dwTemp;
	
	if (tdc.GetSelectedTaskIDs(aIDs, dwTemp, FALSE))
		args.sTaskIDs = Misc::FormatArray(aIDs, _T("|"));
	
	CStringArray aAllocTo;
	
	if (tdc.GetSelectedTaskAllocTo(aAllocTo))
		args.sTaskAllocTo = Misc::FormatArray(aAllocTo, _T("|"));
}

LRESULT CToDoListWnd::OnPreferencesTestTool(WPARAM /*wp*/, LPARAM lp)
{
	USERTOOL* pTool = (USERTOOL*)lp;
	
	if (pTool)
	{
		USERTOOLARGS args;
		PopulateToolArgs(args);

		CToolsHelper th(Prefs().GetEnableTDLExtension(), ID_TOOLS_USERTOOL1);
		th.TestTool(*pTool, args, m_pPrefs);
	}
	
	return 0;
}

LRESULT CToDoListWnd::OnPreferencesClearMRU(WPARAM /*wp*/, LPARAM /*lp*/)
{
	m_mruList.RemoveAll();
	m_mruList.WriteList(CPreferences());
	
	return 0;
}

LRESULT CToDoListWnd::OnPreferencesCleanupDictionary(WPARAM /*wp*/, LPARAM lp)
{
	LPCTSTR szLangFile = (LPCTSTR)lp;
	ASSERT(szLangFile && *szLangFile);

	if (szLangFile && *szLangFile)
	{
		DOPROGRESS(IDS_CLEANINGDICTPROGRESS)

		CString sMasterDict = FileMisc::GetFolderFromFilePath(szLangFile);
		sMasterDict += _T("\\YourLanguage.csv");

		return CLocalizer::CleanupDictionary(sMasterDict);
	}
	
	return 0;
}

void CToDoListWnd::PrepareSortMenu(CMenu* pMenu)
{
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();
		
	if (Prefs().GetShowEditMenuAsColumns())
	{
		int nCountLastSep = 0;
		
		for (int nItem = 0; nItem < (int)pMenu->GetMenuItemCount(); nItem++)
		{
			BOOL bDelete = FALSE;
			BOOL bIsSeparator = FALSE;

			UINT nMenuID = pMenu->GetMenuItemID(nItem);
			
			switch (nMenuID)
			{
			case ID_SORT_BYCOST:		bDelete = !tdc.IsColumnShowing(TDCC_COST); break;
			case ID_SORT_BYFLAG:		bDelete = !tdc.IsColumnShowing(TDCC_FLAG); break; 
			case ID_SORT_BYDONEDATE:	bDelete = !tdc.IsColumnShowing(TDCC_DONEDATE); break; 
			case ID_SORT_BYPRIORITY:	bDelete = !tdc.IsColumnShowing(TDCC_PRIORITY); break; 
			case ID_SORT_BYPERCENT:		bDelete = !tdc.IsColumnShowing(TDCC_PERCENT); break; 
			case ID_SORT_BYSTARTDATE:	bDelete = !tdc.IsColumnShowing(TDCC_STARTDATE); break; 
			case ID_SORT_BYDUEDATE:		bDelete = !tdc.IsColumnShowing(TDCC_DUEDATE); break; 
			case ID_SORT_BYTIMEEST:		bDelete = !tdc.IsColumnShowing(TDCC_TIMEEST); break; 
			case ID_SORT_BYID:			bDelete = !tdc.IsColumnShowing(TDCC_ID); break; 
			case ID_SORT_BYDONE:		bDelete = !tdc.IsColumnShowing(TDCC_DONE); break; 
			case ID_SORT_BYALLOCBY:		bDelete = !tdc.IsColumnShowing(TDCC_ALLOCBY); break; 
			case ID_SORT_BYSTATUS:		bDelete = !tdc.IsColumnShowing(TDCC_STATUS); break; 
			case ID_SORT_BYCATEGORY:	bDelete = !tdc.IsColumnShowing(TDCC_CATEGORY); break; 
			case ID_SORT_BYTIMESPENT:	bDelete = !tdc.IsColumnShowing(TDCC_TIMESPENT); break; 
			case ID_SORT_BYALLOCTO:		bDelete = !tdc.IsColumnShowing(TDCC_ALLOCTO); break; 
			case ID_SORT_BYCREATIONDATE:bDelete = !tdc.IsColumnShowing(TDCC_CREATIONDATE); break; 
			case ID_SORT_BYCREATEDBY:	bDelete = !tdc.IsColumnShowing(TDCC_CREATEDBY); break; 
			case ID_SORT_BYMODIFYDATE:	bDelete = !tdc.IsColumnShowing(TDCC_LASTMOD); break; 
			case ID_SORT_BYRISK:		bDelete = !tdc.IsColumnShowing(TDCC_RISK); break; 
			case ID_SORT_BYEXTERNALID:	bDelete = !tdc.IsColumnShowing(TDCC_EXTERNALID); break; 
			case ID_SORT_BYVERSION:		bDelete = !tdc.IsColumnShowing(TDCC_VERSION); break; 
			case ID_SORT_BYRECURRENCE:	bDelete = !tdc.IsColumnShowing(TDCC_RECURRENCE); break; 
			case ID_SORT_BYREMAINING:	bDelete = !tdc.IsColumnShowing(TDCC_REMAINING); break; 
			case ID_SORT_BYRECENTEDIT:	bDelete = !tdc.IsColumnShowing(TDCC_RECENTEDIT); break; 
			case ID_SORT_BYICON:		bDelete = !tdc.IsColumnShowing(TDCC_ICON); break; 
			case ID_SORT_BYFILEREF:		bDelete = !tdc.IsColumnShowing(TDCC_FILEREF); break; 
			case ID_SORT_BYTIMETRACKING:bDelete = !tdc.IsColumnShowing(TDCC_TRACKTIME); break; 
			case ID_SORT_BYPATH:		bDelete = !tdc.IsColumnShowing(TDCC_PATH); break; 
			case ID_SORT_BYTAG:			bDelete = !tdc.IsColumnShowing(TDCC_TAGS); break; 
			case ID_SORT_BYDEPENDENCY:	bDelete = !tdc.IsColumnShowing(TDCC_DEPENDENCY); break; 
	//		case ID_SORT_BYCOLOR: bDelete = (Prefs().GetTextColorOption() != COLOROPT_DEFAULT); break; 

			case ID_SEPARATOR: 
				bIsSeparator = TRUE;
				bDelete = (nCountLastSep == 0);
				nCountLastSep = 0;
				break;

			default: bDelete = FALSE; break; 
			}

			// delete the item else increment the count since the last separator
			if (bDelete)
			{
				pMenu->DeleteMenu(nItem, MF_BYPOSITION);
				nItem--;
			}
			else if (!bIsSeparator)
				nCountLastSep++;
		}
	}

	// custom sort columns

	// first delete all custom columns and the related separator
	int nPosUnsorted = -1;

	for (int nItem = 0; nItem < (int)pMenu->GetMenuItemCount(); nItem++)
	{
		UINT nMenuID = pMenu->GetMenuItemID(nItem);

		if (nMenuID >= ID_SORT_BYCUSTOMCOLUMN_FIRST && nMenuID <= ID_SORT_BYCUSTOMCOLUMN_LAST)
		{
			pMenu->DeleteMenu(nItem, MF_BYPOSITION);
			nItem--;
		}
	}

	// separator is just before the separator before 'unsorted entry'
	int nInsert = CEnMenu::GetMenuItemPos(pMenu->GetSafeHmenu(), ID_SORT_NONE) - 1;
	ASSERT(nInsert >= 0);

	// delete separator if exist
	if (nInsert > 0 && pMenu->GetMenuItemID(nInsert - 1) == 0)
	{
		nInsert--;
		pMenu->DeleteMenu(nInsert, MF_BYPOSITION);
	}

	// then re-add
	CTDCCustomAttribDefinitionArray aAttribDefs;

	if (tdc.GetCustomAttributeDefs(aAttribDefs))
	{
		// re-add separator on demand
		BOOL bWantSep = TRUE;

		for (int nCol = 0; nCol < aAttribDefs.GetSize(); nCol++)
		{
			const TDCCUSTOMATTRIBUTEDEFINITION& attribDef = aAttribDefs[nCol];

			if (attribDef.bEnabled && attribDef.SupportsFeature(TDCCAF_SORT))
			{
				if (bWantSep)
				{
					bWantSep = FALSE;
					pMenu->InsertMenu(nInsert, MF_BYPOSITION);
					nInsert++;
				}

				UINT nMenuID = (attribDef.GetColumnID() - TDCC_CUSTOMCOLUMN_FIRST) + ID_SORT_BYCUSTOMCOLUMN_FIRST;
				CEnString sColumn(IDS_CUSTOMCOLUMN, attribDef.sLabel);

				pMenu->InsertMenu(nInsert, MF_BYPOSITION, nMenuID, sColumn);
				nInsert++;
			}
		}
	}
}

void CToDoListWnd::PrepareSourceControlMenu(CMenu* pMenu)
{
	if (Prefs().GetEnableSourceControl())
		return;

	int nCountLastSep = 0;
	
	for (int nItem = 0; nItem < (int)pMenu->GetMenuItemCount(); nItem++)
	{
		BOOL bDelete = FALSE;
		BOOL bIsSeparator = FALSE;

		UINT nMenuID = pMenu->GetMenuItemID(nItem);
		
		switch (nMenuID)
		{
		case -1: // its a popup so recursively handle it
			{
				CMenu* pPopup = pMenu->GetSubMenu(nItem);
				PrepareEditMenu(pPopup);
				
				// if the popup is now empty remove it too
				bDelete = !pPopup->GetMenuItemCount();
			}
			break;
			
        case ID_TOOLS_CHECKIN:
        case ID_TOOLS_CHECKOUT:
			bDelete = TRUE;
			break;
			
		case ID_SEPARATOR: 
			bIsSeparator = TRUE;
			bDelete = (nCountLastSep == 0);
			nCountLastSep = 0;
			break;

		default: bDelete = FALSE; break; 
		}

		// delete the item else increment the count since the last separator
		if (bDelete)
		{
			pMenu->DeleteMenu(nItem, MF_BYPOSITION);
			nItem--;
		}
		else if (!bIsSeparator)
			nCountLastSep++;
	}
}

void CToDoListWnd::PrepareEditMenu(CMenu* pMenu)
{
	if (!Prefs().GetShowEditMenuAsColumns())
		return;

	const CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	int nCountLastSep = 0;
	
	for (int nItem = 0; nItem < (int)pMenu->GetMenuItemCount(); nItem++)
	{
		BOOL bDelete = FALSE;
		BOOL bIsSeparator = FALSE;

		UINT nMenuID = pMenu->GetMenuItemID(nItem);
		
		switch (nMenuID)
		{
		case -1: // its a popup so recursively handle it
			{
				CMenu* pPopup = pMenu->GetSubMenu(nItem);

				if (pPopup)
				{
					PrepareEditMenu(pPopup);
				
					// if the popup is now empty remove it too
					bDelete = !pPopup->GetMenuItemCount();
				}
			}
			break;
			
		case ID_EDIT_TASKCOLOR:
		case ID_EDIT_CLEARTASKCOLOR:
			bDelete = (Prefs().GetTextColorOption() != COLOROPT_DEFAULT);
			break;
			
        case ID_EDIT_DECTASKPRIORITY:
        case ID_EDIT_INCTASKPRIORITY:
		case ID_EDIT_SETPRIORITYNONE: 
        case ID_EDIT_SETPRIORITY0:
        case ID_EDIT_SETPRIORITY1:
        case ID_EDIT_SETPRIORITY2:
        case ID_EDIT_SETPRIORITY3:
        case ID_EDIT_SETPRIORITY4:
        case ID_EDIT_SETPRIORITY5:
        case ID_EDIT_SETPRIORITY6:
        case ID_EDIT_SETPRIORITY7:
        case ID_EDIT_SETPRIORITY8:
        case ID_EDIT_SETPRIORITY9:
        case ID_EDIT_SETPRIORITY10:
			bDelete = !tdc.IsColumnShowing(TDCC_PRIORITY);
			break;
			
		case ID_EDIT_OFFSETDATES:
			bDelete = !(tdc.IsColumnShowing(TDCC_STARTDATE) ||
						tdc.IsColumnShowing(TDCC_DUEDATE) || 
						tdc.IsColumnShowing(TDCC_DONEDATE));
			break;
			
        case ID_EDIT_CLOCK_TASK:
			bDelete = !(tdc.IsColumnShowing(TDCC_TRACKTIME) ||
						tdc.IsColumnShowing(TDCC_TIMESPENT));
			break;

        case ID_SHOWTIMELOGFILE:
			bDelete = !((tdc.IsColumnShowing(TDCC_TRACKTIME) ||
						tdc.IsColumnShowing(TDCC_TIMESPENT)) &&
						Prefs().GetLogTimeTracking());
			break;
			
        case ID_EDIT_DECTASKPERCENTDONE:	bDelete = !tdc.IsColumnShowing(TDCC_PERCENT); break;
        case ID_EDIT_INCTASKPERCENTDONE:	bDelete = !tdc.IsColumnShowing(TDCC_PERCENT); break;
        case ID_EDIT_OPENFILEREF:			bDelete = !tdc.IsColumnShowing(TDCC_FILEREF); break;
		case ID_EDIT_SETFILEREF:			bDelete = !tdc.IsColumnShowing(TDCC_FILEREF); break;
        case ID_EDIT_FLAGTASK:				bDelete = !tdc.IsColumnShowing(TDCC_FLAG); break;
        case ID_EDIT_RECURRENCE:			bDelete = !tdc.IsColumnShowing(TDCC_RECURRENCE); break;
        case ID_EDIT_GOTODEPENDENCY:		bDelete = !tdc.IsColumnShowing(TDCC_DEPENDENCY); break;

        case ID_EDIT_SETTASKICON:			
        case ID_EDIT_CLEARTASKICON:	
			bDelete = !(tdc.IsColumnShowing(TDCC_ICON) || Prefs().GetTreeTaskIcons()); 
			break;

		case ID_SEPARATOR: 
			bIsSeparator = TRUE;
			bDelete = (nCountLastSep == 0);
			nCountLastSep = 0;
			break;

		default: bDelete = FALSE; break; 
		}

		// delete the item else increment the count since the last separator
		if (bDelete)
		{
			pMenu->DeleteMenu(nItem, MF_BYPOSITION);
			nItem--;
		}
		else if (!bIsSeparator)
			nCountLastSep++;
	}

	// make sure last item is not a separator
	int nLastItem = (int)pMenu->GetMenuItemCount() - 1;

	if (pMenu->GetMenuItemID(nLastItem) == 0)
		pMenu->DeleteMenu(nLastItem, MF_BYPOSITION);

}

void CToDoListWnd::OnViewNext() 
{
	if (GetTDCCount() < 2)
		return;
	
	int nNext = GetSelToDoCtrl() + 1;
	
	if (nNext >= GetTDCCount())
		nNext = 0;
	
	SelectToDoCtrl(nNext, TRUE, Prefs().GetNotifyDueByOnSwitch());
}

void CToDoListWnd::OnUpdateViewNext(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetTDCCount() > 1);
}

void CToDoListWnd::OnViewPrev() 
{
	if (GetTDCCount() < 2)
		return;
	
	int nPrev = GetSelToDoCtrl() - 1;
	
	if (nPrev < 0)
		nPrev = GetTDCCount() - 1;
	
	SelectToDoCtrl(nPrev, TRUE, Prefs().GetNotifyDueByOnSwitch());
}

void CToDoListWnd::OnUpdateViewPrev(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetTDCCount() > 1);
}

void CToDoListWnd::OnSysCommand(UINT nID, LPARAM lParam) 
{
	switch (nID)
	{
	case SC_MINIMIZE:
		// we don't minimize if we're going to be hiding to then system tray
		{
			m_hwndLastFocus = ::GetFocus();

			int nSysTrayOption = Prefs().GetSysTrayOption();
			
			if (nSysTrayOption == STO_ONMINIMIZE || nSysTrayOption == STO_ONMINCLOSE)
				MinimizeToTray();
			else
			{
				// SPECIAL FIX: Apparently when Ultramon hooks the minimize
				// button it ends up sending us a close message!
				ShowWindow(SW_MINIMIZE);
			}
			return; // eat it
		}

	case SC_HOTKEY:
		Show(FALSE);
		return;
		
	case SC_CLOSE:
		// don't allow closing whilst reloading tasklists
		if (m_bReloading)
			return;
		break;

	case SC_RESTORE:
	case SC_MAXIMIZE:
		PostMessage(WM_APPRESTOREFOCUS, 0L, (LPARAM)m_hwndLastFocus);
		break;
	}

	CFrameWnd::OnSysCommand(nID, lParam);
}


void CToDoListWnd::OnUpdateImport(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!GetToDoCtrl().IsReadOnly());
}

UINT CToDoListWnd::MapNewTaskPos(int nPos, BOOL bSubtask)
{
	if (!bSubtask) // task
	{
		switch (nPos)
		{
		case PUIP_TOP:		return ID_NEWTASK_ATTOP;
		case PUIP_BOTTOM:	return ID_NEWTASK_ATBOTTOM;
		case PUIP_BELOW:	return ID_NEWTASK_AFTERSELECTEDTASK;
			
		case PUIP_ABOVE: 
		default:			return ID_NEWTASK_BEFORESELECTEDTASK;
		}
	}
	else // subtask
	{
		if (nPos == PUIP_BOTTOM)
			return ID_NEWSUBTASK_ATBOTTOM;
		else
			return ID_NEWSUBTASK_ATTOP;
	}
}

UINT CToDoListWnd::GetNewTaskCmdID()
{
	return MapNewTaskPos(Prefs().GetNewTaskPos(), FALSE);
}

UINT CToDoListWnd::GetNewSubtaskCmdID()
{
	return MapNewTaskPos(Prefs().GetNewSubtaskPos(), TRUE);
}

void CToDoListWnd::OnNewtask() 
{
	// convert to users choice
	SendMessage(WM_COMMAND, GetNewTaskCmdID());
}

void CToDoListWnd::OnUpdateNewtask(CCmdUI* pCmdUI) 
{
	switch (GetNewTaskCmdID())
	{
	case ID_NEWTASK_ATTOPSELECTED:
		OnUpdateNewtaskAttopSelected(pCmdUI);
		break;

	case ID_NEWTASK_ATBOTTOMSELECTED:	
		OnUpdateNewtaskAtbottomSelected(pCmdUI);
		break;
	
	case ID_NEWTASK_AFTERSELECTEDTASK:
		OnUpdateNewtaskAfterselectedtask(pCmdUI);
		break;

	case ID_NEWTASK_BEFORESELECTEDTASK:
		OnUpdateNewtaskBeforeselectedtask(pCmdUI);
		break;

	case ID_NEWTASK_ATTOP:
		OnUpdateNewtaskAttop(pCmdUI);
		break;

	case ID_NEWTASK_ATBOTTOM:
		OnUpdateNewtaskAtbottom(pCmdUI);
		break;
	}
}

void CToDoListWnd::OnNewsubtask() 
{
	// convert to users choice
	SendMessage(WM_COMMAND, GetNewSubtaskCmdID());
}

void CToDoListWnd::OnUpdateNewsubtask(CCmdUI* pCmdUI) 
{
	switch (GetNewSubtaskCmdID())
	{
	case ID_NEWSUBTASK_ATTOP:
		OnUpdateNewsubtaskAttop(pCmdUI);
		break;

	case ID_NEWSUBTASK_ATBOTTOM:
		OnUpdateNewsubtaskAtBottom(pCmdUI);
		break;
	}
}

void CToDoListWnd::OnToolsCheckout() 
{
	int nSel = GetSelToDoCtrl();

	// sanity check
	if (!m_mgrToDoCtrls.CanCheckOut(nSel))
		return;
	
	CAutoFlag af(m_bSaving, TRUE);

	CString sCheckedOutTo;
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nSel);
	
	if (tdc.CheckOut(sCheckedOutTo))
	{
		m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nSel);
		m_mgrToDoCtrls.SetLastCheckoutStatus(nSel, TRUE);
		m_mgrToDoCtrls.RefreshLastModified(nSel);
		
		UpdateCaption();

		// update menu icon mgr
		m_mgrMenuIcons.ChangeImageID(ID_TOOLS_CHECKOUT, ID_TOOLS_CHECKIN);
	}
	else // failed
	{
		m_mgrToDoCtrls.SetLastCheckoutStatus(nSel, FALSE);
		
		// notify user
		CEnString sMessage;
		
		if (!sCheckedOutTo.IsEmpty())
		{
			sMessage.Format(IDS_CHECKEDOUTBYOTHER, tdc.GetFilePath(), sCheckedOutTo);
		}
		else
		{
			// if sCheckedOutTo is empty then the error is most likely because
			// the file has been deleted or cannot be opened for editing
			sMessage.Format(IDS_CHECKOUTFAILED, tdc.GetFilePath());
		}
		
		MessageBox(sMessage, IDS_CHECKOUT_TITLE, MB_OK | MB_ICONEXCLAMATION);
	}
}

void CToDoListWnd::OnUpdateToolsCheckout(CCmdUI* pCmdUI) 
{
	int nSel = GetSelToDoCtrl();
	
	pCmdUI->Enable(m_mgrToDoCtrls.CanCheckOut(nSel));
}

void CToDoListWnd::OnToolsToggleCheckin() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (tdc.IsCheckedOut())
		OnToolsCheckin();
	else
		OnToolsCheckout();
}

void CToDoListWnd::OnUpdateToolsToggleCheckin(CCmdUI* pCmdUI) 
{
	int nSel = GetSelToDoCtrl();
	BOOL bEnable = m_mgrToDoCtrls.CanCheckInOut(nSel);
	
	pCmdUI->Enable(bEnable);
	pCmdUI->SetCheck(bEnable && GetToDoCtrl().IsCheckedOut() ? 1 : 0);
}

void CToDoListWnd::OnToolsCheckin() 
{
	int nSel = GetSelToDoCtrl();

	// sanity check
	if (!m_mgrToDoCtrls.CanCheckIn(nSel))
		return;
	
	CAutoFlag af(m_bSaving, TRUE);
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nSel);

	ASSERT (!tdc.GetFilePath().IsEmpty());
	
	tdc.Flush();
	
	// save modifications
	TDC_FILE nSave = TDCO_SUCCESS;

	if (tdc.IsModified())
	{
		if (Prefs().GetConfirmSaveOnExit())
		{
			CString sName = m_mgrToDoCtrls.GetFriendlyProjectName(nSel);
			CEnString sMessage(IDS_SAVEBEFORECHECKIN, sName);
			
			int nRet = MessageBox(sMessage, IDS_CHECKIN_TITLE, MB_YESNOCANCEL);
			
			switch (nRet)
			{
			case IDYES:
				nSave = SaveTaskList(nSel);
				break;
				
			case IDNO:
				ReloadTaskList(nSel, FALSE, FALSE);
				break;
				
			case IDCANCEL:
				return;
			}
		}
		else
			SaveTaskList(nSel); 
	}
	
	if (nSave == TDCO_SUCCESS && tdc.CheckIn())	
	{
		m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nSel);
		m_mgrToDoCtrls.RefreshLastModified(nSel);
		UpdateCaption();

		// update menu icon mgr
		m_mgrMenuIcons.ChangeImageID(ID_TOOLS_CHECKIN, ID_TOOLS_CHECKOUT);
	}

	m_mgrToDoCtrls.SetLastCheckoutStatus(nSel, TRUE); // so we won't try to immediately check it out again
}

void CToDoListWnd::OnUpdateToolsCheckin(CCmdUI* pCmdUI) 
{
	int nSel = GetSelToDoCtrl();
	
	pCmdUI->Enable(m_mgrToDoCtrls.CanCheckIn(nSel));
}

void CToDoListWnd::OnExport() 
{
	const CPreferencesDlg& userPrefs = Prefs();
	
	int nTDCCount = GetTDCCount(), nSelTDC = GetSelToDoCtrl();
	ASSERT (nTDCCount >= 1);

	CTDLExportDlg dialog(m_mgrImportExport, 
						nTDCCount == 1, 
						GetToDoCtrl().GetView(),
						userPrefs.GetExportVisibleColsOnly(), 
						m_mgrToDoCtrls.GetFilePath(nSelTDC, FALSE), 
						userPrefs.GetAutoExportFolderPath());
	
	// keep showing the dialog until either the user
	// selects a filename which does not match a tasklist
	// or they confirm that they want to overwrite the tasklist
	CString sExportPath;
	BOOL bOverWrite = FALSE;
	int nFormat = -1;

	while (!bOverWrite)
	{
		if (dialog.DoModal() != IDOK)
			return;

		sExportPath = dialog.GetExportPath();
		nFormat = dialog.GetExportFormat();

		// interested in overwriting single files
		if (nTDCCount == 1 || !dialog.GetExportAllTasklists() || dialog.GetExportOneFile())
		{
			// check with user if they are about to override a tasklist
			if (m_mgrToDoCtrls.FindToDoCtrl(sExportPath) != -1)
			{
				UINT nRet = MessageBox(IDS_CONFIRM_EXPORT_OVERWRITE, 0, MB_YESNOCANCEL, sExportPath);

				if (nRet == IDCANCEL)
					return;

				// else
				bOverWrite = (nRet == IDYES);
			}
			else
				bOverWrite = TRUE; // nothing to fear
		}
		else // check all open tasklists
		{
			CString sFilePath, sExt = m_mgrImportExport.GetExporterFileExtension(nFormat);
		
			for (int nCtrl = 0; nCtrl < nTDCCount; nCtrl++)
			{
				CString sPath = m_mgrToDoCtrls.GetFilePath(nCtrl);
				CString sFName;
						
				FileMisc::SplitPath(sPath, NULL, NULL, &sFName);
				FileMisc::MakePath(sFilePath, NULL, sExportPath, sFName, sExt);

				if (m_mgrToDoCtrls.FindToDoCtrl(sFilePath) != -1)
				{
					UINT nRet = MessageBox(IDS_CONFIRM_EXPORT_OVERWRITE, 0, MB_YESNOCANCEL, sFilePath);

					if (nRet == IDCANCEL)
						return;

					// else
					bOverWrite = (nRet == IDYES);
					break;
				}
			}

			// no matches?
			bOverWrite = TRUE; // nothing to fear
		}
	}

	UpdateWindow();

	// export
	DOPROGRESS(IDS_EXPORTPROGRESS)
	
	BOOL bHtmlComments = (nFormat == EXPTOHTML);

	if (nTDCCount == 1 || !dialog.GetExportAllTasklists())
	{
		// set the html image folder to be the output path with
		// an different extension
		CString sImgFolder;
		
		if (bHtmlComments)
		{
			sImgFolder = sExportPath;
			FileMisc::ReplaceExtension(sImgFolder, _T("html_images"));
			FileMisc::DeleteFolderContents(sImgFolder, TRUE);
		}
		
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nSelTDC);
		CTaskFile tasks;

		// Note: don't need to verify password if encrypted tasklist is active
		GetTasks(tdc, bHtmlComments, FALSE, dialog.GetTaskSelection(), tasks, sImgFolder);

		// add project name as report title
		CString sTitle = m_mgrToDoCtrls.GetFriendlyProjectName(nSelTDC);
		tasks.SetReportAttributes(sTitle);
		
		// save intermediate tasklist to file as required
		LogIntermediateTaskList(tasks, tdc.GetFilePath());

		if (m_mgrImportExport.ExportTaskList(&tasks, sExportPath, nFormat, FALSE))
		{
			// and preview
			if (userPrefs.GetPreviewExport())
				FileMisc::Run(*this, sExportPath, NULL, SW_SHOWNORMAL);
		}
	} 
	// multiple tasklists
	else if (dialog.GetExportOneFile())
	{
		CMultiTaskFile taskFiles;
		
		for (int nCtrl = 0; nCtrl < nTDCCount; nCtrl++)
		{
			// verify password
			if (nCtrl != nSelTDC && !VerifyToDoCtrlPassword(nCtrl))
				continue;
			
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
			tdc.LockWindowUpdate();
			
			// make sure it's loaded
			if (VerifyTaskListOpen(nCtrl, FALSE))
			{
				CTaskFile& tasks = taskFiles.GetTaskFile(nCtrl);
				tasks.Reset();
				
				// set the html image folder to be the output path with
				// an different extension
				CString sImgFolder;
				
				if (bHtmlComments)
				{
					sImgFolder = sExportPath;
					FileMisc::ReplaceExtension(sImgFolder, _T("html_images"));
				}
				
				GetTasks(tdc, bHtmlComments, FALSE, dialog.GetTaskSelection(), tasks, sImgFolder);
				
				// add project name as report title
				CString sTitle = m_mgrToDoCtrls.GetFriendlyProjectName(nCtrl);
				tasks.SetReportAttributes(sTitle);

				// save intermediate tasklist to file as required
				LogIntermediateTaskList(tasks, tdc.GetFilePath());
			}
			
			tdc.UnlockWindowUpdate();
		}
		
		Resize();
		
		if (m_mgrImportExport.ExportTaskLists(&taskFiles, sExportPath, nFormat, FALSE))
		{
			// and preview
			if (userPrefs.GetPreviewExport())
				FileMisc::Run(*this, sExportPath, NULL, SW_SHOWNORMAL);
		}
	}
	else // separate files
	{
		CString sExt = m_mgrImportExport.GetExporterFileExtension(nFormat);
		
		for (int nCtrl = 0; nCtrl < nTDCCount; nCtrl++)
		{
			// verify password
			if (nCtrl != nSelTDC && !VerifyToDoCtrlPassword(nCtrl))
				continue;
			
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
			tdc.LockWindowUpdate();
			
			// make sure it's loaded
			if (VerifyTaskListOpen(nCtrl, FALSE))
			{
				// build filepath if required (if exporter has an extension)
				CString sFilePath;
				BOOL bOverWrite = -1;
				
				if (!sExt.IsEmpty())
				{
					CString sPath = m_mgrToDoCtrls.GetFilePath(nCtrl);
					
					// if the file has not been saved before we use the tab text
					// and prompt the user to confirm
					if (sPath.IsEmpty())
					{
						sPath = m_mgrToDoCtrls.GetFilePath(nCtrl, FALSE);
						FileMisc::MakePath(sFilePath, NULL, sExportPath, sPath, sExt);
						
						CFileSaveDialog dlgFile(IDS_EXPORTFILEAS_TITLE,
												sExt, 
												sFilePath, 
												EOFN_DEFAULTSAVE,
												m_mgrImportExport.GetExporterFileFilter(nFormat));
						
						CPreferences prefs;

						if (dlgFile.DoModal(&prefs) == IDOK)
							sFilePath = dlgFile.GetPathName();
						else
							continue; // next tasklist
					}
					else
					{
						CString sFName;

						FileMisc::SplitPath(sPath, NULL, NULL, &sFName);
						FileMisc::MakePath(sFilePath, NULL, sExportPath, sFName, sExt);
					}
				}
				
				// set the html image folder to be the output path with
				// an different extension
				CString sImgFolder;
				
				if (bHtmlComments)
				{
					sImgFolder = sFilePath;
					FileMisc::ReplaceExtension(sImgFolder, _T("html_images"));
				}
				
				CTaskFile tasks;
				GetTasks(tdc, bHtmlComments, FALSE, dialog.GetTaskSelection(), tasks, sImgFolder);
				
				// add project name as report title
				CString sTitle = m_mgrToDoCtrls.GetFriendlyProjectName(nCtrl);
				tasks.SetReportAttributes(sTitle);

				// save intermediate tasklist to file as required
				LogIntermediateTaskList(tasks, tdc.GetFilePath());

				m_mgrImportExport.ExportTaskList(&tasks, sFilePath, nFormat, FALSE);
			}
			
			tdc.UnlockWindowUpdate();
		}
	}
}

int CToDoListWnd::GetTasks(CFilteredToDoCtrl& tdc, BOOL bHtmlComments, BOOL bTransform, 
						  TSD_TASKS nWhatTasks, TDCGETTASKS& filter, DWORD dwSelFlags, 
						  CTaskFile& tasks, LPCTSTR szHtmlImageDir) const
{
	// preferences
	const CPreferencesDlg& userPrefs = Prefs();
	
	// project name
	tasks.SetProjectName(tdc.GetFriendlyProjectName());
	tasks.SetCharSet(userPrefs.GetHtmlCharSet());
	
	// export flags
	filter.dwFlags |= TDCGTF_FILENAME;

	if (userPrefs.GetExportParentTitleCommentsOnly())
		filter.dwFlags |= TDCGTF_PARENTTITLECOMMENTSONLY;

	if (bHtmlComments)
	{
		filter.dwFlags |= TDCGTF_HTMLCOMMENTS;
		tasks.SetHtmlImageFolder(szHtmlImageDir);

		// And delete all existing images in that folder
		if (szHtmlImageDir && *szHtmlImageDir)
			FileMisc::DeleteFolderContents(szHtmlImageDir, TRUE);

		if (bTransform)
		{
			ASSERT(bHtmlComments);
			filter.dwFlags |= TDCGTF_TRANSFORM;
		}
	}

	// get the tasks
	tdc.Flush();
	
	switch (nWhatTasks)
	{
	case TSDT_ALL:
		{
			// if there's a filter present then we toggle it off 
			// grab the tasks and then toggle it back on
			BOOL bNeedToggle = (tdc.HasFilter() || tdc.HasCustomFilter());

			if (bNeedToggle)
			{
				::LockWindowUpdate(tdc.GetSafeHwnd());
				tdc.ToggleFilter();
			}

			tdc.GetTasks(tasks, filter);

			if (bNeedToggle)
			{
				tdc.ToggleFilter();
				::LockWindowUpdate(NULL);
			}
		}
		break;

	case TSDT_FILTERED:
		// if no filter is present then this just gets the whole lot
		tdc.GetFilteredTasks(tasks, filter);
		break;

	case TSDT_SELECTED:
		tdc.GetSelectedTasks(tasks, filter, dwSelFlags);
		break;
	}
	
	// delete the HTML image folder if it is empty
	// this will fail if it is not empty.
	if (bHtmlComments && szHtmlImageDir && *szHtmlImageDir)
		RemoveDirectory(szHtmlImageDir);
	
	return tasks.GetTaskCount();
}

int CToDoListWnd::GetTasks(CFilteredToDoCtrl& tdc, BOOL bHtmlComments, BOOL bTransform, 
							const CTaskSelectionDlg& taskSel, CTaskFile& tasks, LPCTSTR szHtmlImageDir) const
{
	DWORD dwSelFlags = 0;
    TSD_TASKS nWhatTasks = taskSel.GetWantWhatTasks();
	
	if (taskSel.GetWantSelectedTasks())
	{
		if (!taskSel.GetWantSelectedSubtasks()) 
			dwSelFlags |= TDCGSTF_NOTSUBTASKS;

		if (taskSel.GetWantSelectedParentTask())
			dwSelFlags |= TDCGSTF_IMMEDIATEPARENT;
	}

	TDC_GETTASKS nFilter = TDCGT_ALL;
	
	// build filter
	if (taskSel.GetWantCompletedTasks() && !taskSel.GetWantInCompleteTasks())
		nFilter = TDCGT_DONE;
		
	else if (!taskSel.GetWantCompletedTasks() && taskSel.GetWantInCompleteTasks())
		nFilter = TDCGT_NOTDONE;
		
	TDCGETTASKS filter(nFilter);

	// attributes to export
	switch (taskSel.GetAttributeOption())
	{
	case TSDA_ALL:
		break;

	case TSDA_VISIBLE:
		{
			CTDCColumnIDArray aCols;
			tdc.GetVisibleColumns(aCols);

			MapColumnsToAttributes(aCols, filter.aAttribs);

			if (taskSel.GetWantCommentsWithVisible())
				filter.aAttribs.Add(TDCA_COMMENTS);

			filter.aAttribs.Add(TDCA_CUSTOMATTRIB); // always
		}
		break;

	case TSDA_USER:
		taskSel.GetUserAttributes(filter.aAttribs);
		filter.dwFlags |= TDCGTF_USERCOLUMNS;
		break;
	}
	
	// get the tasks
   return GetTasks(tdc, bHtmlComments, bTransform, nWhatTasks, filter, dwSelFlags, tasks, szHtmlImageDir);
}

void CToDoListWnd::OnUpdateExport(CCmdUI* pCmdUI) 
{
	// make sure at least one control has items
	int nCtrl = GetTDCCount();
	
	while (nCtrl--)
	{
		if (GetToDoCtrl().GetTaskCount())
		{
			pCmdUI->Enable(TRUE);
			return;
		}
	}
	
	// else
	pCmdUI->Enable(FALSE);
}

void CToDoListWnd::OnToolsTransformactivetasklist() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	// pass the project name as the title field
	CString sTitle = tdc.GetProjectName();
	CTDLTransformDialog dialog(sTitle, tdc.GetView());
	
	if (dialog.DoModal() != IDOK)
		return;
	
	CString sStylesheet = dialog.GetStylesheet();
	sTitle = dialog.GetTitle();
	
	// output path
	CString sOutputPath(tdc.GetFilePath()); 
	{
		if (!sOutputPath.IsEmpty())
			FileMisc::ReplaceExtension(sOutputPath, _T("html"));

		CPreferences prefs;
		CFileSaveDialog dialog(IDS_SAVETRANSFORM_TITLE,
								_T("html"), 
								sOutputPath, 
								OFN_OVERWRITEPROMPT, 
								CEnString(IDS_TRANSFORMFILEFILTER), 
								this);
		
		if (dialog.DoModal(&prefs) != IDOK)
			return; // user elected not to proceed
		
		sOutputPath = dialog.GetPathName();
	}
	
	// export
	DOPROGRESS(IDS_TRANSFORMPROGRESS)

	// set the html image folder to be the same as the 
	// output path without the extension
	CString sHtmlImgFolder(sOutputPath);
	FileMisc::ReplaceExtension(sHtmlImgFolder, _T("html_images"));
	
	CTaskFile tasks;
	GetTasks(tdc, TRUE, TRUE, dialog.GetTaskSelection(), tasks, sHtmlImgFolder);

	// add title and date 
	COleDateTime date;

	if (dialog.GetWantDate())
		date = COleDateTime::GetCurrentTime();

	tasks.SetReportAttributes(sTitle, date);
	
	// save intermediate tasklist to file as required
	LogIntermediateTaskList(tasks, tdc.GetFilePath());
	
	if (tasks.TransformToFile(sStylesheet, sOutputPath, Prefs().GetHtmlCharSet()))
	{
		// preview
		if (Prefs().GetPreviewExport())
			FileMisc::Run(*this, sOutputPath, NULL, SW_SHOWNORMAL);
	}
}

BOOL CToDoListWnd::LogIntermediateTaskList(CTaskFile& tasks, LPCTSTR szRefPath)
{
	if (FileMisc::IsLoggingEnabled())
	{
		CString sRefName = FileMisc::RemoveExtension(FileMisc::GetFileNameFromPath(szRefPath));
		CString sTempTaskPath = FileMisc::GetTempFileName(sRefName, _T("intermediate.txt")); 

		return tasks.Save(sTempTaskPath);
	}

	// else
	return TRUE;
}

void CToDoListWnd::OnNexttopleveltask() 
{
	GetToDoCtrl().GotoNextTopLevelTask(TDCG_NEXT);
}

void CToDoListWnd::OnPrevtopleveltask() 
{
	GetToDoCtrl().GotoNextTopLevelTask(TDCG_PREV);
}

void CToDoListWnd::OnUpdateNexttopleveltask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanGotoNextTopLevelTask(TDCG_NEXT));
}

void CToDoListWnd::OnUpdatePrevtopleveltask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanGotoNextTopLevelTask(TDCG_PREV));
}

void CToDoListWnd::OnGotoNexttask() 
{
	GetToDoCtrl().GotoNextTask(TDCG_NEXT);
}

void CToDoListWnd::OnGotoPrevtask() 
{
	GetToDoCtrl().GotoNextTask(TDCG_PREV);
}

void CToDoListWnd::OnUpdateGotoPrevtask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanGotoNextTask(TDCG_PREV));
}

void CToDoListWnd::OnUpdateGotoNexttask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanGotoNextTask(TDCG_NEXT));
}
//------------------------------------------------------------------------

BOOL CToDoListWnd::InitFindDialog(BOOL bShow)
{
	if (!m_findDlg.GetSafeHwnd())
	{
		UpdateFindDialogCustomAttributes();

		VERIFY(m_findDlg.Initialize(this));

		if (CThemed::IsThemeActive())
			m_findDlg.SetUITheme(m_theme);

		if (bShow)
			m_findDlg.Show(bShow);
	}

	return (m_findDlg.GetSafeHwnd() != NULL);
}

void CToDoListWnd::OnFindTasks() 
{
	InitFindDialog();

	if (IsWindowVisible())
	{
		// remove results from encrypted tasklists but not from the 
		// active tasklist
		if (!m_findDlg.IsWindowVisible())
		{
			int nSelTDC = GetSelToDoCtrl();
			int nTDC = GetTDCCount();

			while (nTDC--)
			{
				const CFilteredToDoCtrl& tdc = GetToDoCtrl(nTDC);

				if (nTDC != nSelTDC && tdc.IsEncrypted())
					m_findDlg.DeleteResults(&tdc);
			}
		}
		m_findDlg.Show();
	}
	
	m_bFindShowing = TRUE;
}

LRESULT CToDoListWnd::OnFindDlgClose(WPARAM /*wp*/, LPARAM /*lp*/)
{
	m_bFindShowing = FALSE;
	GetToDoCtrl().SetFocusToTasks();

	return 0L;
}

LRESULT CToDoListWnd::OnFindDlgFind(WPARAM /*wp*/, LPARAM /*lp*/)
{
	// set up the search
	BOOL bSearchAll = m_findDlg.GetSearchAllTasklists();
	int nSelTaskList = GetSelToDoCtrl();
	
	int nFrom = bSearchAll ? 0 : nSelTaskList;
	int nTo = bSearchAll ? GetTDCCount() - 1 : nSelTaskList;
	
	// search params
	SEARCHPARAMS params;

	if (m_findDlg.GetSearchParams(params))
	{
		int nSel = GetSelToDoCtrl();
		int bFirst = TRUE;
		
		for (int nCtrl = nFrom; nCtrl <= nTo; nCtrl++)
		{
			// load or verify password unless tasklist is already active
			if (nCtrl != nSel)
			{
				// load if necessary (in which case the password will have been checked)
				if (!m_mgrToDoCtrls.IsLoaded(nCtrl))
				{
					if (!VerifyTaskListOpen(nCtrl, FALSE))
						continue;
				}
				else if (!VerifyToDoCtrlPassword(nCtrl))
					continue;
			}
			
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
			CResultArray aResults;
			CHoldRedraw hr(m_bFindShowing ? m_findDlg : NULL);
			
			if (tdc.FindTasks(params, aResults))
			{
				// use tasklist title from tabctrl
				CString sTitle = m_mgrToDoCtrls.GetTabItemText(nCtrl);
				
				m_findDlg.AddHeaderRow(sTitle, !bFirst);
				
				for (int nResult = 0; nResult < aResults.GetSize(); nResult++)
					AddFindResult(aResults[nResult], &tdc);
				
				bFirst = FALSE;
			}
		}
	}	
	
	// auto-select single results
/*	if (m_findDlg.GetResultCount() == 1)
	{
		CFTDResultsArray results;

		m_findDlg.GetAllResults(results);
		ASSERT (results.GetSize() == 1);
		
		if (OnFindSelectResult(0, (LPARAM)&results[0]))
			m_findDlg.Show(FALSE);	
	}
	else*/
		m_findDlg.SetActiveWindow();
	
	return 0;
}

void CToDoListWnd::AddFindResult(const SEARCHRESULT& result, const CFilteredToDoCtrl* pTDC)
{
	CString sTitle = pTDC->GetTaskTitle(result.dwTaskID);
	//CString sPath = pTDC->GetTaskPath(result.dwID);
	
	m_findDlg.AddResult(result, sTitle, /*sPath,*/ pTDC);
}

LRESULT CToDoListWnd::OnFindSelectResult(WPARAM /*wp*/, LPARAM lp)
{
	// extract Task ID
	FTDRESULT* pResult = (FTDRESULT*)lp;
	ASSERT (pResult->dwTaskID); 
	
	int nCtrl = m_mgrToDoCtrls.FindToDoCtrl(pResult->pTDC);
	ASSERT(nCtrl != -1);
	
	if (m_tabCtrl.GetCurSel() != nCtrl)
	{
		if (!SelectToDoCtrl(nCtrl, TRUE))
			return 0L;
	}
	
	// we can't use pResult->pTDC because it's const
	CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);
	tdc.SetFocusToTasks();
	
	if (tdc.GetSelectedTaskID() != pResult->dwTaskID)
	{
		if (!tdc.SelectTask(pResult->dwTaskID))
		{
			// perhaps the task is filtered out so we toggle the filter
			// and try again
			if (tdc.HasFilter())
			{
				tdc.ToggleFilter();

				// if that also fails, we restore the filter
				if (!tdc.SelectTask(pResult->dwTaskID))
					tdc.ToggleFilter();
			}
		}

		Invalidate();
		UpdateWindow();
	}
	
	return 1L;
}

LRESULT CToDoListWnd::OnFindSelectAll(WPARAM /*wp*/, LPARAM /*lp*/)
{
	if (!m_findDlg.GetResultCount())
		return 0;
	
	CWaitCursor cursor;
	
	for (int nTDC = 0; nTDC < GetTDCCount(); nTDC++)
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl(nTDC);
		tdc.DeselectAll();
		
		// collate the taskIDs
		CDWordArray aTaskIDs;
		m_findDlg.GetResultIDs(&tdc, aTaskIDs);

		// select them in one hit
		if (aTaskIDs.GetSize())
			tdc.MultiSelectItems(aTaskIDs, TSHS_SELECT, (nTDC == GetSelToDoCtrl()));
	}

	// if find dialog is floating then hide it
	if (!m_findDlg.IsDocked())
		m_findDlg.Show(FALSE);
	
	return 0;
}

LRESULT CToDoListWnd::OnFindApplyAsFilter(WPARAM /*wp*/, LPARAM lp)
{
	CString sCustom((LPCTSTR)lp);
	SEARCHPARAMS filter;
	m_findDlg.GetSearchParams(filter);

	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.SetCustomFilter(filter, sCustom);
	tdc.SetFocusToTasks();
	
	RefreshFilterBarCustomFilters();
	m_filterBar.RefreshFilterControls(tdc);

	// if find dialog is floating then hide it
	if (!m_findDlg.IsDocked())
		m_findDlg.Show(FALSE);
	
	return 0;
}

LRESULT CToDoListWnd::OnFindAddSearch(WPARAM /*wp*/, LPARAM /*lp*/)
{
	RefreshFilterBarCustomFilters();
	return 0;
}

LRESULT CToDoListWnd::OnFindDeleteSearch(WPARAM /*wp*/, LPARAM /*lp*/)
{
	RefreshFilterBarCustomFilters();
	return 0;
}

void CToDoListWnd::RefreshFilterBarCustomFilters()
{
	CStringArray aFilters;
	
	m_findDlg.GetSavedSearches(aFilters);

	// check for unnamed filter
	if (m_findDlg.GetSafeHwnd())
	{
		CEnString sUnNamed(IDS_UNNAMEDFILTER);

		if (m_findDlg.GetActiveSearch().IsEmpty() && Misc::Find(aFilters, sUnNamed, FALSE, FALSE) == -1)
			aFilters.Add(sUnNamed);
	}

	m_filterBar.AddCustomFilters(aFilters);
}

//------------------------------------------------------------------------

LRESULT CToDoListWnd::OnDropFile(WPARAM wParam, LPARAM lParam)
{
	TLDT_DATA* pData = (TLDT_DATA*)wParam;
	CWnd* pTarget = (CWnd*)lParam;

	if (pTarget == this) // we're the target
	{
		CString sFile = pData->pFilePaths ? pData->pFilePaths->GetAt(0) : _T("");

		if (FileMisc::HasExtension(sFile, _T("tdl")) || FileMisc::HasExtension(sFile, _T("xml"))) // tasklist
		{
			TDC_FILE nRes = OpenTaskList(sFile);
			HandleLoadTasklistError(nRes, sFile);
		}
	}

	return 0L;
}

void CToDoListWnd::OnViewMovetasklistright() 
{
	m_mgrToDoCtrls.MoveToDoCtrl(GetSelToDoCtrl(), 1);
}

void CToDoListWnd::OnUpdateViewMovetasklistright(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!Prefs().GetKeepTabsOrdered() &&
					m_mgrToDoCtrls.CanMoveToDoCtrl(GetSelToDoCtrl(), 1));
}

void CToDoListWnd::OnViewMovetasklistleft() 
{
	m_mgrToDoCtrls.MoveToDoCtrl(GetSelToDoCtrl(), -1);
}

void CToDoListWnd::OnUpdateViewMovetasklistleft(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!Prefs().GetKeepTabsOrdered() &&
					m_mgrToDoCtrls.CanMoveToDoCtrl(GetSelToDoCtrl(), -1));
}

void CToDoListWnd::OnToolsShowtasksDue(UINT nCmdID) 
{
	int nDueBy = PFP_DUETODAY;
	UINT nIDDueBy = IDS_NODUETODAY;
	
	switch (nCmdID)
	{
	case ID_TOOLS_SHOWTASKS_DUETODAY:
		break; // done
		
	case ID_TOOLS_SHOWTASKS_DUETOMORROW:
		nIDDueBy = IDS_NODUETOMORROW;
		nDueBy = PFP_DUETOMORROW;
		break;
		
	case ID_TOOLS_SHOWTASKS_DUEENDTHISWEEK:
		nIDDueBy = IDS_NODUETHISWEEK;
		nDueBy = PFP_DUETHISWEEK;
		break;
		
	case ID_TOOLS_SHOWTASKS_DUEENDNEXTWEEK:
		nIDDueBy = IDS_NODUENEXTWEEK;
		nDueBy = PFP_DUENEXTWEEK;
		break;
		
	case ID_TOOLS_SHOWTASKS_DUEENDTHISMONTH:
		nIDDueBy = IDS_NODUETHISMONTH;
		nDueBy = PFP_DUETHISMONTH;
		break;
		
	case ID_TOOLS_SHOWTASKS_DUEENDNEXTMONTH:
		nIDDueBy = IDS_NODUENEXTMONTH;
		nDueBy = PFP_DUENEXTMONTH;
		break;
		
	default:
		ASSERT(0);
		return;
	}
	
	if (!DoDueTaskNotification(nDueBy))
	{
		MessageBox(nIDDueBy, 0, MB_OK, m_mgrToDoCtrls.GetFriendlyProjectName(GetSelToDoCtrl()));
	}
}

void CToDoListWnd::ResetPrefs()
{
	delete m_pPrefs;
	m_pPrefs = new CPreferencesDlg(&m_mgrShortcuts, IDR_MAINFRAME, &m_mgrContent, &m_mgrImportExport);
	
	// update
	m_mgrToDoCtrls.SetPrefs(m_pPrefs); 
	
	// grab current colors
	Prefs().GetPriorityColors(m_aPriorityColors);

	m_filterBar.SetPriorityColors(m_aPriorityColors);
}

const CPreferencesDlg& CToDoListWnd::Prefs() const
{
	ASSERT (m_pPrefs);
	return *m_pPrefs;
}

void CToDoListWnd::OnSpellcheckcomments() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.SpellcheckSelectedTask(FALSE);
}

void CToDoListWnd::OnUpdateSpellcheckcomments(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.CanSpellcheckSelectedTaskComments());
}

void CToDoListWnd::OnSpellchecktitle() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.SpellcheckSelectedTask(TRUE);
}

void CToDoListWnd::OnUpdateSpellchecktitle(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(!tdc.GetSelectedTaskTitle().IsEmpty());
}

void CToDoListWnd::OnFileEncrypt() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	if (!tdc.IsReadOnly())
	{
		BOOL bWasEncrypted = tdc.IsEncrypted();
		CString sPassword = tdc.GetPassword();

		// if the tasklist is already encrypted then verify password
		// before allowing change
		if (!bWasEncrypted || VerifyToDoCtrlPassword())
			tdc.EnableEncryption(!tdc.IsEncrypted());

		// make sure we disable encryption on the archive too
		if (bWasEncrypted)
		{
			CString sArchive = m_mgrToDoCtrls.GetArchivePath(GetSelToDoCtrl());

			if (FileMisc::FileExists(sArchive))
			{
				CTaskFile archive(sPassword);

				if (archive.Load(sArchive))
				{
					archive.SetPassword(NULL); // remove password
					archive.Save(sArchive);
				}
			}
		}
	}

	UpdateAeroFeatures();
}

void CToDoListWnd::OnUpdateFileEncrypt(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(tdc.CanEncrypt() && !tdc.IsReadOnly());
	pCmdUI->SetCheck(tdc.IsEncrypted() ? 1 : 0);
}

void CToDoListWnd::OnFileResetversion() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (!tdc.IsReadOnly())
	{
		tdc.ResetFileVersion();
		tdc.SetModified();
		
		UpdateStatusbar();
		UpdateCaption();
	}
}

void CToDoListWnd::OnUpdateFileResetversion(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(!tdc.IsReadOnly());
}

void CToDoListWnd::OnSpellcheckTasklist() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.Spellcheck();
}

void CToDoListWnd::OnUpdateSpellcheckTasklist(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.GetTaskCount());
}

TDC_FILE CToDoListWnd::SaveAll(DWORD dwFlags)
{
	TDC_FILE nSaveAll = TDCO_SUCCESS;
	int nCtrl = GetTDCCount();

	BOOL bIncUnsaved = Misc::HasFlag(dwFlags, TDLS_INCLUDEUNSAVED);
	BOOL bClosingWindows = Misc::HasFlag(dwFlags, TDLS_CLOSINGWINDOWS);
	BOOL bClosingAll = Misc::HasFlag(dwFlags, TDLS_CLOSINGTASKLISTS);		

	// scoped to end status bar progress
	// before calling UpdateStatusbar
	{
		DOPROGRESS(IDS_SAVINGPROGRESS)

		while (nCtrl--)
		{
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nCtrl);

			// bypass unsaved tasklists unless closing Windows
			if (!bClosingWindows && !bIncUnsaved && tdc.GetFilePath().IsEmpty())
				continue;
			
			if (Misc::HasFlag(dwFlags, TDLS_FLUSH))
				tdc.Flush(bClosingAll);		

			TDC_FILE nSave = ConfirmSaveTaskList(nCtrl, dwFlags);

			if (nSave == TDCO_CANCELLED) // user cancelled
				return TDCO_CANCELLED;

			// else cache any failure w/o overwriting previous
			if (nSaveAll == TDCO_SUCCESS)
				nSaveAll = nSave;

			m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
		}
	}
	
	if (!bClosingWindows)
	{
		UpdateCaption();
		UpdateStatusbar();
	}
	
    return nSaveAll;
}

void CToDoListWnd::OnEditTimeTrackTask() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.TimeTrackSelectedTask();
}

void CToDoListWnd::OnUpdateEditTimeTrackTask(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(tdc.CanTimeTrackSelectedTask());
	pCmdUI->SetCheck(tdc.IsSelectedTaskBeingTimeTracked() ? 1 : 0);
}

void CToDoListWnd::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	if (nIDCtl == IDC_TABCONTROL)
	{
		TDCM_DUESTATUS nStatus = m_mgrToDoCtrls.GetDueItemStatus(lpDrawItemStruct->itemID);

		if (nStatus == TDCM_PAST || nStatus == TDCM_TODAY)
		{
			// determine appropriate due colour
			COLORREF crDue, crDueToday;

			GetToDoCtrl(lpDrawItemStruct->itemID).GetDueTaskColors(crDue, crDueToday);

			COLORREF crTag = (nStatus == TDCM_PAST) ? crDue : crDueToday;

			if (crTag != CLR_NONE)
			{
				CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
				const CRect& rect = lpDrawItemStruct->rcItem;

				// draw a little tag in the top left corner
				for (int nHPos = 0; nHPos < 6; nHPos++)
				{
					for (int nVPos = 0; nVPos < 6 - nHPos; nVPos++)
					{
						pDC->SetPixelV(rect.left + nHPos, rect.top + nVPos, crTag);
					}
				}

				// draw a black line between the two
				pDC->SelectStockObject(BLACK_PEN);
				pDC->MoveTo(rect.left, rect.top + 6);
				pDC->LineTo(rect.left + 7, rect.top - 1);
			}
		}
		return;
	}
	else if (nIDCtl == 0 && lpDrawItemStruct->itemID == ID_CLOSE)
	{
		if (m_menubar.DrawMDIButton(lpDrawItemStruct))
			return;
	}

	CFrameWnd::OnDrawItem(nIDCtl, lpDrawItemStruct);
} 

void CToDoListWnd::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
{
	if (nIDCtl == 0 && lpMeasureItemStruct->itemID == ID_CLOSE)
	{
		if (m_menubar.MeasureMDIButton(lpMeasureItemStruct))
			return;
	}
	
	CFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}

void CToDoListWnd::OnViewNextSel() 
{
	GetToDoCtrl().SelectNextTasksInHistory();
}

void CToDoListWnd::OnUpdateViewNextSel(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanSelectNextTasksInHistory());
}

void CToDoListWnd::OnViewPrevSel() 
{
	GetToDoCtrl().SelectPrevTasksInHistory();
}

void CToDoListWnd::OnUpdateViewPrevSel(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanSelectPrevTasksInHistory());
}

void CToDoListWnd::OnSplitTaskIntoPieces(UINT nCmdID) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nNumPieces = 2 + (nCmdID - ID_NEWTASK_SPLITTASKINTO_TWO);
	
	tdc.SplitSelectedTask(nNumPieces);
}

void CToDoListWnd::OnUpdateSplitTaskIntoPieces(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(tdc.CanSplitSelectedTask());
}

void CToDoListWnd::OnViewExpandtask() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_SELECTED, TRUE);
}

void CToDoListWnd::OnUpdateViewExpandtask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_SELECTED, TRUE));
}

void CToDoListWnd::OnViewCollapsetask() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_SELECTED, FALSE);
}

void CToDoListWnd::OnUpdateViewCollapsetask(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_SELECTED, FALSE));
}

void CToDoListWnd::OnViewExpandall() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_ALL, TRUE);
}

void CToDoListWnd::OnUpdateViewExpandall(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_ALL, TRUE));
}

void CToDoListWnd::OnViewCollapseall() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_ALL, FALSE);
}

void CToDoListWnd::OnUpdateViewCollapseall(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_ALL, FALSE));
}

void CToDoListWnd::OnViewExpandDuetasks() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_DUE, TRUE);
}

void CToDoListWnd::OnUpdateViewExpandDuetasks(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_DUE, TRUE));
}

void CToDoListWnd::OnViewCollapseDuetasks() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_DUE, FALSE);
}

void CToDoListWnd::OnUpdateViewCollapseDuetasks(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_DUE, FALSE));
}

void CToDoListWnd::OnViewExpandStartedtasks() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_STARTED, TRUE);
}

void CToDoListWnd::OnUpdateViewExpandStartedtasks(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_STARTED, TRUE));
}

void CToDoListWnd::OnViewCollapseStartedtasks() 
{
	GetToDoCtrl().ExpandTasks(TDCEC_STARTED, FALSE);
}

void CToDoListWnd::OnUpdateViewCollapseStartedtasks(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanExpandTasks(TDCEC_STARTED, FALSE));
}

void CToDoListWnd::OnViewToggletaskexpanded() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	tdc.ExpandTasks(TDCEC_SELECTED, tdc.CanExpandTasks(TDCEC_SELECTED, TRUE));
}

void CToDoListWnd::OnUpdateViewToggletaskexpanded(CCmdUI* pCmdUI) 
{
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();

	pCmdUI->Enable(tdc.CanExpandTasks(TDCEC_SELECTED, TRUE) || tdc.CanExpandTasks(TDCEC_SELECTED, FALSE));
}

void CToDoListWnd::OnWindow(UINT nCmdID) 
{
	int nTDC = (int)(nCmdID - ID_WINDOW1);
	
	if (nTDC < GetTDCCount())
		SelectToDoCtrl(nTDC, (nTDC != GetSelToDoCtrl()), Prefs().GetNotifyDueByOnSwitch());
}

void CToDoListWnd::OnUpdateWindow(CCmdUI* pCmdUI) 
{
	if (pCmdUI->m_pMenu)
	{
		ASSERT (ID_WINDOW1 == pCmdUI->m_nID);
		const UINT MAXWINDOWS = 16;
		int nWnd;
		
		// delete existing tool entries first
		for (nWnd = 0; nWnd < MAXWINDOWS; nWnd++)
			pCmdUI->m_pMenu->DeleteMenu(ID_WINDOW1 + nWnd, MF_BYCOMMAND);
		
		int nSel = GetSelToDoCtrl();
		int nPos = 0, nTDCCount = GetTDCCount();
		ASSERT (nTDCCount);

		nTDCCount = min(nTDCCount, MAXWINDOWS);
		
		for (nWnd = 0; nWnd < nTDCCount; nWnd++)
		{
			CFilteredToDoCtrl& tdc = GetToDoCtrl(nWnd);
			
			CString sMenuItem;
			sMenuItem.Format(_T("&%d (%s)"), (nPos + 1) % 10, tdc.GetFriendlyProjectName());
			
			UINT nFlags = MF_BYPOSITION | MF_STRING | (nSel == nWnd ? MF_CHECKED : MF_UNCHECKED);
			pCmdUI->m_pMenu->InsertMenu(pCmdUI->m_nIndex++, nFlags, ID_WINDOW1 + nWnd, sMenuItem);
			
			nPos++;
		}
		
		// update end menu count
		pCmdUI->m_nIndex--; // point to last menu added
		pCmdUI->m_nIndexMax = pCmdUI->m_pMenu->GetMenuItemCount();
		
		pCmdUI->m_bEnableChanged = TRUE;    // all the added items are enabled
	}
}

#if _MSC_VER >= 1400
void CToDoListWnd::OnActivateApp(BOOL bActive, DWORD dwThreadID)
#else
void CToDoListWnd::OnActivateApp(BOOL bActive, HTASK hTask) 
#endif
{
	// don't activate when in the middle of loading
	if (m_bReloading && !bActive)
		return;

#if _MSC_VER >= 1400
	CFrameWnd::OnActivateApp(bActive, dwThreadID);
#else
	CFrameWnd::OnActivateApp(bActive, hTask);
#endif
	
	// don't do any further processing if closing
    if (m_bClosing)
        return; 

	if (!bActive)
	{
		// save focus to restore when we next get activated
		HWND hFocus = ::GetFocus();

		if (hFocus)
			m_hwndLastFocus = hFocus;

		// save tasklists if required
		if (Prefs().GetAutoSaveOnSwitchApp())
			SaveAll(TDLS_FLUSH | TDLS_AUTOSAVE);
	}
	else
	{
		if (GetTDCCount() && (!m_hwndLastFocus || Prefs().GetAutoFocusTasklist()))
			PostMessage(WM_APPRESTOREFOCUS, 0L, (LPARAM)GetToDoCtrl().GetSafeHwnd());
		
		else if (m_hwndLastFocus)
		{
			// delay the restoration of focus else it gets lost
			PostMessage(WM_APPRESTOREFOCUS, 0L, (LPARAM)m_hwndLastFocus);
		}

		UpdateCwd();
	}
}

LRESULT CToDoListWnd::OnAppRestoreFocus(WPARAM /*wp*/, LPARAM lp)
{
	HWND hWnd = (HWND)lp;

	if (GetTDCCount() && hWnd == GetToDoCtrl().GetSafeHwnd())
		GetToDoCtrl().SetFocusToTasks();
	else
		return (LRESULT)::SetFocus(hWnd);

	return 0L;
}

void CToDoListWnd::UpdateCwd()
{
	// set cwd to active tasklist
	if (GetTDCCount())
	{
		CString sFolder	= FileMisc::GetFolderFromFilePath(m_mgrToDoCtrls.GetFilePath(GetSelToDoCtrl()));

		if (FileMisc::FolderExists(sFolder))
			SetCurrentDirectory(sFolder);
	}
}

BOOL CToDoListWnd::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	UpdateWindow();

	return CFrameWnd::OnCommand(wParam, lParam);
}

void CToDoListWnd::OnEnable(BOOL bEnable) 
{
	CFrameWnd::OnEnable(bEnable);
	
	// save current focus because modal window is being shown
	if (!bEnable)
	{
		HWND hFocus = ::GetFocus();

		if (hFocus)
			m_hwndLastFocus = hFocus;
	}
	// then restore it when we are enabled
	else if (m_hwndLastFocus)
	{
		UpdateWindow();
		PostMessage(WM_APPRESTOREFOCUS, 0L, (LPARAM)m_hwndLastFocus);
	}
}

void CToDoListWnd::OnViewSorttasklisttabs() 
{
	int nSel = m_mgrToDoCtrls.SortToDoCtrlsByName();
	SelectToDoCtrl(nSel, FALSE);
}

void CToDoListWnd::OnUpdateViewSorttasklisttabs(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetTDCCount() > 1 && !Prefs().GetKeepTabsOrdered());
}

void CToDoListWnd::OnEditInctaskpercentdone() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.IncrementSelectedTaskPercentDone(TRUE);
}

void CToDoListWnd::OnUpdateEditInctaskpercentdone(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnEditDectaskpercentdone() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.IncrementSelectedTaskPercentDone(FALSE);
}

void CToDoListWnd::OnUpdateEditDectaskpercentdone(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnEditDectaskpriority() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.IncrementSelectedTaskPriority(FALSE);
}

void CToDoListWnd::OnUpdateEditDectaskpriority(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnEditInctaskpriority() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.IncrementSelectedTaskPriority(TRUE);
}

void CToDoListWnd::OnUpdateEditInctaskpriority(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnEditFlagtask() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.SetSelectedTaskFlag(!tdc.IsSelectedTaskFlagged());
}

void CToDoListWnd::OnUpdateEditFlagtask(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
	pCmdUI->SetCheck(tdc.IsSelectedTaskFlagged() ? 1 : 0);
}

void CToDoListWnd::OnEditGotoDependency() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.GotoSelectedTaskDependency();
}

void CToDoListWnd::OnUpdateEditGotoDependency(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	CStringArray aDepends;

	pCmdUI->Enable(tdc.GetSelectedTaskDependencies(aDepends) > 0);	
}

void CToDoListWnd::OnEditRecurrence() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.EditSelectedTaskRecurrence();	
}

void CToDoListWnd::OnUpdateEditRecurrence(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();

	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnFileOpenarchive() 
{
	CString sArchivePath = m_mgrToDoCtrls.GetArchivePath(GetSelToDoCtrl());
	BOOL bArchiveExists = FileMisc::FileExists(sArchivePath);
	
	if (bArchiveExists)
		OpenTaskList(sArchivePath, FALSE);
}

void CToDoListWnd::OnUpdateFileOpenarchive(CCmdUI* pCmdUI) 
{
	CString sArchivePath = m_mgrToDoCtrls.GetArchivePath(GetSelToDoCtrl());
	BOOL bArchiveExists = FileMisc::FileExists(sArchivePath);
	
	pCmdUI->Enable(bArchiveExists);
}

void CToDoListWnd::PrepareFilter(FTDCFILTER& filter) const
{
	if (filter.nShow != FS_CUSTOM)
	{
		// handle title filter option
		switch (Prefs().GetTitleFilterOption())
		{
		case PUIP_MATCHONTITLECOMMENTS:
			filter.nTitleOption = FT_FILTERONTITLECOMMENTS;
			break;

		case PUIP_MATCHONANYTEXT:
			filter.nTitleOption = FT_FILTERONANYTEXT;
			break;

		case PUIP_MATCHONTITLE:
		default:
			filter.nTitleOption = FT_FILTERONTITLEONLY;
			break;
		}
	}
}

void CToDoListWnd::OnViewShowfilterbar() 
{
	m_bShowFilterBar = !m_bShowFilterBar;
	m_filterBar.ShowWindow(m_bShowFilterBar ? SW_SHOW : SW_HIDE);

	Resize();
	Invalidate(TRUE);
}

void CToDoListWnd::OnUpdateViewShowfilterbar(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bShowFilterBar ? 1 : 0);
}

void CToDoListWnd::OnViewClearfilter() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	if (tdc.HasFilter() || tdc.HasCustomFilter())
	{
		tdc.ClearFilter();
	
		// reenable the filter controls
		//m_filterBar.RemoveCustomFilters();
		m_filterBar.RefreshFilterControls(tdc);
	
		RefreshFilterControls();
		UpdateStatusbar();
	}
}

void CToDoListWnd::OnUpdateViewClearfilter(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(tdc.HasFilter() || tdc.HasCustomFilter());
}

void CToDoListWnd::OnViewTogglefilter()
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	tdc.ToggleFilter();

	RefreshFilterControls();
	UpdateStatusbar();

	// reenable the filter controls
	m_filterBar.SetCustomFilter(tdc.HasCustomFilter(), tdc.GetCustomFilterName());
}

void CToDoListWnd::OnUpdateViewTogglefilter(CCmdUI* pCmdUI)
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	pCmdUI->Enable(tdc.HasFilter() || tdc.HasLastFilter() || tdc.HasCustomFilter());
}

LRESULT CToDoListWnd::OnSelchangeFilter(WPARAM wp, LPARAM lp) 
{
	FTDCFILTER* pFilter = (FTDCFILTER*)wp;
	CString sCustom((LPCTSTR)lp);

	ASSERT(pFilter);

	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	if (pFilter->nShow == FS_CUSTOM)
	{
		SEARCHPARAMS params;

		if (sCustom.IsEmpty())
			m_findDlg.GetSearchParams(params);
		else
			m_findDlg.GetSearchParams(sCustom, params);

		tdc.SetCustomFilter(params, sCustom);
	}
	else
	{
		PrepareFilter(*pFilter);
		tdc.SetFilter(*pFilter);
	}

	m_filterBar.RefreshFilterControls(tdc);

	UpdateStatusbar();

	return 0L;
}

void CToDoListWnd::OnViewFilter() 
{
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();
	CStringArray aCustom;
	m_filterBar.GetCustomFilters(aCustom);
	
	CTDLFilterDlg dialog(Prefs().GetMultiSelFilters());

	if (dialog.DoModal(aCustom, tdc, m_aPriorityColors) == IDOK)
	{
		FTDCFILTER filter;
		CString sCustom;
		
		dialog.GetFilter(filter, sCustom);

		OnSelchangeFilter((WPARAM)&filter, (LPARAM)(LPCTSTR)sCustom);
	}
}

void CToDoListWnd::OnUpdateViewFilter(CCmdUI* pCmdUI) 
{
	UNREFERENCED_PARAMETER(pCmdUI);
	//	pCmdUI->Enable(!m_bShowFilterBar);
}

void CToDoListWnd::OnViewRefreshfilter() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	FTDCFILTER filterTDC, filter;

	tdc.GetFilter(filterTDC);
	m_filterBar.GetFilter(filter);
	PrepareFilter(filter);
	
	// if the filter has changed then set the new one else
	// refresh the current one
	if (filterTDC == filter)
		tdc.RefreshFilter();	
	else
	{
		tdc.SetFilter(filter);
	
		if (Prefs().GetExpandTasksOnLoad())
			tdc.ExpandTasks(TDCEC_ALL);
	}

	UpdateStatusbar();
}

void CToDoListWnd::OnUpdateViewRefreshfilter(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	FTDCFILTER filterTDC, filter;

	tdc.GetFilter(filterTDC);
	m_filterBar.GetFilter(filter);
	
	pCmdUI->Enable(tdc.HasFilter() || (filter != filterTDC) || tdc.HasCustomFilter());
}

void CToDoListWnd::OnTabctrlPreferences() 
{
	DoPreferences(PREFPAGE_UI);
}

void CToDoListWnd::OnTasklistSelectColumns() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSel = GetSelToDoCtrl();

	CTDCColumnIDArray aColumns, aDefault;
	tdc.GetVisibleColumns(aColumns);
	Prefs().GetDefaultColumns(aDefault);

	CTDLColumnSelectionDlg dialog(aColumns, aDefault, TRUE);

	if (dialog.DoModal() == IDOK)
	{
		dialog.GetVisibleColumns(aColumns);

		tdc.SetVisibleColumns(aColumns);
		m_filterBar.SetVisibleFilters(aColumns);

		if (dialog.GetApplyActiveTasklist())
			m_mgrToDoCtrls.SetHasOwnColumns(nSel, TRUE);
		else
		{
			// update preferences
			ASSERT(m_pPrefs);
			m_pPrefs->SetDefaultColumns(aColumns);

			// and flag other tasklists as requiring updates
			m_mgrToDoCtrls.SetAllNeedPreferenceUpdate(TRUE, nSel);
			m_mgrToDoCtrls.SetHasOwnColumns(nSel, FALSE);
		}

		// reload the menu if we dynamically alter it
		if (Prefs().GetShowEditMenuAsColumns())
			LoadMenubar();

		Resize();
	}
}

void CToDoListWnd::OnViewProjectname() 
{
	m_bShowProjectName = !m_bShowProjectName;
	
	// mark all tasklists as needing update
	m_mgrToDoCtrls.SetAllNeedPreferenceUpdate(TRUE);
	
	// then update active tasklist
	GetToDoCtrl().SetStyle(TDCS_SHOWPROJECTNAME, m_bShowProjectName);
	m_mgrToDoCtrls.SetNeedsPreferenceUpdate(GetSelToDoCtrl(), FALSE);
}

void CToDoListWnd::OnUpdateViewProjectname(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bShowProjectName ? 1 : 0);
}

void CToDoListWnd::OnEditOffsetdates() 
{
	COffsetDatesDlg dialog;
	
	if (dialog.DoModal() == IDOK)
	{
		ODD_UNITS nUnits;
		int nAmount = dialog.GetOffsetAmount(nUnits);
		
		if (!nAmount)
			return;
		
		DWORD dwWhat = dialog.GetOffsetWhat();
		BOOL bSubtasks = dialog.GetOffsetSubtasks();
		
		// translate units
		int nTDCUnits = (nUnits == ODDU_DAYS) ? TDITU_DAYS :
						((nUnits == ODDU_WEEKS) ? TDITU_WEEKS : TDITU_MONTHS);
		
		// do the offsets
		CFilteredToDoCtrl& tdc = GetToDoCtrl();
		
		if (dwWhat & ODD_STARTDATE)
			tdc.OffsetSelectedTaskDate(TDCD_START, nAmount, nTDCUnits, bSubtasks);
		
		if (dwWhat & ODD_DUEDATE)
			tdc.OffsetSelectedTaskDate(TDCD_DUE, nAmount, nTDCUnits, bSubtasks);
		
		if (dwWhat & ODD_DONEDATE)
			tdc.OffsetSelectedTaskDate(TDCD_DONE, nAmount, nTDCUnits, bSubtasks);
	}
}

void CToDoListWnd::OnUpdateEditOffsetdates(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

void CToDoListWnd::OnPrintpreview() 
{
	DoPrint(TRUE);
}

void CToDoListWnd::OnShowTimelogfile() 
{
	CString sLogPath = GetToDoCtrl().GetSelectedTaskTimeLogPath();
	
	if (!sLogPath.IsEmpty())
		FileMisc::Run(*this, sLogPath, NULL, SW_HIDE);
}

void CToDoListWnd::OnUpdateShowTimelogfile(CCmdUI* pCmdUI) 
{
	const CPreferencesDlg& userPrefs = Prefs();
	int nTasks = GetToDoCtrl().GetSelectedCount();
	BOOL bEnable = FALSE;

	if (userPrefs.GetLogTimeTracking() && 
		(nTasks == 1 || !userPrefs.GetLogTaskTimeSeparately()))
	{
		CString sLogPath = GetToDoCtrl().GetSelectedTaskTimeLogPath();
		bEnable = FileMisc::FileExists(sLogPath);
	}
	
	pCmdUI->Enable(bEnable);	
}

void CToDoListWnd::OnAddtimetologfile() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	DWORD dwTaskID = tdc.GetSelectedTaskID();
	CString sTitle = tdc.GetSelectedTaskTitle();

	CTDLAddLoggedTimeDlg dialog(dwTaskID, sTitle);

	if (dialog.DoModal() == IDOK)
		tdc.AddTimeToTaskLogFile(dwTaskID, dialog.GetLoggedTime(), dialog.GetWhen(), dialog.GetAddToTimeSpent());
}

void CToDoListWnd::OnUpdateAddtimetologfile(CCmdUI* pCmdUI) 
{
	BOOL bEnable = (Prefs().GetLogTimeTracking() && GetToDoCtrl().GetSelectedCount() == 1);
	pCmdUI->Enable(bEnable);	
}

LRESULT CToDoListWnd::OnToDoCtrlDoLengthyOperation(WPARAM wParam, LPARAM lParam)
{
	if (wParam) // start op
	{
		m_sbProgress.BeginProgress(m_statusBar, (LPCTSTR)lParam);
	}
	else // end op
	{
		m_sbProgress.EndProgress();
	}
	
	return 0L;
}

BOOL CToDoListWnd::DoTaskLink(const CString& sPath, DWORD dwTaskID)
{
	// handle no file path => active tasklist
	if (sPath.IsEmpty())
	{
		ASSERT(dwTaskID);
		GetToDoCtrl().SelectTask(dwTaskID);

		return TRUE; // handled regardless of result
	}
	else
	{
		// build the full path to the file
		// from the folder of the active tasklist
		int nSelTDC = GetSelToDoCtrl();

		CString sActivePath = m_mgrToDoCtrls.GetFilePath(nSelTDC);
		CString sFolder = FileMisc::GetFolderFromFilePath(sActivePath);

		CString sFile(sPath);
		FileMisc::MakeFullPath(sFile, sFolder);

		// do we have this tasklist ?
		int nTDC = m_mgrToDoCtrls.FindToDoCtrl(sFile);

		if (nTDC != -1)
		{
			if (SelectToDoCtrl(nTDC, (nTDC != nSelTDC)) && dwTaskID)
				GetToDoCtrl().SelectTask(dwTaskID);

			return TRUE; // handled regardless of result
		}
		else if (!Prefs().GetMultiInstance())
		{
			TDC_FILE nRet = OpenTaskList(sFile);
			
			if (nRet == TDCO_SUCCESS)
			{
				if (dwTaskID)
					GetToDoCtrl().SelectTask(dwTaskID);
			}
			else
				HandleLoadTasklistError(nRet, sFile);

			return TRUE; // handled regardless of result
		}
	}

	// not handled
	return FALSE;
}

LRESULT CToDoListWnd::OnToDoCtrlDoTaskLink(WPARAM wParam, LPARAM lParam)
{
	DWORD dwTaskID = wParam;
	CString sFile((LPCTSTR)lParam);
	
	// can we handle it ?
	if (DoTaskLink(sFile, dwTaskID))
		return TRUE;

	// Pass to our app startup code to look 
	// for another instance who can handle it
	CString sCommandline;
		
	sCommandline.Format(_T("%s -l \"%s?%ld\""),
						FileMisc::GetAppFileName(),
						sFile,
						dwTaskID);

	return FileMisc::Run(*this, sCommandline);
}

LRESULT CToDoListWnd::OnTodoCtrlFailedLink(WPARAM /*wParam*/, LPARAM lParam)
{
	LPCTSTR szLink = (LPCTSTR)lParam;

	// if it's an Outlook link then prompt to install
	// the Outlook URL handler
	if (COutlookHelper::IsOutlookUrl(szLink))
	{
		// if the handler installs properly give the url another go
		if (COutlookHelper::QueryInstallUrlHandler(IDS_QUERYINSTALLOUTLOOKHANDLER))
			FileMisc::Run(*this, szLink);

		return TRUE; // we handled it regardless
	}
	else // see if it's a task link
	{
		CString sFile;
		DWORD dwTaskID = 0;

		CFilteredToDoCtrl::ParseTaskLink(szLink, dwTaskID, sFile);

		if (DoTaskLink(sFile, dwTaskID))
			return TRUE; // we handled it
	}

	// all else
	AfxMessageBox(IDS_COMMENTSGOTOERRMSG);
	return 0L;
}

LRESULT CToDoListWnd::OnToDoCtrlTaskIsDone(WPARAM wParam, LPARAM lParam)
{
	ASSERT (lParam);
	CString sFile((LPCTSTR)lParam);
	
	if (!sFile.IsEmpty())
	{
		// build the full path to the file
		if (::PathIsRelative(sFile))
		{
			// append it to the folder containing the active tasklist
			CString sPathName = m_mgrToDoCtrls.GetFilePath(GetSelToDoCtrl());
			CString sDrive, sFolder;

			FileMisc::SplitPath(sPathName, &sDrive, &sFolder);
			FileMisc::MakePath(sFile, sDrive, sFolder, sFile);
		}
		// else its a full path already
		
		int nTDC = m_mgrToDoCtrls.FindToDoCtrl(sFile);

		if (nTDC != -1) // already loaded
			return GetToDoCtrl(nTDC).IsTaskDone(wParam);
		else
		{
			// we must load the tasklist ourselves
			CTaskFile tasks;

			if (tasks.Load(sFile))
			{
				HTASKITEM ht = tasks.FindTask(wParam);
				return ht ? tasks.IsTaskDone(ht) : FALSE;
			}
		}
	}
	
	return 0L;
}

LRESULT CToDoListWnd::OnPowerBroadcast(WPARAM wp, LPARAM /*lp*/)
{
	const CPreferencesDlg& userPrefs = Prefs();

	switch (wp)
	{
	case PBT_APMSUSPEND:
	case PBT_APMSTANDBY:
	case PBT_APMQUERYSUSPEND:
	case PBT_APMQUERYSTANDBY:
		// Terminate all timers
		SetTimer(TIMER_DUEITEMS, FALSE);
		SetTimer(TIMER_READONLYSTATUS, FALSE);
		SetTimer(TIMER_TIMESTAMPCHANGE, FALSE);
		SetTimer(TIMER_CHECKOUTSTATUS, FALSE);
		SetTimer(TIMER_AUTOSAVE, FALSE);
		SetTimer(TIMER_TIMETRACKING, FALSE);
		break;

	case PBT_APMQUERYSUSPENDFAILED:
	case PBT_APMQUERYSTANDBYFAILED:
	case PBT_APMRESUMECRITICAL:
	case PBT_APMRESUMESUSPEND: 
	case PBT_APMRESUMESTANDBY:
		// reset time tracking as required
		if (!userPrefs.GetTrackHibernated())
		{
			int nCtrl = GetTDCCount();
			
			while (nCtrl--)
				GetToDoCtrl(nCtrl).ResetTimeTracking();
		}

		// restart timers
		SetTimer(TIMER_DUEITEMS, TRUE);
		SetTimer(TIMER_READONLYSTATUS, userPrefs.GetReadonlyReloadOption() != RO_NO);
		SetTimer(TIMER_TIMESTAMPCHANGE, userPrefs.GetTimestampReloadOption() != RO_NO);
		SetTimer(TIMER_AUTOSAVE, userPrefs.GetAutoSaveFrequency());
		SetTimer(TIMER_CHECKOUTSTATUS, userPrefs.GetCheckoutOnCheckin() || userPrefs.GetAutoCheckinFrequency());
		SetTimer(TIMER_TIMETRACKING, TRUE);

		// check for updates
		if (Prefs().GetAutoCheckForUpdates())
			CheckForUpdates(FALSE);
		break;
	}

	return TRUE; // allow 
}

LRESULT CToDoListWnd::OnGetFont(WPARAM /*wp*/, LPARAM /*lp*/)
{
	return (LRESULT)m_fontMain.GetSafeHandle();
}

void CToDoListWnd::OnViewStatusBar() 
{
	m_bShowStatusBar = !m_bShowStatusBar;
	m_statusBar.ShowWindow(m_bShowStatusBar ? SW_SHOW : SW_HIDE);
	
	SendMessage(WM_SIZE, SIZE_RESTORED, 0L);
	//Resize();

	if (m_bShowStatusBar)
		UpdateStatusbar();
	else
		UpdateCaption();
}

void CToDoListWnd::OnUpdateViewStatusBar(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bShowStatusBar ? 1 : 0) ;
}

BOOL CToDoListWnd::OnQueryOpen() 
{
	if (CFrameWnd::OnQueryOpen())
	{
		// fail if the active tasklist is encrypted because we have to verify the password
		// and we're not allowed to display a dialog in this message handler
		if (!m_bQueryOpenAllow && GetToDoCtrl().IsEncrypted())
		{
			PostMessage(WM_TDL_RESTORE); 
			return FALSE;
		}
		
		// all others
		return TRUE;
	}
	
	return FALSE;
}

LRESULT CToDoListWnd::OnToDoListRestore(WPARAM /*wp*/, LPARAM /*lp*/)
{
    ASSERT (IsIconic() && GetToDoCtrl().IsEncrypted()); // sanity check
	
    if (IsIconic())
    {
        if (VerifyToDoCtrlPassword())
		{
			CAutoFlag af(m_bQueryOpenAllow, TRUE);
            ShowWindow(SW_RESTORE);
		}
    }

	return 0L;
}

void CToDoListWnd::OnCopyTaskasLink() 
{
	CopySelectedTasksToClipboard(TDCTC_ASLINK);
}

void CToDoListWnd::OnUpdateCopyTaskasLink(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetSelectedCount() == 1);
}

void CToDoListWnd::OnCopyTaskasDependency() 
{
	CopySelectedTasksToClipboard(TDCTC_ASDEPENDS);
}

void CToDoListWnd::OnUpdateCopyTaskasDependency(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetSelectedCount() == 1);
}

void CToDoListWnd::OnCopyTaskasLinkFull() 
{
	CopySelectedTasksToClipboard(TDCTC_ASLINKFULL);
}

void CToDoListWnd::OnUpdateCopyTaskasLinkFull(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetSelectedCount() == 1);
}

void CToDoListWnd::OnCopyTaskasDependencyFull() 
{
	CopySelectedTasksToClipboard(TDCTC_ASDEPENDSFULL);
}

void CToDoListWnd::OnUpdateCopyTaskasDependencyFull(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetSelectedCount() == 1);
}

void CToDoListWnd::OnCopyTaskasPath() 
{
	CopySelectedTasksToClipboard(TDCTC_ASPATH);
}

void CToDoListWnd::OnUpdateCopyTaskasPath(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetSelectedCount() == 1);
}

BOOL CToDoListWnd::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (CFrameWnd::PreCreateWindow(cs))
	{
		cs.dwExStyle &= ~WS_EX_CLIENTEDGE;

		// Check if our class is already defined
		LPCTSTR pszClassName = _T("ToDoListFrame");
		WNDCLASS wndcls;

		if (!::GetClassInfo(AfxGetInstanceHandle(), pszClassName, &wndcls))
		{
			// Get the current requested window class
			VERIFY(GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndcls));

			// We want to register this info with our name
			wndcls.lpszClassName = pszClassName;

			// Need to preset the icon otherwise the function GetIconWndClass
			// calling us will overwrite our class.
			wndcls.hIcon = GraphicsMisc::LoadIcon(IDR_MAINFRAME);

			// Register our class now and check the outcome
			if (!::RegisterClass(&wndcls))
			{
				ASSERT(0);
				return FALSE;
			}
		}

		// Now use our class 
		cs.lpszClass = pszClassName;
		return TRUE;
	}
	
	// else
	return FALSE;
}

void CToDoListWnd::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) 
{
	CFrameWnd::OnWindowPosChanging(lpwndpos); 
}

void CToDoListWnd::OnToolsCheckforupdates() 
{
	CheckForUpdates(TRUE);
}

void CToDoListWnd::OnEditInsertdatetime() 
{
	DoInsertDateAndTime(TRUE, TRUE);
}

void CToDoListWnd::OnEditInsertdate() 
{
	DoInsertDateAndTime(TRUE, FALSE);
}

void CToDoListWnd::OnEditInserttime() 
{
	DoInsertDateAndTime(FALSE, TRUE);
}

void CToDoListWnd::DoInsertDateAndTime(BOOL bDate, BOOL bTime) 
{
	COleDateTime date = COleDateTime::GetCurrentTime();
	const CPreferencesDlg& userPrefs = Prefs();

	CString sInsert;

	if (bDate) // date only or date and time
	{
		DWORD dwFmt = (bTime ? DHFD_TIME : 0);

		if (userPrefs.GetShowWeekdayInDates())
			dwFmt |= DHFD_DOW;
						
		if (userPrefs.GetDisplayDatesInISO())
			dwFmt |= DHFD_ISO;
						
		sInsert = CDateHelper::FormatDate(date, dwFmt);
	}
	else // time only
	{
		if (userPrefs.GetDisplayDatesInISO())
			sInsert = CTimeHelper::FormatISOTime(date.GetHour(), date.GetMinute(), date.GetSecond(), TRUE);
		else
			sInsert = CTimeHelper::Format24HourTime(date.GetHour(), date.GetMinute(), date.GetSecond(), TRUE);
	}

	// add trailing space
	sInsert += ' ';

	GetToDoCtrl().PasteText(sInsert);
}

void CToDoListWnd::OnUpdateEditInsertdatetime(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanPasteText());
}

void CToDoListWnd::OnUpdateEditInserttime(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanPasteText());
}

void CToDoListWnd::OnUpdateEditInsertdate(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().CanPasteText());
}

void CToDoListWnd::OnSysColorChange() 
{
	CFrameWnd::OnSysColorChange();
	
	InitMenuIconManager();

	SetUITheme(m_sThemeFile);
}

void CToDoListWnd::UpdateSBPaneAndTooltip(UINT nIDPane, UINT nIDTextFormat, const CString& sValue, UINT nIDTooltip, TDC_COLUMN nTDCC)
{
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();
	const CPreferencesDlg& userPrefs = Prefs();
		
	CEnString sText, sTooltip;

	if (!userPrefs.GetShowCtrlsAsColumns() || tdc.IsColumnShowing(nTDCC))
	{
		sText.Format(nIDTextFormat, sValue);
		sTooltip.LoadString(nIDTooltip);
	}
	else
	{
		sText.Empty();
		sTooltip.Empty();
	}

	int nPane = m_statusBar.CommandToIndex(nIDPane);

	m_statusBar.SetPaneText(nPane, sText);
	m_statusBar.SetPaneTooltipIndex(nPane, sTooltip);
}

void CToDoListWnd::UpdateStatusBarInfo(const CFilteredToDoCtrl& tdc, TDCSTATUSBARINFO& sbi) const
{
	sbi.nSelCount = tdc.GetSelectedCount();
	sbi.dwSelTaskID = tdc.GetSelectedTaskID();
	sbi.dCost = tdc.CalcSelectedTaskCost();

	const CPreferencesDlg& userPrefs = Prefs();

	userPrefs.GetDefaultTimeEst(sbi.nTimeEstUnits);
	sbi.dTimeEst = tdc.CalcSelectedTaskTimeEstimate(sbi.nTimeEstUnits);

	userPrefs.GetDefaultTimeSpent(sbi.nTimeSpentUnits);
	sbi.dTimeSpent = tdc.CalcSelectedTaskTimeSpent(sbi.nTimeSpentUnits);
}

void CToDoListWnd::OnUpdateSBSelectionCount(CCmdUI* /*pCmdUI*/)
{
	if (GetTDCCount())
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl();

		// keep track of previous information to avoid unnecessary processing
		static TDCSTATUSBARINFO sbiPrev;

		TDCSTATUSBARINFO sbi;
		UpdateStatusBarInfo(tdc, sbi);

		if (sbi == sbiPrev)
			return;

		sbiPrev = sbi;

		// number of selected tasks
		CEnString sText;

		if (sbi.nSelCount == 1)
		{
			ASSERT(sbi.dwSelTaskID);
			sText.Format(ID_SB_SELCOUNTONE, sbi.dwSelTaskID);
		}
		else
			sText.Format(ID_SB_SELCOUNT, sbi.nSelCount);

		m_statusBar.SetPaneText(m_statusBar.CommandToIndex(ID_SB_SELCOUNT), sText);

		// times
		const CPreferencesDlg& userPrefs = Prefs();

		// estimate
		if (userPrefs.GetUseHMSTimeFormat())
			sText = CTimeHelper().FormatTimeHMS(sbi.dTimeEst, sbi.nTimeEstUnits);
		else
			sText = CTimeHelper().FormatTime(sbi.dTimeEst, sbi.nTimeEstUnits, 2);

		UpdateSBPaneAndTooltip(ID_SB_SELTIMEEST, ID_SB_SELTIMEEST, sText, IDS_SB_SELTIMEEST_TIP, TDCC_TIMEEST);

		// spent
		if (userPrefs.GetUseHMSTimeFormat())
			sText = CTimeHelper().FormatTimeHMS(sbi.dTimeSpent, sbi.nTimeSpentUnits);
		else
			sText = CTimeHelper().FormatTime(sbi.dTimeSpent, sbi.nTimeSpentUnits, 2);

		UpdateSBPaneAndTooltip(ID_SB_SELTIMESPENT, ID_SB_SELTIMESPENT, sText, IDS_SB_SELTIMESPENT_TIP, TDCC_TIMESPENT);

		// cost
		sText = Misc::Format(sbi.dCost, 2);
		UpdateSBPaneAndTooltip(ID_SB_SELCOST, ID_SB_SELCOST, sText, IDS_SB_SELCOST_TIP, TDCC_COST);

		// set tray tip too
		UpdateTooltip();
	}
}

void CToDoListWnd::OnUpdateSBTaskCount(CCmdUI* /*pCmdUI*/)
{
	if (GetTDCCount())
	{
		CFilteredToDoCtrl& tdc = GetToDoCtrl();

		UINT nVisibleTasks;
		UINT nTotalTasks = tdc.GetTaskCount(&nVisibleTasks);

		CEnString sText;
		sText.Format(IDS_SB_TASKCOUNT, nVisibleTasks, nTotalTasks);
		int nIndex = m_statusBar.CommandToIndex(ID_SB_TASKCOUNT);

		m_statusBar.SetPaneText(nIndex, sText);
		m_statusBar.SetPaneTooltipIndex(nIndex, CEnString(IDS_SB_TASKCOUNT_TIP));
	}
}

void CToDoListWnd::OnEditSelectall() 
{
	GetToDoCtrl().SelectAll();
}

void CToDoListWnd::OnUpdateEditSelectall(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetToDoCtrl().GetTaskCount());	
}

void CToDoListWnd::OnCloseallbutthis() 
{
	int nThis = GetSelToDoCtrl();
	int nCtrl = GetTDCCount();
	
	// remove tasklists
	while (nCtrl--)
	{
		if (nCtrl != nThis)
		{
			if (ConfirmSaveTaskList(nCtrl, TDLS_CLOSINGTASKLISTS) != TDCO_SUCCESS)
				continue; // user cancelled

			m_mgrToDoCtrls.RemoveToDoCtrl(nCtrl, TRUE);
		}
	}
}

void CToDoListWnd::OnUpdateCloseallbutthis(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetTDCCount() > 1);
}

void CToDoListWnd::DoSendTasks(BOOL bSelected)
{
	CTDLSendTasksDlg dialog(bSelected, GetToDoCtrl().GetView());

	if (dialog.DoModal() == IDOK)
	{
		// get tasks
		CFilteredToDoCtrl& tdc = GetToDoCtrl();
		CTaskFile tasks;

		GetTasks(tdc, FALSE, FALSE, dialog.GetTaskSelection(), tasks, NULL);

		// package them up
		CString sAttachment, sBody;
		TD_SENDAS nSendAs = dialog.GetSendAs();

		switch (nSendAs)
		{
		case TDSA_TASKLIST:
			{
				CString sFilename, sExt;
				FileMisc::SplitPath(tdc.GetFilePath(), NULL, NULL, &sFilename, &sExt);
				
				sAttachment = FileMisc::GetTempFileName(sFilename, sExt);
				
				if (!tasks.Save(sAttachment))
				{
					// TODO
					return;
				}
				
				sBody.LoadString(IDS_TASKLISTATTACHED);
			}
			break;
			
		case TDSA_BODYTEXT:
			sBody = m_mgrImportExport.ExportTaskListToText(&tasks);
			break;
		}
		
		// form subject
		CString sSubject = tdc.GetFriendlyProjectName();
		
		// recipients
		CStringArray aTo;
		tdc.GetSelectedTaskAllocTo(aTo);

		CString sTo = Misc::FormatArray(aTo, _T(";"));

		// prefix with task name if necessary
		if (dialog.GetTaskSelection().GetWantSelectedTasks() && tdc.GetSelectedCount() == 1)
		{
			sSubject = tdc.GetSelectedTaskTitle() + _T(" - ") + sSubject;
		}

		CSendFileTo().SendMail(*this, sTo, sSubject, sBody, sAttachment);
	}
}

void CToDoListWnd::OnSendTasks() 
{
	DoSendTasks(FALSE);
}

void CToDoListWnd::OnUpdateSendTasks(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.GetTaskCount());
}

void CToDoListWnd::OnSendSelectedTasks() 
{
	DoSendTasks(TRUE);
}

void CToDoListWnd::OnUpdateSendSelectedTasks(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.GetTaskCount());
}

void CToDoListWnd::OnEditUndo() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.UndoLastAction(TRUE);
	UpdateStatusbar();
}

void CToDoListWnd::OnUpdateEditUndo(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.CanUndoLastAction(TRUE));
}

void CToDoListWnd::OnEditRedo() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.UndoLastAction(FALSE);
	UpdateStatusbar();
}

void CToDoListWnd::OnUpdateEditRedo(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.CanUndoLastAction(FALSE));
}

void CToDoListWnd::OnViewCycleTaskViews() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	tdc.SetNextView();
	tdc.SetFocusToTasks();
}

void CToDoListWnd::OnUpdateViewCycleTaskViews(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_nMaxState != TDCMS_MAXCOMMENTS);
}

void CToDoListWnd::OnViewToggleTreeandList() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	FTC_VIEW nView = tdc.GetView();

	switch (nView)
	{
	case FTCV_TASKTREE:
		nView = FTCV_TASKLIST;
		break;

	case FTCV_TASKLIST:
	default:
		nView = FTCV_TASKTREE;
		break;
	}

	tdc.SetView(nView);
	tdc.SetFocusToTasks();
}

void CToDoListWnd::OnUpdateViewToggleTreeandList(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_nMaxState != TDCMS_MAXCOMMENTS);
}

void CToDoListWnd::OnViewToggletasksandcomments() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	if (!tdc.TasksHaveFocus())
		tdc.SetFocusToTasks();
	else
		tdc.SetFocusToComments();
}

void CToDoListWnd::OnUpdateViewToggletasksandcomments(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_nMaxState == TDCMS_NORMAL || 
					(m_nMaxState == TDCMS_MAXTASKLIST && Prefs().GetShowCommentsAlways()));
}

void CToDoListWnd::OnUpdateQuickFind(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_bShowToolbar);
}

void CToDoListWnd::OnQuickFind() 
{
	if (m_bShowToolbar)
		m_cbQuickFind.SetFocus();
}

void CToDoListWnd::OnQuickFindNext() 
{
	if (!m_sQuickFind.IsEmpty())
	{
		if (!GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTNEXT))
			GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTFIRST); // return to start
	}
}

void CToDoListWnd::OnUpdateQuickFindNext(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_sQuickFind.IsEmpty());
}

LRESULT CToDoListWnd::OnQuickFindItemAdded(WPARAM /*wp*/, LPARAM /*lp*/)
{
	// keep only the last 20 items
	int nItem = m_cbQuickFind.GetCount();

	while (nItem > 20)
	{
		nItem--;
		m_cbQuickFind.DeleteString(nItem);
	}

	return 0L;
}

void CToDoListWnd::OnQuickFindPrev() 
{
	if (!m_sQuickFind.IsEmpty())
	{
		if (!GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTPREV))
			GetToDoCtrl().SelectTask(m_sQuickFind, TDC_SELECTLAST); // return to end
	}
}

void CToDoListWnd::OnUpdateQuickFindPrev(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_sQuickFind.IsEmpty());
}

void CToDoListWnd::OnMove(int x, int y) 
{
	CFrameWnd::OnMove(x, y);
}

void CToDoListWnd::OnEditSettaskicon() 
{
	GetToDoCtrl().EditSelectedTaskIcon();
}

void CToDoListWnd::OnUpdateEditSettaskicon(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly());	
}

LRESULT CToDoListWnd::OnToDoCtrlReminder(WPARAM /*wp*/, LPARAM lp)
{
	AF_NOREENTRANT_RET(0L) // macro helper

	// ignore if we are showing a dialog
	if (!IsWindowEnabled())
		return 0L;

	Show(FALSE);

	CTDLShowReminderDlg dialog;
	TDCREMINDER* pReminder = (TDCREMINDER*)lp;

	int nRet = dialog.DoModal(*pReminder);
	
	switch (nRet)
	{
	case IDSNOOZE:
		{
			double dNow = COleDateTime::GetCurrentTime();

			if (pReminder->bRelative)
			{
				if (pReminder->nRelativeFromWhen == TDCR_DUEDATE)
				{
					// in case the user didn't handle the notification immediately we need
					// to soak up any additional elapsed time in the snooze
					COleDateTime dDue = pReminder->pTDC->GetTaskDate(pReminder->dwTaskID, TDCD_DUE);
					
					pReminder->dDaysSnooze = (dNow - dDue + pReminder->dRelativeDaysLeadIn);
				}
				else // from start
				{
					// in case the user didn't handle the notification immediately we need
					// to soak up any additional elapsed time in the snooze
					COleDateTime dStart = pReminder->pTDC->GetTaskDate(pReminder->dwTaskID, TDCD_START);
					
					pReminder->dDaysSnooze = (dNow - dStart + pReminder->dRelativeDaysLeadIn);
				}
			}
			else // absolute
			{
				// in case the user didn't handle the notification immediately we need
				// to soak up any additional elapsed time in the snooze
				pReminder->dDaysSnooze = dNow - pReminder->dtAbsolute;
			}
				
				// then we add the user's snooze
			pReminder->dDaysSnooze += dialog.GetSnoozeDays();
		}
		return 0L; // don't delete (default)

	case IDGOTOTASK:
		{
			int nTDC = m_mgrToDoCtrls.FindToDoCtrl(pReminder->pTDC);
			ASSERT(nTDC != -1);

			SelectToDoCtrl(nTDC, TRUE);
			GetToDoCtrl().SelectTask(pReminder->dwTaskID);
		}
		// fall thru

	case IDCANCEL:
	default:
		// delete unless it's a recurring task in which case we 
		// disable it so that it can later be copied when the 
		// recurring task is completed
		if (GetToDoCtrl().IsTaskRecurring(pReminder->dwTaskID))
		{
			pReminder->bEnabled = FALSE;
			return 0L; // don't delete
		}
		// else
		return 1L; // delete
	}
}

LRESULT CToDoListWnd::OnToDoCtrlTaskHasReminder(WPARAM wParam, LPARAM lParam)
{
	int nRem = m_reminders.FindReminder(wParam, (CFilteredToDoCtrl*)lParam, FALSE);
	return (nRem != -1);
}

LRESULT CToDoListWnd::OnDoubleClkReminderCol(WPARAM /*wp*/, LPARAM /*lp*/)
{
	OnEditSetReminder();
	return 0L;
}

void CToDoListWnd::OnEditSetReminder() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	CTDLSetReminderDlg dialog;
	
	CString sTitle = tdc.GetSelectedTaskTitle();
	
	CDWordArray aTaskIDs;
	int nNumSel = tdc.GetSelectedTaskIDs(aTaskIDs, TRUE);
	
	if (nNumSel == 0)
		return;
	
	// get the first reminder as a reference
	BOOL bNewReminder = TRUE;
	TDCREMINDER rem;
	
	for (int nTask = 0; nTask < nNumSel; nTask++)
	{
		DWORD dwTaskID = aTaskIDs[nTask];
		int nRem = m_reminders.FindReminder(dwTaskID, &tdc);
		
		if (nRem != -1)
		{
			m_reminders.GetReminder(nRem, rem);
			bNewReminder = FALSE;
			break;
		}
	}
	
	// handle new task
	if (bNewReminder)
	{
		rem.dwTaskID = aTaskIDs[0];
		rem.pTDC = &tdc;
	}
	
	if (dialog.DoModal(rem, bNewReminder) == IDOK)
	{
		// apply reminder to selected tasks
		for (int nTask = 0; nTask < nNumSel; nTask++)
		{
			rem.dwTaskID = aTaskIDs[nTask];
			m_reminders.SetReminder(rem);
		}
		
		tdc.RedrawReminders();
	}
}

void CToDoListWnd::OnUpdateEditSetReminder(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	BOOL bEnable = (tdc.GetSelectedCount() > 0) && !tdc.SelectedTasksAreAllDone();
	pCmdUI->Enable(bEnable);
}

void CToDoListWnd::OnEditClearReminder() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	
	CDWordArray aTaskIDs;
	int nTask = tdc.GetSelectedTaskIDs(aTaskIDs, TRUE);
	
	while (nTask--)
	{
		DWORD dwTaskID = aTaskIDs[nTask];
		m_reminders.RemoveReminder(dwTaskID, &tdc);
	}
	
	tdc.RedrawReminders();
}

void CToDoListWnd::OnUpdateEditClearReminder(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	BOOL bEnable = FALSE;
	
	// check at least one selected item has a reminder
	CDWordArray aTaskIDs;
	int nTask = tdc.GetSelectedTaskIDs(aTaskIDs, TRUE);
	
	while (nTask--)
	{
		DWORD dwTaskID = aTaskIDs[nTask];
		
		if (m_reminders.FindReminder(dwTaskID, &tdc) != -1)
		{
			bEnable = TRUE;
			break;
		}
	}
	
	pCmdUI->Enable(bEnable);
}

void CToDoListWnd::OnEditCleartaskicon() 
{
	GetToDoCtrl().ClearSelectedTaskIcon();
}

void CToDoListWnd::OnUpdateEditCleartaskicon(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	int nSelCount = tdc.GetSelectedCount();
	
	pCmdUI->Enable(nSelCount && !tdc.IsReadOnly() && tdc.SelectedTasksHaveIcons());	
}

void CToDoListWnd::OnSortMulti() 
{
	TDSORTCOLUMNS sort;
	CTDCColumnIDArray aColumns;
	CTDCCustomAttribDefinitionArray aAttribDefs;

	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	tdc.GetSortBy(sort);
	tdc.GetVisibleColumns(aColumns);
	tdc.GetCustomAttributeDefs(aAttribDefs);

	CTDLMultiSortDlg dialog(sort, aColumns, aAttribDefs);

	if (dialog.DoModal() == IDOK)
	{
		dialog.GetSortBy(sort);
		tdc.MultiSort(sort);
	}
}

void CToDoListWnd::OnUpdateSortMulti(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(GetToDoCtrl().IsMultiSorting() ? 1 : 0);
}

void CToDoListWnd::OnToolsRemovefromsourcecontrol() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	if (tdc.RemoveFromSourceControl())
	{
		int nCtrl = GetSelToDoCtrl();

		m_mgrToDoCtrls.UpdateToDoCtrlReadOnlyUIState(nCtrl);
		m_mgrToDoCtrls.UpdateTabItemText(nCtrl);
		m_mgrToDoCtrls.SetModifiedStatus(nCtrl, FALSE);
		m_mgrToDoCtrls.RefreshLastModified(nCtrl);
		m_mgrToDoCtrls.RefreshReadOnlyStatus(nCtrl);
		m_mgrToDoCtrls.RefreshPathType(nCtrl);
	}
}

void CToDoListWnd::OnUpdateToolsRemovefromsourcecontrol(CCmdUI* pCmdUI) 
{
	int nCtrl = GetSelToDoCtrl();

	BOOL bEnable = m_mgrToDoCtrls.IsSourceControlled(nCtrl);
//	bEnable &= !Prefs().GetEnableSourceControl();
//	bEnable &= m_mgrToDoCtrls.PathSupportsSourceControl(nCtrl);

	if (bEnable)
	{
		// make sure no-one has the file checked out
		CFilteredToDoCtrl& tdc = GetToDoCtrl();
		bEnable &= !tdc.IsCheckedOut();
	}

	pCmdUI->Enable(bEnable);
}


void CToDoListWnd::OnViewShowTasklistTabbar() 
{
	m_bShowTasklistBar = !m_bShowTasklistBar; 

	Resize();
	Invalidate(TRUE);
}

void CToDoListWnd::OnUpdateViewShowTasklistTabbar(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bShowTasklistBar ? 1 : 0);
}

void CToDoListWnd::OnViewShowTreeListTabbar() 
{
	m_bShowTreeListBar = !m_bShowTreeListBar; 

	GetToDoCtrl().SetStyle(TDCS_SHOWTREELISTBAR, m_bShowTreeListBar);

	// refresh all the other tasklists
	m_mgrToDoCtrls.SetAllNeedPreferenceUpdate(TRUE, GetSelToDoCtrl());
}

void CToDoListWnd::OnUpdateViewShowTreeListTabbar(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bShowTreeListBar ? 1 : 0);
}

void CToDoListWnd::OnFileChangePassword() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	if (!tdc.IsReadOnly() && tdc.IsEncrypted() && VerifyToDoCtrlPassword())
	{
		tdc.EnableEncryption(FALSE); // clears the password
		tdc.EnableEncryption(TRUE); // forces it to be re-got
	}
}

void CToDoListWnd::OnUpdateFileChangePassword(CCmdUI* pCmdUI) 
{
	const CFilteredToDoCtrl& tdc = GetToDoCtrl();

	pCmdUI->Enable(!tdc.IsReadOnly() && tdc.IsEncrypted());
}

void CToDoListWnd::OnTasklistCustomColumns() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();

	if (!tdc.IsReadOnly())
	{
		CTDLCustomAttributeDlg dialog(tdc, m_theme);

		if (dialog.DoModal() == IDOK)
		{
			CTDCCustomAttribDefinitionArray aAttrib;

			dialog.GetAttributes(aAttrib);
			tdc.SetCustomAttributeDefs(aAttrib);
		}
	}
}

void CToDoListWnd::OnUpdateTasklistCustomcolumns(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!GetToDoCtrl().IsReadOnly());
}

void CToDoListWnd::OnEditClearAttribute() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	TDC_ATTRIBUTE nAttrib = MapColumnToAttribute(m_nContextColumnID);

	tdc.ClearSelectedTaskAttribute(nAttrib);
}

void CToDoListWnd::OnUpdateEditClearAttribute(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	TDC_ATTRIBUTE nAttrib = MapColumnToAttribute(m_nContextColumnID);

	pCmdUI->Enable(tdc.CanClearSelectedTaskAttribute(nAttrib));
}

void CToDoListWnd::OnEditClearFocusedAttribute() 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	tdc.ClearSelectedTaskFocusedAttribute();
}

void CToDoListWnd::OnUpdateEditClearFocusedAttribute(CCmdUI* pCmdUI) 
{
	CFilteredToDoCtrl& tdc = GetToDoCtrl();
	pCmdUI->Enable(tdc.CanClearSelectedTaskFocusedAttribute());
}

  

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!