<?xml version="1.0" encoding="UTF-8"?>
<leo_file>
<leo_header file_format="2" tnodes="0" max_tnode_index="192" clone_windows="0"/>
<globals body_outline_ratio="0.55128205128205132">
	<global_window_position top="127" left="222" height="649" width="992"/>
	<global_log_window_position top="0" left="0" height="0" width="0"/>
</globals>
<preferences>
</preferences>
<find_panel_settings>
	<find_string></find_string>
	<change_string></change_string>
</find_panel_settings>
<vnodes>
<v t="szatz.112203232245" a="EV"><vh>@rst ListManagerDocs.html</vh>
<v t="szatz.112203232245.1" a="E" tnodeList="szatz.112203232245.1,szatz.112203232245.3,szatz.112203232245.4,szatz.112203232245.5,szatz.112203232245.6,szatz.112203232245.8,szatz.112203232245.9,szatz.112203232245.10,szatz.112203232245.11,szatz.112203232245.12,szatz.112203232245.13,szatz.112203232245.14,szatz.112203232245.16,szatz.112203232245.15,szatz.112203232245.17,szatz.112203232245.18,szatz.112203232245.19,szatz.20031228000508,szatz.112203232245.21,szatz.112203232245.22,szatz.112203232245.24,szatz.112203232245.25,szatz.112203232245.26,szatz.112203232245.27,szatz.112203232245.28,szatz.112203232245.29,szatz.112203232245.34,szatz.112203232245.35,szatz.112203232245.37,szatz.112203232245.38,szatz.112203232245.40,szatz.112203232245.41,szatz.112203232245.42,szatz.112203232245.43,szatz.112203232245.44,szatz.112203232245.45,szatz.112203232245.47,szatz.112203232245.48,szatz.112203232245.49,szatz.112203232245.51,szatz.112203232245.52,szatz.112203232245.53,szatz.112203232245.54,szatz.112203232245.56,szatz.112203232245.57,szatz.112203232245.58,szatz.112203232245.59,szatz.112203232245.60,szatz.112203232245.61,szatz.112203232245.62,szatz.112203232245.63,szatz.112203232245.64,szatz.112203232245.65,szatz.112203232245.98,szatz.112203232245.67,szatz.112203232245.68,szatz.112203232245.69,szatz.112203232245.70,szatz.112203232245.71,szatz.112203232245.72,szatz.112203232245.73,szatz.112203232245.74,szatz.112203232245.23,szatz.112203232245.77,szatz.112203232245.78,szatz.112203232245.79,szatz.112203232245.80,szatz.112203232245.81,szatz.112203232245.82,szatz.112203232245.83,szatz.112203232245.84,szatz.112203232245.85,szatz.112203232245.86,szatz.112203232245.107,szatz.112203232245.108,szatz.112203232245.88,szatz.112203232245.89,szatz.112203232245.91,szatz.112203232245.92,szatz.112203232245.93,szatz.112203232245.95,szatz.112203232245.96,szatz.112203232245.97,szatz.112203232245.100,szatz.112203232245.101,szatz.112203232245.102,szatz.112203232245.103,szatz.112203232245.104,szatz.112203232245.105,szatz.112203232245.106,szatz.112203232245.75,szatz.112203232245.109,szatz.112203232245.111,szatz.112203232245.112,szatz.112703232517,szatz.112703234413,szatz.112203232245.113,szatz.112203232245.30,szatz.112203232245.31,szatz.112203232245.32,szatz.112203232245.114,szatz.112203232245.115,szatz.112203232245.116,szatz.112203232245.117,szatz.112203232245.118,szatz.112203232245.119,szatz.112203232245.121,szatz.112203232245.122"><vh>@file-nosent wxListManager.py</vh>
<v t="szatz.112203232245.2" a="E"><vh>Initial stuff</vh>
<v t="szatz.20031220175801"><vh>@rst</vh></v>
<v t="szatz.112203232245.3" a="E"><vh>Module Imports</vh>
<v t="szatz.20031220175801.1"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.4" a="E"><vh>Constants</vh>
<v t="szatz.20031220180725"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.5" a="E"><vh>Menu IDs</vh>
<v t="szatz.20031220182141"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.6" a="E"><vh>Read Config File</vh>
<v t="szatz.20031220182540"><vh>@rst</vh></v>
</v>
</v>
<v t="szatz.112203232245.8" a="E"><vh>class ListManager</vh>
<v t="szatz.20031220180151"><vh>@rst</vh></v>
<v t="szatz.20031231232809" a="E"><vh>Instantiation</vh>
<v t="szatz.20031231232809.1"><vh>@rst</vh></v>
<v t="szatz.112203232245.9"><vh>def __init__</vh>
<v t="szatz.20031220092608"><vh>@rst</vh></v>
<v t="szatz.112203232245.10" a="E"><vh>&lt;&lt; List Manager Attributes &gt;&gt;</vh>
<v t="szatz.20031220092608.1"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.11" a="E"><vh>&lt;&lt; Menu Setup &gt;&gt;</vh>
<v t="szatz.20031220093529"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.12" a="E"><vh>&lt;&lt; Toolbar Setup &gt;&gt;</vh>
<v t="szatz.20031220184411"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.13" a="E"><vh>&lt;&lt; Menu/Toolbar Events &gt;&gt;</vh>
<v t="szatz.20031221191501"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.14" a="E"><vh>&lt;&lt; Create Controls&gt;&gt;</vh>
<v t="szatz.20031222222555"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.15" a="E"><vh>&lt;&lt; Other Events &gt;&gt;</vh>
<v t="szatz.20031222222939"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.16" a="E"><vh>&lt;&lt; Layout Stuff &gt;&gt;</vh>
<v t="szatz.20031224183942"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.17" a="E"><vh>&lt;&lt; GUI Instance Objects &gt;&gt;</vh>
<v t="szatz.20031224184335"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.18" a="E"><vh>&lt;&lt; Create Socket &gt;&gt;</vh>
<v t="szatz.20031224184335.1"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.19" a="E"><vh>&lt;&lt; Load Recent Files &gt;&gt;</vh>
<v t="szatz.20031230213149"><vh>@rst</vh></v>
</v>
<v t="szatz.20031228000508" a="E"><vh>&lt;&lt; Idle Timer &gt;&gt;</vh>
<v t="szatz.20031228001152"><vh>@rst</vh></v>
</v>
</v>
</v>
<v t="szatz.112203232245.20" a="E"><vh>Ownerlist creation methods (used by thread)</vh>
<v t="szatz.20031225203839"><vh>@rst</vh></v>
<v t="szatz.112203232245.21" a="E"><vh>def createownerlist</vh>
<v t="szatz.20031225203754"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.22" a="E"><vh>def createownerdialog</vh>
<v t="szatz.20031227163020"><vh>@rst</vh></v>
</v>
</v>
<v t="szatz.20031230213102"><vh>Notebook methods</vh>
<v t="szatz.20031230214130"><vh>@rst</vh></v>
<v t="szatz.112203232245.24"><vh>def CreateNewNotebookPage</vh>
<v t="szatz.20031227164126"><vh>@rst</vh></v>
<v t="szatz.112203232245.25"><vh>&lt;&lt; Fill OwnerListBox &gt;&gt;</vh>
<v t="szatz.20031228133237"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.26"><vh>&lt;&lt; ListControl Events &gt;&gt;</vh>
<v t="szatz.20031228092655"><vh>@rst</vh></v>
</v>
</v>
<v t="szatz.112203232245.27"><vh>def OnPageChange</vh>
<v t="szatz.20031228134301"><vh>@rst</vh></v>
<v t="szatz.112203232245.28" a="E"><vh>&lt;&lt; Find Highlighted Row &gt;&gt;</vh>
<v t="szatz.20031228134301.1"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.29" a="E"><vh>&lt;&lt; Update Title &gt;&gt;</vh>
<v t="szatz.20031228134301.2"><vh>@rst</vh></v>
</v>
</v>
</v>
<v t="szatz.112203232245.33"><vh>Tickler methods</vh>
<v t="szatz.112203232245.34"><vh>def OnShowTickler</vh></v>
<v t="szatz.112203232245.35"><vh>def OnActivateTickler</vh></v>
</v>
<v t="szatz.112203232245.36"><vh>Email methods</vh>
<v t="szatz.112203232245.37"><vh>OnMailItem</vh></v>
<v t="szatz.112203232245.38"><vh>OnMailView</vh></v>
</v>
<v t="szatz.112203232245.39"><vh>Cut/Copy/Paste methods</vh>
<v t="szatz.112203232245.40" a="E"><vh>OnCopyItems</vh>
<v t="szatz.112203232245.41"><vh>&lt;&lt; Find Highlighted Items &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.42"><vh>OnPasteItems</vh></v>
<v t="szatz.112203232245.43"><vh>OnDeleteItems</vh></v>
</v>
<v t="szatz.20031230210944"><vh>MouseDown methods</vh>
<v t="szatz.112203232245.44"><vh>OnLeftDown (Action depends on x coordinate)</vh></v>
<v t="szatz.112203232245.45"><vh>OnRightDown (Display popup sendto menu)</vh></v>
</v>
<v t="szatz.112203232245.46"><vh>Move/Combine items methods</vh>
<v t="szatz.112203232245.47"><vh>OnCombineItems</vh></v>
<v t="szatz.112203232245.48"><vh>OnMoveToList</vh></v>
<v t="szatz.112203232245.49"><vh>OnMoveToSpecificList</vh></v>
</v>
<v t="szatz.112203232245.50"><vh>Change/update items methods</vh>
<v t="szatz.112203232245.51"><vh>OnToggleFinished</vh>
<v t="szatz.112203232245.52"><vh>&lt;&lt; draw item &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.53" a="E"><vh>OnPriority</vh>
<v t="szatz.112203232245.54"><vh>&lt;&lt; draw item &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.55" a="E"><vh>Inplace Edit Methods</vh>
<v t="szatz.112203232245.56"><vh>OnDisplayInPlaceEditor</vh></v>
<v t="szatz.112203232245.57"><vh>OnCloseInPlaceEditor</vh></v>
</v>
<v t="szatz.112203232245.58"><vh>OnDueDate</vh></v>
<v t="szatz.112203232245.59" a="E"><vh>OnEditOwner</vh>
<v t="szatz.112203232245.60"><vh>&lt;&lt; Common Owner Code &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.61" a="E"><vh>OnUpdate</vh>
<v t="szatz.112203232245.62"><vh>&lt;&lt; Common Owner Code &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.63" a="E"><vh>OnNewItem</vh>
<v t="szatz.112203232245.64"><vh>&lt;&lt; Clear data fields &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.65"><vh>Conflict (not in use)</vh></v>
<v t="szatz.112203232245.98"><vh>OnEditNote</vh></v>
</v>
<v t="szatz.112203232245.66"><vh>File menu methods</vh>
<v t="szatz.112203232245.67"><vh>OnNewList</vh></v>
<v t="szatz.112203232245.68"><vh>OnFileList</vh></v>
<v t="szatz.112203232245.69"><vh>OnOpenList</vh></v>
<v t="szatz.112203232245.70"><vh>OnDeleteList</vh></v>
<v t="szatz.112203232245.71"><vh>OnCloseList</vh></v>
<v t="szatz.112203232245.72"><vh>OnCloseAll</vh></v>
<v t="szatz.112203232245.73"><vh>OnSaveAsText</vh></v>
<v t="szatz.112203232245.74"><vh>OnArchive</vh></v>
<v t="szatz.112203232245.23"><vh>OnWorkOffline</vh>
<v t="szatz.20031227163901"><vh>@rst</vh></v>
</v>
</v>
<v t="szatz.112203232245.76"><vh>Display methods</vh>
<v t="szatz.112203232245.77"><vh>OnItemSelected</vh></v>
<v t="szatz.112203232245.78"><vh>OnItemActivated</vh></v>
<v t="szatz.112203232245.79"><vh>OnShowAll</vh></v>
<v t="szatz.112203232245.80"><vh>OnRefresh</vh></v>
<v t="szatz.112203232245.81"><vh>OnFilterOwners</vh></v>
<v t="szatz.112203232245.82"><vh>OnColumnClick (to sort columns)</vh></v>
<v t="szatz.112203232245.83"><vh>OnShowFinished</vh></v>
<v t="szatz.112203232245.84"><vh>OnColumnRightClick (popup to change date displayed)</vh></v>
<v t="szatz.112203232245.85"><vh>OnDisplayDateCategory</vh></v>
<v t="szatz.112203232245.86"><vh>ChangeDateDisplayed</vh></v>
<v t="szatz.112203232245.107"><vh>DisplayList</vh>
<v t="szatz.112203232245.108"><vh>&lt;&lt; draw item &gt;&gt;</vh></v>
</v>
</v>
<v t="szatz.112203232245.87"><vh>Printing methods</vh>
<v t="szatz.112203232245.88"><vh>OnPageSetup</vh></v>
<v t="szatz.112203232245.89"><vh>OnPrint</vh></v>
</v>
<v t="szatz.112203232245.90"><vh>Exiting methods</vh>
<v t="szatz.112203232245.91"><vh>OnWindowExit</vh></v>
<v t="szatz.112203232245.92" a="E"><vh>OnExit</vh>
<v t="szatz.112203232245.93"><vh>&lt;&lt;save configuration file&gt;&gt;</vh></v>
</v>
</v>
<v t="szatz.112203232245.94"><vh>Find methods</vh>
<v t="szatz.112203232245.95"><vh>OnFind</vh></v>
<v t="szatz.112203232245.96"><vh>FindString</vh></v>
<v t="szatz.112203232245.97"><vh>FindNode</vh></v>
</v>
<v t="szatz.112203232245.99"><vh>Database-related methods</vh>
<v t="szatz.112203232245.100"><vh>GetCursor</vh></v>
<v t="szatz.112203232245.101"><vh>GetNote</vh></v>
<v t="szatz.112203232245.102"><vh>CreateTable</vh></v>
<v t="szatz.112203232245.103"><vh>ReadFromDB (returns db results)</vh></v>
<v t="szatz.112203232245.104"><vh>CreateAndDisplayList (returns Item List)</vh>
<v t="szatz.112203232245.105"><vh>&lt;&lt; assign item attributes &gt;&gt;</vh></v>
<v t="szatz.112203232245.106"><vh>&lt;&lt; draw item &gt;&gt;</vh></v>
</v>
<v t="szatz.112203232245.75"><vh>OnSync</vh></v>
<v t="szatz.112203232245.109"><vh>TimeStamper</vh></v>
</v>
<v t="szatz.112203232245.110"><vh>Evaluate methods</vh>
<v t="szatz.112203232245.111"><vh>OnShowEvaluate</vh></v>
<v t="szatz.112203232245.112"><vh>OnEvaluate</vh></v>
</v>
<v t="szatz.20031230213102.1"><vh>Help menu methods</vh>
<v t="szatz.112703232517"><vh>OnShowAbout</vh></v>
<v t="szatz.112703234413"><vh>OnShowHelp</vh></v>
</v>
<v t="szatz.112203232245.113"><vh>GetUID</vh></v>
<v t="szatz.112203232245.30"><vh>OnIdle</vh>
<v t="szatz.20031229001241"><vh>@rst</vh></v>
<v t="szatz.112203232245.31" a="E"><vh>&lt;&lt; Check for Transfers From Outlook &gt;&gt;</vh>
<v t="szatz.20031229001241.1"><vh>@rst</vh></v>
</v>
<v t="szatz.112203232245.32" a="E"><vh>&lt;&lt; Check if Edited File has Changed &gt;&gt;</vh>
<v t="szatz.20031229002108"><vh>@rst</vh></v>
</v>
</v>
</v>
<v t="szatz.112203232245.114"><vh>class ListCtrl</vh>
<v t="szatz.112203232245.115"><vh>__init__</vh></v>
<v t="szatz.112203232245.116"><vh>SetUpColumns</vh></v>
<v t="szatz.112203232245.117"><vh>OnColBeginDrag</vh></v>
</v>
<v t="szatz.112203232245.118"><vh>class MyApp</vh>
<v t="szatz.112203232245.119"><vh>OnInit</vh></v>
</v>
<v t="szatz.112203232245.121"><vh>class Logger</vh></v>
<v t="szatz.112203232245.122"><vh>run</vh></v>
</v>
<v t="szatz.112203232245.123" a="E" tnodeList="szatz.112203232245.123,szatz.112203232245.124,szatz.112203232245.125,szatz.112203232245.126,szatz.112203232245.127,szatz.112203232245.128,szatz.112403214111,szatz.112403214111.1,szatz.112403214111.2,szatz.112203232245.129,szatz.112203232245.130,szatz.112203232245.131,szatz.112203232245.132,szatz.112203232245.133,szatz.112203232245.134,szatz.112203232245.135,szatz.112203232245.136,szatz.112203232245.137,szatz.112203232245.138,szatz.112203232245.139,szatz.112203232245.140,szatz.112203232245.141,szatz.112203232245.143,szatz.112203232245.144,szatz.112203232245.146,szatz.112203232245.147,szatz.112203232245.148,szatz.112203232245.149,szatz.112203232245.150,szatz.112203232245.151,szatz.112203232245.152,szatz.112203232245.153,szatz.112203232245.154,szatz.112203232245.155,szatz.112203232245.156,szatz.112203232245.157,szatz.112203232245.158"><vh>@file LMDialogs.py</vh>
<v t="szatz.112203232245.124" a="E"><vh>class PopDialog</vh>
<v t="szatz.112203232245.125"><vh>__init__</vh></v>
<v t="szatz.112203232245.126"><vh>OnLeftDown</vh></v>
<v t="szatz.112203232245.127"><vh>OnForward</vh></v>
<v t="szatz.112203232245.128"><vh>OnMail</vh></v>
</v>
<v t="szatz.112403214111" a="E"><vh>class StartupDialog</vh>
<v t="szatz.112403214111.1"><vh>__init__</vh></v>
<v t="szatz.112403214111.2"><vh>OnSelection</vh></v>
</v>
<v t="szatz.112203232245.129" a="E"><vh>class ModifierDialog</vh>
<v t="szatz.112203232245.130"><vh>__init__</vh></v>
<v t="szatz.112203232245.131"><vh>GetUserInput</vh></v>
<v t="szatz.112203232245.132"><vh>SelectCurrent</vh></v>
<v t="szatz.112203232245.133"><vh>ClearSelections</vh></v>
</v>
<v t="szatz.112203232245.134" a="E"><vh>class MailDialog</vh>
<v t="szatz.112203232245.135"><vh>__init__</vh></v>
</v>
<v t="szatz.112203232245.136" a="E"><vh>class CalendarDialog</vh>
<v t="szatz.112203232245.137"><vh>__init__</vh></v>
<v t="szatz.112203232245.138"><vh>OnCalSelected</vh></v>
<v t="szatz.112203232245.139"><vh>OnChangeMonth</vh></v>
<v t="szatz.112203232245.140"><vh>OnCloseWindow</vh></v>
<v t="szatz.112203232245.141"><vh>GetDate</vh></v>
</v>
<v t="szatz.112203232245.143" a="E"><vh>class FindDialog</vh>
<v t="szatz.112203232245.144"><vh>__init__</vh></v>
</v>
<v t="szatz.112203232245.146" a="E"><vh>class EvalDialog</vh>
<v t="szatz.112203232245.147"><vh>__init__</vh></v>
<v t="szatz.112203232245.148"><vh>PostOKEvent</vh></v>
</v>
<v t="szatz.112203232245.149" a="E"><vh>class LoggerDialog</vh>
<v t="szatz.112203232245.150"><vh>__init__</vh></v>
<v t="szatz.112203232245.151"><vh>OnSave</vh></v>
</v>
<v t="szatz.112203232245.152" a="E"><vh>class FinishedDialog</vh>
<v t="szatz.112203232245.153"><vh>__init__</vh></v>
<v t="szatz.112203232245.154"><vh>OnSpin</vh></v>
<v t="szatz.112203232245.155"><vh>OnCheck</vh></v>
</v>
<v t="szatz.112203232245.156" a="E"><vh>class TreeDialog</vh>
<v t="szatz.112203232245.157"><vh>__init__</vh></v>
<v t="szatz.112203232245.158"><vh>OnLeftDClick:</vh></v>
</v>
</v>
<v t="szatz.112203232245.159" a="E" tnodeList="szatz.112203232245.159,szatz.112203232245.160,szatz.112203232245.161,szatz.112203232245.162,szatz.112203232245.163,szatz.112203232245.164,szatz.112203232245.165,szatz.112203232245.166,szatz.112203232245.167,szatz.112203232245.168,szatz.112203232245.169,szatz.112203232245.170,szatz.112203232245.171,szatz.112203232245.172,szatz.112203232245.173"><vh>@file outlookAddin.py</vh>
<v t="szatz.112203232245.160"><vh>&lt;&lt; outlookAddin declarations &gt;&gt;</vh></v>
<v t="szatz.112203232245.161" a="E"><vh>class ButtonEvent</vh>
<v t="szatz.112203232245.162"><vh>OnClick</vh></v>
</v>
<v t="szatz.112203232245.163" a="E"><vh>class FolderEvent</vh>
<v t="szatz.112203232245.164"><vh>OnItemAdd</vh></v>
</v>
<v t="szatz.112203232245.165" a="E"><vh>class OutlookAddin</vh>
<v t="szatz.112203232245.166"><vh>&lt;&lt; class OutlookAddin declarations &gt;&gt;</vh></v>
<v t="szatz.112203232245.167"><vh>OnConnection</vh></v>
<v t="szatz.112203232245.168"><vh>OnDisconnection</vh></v>
<v t="szatz.112203232245.169"><vh>OnAddInsUpdate</vh></v>
<v t="szatz.112203232245.170"><vh>OnStartupComplete</vh></v>
<v t="szatz.112203232245.171"><vh>OnBeginShutdown</vh></v>
</v>
<v t="szatz.112203232245.172"><vh>RegisterAddin</vh></v>
<v t="szatz.112203232245.173"><vh>UnregisterAddin</vh></v>
</v>
</v>
<v t="szatz.121403184329" a="E"><vh>Documentation</vh>
<v t="szatz.121403184329.1" a="E"><vh>@rst ListManagerDocs.html</vh>
<v t="szatz.121403184329.2"><vh>Introduction</vh></v>
<v t="szatz.121403184329.3" a="E"><vh>class ListManager</vh>
<v t="szatz.121403184329.4" a="E"><vh>def __init__</vh>
<v t="szatz.121403184329.5"><vh>&lt;&lt; ListManager Attributes &gt;&gt;</vh></v>
</v>
</v>
</v>
</v>
</vnodes>
<tnodes>
<t tx="szatz.112203232245">#########################
ListManager Documentation
#########################

:Author: Steven Zatz
:Contact: slzatz@hotmail.com
:Date: $Date: 2003/12/24  $
:Status: This is a "work in progress"
:Revision: $Revision: 1.02 $
:Copyright: Application and documentation use the Python license which is compatible with the GPL. 

This is experimental documentation of a program called ListManager, written in Python and wxPython using Leo to create both 
the application code and the associated reST documentation.

ListManager is an application that allows a group of people working on a joint project to maintain a common list of todos and related 
items that have owners, due dates and associated notes.  The application uses mysql as its database for group use and also uses 
sqlite for locally resident databases for personal lists.  It works in conjunction with Outlook to allow email messages to be sent 
to ListManager for inclusion in lists and uses Outlook to mail messages to users.

.. contents:: Table of Contents
                                      
    


</t>
<t tx="szatz.112203232245.1">@language python
@others
</t>
<t tx="szatz.112203232245.2"></t>
<t tx="szatz.112203232245.3">from wxPython.wx import *
from wxPython.lib.mixins.listctrl import wxListCtrlAutoWidthMixin

import os
import time
import pickle
import socket
import select
import random
import ConfigParser
import threading
import re
import sys

from pywintypes import CreateGuid
from win32com.client import Dispatch
#import win32pdh
import win32api
#from win32com.client import constants #--&gt; just needed two constants...

import MySQLdb
import sqlite
import mx.DateTime

from LMDialogs import CalendarDialog, ModifierDialog, TicklerDialog, MailDialog,LoggerDialog, FinishedDialog, FindDialog, EvalDialog, TreeDialog, StartupDialog
#from wxTreeCtrl import TreeDialog

from printout import PrintTable
</t>
<t tx="szatz.112203232245.4">cwd = os.getcwd()
DIRECTORY = os.path.split(cwd)[0]
os.chdir(DIRECTORY)
del cwd

#Outlook Constants
olMailItem = 0x0
olFlagMarked = 0x2

OFFLINE_ONLY = False #False-&gt; Online only  ; True-&gt; Online and Offline possible; REMOTE_HOST = None -&gt; Offline only

VERSION = '1.02'
</t>
<t tx="szatz.112203232245.5">#File Menu-----------------#
idNEWLIST = 1000
idOPENLIST = 1010
idCLOSELIST = 1015
idCLOSEALL = 1017
idSAVEAS = 1020
idDELETELIST = 1025
idPAGESETUP = 1030
idPRINT = 1035
idPRINTPREV = 1040
idMAILLIST = 1045
idOFFLINE = 1048
idEXIT = 1050

#Edit Menu-----------------
idCUT = 1055
idCOPY = 1060
idPASTE = 1065
idDELETEITEMS = 1070
idCOMBINEITEMS = 1075
idFIND = 1080

#Item Menu-------------------
idNEWITEM = 1085
idTOGGLEFINISHED = 1090
idEDITOWNER = 1095
idDUEDATE = 1100
idEDITNOTE = 1105
idMAILITEM =1110

