Added 'early alert' and sound capabilities. See Help/README for details
[memo.git] / Window.py
blobcfcbc7cfa8753069e1b3cc4cae914ae1d13abb26
1 import rox
2 from rox import g, TRUE, FALSE, app_options
3 from rox.Menu import Menu
4 from rox.options import Option
5 import gobject
7 import dbus_notify
8 import pretty_time, time
9 import timer
10 from Alarm import Alarm
12 time_format = Option('time_format', 'text')
13 main_sticky = Option('main_sticky', 1)
14 alert_early = Option('alert_early', 0)
16 menu = Menu('main', [
17 (_('/Add Memo...'), 'new_memo', ''),
18 (_('/Show All...'), 'show_all_memos', ''),
19 ('/', '', '<Separator>'),
20 (_('/Options...'), 'show_options',''),
21 (_('/Help'), 'help', ''),
22 (_('/Quit'), 'destroy', ''),
25 class Window(rox.Window):
26 drag_start = None
28 def __init__(self, memo_list):
29 rox.Window.__init__(self)
30 self.set_wmclass('Memo', 'Memo')
31 self.set_title('Memo')
32 self.set_resizable(False)
33 if hasattr(self, 'set_deletable'):
34 self.set_deletable(False)
35 #self.set_type_hint(g.gdk.WINDOW_TYPE_HINT_DIALOG)
37 self.tips = g.Tooltips()
39 if main_sticky.int_value:
40 self.stick()
42 self.memo_list = memo_list
43 self.last_day = None
44 self.prime_in_progress = False
46 vbox = g.VBox(FALSE, 0)
47 self.add(vbox)
49 hbox = g.HBox(False, 0)
50 vbox.pack_start(hbox, expand = False)
52 self.time_label = g.Label('')
53 self.time_button = g.Button()
54 self.time_button.add(self.time_label)
55 self.time_button.unset_flags(g.CAN_FOCUS)
56 hbox.pack_start(self.time_button, expand = True)
58 hbox.pack_start(timer.TimerButton(), expand = False)
60 self.list = g.TreeView(memo_list.visible)
61 vbox.pack_start(self.list, expand = TRUE)
62 self.list.unset_flags(g.CAN_FOCUS)
64 cell = g.CellRendererText()
65 column = g.TreeViewColumn('Time', cell, text = 0)
66 cell.set_property('xalign', 1)
67 self.list.append_column(column)
69 cell = g.CellRendererText()
70 column = g.TreeViewColumn('Message', cell, text = 1)
71 self.list.append_column(column)
73 self.list.set_headers_visible(FALSE)
75 sel = self.list.get_selection()
76 sel.set_mode(g.SELECTION_NONE)
78 def activate(view, path, column):
79 memo = memo_list.visible.get_memo_by_path(path)
80 from EditBox import EditBox
81 EditBox(memo).show()
83 self.add_events(g.gdk.BUTTON_PRESS_MASK)
84 self.list.connect('button-press-event', self.button_press)
85 self.list.connect('row-activated', activate)
86 self.time_button.add_events(g.gdk.BUTTON1_MOTION_MASK)
87 self.time_button.connect('button-press-event', self.button_press)
88 self.time_button.connect('motion-notify-event', self.button_motion)
89 self.time_button.connect('clicked', self.time_button_clicked)
91 menu.attach(self, self)
93 self.update()
94 gobject.timeout_add(10000, self.update) # Update clock
96 self.timeout = None # For next alarm
97 self.alert_box = None
98 self.show_all_box = None
99 self.save_box = None
100 self.prime()
102 # If we had more than one window, we'd need a remove too...
103 memo_list.watchers.append(self.prime)
104 app_options.add_notify(self.options_changed)
106 vbox.show_all()
108 def time_button_clicked(self, widget):
109 ev = g.get_current_event()
110 if ev.type == g.gdk.MOTION_NOTIFY and self.drag_start:
111 # Fake release from motion handler
112 self.begin_move_drag(1, self.drag_start[0], self.drag_start[1], ev.time)
113 self.drag_start = None
114 else:
115 self.new_memo()
117 def show_options(self):
118 rox.edit_options()
120 def options_changed(self):
121 if time_format.has_changed:
122 self.update()
124 if main_sticky.int_value:
125 self.stick()
126 else:
127 self.unstick()
129 def update(self):
130 if time_format.value == 'text':
131 text = pretty_time.rough_time(time.time())
132 self.tips.set_tip(self.time_button,
133 time.strftime('%H:%M %a %Y-%m-%d'))
134 else:
135 # Note: importing gtk breaks strftime for am/pm
136 text = time.strftime('%a %d-%b-%Y ') + \
137 pretty_time.str_time()
138 self.tips.set_tip(self.time_button, None)
139 self.time_label.set_text(text)
141 t = time.localtime()
142 year, month, day, hour, minute, second, weekday, julian, dst = t
143 if self.last_day != day:
144 if self.last_day is not None:
145 self.memo_list.new_day()
146 self.last_day = day
148 return TRUE
150 def new_memo(self, widget = None):
151 from EditBox import EditBox
152 EditBox().show()
154 def help(self):
155 from rox import filer
156 filer.open_dir(rox.app_dir + '/Help')
158 def button_press(self, widget, event):
159 if event.type != g.gdk.BUTTON_PRESS:
160 return
161 elif event.button == 2 or event.button == 3:
162 menu.popup(self, event)
163 return 1
164 self.drag_start = map(int, (event.x_root, event.y_root))
165 return 0
167 def button_motion(self, widget, mev):
168 if self.drag_start is None: return
169 pos = map(int, (mev.x_root, mev.y_root))
170 if self.time_button.drag_check_threshold(*(self.drag_start + pos)):
171 self.time_button.released()
172 if self.drag_start:
173 # Release event was ignored (outside the button)
174 self.time_button_clicked(widget)
176 def show_all_memos(self):
177 if self.show_all_box:
178 self.show_all_box.present()
179 return
180 def destroyed(widget): self.show_all_box = None
181 from ShowAll import ShowAll
182 self.show_all_box = ShowAll()
183 self.show_all_box.connect('destroy', destroyed)
184 self.show_all_box.show()
186 # Deal with any missed alarms
187 # Set a timeout for the next alarm
188 def prime(self):
189 if self.alert_box:
190 return # Don't do anything until closed
192 if self.prime_in_progress:
193 return # Make this method atomic
194 else:
195 self.prime_in_progress = True
197 missed, delay = self.memo_list.catch_up( alert_early.int_value )
198 if missed:
199 if dbus_notify.is_available():
200 for m in missed:
201 dbus_notify.notify(m)
202 else:
203 # Show the first one.
204 self.alert_box = Alarm(missed[0])
205 def destroyed(widget):
206 self.alert_box = None
207 self.prime()
208 self.alert_box.connect('destroy', destroyed)
209 g.gdk.beep()
210 g.gdk.flush()
211 time.sleep(0.3)
212 g.gdk.beep()
213 g.gdk.flush()
214 time.sleep(1)
215 self.alert_box.show()
216 if delay:
217 self.schedule(delay)
218 self.prime_in_progress = False
220 def timeout_cb(self):
221 gobject.source_remove(self.timeout)
222 self.timeout = 0
223 self.prime()
224 return 0
226 def schedule(self, delay):
227 if self.timeout:
228 gobject.source_remove(self.timeout)
230 # Avoid overflows - don't resched more than a day ahead
231 if delay > 60 * 60 * 24:
232 delay = 60 * 60 * 24
234 self.timeout = gobject.timeout_add(int(1000 * delay), self.timeout_cb)