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