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