Minor cleanup
[multilist] / src / multilist_gtk.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 This file is part of Multilist.
6
7 Multilist is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Multilist is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Multilist.  If not, see <http://www.gnu.org/licenses/>.
19
20 Copyright (C) 2008 Christoph Würstle
21 """
22
23 import os
24 import logging
25
26 import gtk
27
28 try:
29         import hildon
30         isHildon = True
31 except:
32         isHildon = False
33
34 try:
35         import osso
36 except ImportError:
37         osso = None
38
39 import constants
40 import hildonize
41 import gtk_toolbox
42
43 import libspeichern
44 import search
45 import sqldialog
46 import libselection
47 import libview
48 import libliststorehandler
49 import libsync
50 import libbottombar
51
52 try:
53         _
54 except NameError:
55         _ = lambda x: x
56
57
58 _moduleLogger = logging.getLogger(__name__)
59 PROFILE_STARTUP = False
60
61
62 class Multilist(hildonize.get_app_class()):
63
64         _user_data = os.path.join(os.path.expanduser("~"), ".%s" % constants.__app_name__)
65         _user_settings = "%s/settings.ini" % _user_data
66
67         def __init__(self):
68                 super(Multilist, self).__init__()
69                 self._clipboard = gtk.clipboard_get()
70
71                 logging.info('Starting Multilist')
72
73                 try:
74                         os.makedirs(self._user_data)
75                 except OSError, e:
76                         if e.errno != 17:
77                                 raise
78
79                 self.db = libspeichern.Speichern()
80                 self.window_in_fullscreen = False #The window isn't in full screen mode initially.
81
82                 #Haupt vbox für alle Elemente
83                 self.window = gtk.Window()
84                 self.vbox = gtk.VBox(homogeneous = False, spacing = 0)
85
86                 self.selection = libselection.Selection(self.db, isHildon)
87                 self._search = search.Search()
88                 self.liststorehandler = libliststorehandler.Liststorehandler(self.db, self.selection)
89                 self.view = libview.View(self.db, self.liststorehandler, self.window)
90                 self.bottombar = libbottombar.Bottombar(self.db, self.view, isHildon)
91
92                 #Menue
93                 if hildonize.GTK_MENU_USED:
94                         fileMenu = gtk.Menu()
95
96                         menu_items = gtk.MenuItem(_("Choose database file"))
97                         menu_items.connect("activate", self.select_db_dialog, None)
98                         fileMenu.append(menu_items)
99
100                         menu_items = gtk.MenuItem(_("SQL history"))
101                         menu_items.connect("activate", self.view_sql_history, None)
102                         fileMenu.append(menu_items)
103
104                         menu_items = gtk.MenuItem(_("SQL optimize"))
105                         menu_items.connect("activate", self.optimizeSQL, None)
106                         fileMenu.append(menu_items)
107
108                         menu_items = gtk.MenuItem(_("Sync items"))
109                         menu_items.connect("activate", self.sync_notes, None)
110                         fileMenu.append(menu_items)
111
112                         menu_items = gtk.MenuItem(_("Quit"))
113                         menu_items.connect("activate", self.destroy, None)
114                         fileMenu.append(menu_items)
115
116                         fileMenuItem = gtk.MenuItem(_("File"))
117                         fileMenuItem.show()
118                         fileMenuItem.set_submenu(fileMenu)
119
120                         categorymenu = gtk.Menu()
121
122                         menu_items = gtk.MenuItem(_("Search"))
123                         categorymenu.append(menu_items)
124                         menu_items.connect("activate", self._on_toggle_search)
125
126                         menu_items = gtk.MenuItem(_("Checkout All"))
127                         categorymenu.append(menu_items)
128                         menu_items.connect("activate", self._on_checkout_all)
129
130                         category_menu = gtk.MenuItem(_("Category"))
131                         category_menu.show()
132                         category_menu.set_submenu(categorymenu)
133
134                         viewMenu = gtk.Menu()
135
136                         menu_items = gtk.MenuItem(_("Show Active"))
137                         menu_items.connect("activate", self._on_toggle_filter, None)
138                         viewMenu.append(menu_items)
139
140                         viewMenuItem = gtk.MenuItem(_("View"))
141                         viewMenuItem.show()
142                         viewMenuItem.set_submenu(viewMenu)
143
144                         toolsMenu = gtk.Menu()
145
146                         menu_items = gtk.MenuItem(_("Choose columns"))
147                         menu_items.connect("activate", self.show_columns_dialog, None)
148                         toolsMenu.append(menu_items)
149
150                         menu_items = gtk.MenuItem(_("Rename Category"))
151                         menu_items.connect("activate", self.bottombar.rename_category, None)
152                         toolsMenu.append(menu_items)
153
154                         menu_items = gtk.MenuItem(_("Rename List"))
155                         menu_items.connect("activate", self.bottombar.rename_list, None)
156                         toolsMenu.append(menu_items)
157
158                         toolsMenuItem = gtk.MenuItem(_("Tools"))
159                         toolsMenuItem.show()
160                         toolsMenuItem.set_submenu(toolsMenu)
161
162                         helpMenu = gtk.Menu()
163                         menu_items = gtk.MenuItem(_("About"))
164                         helpMenu.append(menu_items)
165                         menu_items.connect("activate", self.show_about, None)
166
167                         helpMenuItem = gtk.MenuItem(_("Help"))
168                         helpMenuItem.show()
169                         helpMenuItem.set_submenu(helpMenu)
170
171                         menu_bar = gtk.MenuBar()
172                         menu_bar.show()
173                         menu_bar.append (fileMenuItem)
174                         menu_bar.append (toolsMenuItem)
175                         menu_bar.append (category_menu)
176                         menu_bar.append (viewMenuItem)
177                         # unten -> damit als letztes menu_bar.append (helpMenuItem)
178                         #Als letztes menü
179                         menu_bar.append (helpMenuItem)
180
181                         self.vbox.pack_start(menu_bar, False, False, 0)
182                 else:
183                         menuBar = gtk.MenuBar()
184                         menuBar.show()
185                         self.vbox.pack_start(menuBar, False, False, 0)
186
187                 #add to vbox below (to get it on top)
188                 self.vbox.pack_end(self._search, expand = False, fill = True)
189                 self.vbox.pack_end(self.bottombar, expand = False, fill = True, padding = 0)
190                 self.vbox.pack_end(self.view, expand = True, fill = True, padding = 0)
191                 self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
192
193                 #Get the Main Window, and connect the "destroy" event
194                 self.window.add(self.vbox)
195
196                 self.window = hildonize.hildonize_window(self, self.window)
197                 hildonize.set_application_title(self.window, "%s" % constants.__pretty_app_name__)
198                 menu_bar = hildonize.hildonize_menu(
199                         self.window,
200                         menu_bar,
201                 )
202                 if hildonize.IS_FREMANTLE_SUPPORTED:
203                         button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, None)
204                         button.set_label("All")
205                         menuBar.add_filter(button)
206                         button.connect("clicked", self._on_click_menu_filter, self.liststorehandler.SHOW_ALL)
207                         button.set_mode(False)
208                         filterGroup = button
209
210                         button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, filterGroup)
211                         button.set_label("Active")
212                         menuBar.add_filter(button)
213                         button.connect("clicked", self._on_click_menu_filter, self.liststorehandler.SHOW_ACTIVE)
214                         button.set_mode(False)
215
216                         renameCategoryButton = gtk.Button(_("Rename Category"))
217                         renameCategoryButton.connect("clicked", self.bottombar.rename_category)
218                         menuBar.append(renameCategoryButton)
219
220                         renameListButton= gtk.Button(_("Rename List"))
221                         renameListButton.connect("clicked", self.bottombar.rename_list)
222                         menuBar.append(renameListButton)
223
224                         searchButton= gtk.Button(_("Search Category"))
225                         searchButton.connect("clicked", self._on_toggle_search)
226                         menuBar.append(searchButton)
227
228                         menuBar.show_all()
229
230                 if not hildonize.IS_HILDON_SUPPORTED:
231                         _moduleLogger.info("No hildonization support")
232
233                 if osso is not None:
234                         self.osso_c = osso.Context(
235                                 constants.__app_name__,
236                                 constants.__version__,
237                                 False
238                         )
239                 else:
240                         _moduleLogger.info("No osso support")
241                         self._osso_c = None
242
243                 self.window.connect("delete_event", self.delete_event)
244                 self.window.connect("destroy", self.destroy)
245                 self.window.connect("key-press-event", self.on_key_press)
246                 self.window.connect("window-state-event", self.on_window_state_change)
247                 self._search.connect("search_changed", self._on_search)
248
249                 self.window.show_all()
250                 self._search.hide()
251                 self.prepare_sync_dialog()
252                 self.ladeAlles()
253
254         @gtk_toolbox.log_exception(_moduleLogger)
255         def _on_checkout_all(self, widget):
256                 self.liststorehandler.checkout_rows()
257
258         @gtk_toolbox.log_exception(_moduleLogger)
259         def _on_search(self, widget):
260                 self.liststorehandler.get_liststore(self._search.get_search_pattern())
261
262         @gtk_toolbox.log_exception(_moduleLogger)
263         def _on_click_menu_filter(self, button, val):
264                 self.liststorehandler.set_filter(val)
265
266         def _toggle_search(self):
267                 if self._search.get_property("visible"):
268                         self._search.hide()
269                 else:
270                         self._search.show()
271
272         @gtk_toolbox.log_exception(_moduleLogger)
273         def _on_toggle_search(self, *args):
274                 self._toggle_search()
275
276         @gtk_toolbox.log_exception(_moduleLogger)
277         def _on_toggle_filter(self, *args):
278                 if self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ALL:
279                         self.liststorehandler.set_filter(self.liststorehandler.SHOW_ACTIVE)
280                 elif self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ACTIVE:
281                         self.liststorehandler.set_filter(self.liststorehandler.SHOW_ALL)
282                 else:
283                         assert False, "Unknown"
284
285         @gtk_toolbox.log_exception(_moduleLogger)
286         def on_key_press(self, widget, event, *args):
287                 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
288                 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
289                 if (
290                         event.keyval == gtk.keysyms.F6 or
291                         event.keyval in RETURN_TYPES and isCtrl
292                 ):
293                         # The "Full screen" hardware key has been pressed 
294                         if self.window_in_fullscreen:
295                                 self.window.unfullscreen ()
296                         else:
297                                 self.window.fullscreen ()
298                         return True
299                 elif event.keyval == gtk.keysyms.f and isCtrl:
300                         self._toggle_search()
301                         return True
302                 elif (
303                         event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
304                         event.get_state() & gtk.gdk.CONTROL_MASK
305                 ):
306                         self.window.destroy()
307                 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
308                         with open(constants._user_logpath_, "r") as f:
309                                 logLines = f.xreadlines()
310                                 log = "".join(logLines)
311                                 self._clipboard.set_text(str(log))
312                         return True
313
314         @gtk_toolbox.log_exception(_moduleLogger)
315         def on_window_state_change(self, widget, event, *args):
316                 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
317                         self.window_in_fullscreen = True
318                 else:
319                         self.window_in_fullscreen = False
320
321         def speichereAlles(self, data = None, data2 = None):
322                 logging.info("Speichere alles")
323
324         def ladeAlles(self, data = None, data2 = None):
325                 logging.info("Lade alles")
326
327         def beforeSync(self, data = None, data2 = None):
328                 logging.info("Lade alles")
329
330         @gtk_toolbox.log_exception(_moduleLogger)
331         def sync_finished(self, data = None, data2 = None):
332                 self.selection.comboList_changed()
333                 self.selection.comboCategory_changed()
334                 self.liststorehandler.update_list()
335
336         def prepare_sync_dialog(self):
337                 self.sync_dialog = gtk.Dialog(_("Sync"), None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
338
339                 self.sync_dialog.set_position(gtk.WIN_POS_CENTER)
340                 sync = libsync.Sync(self.db, self.window, 50503)
341                 sync.connect("syncFinished", self.sync_finished)
342                 self.sync_dialog.vbox.pack_start(sync, True, True, 0)
343                 self.sync_dialog.set_size_request(500, 350)
344                 self.sync_dialog.vbox.show_all()
345
346         @gtk_toolbox.log_exception(_moduleLogger)
347         def sync_notes(self, widget = None, data = None):
348                 if self.sync_dialog == None:
349                         self.prepare_sync_dialog()
350                 self.sync_dialog.run()
351                 self.sync_dialog.hide()
352
353         @gtk_toolbox.log_exception(_moduleLogger)
354         def show_columns_dialog(self, widget = None, data = None):
355                 col_dialog = gtk.Dialog(_("Choose columns"), self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
356
357                 col_dialog.set_position(gtk.WIN_POS_CENTER)
358                 cols = libview.Columns_dialog(self.db, self.liststorehandler)
359
360                 col_dialog.vbox.pack_start(cols, True, True, 0)
361                 col_dialog.set_size_request(500, 350)
362                 col_dialog.vbox.show_all()
363
364                 resp = col_dialog.run()
365                 col_dialog.hide()
366                 if resp == gtk.RESPONSE_ACCEPT:
367                         logging.info("changing columns")
368                         cols.save_column_setting()
369                         self.view.reload_view()
370                         #children = self.vbox.get_children()
371                         #while len(children)>1:
372                         #       self.vbox.remove(children[1])
373
374                         #self.vbox.pack_end(self.bottombar, expand = True, fill = True, padding = 0)
375                         #self.vbox.pack_end(view, expand = True, fill = True, padding = 0)
376                         #self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
377
378                 col_dialog.destroy()
379
380         @gtk_toolbox.log_exception(_moduleLogger)
381         def destroy(self, widget = None, data = None):
382                 try:
383                         self.speichereAlles()
384                         self.db.close()
385                         try:
386                                 self._osso_c.close()
387                         except AttributeError:
388                                 pass # Either None or close was removed (in Fremantle)
389                 finally:
390                         gtk.main_quit()
391
392         @gtk_toolbox.log_exception(_moduleLogger)
393         def delete_event(self, widget, event, data = None):
394                 #print "delete event occurred"
395                 return False
396
397         def dlg_delete(self, widget, event, data = None):
398                 return False
399
400         @gtk_toolbox.log_exception(_moduleLogger)
401         def show_about(self, widget = None, data = None):
402                 dialog = gtk.AboutDialog()
403                 dialog.set_position(gtk.WIN_POS_CENTER)
404                 dialog.set_name(constants.__pretty_app_name__)
405                 dialog.set_version(constants.__version__)
406                 dialog.set_copyright("")
407                 dialog.set_website("http://axique.de/f = Multilist")
408                 comments = "%s is a program to handle multiple lists." % constants.__pretty_app_name__
409                 dialog.set_comments(comments)
410                 dialog.set_authors(["Christoph Wurstle <n800@axique.net>", "Ed Page <eopage@byu.net> (Blame him for the most recent bugs)"])
411                 dialog.run()
412                 dialog.destroy()
413
414         def on_info1_activate(self, menuitem):
415                 self.show_about(menuitem)
416
417         @gtk_toolbox.log_exception(_moduleLogger)
418         def view_sql_history(self, widget = None, data = None, data2 = None):
419                 sqldiag = sqldialog.SqlDialog(self.db)
420                 res = sqldiag.run()
421                 sqldiag.hide()
422
423                 try:
424                         if res != gtk.RESPONSE_OK:
425                                 return
426                         logging.info("exporting sql")
427
428                         if not isHildon:
429                                 dlg = gtk.FileChooserDialog(
430                                         parent = self.window,
431                                         action = gtk.FILE_CHOOSER_ACTION_SAVE
432                                 )
433                                 dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
434                                 dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
435                         else:
436                                 dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
437
438                         dlg.set_title(_("Select SQL export file"))
439                         exportFileResponse = dlg.run()
440                         try:
441                                 if exportFileResponse == gtk.RESPONSE_OK:
442                                         fileName = dlg.get_filename()
443                                         sqldiag.exportSQL(fileName)
444                         finally:
445                                 dlg.destroy()
446                 finally:
447                         sqldiag.destroy()
448
449         @gtk_toolbox.log_exception(_moduleLogger)
450         def optimizeSQL(self, widget = None, data = None, data2 = None):
451                 #optimiere sql
452                 self.db.speichereSQL("VACUUM", log = False)
453
454         @gtk_toolbox.log_exception(_moduleLogger)
455         def select_db_dialog(self, widget = None, data = None, data2 = None):
456                 if (isHildon == False):
457                         dlg = gtk.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
458                         dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
459                         dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
460                 else:
461                         #dlg = hildon.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
462                         dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
463
464                 if self.db.ladeDirekt('datenbank'):
465                         dlg.set_filename(self.db.ladeDirekt('datenbank'))
466                 dlg.set_title(_("Choose your database file"))
467                 if dlg.run() == gtk.RESPONSE_OK:
468                         fileName = dlg.get_filename()
469                         self.db.speichereDirekt('datenbank', fileName)
470                         self.speichereAlles()
471                         self.db.openDB()
472                         self.ladeAlles()
473                 dlg.destroy()
474
475
476 def run_multilist():
477         if hildonize.IS_HILDON_SUPPORTED:
478                 gtk.set_application_name(constants.__pretty_app_name__)
479         app = Multilist()
480         if not PROFILE_STARTUP:
481                 gtk.main()
482
483
484 if __name__ == "__main__":
485         logging.basicConfig(level = logging.DEBUG)
486         run_multilist()