8034d58d96b1ace03d5b4112fd891f47587e752f
[gonvert] / src / gonvert_glade.py
1 #!/usr/bin/env python
2 # -*- coding: UTF8 -*-
3
4 """
5 @todo Look into using two columns for displaying the value, split by the
6 decimal place.  The left one would be right aligned and the right would be left
7 aligned (only if not in exponential notation
8 OR display everything in engineering notation
9
10 @tood Add a unit description dialog for when hildonized
11
12 @todo Add support for custom units
13
14 @todo Add support for compound units
15 """
16
17 import os
18 import pickle
19 import logging
20
21 import pango
22 import gobject
23 import gtk
24 import gtk.glade
25 import gtk.gdk
26
27 import constants
28 import hildonize
29 import unit_data
30
31 try:
32         import gettext
33 except ImportError:
34         _ = lambda x: x
35         gettext = None
36 else:
37         _ = gettext.gettext
38
39
40 _moduleLogger = logging.getLogger("gonvert_glade")
41 PROFILE_STARTUP = False
42 FORCE_HILDON_LIKE = False
43
44 if gettext is not None:
45         gettext.bindtextdomain('gonvert', '/usr/share/locale')
46         gettext.textdomain('gonvert')
47
48
49 def change_menu_label(widgets, labelname, newtext):
50         item_label = widgets.get_widget(labelname).get_children()[0]
51         item_label.set_text(newtext)
52
53
54 class Gonvert(object):
55
56         _glade_files = [
57                 os.path.join(os.path.dirname(__file__), "gonvert.glade"),
58                 os.path.join(os.path.dirname(__file__), "../data/gonvert.glade"),
59                 os.path.join(os.path.dirname(__file__), "../lib/gonvert.glade"),
60                 '/usr/share/gonvert/gonvert.glade',
61                 '/usr/lib/gonvert/gonvert.glade',
62         ]
63
64         UNITS_NAME_IDX = 0
65         UNITS_VALUE_IDX = 1
66         UNITS_SYMBOL_IDX = 2
67
68         def __init__(self):
69                 self._unitDataInCategory = None
70                 self._unit_sort_direction = False
71                 self._value_sort_direction = False
72                 self._units_sort_direction = False
73                 self._isFullScreen = False
74
75                 self._find_result = [] # empty find result list
76                 self._findIndex = 0 # default to find result number zero
77
78                 self._selectedCategoryName = '' # preset to no selected category
79                 self._defaultUnitForCategory = {} # empty dictionary for later use
80
81                 #check to see if glade file is in current directory (user must be
82                 # running from download untar directory)
83                 for gladePath in self._glade_files:
84                         if os.path.isfile(gladePath):
85                                 homepath = os.path.dirname(gladePath)
86                                 pixmapspath = "/".join((homepath, "pixmaps"))
87                                 widgets = gtk.glade.XML(gladePath)
88                                 break
89                 else:
90                         _moduleLogger.error("UI Descriptor not found!")
91                         gtk.main_quit()
92                         return
93
94                 self._mainWindow = widgets.get_widget('mainWindow')
95                 self._app = hildonize.get_app_class()()
96                 self._mainWindow = hildonize.hildonize_window(self._app, self._mainWindow)
97
98                 change_menu_label(widgets, 'fileMenuItem', _('File'))
99                 change_menu_label(widgets, 'exitMenuItem', _('Exit'))
100                 change_menu_label(widgets, 'toolsMenuItem', _('Tools'))
101                 change_menu_label(widgets, 'clearSelectionMenuItem', _('Clear selections'))
102                 change_menu_label(widgets, 'helpMenuItem', _('Help'))
103                 change_menu_label(widgets, 'aboutMenuItem', _('About'))
104                 change_menu_label(widgets, 'findButton', _('Find'))
105
106                 self._shortlistcheck = widgets.get_widget('shortlistcheck')
107                 self._toggleShortList = widgets.get_widget('toggleShortList')
108
109                 self._categorySelectionButton = widgets.get_widget("categorySelectionButton")
110                 self._categoryView = widgets.get_widget('categoryView')
111
112                 self._unitsView = widgets.get_widget('unitsView')
113                 self._unitsView.set_property('rules_hint', 1)
114                 self._unitsView_selection = self._unitsView.get_selection()
115
116                 self._unitName = widgets.get_widget('unitName')
117                 self._unitValue = widgets.get_widget('unitValue')
118                 self._previousUnitName = widgets.get_widget('previousUnitName')
119                 self._previousUnitValue = widgets.get_widget('previousUnitValue')
120
121                 self._unitSymbol = widgets.get_widget('unitSymbol')
122                 self._previousUnitSymbol = widgets.get_widget('previousUnitSymbol')
123
124                 self._unitDescription = widgets.get_widget('unitDescription')
125
126                 self._searchLayout = widgets.get_widget('searchLayout')
127                 self._searchLayout.hide()
128                 self._findEntry = widgets.get_widget('findEntry')
129                 self._findLabel = widgets.get_widget('findLabel')
130                 self._findButton = widgets.get_widget('findButton')
131
132                 #insert a self._categoryColumnumn into the units list even though the heading will not be seen
133                 renderer = gtk.CellRendererText()
134                 renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
135                 renderer.set_property("width-chars", len("grams per cubic cm plus some"))
136                 hildonize.set_cell_thumb_selectable(renderer)
137                 self._unitNameColumn = gtk.TreeViewColumn(_('Name'), renderer)
138                 self._unitNameColumn.set_property('resizable', 1)
139                 self._unitNameColumn.add_attribute(renderer, 'text', self.UNITS_NAME_IDX)
140                 self._unitNameColumn.set_clickable(True)
141                 self._unitNameColumn.connect("clicked", self._on_click_unit_column)
142                 self._unitsView.append_column(self._unitNameColumn)
143
144                 renderer = gtk.CellRendererText()
145                 hildonize.set_cell_thumb_selectable(renderer)
146                 self._unitValueColumn = gtk.TreeViewColumn(_('Value'), renderer)
147                 self._unitValueColumn.set_property('resizable', 1)
148                 self._unitValueColumn.add_attribute(renderer, 'text', self.UNITS_VALUE_IDX)
149                 self._unitValueColumn.set_clickable(True)
150                 self._unitValueColumn.connect("clicked", self._on_click_unit_column)
151                 self._unitsView.append_column(self._unitValueColumn)
152
153                 renderer = gtk.CellRendererText()
154                 renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
155                 renderer.set_property("width-chars", len("G ohm plus some"))
156                 hildonize.set_cell_thumb_selectable(renderer)
157                 self._unitSymbolColumn = gtk.TreeViewColumn(_('Units'), renderer)
158                 self._unitSymbolColumn.set_property('resizable', 1)
159                 self._unitSymbolColumn.add_attribute(renderer, 'text', self.UNITS_SYMBOL_IDX)
160                 self._unitSymbolColumn.set_clickable(True)
161                 self._unitSymbolColumn.connect("clicked", self._on_click_unit_column)
162                 self._unitsView.append_column(self._unitSymbolColumn)
163
164                 self._unitModel = gtk.ListStore(
165                         gobject.TYPE_STRING, # UNITS_NAME_IDX
166                         gobject.TYPE_STRING, # UNITS_VALUE_IDX
167                         gobject.TYPE_STRING, # UNITS_SYMBOL_IDX
168                 )
169                 self._sortedUnitModel = gtk.TreeModelSort(self._unitModel)
170                 columns = self._get_column_sort_stuff()
171                 for columnIndex, (column, sortDirection, col_cmp) in enumerate(columns):
172                         self._sortedUnitModel.set_sort_func(columnIndex, col_cmp)
173                 self._unitsView.set_model(self._sortedUnitModel)
174
175                 #Insert a column into the category list even though the heading will not be seen
176                 renderer = gtk.CellRendererText()
177                 self._categoryColumn = gtk.TreeViewColumn('Title', renderer)
178                 self._categoryColumn.set_property('resizable', 1)
179                 self._categoryColumn.add_attribute(renderer, 'text', 0)
180                 self._categoryView.append_column(self._categoryColumn)
181
182                 self._categoryModel = gtk.ListStore(gobject.TYPE_STRING)
183                 self._categoryView.set_model(self._categoryModel)
184                 #colourize each row differently for easier reading
185                 self._categoryView.set_property('rules_hint', 1)
186
187                 #Populate the catagories list
188                 for key in unit_data.UNIT_CATEGORIES:
189                         row = (key, )
190                         self._categoryModel.append(row)
191
192                 #--------- connections to GUI ----------------
193                 self._mainWindow.connect("delete-event", self._on_user_exit)
194                 self._mainWindow.connect("key-press-event", self._on_key_press)
195                 self._mainWindow.connect("window-state-event", self._on_window_state_change)
196                 self._categorySelectionButton.connect("clicked", self._on_category_selector_clicked)
197                 self._categoryView.connect("cursor-changed", self._on_click_category)
198                 self._findButton.connect("clicked", self._on_find_activate)
199                 self._findEntry.connect("activate", self._on_find_activate)
200                 self._findEntry.connect("changed", self._on_findEntry_changed)
201                 self._previousUnitValue.connect("changed", self._on_previous_unit_value_changed)
202                 self._shortlistcheck.connect("toggled", self._on_shortlist_changed)
203                 self._unitValue.connect("changed", self._on_unit_value_changed)
204                 self._unitsView.connect("cursor-changed", self._on_click_unit)
205                 if hildonize.GTK_MENU_USED:
206                         widgets.get_widget("aboutMenuItem").connect("activate", self._on_about_clicked)
207                         widgets.get_widget("clearSelectionMenuItem").connect("activate", self._on_user_clear_selections)
208                         widgets.get_widget("editShortListMenuItem").connect("activate", self._on_edit_shortlist)
209                         widgets.get_widget("exitMenuItem").connect("activate", self._on_user_exit)
210
211                 for scrollingWidgetName in (
212                         "unitsViewScrolledWindow",
213                 ):
214                         scrollingWidget = widgets.get_widget(scrollingWidgetName)
215                         assert scrollingWidget is not None, scrollingWidgetName
216                         hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
217
218                 if hildonize.IS_HILDON_SUPPORTED or FORCE_HILDON_LIKE:
219                         self._categoryView.get_parent().hide()
220                         self._unitsView.set_headers_visible(False)
221                         self._previousUnitName.get_parent().hide()
222                         self._unitDescription.get_parent().get_parent().hide()
223                 else:
224                         self._categorySelectionButton.hide()
225
226                 replacementButtons = []
227                 menu = hildonize.hildonize_menu(
228                         self._mainWindow,
229                         widgets.get_widget("mainMenuBar"),
230                         replacementButtons
231                 )
232
233                 if not hildonize.IS_HILDON_SUPPORTED:
234                         _moduleLogger.info("No hildonization support")
235
236                 hildonize.set_application_title(
237                         self._mainWindow, "%s - Unit Conversion Utility" % constants.__pretty_app_name__
238                 )
239                 iconPath = pixmapspath + '/gonvert.png'
240                 if os.path.exists(iconPath):
241                         self._mainWindow.set_icon(gtk.gdk.pixbuf_new_from_file(iconPath))
242                 else:
243                         _moduleLogger.warn("Error: Could not find gonvert icon: %s" % iconPath)
244
245                 self._load_settings()
246                 self._mainWindow.show()
247
248         def _load_settings(self):
249                 #Restore window size from previously saved settings if it exists and is valid.
250                 windowDatPath = "/".join((constants._data_path_, "window.dat"))
251                 if os.path.exists(windowDatPath):
252                         saved_window = pickle.load(open(windowDatPath, "r"))
253                         try:
254                                 a, b = saved_window['size']
255                         except KeyError:
256                                 pass
257                         else:
258                                 self._mainWindow.resize(a, b)
259
260                 #Restore selections from previously saved settings if it exists and is valid.
261                 categoryIndex = 0
262                 selectedCategoryName = unit_data.UNIT_CATEGORIES[0]
263                 selectionsDatPath = "/".join((constants._data_path_, "selections.dat"))
264                 if os.path.exists(selectionsDatPath):
265                         selections = pickle.load(open(selectionsDatPath, 'r'))
266                         try:
267                                 self._defaultUnitForCategory = selections['selected_units']
268                         except KeyError:
269                                 pass
270
271                         try:
272                                 selectedCategoryName = selections['selected_category']
273                         except KeyError:
274                                 pass
275                         else:
276                                 try:
277                                         categoryIndex = unit_data.UNIT_CATEGORIES.index(selectedCategoryName)
278                                 except ValueError:
279                                         _moduleLogger.warn("Unknown category: %s" % selectedCategoryName)
280
281                 self._categorySelectionButton.set_label(selectedCategoryName)
282                 self._categoryView.set_cursor(categoryIndex, self._categoryColumn, False)
283                 self._categoryView.grab_focus()
284
285                 self._select_default_unit()
286
287         def _save_settings(self):
288                 """
289                 This routine saves the selections to a file, and
290                 should therefore only be called when exiting the program.
291
292                 Update selections dictionary which consists of the following keys:
293                 'self._selectedCategoryName': full name of selected category
294                 'self._defaultUnitForCategory': self._defaultUnitForCategory dictionary which contains:
295                 [categoryname: #1 displayed unit, #2 displayed unit]
296                 """
297                 #Determine the contents of the selected category row
298                 selected, iter = self._categoryView.get_selection().get_selected()
299                 self._selectedCategoryName = self._categoryModel.get_value(iter, 0)
300
301                 selections = {
302                         'selected_category': self._selectedCategoryName,
303                         'selected_units': self._defaultUnitForCategory
304                 }
305                 selectionsDatPath = "/".join((constants._data_path_, "selections.dat"))
306                 pickle.dump(selections, open(selectionsDatPath, 'w'))
307
308                 #Get last size of app and save it
309                 window_settings = {
310                         'size': self._mainWindow.get_size()
311                 }
312                 windowDatPath = "/".join((constants._data_path_, "window.dat"))
313                 pickle.dump(window_settings, open(windowDatPath, 'w'))
314
315         def _clear_find(self):
316                 # switch to "new find" state
317                 self._find_result = []
318                 self._findIndex = 0
319
320                 # Clear our user message
321                 self._findLabel.set_text('')
322
323         def _find_first(self):
324                 assert len(self._find_result) == 0
325                 assert self._findIndex == 0
326                 findString = self._findEntry.get_text().strip().lower()
327                 if not findString:
328                         return
329
330                 # Gather info on all the matching units from all categories
331                 for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
332                         units = unit_data.get_units(category)
333                         for unitIndex, unit in enumerate(units):
334                                 loweredUnit = unit.lower()
335                                 if loweredUnit in findString or findString in loweredUnit:
336                                         self._find_result.append((category, unit, catIndex, unitIndex))
337
338         def _update_find_selection(self):
339                 assert 0 < len(self._find_result)
340
341                 #check if next find is in a new category (prevent category changes when unnecessary
342                 searchCategoryName = self._find_result[self._findIndex][0]
343                 if self._selectedCategoryName != searchCategoryName:
344                         self._categorySelectionButton.set_label(searchCategoryName)
345                         self._categoryView.set_cursor(
346                                 self._find_result[self._findIndex][2], self._categoryColumn, False
347                         )
348
349                 self._unitsView.set_cursor(
350                         self._find_result[self._findIndex][3], self._unitNameColumn, True
351                 )
352
353         def _find_next(self):
354                 if len(self._find_result) == 0:
355                         self._find_first()
356                 else:
357                         if self._findIndex == len(self._find_result)-1:
358                                 self._findIndex = 0
359                         else:
360                                 self._findIndex += 1
361
362                 if not self._find_result:
363                         self._findLabel.set_text('Text not found')
364                 else:
365                         self._update_find_selection()
366                         resultsLeft = len(self._find_result) - self._findIndex - 1
367                         self._findLabel.set_text(
368                                 '%s result(s) left' % (resultsLeft, )
369                         )
370
371         def _find_previous(self):
372                 if len(self._find_result) == 0:
373                         self._find_first()
374                 else:
375                         if self._findIndex == 0:
376                                 self._findIndex = len(self._find_result)-1
377                         else:
378                                 self._findIndex -= 1
379
380                 if not self._find_result:
381                         self._findLabel.set_text('Text not found')
382                 else:
383                         self._update_find_selection()
384                         resultsLeft = len(self._find_result) - self._findIndex - 1
385                         self._findLabel.set_text(
386                                 '%s result(s) left' % (resultsLeft, )
387                         )
388
389         def _toggle_find(self):
390                 if self._searchLayout.get_property("visible"):
391                         self._searchLayout.hide()
392                         self._unitsView.grab_focus()
393                 else:
394                         self._searchLayout.show()
395                         self._findEntry.grab_focus()
396
397         def _unit_model_cmp(self, sortedModel, leftItr, rightItr):
398                 leftUnitText = self._unitModel.get_value(leftItr, 0)
399                 rightUnitText = self._unitModel.get_value(rightItr, 0)
400                 return cmp(leftUnitText, rightUnitText)
401
402         def _symbol_model_cmp(self, sortedModel, leftItr, rightItr):
403                 leftSymbolText = self._unitModel.get_value(leftItr, 2)
404                 rightSymbolText = self._unitModel.get_value(rightItr, 2)
405                 return cmp(leftSymbolText, rightSymbolText)
406
407         def _value_model_cmp(self, sortedModel, leftItr, rightItr):
408                 #special sorting exceptions for ascii values (instead of float values)
409                 if self._selectedCategoryName == "Computer Numbers":
410                         leftValue = self._unitModel.get_value(leftItr, 1)
411                         rightValue = self._unitModel.get_value(rightItr, 1)
412                 else:
413                         leftValueText = self._unitModel.get_value(leftItr, 1)
414                         leftValue = float(leftValueText) if leftValueText else 0.0
415
416                         rightValueText = self._unitModel.get_value(rightItr, 1)
417                         rightValue = float(rightValueText) if rightValueText else 0.0
418                 return cmp(leftValue, rightValue)
419
420         def _get_column_sort_stuff(self):
421                 columns = (
422                         (self._unitNameColumn, "_unit_sort_direction", self._unit_model_cmp),
423                         (self._unitValueColumn, "_value_sort_direction", self._value_model_cmp),
424                         (self._unitSymbolColumn, "_units_sort_direction", self._symbol_model_cmp),
425                 )
426                 return columns
427
428         def _switch_category(self, category):
429                 self._selectedCategoryName = category
430                 self._unitDataInCategory = unit_data.UNIT_DESCRIPTIONS[self._selectedCategoryName]
431
432                 #Fill up the units descriptions and clear the value cells
433                 self._clear_visible_unit_data()
434                 for key in unit_data.get_units(self._selectedCategoryName):
435                         iter = self._unitModel.append()
436                         self._unitModel.set(iter, 0, key, 1, '', 2, self._unitDataInCategory[key][1])
437                 self._sortedUnitModel.sort_column_changed()
438
439                 self._select_default_unit()
440
441         def _clear_visible_unit_data(self):
442                 self._unitDescription.get_buffer().set_text("")
443                 self._unitName.set_text('')
444                 self._unitValue.set_text('')
445                 self._unitSymbol.set_text('')
446
447                 self._previousUnitName.set_text('')
448                 self._previousUnitValue.set_text('')
449                 self._previousUnitSymbol.set_text('')
450
451                 self._unitModel.clear()
452
453         def _select_default_unit(self):
454                 # Restore the previous historical settings of previously selected units
455                 # in this newly selected category
456                 defaultPrimary = unit_data.get_base_unit(self._selectedCategoryName)
457                 defaultSecondary = ""
458                 if self._selectedCategoryName in self._defaultUnitForCategory:
459                         if self._defaultUnitForCategory[self._selectedCategoryName][0]:
460                                 defaultPrimary = self._defaultUnitForCategory[self._selectedCategoryName][0]
461                         if self._defaultUnitForCategory[self._selectedCategoryName][1]:
462                                 defaultSecondary = self._defaultUnitForCategory[self._selectedCategoryName][1]
463
464                 units = unit_data.get_units(self._selectedCategoryName)
465
466                 #Restore oldest selection first.
467                 if defaultPrimary:
468                         try:
469                                 unitIndex = units.index(defaultPrimary)
470                         except ValueError:
471                                 unitIndex = 0
472                         self._unitsView.set_cursor(unitIndex, self._unitNameColumn, True)
473
474                 #Restore newest selection second.
475                 if defaultSecondary:
476                         try:
477                                 unitIndex = units.index(defaultSecondary)
478                         except ValueError:
479                                 unitIndex = 0
480                         self._unitsView.set_cursor(unitIndex, self._unitNameColumn, True)
481
482                 # select the text so user can start typing right away
483                 self._unitValue.grab_focus()
484                 self._unitValue.select_region(0, -1)
485
486         def _sanitize_value(self, userEntry):
487                 if self._selectedCategoryName == "Computer Numbers":
488                         if userEntry == '':
489                                 value = '0'
490                         else:
491                                 value = userEntry
492                 else:
493                         if userEntry == '':
494                                 value = 0.0
495                         else:
496                                 value = float(userEntry)
497                 return value
498
499         def _on_shortlist_changed(self, *args):
500                 try:
501                         raise NotImplementedError("%s" % self._shortlistcheck.get_active())
502                 except Exception:
503                         _moduleLogger.exception("_on_shortlist_changed")
504
505         def _on_edit_shortlist(self, *args):
506                 try:
507                         raise NotImplementedError("%s" % self._toggleShortList.get_active())
508                 except Exception:
509                         _moduleLogger.exception("_on_edit_shortlist")
510
511         def _on_user_clear_selections(self, *args):
512                 try:
513                         selectionsDatPath = "/".join((constants._data_path_, "selections.dat"))
514                         os.remove(selectionsDatPath)
515                         self._defaultUnitForCategory = {}
516                 except Exception:
517                         _moduleLogger.exception("_on_user_clear_selections")
518
519         def _on_key_press(self, widget, event, *args):
520                 """
521                 @note Hildon specific
522                 """
523                 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
524                 try:
525                         if (
526                                 event.keyval == gtk.keysyms.F6 or
527                                 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
528                         ):
529                                 if self._isFullScreen:
530                                         self._mainWindow.unfullscreen()
531                                 else:
532                                         self._mainWindow.fullscreen()
533                         elif event.keyval == gtk.keysyms.f and event.get_state() & gtk.gdk.CONTROL_MASK:
534                                 self._toggle_find()
535                         elif event.keyval == gtk.keysyms.p and event.get_state() & gtk.gdk.CONTROL_MASK:
536                                 self._find_previous()
537                         elif event.keyval == gtk.keysyms.n and event.get_state() & gtk.gdk.CONTROL_MASK:
538                                 self._find_next()
539                 except Exception, e:
540                         _moduleLogger.exception("_on_key_press")
541
542         def _on_window_state_change(self, widget, event, *args):
543                 """
544                 @note Hildon specific
545                 """
546                 try:
547                         if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
548                                 self._isFullScreen = True
549                         else:
550                                 self._isFullScreen = False
551                 except Exception, e:
552                         _moduleLogger.exception("_on_window_state_change")
553
554         def _on_findEntry_changed(self, *args):
555                 """
556                 Clear out find results since the user wants to look for something new
557                 """
558                 try:
559                         self._clear_find()
560                 except Exception:
561                         _moduleLogger.exception("_on_findEntry_changed")
562
563         def _on_find_activate(self, *args):
564                 try:
565                         self._find_next()
566                         self._findButton.grab_focus()
567                 except Exception:
568                         _moduleLogger.exception("_on_find_activate")
569
570         def _on_click_unit_column(self, col):
571                 """
572                 Sort the contents of the col when the user clicks on the title.
573                 """
574                 try:
575                         #Determine which column requires sorting
576                         columns = self._get_column_sort_stuff()
577                         for columnIndex, (maybeCol, directionName, col_cmp) in enumerate(columns):
578                                 if col is maybeCol:
579                                         direction = getattr(self, directionName)
580                                         gtkDirection = gtk.SORT_ASCENDING if direction else gtk.SORT_DESCENDING
581
582                                         # cause a sort
583                                         self._sortedUnitModel.set_sort_column_id(columnIndex, gtkDirection)
584
585                                         # set the visual for sorting
586                                         col.set_sort_indicator(True)
587                                         col.set_sort_order(not direction)
588
589                                         setattr(self, directionName, not direction)
590                                         break
591                                 else:
592                                         maybeCol.set_sort_indicator(False)
593                         else:
594                                 assert False, "Unknown column: %s" % (col.get_title(), )
595                 except Exception:
596                         _moduleLogger.exception("_on_click_unit_column")
597
598         def _on_category_selector_clicked(self, *args):
599                 try:
600                         currenntIndex = unit_data.UNIT_CATEGORIES.index(self._selectedCategoryName)
601                         newIndex = hildonize.touch_selector(
602                                 self._mainWindow,
603                                 "Categories",
604                                 unit_data.UNIT_CATEGORIES,
605                                 currenntIndex,
606                         )
607
608                         selectedCategoryName = unit_data.UNIT_CATEGORIES[newIndex]
609                         self._categorySelectionButton.set_label(selectedCategoryName)
610                         self._categoryView.set_cursor(newIndex, self._categoryColumn, False)
611                         self._categoryView.grab_focus()
612                 except Exception:
613                         _moduleLogger.exception("_on_category_selector_clicked")
614
615         def _on_click_category(self, *args):
616                 try:
617                         selected, iter = self._categoryView.get_selection().get_selected()
618                         if iter is None:
619                                 # User is typing in an invalid string, not selecting any category
620                                 return
621                         selectedCategory = self._categoryModel.get_value(iter, 0)
622                         self._switch_category(selectedCategory)
623                 except Exception:
624                         _moduleLogger.exception("_on_click_category")
625
626         def _on_click_unit(self, *args):
627                 try:
628                         selected, iter = self._unitsView.get_selection().get_selected()
629                         selected_unit = selected.get_value(iter, 0)
630                         unit_spec = self._unitDataInCategory[selected_unit]
631
632                         showSymbol = False
633
634                         if self._unitName.get_text() != selected_unit:
635                                 self._previousUnitName.set_text(self._unitName.get_text())
636                                 self._previousUnitValue.set_text(self._unitValue.get_text())
637                                 self._previousUnitSymbol.set_text(self._unitSymbol.get_text())
638                                 if self._unitSymbol.get_text():
639                                         showSymbol = True
640
641                         self._unitName.set_text(selected_unit)
642                         self._unitValue.set_text(selected.get_value(iter, 1))
643                         buffer = self._unitDescription.get_buffer()
644                         buffer.set_text(unit_spec[2])
645                         self._unitSymbol.set_text(unit_spec[1]) # put units into label text
646                         if unit_spec[1]:
647                                 showSymbol = True
648                         else:
649                                 showSymbol = False
650
651                         if showSymbol:
652                                 self._unitSymbol.show()
653                                 self._previousUnitSymbol.show()
654                         else:
655                                 self._unitSymbol.hide()
656                                 self._previousUnitSymbol.hide()
657
658                         if self._unitValue.get_text() == '':
659                                 if self._selectedCategoryName == "Computer Numbers":
660                                         self._unitValue.set_text("0")
661                                 else:
662                                         self._unitValue.set_text("0.0")
663
664                         self._defaultUnitForCategory[self._selectedCategoryName] = [
665                                 self._unitName.get_text(), self._previousUnitName.get_text()
666                         ]
667
668                         # select the text so user can start typing right away
669                         self._unitValue.grab_focus()
670                         self._unitValue.select_region(0, -1)
671                 except Exception:
672                         _moduleLogger.exception("_on_click_unit")
673
674         def _on_unit_value_changed(self, *args):
675                 try:
676                         if self._unitName.get_text() == '':
677                                 return
678                         if not self._unitValue.is_focus():
679                                 return
680
681                         #retrieve the conversion function and value from the selected unit
682                         value = self._sanitize_value(self._unitValue.get_text())
683                         func, arg = self._unitDataInCategory[self._unitName.get_text()][0]
684                         base = func.to_base(value, arg)
685
686                         #point to the first row
687                         for row in self._unitModel:
688                                 func, arg = self._unitDataInCategory[row[0]][0]
689                                 row[1] = str(func.from_base(base, arg))
690
691                         # Update the secondary unit entry
692                         if self._previousUnitName.get_text() != '':
693                                 func, arg = self._unitDataInCategory[self._previousUnitName.get_text()][0]
694                                 self._previousUnitValue.set_text(str(func.from_base(base, arg, )))
695                 except Exception:
696                         _moduleLogger.exception("_on_unit_value_changed")
697
698         def _on_previous_unit_value_changed(self, *args):
699                 try:
700                         if self._previousUnitName.get_text() == '':
701                                 return
702                         if not self._previousUnitValue.is_focus():
703                                 return
704
705                         #retrieve the conversion function and value from the selected unit
706                         value = self._sanitize_value(self._previousUnitValue.get_text())
707                         func, arg = self._unitDataInCategory[self._previousUnitName.get_text()][0]
708                         base = func.to_base(value, arg)
709
710                         #point to the first row
711                         for row in self._unitModel:
712                                 func, arg = self._unitDataInCategory[row[0]][0]
713                                 row[1] = str(func.from_base(base, arg))
714
715                         # Update the primary unit entry
716                         func, arg = self._unitDataInCategory[self._unitName.get_text()][0]
717                         self._unitValue.set_text(str(func.from_base(base, arg, )))
718                 except Exception:
719                         _moduleLogger.exception("_on_previous_unit_value_changed")
720
721         def _on_about_clicked(self, a):
722                 dlg = gtk.AboutDialog()
723                 dlg.set_name(constants.__pretty_app_name__)
724                 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
725                 dlg.set_copyright("Copyright 2009 - GPL")
726                 dlg.set_comments("")
727                 dlg.set_website("http://unihedron.com/projects/gonvert/gonvert.php")
728                 dlg.set_authors(["Anthony Tekatch <anthony@unihedron.com>", "Ed Page <edpage@byu.net>"])
729                 dlg.run()
730                 dlg.destroy()
731
732         def _on_user_exit(self, *args):
733                 try:
734                         self._save_settings()
735                 except Exception:
736                         _moduleLogger.exception("_on_user_exit")
737                 finally:
738                         gtk.main_quit()
739
740
741 def run_gonvert():
742         gtk.gdk.threads_init()
743         if hildonize.IS_HILDON_SUPPORTED:
744                 gtk.set_application_name(constants.__pretty_app_name__)
745         handle = Gonvert()
746         if not PROFILE_STARTUP:
747                 gtk.main()
748
749
750 if __name__ == "__main__":
751         logging.basicConfig(level = logging.DEBUG)
752         try:
753                 os.makedirs(constants._data_path_)
754         except OSError, e:
755                 if e.errno != 17:
756                         raise
757
758         run_gonvert()