#Diplay Menu---------------------
idSHOWFINISHED = 1115
idSHOWALL = 1120
idREFRESH = 1125
idDISPLAYDATE = 1130

#Tool Menu------------------------
idTICKLERACTIVE = 1135
idSHOWNEXT = 1140
idSYNC = 1145
idARCHIVE = 1150
idEVALUATE = 1155
idTOOLPRINT = 1165
idSENDTO = 1170

#Help Menu-------------------------
idABOUT = 1175
idHELP = 1180
</t>
<t tx="szatz.112203232245.6">config_file = os.path.join(DIRECTORY, "List Manager.ini")
defaults = dict(pw='python', db='listmanager', ext='txt', local='wxLMDB:sqlite', x='700', y='400')
cp = ConfigParser.ConfigParser(defaults=defaults)
cp.read(config_file) #ConfigParser closes the file

USER = cp.has_option('User','user') and cp.get('User','user') or win32api.GetUserName()

# the following all have default values provided in the constructor
PW = cp.get('User','pw')
DB = cp.get('Database','db')
NOTE_EXT = cp.get('Note','ext')
LOCAL_HOST = cp.get('Hosts','local')
X = cp.getint('Configuration','x')
Y = cp.getint('Configuration','y')

# the folloowing default to None
MAIL_LIST_PATH = cp.has_option('Mail','path') and cp.get('Mail','path') or None
QUICK_LIST = cp.has_option('User','quicklist') and cp.get('User','quicklist') or None

# the following default to False
STARTUP_DIALOG = cp.has_option('User','startup_dialog') and cp.getboolean('User','startup_dialog')
DELETE_LIST = cp.has_option('User','delete_list') and cp.getboolean('User','delete_list')
OUTLOOK = cp.has_option('Mail','outlook') and cp.getboolean('Mail','outlook')

if cp.has_option('Hosts','remote'):
    REMOTE_HOST = cp.get('Hosts','remote')
else:
    REMOTE_HOST = None
    OFFLINE_ONLY = True
    
# reading it again because of the way defaults are handled
cp = ConfigParser.ConfigParser()
cp.read(config_file) #ConfigParser closes the file

if cp.has_section('Synchronization'):
    SYNC_TABLES = [t[1] for t in cp.items('Synchronization')]
else:
    SYNC_TABLES = ['follow_ups']

</t>
<t tx="szatz.112203232245.8">class ListManager(wxFrame):
    @others
</t>
<t tx="szatz.112203232245.9">def __init__(self, parent, id, title, size):
    wxFrame.__init__(self, parent, id, title, size = size)

    self.SetIcon(wxIcon('bitmaps//wxpdemo.ico', wxBITMAP_TYPE_ICO))
    self.CreateStatusBar()
  
    &lt;&lt; ListManager Attributes &gt;&gt;
    &lt;&lt; Menu Setup &gt;&gt;
    &lt;&lt; Toolbar Setup &gt;&gt;
    &lt;&lt; Menu/Toolbar Events &gt;&gt;
    &lt;&lt; Create Controls&gt;&gt;
    &lt;&lt; Layout Stuff &gt;&gt;
    &lt;&lt; Other Events &gt;&gt;
    &lt;&lt; GUI Instance Objects &gt;&gt;
    &lt;&lt; Create Socket &gt;&gt;
    &lt;&lt; Load Recent Files &gt;&gt;
    &lt;&lt; Idle Timer &gt;&gt;
    
    ownerthread = threading.Thread(target=self.createownerlist)
    ownerthread.start()
    self.ModifierDialog = None

</t>
<t tx="szatz.112203232245.10">self.PropertyDicts = []
self.ItemLists = []
self.ListCtrls = []
self.OwnerLBoxes = []

self.L = -1
self.curIdx = -1

self.printdata = wxPrintData()
self.printdata.SetPaperId(wxPAPER_LETTER)
self.printdata.SetOrientation(wxPORTRAIT)

#self._options = {} #would be used in loadconfig

self.copyitems = []    
self.modified = {}
self.tickler_active = False

#there is a wxPanel in the AddListControl method so each wxListCtrl has a different panel as parent
#there is a nb_sizer = wxNotebookSizer(nb) class but doesn't seem to make any difference

self.editor = []

self.Cursors = {}
self.sqlite_connections = []
self.popupvisible = False
self.in_place_editor = None
self.showrecentcompleted = 0

self.LC_font = wxFont(9, wxSWISS, wxNORMAL, wxNORMAL)

self.date_titles = {'createdate':"Create Date",'duedate':"Due Date",'timestamp':"Last Modified",'finisheddate':"Completion Date"}
self.attr2col_num = {'priority':0, 'name':1,'owners':2, 'date':3}

self.FindDialog = FindDialog(self, "Find...", "")
self.EvalDialog = EvalDialog(self, "Evaluate...", "")
</t>
<t tx="szatz.112203232245.11">filemenu = wxMenu()
filemenu.Append(idNEWLIST, "New List...", "Create a new List")
filemenu.Append(idOPENLIST, "Open List...", "Open a List")
filemenu.Append(idCLOSELIST, "Close", "Close the current List")
filemenu.Append(idCLOSEALL, "Close All", "Close all open Lists")
filemenu.Append(idSAVEAS, "Save As Text File...", "Save the current List")
filemenu.AppendSeparator()
filemenu.Append(idDELETELIST, "Delete List...", "Select a list to delete")
filemenu.AppendSeparator()
filemenu.Append(idPAGESETUP, "Page Setup...")
filemenu.Append(idPRINT, "Print...", "Print the current view")
filemenu.Append(idPRINTPREV, "Print Preview")
filemenu.AppendSeparator()
filemenu.Append(idMAILLIST, "Mail...", "Mail the current view")
filemenu.AppendSeparator()
filemenu.AppendCheckItem(idOFFLINE, "Work Offline")
filemenu.AppendSeparator()
filemenu.Append(idEXIT, "Exit", "Exit the program")

editmenu = wxMenu()
editmenu.Append(idCUT, "Cut\tCtrl+X")
editmenu.Append(idCOPY, "Copy\tCtrl+C")
editmenu.Append(idPASTE, "Paste\tCtrl+V")
editmenu.AppendSeparator()
editmenu.Append(idDELETEITEMS, "Delete")
editmenu.AppendSeparator()
editmenu.Append(idCOMBINEITEMS, "Combine Items...")
editmenu.AppendSeparator()
editmenu.Append(idFIND, "Find...")

itemmenu = wxMenu()
itemmenu.Append(idNEWITEM, "New Item")
itemmenu.AppendSeparator()
itemmenu.Append(idTOGGLEFINISHED, "Toggle Finished")
itemmenu.Append(idEDITOWNER, "Owner...")
itemmenu.Append(idDUEDATE, "Due Date...")
itemmenu.Append(idEDITNOTE, "Note...")
itemmenu.AppendSeparator()
itemmenu.Append(idMAILITEM, "Mail...")

displaymenu = wxMenu()
displaymenu.Append(idSHOWFINISHED, "Show/Hide Finished...")
displaymenu.AppendSeparator()
displaymenu.Append(idSHOWALL, "Show All", "Show all items in the current list")
displaymenu.AppendSeparator()
displaymenu.Append(idREFRESH, "Refresh Display", "Refresh the Display")
displaymenu.Append(idDISPLAYDATE, "Select Date to Display")

toolmenu = wxMenu()
toolmenu.AppendCheckItem(idTICKLERACTIVE, "Tickler Active")
toolmenu.Check(idTICKLERACTIVE,False)
toolmenu.Append(idSHOWNEXT, "Show Next Reminder")
toolmenu.Append(idSYNC, "Synchronize local and remote DBs")
toolmenu.Append(idARCHIVE, "Archive completed items in list...")
toolmenu.Append(idEVALUATE, "Evaluate an expression...")

helpmenu = wxMenu()
helpmenu.Append(idABOUT, "About ListManager")
helpmenu.Append(idHELP, "Help")

menubar = wxMenuBar()
menubar.Append(filemenu, '&amp;File')
menubar.Append(editmenu, 'Edit')
menubar.Append(itemmenu, 'Item')
menubar.Append(displaymenu, 'Display')
menubar.Append(toolmenu, 'Tools')
menubar.Append(helpmenu, 'Help')
self.SetMenuBar(menubar)
toolmenu.Enable(idSHOWNEXT,self.tickler_active)
filemenu.Enable(idDELETELIST,DELETE_LIST)
filemenu.Check(idOFFLINE,OFFLINE_ONLY)

#file history
self.filehistory = wxFileHistory()
self.filehistory.UseMenu(filemenu)

</t>
<t tx="szatz.112203232245.12">tb = self.CreateToolBar(wxTB_HORIZONTAL|wxTB_FLAT)

tb.AddLabelTool(idNEWLIST, "New (local) List", wxBitmap('bitmaps\\new.bmp'), shortHelp="Create New List")
tb.AddLabelTool(idOPENLIST, "Open", wxBitmap('bitmaps\\open.bmp'), shortHelp="Open List")
tb.AddSeparator()
tb.AddLabelTool(idTOOLPRINT, "Print", wxBitmap('bitmaps\\print.bmp'), shortHelp="Print List")
tb.AddLabelTool(idPRINTPREV, "Preview", wxBitmap('bitmaps\\preview.bmp'), shortHelp="Print Preview")
tb.AddLabelTool(idPAGESETUP, "Setup", wxBitmap('bitmaps\\setup.bmp'), shortHelp="Page Setup")
tb.AddSeparator()
tb.AddLabelTool(idNEWITEM, "New Item", wxBitmap('bitmaps\\new_item.bmp'), shortHelp="Create New Item")
tb.AddSeparator()
tb.AddLabelTool(idREFRESH, "Refresh", wxBitmap('bitmaps\\refresh.bmp'), shortHelp="Refresh Display")     
tb.AddSeparator()
tb.AddLabelTool(idEDITNOTE, "Edit Note", wxBitmap('bitmaps\\edit_doc.bmp'), shortHelp="Edit Note")
tb.AddSeparator()
tb.AddLabelTool(idFIND, "Find", wxBitmap('bitmaps\\find.bmp'), shortHelp = "Find Item")        
tb.AddSeparator()
tb.AddLabelTool(idCUT, "Cut", wxBitmap('bitmaps\\editcut.bmp'), shortHelp ="Cut Item")        
tb.AddLabelTool(idCOPY, "Copy", wxBitmap('bitmaps\\copy.bmp'), shortHelp ="Copy Item")
tb.AddLabelTool(idPASTE, "Paste", wxBitmap('bitmaps\\paste.bmp'), shortHelp="Paste Item")
tb.AddSeparator()
tb.AddLabelTool(idTOGGLEFINISHED, "Toggle Date", wxBitmap('bitmaps\\filledbox.bmp'), shortHelp="Toggle Finished Date")
tb.AddLabelTool(idDELETEITEMS, "Delete", wxBitmap('bitmaps\\delete.bmp'), shortHelp="Delete Item")
tb.AddLabelTool(idDUEDATE, "Due Date", wxBitmap('bitmaps\\calendar.bmp'), shortHelp="Enter Due Date")
tb.AddLabelTool(idEDITOWNER,"Owner", wxBitmap('bitmaps\\owners.bmp'), shortHelp="Select Owner(s)")
tb.AddSeparator()
tb.AddLabelTool(idMAILITEM, "Mail", wxBitmap('bitmaps\\mail.bmp'), shortHelp="Mail Item")

if QUICK_LIST:
    tb.AddSeparator()
    tb.AddLabelTool(idSENDTO, "Send to", wxBitmap('bitmaps\\sendto.bmp'), shortHelp="Send to %s"%QUICK_LIST)
    
tb.Realize()
</t>
<t tx="szatz.112203232245.13">#File Menu ------------------------------------
EVT_MENU(self, idNEWLIST, self.OnNewList)
EVT_MENU(self, idOPENLIST, self.OnOpenList)
EVT_MENU(self, idCLOSELIST, self.OnCloseList)
EVT_MENU(self, idCLOSEALL, self.OnCloseAll)
EVT_MENU(self, idSAVEAS, self.OnSaveAsText)
EVT_MENU(self, idDELETELIST, self.OnDeleteList)
EVT_MENU(self, idPAGESETUP, self.OnPageSetup)
EVT_MENU(self, idPRINT, self.OnPrint)
EVT_MENU(self, idPRINTPREV, lambda e: self.OnPrint(e, prev=True))
EVT_MENU(self, idOFFLINE, self.OnWorkOffline)
EVT_MENU(self, idMAILLIST, self.OnMailView)      
EVT_MENU_RANGE(self, wxID_FILE1, wxID_FILE9, self.OnFileList)
EVT_MENU(self, idEXIT, self.OnExit)
#Edit Menu ------------------------------------
EVT_MENU(self, idCUT, lambda e: self.OnCopyItems(e, cut=True))        
EVT_MENU(self, idCOPY, self.OnCopyItems)
EVT_MENU(self, idPASTE, self.OnPasteItems)
EVT_MENU(self, idDELETEITEMS, self.OnDeleteItems)
EVT_MENU(self, idCOMBINEITEMS, self.OnCombineItems)
EVT_MENU(self, idFIND, self.OnFind)
#item Menu ------------------------------------
EVT_MENU(self, idNEWITEM, self.OnNewItem)
EVT_MENU(self, idTOGGLEFINISHED, self.OnToggleFinished)             
EVT_MENU(self, idDUEDATE, self.OnDueDate)
EVT_MENU(self, idEDITOWNER, self.OnEditOwner)
EVT_MENU(self, idEDITNOTE, self.OnEditNote)
EVT_MENU(self, idMAILITEM, self.OnMailItem)
#Dips Menu ------------------------------------
EVT_MENU(self, idSHOWFINISHED, self.OnShowFinished)
EVT_MENU(self, idSHOWALL, self.OnShowAll)
EVT_MENU(self, idREFRESH, self.OnRefresh)
EVT_MENU(self, idDISPLAYDATE, self.OnDisplayDateCategory)
#Tool Menu ---------------------------------------
EVT_MENU(self, idTICKLERACTIVE, self.OnActivateTickler)
EVT_MENU(self, idSHOWNEXT, self.OnShowTickler)
EVT_MENU(self, idSYNC, self.OnSync)
EVT_MENU(self, idARCHIVE, self.OnArchive)
EVT_MENU(self, idEVALUATE, self.OnShowEvaluate)
#Help Menu -----------------------------------------
EVT_MENU(self, idABOUT, self.OnShowAbout)
EVT_MENU(self, idHELP, self.OnShowHelp)

EVT_TOOL(self, idTOOLPRINT, lambda e: self.OnPrint(e,showprtdlg=False))

if QUICK_LIST:
    EVT_TOOL(self, idSENDTO, lambda e: self.OnMoveToSpecificList(e,QUICK_LIST))
</t>
<t tx="szatz.112203232245.14">upper_panel = wxPanel(self, -1)   #size = (900,400)
bottom_panel = wxPanel(self, -1, size = (900,150)) #900 note that 000 seems to work???

nb = wxNotebook(upper_panel, -1, size=(900,500), style=wxNB_BOTTOM)

f = wxFont(10, wxSWISS, wxNORMAL, wxNORMAL)
self.name = wxTextCtrl(bottom_panel, -1, size = (285,42), style = wxTE_MULTILINE|wxTE_RICH2)#34 #wxTE_PROCESS_ENTER
self.name.SetDefaultStyle(wxTextAttr("BLACK", font = f))
     
self.owners = wxTextCtrl(bottom_panel, -1, size = (250,42),style = wxTE_MULTILINE|wxTE_RICH2)
self.owners.SetDefaultStyle(wxTextAttr("BLACK", font = f))

self.note = wxTextCtrl(bottom_panel, -1, size = (400,50), style=wxTE_MULTILINE)
 </t>
<t tx="szatz.112203232245.15">EVT_TEXT(self, self.name.GetId(), lambda e: self.modified.update({'name':1}))
EVT_TEXT(self, self.note.GetId(), lambda e: self.modified.update({'note':1}))
EVT_TEXT(self, self.owners.GetId(), lambda e: self.modified.update({'owners':1}))

EVT_CLOSE(self, self.OnWindowExit)

EVT_IDLE(self, self.OnIdle)

</t>
<t tx="szatz.112203232245.16">#Appears necessary to really get the listcontrol to size with the overall window  
#upper_panel sizer
sizer = wxBoxSizer(wxHORIZONTAL)
sizer.Add(nb,1,wxALIGN_LEFT|wxEXPAND)
upper_panel.SetSizer(sizer)        

#sizer for the row of data items
box = wxBoxSizer(wxHORIZONTAL)
box.Add(self.name,1,wxEXPAND)
box.Add(self.owners,0)

#bottom_panel sizer  
sizer = wxBoxSizer(wxVERTICAL)        
sizer.AddSizer(box, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5)
sizer.Add(self.note,1,wxALIGN_LEFT|wxEXPAND)
bottom_panel.SetSizer(sizer)

sizer = wxBoxSizer(wxVERTICAL)
sizer.Add(upper_panel,1,wxALIGN_TOP|wxEXPAND)
sizer.Add(bottom_panel,0,wxALIGN_TOP|wxEXPAND)

self.SetAutoLayout(1)
self.SetSizer(sizer)
#sizer.Fit(self) #actively does bad things to the dimensions on startup
</t>
<t tx="szatz.112203232245.17">self.toolmenu = toolmenu
self.filemenu = filemenu
self.nb = nb
self.tb = tb
</t>
<t tx="szatz.112203232245.18">if OUTLOOK:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create a TCP socket
    s.bind(('localhost',8888)) # Bind to port 8888
    s.listen(5) # Listen, but allow no more than
    self.sock = s
</t>
<t tx="szatz.112203232245.19">try:
    pathlist = [f[1] for f in cp.items('Files')]
except:
    pathlist = []
    
if pathlist:
    pathlist.sort()
    pathlist.reverse()
    for path in pathlist[1:]:
        self.OnFileList(path=path)

    #don't want to trigger the page change event until n-1 of n files are loaded
    EVT_NOTEBOOK_PAGE_CHANGED(self,nb.GetId(),self.OnPageChange)

    self.OnFileList(path=pathlist[0])
else:
    EVT_NOTEBOOK_PAGE_CHANGED(self,nb.GetId(),self.OnPageChange)



</t>
<t tx="szatz.112203232245.20"></t>
<t tx="szatz.112203232245.21">def createownerlist(self):
    
    if REMOTE_HOST and OFFLINE_ONLY is False:
        cursor = self.GetCursor(REMOTE_HOST)
        sql = "SHOW TABLES" #sorted
    else:
        cursor = self.GetCursor(LOCAL_HOST)
        sql = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
        
    cursor.execute(sql)
    results = cursor.fetchall()

    #excluding 'system' tables and archive tables
    excluded_tables = ['user_sync','sync','owners']
    tables = [t for (t,) in results if t.find('_archive')== -1 and t not in excluded_tables]

    sql_list = []
    for table in tables:
        sql_list.append("""SELECT owner1 FROM %s UNION SELECT owner2 FROM %s UNION SELECT owner3 FROM %s"""%((table,)*3))
                
    sql = " UNION ".join(sql_list)
    cursor.execute(sql)
    results = cursor.fetchall()
    
    _list = [x[0] for x in results]
    if '' in _list:
        _list.remove('')
    if None in _list:
        _list.remove(None)
        
    self._list = _list
    
    #posting custom event to signal that this thread is done
    evt = wxPyEvent()
    evt_id = wxNewEventType()
    evt.SetEventType(evt_id)
    self.Connect(-1, -1, evt_id, self.createownerdialog)
    wxPostEvent(self, evt)

</t>
<t tx="szatz.112203232245.22">def createownerdialog(self, evt=None):
    self.ModifierDialog = ModifierDialog(parent=self, title="Select owner(s)", size=(180,300), style=wxCAPTION, modifierlist = self._list)
    del self._list

</t>
<t tx="szatz.112203232245.23">def OnWorkOffline(self, evt=None):
    global OFFLINE_ONLY
    OFFLINE_ONLY = not OFFLINE_ONLY
    if OFFLINE_ONLY:
        del self.Cursors[REMOTE_HOST]
    else:
        server = REMOTE_HOST.split(':')[0]
        try:
            socket.gethostbyname(server)
        except:
            dlg = wxMessageDialog(None, "Cannot connect to remote server! Will set to work offline.", "ListManager", style=wxOK|wxICON_EXCLAMATION|wxSTAY_ON_TOP)
            dlg.ShowModal()
            dlg.Destroy()
            OFFLINE_ONLY = True

    self.filemenu.Check(idOFFLINE,OFFLINE_ONLY)
    
</t>
<t tx="szatz.112203232245.24">def CreateNewNotebookPage(self, host, table):
    
    Properties = {'owner':'*ALL',
                'LCdate':'duedate',
                'sort':{'attribute':'priority','direction':0}, #these could be set in Config
                'showfinished':0} #-1 show them all; 0 show none; integer show for that many days
    
    Properties['table'] = table
    Properties['host'] = host
                
    self.PropertyDicts.append(Properties)

    self.L = len(self.ItemLists)#could use self.ListCtrls, self.OwnerLBoxes, etc. with a -1
    
    results = self.ReadFromDB()
    if results is None:
        self.PropertyDicts = self.PropertyDicts[:-1]
        self.L = self.L - 1
        return
        
    panel = wxPanel(self.nb, -1, size = (900,400))
    LCtrl = ListCtrl(panel, -1, style=wxLC_REPORT|wxSUNKEN_BORDER|wxLC_VRULES|wxLC_HRULES)
    LCtrl.SetFont(self.LC_font)
    self.ListCtrls.append(LCtrl)
    
    OLBox = wxListBox(panel, -1, size = (126,550), choices = [""], style=wxLB_SORT|wxSUNKEN_BORDER)
    self.OwnerLBoxes.append(OLBox)
    
    sizer = wxBoxSizer(wxHORIZONTAL)
    sizer.Add(OLBox,0,wxALIGN_LEFT|wxEXPAND)
    sizer.Add(LCtrl,1,wxALIGN_LEFT|wxEXPAND)
    panel.SetSizer(sizer)
        
    self.ItemLists.append(self.CreateAndDisplayList(results)) 

    &lt;&lt; Fill OwnerListBox &gt;&gt;
    &lt;&lt; ListControl Events &gt;&gt;
    
    #img_num = LCtrl.arrows[Properties['sort']['direction']]
    #LCtrl.SetColumnImage(self.attr2col_num[Properties['sort']['attribute']], img_num)
    
    rdbms = host.split(':')[1]
    if rdbms == 'mysql':
        tab_title = '%s (remote)'%table
    else:
        tab_title = table
    
    if table in SYNC_TABLES:
        tab_title = '*'+tab_title
                             
    self.nb.AddPage(panel,tab_title)
    self.nb.SetSelection(self.L)
    
    self.filehistory.AddFileToHistory('%s:%s'%(host,table))

    self.SetStatusText("Successfully loaded %s"%tab_title)
    
</t>
<t tx="szatz.112203232245.25">cursor = self.GetCursor(host)
if cursor is None:
    print "Couldn't get cursor to fill OwnerListBox"
    return
    
cursor.execute("SELECT owner1 FROM %s UNION SELECT owner2 FROM %s UNION SELECT owner3 FROM %s"%((table,)*3))

owners = [x for (x,) in cursor.fetchall()]

if None in owners:
    owners.remove(None)
if '' in owners:
    owners.remove('')

OLBox.Clear()
for name in owners: 
    OLBox.Append(name)
OLBox.Append('*ALL')
OLBox.SetSelection(0)

</t>
<t tx="szatz.112203232245.26">LCId = LCtrl.GetId()
EVT_LIST_ITEM_SELECTED(self, LCId, self.OnItemSelected)
EVT_LIST_ITEM_ACTIVATED(self, LCId, self.OnDisplayInPlaceEditor)
EVT_LEFT_DOWN(LCtrl, self.OnLeftDown) 
EVT_LEFT_DCLICK(LCtrl, self.OnLeftDown)
EVT_RIGHT_DOWN(LCtrl, self.OnRightDown)
EVT_LIST_COL_CLICK(self, LCId, self.OnColumnClick)
EVT_LIST_COL_RIGHT_CLICK(self, LCId, self.OnColumnRightClick)

# the following is a ListBox event
EVT_LISTBOX(self, OLBox.GetId(), self.OnFilterOwners)

</t>
<t tx="szatz.112203232245.27">def OnPageChange(self, evt=None):
    if self.modified:
        self.OnUpdate()
        
    self.L = L = self.nb.GetSelection()

    &lt;&lt; Find Highlighted Row &gt;&gt;
    &lt;&lt; Update Title &gt;&gt;
    
    evt.Skip() #051403
    
</t>
<t tx="szatz.112203232245.28">idx = self.ListCtrls[L].GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)
if idx != -1:
    self.curIdx = idx
    #LCtrl.EnsureVisible(idx)
    self.OnItemSelected()
elif self.ItemLists[L]:
    self.curIdx = 0
    self.ListCtrls[L].SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
    #the line above triggers an OnItemSelected EVT so don't need self.OnItemSelected() 092803
else:
    self.curIdx = -1

</t>
<t tx="szatz.112203232245.29">location,rdbms = self.PropertyDicts[L]['host'].split(':')
table = self.PropertyDicts[L]['table']
self.SetTitle("List Manager:  %s:  %s:  %s"%(location,rdbms,table))

