Having note grab focus on search close
[quicknote] / src / notizen.py
1 #!/usr/bin/env python2.5
2 # -*- coding: utf-8 -*-
3
4 """
5  Copyright (C) 2007 Christoph Würstle
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
10
11 @todo It would be nice to move the category selector to the category list's column and search below everything.
12 @todo Search would be activated by menu or CTRL+F rather than zoom
13 """
14
15 import time
16 import logging
17 import uuid
18
19 import gobject
20 import gtk
21 try:
22         import gtkspell
23 except ImportError:
24         gtkspell = None
25
26 import hildonize
27 import gtk_toolbox
28 import simple_list
29
30
31 try:
32         _
33 except NameError:
34         _ = lambda x: x
35
36
37 _moduleLogger = logging.getLogger("notizen")
38
39
40 class Notizen(gtk.HBox):
41
42         def __init__(self, db, category, search):
43                 self._db = db
44                 self._category = category
45                 self._search = search
46                 self._search.connect("hide", self._on_search_close)
47                 self.noteId = -1
48                 self._pos = -1
49                 self._noteBody = None #Last notetext
50                 self._categoryName = ""
51
52                 gtk.HBox.__init__(self, homogeneous = False, spacing = 0)
53                 _moduleLogger.info("libnotizen, init")
54
55                 # Note list
56                 self._noteslist = simple_list.SimpleList()
57                 self._noteslist.set_eventfunction_cursor_changed(self._update_noteslist)
58                 self._noteslist.widget.set_size_request(250, -1)
59
60                 buttonHBox = gtk.HBox()
61
62                 button = gtk.Button(stock = gtk.STOCK_ADD)
63                 button.connect("clicked", self._on_add_note, None)
64                 buttonHBox.pack_start(button, expand = True, fill = True, padding = 3)
65
66                 button = gtk.Button(stock = gtk.STOCK_DELETE)
67                 button.connect("clicked", self._on_delete_note, None)
68                 buttonHBox.pack_start(button, expand = True, fill = True, padding = 3)
69
70                 listVbox = gtk.VBox(homogeneous = False, spacing = 0)
71                 listVbox.pack_start(self._category, expand = False, fill = True, padding = 3)
72                 listVbox.pack_start(self._noteslist.widget, expand = True, fill = True, padding = 3)
73                 listVbox.pack_start(buttonHBox, expand = False, fill = True, padding = 3)
74                 self.pack_start(listVbox, expand = False, fill = True, padding = 3)
75
76                 # Note view
77                 self._noteBodyView = gtk.TextView()
78                 self._noteBodyView.connect("focus-out-event", self.save_note, "focus-out-event")
79                 buf = self._noteBodyView.get_buffer()
80                 buf.set_text("")
81                 buf.connect("changed", self._on_note_changed, None)
82                 if gtkspell is not None:
83                         self._noteBodySpellChecker = gtkspell.Spell(self._noteBodyView)
84                 else:
85                         self._noteBodySpellChecker = None
86
87                 self._noteScrollWindow = gtk.ScrolledWindow()
88                 self._noteScrollWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
89                 self._noteScrollWindow.add(self._noteBodyView)
90                 hildonize.hildonize_scrollwindow_with_viewport(self._noteScrollWindow)
91
92                 # Note and history stuff in same column
93                 noteVbox = gtk.VBox(homogeneous = False, spacing = 0)
94                 noteVbox.pack_start(self._noteScrollWindow, expand = True, fill = True, padding = 3)
95                 self.pack_start(noteVbox, expand = True, fill = True, padding = 3)
96
97                 self.load_notes()
98                 self._category.connect("category_changed", self.load_notes)
99                 self._search.connect("search_changed", self.load_notes)
100
101         def set_wordwrap(self, enableWordWrap):
102                 if enableWordWrap:
103                         self._noteScrollWindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
104                         self._noteBodyView.set_wrap_mode(gtk.WRAP_WORD)
105                 else:
106                         self._noteScrollWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
107                         self._noteBodyView.set_wrap_mode(gtk.WRAP_NONE)
108
109         def load_notes(self, data = None):
110                 _moduleLogger.info("load_notes params: pos:"+str(self._pos)+" noteid:"+str(self.noteId))
111                 self._noteslist.clear_items()
112
113                 self._categoryName = self._category.get_queryable_category()
114                 search = self._search.get_search_pattern()
115                 notes = self._db.searchNotes(search, self._categoryName)
116
117                 if notes is not None:
118                         for note in notes:
119                                 noteid, category, noteText = note
120                                 title = self._get_title(noteText)
121                                 self._noteslist.append_item(title, noteid)
122
123                 self.noteId = -1
124                 self._pos = -1
125                 self._noteBodyView.get_buffer().set_text("")
126
127         def save_note(self, widget = None, data = None, data2 = None):
128                 _moduleLogger.info("save_note params: pos:"+str(self._pos)+" noteid:"+str(self.noteId))
129                 #print "params:", data, data2
130                 buf = self._noteBodyView.get_buffer().get_text(self._noteBodyView.get_buffer().get_start_iter(), self._noteBodyView.get_buffer().get_end_iter())
131                 if buf is None or len(buf) == 0:
132                         return
133
134                 if buf == self._noteBody:
135                         return
136
137                 title = self._get_title(buf)
138                 _moduleLogger.info("Saving note: " + title)
139                 if self._pos == -1 or self.noteId == -1:
140                         self._pos = -1
141                         self.noteId = str(uuid.uuid4())
142                         self._db.saveNote(self.noteId, buf, self._categoryName)
143                         self._noteslist.append_item(title, self.noteId)
144                         self._pos = self._noteslist.select_last_item()
145                 else:
146                         self._db.saveNote(self.noteId, buf, self._categoryName)
147
148         def show_history(self, *args):
149                 if self.noteId == -1:
150                         mbox =  gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("No note selected."))
151                         response = mbox.run()
152                         mbox.hide()
153                         mbox.destroy()
154                         return
155
156                 rows = self._db.getNoteHistory(self.noteId)
157
158                 import history
159                 dialog = history.HistorySelectionDialog()
160
161                 lastNoteStr = ""
162                 for row in rows:
163                         daten = row[4][1]
164                         if daten != "" and lastNoteStr != daten:
165                                 lastNoteStr = daten
166                                 dialog.noteHistory.append([row[0], row[1], row[2], row[3], daten+"\n"])
167
168                 dialog.vbox.show_all()
169                 dialog.set_size_request(600, 380)
170
171                 try:
172                         userResponse = dialog.run()
173
174                         if userResponse == gtk.RESPONSE_ACCEPT:
175                                 self.save_note()
176                                 data = dialog.get_selected_row()
177                                 if data is not None:
178                                         self._db.speichereSQL(data[2], data[3].split(" <<Tren-ner>> "), rowid = self.noteId)
179                                         _moduleLogger.info("loading History")
180                                         self._update_noteslist()
181
182                 finally:
183                         dialog.destroy()
184
185         def _get_title(self, buf):
186                 """
187                 @returns the title of the current note
188                 """
189                 eol = buf.find("\n")
190                 if -1 == eol:
191                         eol = len(buf)
192                 title = buf[:eol]
193                 return title
194
195         def _set_focus(self):
196                 self._noteBodyView.grab_focus()
197                 return False
198
199         def _update_noteslist(self, *args):
200                 if self._pos != -1:
201                         self.save_note()
202
203                 if args:
204                         data = args[0]
205                 else:
206                         data = None
207
208                 try:
209                         (pos, key, value) = self._noteslist.get_selection_data()
210                         if pos == -1:
211                                 return
212                 except StandardError:
213                         if data != "new":
214                                 return
215                         key = None
216
217                 if key == "new" or data == "new":
218                         #both methods supported click add note or new note (first one disabled)
219                         self.noteId = str(uuid.uuid4())
220                         self._db.saveNote(self.noteId, "", self._categoryName)
221                         self._pos = -1
222                         self._noteslist.append_item("", self.noteId)
223                         self._noteBodyView.get_buffer().set_text("")
224                         self._pos = self._noteslist.select_last_item()
225                 else:
226                         self._pos = pos
227                         self.noteId, pcdatum, self._categoryName, self._noteBody = self._db.loadNote(key)
228                         buf = self._noteBodyView.get_buffer()
229                         buf.set_text(self._noteBody)
230
231                 gobject.timeout_add(200, self._set_focus)
232
233         @gtk_toolbox.log_exception(_moduleLogger)
234         def _on_search_close(self, *args):
235                 if self.noteId != -1:
236                         self._noteBodyView.grab_focus()
237
238         @gtk_toolbox.log_exception(_moduleLogger)
239         def _on_note_changed(self, widget = None, data = None):
240                 if self._pos == -1 or self.noteId == -1:
241                         return
242
243                 buf = self._noteBodyView.get_buffer().get_text(self._noteBodyView.get_buffer().get_start_iter(), self._noteBodyView.get_buffer().get_end_iter())
244
245                 title = self._get_title(buf)
246                 value, key = self._noteslist.get_item(self._pos)
247
248                 if value != title:
249                         self._noteslist.change_item(self._pos, title, self.noteId)
250
251         @gtk_toolbox.log_exception(_moduleLogger)
252         def _on_add_note(self, widget = None, data = None):
253                 self._update_noteslist("new")
254
255         @gtk_toolbox.log_exception(_moduleLogger)
256         def _on_delete_note(self, widget = None, data = None):
257                 if self.noteId == -1:
258                         return
259
260                 doDeletion = False
261                 if self._noteBodyView.get_buffer().get_char_count() == 0:
262                         doDeletion = True
263                 else:
264                         mbox = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, _("Really delete?"))
265                         try:
266                                 response = mbox.run()
267                         finally:
268                                 mbox.hide()
269                                 mbox.destroy()
270
271                         doDeletion = response == gtk.RESPONSE_YES
272
273                 if doDeletion:
274                         self._db.delNote(self.noteId)
275                         self.noteId = -1
276                         self._noteslist.remove_item(self._pos)
277                         self._pos = -1
278                         self._noteBodyView.get_buffer().set_text("")