2 # -*- coding: utf-8 -*-
4 from __future__ import with_statement
7 This file is part of Multilist.
9 Multilist is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Multilist is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Multilist. If not, see <http://www.gnu.org/licenses/>.
22 Copyright (C) 2008 Christoph Würstle
51 import libliststorehandler
61 _moduleLogger = logging.getLogger(__name__)
62 PROFILE_STARTUP = False
65 class Multilist(hildonize.get_app_class()):
67 _user_data = os.path.join(os.path.expanduser("~"), ".%s" % constants.__app_name__)
68 _user_settings = "%s/settings.ini" % _user_data
71 super(Multilist, self).__init__()
72 self._clipboard = gtk.clipboard_get()
74 logging.info('Starting Multilist')
77 os.makedirs(self._user_data)
82 self.db = libspeichern.Speichern()
83 self.window_in_fullscreen = False #The window isn't in full screen mode initially.
84 self.__isLandscape = True
86 #Haupt vbox für alle Elemente
87 self.window = gtk.Window()
88 self.__settingsWindow = None
89 self.__settingsManager = None
90 self.vbox = gtk.VBox(homogeneous = False, spacing = 0)
92 self.selection = libselection.Selection(self.db, isHildon)
93 self._search = search.Search()
94 self.liststorehandler = libliststorehandler.Liststorehandler(self.db, self.selection)
95 self.view = libview.View(self.db, self.liststorehandler, self.window)
96 self.bottombar = libbottombar.Bottombar(self.db, self.view, isHildon)
99 if hildonize.GTK_MENU_USED:
100 fileMenu = gtk.Menu()
102 menu_items = gtk.MenuItem(_("Choose database file"))
103 menu_items.connect("activate", self._on_select_db, None)
104 fileMenu.append(menu_items)
106 menu_items = gtk.MenuItem(_("SQL history"))
107 menu_items.connect("activate", self._on_view_sql_history, None)
108 fileMenu.append(menu_items)
110 menu_items = gtk.MenuItem(_("SQL optimize"))
111 menu_items.connect("activate", self._on_optimize_sql, None)
112 fileMenu.append(menu_items)
114 menu_items = gtk.MenuItem(_("Sync items"))
115 menu_items.connect("activate", self.sync_notes, None)
116 fileMenu.append(menu_items)
118 menu_items = gtk.MenuItem(_("Quit"))
119 menu_items.connect("activate", self._on_destroy, None)
120 fileMenu.append(menu_items)
122 fileMenuItem = gtk.MenuItem(_("File"))
124 fileMenuItem.set_submenu(fileMenu)
126 listmenu = gtk.Menu()
128 menu_items = gtk.MenuItem(_("Search"))
129 menu_items.connect("activate", self._on_toggle_search)
130 listmenu.append(menu_items)
132 menu_items = gtk.MenuItem(_("Checkout All"))
133 menu_items.connect("activate", self._on_checkout_all)
134 listmenu.append(menu_items)
136 menu_items = gtk.MenuItem(_("Rename List"))
137 menu_items.connect("activate", self.bottombar.rename_list, None)
138 listmenu.append(menu_items)
140 menu_items = gtk.MenuItem(_("Rename Category"))
141 menu_items.connect("activate", self.bottombar.rename_category, None)
142 listmenu.append(menu_items)
144 listMenuItem = gtk.MenuItem(_("List"))
146 listMenuItem.set_submenu(listmenu)
148 viewMenu = gtk.Menu()
150 menu_items = gtk.MenuItem(_("Show Active"))
151 menu_items.connect("activate", self._on_toggle_filter, None)
152 viewMenu.append(menu_items)
154 menu_items = gtk.MenuItem(_("Settings"))
155 menu_items.connect("activate", self._on_settings, None)
156 viewMenu.append(menu_items)
158 viewMenuItem = gtk.MenuItem(_("View"))
160 viewMenuItem.set_submenu(viewMenu)
162 helpMenu = gtk.Menu()
163 menu_items = gtk.MenuItem(_("About"))
164 helpMenu.append(menu_items)
165 menu_items.connect("activate", self._on_about, None)
167 helpMenuItem = gtk.MenuItem(_("Help"))
169 helpMenuItem.set_submenu(helpMenu)
171 menuBar = gtk.MenuBar()
173 menuBar.append (fileMenuItem)
174 menuBar.append (listMenuItem)
175 menuBar.append (viewMenuItem)
176 # unten -> damit als letztes menuBar.append (helpMenuItem)
178 menuBar.append (helpMenuItem)
180 self.vbox.pack_start(menuBar, False, False, 0)
182 menuBar = gtk.MenuBar()
184 self.vbox.pack_start(menuBar, False, False, 0)
186 #add to vbox below (to get it on top)
187 self.vbox.pack_end(self._search, expand = False, fill = True)
188 self.vbox.pack_end(self.bottombar, expand = False, fill = True, padding = 0)
189 self.vbox.pack_end(self.view, expand = True, fill = True, padding = 0)
190 self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
192 #Get the Main Window, and connect the "destroy" event
193 self.window.add(self.vbox)
195 self.window = hildonize.hildonize_window(self, self.window)
196 hildonize.set_application_title(self.window, "%s" % constants.__pretty_app_name__)
197 menuBar = hildonize.hildonize_menu(
201 if hildonize.IS_FREMANTLE_SUPPORTED:
202 button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, None)
203 button.set_label("All")
204 menuBar.add_filter(button)
205 button.connect("clicked", self._on_click_menu_filter, self.liststorehandler.SHOW_ALL)
206 button.set_mode(False)
209 button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, filterGroup)
210 button.set_label("Active")
211 menuBar.add_filter(button)
212 button.connect("clicked", self._on_click_menu_filter, self.liststorehandler.SHOW_ACTIVE)
213 button.set_mode(False)
215 renameListButton= gtk.Button(_("Rename List"))
216 renameListButton.connect("clicked", self.bottombar.rename_list)
217 menuBar.append(renameListButton)
219 renameCategoryButton = gtk.Button(_("Rename Category"))
220 renameCategoryButton.connect("clicked", self.bottombar.rename_category)
221 menuBar.append(renameCategoryButton)
223 searchButton= gtk.Button(_("Search Category"))
224 searchButton.connect("clicked", self._on_toggle_search)
225 menuBar.append(searchButton)
227 searchButton= gtk.Button(_("Settings"))
228 searchButton.connect("clicked", self._on_settings)
229 menuBar.append(searchButton)
233 if not hildonize.IS_HILDON_SUPPORTED:
234 _moduleLogger.info("No hildonization support")
237 self.osso_c = osso.Context(
238 constants.__app_name__,
239 constants.__version__,
243 _moduleLogger.info("No osso support")
246 self.window.connect("delete_event", self._on_delete_event)
247 self.window.connect("destroy", self._on_destroy)
248 self.window.connect("key-press-event", self.on_key_press)
249 self.window.connect("window-state-event", self._on_window_state_change)
250 self._search.connect("search_changed", self._on_search)
252 self.window.show_all()
254 self._prepare_sync_dialog()
256 def _toggle_search(self):
257 if self._search.get_property("visible"):
262 def set_orientation(self, orientation):
263 if orientation == gtk.ORIENTATION_VERTICAL:
264 hildonize.window_to_portrait(self.window)
265 self.bottombar.set_orientation(gtk.ORIENTATION_VERTICAL)
266 self.selection.set_orientation(gtk.ORIENTATION_VERTICAL)
267 self.__isLandscape = False
268 elif orientation == gtk.ORIENTATION_HORIZONTALt :
269 hildonize.window_to_landscape(self.window)
270 self.bottombar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
271 self.selection.set_orientation(gtk.ORIENTATION_HORIZONTAL)
272 self.__isLandscape = True
274 raise NotImplementedError(orientation)
276 def get_orientation(self):
277 return gtk.ORIENTATION_HORIZONTAL if self.__isLandscape else gtk.ORIENTATION_VERTICAL
279 def _toggle_rotate(self):
280 if self.__isLandscape:
281 self.set_orientation(gtk.ORIENTATION_VERTICAL)
283 self.set_orientation(gtk.ORIENTATION_HORIZONTAL)
285 @gtk_toolbox.log_exception(_moduleLogger)
286 def _on_checkout_all(self, widget):
287 self.liststorehandler.checkout_rows()
289 @gtk_toolbox.log_exception(_moduleLogger)
290 def _on_search(self, widget):
291 self.liststorehandler.get_liststore(self._search.get_search_pattern())
293 @gtk_toolbox.log_exception(_moduleLogger)
294 def _on_click_menu_filter(self, button, val):
295 self.liststorehandler.set_filter(val)
297 @gtk_toolbox.log_exception(_moduleLogger)
298 def _on_toggle_search(self, *args):
299 self._toggle_search()
301 @gtk_toolbox.log_exception(_moduleLogger)
302 def _on_toggle_filter(self, *args):
303 if self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ALL:
304 self.liststorehandler.set_filter(self.liststorehandler.SHOW_ACTIVE)
305 elif self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ACTIVE:
306 self.liststorehandler.set_filter(self.liststorehandler.SHOW_ALL)
308 assert False, "Unknown"
310 @gtk_toolbox.log_exception(_moduleLogger)
311 def on_key_press(self, widget, event, *args):
312 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
313 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
315 event.keyval == gtk.keysyms.F6 or
316 event.keyval in RETURN_TYPES and isCtrl
318 # The "Full screen" hardware key has been pressed
319 if self.window_in_fullscreen:
320 self.window.unfullscreen ()
322 self.window.fullscreen ()
324 elif event.keyval == gtk.keysyms.r and isCtrl:
325 self._toggle_rotate()
327 elif event.keyval == gtk.keysyms.f and isCtrl:
328 self._toggle_search()
331 event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
332 event.get_state() & gtk.gdk.CONTROL_MASK
334 self.window.destroy()
335 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
336 with open(constants._user_logpath_, "r") as f:
337 logLines = f.xreadlines()
338 log = "".join(logLines)
339 self._clipboard.set_text(str(log))
342 @gtk_toolbox.log_exception(_moduleLogger)
343 def _on_window_state_change(self, widget, event, *args):
344 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
345 self.window_in_fullscreen = True
347 self.window_in_fullscreen = False
349 @gtk_toolbox.log_exception(_moduleLogger)
350 def _on_sync_finished(self, data = None, data2 = None):
351 self.selection.comboList_changed()
352 self.selection.comboCategory_changed()
353 self.liststorehandler.update_list()
355 def _prepare_sync_dialog(self):
356 self.sync_dialog = gtk.Dialog(_("Sync"), None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
358 self.sync_dialog.set_position(gtk.WIN_POS_CENTER)
359 sync = libsync.Sync(self.db, self.window, 50503)
360 sync.connect("syncFinished", self._on_sync_finished)
361 self.sync_dialog.vbox.pack_start(sync, True, True, 0)
362 self.sync_dialog.set_size_request(500, 350)
363 self.sync_dialog.vbox.show_all()
365 @gtk_toolbox.log_exception(_moduleLogger)
366 def sync_notes(self, widget = None, data = None):
367 if self.sync_dialog is None:
368 self._prepare_sync_dialog()
369 self.sync_dialog.run()
370 self.sync_dialog.hide()
372 @gtk_toolbox.log_exception(_moduleLogger)
373 def _on_settings(self, *args):
374 if self.__settingsWindow is None:
376 self.__settingsManager = settings.SettingsDialog(vbox, self.db, self.liststorehandler)
378 self.__settingsWindow = gtk.Window()
379 self.__settingsWindow.add(vbox)
380 self.__settingsWindow = hildonize.hildonize_window(self, self.__settingsWindow)
382 self.__settingsWindow.set_title(_("Settings"))
383 self.__settingsWindow.set_transient_for(self.window)
384 self.__settingsWindow.set_default_size(*self.window.get_size())
385 self.__settingsWindow.connect("delete-event", self._on_settings_delete)
386 self.__settingsWindow.set_modal(True)
387 self.__settingsWindow.show_all()
389 @gtk_toolbox.log_exception(_moduleLogger)
390 def _on_settings_delete(self, *args):
391 self.__settingsWindow.emit_stop_by_name("delete-event")
392 self.__settingsWindow.hide()
393 self.__settingsWindow.set_modal(False)
395 logging.info("changing columns")
396 self.__settingsManager.save(self.db)
397 self.view.reload_view()
401 @gtk_toolbox.log_exception(_moduleLogger)
402 def _on_destroy(self, widget = None, data = None):
407 except AttributeError:
408 pass # Either None or close was removed (in Fremantle)
412 @gtk_toolbox.log_exception(_moduleLogger)
413 def _on_delete_event(self, widget, event, data = None):
416 @gtk_toolbox.log_exception(_moduleLogger)
417 def _on_view_sql_history(self, widget = None, data = None, data2 = None):
418 sqldiag = sqldialog.SqlDialog(self.db)
423 if res != gtk.RESPONSE_OK:
425 logging.info("exporting sql")
428 dlg = gtk.FileChooserDialog(
429 parent = self.window,
430 action = gtk.FILE_CHOOSER_ACTION_SAVE
432 dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
433 dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
435 dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
437 dlg.set_title(_("Select SQL export file"))
438 exportFileResponse = dlg.run()
440 if exportFileResponse == gtk.RESPONSE_OK:
441 fileName = dlg.get_filename()
442 sqldiag.exportSQL(fileName)
448 @gtk_toolbox.log_exception(_moduleLogger)
449 def _on_optimize_sql(self, widget = None, data = None, data2 = None):
451 self.db.speichereSQL("VACUUM", log = False)
453 @gtk_toolbox.log_exception(_moduleLogger)
454 def _on_select_db(self, widget = None, data = None, data2 = None):
455 dlg = hildon.FileChooserDialog(parent=self._window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
457 if self.db.ladeDirekt('datenbank'):
458 dlg.set_filename(self.db.ladeDirekt('datenbank'))
459 dlg.set_title(_("Choose your database file"))
462 if resp == gtk.RESPONSE_OK:
463 fileName = dlg.get_filename()
464 self.db.speichereDirekt('datenbank', fileName)
469 @gtk_toolbox.log_exception(_moduleLogger)
470 def _on_about(self, widget = None, data = None):
471 dialog = gtk.AboutDialog()
472 dialog.set_position(gtk.WIN_POS_CENTER)
473 dialog.set_name(constants.__pretty_app_name__)
474 dialog.set_version(constants.__version__)
475 dialog.set_copyright("")
476 dialog.set_website("http://axique.de/f = Multilist")
477 comments = "%s is a program to handle multiple lists." % constants.__pretty_app_name__
478 dialog.set_comments(comments)
479 dialog.set_authors(["Christoph Wurstle <n800@axique.net>", "Ed Page <eopage@byu.net> (Blame him for the most recent bugs)"])
485 if hildonize.IS_HILDON_SUPPORTED:
486 gtk.set_application_name(constants.__pretty_app_name__)
488 if not PROFILE_STARTUP:
492 if __name__ == "__main__":
493 logging.basicConfig(level = logging.DEBUG)