</t>
<t tx="szatz.112203232245.30">def OnIdle(self, evt):	
    &lt;&lt; Check for Transfers From Outlook &gt;&gt;
    &lt;&lt; Check if Edited File has Changed &gt;&gt;
    
</t>
<t tx="szatz.112203232245.31">if OUTLOOK:
    input,output,exc = select.select([self.sock],[],[],0)
    if input:
        client,addr = self.sock.accept() # Get a connection
        rec = client.recv(8192)
        d = pickle.loads(rec)
        
        class Item: pass
        
        item = Item()
        item.id = self.GetUID()
        item.priority = 1
        item.createdate = mx.DateTime.now()
        item.duedate = item.finisheddate = None
        
        #outlook strings are unicode; ascii encode makes sure no chars above 127
        name = d['Subject'].encode('ascii','replace') 
        item.name = name[:150]
        
        owner = d['SenderName'].encode('ascii','replace') #encode takes unicode to standard strings
        owner = owner[:25]
        item.owners = [owner]
        
        note = d['CreationTime'] + '\n' + d['Body'].encode('ascii','replace')
        #foldername = d['Parent.Name']
        
        #location, rdbms, table = MAIL_LIST_PATH.split(':')
        #host = '%s:%s'%(location,rdbms)
        host, table = re.split('(.*?:.*?):', MAIL_LIST_PATH)[1:3] #really just for fun
        
        cursor = self.Cursors[host]
        
        cursor.execute("INSERT INTO "+table+" (priority,name,createdate,finisheddate,duedate,owner1,note,id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
            (item.priority, name, item.createdate, item.finisheddate, item.duedate, owner, note, item.id))
        
        item.timestamp = self.TimeStamper(host, cursor, table, item.id)
        
        #check to see if table is open
        for L,Properties in enumerate(self.PropertyDicts):
            if Properties['table'] == table and Properties['host'] == host:
                break
        else:
            print "Table not open but wrote to database anyway" #Needs to be a dialog box
            return
        
        # could have started to edit something and never finished it
        if self.modified:
            self.OnUpdate()
        
        if self.L != L:
            self.nb.SetSelection(L) # Note that this does not call OnPageChange if the page doesn't change
        
        LCtrl = self.ListCtrls[L]
        
        if self.curIdx != -1:
            LCtrl.SetItemState(self.curIdx, 0, wxLIST_STATE_SELECTED)
        
        self.ItemLists[L].insert(0,item)    
        LCtrl.InsertImageStringItem(0,"1", LCtrl.idx1)
        LCtrl.SetStringItem(0,self.attr2col_num['name'],name)
        LCtrl.SetStringItem(0, self.attr2col_num['owners'], owner)
        
        if Properties['LCdate'] == 'timestamp':
            LCtrl.SetStringItem(0, self.attr2col_num['date'], item.timestamp.Format("%m/%d %H:%M:%S"))
        elif Properties['LCdate'] == 'createdate':
            LCtrl.SetStringItem(0, self.attr2col_num['date'], item.createdate.Format('%m/%d/%y'))
        
        LCtrl.SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
        self.curIdx = 0 

</t>
<t tx="szatz.112203232245.32">for ed in self.editor:
    path = ed['path']
    t = os.path.getmtime(path)
    if t != ed['time']:
        f = file(path,'r')
        note = f.read()
        f.close()
        ed['time'] = t
        
        host = ed['host']
        cursor = self.Cursors[host]
        table = ed['table']
        id = ed['id']
        cursor.execute("UPDATE "+table+" SET note = %s WHERE id = %s", (note,id)) 
        # see @rst documentation note
        ts = self.TimeStamper(host, cursor, table, id)
        
        idx = self.curIdx
        L = self.L
        if idx != -1:
            item = self.ItemLists[L][idx]
            if item.id == id:
                self.note.SetValue(note)
                item.timestamp = ts
                
                if self.PropertyDicts[L]['LCdate'] == 'timestamp':
                    self.ListCtrls[L].SetStringItem(idx, self.attr2col_num['date'], item.timestamp.Format("%m/%d %H:%M:%S"))
    
                if 'note' in self.modified: #if necessary only if somehow note text didn't change
                    del self.modified['note']

</t>
<t tx="szatz.112203232245.33"></t>
<t tx="szatz.112203232245.34">def OnShowTickler(self, evt=None):
    if self.popupvisible:
        return
    
    self.popupvisible = True
    
    host = 'wxLMDB:sqlite'
    cursor = self.Cursors[host]
    table = 'follow_ups'

    sql = "SELECT COUNT() FROM "+table+" WHERE finisheddate IS NULL AND priority &gt; 1"
    cursor.execute(sql)
    results = cursor.fetchone()

    num_items = int(results[0])
    
    if not num_items:
        return

    if self.modified: #Should decide if this should be put back or not
        self.OnUpdate()
        
    n = random.randint(0,num_items-1)

    sql = "SELECT priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,id,timestamp,note FROM "+table+" WHERE finisheddate IS NULL AND priority &gt; 1 LIMIT 1 OFFSET %d"%n
    
    try:
        cursor.execute(sql)
    except:
        print "In OnShowTickler and attempt to Select an item failed"
        return
        
    row = cursor.fetchone()
    
    class Item: pass
    item = Item()

    item.priority = int(row[0]) #int(row[0]) needs int because it seems to come back as a long from MySQL
    item.name = row[1]
    item.createdate = row[2]
    item.finisheddate = row[3]
    item.duedate = row[4]
    item.owners = [z for z in row[5:7] if z is not None] #if you carry around ['tom',None,None] you have an issue when you go write it
    item.id = row[8]
    item.timestamp = row[9]
    item.note = row[10]

    dlg = TicklerDialog(self, "", "Do something about this!!!", size=(550,350))
    TC = dlg.TC
    
    f = wxFont(14, wxSWISS, wxITALIC, wxBOLD, False)
    TC.SetDefaultStyle(wxTextAttr("BLUE",wxNullColour, f))
    TC.AppendText("%s..."%item.name)

    if item.priority == 3:
        TC.SetDefaultStyle(wxTextAttr("RED","YELLOW",f))
    TC.AppendText("%d\n\n"%item.priority)
    
    f = wxFont(8, wxSWISS, wxNORMAL, wxNORMAL)
    TC.SetDefaultStyle(wxTextAttr("BLACK","WHITE", f))
    TC.AppendText("owners: %s\n"%", ".join(item.owners))
    TC.AppendText("created on: %s\n"%item.createdate.Format('%m/%d/%y'))
    if item.duedate:
        ddate = item.duedate.Format('%m/%d/%y')
    else:
        ddate = "&lt;no due date&gt;"
    TC.AppendText("due on: %s\n\n"%ddate)

    note = item.note
    if not note:
        note = "&lt;no note&gt;"
    TC.AppendText("%s\n\n"%note)
    f = wxFont(10, wxSWISS, wxITALIC, wxBOLD)
    TC.SetDefaultStyle(wxTextAttr("BLACK",wxNullColour, f))
    TC.AppendText('follow_ups')
    TC.ShowPosition(0)   #did not do anything
    TC.SetInsertionPoint(0)
    result = dlg.ShowModal()
    dlg.Destroy()
    self.popupvisible = False     

    if result in (wxID_OK, wxID_APPLY):

        for L,Properties in enumerate(self.PropertyDicts):
            if Properties['table'] == table:
                break
        else:
            print "Can't find %s"%table
            return
                    
        self.nb.SetSelection(L) #if the page changes it sends a EVT_NOTEBOOK_PAGE_CHANGED, which calls OnPageChange
        self.L = L
        self.FindNode(item)
        if result==wxID_APPLY:
            self.OnMailItem(item)

    elif result==wxID_FORWARD:
        self.OnShowTickler()

</t>
<t tx="szatz.112203232245.35">def OnActivateTickler(self, evt):
    self.tickler_active = not self.tickler_active
    self.toolmenu.Enable(idSHOWNEXT,self.tickler_active)

    
</t>
<t tx="szatz.112203232245.36"></t>
<t tx="szatz.112203232245.37">def OnMailItem(self, evt=None, item=None):
    if item is None:
        if self.curIdx == -1:
            return
        else:
            item = self.ItemLists[self.L][self.curIdx]
        
    dlg = MailDialog(self,"Mail a reminder", size=(450,500),
               recipients=item.owners,    
               subject=item.name,
               body=self.GetNote())          
    result = dlg.ShowModal()
    if result==wxID_OK:
        outlook= Dispatch("Outlook.Application")
        newMsg = outlook.CreateItem(olMailItem) #outlook.CreateItem(constants.olMailItem)
        newMsg.To = to = dlg.RTC.GetValue()
        newMsg.Subject = subject = dlg.STC.GetValue()
        newMsg.Body = body = dlg.BTC.GetValue()

        #newMsg.FlagStatus = constants.olFlagMarked
        
        newMsg.Display()

        dlg.Destroy()            
        #del outlook

        self.note.SetSelection(0,0)
        self.note.WriteText("**************************************************\n")
        self.note.WriteText("Email sent on %s\n"%mx.DateTime.today().Format("%m/%d/%y"))
        self.note.WriteText("To: %s\n"%to)
        self.note.WriteText("Subject: %s\n"%subject)
        self.note.WriteText("%s\n"%body)
        self.note.WriteText("**************************************************\n")

</t>
<t tx="szatz.112203232245.38">def OnMailView(self, evt=None):
    recipients = [self.PropertyDicts[self.L]['owner']]
    
    body = ""
    for i,item in enumerate(self.ItemLists[self.L]):
        body = body+"%d. %s (%d)\n"%(i+1, item.name, item.priority)
    
    subject = "Follow-ups " + mx.DateTime.today().Format("%m/%d/%y")
            
    dlg = MailDialog(self,"Follow-up List", size=(450,500),
               recipients=recipients,
               subject=subject,
               body=body)
               
    val = dlg.ShowModal()
    dlg.Destroy()
    if val==wxID_OK:
        outlook= Dispatch("Outlook.Application")
        newMsg = outlook.CreateItem(olMailItem) #outlook.CreateItem(constants.olMailItem)
        newMsg.To = dlg.RTC.GetValue()
        newMsg.Subject = dlg.STC.GetValue()
        newMsg.Body = dlg.BTC.GetValue()

        newMsg.FlagStatus = olFlagMarked #constants.olFlagMarked
        newMsg.Categories = "Follow-up"
        
        newMsg.Display()
    
        #del outlook

</t>
<t tx="szatz.112203232245.39"></t>
<t tx="szatz.112203232245.40">def OnCopyItems(self, event=None, cut=False):
    if self.curIdx == -1:
        return
        
    L = self.L
    IList = self.ItemLists[L]
    LCtrl = self.ListCtrls[L]
    
    &lt;&lt; Find Highlighted Items &gt;&gt;
    
    self.SetStatusText("%d items copied"%len(copyitems))
    if cut:
        self.OnDeleteItems()

</t>
<t tx="szatz.112203232245.41">copyitems = []
i = -1
while 1:
    i = LCtrl.GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)
    if i==-1:
        break
    item = IList[i]
    item.notes = self.GetNote(L,item) #handles the database situation
    copyitems.append(item)

self.copyitems = copyitems</t>
<t tx="szatz.112203232245.42">def OnPasteItems(self, evt=None, L=None): #noselect 051603
    #used by OnMoveToList, OnMoveToSpecificList and called directly
    if not self.copyitems:
        print "Nothing was selected to be copied"
        return
        
    if L is None: #this is not needed by OnMoveTo or OnDragToTab but is for a straight call
        L = self.L
        
    Properties = self.PropertyDicts[L]
    LCtrl = self.ListCtrls[L]
    IList = self.ItemLists[L]
    
    items = self.copyitems
    numitems = len(items)
    
    for item in items:

        z = item.owners+[None,None,None]

        id = self.GetUID() #we do give it a new id
        host = Properties['host']
        cursor = self.Cursors[host]
        table = Properties['table']
        
        createdate = mx.DateTime.now() #need this or else it won't be seen as a new item when synching; would be seen as updated
        command = "INSERT INTO "+table+" (priority,name,createdate,finisheddate,duedate,note,owner1,owner2,owner3,id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
        cursor.execute(command,(item.priority,item.name,createdate,item.finisheddate,item.duedate,item.notes,z[0],z[1],z[2],id))
        
        timestamp = self.TimeStamper(host, cursor, table, id)
        
        #creating a new item breaks the connection between item.x and new_item.x
        class Item: pass
        new_item = Item()
        new_item.id = id
        new_item.priority = item.priority
        new_item.owners = item.owners
        new_item.name = item.name
        new_item.timestamp = timestamp
        new_item.duedate =item.duedate
        new_item.finisheddate = item.finisheddate
        new_item.createdate = createdate
        IList.insert(0,new_item)
        
    self.DisplayList(IList,L)
    
    #If we didn't come from OnMoveToList or OnMoveToSpecificList where L != self.L
    if L==self.L:
        for i in range(numitems):
            LCtrl.SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
        self.curIdx = numitems-1



</t>
<t tx="szatz.112203232245.43">def OnDeleteItems(self, event=None):
    """Called directly and by OnCopyItems (cut = true)
    """
    if self.curIdx == -1: #not absolutely necessary but gets you out quickly
        return
        
    L = self.L
    LCtrl = self.ListCtrls[L]
    IList = self.ItemLists[L]
    Properties = self.PropertyDicts[L]
    
    i = -1
    while 1:
        i = LCtrl.GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)
        if i==-1:
            break
        item = IList.pop(i)
        LCtrl.DeleteItem(i)

        host = Properties['host']
        cursor = self.Cursors[host]
        table = Properties['table']
        
        cursor.execute("DELETE from "+table+" WHERE id = %s", (item.id,))
            
        #Track Deletes for Syncing ############################################
        if table in SYNC_TABLES:
            if host.split(':')[1] == 'sqlite':
                timestamp = mx.DateTime.now()
                cursor.execute("INSERT INTO sync (id,action,table_name,name,timestamp) VALUES (%s,%s,%s,%s,%s)",(item.id,'d',table,item.name,timestamp))
            else:
                cursor.execute("INSERT INTO sync (id,action,table_name,user,name) VALUES (%s,%s,%s,%s,%s)",(item.id,'d',table,USER,item.name))
        #########################################################################
        i-=1

    self.name.Clear()
    self.owners.Clear()
    self.note.Clear()
    #note that Clearing does cause self.modified --&gt;{'name':1}
    self.modified = {}
    self.curIdx = -1

</t>
<t tx="szatz.112203232245.44">def OnLeftDown(self, evt):
    print "Here"
    if self.modified:
        #if inplace editor is open and you click anywhere (same or different row from current row) but in the editor itself then just to close editor
        flag = self.modified.has_key('inplace')
        self.OnUpdate()
        if flag:
            evt.Skip() #without Skip, EVT_LIST_ITEM_SELECTED is not generated if you click in a new row
            return
    
    x,y = evt.GetPosition()
    LCtrl = self.ListCtrls[self.L]
    
    #Using HitTest to obtain row clicked on because there was a noticable delay in the generation of an
    #EVT_LIST_ITEM_SELECTED event when you click on the already selected row
    idx,flags = LCtrl.HitTest((x,y))
    
    #if you are below rows of items then idx = -1 which could match self.curIdx = -1
    if idx == -1:
        return
    
    # only if you click on the currently selected row do the following events occur
    if idx == self.curIdx:
        if x &lt; 18:
            self.OnToggleFinished()
        elif x &lt; 33:
            self.OnPriority()
        elif x &lt; 33 + LCtrl.GetColumnWidth(1):
            self.OnDisplayInPlaceEditor()
        elif x &lt; 33 + LCtrl.GetColumnWidth(1) + LCtrl.GetColumnWidth(2): 
            self.OnEditOwner()
        else:
            self.OnDueDate
    else:
        evt.Skip() #without Skip, EVT_LIST_ITEM_SELECTED is not generated if you click in a new row



</t>
<t tx="szatz.112203232245.45">def OnRightDown(self, evt):
    x,y = evt.GetPosition()
    
    sendtomenu = wxMenu()
    
    open_tables = []
    for page,Properties in enumerate(self.PropertyDicts):
        host,table = Properties['host'],Properties['table']
        open_tables.append((host,table))
        sendtomenu.Append(1+page,"%s (%s)"%(table,host))
        EVT_MENU(self, 1+page, lambda e,p=page: self.OnMoveToList(e,p))
        
    sendtomenu.Delete(self.L+1) # don't send it to the page you're already on
    sendtomenu.AppendSeparator()
    
    self.closed_tables = []
    for host,cursor in self.Cursors.items():

        location, rdbms = host.split(':')

        if rdbms == 'sqlite':
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
        elif rdbms == 'mysql':
            cursor.execute("SHOW tables")

        results = cursor.fetchall()
        
        page+=1
        for (table,) in results:
            if not ((host,table) in open_tables or table in ['user_sync','owners','sync']):
                self.closed_tables.append((host,table))
                sendtomenu.Append(1+page,"%s (%s)"%('*'+table,host))
                EVT_MENU(self, 1+page, lambda e,p=page: self.OnMoveToList(e,p))
                page+=1

    self.PopupMenu(sendtomenu,(x+125,y+40))
    sendtomenu.Destroy()

</t>
<t tx="szatz.112203232245.46"></t>
<t tx="szatz.112203232245.47">def OnCombineItems(self, evt):
    L = self.L
    idx = self.curIdx
    IList = self.ItemLists[L]
    LCtrl = self.ListCtrls[L]
    
    combine_list = []
    i = -1
    while 1:
        i = LCtrl.GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)
        if i==-1:
            break
        combine_list.append((IList[i].createdate,IList[i]))

    
    if len(combine_list) &lt; 2:
        print "Fewer than two items highlighted"
        return
    
    combine_list.sort()
    combine_list.reverse()
    
    dlg = wxMessageDialog(self,
                        "Combine the %d selected items?"%len(combine_list),
                        "Combine Items?",
                        wxICON_QUESTION|wxYES_NO)
                        
    if dlg.ShowModal() == wxID_YES:
        Properties = self.PropertyDicts[L]
        host = Properties['host']
        cursor = self.Cursors[host]
        table = Properties['table']
        
        t_item = combine_list[0][1]
        merge_list = combine_list[1:]
        new_note = ""
        
        for date,item in merge_list:
            note = self.GetNote(item=item)
            date = date.Format("%m/%d/%y")
            new_note = "%s\n%s %s\n\n%s"%(new_note, date, item.name, note)
            
            cursor.execute("DELETE from "+table+" WHERE id = %s", (item.id,))
            #Track Deletes for Syncing ############################################
            if table in SYNC_TABLES:
                if host.split(':')[1] == 'sqlite':
                    timestamp = mx.DateTime.now()
                    cursor.execute("INSERT INTO sync (id,action,table_name,name,timestamp) VALUES (%s,%s,%s,%s,%s)",(item.id,'d',table,item.name,timestamp))
                else:
                    cursor.execute("INSERT INTO sync (id,action,table_name,user,name) VALUES (%s,%s,%s,%s,%s)",(item.id,'d',table,USER,item.name))
            #########################################################################
                
        t_note = self.GetNote(item=t_item)
        t_note = "%s\n%s"%(t_note,new_note)
        
        #What about combining owners?######################################
        
        cursor.execute("UPDATE "+table+" SET name = %s, note = %s WHERE id = %s", (t_item.name+"*",t_note,t_item.id))
        t_item.timestamp = self.TimeStamper(host, cursor, table, t_item.id)
        
        self.OnRefresh()
        LCtrl.SetItemState(0, 0, wxLIST_STATE_SELECTED)
        IList = self.ItemLists[L]
        id = t_item.id
        idx = -1
        for item in IList:
            idx+=1
            if id == item.id:
                break
        else:
            idx = -1 
    
        #should never be -1
        if idx != -1:	
            LCtrl.SetItemState(idx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
            LCtrl.EnsureVisible(idx)
        self.curIdx = idx
        
    dlg.Destroy()

</t>
<t tx="szatz.112203232245.48">def OnMoveToList(self, evt=None, page=0):
    self.OnCopyItems(cut=True)
    pc = self.nb.GetPageCount()
    if page &lt; pc:		
        self.OnPasteItems(L=page)
    else:
        host,table = self.closed_tables[page-pc]
        cursor = self.Cursors[host]# in ini self.Cursors[host]
    
        for item in self.copyitems:
            z = item.owners+[None,None,None]
            id = self.GetUID() #give it a new id
            
            #need this or else it won't be seen as a new item when syncing; would be seen as updated
            createdate = mx.DateTime.now() 
            command = "INSERT INTO "+table+" (priority,name,createdate,finisheddate,duedate,note,owner1,owner2,owner3,id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
            cursor.execute(command,(item.priority,item.name,createdate,item.finisheddate,item.duedate,item.notes,z[0],z[1],z[2],id))
            timestamp = self.TimeStamper(host, cursor, table, id)
            
    self.copyitems = []
    
</t>
<t tx="szatz.112203232245.49">def OnMoveToSpecificList(self, evt=None, table='follow_ups'):
    matches = {}
    for page,Properties in enumerate(self.PropertyDicts):
        host,tble = Properties['host'],Properties['table']
        if tble == table:
            rdbms = host.split(':')[1]
            matches[rdbms] = page
        
    self.OnCopyItems(cut=True)
    
    if matches:
        if matches.get('mysql'):	
            self.OnPasteItems(L=matches['mysql'])
        else:
            self.OnPasteItems(L=matches['sqlite'])
    else:
        cursor = self.Cursors[LOCAL_HOST]
    
        for item in self.copyitems:
            z = item.owners+[None,None,None]
            id = self.GetUID() #give it a new id
            
            #need this or else it won't be seen as a new item when syncing; would be seen as updated
            createdate = mx.DateTime.now() 
            command = "INSERT INTO "+table+" (priority,name,createdate,finisheddate,duedate,note,owner1,owner2,owner3,id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
            cursor.execute(command,(item.priority,item.name,createdate,item.finisheddate,item.duedate,item.notes,z[0],z[1],z[2],id))
            timestamp = self.TimeStamper(host, cursor, table, id)
            
    self.copyitems = []

            

</t>
<t tx="szatz.112203232245.50"></t>
<t tx="szatz.112203232245.51">def OnToggleFinished(self, evt=None):
    L = self.L
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    idx = self.curIdx

    item = self.ItemLists[L][idx]
    LC_Item = LCtrl.GetItem(idx)
    
    if not item.finisheddate:
        item.finisheddate = mx.DateTime.today()
        LC_Item.SetImage(LCtrl.idx0)
    else:
        item.finisheddate = None
        LC_Item.SetImage(LCtrl.idx1)
    
    &lt;&lt; draw item &gt;&gt;

    self.tb.EnableTool(30, True)
    
    host = Properties['host']	
    cursor = self.Cursors[host]
    table = Properties['table']
    
    cursor.execute("UPDATE "+table+" SET finisheddate = %s WHERE id = %s", (item.finisheddate, item.id))
    item.timestamp = self.TimeStamper(host, cursor, table, item.id)
    
    if Properties['LCdate'] == 'timestamp':
        LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.timestamp.Format("%m/%d %H:%M:%S"))
    elif Properties['LCdate'] == 'finisheddate':
        LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.finisheddate.Format('%m/%d/%y'))



</t>
<t tx="szatz.112203232245.52">if item.finisheddate:
    #It appears that SetTextColour resets font weight to Normal but this makes no sense
    #This means that all finished items have Normal weight whether they are priority 3,2 or 1
    #May actually be that GetItem() and then SetItem() sets the weight to Normal no matter what it was originally
    LC_Item.SetTextColour(wxLIGHT_GREY)
    
elif item.priority==1:
    #see note above about SetTextColour apparently resetting weight
    LC_Item.SetTextColour(wxBLACK)
    
elif item.priority==2:
    #LC_Item.SetTextColour(wxBLACK) -- this line should be necessary but it does not appear to be
    # ? font is black so ? if have to reset it
    f = self.LC_font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) # resetting font weight

else:
    LC_Item.SetTextColour(wxRED) #appears to be the only way to set color - can't through font
    f = self.LC_font #LCtrl.font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) # resetting font weight
    
LCtrl.SetItem(LC_Item)</t>
<t tx="szatz.112203232245.53">def OnPriority(self, event=None, input=None):
    L = self.L
    idx = self.curIdx
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    item = self.ItemLists[L][idx]
    
    if input:
        item.priority=input

    else:
        if item.priority &lt; 3:
            item.priority+= 1
        else:
            item.priority=1

    LC_Item = LCtrl.GetItem(idx)

    &lt;&lt; draw item &gt;&gt;

    text = str(item.priority)        
    LCtrl.SetStringItem(idx, 0, text)

    host = Properties['host']
    cursor = self.Cursors[host]
    table = Properties['table']
    
    cursor.execute("UPDATE "+table+" SET priority = %s WHERE id = %s", (item.priority,item.id))
    item.timestamp = self.TimeStamper(host, cursor, table, item.id)
    
    if Properties['LCdate'] == 'timestamp':
        LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.timestamp.Format('%m/%d %H:%M:%S'))
        
    wxCallAfter(LCtrl.SetFocus)
    
</t>
<t tx="szatz.112203232245.54">if item.finisheddate:
    #It appears that SetTextColour resets font weight to Normal but this makes no sense
    #This means that all finished items have Normal weight whether they are priority 3,2 or 1
    #May actually be that GetItem() and then SetItem() sets the weight to Normal no matter what it was originally
    LC_Item.SetTextColour(wxLIGHT_GREY)
    
elif item.priority==1:
    #see note above about SetTextColour apparently resetting weight
    LC_Item.SetTextColour(wxBLACK)
    
