use constants
[meabook] / ui / hildon_ui.py
1 """
2 Hildon UI for Meabook
3 """
4
5 import gtk
6 import hildon
7 import gobject
8 from gettext import gettext as _
9 from meabook.constants import *
10 from meabook.ui.ui import MeabookUI
11
12
13 def create_button(title, value):
14     """Creates HildonButton."""
15
16     button = hildon.Button(gtk.HILDON_SIZE_AUTO | \
17         gtk.HILDON_SIZE_FINGER_HEIGHT, \
18         hildon.BUTTON_ARRANGEMENT_VERTICAL, title, value)
19     button.set_style(hildon.BUTTON_STYLE_PICKER)
20     button.set_alignment(0, 0, 0, 0)
21     return button
22
23
24 def create_menu_button(title):
25     """Creates Hildon menu button."""
26
27     return hildon.Button(gtk.HILDON_SIZE_AUTO, \
28         hildon.BUTTON_ARRANGEMENT_HORIZONTAL, title)
29
30
31 class HildonMeabook(MeabookUI):
32     def __init__(self, controller, renderer, config):
33         MeabookUI.__init__(self, controller, renderer, config)
34         self.handler = None
35         self.window = hildon.StackableWindow()
36         self.window.set_title(_('Meabook'))
37         self.window.connect('destroy', self.exit)
38
39         # create menu buttons
40         self.menu = hildon.AppMenu()
41         settings_button = create_menu_button(_('Settings'))
42         about_button = create_menu_button(_('About'))
43         import_button = create_menu_button(_('Import'))
44
45         # create filter widgets and connect signals
46         self.level1_filter = gtk.RadioButton(None, _('level1'))
47         self.level2_filter = gtk.RadioButton(self.level1_filter, _('level2'))
48         self.level3_filter = gtk.RadioButton(self.level2_filter, _('level3'))
49         for filter_widget in [self.level1_filter, self.level2_filter, \
50             self.level3_filter]:
51             filter_widget.set_mode(False)
52             filter_widget.connect('toggled', self.apply_filter_cb)
53
54         # create items list
55         self.selector = hildon.TouchSelector()
56
57         widgets_box = gtk.VBox()
58         self.search_entry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
59
60         # connect signals
61         settings_button.connect('clicked', self.show_settings_dialog_cb)
62         about_button.connect('clicked', self.show_about_dialog_cb)
63         import_button.connect('clicked', self.show_import_dialog_cb)
64         self.handler = self.selector.connect('changed', self.select_item_cb)
65         self.search_entry.connect('key-release-event', self.search_item_cb)
66
67         # packing widgets
68         widgets_box.pack_start(self.selector, expand=True)
69         widgets_box.pack_end(self.search_entry, expand=False)
70         self.window.add(widgets_box)
71         self.menu.add_filter(self.level1_filter)
72         self.menu.add_filter(self.level2_filter)
73         self.menu.add_filter(self.level3_filter)
74         self.menu.append(settings_button)
75         self.menu.append(import_button)
76         self.menu.append(about_button)
77         self.menu.show_all()
78         self.window.set_app_menu(self.menu)
79         self.window.show_all()
80
81     def _unfreeze_ui(self):
82         while gtk.events_pending():
83             gtk.main_iteration(False)
84
85     def _update_title(self, title):
86         """Set window title text."""
87
88         if title is not None:
89             self.window.set_title(title)
90
91     def _set_selector_content(self, selector, handler, items=[]):
92         """Updates selector content."""
93
94         # temporary block handler
95         selector.handler_block(handler)
96
97         # setting new content
98         # model: name, internal_name, type
99         model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT, \
100             gobject.TYPE_STRING)
101         for item in items:
102             model.append(item)
103         if selector.get_num_columns():
104             selector.remove_column(0)
105         selector.append_text_column(model, False)
106         selector.unselect_all(0)
107
108         # reconnect callback
109         selector.handler_unblock(handler)
110
111     def _show_items_dialog(self, title, items):
112         """Creates dialog with items."""
113
114         selector = hildon.TouchSelector()
115         window = hildon.StackableWindow()
116         window.set_title(title)
117         window.add(selector)
118         handler = selector.connect('changed', self.select_item_cb)
119         self._set_selector_content(selector, handler, items)
120         window.show_all()
121
122     def _show_item_dialog(self, title, entry_id):
123         """Shows detailed item information."""
124
125         def show_settings_dialog(widget, parent, func1, func2, entry_id):
126             dialog = ConfigurationDialog(self.controller, self.config)
127             response = getattr(dialog, func1)(None, parent)
128             if response == gtk.RESPONSE_OK:
129                 func2(parent, entry_id)
130
131         def update_entry(window, entry_id):
132             # create widgets
133             entry = self.controller.get_item(entry_id)
134             widgets_table = gtk.Table(rows=1, columns=1)
135             info_box = gtk.VBox()
136             image_box = gtk.VBox()
137             pannable_area = hildon.PannableArea()
138             pannable_area.set_property('mov-mode', hildon.MOVEMENT_MODE_BOTH)
139             image = self.renderer.render_image(dict(entry))
140             for fname, fvalue in entry:
141                 if fname == 'image':
142                     continue
143                 button = self.renderer.render_button(_(fname) , fvalue, fname)
144                 info_box.pack_start(button, expand=False)
145             # pack widgets
146             image_box.pack_start(image, expand=False)
147             widgets_table.attach(image_box, 0, 1, 0, 1, xoptions=gtk.SHRINK, \
148                 yoptions=gtk.FILL, xpadding=4, ypadding=8)
149             pannable_area.add_with_viewport(info_box)
150             widgets_table.attach(pannable_area, 1, 2, 0, 1, ypadding=8)
151             child = window.get_child()
152             if child:
153                 child.destroy()
154             window.add(widgets_table)
155             widgets_table.show_all()
156
157
158         # create widgets
159         window = hildon.StackableWindow()
160         window.set_title(title)
161         menu = hildon.AppMenu()
162         fields_button = create_menu_button(_('Fields to show'))
163         order_button = create_menu_button(_('Fields order'))
164         fields_button.connect('clicked', show_settings_dialog, window, \
165             'show_fields_settings_cb', update_entry, entry_id)
166         order_button.connect('clicked', show_settings_dialog, window, \
167             'show_order_settings_cb', update_entry, entry_id)
168         menu.append(fields_button)
169         menu.append(order_button)
170
171         update_entry(window, entry_id)
172         window.set_app_menu(menu)
173         menu.show_all()
174         window.show_all()
175
176
177     # Implementation of Base UI interface
178     def start(self):
179         self.apply_filter_cb(self.level1_filter)
180         gtk.main()
181
182     def exit(self, event):
183         gtk.main_quit()
184         self.controller.stop()
185
186     def create_information_dialog(self, title, message):
187         dialog = hildon.Dialog()
188         dialog.set_title(title)
189         label = gtk.Label(message)
190         dialog.vbox.add(label)
191         dialog.vbox.show_all()
192         dialog.run()
193         dialog.destroy()
194
195     def create_about_dialog(self):
196         from meabook.version import version
197         dialog = hildon.Dialog()
198         dialog.set_title(_('About'))
199         info_label = gtk.Label()
200         info_label.set_use_markup(True)
201         info_label.set_justify(gtk.JUSTIFY_CENTER)
202         info_label.set_markup("<span foreground='white' size='medium'><b>" \
203             "Meabook</b></span><span foreground='white' size='small'> - " \
204             "Enterprise address book</span>\n<span foreground='white' " \
205             "size='small'>Version %s</span>\n\n\n<span foreground='white'" \
206             "size='small'><b>Developers:</b></span>\n<span foreground=" \
207             "'white' size='small'>Tanya Makova | </span><span foreground=" \
208             "'#299BFC' size='small'>tanyshk@gmail.com</span>\n<span " \
209             "foreground='white' size='small'>Max Usachev | </span><span " \
210             "foreground='#299BFC' size='small'>maxusachev@gmail.com</span>" \
211             "\n" % version)
212         dialog.vbox.add(info_label)
213         dialog.vbox.show_all()
214         dialog.run()
215         dialog.destroy()
216
217     def create_import_dialog(self):
218         chooser = gobject.new(hildon.FileChooserDialog, \
219             action=gtk.FILE_CHOOSER_ACTION_OPEN)
220         chooser.set_property('show-files', True)
221         chooser.run()
222         path = chooser.get_filename()
223         chooser.destroy()
224         return path
225
226     def create_progress_dialog(self, title=None):
227         self._update_title(title)
228         self.selector.hide()
229         hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
230         self._unfreeze_ui()
231
232     def create_configuration_dialog(self, controller, config):
233         dialog = ConfigurationDialog(controller, config)
234         dialog.run()
235
236
237     # Hildon UI callbacks
238     def show_about_dialog_cb(self, widget):
239         """Shows About Dialog."""
240
241         self.controller.show_about_dialog()
242
243     def show_import_dialog_cb(self, widget):
244         """Shows Import Dialog."""
245
246         if self.controller.show_import_dialog():
247             self.apply_filter_cb(self.level1_filter)
248             self.selector.show_all()
249         hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
250
251     def show_settings_dialog_cb(self, widget):
252         """Shows Config dialog."""
253
254         self.controller.show_configuration_dialog()
255
256     def apply_filter_cb(self, widget):
257         """Updates toplevel selector with different level items."""
258
259         if not widget.get_active():
260             return
261
262         self._update_title(' - '.join([_('Meabook'), widget.get_label()]))
263         self.search_entry.hide()
264
265         if widget == self.level1_filter:
266             self._set_selector_content(self.selector, self.handler, \
267                 self.controller.get_all_folders())
268         elif widget == self.level2_filter:
269             self._set_selector_content(self.selector, self.handler, \
270                 self.controller.get_all_subfolders())
271         else:
272             self.search_entry.set_text('')
273             self.search_entry.set_placeholder(_('Enter search text here'))
274             self.search_entry.show()
275             self._set_selector_content(self.selector, self.handler)
276
277     def select_item_cb(self, widget, column):
278         """
279         Emits when changes selector content.
280         Opens new StackableWindow with new content.
281         """
282
283         item_name, internal_name, item_type = \
284             widget.get_model(0)[widget.get_active(0)]
285         if item_type == TYPE_DIRECTORY:
286             self._show_items_dialog(item_name, self.controller.get_items( \
287                 internal_name))
288         else:
289             self._show_item_dialog(item_name, internal_name)
290
291     def search_item_cb(self, widget, event):
292         """Search items from database."""
293
294         self._set_selector_content(self.selector, self.handler, \
295             self.controller.get_all_files_by_pattern(widget.get_text()))
296         widget.grab_focus()
297
298
299
300
301 class ConfigurationDialog:
302     """Configuration dialog"""
303
304     def __init__(self, controller, config):
305         self.config = config
306         self.controller = controller
307
308     def run(self):
309         dialog = hildon.Dialog()
310         dialog.set_title(_('Settings'))
311
312         button_order = create_button(_('Fields order'), self._update_value( \
313             None, self.config.get_order()))
314         button_order.connect('clicked', self.show_order_settings_cb, dialog)
315         button_fields = create_button(_('Fields to show'), self._update_value( \
316             None, self.config.get_fields()))
317         button_fields.connect('clicked', self.show_fields_settings_cb, dialog)
318
319         dialog.vbox.pack_start(button_fields, expand=False)
320         dialog.vbox.pack_start(button_order, expand=False)
321         dialog.vbox.show_all()
322         dialog.run()
323         dialog.destroy()
324
325     def _update_value(self, widget, fields):
326         """Updates widget title."""
327
328         value = ', '.join([_(field) for field in fields])
329         if widget is not None:
330             widget.set_value(value)
331         return value
332
333     def show_fields_settings_cb(self, widget, parent):
334         """Shows dialog for selecting fields to show."""
335
336         dialog = hildon.PickerDialog(parent)
337         dialog.set_title(_('Fields to show'))
338         selector = hildon.TouchSelector(text=True)
339
340         fields = self.controller.get_fields()
341         # fill items list
342         for field in fields:
343             selector.append_text(_(field))
344
345         selector.set_column_selection_mode( \
346             hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
347         selector.unselect_all(0)
348
349         # mark necessary fields
350         fields_to_select = self.config.get_fields()
351         model = selector.get_model(0)
352         for index, field in enumerate(fields):
353             if field in fields_to_select:
354                 selector.select_iter(0, model.get_iter(index), False)
355
356         dialog.set_selector(selector)
357         response = dialog.run()
358         if response == gtk.RESPONSE_OK:
359             model = selector.get_model(0)
360             selected_item_indexes = [index for index in [item[0] for item \
361                 in selector.get_selected_rows(0)]]
362             selected_fields = [fields[index] for index in selected_item_indexes]
363             self.config.set_fields(selected_fields)
364             self._update_value(widget, selected_fields)
365         dialog.destroy()
366         return response
367
368     def show_order_settings_cb(self, widget, parent):
369         """Shows dialog for setting fields order."""
370
371
372         def show_fields_chooser(widget, parent):
373             """Shows dialog to select field from fields list."""
374
375             dialog = hildon.PickerDialog(parent)
376             dialog.set_title(_('Fields'))
377             selector = hildon.TouchSelector(text=True)
378             dialog.set_selector(selector)
379             selector.set_column_selection_mode( \
380                 hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
381             # fill fields list
382             for field in self.controller.get_fields():
383                 selector.append_text(_(field))
384             dialog.run()
385             widget.set_value(selector.get_current_text())
386             dialog.destroy()
387
388
389         dialog = hildon.Dialog()
390         dialog.set_title(_('Fields order'))
391         pannable_area = hildon.PannableArea()
392         pannable_area.set_size_request_policy(hildon.SIZE_REQUEST_CHILDREN)
393
394         vbox = gtk.VBox()
395         for index, field in enumerate(self.config.get_order()):
396             button = create_button(' '.join([_('Position'), str(index)]), \
397                 _(field))
398             button.connect('clicked', show_fields_chooser, dialog)
399             vbox.pack_start(button, expand=False)
400         pannable_area.add_with_viewport(vbox)
401         dialog.add_button(_('Done'), gtk.RESPONSE_OK)
402         dialog.vbox.pack_start(pannable_area)
403         dialog.vbox.show_all()
404         response = dialog.run()
405         if response == gtk.RESPONSE_OK:
406             fields_dict = dict([(_(field).decode('utf-8'), field) for field \
407                 in self.controller.get_fields()])
408             new_ordered_fields = [fields_dict[button.get_value().decode( \
409                 'utf-8')] for button in vbox.get_children()]
410             self.config.set_order(new_ordered_fields)
411             self._update_value(widget, new_ordered_fields)
412         dialog.destroy()
413         return response