1 from __future__
import generators
4 from rox
import g
, app_options
, options
, basedir
9 from Memo
import Memo
, memo_from_node
11 max_visible
= options
.Option('max_visible', 5)
12 max_future
= options
.Option('max_future', 6)
20 class MemoList(g
.ListStore
):
22 'MemoListChanged' : (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, [])
26 g
.ListStore
.__init
__(self
, gobject
.TYPE_STRING
, # Time
27 gobject
.TYPE_STRING
, # Brief
28 gobject
.TYPE_OBJECT
, # Memo
29 gobject
.TYPE_BOOLEAN
) # Deleted
32 "When used as a python iterator, return a list of TreeIters"
33 iter = self
.get_iter_first()
36 iter = self
.iter_next(iter)
38 def delete(self
, memo
, update
= 1):
40 dbus_notify
.close(memo
)
42 m
= self
.get_value(iter, MEMO
)
48 # Not found. That's OK.
51 "Recalculate the time display after midnight."
53 self
.set(memo
, TIME
, self
.get_value(memo
, MEMO
).str_when())
56 def add(self
, memo
, update
= 1):
57 assert isinstance(memo
, Memo
)
60 m
= self
.get_value(iter, MEMO
)
61 if m
.comes_after(memo
):
67 new
= self
.insert_before(iter)
73 TIME
, memo
.str_when(),
81 def notify_changed(self
):
82 "Called after a Memo is added, removed or updated."
83 self
.emit( "MemoListChanged" );
85 def get_memo_by_path(self
, path
):
86 iter = self
.get_iter(path
)
87 return self
.get_memo_by_iter(iter)
89 def get_memo_by_iter(self
, iter):
90 return self
.get_value(iter, MEMO
)
92 def catch_up(self
, early
= 0):
93 "Returns a list of alarms to go off, and the time until the "
94 "next alarm (in seconds) or None."
101 m
= self
.get_value(iter, MEMO
)
103 if m
.hidden
or not m
.at
or m
.state
== Memo
.DONE
:
107 earlyDelay
= delay
- (early
*60)
109 if (delay
<= 0) or (m
.state
== Memo
.READY
and earlyDelay
<= 0):
112 if earlyDelay
> 0 and (minDelay
is None or earlyDelay
< minDelay
):
113 minDelay
= earlyDelay
114 elif delay
> 0 and (minDelay
is None or delay
< minDelay
):
116 elif minDelay
is not None and \
117 earlyDelay
> minDelay
and delay
> minDelay
:
118 # Memos are sorted by delay time, so if you hit one that has both
119 # delay and early delay after the lowest registered delay time, you
121 # This will no longer be true if we allow custom per-memo early
122 # delay times. Which would be ugly.
125 return (missed
, minDelay
)
127 class MasterList(MemoList
):
129 MemoList
.__init
__(self
)
131 self
.visible
= MemoList()
133 path
= basedir
.load_first_config('rox.sourceforge.net', 'Memo', 'Entries')
136 from xml
.dom
import minidom
, Node
137 doc
= minidom
.parse(path
)
139 rox
.report_exception()
142 root
= doc
.documentElement
143 for node
in root
.getElementsByTagName('memo'):
145 memo
= memo_from_node(node
)
146 self
.add(memo
, update
= 0)
149 rox
.report_exception()
151 self
.update_visible()
152 app_options
.add_notify(self
.update_visible
)
155 MemoList
.new_day(self
)
156 self
.visible
.new_day()
158 def toggle_hidden(self
, path
):
159 if g
.pygtk_version
== (1, 99, 12):
160 iter = self
.get_iter_first()
161 self
.get_iter_from_string(iter, path
)
163 iter = self
.get_iter_from_string(path
)
165 memo
= self
.get_memo_by_iter(iter)
166 self
.set_hidden(memo
, not memo
.hidden
)
168 def set_hidden(self
, memo
, hidden
):
169 self
.delete(memo
, update
= 0)
170 memo
.set_hidden(hidden
)
174 save_dir
= basedir
.save_config_path('rox.sourceforge.net', 'Memo')
175 path
= os
.path
.join(save_dir
, 'Entries.new')
178 "Memo: Saving disabled by CHOICESPATH\n")
181 f
= os
.open(path
, os
.O_CREAT | os
.O_WRONLY
, 0600)
182 self
.save_to_stream(os
.fdopen(f
, 'w'))
184 real_path
= os
.path
.join(save_dir
, 'Entries')
185 os
.rename(path
, real_path
)
187 rox
.report_exception()
189 def save_to_stream(self
, stream
):
190 from xml
.dom
import minidom
191 doc
= minidom
.Document()
193 root
= doc
.createElement('memos')
194 doc
.appendChild(root
)
196 m
= self
.get_value(iter, MEMO
)
198 root
.appendChild(doc
.createTextNode('\n'))
201 def notify_changed(self
):
202 "Called after a Memo is added, removed or updated."
203 MemoList
.notify_changed(self
)
204 self
.update_visible()
207 def count_today(self
):
208 "Return the count of memos with today's date in a tuple: (all,hidden)"
209 # TODO: Could be more efficient and just return an int that is
210 # automatically kept up-to-date by all add/remove/new_day routines instead
211 # of counting all memos every time.
213 now
= datetime
.datetime
.now()
214 todayStart
= now
.replace(hour
=0, minute
=0, second
=0, microsecond
=0)
215 todayEnd
= now
.replace(hour
=23, minute
=59, second
=59, microsecond
=999999)
216 START_OF_TODAY
= time
.mktime(todayStart
.timetuple())
217 END_OF_TODAY
= time
.mktime(todayEnd
.timetuple())
221 memo
= self
.get_value(iter, MEMO
)
222 if memo
.time
< START_OF_TODAY
:
224 if memo
.time
> END_OF_TODAY
:
231 def choose_visible(self
):
232 "Return a list of Memos which should be made/kept visible."
236 VISIBLE_REGION
= A_DAY
* 31 * max_future
.int_value
240 memo
= self
.get_value(iter, MEMO
)
245 if memo
.time
> now
+ VISIBLE_REGION
:
246 break # Way too far ahead
248 if memo
.time
> now
+ A_DAY
:
249 # Skip future memos if the list is too long
250 if len(out
) >= max_visible
.int_value
:
257 def update_visible(self
):
258 # Find what was in the visible list
260 for iter in self
.visible
:
261 old_vis
[self
.visible
.get_value(iter, MEMO
)] = None
263 new_vis
= self
.choose_visible()
265 # Find what has changed
274 # Anything remaining was visible but isn't now
278 if not to_show
and not to_hide
:
283 self
.visible
.delete(m
, update
= 0)
285 self
.visible
.add(m
, update
= 0)
286 self
.visible
.notify_changed()
288 def warn_if_not_visible(self
, memo
):
291 for iter in self
.visible
:
292 m
= self
.visible
.get_value(iter, MEMO
)
295 rox
.info("This memo has been added, but is not shown in the main "
296 "window. Use Show All from the popup menu to see it.\n\n"
297 "You can use the Options window to control when memos "
298 "are shown in the main window.")