elif item.priority==2:
    #LC_Item.SetTextColour(wxBLACK) -- this line should be necessary but it does not appear to be
    # ? font is black so ? if have to reset it
    f = self.LC_font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) # resetting font weight

else:
    LC_Item.SetTextColour(wxRED) #appears to be the only way to set color - can't through font
    f = self.LC_font #LCtrl.font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) # resetting font weight
    
LCtrl.SetItem(LC_Item)</t>
<t tx="szatz.112203232245.55"></t>
<t tx="szatz.112203232245.56">def OnDisplayInPlaceEditor(self,evt=None):
    L = self.L
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    idx = self.curIdx
    item = self.ItemLists[L][idx]
    
    host = Properties['host']
    cursor = self.Cursors[host]
    table = Properties['table']
        
    #if self.Conflict(host, cursor, table, item): return #works -- may be overkill so i've commented it out
    
    TCid = wxNewId()
    y = LCtrl.GetItemPosition(idx)[1] 
    length = LCtrl.GetColumnWidth(1)

    editor = wxTextCtrl(self, TCid, pos = (167,y+28), size = (length,23), style=wxTE_PROCESS_ENTER)
    editor.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxNORMAL))
    editor.SetBackgroundColour(wxColour(red=255,green=255,blue=175)) #Yellow
    editor.AppendText(item.name)
    editor.Show(True)
    editor.Raise()
    editor.SetSelection(-1,-1)
    editor.SetFocus()	
    
    EVT_TEXT_ENTER(self, TCid, self.OnCloseInPlaceEditor)		

    self.in_place_editor = editor
    self.modified['inplace'] = 1	





</t>
<t tx="szatz.112203232245.57">def OnCloseInPlaceEditor(self,evt=None):
    L = self.L
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    idx = self.curIdx
    item = self.ItemLists[L][idx]
    
    host = Properties['host']
    cursor = self.Cursors[host]
    table = Properties['table']
    LCdate = Properties['LCdate']
    
    #if self.Conflict(host, cursor, table, item)...

    text = self.in_place_editor.GetValue().strip()[:150]
    item.name = text
    LCtrl.SetStringItem(idx, self.attr2col_num['name'], text)
    self.in_place_editor.Destroy()
    
    cursor.execute("UPDATE "+table+" SET name = %s WHERE id = %s", (text, item.id))
    item.timestamp = self.TimeStamper(host, cursor, table, item.id)

    if Properties['LCdate'] == 'timestamp':
        LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.timestamp.Format('%m/%d %H:%M:%S'))

    self.name.Clear()
    self.name.AppendText(text) #this will cause self.modified['name'] = 1, which is dealt with below
    
    #using default in case for some reason self.modified does not have the keys
    self.modified.pop('inplace', None)
    self.modified.pop('name', None)
        
    wxCallAfter(LCtrl.SetFocus) #sets focus on LCtrl and current selection to be highlighted



</t>
<t tx="szatz.112203232245.58">def OnDueDate(self, evt=None):
    idx = self.curIdx
    if idx == -1:
        return
    L = self.L
    Properties = self.PropertyDicts[L]
    item = self.ItemLists[L][idx]
    LCtrl = self.ListCtrls[L]

    if item.duedate:
        date = wxDateTime()
        date.SetTimeT(item.duedate) #I am surprised it takes a mx.DateTime object; supposed to need ticks
    else:
        date = 0
    dlg = CalendarDialog(parent=self,
                 title="Select a date",
                 size=(400,400),
                 style=wxCAPTION,
                 date = date)
    if dlg.ShowModal()==wxID_OK:
        date = dlg.cal.GetDate() # this is some date object
        #date = date.GetTicks()
        item.duedate = mx.DateTime.DateFromTicks(date.GetTicks())

        host = Properties['host']
        cursor = self.Cursors[host]
        table = Properties['table']
        
        cursor.execute("UPDATE "+table+" SET duedate = %s WHERE id = %s", (item.duedate,item.id))
        item.timestamp = self.TimeStamper(host, cursor, table, item.id)
        if Properties['LCdate'] == 'timestamp':
            LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.timestamp.Format("%m/%d %H:%M:%S"))
        elif Properties['LCdate'] == 'duedate':
            LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.duedate.Format('%m/%d/%y'))
    dlg.cal.Destroy()
    dlg.Destroy()
    
</t>
<t tx="szatz.112203232245.59">def OnEditOwner(self, evt=None): #, new=False) removed Aug. 31 for simplicity
    idx = self.curIdx
    if idx == -1:
        return
    L = self.L
    Properties = self.PropertyDicts[L]
    LCtrl = self.ListCtrls[L]
    item = self.ItemLists[L][idx]
    if not self.ModifierDialog:
        print "self.ModifierDialog is still being constructed"
        return
    #need to clear the current selections or you'll just be making more and more selections
    self.ModifierDialog.SelectCurrent(item.owners)
    self.ModifierDialog.tc.Clear()
    self.ModifierDialog.CenterOnParent()
    
    val = self.ModifierDialog.ShowModal()

    if val == wxID_OK:
        item.owners, new_names = self.ModifierDialog.GetUserInput()
        
        &lt;&lt; Common Owner Code &gt;&gt;

        for owner in item.owners:
            if self.OwnerLBoxes[L].FindString(owner) == -1:
                self.OwnerLBoxes[L].Append(owner)

        for owner in new_names:
            self.ModifierDialog.lb.Append(owner)
        
        host = Properties['host']
        cursor = self.Cursors[host]
        table = Properties['table']

        cursor.execute("UPDATE "+table+" SET owner1 = %s, owner2 = %s, owner3 = %s WHERE id = %s", (z[0],z[1],z[2],item.id))
        item.timestamp = self.TimeStamper(host, cursor, table, item.id)
        if Properties['LCdate'] == 'timestamp':
            LCtrl.SetStringItem(idx, self.attr2col_num['date'], item.timestamp.Format("%m/%d %H:%M:%S"))

        if 'owners' in self.modified:
            del self.modified['owners']
            
    wxCallAfter(LCtrl.SetFocus)
    
</t>
<t tx="szatz.112203232245.60">owner_str = '; '.join(item.owners)
LCtrl.SetStringItem(idx, self.attr2col_num['owners'], owner_str)
self.owners.Clear()
self.owners.AppendText(owner_str)
        
z = item.owners+[None,None,None] #note that + creates a new list
</t>
<t tx="szatz.112203232245.61">def OnUpdate(self, evt=None):
    if 'inplace' in self.modified:
        self.OnCloseInPlaceEditor()
        if not self.modified:
            return

    L = self.L
    LCtrl = self.ListCtrls[L]
    IList = self.ItemLists[L]
    Properties = self.PropertyDicts[L]
    OLBox = self.OwnerLBoxes[L]
    idx = self.curIdx

    # there is some chance that it is never true that idx == -1 and then this could be eliminated
    if idx != -1:
        item = IList[idx]
    else:
        msg = wxMessageDialog(self, "There is no selected item to update", "", wxICON_ERROR|wxOK)
        msg.ShowModal()
        msg.Destroy()
        self.modified = {}
        return
        
    host = Properties['host']
    cursor = self.Cursors[host]
    table = Properties['table']
    
    if 'name' in self.modified:
        item.name = self.name.GetValue().strip()[:150]
        LCtrl.SetStringItem(idx, self.attr2col_num['name'], item.name)
        cursor.execute("UPDATE "+table+" SET name =%s WHERE id = %s",(item.name,item.id))
        
    if 'note' in self.modified:
        note = self.note.GetValue() #a blank note starts out as None but after this it becomes '' -- ??
        cursor.execute("UPDATE "+table+" SET note =%s WHERE id = %s",(note,item.id))
        
    if 'owners' in self.modified:
        owner_str = self.owners.GetValue().strip()
        item.owners = []
        if owner_str:
            owner_list = [x.strip() for x in owner_str.split(';')]
            for owner in owner_list:
                owner = ", ".join([x.strip().title() for x in owner.split(',')])
                item.owners.append(owner)
            
        &lt;&lt; Common Owner Code &gt;&gt;

        cursor.execute("UPDATE "+table+" SET owner1 = %s, owner2 = %s, owner3 = %s WHERE id = %s", (z[0],z[1],z[2],item.id))
        
        for owner in item.owners:
            if self.ModifierDialog.lb.FindString(owner) == -1:
                self.ModifierDialog.lb.Append(owner)
                OLBox.Append(owner)
            elif OLBox.FindString(owner) == -1:
                OLBox.Append(owner)		
                
    item.timestamp = self.TimeStamper(host, cursor, table, item.id)
    if Properties['LCdate'] == 'timestamp':
        LCtrl.SetStringItem(idx, 3, item.timestamp.Format("%m/%d %H:%M:%S"))
    
    self.modified = {}
    
    
</t>
<t tx="szatz.112203232245.62">owner_str = '; '.join(item.owners)
LCtrl.SetStringItem(idx, self.attr2col_num['owners'], owner_str)
self.owners.Clear()
self.owners.AppendText(owner_str)
        
z = item.owners+[None,None,None] #note that + creates a new list
</t>
<t tx="szatz.112203232245.63">def OnNewItem(self, evt=None):
    L=self.L
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    
    if self.curIdx != -1:
        LCtrl.SetItemState(self.curIdx, 0, wxLIST_STATE_SELECTED)
    
    &lt;&lt; Clear data fields &gt;&gt;
    
    class Item: pass
    item = Item()
    item.name = '&lt;New Item&gt;'
    item.priority = 1
    item.owners = []
    item.createdate = mx.DateTime.now() #need this to be a timestamp and not just date for syncing
    item.duedate = item.finisheddate = None

    self.ItemLists[L].insert(0,item)
    
    host = Properties['host']
    cursor = self.Cursors[host]
    table = Properties['table']
    item.id = self.GetUID()
    
    cursor.execute("INSERT INTO "+table+" (priority,name,createdate,finisheddate,duedate,id) VALUES (%s,%s,%s,%s,%s,%s)",
                (item.priority,item.name,item.createdate,None,None,item.id))
        
    item.timestamp = self.TimeStamper(host, cursor, table, item.id)
    
    #tracking new item for syncing will happen in Edit Name

    LCtrl.InsertImageStringItem(0,"1", LCtrl.idx1)
    LCtrl.SetStringItem(0,1,item.name)

    if Properties['LCdate'] == 'timestamp':
        LCtrl.SetStringItem(0, self.attr2col_num['date'], item.timestamp.Format("%m/%d %H:%M:%S"))
    elif Properties['LCdate'] == 'createdate':
        LCtrl.SetStringItem(0, self.attr2col_num['date'], item.createdate.Format('%m/%d/%y'))

    self.curIdx = 0
    
    #if Display is being filtered we assume that is the owner of the new node
    owner = Properties['owner']	
    if owner and owner!='*ALL':
        self.ListCtrls[L].SetStringItem(0, self.attr2col_num['owners'], owner)
        item.owners = [owner]
        
        self.owners.Clear()
        self.owners.AppendText(owner)
        
        cursor.execute("UPDATE "+table+" SET owner1 = %s WHERE id = %s", (owner,item.id))
        item.timestamp = self.TimeStamper(host, cursor, table, item.id)  #not really necessary since just got a timestamp
    
    # decided that it was actually better not to ask for the owner on a new node	
    #else:
        #self.OnEditOwner()
    
    LCtrl.SetFocus() #needed for the in place editor to look right
    LCtrl.SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
    
    self.OnDisplayInPlaceEditor() #(new=True)</t>
<t tx="szatz.112203232245.64">self.name.Clear()
self.owners.Clear()
self.note.Clear()</t>
<t tx="szatz.112203232245.65">@ Need to decide if we are going to have timestamp checking to be sure something hasn't changed
Note that there would not need to be timestamp checking on a new node
Also  there is no need to timestamp check on a local DB
The following code seems to work fine, however, I have just commented out the calls to it in NameEditor methods
@c
def Conflict(self, host, cursor, table, item):
    if host is 'sqlite':
        return False
    cursor.execute("Select timestamp from "+table+" WHERE id = %s", (item.id,))
    db_timestamp = cursor.fetchone()[0]
    if db_timestamp != item.timestamp:
        print "There is a conflict and you should refresh display"
        return True
    else:
        return False
</t>
<t tx="szatz.112203232245.66"></t>
<t tx="szatz.112203232245.67">def OnNewList(self, event=None):
    if self.modified:
        self.OnUpdate()
    
    if OFFLINE_ONLY is True or REMOTE_HOST is None:
        hosts = [LOCAL_HOST]
    else:
        hosts = [LOCAL_HOST, REMOTE_HOST]
        
    dlg = wxSingleChoiceDialog(self, 'Databases', 'Choose a database:', hosts, wxCHOICEDLG_STYLE)
    val = dlg.ShowModal()
    dlg.Destroy()
    if val == wxID_OK:
        host = dlg.GetStringSelection()
    else:
        return
        
    cursor = self.GetCursor(host)
    if cursor is None:
        return
        
    dlg = wxTextEntryDialog(self, 'What is the name of the new table?', 'Create Table')
    val = dlg.ShowModal()
    dlg.Destroy()
    if val == wxID_OK:
        table = dlg.GetValue()
    else:
        return
    
    if not table:
        return
        
    location, rdbms = host.split(':')
    
    if rdbms == 'sqlite':
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
    else:
        cursor.execute("SHOW tables")
    
    if (table,) in cursor.fetchall():
        msg = wxMessageDialog(self,
                              "Table '%s' already exists"%table,
                              "Duplicate Table",
                              wxICON_ERROR|wxOK)
        msg.ShowModal()
        msg.Destroy()
        return
        
    dlg = wxMessageDialog(self,
          "Are you sure you want to create Table '%s'?"%table,
          "Create Table?",
          wxICON_QUESTION|wxYES_NO)

    if dlg.ShowModal() == wxID_YES:
        self.CreateTable(host,table)
        self.CreateNewNotebookPage(host,table)

        #self.AddListControl(tab_title) #add listcontrol displays the list
        
        #self.OnNewItem()
        
    dlg.Destroy()


</t>
<t tx="szatz.112203232245.68">def OnFileList(self, evt=None, path=None):
    if self.modified:
        self.OnUpdate()
        
    #if there is no event, we got here through the start up loading of lists
    if evt:
        fileNum = evt.GetId() - wxID_FILE1			
        path = self.filehistory.GetHistoryFile(fileNum)
        location, rdbms, table = path.split(':')
        host = '%s:%s'%(location, rdbms)
        # only need to check if table is open if this is not at startup
        if table in [p['table'] for p in self.PropertyDicts if p['host'] == host]:
            dlg = wxMessageDialog(self,"%s (%s) is already open!"%(table,host),"List Open",wxICON_ERROR|wxOK)
            dlg.ShowModal()
            dlg.Destroy()
            return
        
    else:
        location, rdbms, table = path.split(':')
        host = '%s:%s'%(location, rdbms)
    
    cursor = self.GetCursor(host)
    if cursor is None:
        return
        
    if rdbms == 'sqlite':
        sql = "SELECT name FROM sqlite_master WHERE name = '%s'"%table
    else:
        sql = "SHOW TABLES LIKE '%s'"%table
    
    cursor.execute(sql)
    if not cursor.fetchall():
        dlg = wxMessageDialog(self,
                    "Table '%s' at host '%s' does not appear to exist!"%(table,host),
                    "Table does not exist",
                    wxICON_ERROR|wxOK)
        dlg.ShowModal()
        dlg.Destroy()
        return
        
    self.CreateNewNotebookPage(host,table)

</t>
<t tx="szatz.112203232245.69">def OnOpenList(self, evt=None):
    if self.modified:
        self.OnUpdate()
        
    tree = {}
    
    if OFFLINE_ONLY is True or REMOTE_HOST is None:
        hosts = [LOCAL_HOST]
    else:
        hosts = [LOCAL_HOST, REMOTE_HOST]
        
    for host in hosts:
        cursor = self.GetCursor(host)
        if cursor:
            if host.split(':')[1] == 'sqlite':
                sql = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
            else:
                sql = "SHOW TABLES" #sorted
    
            cursor.execute(sql)
            results = cursor.fetchall()
    
            #excluding already open tables + 'system' tables
            excluded_tables = [p['table'] for p in self.PropertyDicts if p['host'] == host]
            excluded_tables.extend(['user_sync','sync','owners'])
    
            tables = [t for (t,) in results if t not in excluded_tables]
    
            tree[host] = tables

    dlg = TreeDialog(self, "Open List", tree=tree)
    val = dlg.ShowModal()
    dlg.Destroy()
    if val == wxID_OK:
        sel = dlg.TreeCtrl.GetSelection()
        table = dlg.TreeCtrl.GetItemText(sel)
        sel = dlg.TreeCtrl.GetItemParent(sel)
        host = dlg.TreeCtrl.GetItemText(sel)
        
        if host in hosts: #takes care of highlighting root or hosts
            self.CreateNewNotebookPage(host,table)
</t>
<t tx="szatz.112203232245.70">def OnDeleteList(self, evt=None):
    #ini controls whether the menu item is enabled
    Properties = self.PropertyDicts[self.L]
    host = Properties['host']
    table = Properties['table']
        
    #if table is in SYNC_TABLES, should we make a point of that?
    dlg = wxMessageDialog(self,
                        "Are you sure that you want to delete table %s (%s)?\n(Please note that you cannot recover it once it is deleted!)"%(table,host),
                        "Delete Table...",
                        wxICON_EXCLAMATION|wxYES_NO|wxNO_DEFAULT)
    
    val = dlg.ShowModal()
    dlg.Destroy()
    if val == wxID_NO:
        return
        
    rdbms = host.split(':')[1]
    
    if rdbms == 'mysql':
        dlg = wxMessageDialog(self,
                        "Are you sure really really sure you want to delete table %s (%s)?\n(You really really cannot recover it once it is deleted)"%(table,host),
                        "Delete Table...",
                        wxICON_EXCLAMATION|wxYES_NO|wxNO_DEFAULT)
                        
        val = dlg.ShowModal()
        dlg.Destroy()
        if val == wxID_NO:
            return

    cursor = self.Cursors[host]
    cursor.execute("DROP TABLE %s"%table)
    
    self.OnCloseList()

</t>
<t tx="szatz.112203232245.71">def OnCloseList(self, evt=None):
    if self.modified:
        self.OnUpdate()
        
    L = self.L
            
    del self.ItemLists[L]
    del self.PropertyDicts[L]
    del self.ListCtrls[L]
    del self.OwnerLBoxes[L]

    self.nb.DeletePage(L)        

    ln = len(self.PropertyDicts)
    if ln:
        self.nb.SetSelection(0)
        self.L = 0
    else:
        self.L = -1




</t>
<t tx="szatz.112203232245.72">def OnCloseAll(self, evt=None):
    if self.modified:
        self.OnUpdate()
        
    while self.L != -1:
        self.OnCloseList()
        
    self.name.Clear()
    self.owners.Clear()
    self.note.Clear()
    #note that Clearing does set self.modified (eg {'name':1})
    self.modified = {}
    
</t>
<t tx="szatz.112203232245.73">def OnSaveAsText(self, evt=None):
    if self.modified:
        self.OnUpdate()
        
    Properties = self.PropertyDicts[self.L]
    wildcard = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
    #dlg = wxFileDialog(self, "Save file", "", Properties['table'], wildcard, wxSAVE|wxOVERWRITE_PROMPT|wxCHANGE_DIR)
        
    body = ""
    for i,item in enumerate(self.ItemLists[self.L]):
        body = body+"%d. %s (%d)\n"%(i+1, item.name, item.priority)
    
    table = Properties['table']
    location, rdbms = Properties['host'].split(':')
    filename = re.sub('[\\/:*"&lt;&gt;|\?]','-','%s-%s-%s'%(location,rdbms,table)) 
    filename = filename[:50]+'.txt'

    path = os.path.join(DIRECTORY,filename)
    
    f = file(path,'w')
    f.write(body)
    f.close()

    os.startfile(path)

    self.SetStatusText("Saved file %s"%path)
    
</t>
<t tx="szatz.112203232245.74">def OnArchive(self, evt=None):
    if self.modified:
        self.OnUpdate()
        
    Properties = self.PropertyDicts[self.L]
    host = Properties['host']
    cursor = self.Cursors[host]
    table = Properties['table']
    rdbms = host.split(':')[1]
        
    table_archive = table+'_archive'
    
    #need to test for existence of table_archive
    if rdbms == 'sqlite':
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
    else:
        cursor.execute("SHOW tables")

    results = cursor.fetchall()
    
    if (table_archive,) not in results:
        dlg = wxMessageDialog(self,
                    "Do you want to create an archive for table %s (%s)"%(table,rdbms),
                    "Create an archive...",
                    wxICON_QUESTION|wxYES_NO)
        val = dlg.ShowModal()
        dlg.Destroy()
        if val==wxID_YES:
            self.CreateTable(host,table_archive)
        else:
            return
    
    label1 = "In table %s (%s) \narchive all finished items older than:"%(table,rdbms)
    label2 = "Archive all finished items"
    dlg = FinishedDialog(self, "Archive completed items", days=7, spin_label=label1, check_label=label2)
    
    val = dlg.ShowModal()
    dlg.Destroy() #dialogs and frames not destroyed right away to allow processing events, methods
    if val==wxID_CANCEL:
        return
        
    if dlg.check.GetValue():
        cursor.execute("SELECT id,priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,note FROM "+table+" WHERE finisheddate IS NOT NULL")
    else:
        days = dlg.text.GetValue()
        date = mx.DateTime.today() - int(days)
        cursor.execute("SELECT id,priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,note FROM "+table+" WHERE finisheddate &lt; %s",(date,))

    results = cursor.fetchall()
    dlg = wxMessageDialog(self,
                        "Archiving will remove %d records from %s.\nDo you want to proceed?"%(len(results),table),
                        "Proceed to archive...",
                        wxICON_QUESTION|wxYES_NO)
    
    val = dlg.ShowModal()
    dlg.Destroy()
    if val == wxID_NO:
        return

    if table in SYNC_TABLES:
        if rdbms == 'sqlite':
            def track_deletes():
                timestamp = mx.DateTime.now()
                cursor.execute("INSERT INTO sync (id,action,table_name,name,timestamp) VALUES (%s,%s,%s,%s,%s)",(id,'d',table,name,timestamp))
        else:
            def track_deletes():
                cursor.execute("INSERT INTO sync (id,action,table_name,user,name) VALUES (%s,%s,%s,%s,%s)",(id,'d',table,USER,name))
    else:
        def track_deletes():
            pass	

    for row in results:
        # the next line is necessary because pysqlite returns a tuple-like object that is not a tuple
        r = tuple(row)
        id = r[0]
        name = r[2]
        cursor.execute("INSERT INTO "+table_archive+"  (id,priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,note) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",r)
        timestamp = self.TimeStamper(host, cursor, table_archive, id)
        cursor.execute("DELETE from "+table+" WHERE id = %s", (id,))
        track_deletes()
        
    self.OnRefresh()
    dlg = wxMessageDialog(self,
                        "Table %s had items older than %s days successfully archived"%(table,days),
                        "Archiving successful...",
                        wxICON_INFORMATION|wxOK)
    dlg.ShowModal()
