Switching to quicknote style search
[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                         category_menu = gtk.MenuItem(_("Category"))
127                         category_menu.show()
128                         category_menu.set_submenu(categorymenu)
129
130                         viewMenu = gtk.Menu()
131
132                         menu_items = gtk.MenuItem(_("Show Active"))
133                         menu_items.connect("activate", self._on_toggle_filter, None)
134                         viewMenu.append(menu_items)
135
136                         viewMenuItem = gtk.MenuItem(_("View"))
137                         viewMenuItem.show()
138                         viewMenuItem.set_submenu(viewMenu)
139
140                         toolsMenu = gtk.Menu()
141
142                         menu_items = gtk.MenuItem(_("Choose columns"))
143                         menu_items.connect("activate", self.show_columns_dialog, None)
144                         toolsMenu.append(menu_items)
145
146                         menu_items = gtk.MenuItem(_("Rename Category"))
147                         menu_items.connect("activate", self.bottombar.rename_category, None)
148                         toolsMenu.append(menu_items)
149
150                         menu_items = gtk.MenuItem(_("Rename List"))
151                         menu_items.connect("activate", self.bottombar.rename_list, None)
152                         toolsMenu.append(menu_items)
153
154                         toolsMenuItem = gtk.MenuItem(_("Tools"))
155                         toolsMenuItem.show()
156                         toolsMenuItem.set_submenu(toolsMenu)
157
158                         helpMenu = gtk.Menu()
159                         menu_items = gtk.MenuItem(_("About"))
160                         helpMenu.append(menu_items)
161                         menu_items.connect("activate", self.show_about, None)
162
163                         helpMenuItem = gtk.MenuItem(_("Help"))
164                         helpMenuItem.show()
165                         helpMenuItem.set_submenu(helpMenu)
166
167                         menu_bar = gtk.MenuBar()
168                         menu_bar.show()
169                         menu_bar.append (fileMenuItem)
170                         menu_bar.append (toolsMenuItem)
171                         menu_bar.append (category_menu)
172                         menu_bar.append (viewMenuItem)
173                         # unten -> damit als letztes menu_bar.append (helpMenuItem)
174                         #Als letztes menü
175                         menu_bar.append (helpMenuItem)
176
177                         self.vbox.pack_start(menu_bar, False, False, 0)
178                 else:
179                         menuBar = gtk.MenuBar()
180                         menuBar.show()
181                         self.vbox.pack_start(menuBar, False, False, 0)
182
183                 #add to vbox below (to get it on top)
184                 self.vbox.pack_end(self._search, expand = False, fill = True)
185                 self.vbox.pack_end(self.bottombar, expand = False, fill = True, padding = 0)
186                 self.vbox.pack_end(self.view, expand = True, fill = True, padding = 0)
187                 self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
188
189                 #Get the Main Window, and connect the "destroy" event
190                 self.window.add(self.vbox)
191
192                 self.window = hildonize.hildonize_window(self, self.window)
193                 hildonize.set_application_title(self.window, "%s" % constants.__pretty_app_name__)
194                 menu_bar = hildonize.hildonize_menu(
195                         self.window,
196                         menu_bar,
197                 )
198                 if hildonize.IS_FREMANTLE_SUPPORTED:
199                         button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, None)
200                         button.set_label("All")
201                         menuBar.add_filter(button)
202                         button.connect("clicked", self._on_click_menu_filter, self.liststorehandler.SHOW_ALL)
203                         button.set_mode(False)
204                         filterGroup = button
205
206                         button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, filterGroup)
207                         button.set_label("Active")
208                         menuBar.add_filter(button)
209                         button.connect("clicked", self._on_click_menu_filter, self.liststorehandler.SHOW_ACTIVE)
210                         button.set_mode(False)
211
212                         renameCategoryButton = gtk.Button(_("Rename Category"))
213                         renameCategoryButton.connect("clicked", self.bottombar.rename_category)
214                         menuBar.append(renameCategoryButton)
215
216                         renameListButton= gtk.Button(_("Rename List"))
217                         renameListButton.connect("clicked", self.bottombar.rename_list)
218                         menuBar.append(renameListButton)
219
220                         searchButton= gtk.Button(_("Search Category"))
221                         searchButton.connect("clicked", self._on_toggle_search)
222                         menuBar.append(searchButton)
223
224                         menuBar.show_all()
225
226                 if not hildonize.IS_HILDON_SUPPORTED:
227                         _moduleLogger.info("No hildonization support")
228
229                 if osso is not None:
230                         self.osso_c = osso.Context(
231                                 constants.__app_name__,
232                                 constants.__version__,
233                                 False
234                         )
235                 else:
236                         _moduleLogger.info("No osso support")
237                         self._osso_c = None
238
239                 self.window.connect("delete_event", self.delete_event)
240                 self.window.connect("destroy", self.destroy)
241                 self.window.connect("key-press-event", self.on_key_press)
242                 self.window.connect("window-state-event", self.on_window_state_change)
243                 self._search.connect("search_changed", self._on_search)
244
245                 self.window.show_all()
246                 self._search.hide()
247                 self.prepare_sync_dialog()
248                 self.ladeAlles()
249
250         @gtk_toolbox.log_exception(_moduleLogger)
251         def _on_search(self, widget):
252                 self.liststorehandler.get_liststore(self._search.get_search_pattern())
253
254         @gtk_toolbox.log_exception(_moduleLogger)
255         def _on_click_menu_filter(self, button, val):
256                 self.liststorehandler.set_filter(val)
257
258         def _toggle_search(self):
259                 if self._search.get_property("visible"):
260                         self._search.hide()
261                 else:
262                         self._search.show()
263
264         @gtk_toolbox.log_exception(_moduleLogger)
265         def _on_toggle_search(self, *args):
266                 self._toggle_search()
267
268         @gtk_toolbox.log_exception(_moduleLogger)
269         def _on_toggle_filter(self, *args):
270                 if self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ALL:
271                         self.liststorehandler.set_filter(self.liststorehandler.SHOW_ACTIVE)
272                 elif self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ACTIVE:
273                         self.liststorehandler.set_filter(self.liststorehandler.SHOW_ALL)
274                 else:
275                         assert False, "Unknown"
276
277         @gtk_toolbox.log_exception(_moduleLogger)
278         def on_key_press(self, widget, event, *args):
279                 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
280                 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
281                 if (
282                         event.keyval == gtk.keysyms.F6 or
283                         event.keyval in RETURN_TYPES and isCtrl
284                 ):
285                         # The "Full screen" hardware key has been pressed 
286                         if self.window_in_fullscreen:
287                                 self.window.unfullscreen ()
288                         else:
289                                 self.window.fullscreen ()
290                         return True
291                 elif event.keyval == gtk.keysyms.f and isCtrl:
292                         self._toggle_search()
293                         return True
294                 elif (
295                         event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
296                         event.get_state() & gtk.gdk.CONTROL_MASK
297                 ):
298                         self.window.destroy()
299                 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
300                         with open(constants._user_logpath_, "r") as f:
301                                 logLines = f.xreadlines()
302                                 log = "".join(logLines)
303                                 self._clipboard.set_text(str(log))
304                         return True
305
306         @gtk_toolbox.log_exception(_moduleLogger)
307         def on_window_state_change(self, widget, event, *args):
308                 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
309                         self.window_in_fullscreen = True
310                 else:
311                         self.window_in_fullscreen = False
312
313         def speichereAlles(self, data = None, data2 = None):
314                 logging.info("Speichere alles")
315
316         def ladeAlles(self, data = None, data2 = None):
317                 logging.info("Lade alles")
318
319         def beforeSync(self, data = None, data2 = None):
320                 logging.info("Lade alles")
321
322         @gtk_toolbox.log_exception(_moduleLogger)
323         def sync_finished(self, data = None, data2 = None):
324                 self.selection.comboList_changed()
325                 self.selection.comboCategory_changed()
326                 self.liststorehandler.update_list()
327
328         def prepare_sync_dialog(self):
329                 self.sync_dialog = gtk.Dialog(_("Sync"), None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
330
331                 self.sync_dialog.set_position(gtk.WIN_POS_CENTER)
332                 sync = libsync.Sync(self.db, self.window, 50503)
333                 sync.connect("syncFinished", self.sync_finished)
334                 self.sync_dialog.vbox.pack_start(sync, True, True, 0)
335                 self.sync_dialog.set_size_request(500, 350)
336                 self.sync_dialog.vbox.show_all()
337
338         @gtk_toolbox.log_exception(_moduleLogger)
339         def sync_notes(self, widget = None, data = None):
340                 if self.sync_dialog == None:
341                         self.prepare_sync_dialog()
342                 self.sync_dialog.run()
343                 self.sync_dialog.hide()
344
345         @gtk_toolbox.log_exception(_moduleLogger)
346         def show_columns_dialog(self, widget = None, data = None):
347                 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))
348
349                 col_dialog.set_position(gtk.WIN_POS_CENTER)
350                 cols = libview.Columns_dialog(self.db, self.liststorehandler)
351
352                 col_dialog.vbox.pack_start(cols, True, True, 0)
353                 col_dialog.set_size_request(500, 350)
354                 col_dialog.vbox.show_all()
355
356                 resp = col_dialog.run()
357                 col_dialog.hide()
358                 if resp == gtk.RESPONSE_ACCEPT:
359                         logging.info("changing columns")
360                         cols.save_column_setting()
361                         self.view.reload_view()
362                         #children = self.vbox.get_children()
363                         #while len(children)>1:
364                         #       self.vbox.remove(children[1])
365
366                         #self.vbox.pack_end(self.bottombar, expand = True, fill = True, padding = 0)
367                         #self.vbox.pack_end(view, expand = True, fill = True, padding = 0)
368                         #self.vbox.pack_end(self.selection, expand = False, fill = True, padding = 0)
369
370                 col_dialog.destroy()
371
372         @gtk_toolbox.log_exception(_moduleLogger)
373         def destroy(self, widget = None, data = None):
374                 try:
375                         self.speichereAlles()
376                         self.db.close()
377                         try:
378                                 self._osso_c.close()
379                         except AttributeError:
380                                 pass # Either None or close was removed (in Fremantle)
381                 finally:
382                         gtk.main_quit()
383
384         @gtk_toolbox.log_exception(_moduleLogger)
385         def delete_event(self, widget, event, data = None):
386                 #print "delete event occurred"
387                 return False
388
389         def dlg_delete(self, widget, event, data = None):
390                 return False
391
392         @gtk_toolbox.log_exception(_moduleLogger)
393         def show_about(self, widget = None, data = None):
394                 dialog = gtk.AboutDialog()
395                 dialog.set_position(gtk.WIN_POS_CENTER)
396                 dialog.set_name(constants.__pretty_app_name__)
397                 dialog.set_version(constants.__version__)
398                 dialog.set_copyright("")
399                 dialog.set_website("http://axique.de/f = Multilist")
400                 comments = "%s is a program to handle multiple lists." % constants.__pretty_app_name__
401                 dialog.set_comments(comments)
402                 dialog.set_authors(["Christoph Wurstle <n800@axique.net>", "Ed Page <eopage@byu.net> (Blame him for the most recent bugs)"])
403                 dialog.run()
404                 dialog.destroy()
405
406         def on_info1_activate(self, menuitem):
407                 self.show_about(menuitem)
408
409         @gtk_toolbox.log_exception(_moduleLogger)
410         def view_sql_history(self, widget = None, data = None, data2 = None):
411                 sqldiag = sqldialog.SqlDialog(self.db)
412                 res = sqldiag.run()
413                 sqldiag.hide()
414
415                 try:
416                         if res != gtk.RESPONSE_OK:
417                                 return
418                         logging.info("exporting sql")
419
420                         if not isHildon:
421                                 dlg = gtk.FileChooserDialog(
422                                         parent = self.window,
423                                         action = gtk.FILE_CHOOSER_ACTION_SAVE
424                                 )
425                                 dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
426                                 dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
427                         else:
428                                 dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
429
430                         dlg.set_title(_("Select SQL export file"))
431                         exportFileResponse = dlg.run()
432                         try:
433                                 if exportFileResponse == gtk.RESPONSE_OK:
434                                         fileName = dlg.get_filename()
435                                         sqldiag.exportSQL(fileName)
436                         finally:
437                                 dlg.destroy()
438                 finally:
439                         sqldiag.destroy()
440
441         @gtk_toolbox.log_exception(_moduleLogger)
442         def optimizeSQL(self, widget = None, data = None, data2 = None):
443                 #optimiere sql
444                 self.db.speichereSQL("VACUUM", log = False)
445
446         @gtk_toolbox.log_exception(_moduleLogger)
447         def select_db_dialog(self, widget = None, data = None, data2 = None):
448                 if (isHildon == False):
449                         dlg = gtk.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
450                         dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
451                         dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
452                 else:
453                         #dlg = hildon.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
454                         dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
455
456                 if self.db.ladeDirekt('datenbank'):
457                         dlg.set_filename(self.db.ladeDirekt('datenbank'))
458                 dlg.set_title(_("Choose your database file"))
459                 if dlg.run() == gtk.RESPONSE_OK:
460                         fileName = dlg.get_filename()
461                         self.db.speichereDirekt('datenbank', fileName)
462                         self.speichereAlles()
463                         self.db.openDB()
464                         self.ladeAlles()
465                 dlg.destroy()
466
467
468 def run_multilist():
469         if hildonize.IS_HILDON_SUPPORTED:
470                 gtk.set_application_name(constants.__pretty_app_name__)
471         app = Multilist()
472         if not PROFILE_STARTUP:
473                 gtk.main()
474
475
476 if __name__ == "__main__":
477         logging.basicConfig(level = logging.DEBUG)
478         run_multilist()