38eb7108500a12c17a903ef6c82922595384bc47
[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 sqldialog
45 import libselection
46 import libview
47 import libliststorehandler
48 import libsync
49 import libbottombar
50
51 try:
52         _
53 except NameError:
54         _ = lambda x: x
55
56
57 _moduleLogger = logging.getLogger(__name__)
58 PROFILE_STARTUP = False
59
60
61 class Multilist(hildonize.get_app_class()):
62
63         _user_data = os.path.join(os.path.expanduser("~"), ".%s" % constants.__app_name__)
64         _user_settings = "%s/settings.ini" % _user_data
65
66         def __init__(self):
67                 super(Multilist, self).__init__()
68                 self._clipboard = gtk.clipboard_get()
69
70                 logging.info('Starting Multilist')
71
72                 try:
73                         os.makedirs(self._user_data)
74                 except OSError, e:
75                         if e.errno != 17:
76                                 raise
77
78                 self.db = libspeichern.Speichern()
79                 self.window_in_fullscreen = False #The window isn't in full screen mode initially.
80
81                 #Haupt vbox für alle Elemente
82                 self.window = gtk.Window()
83                 self.vbox = gtk.VBox(homogeneous = False, spacing = 0)
84
85                 self.selection = libselection.Selection(self.db, isHildon)
86                 self.liststorehandler = libliststorehandler.Liststorehandler(self.db, self.selection)
87                 self.view = libview.View(self.db, self.liststorehandler, self.window)
88                 self.bottombar = libbottombar.Bottombar(self.db, self.view, isHildon)
89
90                 #Menue
91                 if hildonize.GTK_MENU_USED:
92                         fileMenu = gtk.Menu()
93
94                         menu_items = gtk.MenuItem(_("Choose database file"))
95                         menu_items.connect("activate", self.select_db_dialog, None)
96                         fileMenu.append(menu_items)
97
98                         menu_items = gtk.MenuItem(_("SQL history"))
99                         menu_items.connect("activate", self.view_sql_history, None)
100                         fileMenu.append(menu_items)
101
102                         menu_items = gtk.MenuItem(_("SQL optimize"))
103                         menu_items.connect("activate", self.optimizeSQL, None)
104                         fileMenu.append(menu_items)
105
106                         menu_items = gtk.MenuItem(_("Sync items"))
107                         menu_items.connect("activate", self.sync_notes, None)
108                         fileMenu.append(menu_items)
109
110                         menu_items = gtk.MenuItem(_("Quit"))
111                         menu_items.connect("activate", self.destroy, None)
112                         fileMenu.append(menu_items)
113
114                         fileMenuItem = gtk.MenuItem(_("File"))
115                         fileMenuItem.show()
116                         fileMenuItem.set_submenu(fileMenu)
117
118                         viewMenu = gtk.Menu()
119
120                         menu_items = gtk.MenuItem(_("Show Active"))
121                         menu_items.connect("activate", self._on_toggle_filter, None)
122                         viewMenu.append(menu_items)
123
124                         viewMenuItem = gtk.MenuItem(_("View"))
125                         viewMenuItem.show()
126                         viewMenuItem.set_submenu(viewMenu)
127
128                         toolsMenu = gtk.Menu()
129
130                         menu_items = gtk.MenuItem(_("Choose columns"))
131                         menu_items.connect("activate", self.show_columns_dialog, None)
132                         toolsMenu.append(menu_items)
133
134                         menu_items = gtk.MenuItem(_("Rename Category"))
135                         menu_items.connect("activate", self.bottombar.rename_category, None)
136                         toolsMenu.append(menu_items)
137
138                         menu_items = gtk.MenuItem(_("Rename List"))
139                         menu_items.connect("activate", self.bottombar.rename_list, None)
140                         toolsMenu.append(menu_items)
141
142                         toolsMenuItem = gtk.MenuItem(_("Tools"))
143                         toolsMenuItem.show()
144                         toolsMenuItem.set_submenu(toolsMenu)
145
146                         helpMenu = gtk.Menu()
147                         menu_items = gtk.MenuItem(_("About"))
148                         helpMenu.append(menu_items)
149                         menu_items.connect("activate", self.show_about, None)
150
151                         helpMenuItem = gtk.MenuItem(_("Help"))
152                         helpMenuItem.show()
153                         helpMenuItem.set_submenu(helpMenu)
154
155                         menu_bar = gtk.MenuBar()
156                         menu_bar.show()
157                         menu_bar.append (fileMenuItem)
158                         menu_bar.append (toolsMenuItem)
159                         menu_bar.append (viewMenuItem)
160                         # unten -> damit als letztes menu_bar.append (helpMenuItem)
161                         #Als letztes menü
162                         menu_bar.append (helpMenuItem)
163
164                         self.vbox.pack_start(menu_bar, False, False, 0)
165                 else:
166                         menuBar = gtk.MenuBar()
167                         menuBar.show()
168                         self.vbox.pack_start(menuBar, False, False, 0)
169
170                 #add to vbox below (to get it on top)
171                 self.vbox.pack_end(self.bottombar, expand = False, fill = True, padding = 0)
172                 self.vbox.pack_end(self.view, expand = True, fill = True, padding = 0)
173                 self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
174
175                 #Get the Main Window, and connect the "destroy" event
176                 self.window.add(self.vbox)
177
178                 self.window = hildonize.hildonize_window(self, self.window)
179                 hildonize.set_application_title(self.window, "%s" % constants.__pretty_app_name__)
180                 menu_bar = hildonize.hildonize_menu(
181                         self.window,
182                         menu_bar,
183                 )
184                 if hildonize.IS_FREMANTLE_SUPPORTED:
185                         pass
186
187                 if not hildonize.IS_HILDON_SUPPORTED:
188                         _moduleLogger.info("No hildonization support")
189
190                 if osso is not None:
191                         self.osso_c = osso.Context(
192                                 constants.__app_name__,
193                                 constants.__version__,
194                                 False
195                         )
196                 else:
197                         _moduleLogger.info("No osso support")
198                         self._osso_c = None
199
200                 self.window.connect("delete_event", self.delete_event)
201                 self.window.connect("destroy", self.destroy)
202                 self.window.connect("key-press-event", self.on_key_press)
203                 self.window.connect("window-state-event", self.on_window_state_change)
204
205                 self.window.show_all()
206                 self.prepare_sync_dialog()
207                 self.ladeAlles()
208
209         @gtk_toolbox.log_exception(_moduleLogger)
210         def _on_toggle_filter(self, *args):
211                 if self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ALL:
212                         self.liststorehandler.set_filter(self.liststorehandler.SHOW_ACTIVE)
213                 elif self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ACTIVE:
214                         self.liststorehandler.set_filter(self.liststorehandler.SHOW_ALL)
215                 else:
216                         assert False, "Unknown"
217
218         @gtk_toolbox.log_exception(_moduleLogger)
219         def on_key_press(self, widget, event, *args):
220                 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
221                 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
222                 if (
223                         event.keyval == gtk.keysyms.F6 or
224                         event.keyval in RETURN_TYPES and isCtrl
225                 ):
226                         # The "Full screen" hardware key has been pressed 
227                         if self.window_in_fullscreen:
228                                 self.window.unfullscreen ()
229                         else:
230                                 self.window.fullscreen ()
231                         return True
232                 #elif event.keyval == gtk.keysyms.f and isCtrl:
233                 #       self._toggle_search()
234                 #       return True
235                 elif (
236                         event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
237                         event.get_state() & gtk.gdk.CONTROL_MASK
238                 ):
239                         self.window.destroy()
240                 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
241                         with open(constants._user_logpath_, "r") as f:
242                                 logLines = f.xreadlines()
243                                 log = "".join(logLines)
244                                 self._clipboard.set_text(str(log))
245                         return True
246
247         @gtk_toolbox.log_exception(_moduleLogger)
248         def on_window_state_change(self, widget, event, *args):
249                 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
250                         self.window_in_fullscreen = True
251                 else:
252                         self.window_in_fullscreen = False
253
254         def speichereAlles(self, data = None, data2 = None):
255                 logging.info("Speichere alles")
256
257         def ladeAlles(self, data = None, data2 = None):
258                 logging.info("Lade alles")
259
260         def beforeSync(self, data = None, data2 = None):
261                 logging.info("Lade alles")
262
263         @gtk_toolbox.log_exception(_moduleLogger)
264         def sync_finished(self, data = None, data2 = None):
265                 self.selection.comboList_changed()
266                 self.selection.comboCategory_changed()
267                 self.liststorehandler.update_list()
268
269         def prepare_sync_dialog(self):
270                 self.sync_dialog = gtk.Dialog(_("Sync"), None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
271
272                 self.sync_dialog.set_position(gtk.WIN_POS_CENTER)
273                 sync = libsync.Sync(self.db, self.window, 50503)
274                 sync.connect("syncFinished", self.sync_finished)
275                 self.sync_dialog.vbox.pack_start(sync, True, True, 0)
276                 self.sync_dialog.set_size_request(500, 350)
277                 self.sync_dialog.vbox.show_all()
278
279         @gtk_toolbox.log_exception(_moduleLogger)
280         def sync_notes(self, widget = None, data = None):
281                 if self.sync_dialog == None:
282                         self.prepare_sync_dialog()
283                 self.sync_dialog.run()
284                 self.sync_dialog.hide()
285
286         @gtk_toolbox.log_exception(_moduleLogger)
287         def show_columns_dialog(self, widget = None, data = None):
288                 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))
289
290                 col_dialog.set_position(gtk.WIN_POS_CENTER)
291                 cols = libview.Columns_dialog(self.db, self.liststorehandler)
292
293                 col_dialog.vbox.pack_start(cols, True, True, 0)
294                 col_dialog.set_size_request(500, 350)
295                 col_dialog.vbox.show_all()
296
297                 resp = col_dialog.run()
298                 col_dialog.hide()
299                 if resp == gtk.RESPONSE_ACCEPT:
300                         logging.info("changing columns")
301                         cols.save_column_setting()
302                         self.view.reload_view()
303                         #children = self.vbox.get_children()
304                         #while len(children)>1:
305                         #       self.vbox.remove(children[1])
306
307                         #self.vbox.pack_end(self.bottombar, expand = True, fill = True, padding = 0)
308                         #self.vbox.pack_end(view, expand = True, fill = True, padding = 0)
309                         #self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
310
311                 col_dialog.destroy()
312
313         @gtk_toolbox.log_exception(_moduleLogger)
314         def destroy(self, widget = None, data = None):
315                 try:
316                         self.speichereAlles()
317                         self.db.close()
318                         try:
319                                 self._osso_c.close()
320                         except AttributeError:
321                                 pass # Either None or close was removed (in Fremantle)
322                 finally:
323                         gtk.main_quit()
324
325         @gtk_toolbox.log_exception(_moduleLogger)
326         def delete_event(self, widget, event, data = None):
327                 #print "delete event occurred"
328                 return False
329
330         def dlg_delete(self, widget, event, data = None):
331                 return False
332
333         @gtk_toolbox.log_exception(_moduleLogger)
334         def show_about(self, widget = None, data = None):
335                 dialog = gtk.AboutDialog()
336                 dialog.set_position(gtk.WIN_POS_CENTER)
337                 dialog.set_name(constants.__pretty_app_name__)
338                 dialog.set_version(constants.__version__)
339                 dialog.set_copyright("")
340                 dialog.set_website("http://axique.de/f = Multilist")
341                 comments = "%s is a program to handle multiple lists." % constants.__pretty_app_name__
342                 dialog.set_comments(comments)
343                 dialog.set_authors(["Christoph Wurstle <n800@axique.net>", "Ed Page <eopage@byu.net> (Blame him for the most recent bugs)"])
344                 dialog.run()
345                 dialog.destroy()
346
347         def on_info1_activate(self, menuitem):
348                 self.show_about(menuitem)
349
350         @gtk_toolbox.log_exception(_moduleLogger)
351         def view_sql_history(self, widget = None, data = None, data2 = None):
352                 sqldiag = sqldialog.SqlDialog(self.db)
353                 res = sqldiag.run()
354                 sqldiag.hide()
355
356                 try:
357                         if res != gtk.RESPONSE_OK:
358                                 return
359                         logging.info("exporting sql")
360
361                         if not isHildon:
362                                 dlg = gtk.FileChooserDialog(
363                                         parent = self.window,
364                                         action = gtk.FILE_CHOOSER_ACTION_SAVE
365                                 )
366                                 dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
367                                 dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
368                         else:
369                                 dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
370
371                         dlg.set_title(_("Select SQL export file"))
372                         exportFileResponse = dlg.run()
373                         try:
374                                 if exportFileResponse == gtk.RESPONSE_OK:
375                                         fileName = dlg.get_filename()
376                                         sqldiag.exportSQL(fileName)
377                         finally:
378                                 dlg.destroy()
379                 finally:
380                         sqldiag.destroy()
381
382         @gtk_toolbox.log_exception(_moduleLogger)
383         def optimizeSQL(self, widget = None, data = None, data2 = None):
384                 #optimiere sql
385                 self.db.speichereSQL("VACUUM", log = False)
386
387         @gtk_toolbox.log_exception(_moduleLogger)
388         def select_db_dialog(self, widget = None, data = None, data2 = None):
389                 if (isHildon == False):
390                         dlg = gtk.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
391                         dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
392                         dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
393                 else:
394                         #dlg = hildon.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
395                         dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
396
397                 if self.db.ladeDirekt('datenbank'):
398                         dlg.set_filename(self.db.ladeDirekt('datenbank'))
399                 dlg.set_title(_("Choose your database file"))
400                 if dlg.run() == gtk.RESPONSE_OK:
401                         fileName = dlg.get_filename()
402                         self.db.speichereDirekt('datenbank', fileName)
403                         self.speichereAlles()
404                         self.db.openDB()
405                         self.ladeAlles()
406                 dlg.destroy()
407
408
409 def run_multilist():
410         if hildonize.IS_HILDON_SUPPORTED:
411                 gtk.set_application_name(constants.__pretty_app_name__)
412         app = Multilist()
413         if not PROFILE_STARTUP:
414                 gtk.main()
415
416
417 if __name__ == "__main__":
418         logging.basicConfig(level = logging.DEBUG)
419         run_multilist()