</t>
<t tx="szatz.112203232245.75">def OnSync(self, evt=None):
    if self.modified:
        self.OnUpdate()
    #Note that the results of an sqlite query are an instance that you need to turn into a tuple or MySQL gets unhappy

    if OFFLINE_ONLY:
        dlg = wxMessageDialog(self, "You need to be online to synchronize!", style = wxOK|wxICON_ERROR)
        dlg.ShowModal()
        dlg.Destroy()
        return
        
    dlg = wxMessageDialog(self,"Synchronize Table(s): "+" and ".join(SYNC_TABLES),"Synchronize...",wxICON_QUESTION|wxYES_NO)
    val = dlg.ShowModal()
    dlg.Destroy()
    if val == wxID_NO:
        return
    
    if REMOTE_HOST is None:
        print "There doesn't appear to be a Remote Server"
        return

    if LOCAL_HOST is None:
        print "There doesn't appear to be a Local Server"
        return
        
    print "LOCAL_HOST=",LOCAL_HOST
    print "REMOTE_HOST=",REMOTE_HOST

    r_cursor = self.GetCursor(REMOTE_HOST)
    if r_cursor is None:
        print "Couldn't get a cursor for %s"%REMOTE_HOST
        return

    l_cursor = self.GetCursor(LOCAL_HOST)
    if l_cursor is None:
        print "Couldn't get a cursor for %s"%LOCAL_HOST
        return

    # moving the sync time back a second to make sure that we don't lose track of any nodes
    #that are being updated or inserted at the same time as we are syncing
    r_cursor.execute("SELECT NOW()")
    l_now = mx.DateTime.now()-mx.DateTime.oneSecond
    r_now = r_cursor.fetchone()[0]-mx.DateTime.oneSecond
    #because of some inconsistent rounding appears necessary to make sure the sqlite timestamp is less than l_now
    #having seen same issue for mysql but for consistency (and because sqlite could also be "server" rdbms
    l_ts = l_now - mx.DateTime.DateTimeDelta(0,0,0,0.02)
    r_ts = r_now - mx.DateTime.DateTimeDelta(0,0,0,0.02)
    print "l_now=",l_now, "l_ts =",l_ts
    print "r_now=",r_now, "r_ts=",r_ts

    r_cursor.execute("SELECT MAX(last_sync) FROM user_sync WHERE user = %s", (USER,))
    r_last_sync = r_cursor.fetchone()[0]
    print "last sync (remote time) =",r_last_sync

    l_cursor.execute("SELECT MAX(last_sync) FROM user_sync")
    l_last_sync = l_cursor.fetchone()[0] #note MAX returns a string with sqlite so we turn it make into DateTime
    l_last_sync = mx.DateTime.DateTimeFrom(l_last_sync)
    print "last sync (local time) =",l_last_sync

    for table in SYNC_TABLES:
        # Need to pick up changes for both so syncing one doesn't add new things and screw up the second sync
        print "Checking "+table+" on the Remote Server; changes (excluding deletes) are:"
        r_cursor.execute("SELECT id,createdate from "+table+" WHERE timestamp &gt; %s AND timestamp &lt;= %s",(r_last_sync,r_now)) 
        r_results = r_cursor.fetchall()
        print "Server changes (excluding deletes)"
        print r_results
        
        print "Checking "+table+" on Local; changes (excluding deletes) are:"
        l_cursor.execute("SELECT id,createdate from "+table+" WHERE timestamp &gt; %s AND timestamp &lt;= %s",(l_last_sync,l_now))
        l_results = l_cursor.fetchall()
        print "Local changes (excluding deletes)"
        print l_results

        for id, createdate in r_results:
            r_cursor.execute("SELECT priority,name,owner1,owner2,owner3,createdate,finisheddate,duedate,note,id FROM "+table+" WHERE ID = %s",(id,))
            row = r_cursor.fetchone()
            if row:
                if createdate &gt; r_last_sync:
                    l_cursor.execute("INSERT INTO "+table+" (priority,name,owner1,owner2,owner3,createdate,finisheddate,duedate,note,id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)", row) #*row also works
                    print "Created %s in %s on Local"%(id,table)
                else:
                    l_cursor.execute("UPDATE "+table+" SET priority = %s, name =%s, owner1 = %s, owner2 = %s, owner3 = %s, createdate = %s, finisheddate = %s, duedate = %s, note = %s WHERE id = %s", row)
                    print "Updated %s in %s on Local"%(id,table)
                # for reasons I don't understand l_now here is a 1/100 ahead of l_now when inserted into user_sync
                l_cursor.execute("UPDATE "+table+" SET timestamp = %s WHERE id = %s", (l_ts,id))
        
        for id, createdate in l_results:
            l_cursor.execute("SELECT priority,name,owner1,owner2,owner3,createdate,finisheddate,duedate,note,id FROM "+table+" WHERE ID = %s",(id,))
            row = l_cursor.fetchone()
            if row:
                row = tuple(row)
                #above needed because sqlite returns an enhanced tuple-like object that is not a tuple
                if createdate &gt; l_last_sync:
                    r_cursor.execute("INSERT INTO "+table+" (priority,name,owner1,owner2,owner3,createdate,finisheddate,duedate,note,id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)", row)
                    print "Created %s in %s on Server"%(id,table)
                else:
                    r_cursor.execute("UPDATE "+table+" SET priority = %s, name =%s, owner1 = %s, owner2 = %s, owner3 = %s, createdate = %s, finisheddate = %s, duedate = %s, note = %s WHERE id = %s", row)
                    print "Updated %s in %s on Server"%(id,table)
                r_cursor.execute("UPDATE "+table+" SET timestamp = %s WHERE id = %s", (r_ts,id))
    
    #Handle the deletes; Note if at some point only 'd's are being written won't have to check for 'd'
    r_cursor.execute("SELECT id,table_name FROM sync WHERE timestamp &gt; %s AND timestamp &lt;= %s AND action = 'd'",(r_last_sync,r_now))
    r_results = r_cursor.fetchall()

    l_cursor.execute("SELECT id,table_name FROM sync WHERE timestamp &gt; %s AND timestamp &lt;= %s AND action = 'd'",(l_last_sync,l_now))
    l_results = l_cursor.fetchall()

    for id,table in l_results:
        r_cursor.execute("DELETE from "+table+" WHERE id = %s", (id,))
        print "Deleted %s from %s on Server (if it existed there)"%(id,table)

    for id,table in r_results:
        l_cursor.execute("DELETE from "+table+" WHERE id = %s", (id,))
        print "Deleted %s from %s on Local (if it existed there)"%(id,table)	
    #End of deletes code
    
    #update the user_sync database with the latest sync times
    l_cursor.execute("INSERT INTO user_sync (user,last_sync) VALUES (%s,%s)", (USER,l_now)) #don't really need USER for local
    r_cursor.execute("INSERT INTO user_sync (user,last_sync) VALUES (%s,%s)", (USER,r_now)) 
    
    print "Synchronization completed"

</t>
<t tx="szatz.112203232245.76"></t>
<t tx="szatz.112203232245.77">def OnItemSelected(self, evt=None):
    if self.modified:
        self.OnUpdate()

    if evt:
        idx = evt.GetIndex()
    elif self.curIdx != -1:
        idx = self.curIdx
    else: # really to catch self.curIdx = -1 (see OnDelete and OnRefresh)
        self.name.Clear() # could be moved out of if
        self.owners.Clear() # could be moved out of if
        self.note.Clear()
        #note that Clearing does set self.modified (eg {'name':1})
        self.modified = {}
        return
    
    L = self.L
    item = self.ItemLists[L][idx]

    self.name.Clear()
    self.name.AppendText(item.name) #SetValue(item.name) - if you use setvalue you don't get the font
        
    self.owners.Clear()
    self.owners.AppendText('; '.join(item.owners))
    
    note = self.GetNote(L,item)
    if note.find("&lt;leo_file&gt;") != -1:
        self.note.SetValue("Leo Outline")
        self.note.SetEditable(False)
    else:
        self.note.SetValue(note)
        self.note.SetEditable(True)
        
    self.ListCtrls[L].EnsureVisible(idx)
    self.curIdx = idx
    
    #writing to text widgets caused wxEVT_COMMAND_TEXT_UPDATED which is caught by EVT_TEXT, which updates self.modified
    self.modified={}

</t>
<t tx="szatz.112203232245.78">def OnItemActivated(self,evt):
    print "On Activated"
    
</t>
<t tx="szatz.112203232245.79">def OnShowAll(self, evt=None):
    L = self.L
    OLBox = self.OwnerLBoxes[L]
    
    Properties = self.PropertyDicts[L]
    Properties['showfinished'] = -1
    Properties['owner'] = '*ALL'
    
    OLBox.SetStringSelection('*ALL')
    
    self.OnRefresh()
</t>
<t tx="szatz.112203232245.80">def OnRefresh(self, evt=None):
    #OnItemSelected should be able to handle no items so this could be very short
    L = self.L
    
    results = self.ReadFromDB()
    self.ItemLists[L] = self.CreateAndDisplayList(results)

    if self.ItemLists[L]:
        self.ListCtrls[L].SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
        self.curIdx = 0
    else:
        self.curIdx = -1		
        
    self.OnItemSelected()
</t>
<t tx="szatz.112203232245.81">def OnFilterOwners(self, evt=None):
    if self.modified:
        self.OnUpdate()
    sel = self.OwnerLBoxes[self.L].GetStringSelection()
    
    if sel:
        self.PropertyDicts[self.L]['owner'] = sel
        self.OnRefresh()
</t>
<t tx="szatz.112203232245.82">def OnColumnClick(self, evt):
    col_num = evt.GetColumn()
    L = self.L
    LCtrl = self.ListCtrls[L]
    Sort = self.PropertyDicts[L]['sort']
    attr2col = self.attr2col_num
    
    prev_sort_attr = Sort.get('attribute') #if this is the first sort Properties['sort'] is {}
    
    #following is a little bit ugly but gets the key from the value, which is col_num
    Sort['attribute'] = attr2col.keys()[attr2col.values().index(col_num)]
    
    if prev_sort_attr == Sort['attribute']:
        Sort['direction'] = not Sort['direction']
    else:
        Sort['direction'] = 0
    
    self.OnRefresh()

    LCtrl.ClearColumnImage(attr2col['priority'])
    LCtrl.ClearColumnImage(attr2col['date'])
    img_num = LCtrl.arrows[Sort['direction']]
    LCtrl.SetColumnImage(col_num, img_num)
    
</t>
<t tx="szatz.112203232245.83">def OnShowFinished(self,evt):
    Properties = self.PropertyDicts[self.L]
    label1 = "Enter the number of days to retain\ncompleted tasks in the display:"
    label2 = "Show all finished items"
    dlg = FinishedDialog(self, "Display of completed items", days=Properties['showfinished'], spin_label=label1, check_label=label2)
    if dlg.ShowModal()==wxID_OK:
        if dlg.check.GetValue():
            Properties['showfinished'] = -1
        else:
            days = dlg.text.GetValue()
            Properties['showfinished'] = int(days)			
        self.OnRefresh()
    dlg.Destroy()
    
</t>
<t tx="szatz.112203232245.84">def OnColumnRightClick(self, evt=None):
    col = evt.GetColumn()
    if col != self.attr2col_num['date']:
        return
        
    L = self.L
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    
    #x,y = evt.GetPosition()
    datemenu = wxMenu()
    
    for i,date in enumerate(['Create Date','Last Modified','Due Date','Completion Date']):
        datemenu.Append(200+i, date)
        EVT_MENU(self, 200+i, lambda e, i=i: self.ChangeDateDisplayed(e,i))

    x = LCtrl.GetColumnWidth(1)+ LCtrl.GetColumnWidth(2) + LCtrl.GetColumnWidth(3)
    self.PopupMenu(datemenu,(x,40))
    datemenu.Destroy()


</t>
<t tx="szatz.112203232245.85">def OnDisplayDateCategory(self, evt=None):
    dlg = wxSingleChoiceDialog(self, 'Date Display', 'Choose a date to display:',
                    ['Create Date','Last Modified','Due Date','Completion Date']
                    , wxOK|wxCANCEL)
    val = dlg.ShowModal()
    dlg.Destroy()
    
    if val == wxID_OK:
        idx = dlg.GetSelection()
        self.ChangeDateDisplayed(i=idx)
        
</t>
<t tx="szatz.112203232245.86">def ChangeDateDisplayed(self, evt=None, i=0):
    L = self.L
    LCtrl = self.ListCtrls[L]
    self.PropertyDicts[L]['LCdate'] = displaydate = ('createdate','timestamp','duedate','finisheddate')[i]	
    col_num = self.attr2col_num['date']
    col_info = LCtrl.GetColumn(col_num)
    col_info.SetText(self.date_titles[displaydate])
    LCtrl.SetColumn(col_num,col_info)
    self.DisplayList(self.ItemLists[L])
    #self.OnRefresh() #have gone back and forth but think that it should be self.DisplayList
</t>
<t tx="szatz.112203232245.87"></t>
<t tx="szatz.112203232245.88">def OnPageSetup(self, evt):
    #need to pass printdata to tableprint

    psdata = wxPageSetupDialogData()

    # if want to vary margins will need to save them as ivars and then set
    #psdata.SetMarginTopLeft((self.Left,self.Top))
    psdata.EnableMargins(False)
    psdata.SetPrintData(self.printdata) #gets Paper Orientation and PaperId info from printdata
    
    dlg = wxPageSetupDialog(self, psdata)
    if dlg.ShowModal() == wxID_OK:
        self.printdata = dlg.GetPageSetupData().GetPrintData()
        dlg.Destroy()
</t>
<t tx="szatz.112203232245.89">def OnPrint(self, evt=None, prev=False, showprtdlg=True): 		#???self.psdata = psdata
    IList = self.ItemLists[self.L]
    Properties = self.PropertyDicts[self.L]
    
    prt = PrintTable(self.printdata) #self.printdata is the wxPrintData object with Orientation Info

    font_name = prt.default_font_name
    prt.text_font = {'Name':font_name, 'Size':11, 'Colour':[0, 0, 0], 'Attr':[0, 0, 0]}
    prt.label_font = {'Name':font_name, 'Size':12, 'Colour':[0, 0, 0], 'Attr':[1, 0, 0]}
    prt.header_font = {'Name':font_name, 'Size':14, 'Colour':[0, 0, 0], 'Attr':[1, 0, 0]}
    
    prt.row_def_line_colour = wxLIGHT_GREY
    prt.column_def_line_colour = wxLIGHT_GREY
    
    prt.left_margin = 0.5

    data = []
    for row,item in enumerate(IList):	
        data.append([str(item.priority),
                    item.name,
                    item.duedate and item.duedate.Format('%m/%d/%y') or '',
                    '; '.join([x.split(',')[0] for x in item.owners])]) #just last names
                    
        if item.finisheddate:
            prt.SetCellText(row, 0, wxLIGHT_GREY)
            prt.SetCellText(row, 1, wxLIGHT_GREY)
            prt.SetCellText(row, 2, wxLIGHT_GREY)
            prt.SetCellText(row, 3, wxLIGHT_GREY)

    prt.data = data
    prt.label = ['P','Item','Due','Owner']
    
    if self.printdata.GetOrientation() == wxPORTRAIT:
        prt.set_column = [.2, 5, .65, 1]
    else:
        prt.set_column = [.2, 7, .65, 1.5]
                       
    title = "Table: %s   Owner: %s    "%(Properties['table'],Properties['owner'])
    prt.SetHeader(title, type='Date &amp; Time', align=wxALIGN_LEFT, indent = 1.5)
    prt.SetFooter("Page No ", type ="Num")

    if prev:
        prt.Preview()
    else:
        prt.Print(prompt=showprtdlg)
</t>
<t tx="szatz.112203232245.90"></t>
<t tx="szatz.112203232245.91">def OnWindowExit(self, evt):
    #this is called if you close the ListManager Window with the X
    if evt.CanVeto():
        self.OnExit()
    else:
        evt.Skip()
</t>
<t tx="szatz.112203232245.92">def OnExit(self, event=None):   
    &lt;&lt; save configuration file &gt;&gt;
    sys.stderr.dlg.Destroy() #destroys the error dialog; need to do this to shut down correctly
    if self.ModifierDialog: #only reason to check is if closed before ModifierDialog is constructed
        self.ModifierDialog.Destroy()
    self.Close(1)
</t>
<t tx="szatz.112203232245.93">cp.remove_section('Files')
cp.add_section("Files")

x,y = self.GetSizeTuple()

cp.set('Configuration','x', str(x))
cp.set('Configuration','y', str(y))

numfiles = self.filehistory.GetNoHistoryFiles()

for n in range(numfiles):
    cp.set("Files", "path%d"%n, self.filehistory.GetHistoryFile(n))

try:
    #you have to give ConfigParser a writable object
    cfile = file(config_file, 'w')
    cp.write(cfile)
    cfile.close()
except IOError:
    print "The configuration file can't be written!"
    time.sleep(10) #so you can see that there was a problem
</t>
<t tx="szatz.112203232245.94"></t>
<t tx="szatz.112203232245.95">def OnFind(self, evt=None):
    self.FindDialog.Show(True)
    self.FindDialog.FindText.SetSelection(-1,-1)
    self.FindDialog.FindText.SetFocus()


</t>
<t tx="szatz.112203232245.96">def FindString(self, evt=None):
    L = self.L
    Properties = self.PropertyDicts[L]
    cursor = self.Cursors[Properties['host']]
    table = Properties['table']
    
    pat = self.FindDialog.FindText.GetValue()
    likepat = r"'%"+pat+r"%'"
    finished = self.FindDialog.SearchFinished.GetValue()
    notes = self.FindDialog.SearchNotes.GetValue()
    
    if finished:
        WHERE = "WHERE "
    else:
        WHERE = "WHERE finisheddate IS NULL AND "
    
    if notes:
        SELECT = "SELECT priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,id,timestamp,note FROM %s "%table
        WHERE = WHERE + "(name LIKE %s OR note LIKE %s) ORDER BY timestamp DESC"%(likepat,likepat)
    else:
        SELECT = "SELECT priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,id,timestamp FROM %s "%table
        WHERE = WHERE + "name LIKE %s ORDER BY timestamp DESC"%likepat

    sql = SELECT + WHERE			
    try:
        cursor.execute(sql)
    except:
        print "Cannot read %s: %s"%(Properties['host'],table)
        return
    else:
        results = cursor.fetchall()
    
    case = self.FindDialog.MatchCase.GetValue()
    whole = self.FindDialog.MatchWhole.GetValue()
    
    if whole:
        pat = '\\b%s\\b'%pat
    
    if case:
        z = re.compile(pat)
    else:
        z =re.compile(pat, re.I)

    if notes:
        results = [x for x in results if re.search(z,x[1]) or re.search(z,x[10])]
    else:
        results = [x for x in results if re.search(z,x[1])]
    
    Properties['LCdate'] = 'timestamp'
    self.ItemLists[L]= IList = self.CreateAndDisplayList(results)
    
    LCtrl = self.ListCtrls[L]
    col_num = self.attr2col_num['date']
    col_info = LCtrl.GetColumn(col_num)
    col_info.SetText(self.date_titles['timestamp'])
    LCtrl.SetColumn(col_num,col_info)
    
    if IList:
        self.curIdx = 0
        LCtrl.SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
    else:		
        self.curIdx = -1
        
    self.OnItemSelected()
    
    Properties['sort'] = {'direction':0,'attribute':'date'}
    Properties['owner'] = '*ALL'
    
    owner_idx = self.OwnerLBoxes[L].GetSelection()
    if owner_idx != -1:
        self.OwnerLBoxes[L].SetSelection(owner_idx, 0) #get exception if index = -1

    self.SetStatusText("Found %d items"%len(IList))
</t>
<t tx="szatz.112203232245.97">def FindNode(self, item, showfinished=True):
    L = self.L
    LCtrl = self.ListCtrls[L]
    Properties = self.PropertyDicts[L]
    
    Properties['owner'] = '*ALL'
    Properties['showfinished'] = showfinished
    
    self.ItemLists[L] = IList = self.CreateAndDisplayList(self.ReadFromDB())
    
    id = item.id
    idx = -1
    for item in IList:
        idx+=1
        if id == item.id:
            break
    else:
        idx = -1

    if idx != -1:	
        LCtrl.SetItemState(idx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
        LCtrl.EnsureVisible(idx)
    self.curIdx = idx
    
</t>
<t tx="szatz.112203232245.98">def OnEditNote(self, evt=None):
    if self.modified:
        self.OnUpdate()
    
    idx = self.curIdx
    
    if idx == -1:
        return
        
    L = self.L
        
    #if self.editor:
        #machine = None
        #win32pdh.EnumObjects(None, machine, 0, 1) # resets Enum otherwise it seems to hold onto old data
        #object = "Process"
        #items, instances = win32pdh.EnumObjectItems(None,None,"Process", -1)
        #if 'TextPad' in instances:
            #print "TextPad is running"
        #else:
            #self.editor = {}
    
    item = self.ItemLists[L][idx]
    file_name = re.sub('[\\/:*"&lt;&gt;|\?]','-',item.name) #make sure all chars are legal file name characters
    
    path = os.path.join(os.environ['TMP'],file_name[:50])+'.%s'%NOTE_EXT
        
    f = file(path,'w')
    f.write(self.GetNote())
    f.close()
    
    os.startfile(path)
    
    id = item.id
    for d in self.editor:
        if d['id'] == id:
            return

    ed = {}
    ed['time'] = os.path.getmtime(path)
    ed['host'] = self.PropertyDicts[L]['host']
    ed['table'] = self.PropertyDicts[L]['table']
    ed['path'] = path
    ed['id'] = item.id
    
    self.editor.append(ed)
    
    time.sleep(.1)
</t>
<t tx="szatz.112203232245.99"></t>
<t tx="szatz.112203232245.100">def GetCursor(self, host):
    cursor = self.Cursors.get(host)
    if cursor:
        return cursor
        
    location, rdbms = host.split(':')
        
    if rdbms == 'sqlite':
        db = os.path.join(DIRECTORY,location,DB)
        try:
            Con = sqlite.connect(db=db, autocommit=1)
            cursor = Con.cursor()
            self.sqlite_connections.append(Con)  #getting a weak reference error from PySQLite and this makes it go away
        except:
            dlg = wxMessageDialog(self,
                    "Could not connect to SQLite database at %s"%location,
                    "Connection problem!",
                    wxICON_ERROR|wxOK)
            dlg.ShowModal()
            dlg.Destroy()
            cursor = None
        
    elif not OFFLINE_ONLY:
        try:
            Con = MySQLdb.connect(host=location, user=USER, passwd=PW, db=DB)
            cursor = Con.cursor()
        except:
            dlg = wxMessageDialog(self,
                    "host = %s | user = %s | password = %s**** | db = %s - could not connect!"%(host,USER,PW[:3],DB),
                    "Connection problem",
                    wxICON_ERROR|wxOK)
            dlg.ShowModal()
            dlg.Destroy()
            cursor = None
            
    if cursor:
        self.Cursors[host] = cursor
        
    return cursor


</t>
<t tx="szatz.112203232245.101">def GetNote(self, L=None, item=None):
    if L is None:
        L = self.L
        
    if item is None:
        if self.curIdx != -1:
            item = self.ItemLists[L][self.curIdx]
        else:
            return ''
        
    Properties = self.PropertyDicts[L]
    
    cursor = self.Cursors[Properties['host']]
    table = Properties['table']
    cursor.execute("SELECT note from "+table+" WHERE id = %s", (item.id,))
    
    ###### Debug -- this does happen where note brings back None 053003
    z = cursor.fetchone()
    if z is None:
        print "In GetNote -&gt; SELECT should not bring back None"
        print "           -&gt; item.id=",item.id
        z = (None,)
    note = z[0]
    if note is None:
        note = ''
    return note
    
</t>
<t tx="szatz.112203232245.102">def CreateTable(self, host, table):
    cursor = self.Cursors[host]
    rdbms = host.split(':')[1]
    if rdbms == 'sqlite':
        sql = """CREATE TABLE '%s' ('id' varchar(36) PRIMARY KEY,
'priority' int(1),
'name' varchar(150),
'createdate' datetime,
'finisheddate' date,
'duedate' date,
'owner1' varchar(25),
'owner2' varchar(25),
'owner3' varchar(25),
'note' text,
'timestamp' timestamp(14))"""%table
    else:
        sql = """CREATE TABLE `%s` (`id` varchar(36) NOT NULL default '',
`priority` int(1) NOT NULL default '1',
`name` varchar(150) NOT NULL default '',
`createdate` datetime NOT NULL default '0000-00-00 00:00:00',
`finisheddate` date default '0000-00-00',
`duedate` date default '0000-00-00',
`owner1` varchar(25) default '',
`owner2` varchar(25) default '',
`owner3` varchar(25) default '',
`note` text,
`timestamp` timestamp(14) NOT NULL,PRIMARY KEY  (`id`)) TYPE=MyISAM"""%table
        
    cursor.execute(sql)
</t>
<t tx="szatz.112203232245.103">def ReadFromDB(self):
    L = self.L
    Properties = self.PropertyDicts[L]
    
    host = Properties['host']
    cursor = self.GetCursor(host)
    if cursor is None:
        return None
        
    table = Properties['table']
    
    owner = Properties['owner']
    if owner == '*ALL':
        WHERE = ""
    else:
        WHERE = 'WHERE (owner1 = "%s" OR owner2 = "%s" OR owner3 = "%s")'%(owner,owner,owner)
    
    #-1 show them all; 0 show none; integer show for that many days
    days = Properties['showfinished']	
    if days != -1:
        if days:
            date = mx.DateTime.now() - days
            t = "(finisheddate IS NULL OR finisheddate &gt; '%s')"%date
        else:
            t = "finisheddate IS NULL"
        
        if WHERE:
            WHERE = "%s AND %s"%(WHERE,t)
        else:
            WHERE = " WHERE %s"%t

    Sort = Properties['sort']
    if Sort:
        sort_attr = Sort['attribute']
        if sort_attr == 'date':
            sort_attr = Properties['LCdate']
        elif sort_attr == 'owners':
            sort_attr = 'owner1'
        
        WHERE = WHERE + " ORDER BY " + sort_attr
        #if not direction: WHERE = WHERE + " DESC"   works because ASC is the default
        if not Sort['direction']:
            WHERE = WHERE + " DESC" 

    sql = "SELECT priority,name,createdate,finisheddate,duedate,owner1,owner2,owner3,id,timestamp FROM %s %s"%(table,WHERE)
            
    try:
        cursor.execute(sql)
    except:
        print "Cannot read %s: %s"%(Properties['host'],table)
        return None #[]
    else:
        return cursor.fetchall()
        


</t>
<t tx="szatz.112203232245.104">def CreateAndDisplayList(self, results):
    LCtrl = self.ListCtrls[self.L]
    LCdate = self.PropertyDicts[self.L]['LCdate']
    if LCdate == 'timestamp':
        format = '%m/%d %H:%M:%S'
    else:
        format = '%m/%d/%y'
    itemlist = []

    LCtrl.DeleteAllItems()
    class Item: pass
    
    for x,row in enumerate(results):
        
        item = Item()
        &lt;&lt; assign item attributes &gt;&gt;
        itemlist.append(item)
        
        &lt;&lt; draw item &gt;&gt;

    return itemlist


</t>
<t tx="szatz.112203232245.105">item.priority = int(row[0]) #int(row[0]) needs int because it seems to come back as a long from MySQL
item.name = row[1]
item.createdate = row[2]
item.finisheddate = row[3]
item.duedate = row[4]
item.owners = [y for y in row[5:8] if y] #if you carry around ['tom',None,None] Note this is 5:8 not 5:7
item.id = row[8]
item.timestamp = row[9]

</t>
<t tx="szatz.112203232245.106">LCtrl.InsertImageStringItem(x, str(item.priority), LCtrl.idx1)
LCtrl.SetStringItem(x,1,item.name)
LCtrl.SetStringItem(x,2,'; '.join(item.owners))
date = item.__dict__[LCdate]
LCtrl.SetStringItem(x,3,date and date.Format(format) or "")

if item.finisheddate:
    LC_Item = LCtrl.GetItem(x)
    LC_Item.SetImage(LCtrl.idx0) #might just want generic number or greyed one two three
    LC_Item.SetTextColour(wxLIGHT_GREY)
    LCtrl.SetItem(LC_Item)
    
elif item.priority==2:
    LC_Item = LCtrl.GetItem(x)
    f = self.LC_font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) #resetting weight
    LCtrl.SetItem(LC_Item)

