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