elif item.priority==3:
    LC_Item = LCtrl.GetItem(x)
    f = self.LC_font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) #return to normal
    LC_Item.SetTextColour(wxRED)
    LCtrl.SetItem(LC_Item)</t>
<t tx="szatz.112203232245.107">def DisplayList(self, List, L=None):
    #OnPasteItems needs to be able to have an L that is not self.L
    if L is None:
        L = self.L
    LCtrl = self.ListCtrls[L]
    LCdate = self.PropertyDicts[L]['LCdate']
    if LCdate == 'timestamp':
        format = '%m/%d %H:%M:%S'
    else:
        format = '%m/%d/%y'
    LCtrl.DeleteAllItems()
    
    for x,item in enumerate(List):
        &lt;&lt; draw item &gt;&gt;
        

</t>
<t tx="szatz.112203232245.108">LCtrl.InsertImageStringItem(x, str(item.priority), LCtrl.idx1)
LCtrl.SetStringItem(x,1,item.name)
LCtrl.SetStringItem(x,2,'; '.join(item.owners))
date = item.__dict__[LCdate]
LCtrl.SetStringItem(x,3,date and date.Format(format) or "")

if item.finisheddate:
    LC_Item = LCtrl.GetItem(x)
    LC_Item.SetImage(LCtrl.idx0) #might just want generic number or greyed one two three
    LC_Item.SetTextColour(wxLIGHT_GREY)
    LCtrl.SetItem(LC_Item)
    
elif item.priority==2:
    LC_Item = LCtrl.GetItem(x)
    f = self.LC_font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) #resetting weight
    LCtrl.SetItem(LC_Item)

elif item.priority==3:
    LC_Item = LCtrl.GetItem(x)
    f = self.LC_font
    f.SetWeight(wxBOLD)
    LC_Item.SetFont(f)
    f.SetWeight(wxNORMAL) #return to normal
    LC_Item.SetTextColour(wxRED)
    LCtrl.SetItem(LC_Item)</t>
<t tx="szatz.112203232245.109">def TimeStamper(self, host, cursor, table, id):
    #note that you can insert a timestamp value into an mysql timestamp field
    if host.split(':')[1] == 'sqlite': #host -&gt; location:rdbms
        timestamp = mx.DateTime.now()
        cursor.execute("UPDATE "+table+" SET timestamp = %s WHERE id = %s", (timestamp,id))
    else:
        cursor.execute("Select timestamp from "+table+" WHERE id = %s", (id,))
        timestamp = cursor.fetchone()[0]
        
    return timestamp
</t>
<t tx="szatz.112203232245.110"></t>
<t tx="szatz.112203232245.111">def OnShowEvaluate(self, evt=None):
    
    self.EvalDialog.Show(True)
    self.EvalDialog.EvalText.SetSelection(-1,-1)
    self.EvalDialog.EvalText.SetFocus()
    
</t>
<t tx="szatz.112203232245.112">def OnEvaluate(self, evt=None):
    expr = self.EvalDialog.EvalText.GetValue()
    print "%s =&gt; "%expr,
    print eval(expr)
    
</t>
<t tx="szatz.112203232245.113">def GetUID(self):
    pyiid = CreateGuid()
    # the str(pyiid) looks like {....} and doing [1:-1] strips that off
    return str(pyiid)[1:-1]
    
</t>
<t tx="szatz.112203232245.114">class ListCtrl(wxListCtrl, wxListCtrlAutoWidthMixin):
    @others
</t>
<t tx="szatz.112203232245.115">def __init__(self, parent, ID, pos=wxDefaultPosition, size=wxDefaultSize, style=0):
    wxListCtrl.__init__(self, parent, ID, pos, size, style)
    wxListCtrlAutoWidthMixin.__init__(self)

    self.il = wxImageList(16,16)

    sm_up = self.il.Add(wxBitmap('bitmaps\\up_arrow.bmp')) #(images.getSmallUpArrowBitmap())
    sm_dn = self.il.Add(wxBitmap('bitmaps\\down_arrow.bmp'))
    self.arrows = (sm_up,sm_dn)
    
    self.idx1 = self.il.Add(wxBitmap('bitmaps\\box.bmp'))
    self.idx0 = self.il.Add(wxBitmap('bitmaps\\filledwhitebox.bmp'))    

    self.SetImageList(self.il, wxIMAGE_LIST_SMALL)

    EVT_LIST_COL_BEGIN_DRAG(self, self.GetId(), self.OnColBeginDrag)    

    self.SetUpColumns()

</t>
<t tx="szatz.112203232245.116">def SetUpColumns(self):
    #Need to to construct column heads for columns with sorting by hand to get sorting images on columns
    info = wxListItem()
    info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_FORMAT
    info.m_image = -1

    #Oth column is priority which is sortable
    info.m_format = wxLIST_FORMAT_LEFT
    info.m_text = "P"
    self.InsertColumnInfo(0, info)
    self.SetColumnWidth(0, 35)
    
    self.InsertColumn(1, "Name")
    self.SetColumnWidth(1, 590)

    self.InsertColumn(2, "Owner")
    self.SetColumnWidth(2, 100)
    
    #3th column is create ate and same as with priority - needs to constructed by hand
    info.m_format = wxLIST_FORMAT_LEFT
    info.m_text = "Due Date"
    self.InsertColumnInfo(3, info)
    self.SetColumnWidth(3, 75)                

</t>
<t tx="szatz.112203232245.117">def OnColBeginDrag(self, evt):
    #if inplace editor then change its dimensions
    if evt.GetColumn() == 0:
        evt.Veto()
</t>
<t tx="szatz.112203232245.118">class MyApp(wxApp):
    @others
</t>
<t tx="szatz.112203232245.119">def OnInit(self):
    global OFFLINE_ONLY, CANCEL
    wxInitAllImageHandlers()
    
    if STARTUP_DIALOG:
        startup = StartupDialog(None, 'List Manager')
        val = startup.ShowModal()
        startup.Destroy()
        if val == wxID_YES:
            OFFLINE_ONLY = True
        elif val == wxID_NO:
            OFFLINE_ONLY = False
        elif val == wxID_CANCEL:
            CANCEL = True
            return True

    if OFFLINE_ONLY is False:
        server = REMOTE_HOST.split(':')[0]
        try:
            socket.gethostbyname(server)
        except:
            dlg = wxMessageDialog(None, "Cannot connect to remote server! Only offline access is possible.", "ListManager", style=wxOK|wxICON_EXCLAMATION|wxSTAY_ON_TOP)
            dlg.ShowModal()
            dlg.Destroy()
            OFFLINE_ONLY = True
            
    frame = ListManager(None, -1, "List Manager", size = (X,Y))
    frame.Show(True)
    self.SetTopWindow(frame)
    CANCEL = False
    return True


</t>
<t tx="szatz.112203232245.121">class Logger:
    def __init__(self):
        self.dlg = LoggerDialog(None, "", "Alerts and Exceptions", dir=DIRECTORY)
    def write(self, error_msg):
        if not self.dlg.IsShown():
            self.dlg.text.AppendText("\n%s\n"%time.asctime())
            self.dlg.Show(True)
        
        self.dlg.text.AppendText(error_msg)

</t>
<t tx="szatz.112203232245.122">def run():
    app = MyApp(0)
    if not CANCEL:
        sys.stderr = sys.stdout = Logger()
        app.MainLoop()
    
if __name__ == '__main__':
    run()
</t>
<t tx="szatz.112203232245.123">@language python
from wxPython.wx import *
# the following two are needed for the calendar
from wxPython.calendar import *
from wxPython.utils import *
import os
@others</t>
<t tx="szatz.112203232245.124">class TicklerDialog(wxDialog):
    @others
</t>
<t tx="szatz.112203232245.125">def __init__(self, parent, msg, caption, pos = wxDefaultPosition, size = wxDefaultSize):
    wxDialog.__init__(self, parent, -1, caption, pos, size, style=wxSTAY_ON_TOP | wxTHICK_FRAME | wxCAPTION)

    TC = wxTextCtrl(self, -1, msg, wxDefaultPosition,
                    (450,250),
                    wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2)

    sizer = wxBoxSizer(wxVERTICAL)
    box = wxBoxSizer(wxHORIZONTAL)        
    
    sizer.Add(TC, 1, wxALIGN_CENTRE|wxALL, 5)
    line = wxStaticLine(self, -1, size = (20,-1), style = wxLI_HORIZONTAL)
    
    sizer.Add(line, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP, 5)
    btn = wxButton(self, wxID_OK, "GO TO ITEM")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    btn.SetDefault()

    btn = wxButton(self, wxID_FORWARD, "SHOW NEXT")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)

    btn = wxButton(self, wxID_APPLY, "MAIL")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)        

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    
    sizer.AddSizer(box, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5)
    self.SetSizer(sizer)
    self.SetAutoLayout(True)
    sizer.Fit(self)

    EVT_LEFT_DOWN(TC, self.OnLeftDown)
    EVT_BUTTON(self, wxID_FORWARD, self.OnForward)
    EVT_BUTTON(self, wxID_APPLY, self.OnMail)

    TC.SetCursor(wxStockCursor(wxCURSOR_ARROW))        

    self.TC = TC
</t>
<t tx="szatz.112203232245.126">def OnLeftDown(self, evt):
    self.EndModal(wxID_OK)
</t>
<t tx="szatz.112203232245.127">def OnForward(self, evt):
    self.EndModal(wxID_FORWARD)
</t>
<t tx="szatz.112203232245.128">def OnMail(self, evt):
    self.EndModal(wxID_APPLY)        
</t>
<t tx="szatz.112203232245.129">class ModifierDialog(wxDialog):
    @others
</t>
<t tx="szatz.112203232245.130">def __init__(self, parent, title,
             pos=wxDefaultPosition,
             size=wxDefaultSize,
             style=wxCAPTION,
             modifierlist=None,
             curselections = ''):
    wxDialog.__init__(self, parent, -1, title, pos, size, style)

    sizer1 = wxBoxSizer(wxVERTICAL)
    sizer2 = wxBoxSizer(wxHORIZONTAL)
    
    tc = wxTextCtrl(self, -1, "", size = (150,-1))
    sizer1.Add(tc, 0, wxALIGN_CENTRE|wxALL, 5)
    self.tc = tc

    if not modifierlist:
        modifierlist = []
    lb = wxListBox(self, -1,  wxDefaultPosition, (150,300), #wxPoint(90, 80)
                    modifierlist, wxLB_MULTIPLE|wxLB_SORT)

    sizer1.Add(lb, 1, wxALIGN_CENTRE|wxALL, 5)

    line = wxStaticLine(self, -1, size = (20,-1), style = wxLI_HORIZONTAL)
    sizer1.Add(line, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP, 5)
    

    btn = wxButton(self, wxID_OK, "OK")
    sizer2.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    btn.SetDefault()

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    sizer2.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)

    sizer1.AddSizer(sizer2, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5)
    self.SetSizer(sizer1)
    self.SetAutoLayout(True)
    sizer1.Fit(self)

    for sel in curselections:
        index = lb.FindString(sel)
        if index !=-1:
            lb.SetSelection(index)

    self.lb = lb

    EVT_BUTTON(self, wxID_CANCEL, self.ClearSelections)


</t>
<t tx="szatz.112203232245.131">def GetUserInput(self):
    idx_list = self.lb.GetSelections()
    mod_list =[]
    for i in idx_list:
        mod_list.append(self.lb.GetString(i))
        self.lb.Deselect(i) #071203

    new_list = []
    manual_string = self.tc.GetValue() #text entry box
    
    if manual_string:
        manual_list = [x.strip() for x in manual_string.split(';')]
        for name in manual_list:
            clean_name = ", ".join([x.strip().title() for x in name.split(',')])
            if clean_name not in mod_list:
                mod_list.append(clean_name)
                new_list.append(clean_name)

        
    return (mod_list, new_list)</t>
<t tx="szatz.112203232245.132">def SelectCurrent(self, cur_sel):
    for sel in cur_sel:
        index = self.lb.FindString(sel)
        if index !=-1:
            self.lb.SetSelection(index)


</t>
<t tx="szatz.112203232245.133">def ClearSelections(self, evt=None):
    idx_list = self.lb.GetSelections() #note you can't just use the indexes of the SelectCurrent since they may have clicked before cancelling
    for i in idx_list:
        self.lb.Deselect(i)

    evt.Skip()</t>
<t tx="szatz.112203232245.134">class MailDialog(wxDialog):
    @others
</t>
<t tx="szatz.112203232245.135">def __init__(self, parent, title,
             pos=wxDefaultPosition,
             size=wxDefaultSize,
             style=wxSTAY_ON_TOP| wxTHICK_FRAME|wxCAPTION|wxSYSTEM_MENU,
             recipients='',
             subject = '',
             body = ''):
    
    wxDialog.__init__(self, parent, -1, title, pos, size, style)

    sizer = wxBoxSizer(wxVERTICAL)
    box = wxBoxSizer(wxHORIZONTAL)

    recipients = "; ".join(recipients)
    label = wxStaticText(self, -1, "To:",wxDefaultPosition, size=(40,-1), style=wxALIGN_LEFT)
    RTC = wxTextCtrl(self, -1, recipients, size = (480,-1))
    box.Add(label)
    box.Add(RTC)

    #sizer.Add(10,10,0)      

    sizer.AddSizer(box)        

    box = wxBoxSizer(wxHORIZONTAL)       
    label = wxStaticText(self, -1, "Subject:",wxDefaultPosition, size=(40,-1),style=wxALIGN_LEFT)
    STC = wxTextCtrl(self, -1, subject, size = (480,-1)) 
    box.Add(label)
    box.Add(STC)

    sizer.AddSizer(box)
    sizer.Add(1, 5, 0)
    
    BTC = wxTextCtrl(self, -1, body, wxDefaultPosition, size = (500,400), style=wxTE_MULTILINE|wxTE_RICH2)

    sizer.Add(BTC)

    box = wxBoxSizer(wxHORIZONTAL)
    btn = wxButton(self, wxID_OK, "SEND MAIL")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    btn.SetDefault()

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)

    sizer.AddSizer(box)
    self.SetSizer(sizer)
    self.SetAutoLayout(True)
    sizer.Fit(self)

    self.RTC = RTC
    self.STC = STC
    self.BTC = BTC

</t>
<t tx="szatz.112203232245.136">class CalendarDialog(wxDialog):
    @others
</t>
<t tx="szatz.112203232245.137">def __init__(self, parent, title,
             pos=wxDefaultPosition,
             size=wxDefaultSize,
             style=wxCAPTION,
             date=0):
    
    wxDialog.__init__(self, parent, -1, title, pos, size, style)

    if not date:
        date = wxDateTime_Now()

    cal = wxCalendarCtrl(self, -1, date, #pos = (25,50),
                         style = wxCAL_SHOW_HOLIDAYS | wxCAL_SUNDAY_FIRST)

    EVT_CALENDAR(self, cal.GetId(), self.OnCalSelected)

    #EVT_CLOSE(self, self.OnCloseWindow)          
    
    self.cal = cal

    # Set up control to display a set of holidays:
    EVT_CALENDAR_MONTH(self, cal.GetId(), self.OnChangeMonth)
    
    self.holidays = [(1,1), (10,31), (12,25) ]    # (these don't move around)
    
    self.OnChangeMonth()        

#-------------------------------------------------------------------------        
    sizer1 = wxBoxSizer(wxVERTICAL)
    sizer2 = wxBoxSizer(wxHORIZONTAL)
 
    sizer1.Add(cal, 0, wxALIGN_CENTRE|wxALL, 5)

    line = wxStaticLine(self, -1, size = (20,-1), style = wxLI_HORIZONTAL)
    sizer1.Add(line, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxTOP, 5)
    

    btn = wxButton(self, wxID_OK, "OK")
    btn.SetDefault()
    sizer2.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    #btn.SetDefault()
    sizer2.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)

    sizer1.AddSizer(sizer2, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5)
    self.SetSizer(sizer1)
    self.SetAutoLayout(True)
    sizer1.Fit(self)
</t>
<t tx="szatz.112203232245.138">def OnCalSelected(self, evt):
    self.result = evt.GetDate()
    self.EndModal(wxID_OK)
</t>
<t tx="szatz.112203232245.139">def OnChangeMonth(self, evt=None):
    cur_month = self.cal.GetDate().GetMonth() + 1   # convert wxDateTime 0-11 =&gt; 1-12
    for month, day in self.holidays:
        if month == cur_month:
            self.cal.SetHoliday(day)        
</t>
<t tx="szatz.112203232245.140">def OnCloseWindow(self, event):
    #self.cal.Destroy
    #self.Destroy()
    print "I got to close window"
</t>
<t tx="szatz.112203232245.141">def GetDate(self):
    return self.result
</t>
<t tx="szatz.112203232245.143">class FindDialog(wxDialog):
    @others
</t>
<t tx="szatz.112203232245.144">def __init__(self, parent, caption, msg, pos=wxDefaultPosition, size=(300,120)):
    wxDialog.__init__(self, parent, -1, caption, pos, size, style=wxSTAY_ON_TOP|wxCAPTION)    

    self.FindText = wxTextCtrl(self, -1, msg, wxDefaultPosition,(200,24))
         
    box_a = wxBoxSizer(wxHORIZONTAL)
    box_a.Add(self.FindText, 1, wxALIGN_CENTER|wxALL, 5)

    box_b = wxBoxSizer(wxVERTICAL)        
    btn = wxButton(self, wxID_OK, "OK")
    box_b.Add(btn, 0, wxALIGN_CENTER|wxALL,5)
    btn.SetDefault()               

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    box_b.Add(btn, 0, wxALIGN_CENTER)

    box_a.AddSizer(box_b)

    self.MatchCase = wxCheckBox(self, -1, "Match Case")
    self.MatchWhole = wxCheckBox(self, -1, "Match Whole Word")
    box_c = wxBoxSizer(wxVERTICAL)
    box_c.Add(self.MatchCase, 0, wxLEFT|wxBOTTOM, 5)
    box_c.Add(self.MatchWhole, 0, wxLEFT, 5)

    self.SearchNotes = wxCheckBox(self, -1, "Search Notes")
    self.SearchFinished = wxCheckBox(self, -1, "Search Finished")
    box_d = wxBoxSizer(wxVERTICAL)
    box_d.Add(self.SearchNotes, 0, wxLEFT|wxBOTTOM, 5)
    box_d.Add(self.SearchFinished, 0, wxLEFT, 5)

    box_e = wxBoxSizer(wxHORIZONTAL)
    box_e.AddSizer(box_c)
    box_e.AddSizer(box_d)

    sizer = wxBoxSizer(wxVERTICAL)
    sizer.AddSizer(box_a)
    sizer.AddSizer(box_e)

    self.SetSizer(sizer)

    EVT_BUTTON(self, wxID_OK, parent.FindString)


</t>
<t tx="szatz.112203232245.146">class EvalDialog(wxDialog):
    @others
</t>
<t tx="szatz.112203232245.147">def __init__(self, parent, caption, msg, pos=wxDefaultPosition, size=(300,80)):
    wxDialog.__init__(self, parent, -1, caption, pos, size, style=wxSTAY_ON_TOP|wxCAPTION)    

    EvalText = wxTextCtrl(self, -1, msg, wxDefaultPosition,(200,24))

         
    box_a = wxBoxSizer(wxHORIZONTAL)
    box_a.Add(EvalText, 1, wxALIGN_CENTER|wxALL, 5)

    box_b = wxBoxSizer(wxVERTICAL)        
    btn = wxButton(self, wxID_OK, "OK")
    box_b.Add(btn, 0, wxALIGN_CENTER|wxALL,5)
    btn.SetDefault()               

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    box_b.Add(btn, 0, wxALIGN_CENTER)

    box_a.AddSizer(box_b)

    self.SetSizer(box_a)

    self.EvalText = EvalText
    self.parent = parent

    #EVT_BUTTON(self, wxID_OK, self.PostOKEvent)
    EVT_BUTTON(self, wxID_OK, parent.OnEvaluate)



</t>
<t tx="szatz.112203232245.148">def PostOKEvent(self, evt=None):
    wxPostEvent(self.parent, evt)
</t>
<t tx="szatz.112203232245.149">class LoggerDialog(wxDialog):
    @others</t>
<t tx="szatz.112203232245.150">def __init__(self, parent, msg, caption, pos=(-1,-1), size=(500,300), dir=None):
    wxDialog.__init__(self, parent, -1, caption, pos, size)
    #if pos == (-1,-1):
        #self.CenterOnScreen(wxBOTH)
        
    if dir:
        self.dir = dir
    else:
        self.dir = os.getcwd()
        
    text = wxTextCtrl(self, -1, msg, (-1,-1), (450,250), wxTE_MULTILINE | wxTE_READONLY)

    sizer = wxBoxSizer(wxVERTICAL)
    box = wxBoxSizer(wxHORIZONTAL)        
    
    sizer.Add(text, 1, wxALIGN_CENTRE|wxALL, 5)

    btn = wxButton(self, wxID_OK, "Close")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    btn.SetDefault()
    
    ID_SAVE = wxNewId()

    btn = wxButton(self, ID_SAVE, "Save to File")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)        

    sizer.AddSizer(box, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5)
    self.SetSizer(sizer)
    self.SetAutoLayout(True)
    sizer.Fit(self)
    
    self.text = text
    
    EVT_BUTTON(self, ID_SAVE, self.OnSave)
</t>
<t tx="szatz.112203232245.151">def OnSave(self, evt):
        
    path = os.path.join(self.dir, 'logfile.txt')
        
    f = file(path,'a')
    f.write(self.text.GetValue())
    f.close()

    dlg = wxMessageDialog(self,"Appended text to logfile.text", "Notice", wxICON_INFORMATION|wxOK)
    dlg.ShowModal()
    dlg.Destroy()
    
    self.text.Clear()
</t>
<t tx="szatz.112203232245.152">class FinishedDialog(wxDialog):
    @others</t>
<t tx="szatz.112203232245.153">def __init__(self, parent, title,
            pos=wxDefaultPosition,
            size=wxDefaultSize,
            style=wxCAPTION,
            days=0,
            spin_label="",
            check_label=""):
             
    wxDialog.__init__(self, parent, -1, title, pos, size)
    self.Centre()
    
    self.check = wxCheckBox(self, -1, check_label)
    
    if days == -1:
        self.check.SetValue(True)
        days = 0
    
    panel = wxPanel(self, -1, (-1,-1),(225,75))
    wxStaticText(panel, -1, spin_label,(15, 15))
    self.text = wxTextCtrl(panel, -1, str(days), (30, 50), (30, -1))
    h = self.text.GetSize().height
    self.spin = wxSpinButton(panel, -1, (56, 50), (h, h), wxSP_VERTICAL)
    wxStaticText(panel, -1, 'days',(76, 53))
    self.spin.SetRange(0, 14)
    self.spin.SetValue(days)
    
    H_sizer = wxBoxSizer(wxHORIZONTAL)
    
    line = wxStaticLine(self, -1, size = (20,-1), style = wxLI_HORIZONTAL)
    
    btn = wxButton(self, wxID_OK, "OK")
    H_sizer.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    btn.SetDefault()

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    H_sizer.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    
    V_sizer = wxBoxSizer(wxVERTICAL)
    V_sizer.Add(panel,1,wxALIGN_CENTER|wxEXPAND)
    V_sizer.Add(-1,5)
    V_sizer.Add(self.check,0,wxALIGN_LEFT|wxALL,5)
    V_sizer.Add(line,0, wxGROW|wxALIGN_CENTER_VERTICAL|wxTOP, 5)
    V_sizer.AddSizer(H_sizer, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5)
    
    self.SetSizer(V_sizer)
    self.SetAutoLayout(True)
    V_sizer.Fit(self)

    EVT_SPIN(self, self.spin.GetId(), self.OnSpin)
    EVT_CHECKBOX(self, self.check.GetId(), self.OnCheck)
    
    if self.check.GetValue():
        self.spin.Enable(False)
        self.text.Enable(False)
        
    self.Layout() #doesn't appear necessary


</t>
<t tx="szatz.112203232245.154">def OnSpin(self, evt):
    self.text.SetValue(str(evt.GetPosition()))</t>
<t tx="szatz.112203232245.155">def OnCheck(self, evt=None):
    if self.check.GetValue():
        self.spin.Enable(False)
        self.text.Enable(False)
    else:
        self.spin.Enable(True)
        self.text.Enable(True)
</t>
<t tx="szatz.112203232245.156">class TreeDialog(wxDialog):
    @others</t>
<t tx="szatz.112203232245.157">def __init__(self, parent, caption, pos=wxDefaultPosition, size=(300,400), tree={}):
    wxDialog.__init__(self, parent, -1, caption, pos, size, style=wxSTAY_ON_TOP|wxCAPTION)

    TreeCtrl = wxTreeCtrl(self, -1, wxDefaultPosition, (300,400), wxTR_HAS_BUTTONS)    #|wxTR_HIDE_ROOT)#wxDefaultSize,
    
    sizer = wxBoxSizer(wxVERTICAL)
    sizer.Add(TreeCtrl, 1, wxALIGN_CENTER|wxALL, 5)

    box = wxBoxSizer(wxHORIZONTAL)
    btn = wxButton(self, wxID_OK, "OK")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)
    btn.SetDefault()

    btn = wxButton(self, wxID_CANCEL, "CANCEL")
    box.Add(btn, 0, wxALIGN_CENTRE|wxALL, 5)

    sizer.AddSizer(box)
    self.SetAutoLayout(1)
    self.SetSizer(sizer)

    il = wxImageList(16,16)
    
    fldridx = il.Add(wxBitmap('bitmaps\\folder.bmp'))
    fldropenidx = il.Add(wxBitmap('bitmaps\\folder_open.bmp'))
    listidx =  il.Add(wxBitmap('bitmaps\\list.bmp'))

    TreeCtrl.SetImageList(il)

    root = TreeCtrl.AddRoot("List Manager")
    TreeCtrl.SetItemImage(root, fldridx, wxTreeItemIcon_Normal)
    TreeCtrl.SetItemImage(root, fldropenidx, wxTreeItemIcon_Expanded)

    for host in tree:
        child = TreeCtrl.AppendItem(root, host)
        TreeCtrl.SetItemImage(child, fldridx, wxTreeItemIcon_Normal)
        TreeCtrl.SetItemImage(child, fldropenidx, wxTreeItemIcon_Expanded)
        for listname in tree[host]:
            last = TreeCtrl.AppendItem(child, listname)
            TreeCtrl.SetItemImage(last, listidx, wxTreeItemIcon_Normal)
            TreeCtrl.SetItemImage(last, listidx, wxTreeItemIcon_Selected)

    TreeCtrl.Expand(root)

    self.TreeCtrl= TreeCtrl
    self.il = il #? prevents GC

    EVT_LEFT_DCLICK(TreeCtrl, self.OnLeftDClick)
</t>
<t tx="szatz.112203232245.158">def OnLeftDClick(self, event=None):
    self.EndModal(wxID_OK)
</t>
<t tx="szatz.112203232245.159">@language python
&lt;&lt; outlookAddin declarations &gt;&gt;
@others

if __name__ == '__main__':
    import win32com.server.register
    win32com.server.register.UseCommandLine(OutlookAddin)
    if "--unregister" in sys.argv:
        UnregisterAddin(OutlookAddin)
    else:
        RegisterAddin(OutlookAddin)
</t>
<t tx="szatz.112203232245.160"># This is mainly stolen from Mark Hammond's demo plugin for win32com.client
# A demo plugin for Microsoft Outlook (NOT Outlook Express)

from win32com import universal
from win32com.server.exception import COMException
from win32com.client import gencache, DispatchWithEvents
from win32com.client import Dispatch
import winerror
import pythoncom
from win32com.client import constants
import win32ui ##
import sys
from socket import *
import pickle

# Support for COM objects we use.
#sz comment gencache.EnsureModule makes sure you are using makepy if the makepy-derived
#file doesn't already exist
#but as long as you did run makepy then you should just be alble to do a normal dispatch

mod = gencache.EnsureModule('{00062FFF-0000-0000-C000-000000000046}', 0, 9, 0, bForDemand=True) # Outlook 9
gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True) # Office 9

# The TLB defining the interfaces we implement
universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, ["_IDTExtensibility2"])

Target = 'mail_transfer'


</t>
<t tx="szatz.112203232245.161">class ButtonEvent:
    @others
</t>
<t tx="szatz.112203232245.162">def OnClick(self, button, cancel):
    #activeExplorer and MailTransferFolder are globals defined in OnConnection
    sel = activeExplorer.Selection

    for i in range(1,sel.Count+1):
        item = sel.Item(i)
        item.Move(MailTransferFolder)

    return cancel

</t>
<t tx="szatz.112203232245.163">class FolderEvent:
    @others
</t>
<t tx="szatz.112203232245.164">def OnItemAdd(self, item):
    try:
        s = socket(AF_INET,SOCK_STREAM)
        s.connect(('localhost', 8888))
        d = {}
        d['Parent.Name'] = item.Parent.Name
        d['SenderName'] = item.SenderName
        d['Subject'] = item.Subject
        d['Body'] = item.Body[:5000]
        d['CreationTime'] = item.CreationTime.Format()
        str = pickle.dumps(d)
        s.send(str) # ?Receive no more than 1024 bytes
        s.close()
        win32ui.MessageBox("Sent %s to ListManager"%item.Subject)
    except:
        pass
</t>
<t tx="szatz.112203232245.165">class OutlookAddin:
    &lt;&lt; class OutlookAddin declarations &gt;&gt;
    @others
</t>
<t tx="szatz.112203232245.166">_com_interfaces_ = ['_IDTExtensibility2']
_public_methods_ = []
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
_reg_clsid_ = "{0F47D9F3-598B-4d24-B7E3-92AC15ED27E2}"
_reg_progid_ = "Python.Test.OutlookAddin"
_reg_policy_spec_ = "win32com.server.policy.EventHandlerPolicy"
</t>
<t tx="szatz.112203232245.167">def OnConnection(self, application, connectMode, addin, custom):
    global MailTransferFolder
    global activeExplorer
    # ActiveExplorer may be none when started without a UI (eg, WinCE synchronisation)
    activeExplorer = application.ActiveExplorer()
    if activeExplorer:
        bars = activeExplorer.CommandBars
        toolbar = bars.Item("Standard")
        item = toolbar.Controls.Add(Type=constants.msoControlButton, Temporary=True)
        item = self.toolbarButton = DispatchWithEvents(item, ButtonEvent) #? just need this to be an ivar
        item.Caption="List Manager"
        item.TooltipText = "Click to move"
        item.Enabled = True
        #self.toolbarButton = DispatchWithEvents(item, ButtonEvent) #need something that won't get GC'd. Note Dispatch returns item

    ns = application.GetNamespace("MAPI")
    Folders = ns.Folders

    for i in range(1,len(Folders)+1):
        if Folders[i].Name.find("Mailbox") != -1:
            folders = Folders[i].Folders
            break
    else:
        win32ui.MessageBox("Can't find Mailbox!")
        return	
    
    for i in range(1,len(folders)+1):
        if folders[i].Name == Target:
            MailTransferFolder = folders[i]
            self.targetMailbox = DispatchWithEvents(folders[i].Items, FolderEvent) #? just need this to be an ivar
            win32ui.MessageBox("Enabled: %s\nOutlookAddin3"%Target)
            break
    else:
        win32ui.MessageBox("Could not find mail folder: %s\nOutlookAddin3"%Target)</t>
<t tx="szatz.112203232245.168">def OnDisconnection(self, mode, custom):
    print "OnDisconnection"
</t>
<t tx="szatz.112203232245.169">def OnAddInsUpdate(self, custom):
    print "OnAddInsUpdate", custom
</t>
<t tx="szatz.112203232245.170">def OnStartupComplete(self, custom):
    print "OnStartupComplete", custom
</t>
<t tx="szatz.112203232245.171">def OnBeginShutdown(self, custom):
    print "OnBeginShutdown", custom
</t>
<t tx="szatz.112203232245.172">def RegisterAddin(klass):
    import _winreg
    key = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Outlook\\Addins")
    subkey = _winreg.CreateKey(key, klass._reg_progid_)
    _winreg.SetValueEx(subkey, "CommandLineSafe", 0, _winreg.REG_DWORD, 0)
    _winreg.SetValueEx(subkey, "LoadBehavior", 0, _winreg.REG_DWORD, 3)
    _winreg.SetValueEx(subkey, "Description", 0, _winreg.REG_SZ, klass._reg_progid_)
    _winreg.SetValueEx(subkey, "FriendlyName", 0, _winreg.REG_SZ, klass._reg_progid_)
</t>
<t tx="szatz.112203232245.173">def UnregisterAddin(klass):
    import _winreg
    try:
        _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Outlook\\Addins\\" + klass._reg_progid_)
    except WindowsError:
        pass
</t>
<t tx="szatz.112403214111">class StartupDialog(wxDialog):
    @others
</t>
<t tx="szatz.112403214111.1">def __init__(self, parent, caption, pos=wxDefaultPosition, size=(300,115)):
    wxDialog.__init__(self, parent, -1, caption, pos, size, style=wxSTAY_ON_TOP|wxCAPTION)

    msg = "You can connect to the server using the network,\nor work offline, or cancel this logon."

    image = wxStaticBitmap(self, -1, wxBitmap('bitmaps\\wxpdemo.bmp'), (-1,-1), size=(32,32)) #sizer determines position
    text = wxStaticText(self, -1, msg, (-1,-1), size=(250,32)) #sizer determines position

    rect = wxBoxSizer(wxHORIZONTAL)
    rect.Add(image, 0, wxALIGN_LEFT|wxALL, 4)
    rect.Add(text, 1, wxALIGN_CENTER|wxTOP, 7)
    sizer = wxBoxSizer(wxVERTICAL)


    box = wxBoxSizer(wxHORIZONTAL)
    btn = wxButton(self, wxID_NO, 'Connect')
    box.Add(btn, 0, wxALL, 10)
    btn.SetDefault()

    btn = wxButton(self, wxID_YES, 'Work Offline')
    box.Add(btn, 0, wxALL, 10)

    btn = wxButton(self, wxID_CANCEL, 'Cancel')
    box.Add(btn, 0, wxALL, 10)

    sizer.AddSizer(rect)
    sizer.AddSizer(box)

    self.SetSizer(sizer)

    EVT_BUTTON(self, wxID_NO, self.OnSelection)
    EVT_BUTTON(self, wxID_YES, self.OnSelection)
    EVT_BUTTON(self, wxID_CANCEL, self.OnSelection)</t>
<t tx="szatz.112403214111.2">def OnSelection(self,evt):
    val = evt.GetId()
    self.EndModal(val)</t>
<t tx="szatz.112703232517">def OnShowAbout(self, evt=None):
    from about import AboutBox
    dlg = AboutBox(self, app_version = VERSION)
    dlg.ShowModal()
    dlg.Destroy()
    
</t>
<t tx="szatz.112703234413">def OnShowHelp(self, evt=None):
    os.startfile('ListManager.chm')
    
</t>
<t tx="szatz.121403184329"></t>
<t tx="szatz.121403184329.1">#########################
ListManager Documentation
#########################

.. contents:: Table of Contents
.. section-numbering::
    
.. bibliographic fields (which also require a transform):

:Author: Steven Zatz
:Contact: szatz@webmd.net
:Date: $Date: 2003/12/13 15:15:42 $
:Status: This is a "work in progress"
:Revision: $Revision: 0.80 $
:Copyright: This document has been placed in the public domain. You may do with it as you wish. 

</t>
<t tx="szatz.121403184329.2">Need some intro sentence

Main structure of file wxListManager.py is:

- class ListManager
- class ListCtrl
- class myApp
- class main

</t>
<t tx="szatz.121403184329.3">This is where all the action is.
The single instance of this class is the top frame of the application.
ListManager subclasses wxFrame which is generally true of the top window in a wxPython application.

::

    wxFrame(parent, id, title, pos=wxDefaultPosition, size=wxDefaultSize, style=wxDEFAULT_FRAME_STYLE)

*size* can take a tuple and the default is (-1,-1).  **wxSize** is an object that looks like a tuple but it is not a tuple although:

    ``wxSize(1,1) == (1,1)  &gt;&gt; True``

So hen you need a wxSize object, you can just use a tuple.

Same is true of wxPoint(1,1) – looks like a tuple and can just use a tuple when a wxPoint is called for::

    def __init__(self, parent, id, title, size):
        wxFrame.__init__(self, parent, id, title, size = size)

        self.SetIcon(wxIcon('bitmaps//wxpdemo.ico', wxBITMAP_TYPE_ICO))
        self.CreateStatusBar() # Needs to happen before Load Recent files
</t>
<t tx="szatz.121403184329.4">The init arguments (are these called arguments?) are exactly the same ones necessary to pass to the wxFrame __init__ method.

::
    self.SetIcon(wxIcon('bitmaps//wxpdemo.ico', wxBITMAP_TYPE_ICO)) #set the app window icon
    self.CreateStatusBar() # Needs to happen before Load Recent files
    
**SetIcon** is a method of Frame that sets the icon in the upper left of the title bar of the frame.

Note that the default style (wxDefault_Frame_Style) includes wxMINIMIZE_BOX, wxMAXIMIZE_BOX, wxRESIZE_BORDER, wxSYSTEM_MENU, wxCAPTION (this is the title that appears in the title bar).

``wxIcon(filename, type, desiredWidth = -1, desiredHeight = -1)``

**CreateStatusBar** --&gt; method of Frame

**CreateStatusBar** (number=1, style=0, id =-1)

*number*
    number of fields to create. Specify a value greater than 1 to create a multi-field status bar.




</t>
<t tx="szatz.121403184329.5">self.PropertyDicts = [ ]
    List of Dictionaries that describe properties of each List

self.ItemLists = [ ]
    List of lists that consist of instance objects of class Item.  Each of the lists contained in self.ItemLists correspond to the items that are being displayed in the ListCtrl.

So self.Itemlist[2] corresponds to the 2nd tab of the notebook and to the items in self.ListCtrls[2].

Class Item is just an empty Class as follows::

    Class Item: pass

The purpose of the class is just to create an object that can have various attributes as follows::

    item = Item()
    item.name = "Hello young lovers"
    item.priority = 2
    ...

self.ListCtrls = [ ]
    List of of instance objects of class ListCtrls, which are a subclass of wxPython class wxListCtrl.

self.OwnerLBoxes = [ ]
    List of of instance objects of wxPython class wxListBox, which is a simple one column List Control.

::

    wxListBox(parent, id, pos = wxDefaultPosition, size = wxDefaultSize, choices=[], style=0)

self.L = -1 
    The index of the currently active notebook tab.  If there are any tabs then one is always selected.  If there are no tabs then this is indicated by setting self.L = -1.

self.curIdx = -1
    The currently selected row in the active ListCtrl.  There are times like after a row is deleted in which there may be rows visible but no row is selected.

::

    self.printdata = wxPrintData()
    self.printdata.SetPaperId(wxPAPER_LETTER)
    self.printdata.SetOrientation(wxPORTRAIT) #####

wxPrintData
This class holds a variety of information related to printers and printer device contexts. This class is used to create a wxPrinterDC and a wxPostScriptDC. It is also used as a data member of wxPrintDialogData and wxPageSetupDialogData, as part of the mechanism for transferring data between the print dialogs and the application.

self.copyitems = [ ]
List that contains item instance objects that have been copied from one list to be moved to another list.

self.modified = { }
This is a dictionary that contains the information concerning whether any of several elements have been changed.  Chose a dictionary more to test the idea that I could create a simple method that would update the dictionary and here is an example::

    EVT_TEXT(self, self.name.GetId(), lambda e: self.modified.update({'name':1}))

So this is a nice lambda function that says if an EVT_TEXT event occurs then update the dictionary by adding the key to the dictionary::

    EVT_TEXT(window, id, func)

Respond to a wxEVT_COMMAND_TEXT_UPDATED event, generated when the text changes. Note that this event will always be sent when the text control’s content changes - whether this is due to user input or comes programmatically (for example, if SetValue() is called)

self.Cursors = { } 
    Dictionary that holds the database cursor objects.  For example, it will look like:

``{'sqlite':&lt;sqlite cursor object&gt;,'nycpsltszatz':&lt;mysql cursor object&gt;}``

self.tickler_active = False
    Should the tickler be shown; can be shut off by unchecking Tickler menu item

self.editor = [ ]
    List that holds the dictionaries that describe the notes that are edited by the external text editor

::

    [{'table': 'mine',
    'host': 'wxLMDB:sqlite',
    'path': 'C:\\DOCUME~1\\STEVEN~1\\LOCALS~1\\Temp\\Journal Scan schedule.txt',
    'id': '1AB34FB9-9EE6-4AFC-8AF0-FFCA50103BF3',
    'time': 1070850894}, 
    {'table': 'factoids',
    'host': 'wxLMDB:sqlite',
    'path': 'C:\\DOCUME~1\\STEVEN~1\\LOCALS~1\\Temp\\How many cme programs are sponsored- - 91%.txt', 
    'id': '9CAC4D18-DE1C-4535-B9A5-4CDB1AD3F304', 
    'time': 1070850908}]

#there is a wxPanel in the AddListControl method so each wxListCtrl has a different panel as parent
#there is a nb_sizer = wxNotebookSizer(nb) class but doesn't seem to make any difference

self.sqlite_connections = [] 
    Needed because the sqlite connection has a weakreference that deletes it when you want it around
    
self.popupvisible = False
    Needed to check so that there are not two reminder popups visible at once
    
self.in_place_editor = None 
    Not really necessary since set and used later but here as a reminder
    
self.showrecentcompleted = 0
    Show completed tasks going back x days

self.LC_font = wxFont(9, wxSWISS, wxNORMAL, wxNORMAL)

self.date_titles = {'createdate':"Create Date",'duedate':"Due Date",'timestamp':"Last Modified",'finisheddate':"Completion Date"}
self.attr2col_num = {'priority':0, 'name':1,'owners':2, 'date':3}

self.FindDialog = FindDialog(self, "Find...", "")
self.EvalDialog = EvalDialog(self, "Evaluate...", "")</t>
<t tx="szatz.20031220092608">@ignore
@nocolor
@wrap

The ListManager ``__init__`` method is pretty straightforward.  The ``__init__`` arguments are the ones that need to be passed to ``wxFrame __init__`` method. The wxFrame class has the following form:

    ``wxFrame(parent, id, title, pos=wxDefaultPosition, size=wxDefaultSize, style=wxDEFAULT_FRAME_STYLE, name="frame")``

The default style (``wxDEFAULT_FRAME_STYLE``) includes ``wxMINIMIZE_BOX``, ``wxMAXIMIZE_BOX``, ``wxRESIZE_BORDER``, ``wxSYSTEM_MENU``, ``wxCAPTION`` (the latter is the text that appears in the title bar).

``SetIcon`` is a method of ``wxFrame`` that sets the icon in the upper left of the title bar of the frame.  The wxIcon class has the following form:

    ``wxIcon(filename, type, desiredWidth=-1, desiredHeight=-1)``

``CreateStatusBar`` is a method of ``wxFrame``. The wxPython form is:

        ``CreateStatusBar(number=1, style=0, id=-1)``

*number* --&gt;
    number of fields to create. Specify a value greater than 1 to create a multi-field status bar.

``CreateStatusBar`` needs to be called before &lt;&lt; Load Recent Files &gt;&gt;.

The various sections of ``__init__`` are explained in their corresponding section::

    &lt;&lt; ListManager Attributes &gt;&gt;
    &lt;&lt; Menu Setup &gt;&gt;
    &lt;&lt; Toolbar Setup &gt;&gt;
    &lt;&lt; Menu/Toolbar Events &gt;&gt;
    &lt;&lt; Create Controls&gt;&gt;
    &lt;&lt; Layout Stuff &gt;&gt;
    &lt;&lt; Other Events &gt;&gt;
    &lt;&lt; GUI Instance Objects &gt;&gt;
    &lt;&lt; Create Socket &gt;&gt;
    &lt;&lt; Load Recent Files &gt;&gt;</t>
<t tx="szatz.20031220092608.1">@ignore
@nocolor
@wrap

self.PropertyDicts
    list of dictionaries that describe properties of each ListManager List (note that when referring to a collection of ListManager items a capital *L* List and table are used interchangeably).

self.ItemLists
    list of lists that consist of instance objects of class ``Item``.  Each of the lists contained in self.ItemLists correspond to the items that are being displayed in the ListCtrl.  So ``self.Itemlist[2]`` corresponds to the 2nd tab of the notebook and to the items in self.ListCtrls[2].

The class ``Item`` is just an empty class being used as a convenience to hold item attributes::

    class Item:
        pass

The purpose of the class is just to create an object that can have various attributes as follows:

+-----------------+----------------------------------------------------+
|item.id          |GUID                                                |
+-----------------+----------------------------------------------------+
|item.name        |string that describes the item                      |
+-----------------+----------------------------------------------------+
|item.priority    |integer ranging from 1 (high) to 3 (low)            |
+-----------------+----------------------------------------------------+
|item.owners      |list of the form ["Zatz, Steve", "Hoffman, Steve"]  |
+-----------------+----------------------------------------------------+
|item.note        |string that provides additional info on item        |
+-----------------+----------------------------------------------------+
|item.timestamp   |timestamp indicating when an item was last modified |
+-----------------+----------------------------------------------------+
|item.duedate     |default is None; mx.DateTime date                   |
+-----------------+----------------------------------------------------+
|item.createdate  |mx.DateTime.now() mx.DateTime timestamp             |
+-----------------+----------------------------------------------------+
|item.finisheddate|efaut is None; mx.DateTime date                     |
+-----------------+----------------------------------------------------+

self.ListCtrls
    list of of instance objects of class ListCtrls, which are a subclass of wxPython class wxListCtrl.

self.OwnerLBoxes
    list of of instance objects of wxPython class wxListBox, which is a simple one column List Control.

The wxPython constructor for a wxListBox is:

    ``wxListBox(parent, id, pos=wxDefaultPosition, size=wxDefaultSize, choices=[], style=0)``

self.L
    index of the currently active notebook tab.  If there are any tabs in the notebook then one of them is always selected.  If there are no tabs then this is indicated by setting ``self.L = -1``.

self.curIdx
    currently selected row in the active ``ListCtrl``.  There are times like after a row is deleted in which there may be rows visible but no row is selected.

The following lines set the default printer data::

    self.printdata = wxPrintData()
    self.printdata.SetPaperId(wxPAPER_LETTER)
    self.printdata.SetOrientation(wxPORTRAIT)


The wxPython class ``wxPrintData`` holds a variety of information related to printers and printer device contexts. This class is used to create a wxPrinterDC and a wxPostScriptDC. It is also used as a data member of wxPrintDialogData and wxPageSetupDialogData, as part of the mechanism for transferring data between the print dialogs and the application.

self.copyitems
    list that contains item instance objects that have been copied from one list to be moved to another list.

self.modified
    dictionary that contains the information concerning whether any of several elements have been changed.  Chose a dictionary more to test the idea that I could create a simple method that would update the dictionary and here is an example:

    ``EVT_TEXT(self, self.name.GetId(), lambda e: self.modified.update({'name':1}))``

So this lambda function means that if an ``EVT_TEXT`` event occurs then update the dictionary by adding the key to the dictionary (the value is not used and arbitrarily set to 1).  The wxPython form for the macro ``EVT_TEXT`` is:

    ``EVT_TEXT(window, id, func)``

A ``wxEVT_COMMAND_TEXT_UPDATED`` event is generated when the text in a ``wxTextCtrl`` changes and that is what ``EVT_TEXT`` catches. Note that this event will always be sent when the text control’s content changes - whether this is due to user input or comes programmatically (for example, if ``SetValue()`` is called)

self.Cursors
    dictionary that holds the database cursor objects.  For example, it will look like:  ``{'sqlite':&lt;sqlite cursor object&gt;,'nycpsltszatz':&lt;mysql cursor object&gt;}``

self.tickler_active
    booean determines whether the tickler capabililty is active; can be shut off by unchecking Tickler menu item

self.editor
    list that holds the dictionaries that describe the notes that are edited by the external text editor::

        [
        {
        'table': 'mine',
        'host': 'wxLMDB:sqlite',
        'path': 'C:\\DOCUME~1\\STEVEN~1\\LOCALS~1\\Temp\\Journal Scan schedule.txt',
        'id': '1AB34FB9-9EE6-4AFC-8AF0-FFCA50103BF3',
        'time': 1070850894
        }, 
        {
        'table': 'factoids',
        'host': 'wxLMDB:sqlite',
        'path': 'C:\\DOCUME~1\\STEVEN~1\\LOCALS~1\\Temp\\How many cme programs are sponsored- - 91%.txt', 
        'id': '9CAC4D18-DE1C-4535-B9A5-4CDB1AD3F304', 
        'time': 1070850908
        }
        ]

The method that uses self.editor is `&lt;&lt; Check if Edited File has Changed &gt;&gt;`_.

There is a ``wxPanel`` in the ``AddListControl`` method so each ``wxListCtrl`` has a different panel as parent.

There is a nb_sizer = wxNotebookSizer(nb) class but doesn't seem to make any difference.

self.sqlite_connections
    Here because the sqlite connection has a weakreference that deletes it when you want it around

self.popupvisible
    boolean that is used to ensure that two reminder popups aren't visible at the same time.

self.in_place_editor 
    boolean that indicates whether the inplace item name text editor is active or not.

self.showrecentcompleted
    integer that determines the number of days in the past to retain completed items in the display.

self.LC_font
    default font for all of the ``ListCtrls``:  ``self.LC_font = wxFont(9, wxSWISS, wxNORMAL, wxNORMAL)``

The wxPython ``wxFont`` constructor is:

    ``wxFont(pointSize, family, style, weight, underline=False, faceName="", wencoding=wxFONTENCODING_DEFAULT)``

self.date_titles
    dictionary that holds the various dates that are associated with each item and which can be displayed in the date column.  The dictionary is not modified.  We use one column of each ``ListCtrl`` to display any one of the four dates that that the application tracks. This dictionary associates the item attribute with the text that will be displayed in both the column header for the date and in the dropdown that allows you to change the date:  ``self.date_titles = {'createdate':"Create Date",'duedate':"Due Date",'timestamp':"Last Modified",'finisheddate':"Completion Date"}``

self.attr2col_num
    dictionary that associates the item attribute with the column that attribute is displayed in in the ``ListCtrl``:  ``self.attr2col_num = {'priority':0, 'name':1,'owners':2, 'date':3}``

The following lines construct the Find Dialog and the Dialog that catches errors and shows expressions for debugging::

    self.FindDialog = FindDialog(self, "Find...", "")
    self.EvalDialog = EvalDialog(self, "Evaluate...", "")</t>
<t tx="szatz.20031220093529">@ignore
@nocolor
@wrap

+------------------------+------------------------------------------------+
|**File Menu**           |                                                |
+------------------------+------------------------------------------------+
| "New List... "         ||nl| ``self.OnNewList``                         |
+------------------------+------------------------------------------------+
| "Open List..."         ||ol| ``self.OnOpenList``                        |
+------------------------+------------------------------------------------+
| "Close"                |``self.OnCloseList``                            |
+------------------------+------------------------------------------------+
| "Close All"            |``self.OnCloseAll``                             |
+------------------------+------------------------------------------------+
| "Save As Text File..." |``self.OnSaveAsText``                           |
+------------------------+------------------------------------------------+
| "Delete List..."       ||de| ``self.OnDeleteList``                      |
+------------------------+------------------------------------------------+
| "Page Setup..."        ||ps| ``self.OnPageSetup``                       |
+------------------------+------------------------------------------------+
| "Print..."             ||pt| ``self.OnPrint``                           |
+------------------------+------------------------------------------------+
| "Print Preview"        ||pp| ``lambda e: self.OnPrint(e, prev=True)``   |
+------------------------+------------------------------------------------+
| "Mail..."              |``self.OnMailView``                             |
+------------------------+------------------------------------------------+
| "Work Offline"         |``self.OnWorkOffline``                          |
+------------------------+------------------------------------------------+
| "Exit"                 |``self.OnExit``                                 |
+------------------------+------------------------------------------------+
| **Edit Menu**          |                                                |
+------------------------+------------------------------------------------+
| "Cut" [Ctrl+X ]        ||ec| ``lambda e: self.OnCopyItems(e, cut=True)``|
+------------------------+------------------------------------------------+
| "Copy" [Ctrl+C]        ||ey| ``self.OnCopyItems``                       |
+------------------------+------------------------------------------------+
| "Paste" [Ctrl+V]       ||ep| ``self.OnPasteItems``                      |
+------------------------+------------------------------------------------+
| "Delete"               ||de| ``self.OnDeleteItems``                     |
+------------------------+------------------------------------------------+
| "Combine Items..."     |``self.OnCombineItems``                         |
+------------------------+------------------------------------------------+
| "Find..."              ||fi| ``self.OnFind``                            |
+------------------------+------------------------------------------------+
| **Item Menu**          |                                                |
+------------------------+------------------------------------------------+
| "New Item"             ||ni| ``self.OnNewItem``                         |
+------------------------+------------------------------------------------+
| "Toggle Finished"      ||co| ``self.OnToggleFinished``                  |
+------------------------+------------------------------------------------+
| "Owner..."             ||ow| ``self.OnEditOwner``                       |
+------------------------+------------------------------------------------+
| "Due Date..."          ||dd| ``self.OnDueDate``                         |
+------------------------+------------------------------------------------+
| "Note..."              ||en| ``self.OnEditNote``                        |
+------------------------+------------------------------------------------+
| "Mail..."              ||mi| ``self.OnMailItem``                        |
+------------------------+------------------------------------------------+
| **Display Menu**       |                                                |
+------------------------+------------------------------------------------+
| "Show/Hide Finished..."|``self.OnShowFinished``                         |
+------------------------+------------------------------------------------+
| "Show All"             |``self.OnShowAll``                              |
+------------------------+------------------------------------------------+
| "Refresh Display"      ||re| ``self.OnRefresh``                         |
+------------------------+------------------------------------------------+
|"Select Date to Display"|``self.OnDisplayDateCategory``                  |
+------------------------+------------------------------------------------+
| **Tool Menu**          |                                                |
+------------------------+------------------------------------------------+
| "Tickler Active"       |``self.OnActivateTickler``                      |
+------------------------+------------------------------------------------+
| "Show Next Reminder"   |``self.OnShowTickler``                          |
+------------------------+------------------------------------------------+
| "Synchronize ..."      |``self.OnSync``                                 |
+------------------------+------------------------------------------------+
| "Archive completed..." |``self.OnArchive``                              |
+------------------------+------------------------------------------------+
| "Evaluate expression"  |``self.OnShowEvaluate``                         |
+------------------------+------------------------------------------------+
| **Help Menu**          |                                                |
+------------------------+------------------------------------------------+
| "About ListManager"    |``self.OnShowAbout``                            |
+------------------------+------------------------------------------------+
| "Help"                 |``self.OnShowHelp``                             |
+------------------------+------------------------------------------------+




</t>
<t tx="szatz.20031220175801">@ignore
@nocolor
@wrap

Nothing unusual in what follows:  we start with the module imports, setting some global constants including Menu Ids and read the ListManager.ini file.</t>
<t tx="szatz.20031220175801.1">@ignore
@nocolor
@wrap

os
    uses ``os.getcwd``, ``os.path.split``, ``os.chdir``, ``os.path.join``, ``os.path.getmtime``, ``os.startfile``, ``os.environ``

time
    uses ``time.sleep``, ``time.asctime``

pickle
    used to serialize data that is moved from Outlook to ListManager via sockets.  

socket
    as noted above, a socket is opened between Outlook and ListManager to move messages back and forth

select
    ListManager selects on the socket to see if there is a message that has been queued by Outlook

random
    used by the reminder popup to select messages

ConfigParser
    not surprisingly, using ConfiParser to parse the ListManager.ini file.  

threading
    more for fun than absolute necessity, a thread is opened on starting the program that constructs the list of owners for items.  In theory, if the datasize and number of Lists were large enough it could delay the appearance of the GUI and its initial responsiveness if we didn't construct the ownerlist in a thread.  On the other hand, it really let me play with threads and with creating a custom event that signalled the construction of the owner list to the main thread by posting a custom event.

re
    mainly using ``re.sub('[\\/:*"&lt;&gt;|\?]','-',f)`` to make sure that files are constructed only with legal characters.  Also searching the body text of nodes using re because it allows case insensitive searches through ``re.compile(pat, re.I)``.

pywintypes.CreateGuid
    probably should use pure python GUID that is in ASPN cookbook but it was easiest to just use the Windows GUID function.  Thank you Mark Hammond for win32all.

win32com.client.Dispatch
    used when launching Outlook to send email messages

win32api
    using win32api.GetUserName() in case there is no user name in the ini file or no ini file

MySQLdb
    using Andy Dustman's python extension module to connect to mysql back-end.

sqlite
    using  D. Richard Hipp's python extension to connect to local sqlite databases

import mx.DateTime
    using Marc-André Lemburg's mx.DateTime for dealing with datetime stuff in the databases

CalendarDialog, ModifierDialog, TicklerDialog, MailDialog,LoggerDialog, FinishedDialog, FindDialog, EvalDialog, TreeDialog, StartupDialog
   should just import LMDialogs and then access each dialog class by LM.WhateverDialog

printout.PrintTable
    There was an existing wxPython print module for printing from tables that I have modified to print Lists.

*#from win32com.client import constants*
    probably not wise but since the app only needs two constants from this module, just set the directly.  If MSFT decides to change the api, this is not good.

    

</t>
<t tx="szatz.20031220180151">@ignore
@nocolor
@wrap

ListManager is the main class in the application and is a sublass of ``wxFrame``, which is typical for a wxPython application.  From a GUI standpoint, the main child window of the ListManager object is a ``wxNoteBook`` object that holds one ``wxListCtrl`` per notebook page and one ``wxListBox``.  The ``wxListCtrl``\s display item information (e.g., name of the item, owners of the item, etc.) for a particular List and the ``wxListBox``\es displays a list of owners that is used to filter the items displayed by the ``wxListCtrl`` object.

Each ``wxListCtrl`` object has its own set of events that it is hooked to (see CreateNewNotebookPage`&lt;&lt; ListControl Events &gt;&gt;`_.
</t>
<t tx="szatz.20031220180725">@ignore
@nocolor
@wrap

The following two global constants are needed to create emails through Outlook via COM::

    olMailItem = 0x0
    olFlagMarked = 0x2

For some reason, it seemed easier to just include them explicitly rather than worrying about generating all the Outlook constants in order to use early binding.  I supppose if MSFT changes the api, that would be a problem.

</t>
<t tx="szatz.20031220182141">@ignore
@nocolor
@wrap

Menu Ids -- not much more to say although there should be something to say.

</t>
<t tx="szatz.20031220182540">@ignore
@nocolor
@wrap

.. sidebar:: A typical *List Manager.ini* file:

    ::

        [Files]
        path0 = wxLMDB:sqlite:mine
        path1 = nycpsszatzsql:mysql:follow_ups
        
        [Database]
        db = listmanager
        
        [Note]
        ext = txt
        
        [Synchronization]
        sync2 = follow_ups
	sync1 = test
        
        [Hosts]
        remote = nycpsszatzsql:mysql
        local = wxLMDB:sqlite
        
        [User]
        startup_dialog = true
        user = szatz
        pw = python
        
        [Mail]
	outlook = true
	path = wxLMDB:sqlite:mail_transfer
        
        [Configuration]
        y = 642
        x = 975

Application uses the ``ConfigParser`` module ito parse the ini file.  Unfortunately, ``ConfigParser`` doesn't work exactly like I think it should although it has been improved in 2.3.  My main issue is in the handling of default options.  The default options specified through the constructor show up in every section.  For example, if you use the items(*section*) method
then in addition to returning a list of tuples with whatever option/value pairs exist in the section, the list will include all the default option/value pairs, which does not make a whole lot of sense to me.  At the least, there should be a 'nodefaults' argument whose default was *False* but which could be set to *True*.  The following methods should have this option:

- items
- options
- has_option

In any event, because a nodefaults option does not exist, I create the ConfigParser object twice -- once with default options and once without them.  

The application will work fine if there is no ini file. In an effort to save some typing but not be too obscure, many of the options are read such that they default to the correct value either through explicit defaults in the constructor or statements that evaluate to *None* or *False*.

    ``QUICK_LIST = cp.has_option('User','quicklist') and cp.get('User','quicklist') or None``
    
    ``OUTLOOK = cp.has_option('Mail','outlook') and cp.getboolean('Mail,'outlook')``





    </t>
<t tx="szatz.20031220184411">@ignore
@nocolor
@wrap

.. image:: images\toolbar.gif

+------+-----------------------+-----------------------------------------------+
||nl|  | Creates a new List    |``self.OnNewList``                             |                  
+------+-----------------------+-----------------------------------------------+
||ol|  | Open an existing List |``self.OnOpenList``                            |
+------+-----------------------+-----------------------------------------------+
||pt|  | Print                 |``lambda e: self.OnPrint(e,showprtdlg=False))``|
+------+-----------------------+-----------------------------------------------+
||pp|  | Print Preview         |``lambda e: self.OnPrint(e, prev=True))``      |
+------+-----------------------+-----------------------------------------------+
||ps|  | Page Setup            |``self.OnPageSetup``                           |
+------+-----------------------+-----------------------------------------------+
||ni|  | New Item              |``self.OnNewItem``                             |
+------+-----------------------+-----------------------------------------------+
||re|  | Refresh               |``self.OnRefresh``                             |
+------+-----------------------+-----------------------------------------------+
||en|  | Edit Note             |``self.OnEditNote``                            |
+------+-----------------------+-----------------------------------------------+
||fi|  | Find                  |``self.OnFind``                                |
+------+-----------------------+-----------------------------------------------+
||ec|  | Cut                   |``lambda e: self.OnCopyItems(e, cut=True))``   |
+------+-----------------------+-----------------------------------------------+
||ey|  | Copy                  |``self.OnCopyItems``                           |
+------+-----------------------+-----------------------------------------------+
||ep|  | Paste                 |``self.OnPasteItems``                          |
+------+-----------------------+-----------------------------------------------+
||co|  | Toggle Finished       |``self.OnToggleFinished``                      |
+------+-----------------------+-----------------------------------------------+
||de|  | Delete Item           |``self.OnDeleteItems``                         |
+------+-----------------------+-----------------------------------------------+
||dd|  | Set Item Due Date     |``self.OnDueDate``                             |
+------+-----------------------+-----------------------------------------------+
||ow|  | Set Item Owners       |``self.OnEditOwner``                           |
+------+-----------------------+-----------------------------------------------+
||mi|  | Mail Item             |``self.OnMailItem``                            |
+------+-----------------------+-----------------------------------------------+


.. |nl| image:: images\newlist.gif
.. |ol| image:: images\openlist.gif
.. |pt| image:: images\print.gif
.. |pp| image:: images\printprev.gif
.. |ps| image:: images\page_setup.gif
.. |ni| image:: images\new_item.gif
.. |re| image:: images\refresh.gif
.. |en| image:: images\edit_note.gif
.. |fi| image:: images\find.gif
.. |ec| image:: images\editcut.gif
.. |ey| image:: images\editcopy.gif
.. |ep| image:: images\editpaste.gif
.. |co| image:: images\complete.gif
.. |de| image:: images\delete.gif
.. |dd| image:: images\duedate.gif
.. |ow| image:: images\owners.gif
.. |mi| image:: images\mail_item.gif
</t>
<t tx="szatz.20031221191501">@ignore
@nocolor
@wrap

(see toolbar table above)

Note about the use of lambda functions in ``EVT`` macros:

    When an event occurs, it sends an event object to the method that the event is connected to through the ``EVT`` macro.  If we want to have several ``EVT`` macros connect to the same method with different arguments then one way to accomplish that is to use a lambda function.  Instead of calling the a method directly with the event object as an argument, the lambda function will be called (with the event object argument) and the action of the lambda function will be to call the method in question with whatever additional arguments are specified.  For example::

        EVT_MENU(self, idPRINTPREV, lambda e: self.OnPrint(e, prev=True))

    The method ``OnPrint`` is the callback for both the Print event and the Print Preview event and by using a lambda function, we can pass an additional argument to the method (in this case setting the additional argument *prev* to True).

Printing by clicking the print icon in the Toolbar |pt| doesn't bring up a dialog it just prints to the default printer and that is why there is a separate ``EVT_TOOL`` macro with an ``idTOOLPRINT`` id::

    EVT_TOOL(self, idTOOLPRINT, lambda e: self.OnPrint(e,showprtdlg=False))</t>
<t tx="szatz.20031222222555">@ignore
@nocolor
@wrap

The ``wxFrame`` has two ``wxPanels``:  *upper_panel* will contain the notebook.  The *bottom_panel* will contain the various item textctrls including name, owners and note.

</t>
<t tx="szatz.20031222222939">@ignore
@nocolor
@wrap

The ``EVT_TEXT`` event macros indicate whether a particular textctrl has changed.

``EVT_CLOSE(self, self.OnWindowExit)`` is used to record settings and cleanup on exiting

``EVT_IDLE(self, self.OnIdle)`` --&gt; Idle events used for checking text files and transfers from Outlook

There are also a number of events related to the individual ListCtrls that are placed on Notebook pages `&lt;&lt; ListControl Events &gt;&gt;`_.


    
</t>
<t tx="szatz.20031224183942">@ignore
@nocolor
@wrap

.. Not sure I have the strength but will discuss sizers here.  The basis use of sizers is pretty straightforward in my opinion, however, what is wildly hard to remember are the meaning of the parameters in the sizer Add method.

The parent of the ``wxPanel`` object *upper_panel* is the *ListManager*, which is a subclass of ``wxFrame``.  The parent of ``wxNotebook`` object *nb* is *upper_panel*.  Since the only child of *upper_panel* is the *nb* it wasn't obvious to me that a sizer was needed but apparently without it the ``wxListCtrl`` that will be a child of the wxPanel of *nb* won't size right if we don't do it this was.

::

    Frame ---&gt; upper_panel ---&gt; notebook ---&gt; panel (for each page) ---&gt; listctrl
      |                                                             |            } one on each page
      ---&gt; lower_panel ---&gt; variety of textctrls                    ---&gt; listbox</t>
<t tx="szatz.20031224184335">@ignore
@nocolor
@wrap

No comments yet.</t>
<t tx="szatz.20031224184335.1">@ignore
@nocolor
@wrap

No comment</t>
<t tx="szatz.20031225203754">@ignore
@nocolor
@wrap

This method grabs the owners from many of the tables to create a list of possile owners for each item.  The alternative is actually to create a separate owner table but it seemed to make sense to just construct the owners on the fly from the various List databases.  This is done in a thread so no matter how long it takes to construct the owners it doesn't slow the appearance of the GUI.  The result of this method is the contruction of the instance variable *self._list*.

The most interesting thing here is creating a custom event (without needing to create an event macro) to signal that this thread is done::
    
    evt = wxPyEvent()
    evt_id = wxNewEventType()
    evt.SetEventType(evt_id)
    self.Connect(-1, -1, evt_id, self.createownerdialog)
    wxPostEvent(self, evt)

The code above is adapted from the more general ``wxCallAfter``, which I could have used but just wanted to explicitly show the steps involved in creating a custom event, associating it with a callback and posting it.   

The general point is that if you want to notify the main GUI thread of something going on in a non-GUI thread, posting events is an easy way to do it whether you use the code above, the more complete wxCallAfter (see below) from which it was derived or actually create your own custom event macro (farther below).

The code for ``wxCallAfter`` is::

    def wxCallAfter(callable, *args, **kw):
         """
         Call the specified function after the current and pending event
         handlers have been completed.  This is also good for making GUI
         method calls from non-GUI threads.
         """
         app = wxGetApp()
         assert app, 'No wxApp created yet'
    
         global _wxCallAfterId
         if _wxCallAfterId is None:
             _wxCallAfterId = wxNewEventType()
             app.Connect(-1, -1, _wxCallAfterId,
                   lambda event: event.callable(*event.args, **event.kw) )
         evt = wxPyEvent()
         evt.SetEventType(_wxCallAfterId)
         evt.callable = callable
         evt.args = args
         evt.kw = kw
         wxPostEvent(app, evt)

Unless you want multiple handlers to be able to respond to a custom event (by using evt.Skip()) or just want custom event macros that are like native event macros there doesn't seem to be much need to create full-blown custom events.  If you do need to, here is how it is done::

    wxEVT_THREAD_DONE = wxNewEventType()
    
    def EVT_THREAD_DONE(win, func):
        win.Connect(-1, -1, wxEVT_THREAD_DONE, func)
        
    class ThreadDoneEvent(wxPyEvent):
        def __init__(self):
            wxPyEvent.__init__(self)
            self.SetEventType(wxEVT_THREAD_DONE)
            
When you want to post the custom event, you do the following::
    
    evt = ThreadDoneEvent() 
    wxPostEvent(win, evt)</t>
<t tx="szatz.20031225203839">@ignore
@nocolor
@wrap

No comment.</t>
<t tx="szatz.20031227163020">@ignore
@nocolor
@wrap

When the thread is done that creates the owner list it posts an event whose callback is this method.  This method uses *self._list* that was generated by the ``createownerlist`` method.  As an alternative, we could probably pass the list as an attribute of the event that is generated in the thread.</t>
<t tx="szatz.20031227163901">@ignore
@nocolor
@wrap


This method toggles whether we are working offline only or both on and offline.</t>
<t tx="szatz.20031227164126">@ignore
@nocolor
@wrap

This method creates the ListCtrl and ListBox that appears on every notebook page.

Each list has a properties dictionary associated with it.

+--------------+----------------------------------------------------------------------------------------------------+
|'owner'       |The owner that is filtering the display or '\*ALL'                                                  |
+--------------+----------------------------------------------------------------------------------------------------+
|'LCdate'      |Date that is displayed by the ListCtrl; values: 'duedate', 'createdate', 'timestamp', 'finisheddate'|
+--------------+----------------------------------------------------------------------------------------------------+
|'sort'        |Value is a dictionary of the form {'attribute':'priority','direction':0}                            |
+--------------+----------------------------------------------------------------------------------------------------+
|'showfinished'|Values: -1 show them all; 0 show none; integer show for that many days                              |
+--------------+----------------------------------------------------------------------------------------------------+
|'table'       |The table that holds the List                                                                       |
+--------------+----------------------------------------------------------------------------------------------------+
|'host'        |The form for this is 'nycpsszatzsql:mysql' or 'wxLMDB:sqlite'                                       |
+--------------+----------------------------------------------------------------------------------------------------+

</t>
<t tx="szatz.20031228000508">ID_TIMER = wxNewId()
self.timer = wxTimer(self, ID_TIMER) 
EVT_TIMER(self,  ID_TIMER, self.OnIdle)
self.timer.Start(3000)
</t>
<t tx="szatz.20031228001152">@ignore
@nocolor
@wrap

Need to figure out exactly what this timer is doing.</t>
<t tx="szatz.20031228092655">@ignore
@nocolor
@wrap

It would seem that the two mouse events:  ``EVT_LEFT_DOWN(LCtrl, self.OnLeftDown)`` and ``EVT_LEFT_DCLICK(LCtrl, self.OnLeftDown)`` could be owned by the ListManager object and not each ListCtrl object, but when I tried this, the mouse events were not detected. I did not investigate this for long so maybe I was just screwing things up or perhaps a ``wxFrame`` cannot detect a mousedown event (does that make sense?).  In any event (no pun intended), it is certainly not a big deal to create these mousedown events for each ListCtrl.

Each ListBox object has one event associated with it that occurs, not surprisingly, when a name in the control is selected.  The callback, *self.OnFilterOwners*, causes the ListCtrl to display only the items of the selected owner.</t>
<t tx="szatz.20031228133237">@ignore
@nocolor
@wrap

The list is sorted by the ListBox control.

mysql doesn't like '%s' while sqlite is fine with '%s' for table names.

If you don't do ``OLBox.Clear()`` then you get a blank line in the list that must be from initiating it with "".

Relying on the fact that '\*All' should be first alphabetically, which is dumb so should change it.</t>
<t tx="szatz.20031228134301">@ignore
@nocolor
@wrap



Call-back for the ``EVT_NOTEBOOK_PAGE_CHANGED(self,nb.GetId(),self.OnPageChange)`` event.

self.modified is the dictionary that indicates which textctrls have data that has changed.

.. note::

    Not sure whether event.Skip() is needed or not.</t>
<t tx="szatz.20031228134301.1">@ignore
@nocolor
@wrap

</t>
<t tx="szatz.20031228134301.2">@ignore
@nocolor
@wrap

</t>
<t tx="szatz.20031229001241">@ignore
@nocolor
@wrap


First thing, when are idle events triggered? --&gt; in wxPython they occur after there are no more events to process in the event queue.  So when things are happening rapidly (such as mouse movement) you actually see more idle events and when nothing at all is happening, you don't see any.  This is pretty counterintuitive initially since you would think that idle events should happen when things are truly idle.</t>
<t tx="szatz.20031229001241.1">@ignore
@nocolor 
@wrap


The select statement always confused me a bit so I'll come back to this to provide some more documentation of what is going on here.

The emails identified in Outlook are moved to ListManager through a socket connection.  There is a separate Outlook addin that allows the user to highlight an Outlook email message and click on a button in the Outlook toolbar to move the message into ListManager.  I find that many tasks originate or are documented in an email and this makes it very convenient to move those items from Outlook into ListManager without any retyping.
</t>
<t tx="szatz.20031229002108">@ignore
@nocolor
@wrap

This section in the OnIdle method checks to see if there are any open editors that have had a change in data in a manner that is similar to the code in Leo that works with the Leo Open With menu item.

.. note::

    After the line::
    
        cursor.execute("UPDATE "+table+" SET note = %s WHERE id = %s", (note,id))
        
    normally the next line would be:: 
    
    	item.timestamp = self.TimeStamper(host, cursor, table, id)
    
    however, this is actually a bit tricky since by the time the editor is closed the user may be on a different notebook page and you'd have to go from id --&gt; self.ItemLists[L][idx] to update the display so it didn't seem like it was worth the effort.  This means that the display timestamp doesn't automatically update when the external editor is closed but will update if the display is refreshed. 
</t>
<t tx="szatz.20031230210944"></t>
<t tx="szatz.20031230213102"></t>
<t tx="szatz.20031230213102.1"></t>
<t tx="szatz.20031230213149">@ignore
@nocolor
@wrap

``pathlist = [f[1] for f in cp.items('Files')]``

This uses the new in 2.3 ``ConfigParser`` method ``items``.  This will not work unless ConfigParser has been constructed without any defaults and so a ConfigParser object is created twice.

The only remotely subtle thing here is that we don't want to execute the ``EVT_NOTEBOOK_PAGE_CHANGED`` statement while we're doing the initial loading of files since it does unnecessary processing.  The statement is executed before the last file is loaded.</t>
<t tx="szatz.20031230214130">@ignore
@nocolor
@wrap

The main method here is the one that constructs a new Notebook page by creating a new ListCtrl and new OwnerListBox and populating them.  The second method does what is needed when an existing notebook page is selected.

</t>
<t tx="szatz.20031231232809"></t>
<t tx="szatz.20031231232809.1">@ignore
@nocolor
@wrap

    </t>
</tnodes>
</leo_file>
