Pulling in my skeleton
authorEd Page <eopage@byu.net>
Mon, 31 May 2010 15:00:14 +0000 (10:00 -0500)
committerEd Page <eopage@byu.net>
Mon, 31 May 2010 15:00:14 +0000 (10:00 -0500)
1  2 
Makefile
src/gonvert_glade.py
src/gtk_toolbox.py
src/hildonize.py
support/builddeb.py

diff --cc Makefile
+++ b/Makefile
@@@ -46,10 -45,14 +46,10 @@@ package: $(OBJ
        cp $(SOURCE_PATH)/$(PROJECT_NAME).py  $(BUILD_PATH)/generic
        $(foreach file, $(DATA), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
        $(foreach file, $(SOURCE), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
--      #$(foreach file, $(OBJ), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
        cp support/$(PROJECT_NAME).desktop $(BUILD_PATH)/generic
 -      cp support/icons/hicolor/26x26/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/26x26-$(PROJECT_NAME).png
 -      cp support/icons/hicolor/64x64/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/64x64-$(PROJECT_NAME).png
 -      cp support/icons/hicolor/scalable/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/scale-$(PROJECT_NAME).png
        cp support/builddeb.py $(BUILD_PATH)/generic
        cp support/py2deb.py $(BUILD_PATH)/generic
+       cp support/fake_py2deb.py $(BUILD_PATH)/generic
  
        mkdir -p $(BUILD_PATH)/diablo
        cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/diablo
index 1ffc3ae,0000000..e42b1e4
mode 100755,000000..100755
--- /dev/null
@@@ -1,892 -1,0 +1,892 @@@
 +#!/usr/bin/env python
 +# -*- coding: UTF8 -*-
 +
 +from __future__ import with_statement
 +
 +import os
 +import math
 +import pickle
 +import logging
 +
 +import pango
 +import gobject
 +import gtk
 +import gtk.glade
 +import gtk.gdk
 +
 +import constants
 +import hildonize
 +import gtk_toolbox
 +import unit_data
 +
 +try:
 +      import gettext
 +except ImportError:
 +      _ = lambda x: x
 +      gettext = None
 +else:
 +      _ = gettext.gettext
 +
 +
 +_moduleLogger = logging.getLogger("gonvert_glade")
 +
 +if gettext is not None:
 +      gettext.bindtextdomain('gonvert', '/usr/share/locale')
 +      gettext.textdomain('gonvert')
 +
 +
 +def change_menu_label(widgets, labelname, newtext):
 +      item_label = widgets.get_widget(labelname).get_children()[0]
 +      item_label.set_text(newtext)
 +
 +
 +def split_number(number):
 +      try:
 +              fractional, integer = math.modf(number)
 +      except TypeError:
 +              integerDisplay = number
 +              fractionalDisplay = ""
 +      else:
 +              integerDisplay = str(integer)
 +              fractionalDisplay = str(fractional)
 +              if "e+" in integerDisplay:
 +                      integerDisplay = number
 +                      fractionalDisplay = ""
 +              elif "e-" in fractionalDisplay and 0.0 < integer:
 +                      integerDisplay = number
 +                      fractionalDisplay = ""
 +              elif "e-" in fractionalDisplay:
 +                      integerDisplay = ""
 +                      fractionalDisplay = number
 +              else:
 +                      integerDisplay = integerDisplay.split(".", 1)[0] + "."
 +                      fractionalDisplay = fractionalDisplay.rsplit(".", 1)[-1]
 +
 +      return integerDisplay, fractionalDisplay
 +
 +
 +class Gonvert(object):
 +
 +      _glade_files = [
 +              os.path.join(os.path.dirname(__file__), "gonvert.glade"),
 +              os.path.join(os.path.dirname(__file__), "../data/gonvert.glade"),
 +              os.path.join(os.path.dirname(__file__), "../lib/gonvert.glade"),
 +              '/usr/share/gonvert/gonvert.glade',
 +              '/usr/lib/gonvert/gonvert.glade',
 +      ]
 +
 +      UNITS_NAME_IDX = 0
 +      UNITS_VALUE_IDX = 1
 +      UNITS_SYMBOL_IDX = 2
 +      UNITS_INTEGER_IDX = 3
 +      UNITS_FRACTION_IDX = 4
 +
 +      def __init__(self):
 +              self._unitDataInCategory = None
 +              self._unit_sort_direction = False
 +              self._value_sort_direction = False
 +              self._units_sort_direction = False
 +              self.__isPortrait = False
 +              self._isFullScreen = False
 +              self._clipboard = gtk.clipboard_get()
 +
 +              self._find_result = [] # empty find result list
 +              self._findIndex = 0 # default to find result number zero
 +
 +              self._selectedCategoryName = '' # preset to no selected category
 +              self._defaultUnitForCategory = {} # empty dictionary for later use
 +
 +              #check to see if glade file is in current directory (user must be
 +              # running from download untar directory)
 +              for gladePath in self._glade_files:
 +                      if os.path.isfile(gladePath):
 +                              homepath = os.path.dirname(gladePath)
 +                              pixmapspath = "/".join((homepath, "pixmaps"))
 +                              widgets = gtk.glade.XML(gladePath)
 +                              break
 +              else:
 +                      _moduleLogger.error("UI Descriptor not found!")
 +                      gtk.main_quit()
 +                      return
 +
 +              self._mainWindow = widgets.get_widget('mainWindow')
 +              self._app = hildonize.get_app_class()()
 +              self._mainWindow = hildonize.hildonize_window(self._app, self._mainWindow)
 +
 +              change_menu_label(widgets, 'fileMenuItem', _('File'))
 +              change_menu_label(widgets, 'exitMenuItem', _('Exit'))
 +              change_menu_label(widgets, 'helpMenuItem', _('Help'))
 +              change_menu_label(widgets, 'aboutMenuItem', _('About'))
 +
 +              self._categorySelectionButton = widgets.get_widget("categorySelectionButton")
 +              self._categoryView = widgets.get_widget('categoryView')
 +
 +              self._unitsView = widgets.get_widget('unitsView')
 +              self._unitsView.set_property('rules_hint', 1)
 +              self._unitsView_selection = self._unitsView.get_selection()
 +
 +              self._unitName = widgets.get_widget('unitName')
 +              self._unitValue = widgets.get_widget('unitValue')
 +              self._previousUnitName = widgets.get_widget('previousUnitName')
 +              self._previousUnitValue = widgets.get_widget('previousUnitValue')
 +
 +              self._unitSymbol = widgets.get_widget('unitSymbol')
 +              self._previousUnitSymbol = widgets.get_widget('previousUnitSymbol')
 +
 +              self._unitDescription = widgets.get_widget('unitDescription')
 +
 +              self._searchLayout = widgets.get_widget('searchLayout')
 +              self._searchLayout.hide()
 +              self._findEntry = widgets.get_widget('findEntry')
 +              self._findLabel = widgets.get_widget('findLabel')
 +              self._findButton = widgets.get_widget('findButton')
 +              self._closeSearchButton = widgets.get_widget('closeSearchButton')
 +
 +              self._unitsNameRenderer = gtk.CellRendererText()
 +              self._unitsNameRenderer.set_property("scale", 0.75)
 +              if constants.FORCE_HILDON_LIKE:
 +                      self._unitsNameRenderer.set_property("ellipsize", pango.ELLIPSIZE_END)
 +                      self._unitsNameRenderer.set_property("width-chars", 5)
 +              self._unitNameColumn = gtk.TreeViewColumn(_('Name'), self._unitsNameRenderer)
 +              self._unitNameColumn.set_property('resizable', True)
 +              self._unitNameColumn.add_attribute(self._unitsNameRenderer, 'text', self.UNITS_NAME_IDX)
 +              self._unitNameColumn.set_clickable(True)
 +              self._unitNameColumn.connect("clicked", self._on_click_unit_column)
 +              self._unitsView.append_column(self._unitNameColumn)
 +
 +              renderer = gtk.CellRendererText()
 +              renderer.set_property("xalign", 1.0)
 +              renderer.set_property("alignment", pango.ALIGN_RIGHT)
 +              hildonize.set_cell_thumb_selectable(renderer)
 +              self._unitIntegerColumn = gtk.TreeViewColumn(_('Value'), renderer)
 +              self._unitIntegerColumn.set_property('resizable', True)
 +              self._unitIntegerColumn.add_attribute(renderer, 'text', self.UNITS_INTEGER_IDX)
 +              self._unitIntegerColumn.set_clickable(True)
 +              self._unitIntegerColumn.connect("clicked", self._on_click_unit_column)
 +              self._unitsView.append_column(self._unitIntegerColumn)
 +
 +              renderer = gtk.CellRendererText()
 +              renderer.set_property("xalign", 0.0)
 +              renderer.set_property("alignment", pango.ALIGN_LEFT)
 +              renderer.set_property("scale", 0.75)
 +              self._unitFractionalColumn = gtk.TreeViewColumn(_(''), renderer)
 +              self._unitFractionalColumn.set_property('resizable', True)
 +              self._unitFractionalColumn.add_attribute(renderer, 'text', self.UNITS_FRACTION_IDX)
 +              self._unitFractionalColumn.set_clickable(True)
 +              self._unitFractionalColumn.connect("clicked", self._on_click_unit_column)
 +              self._unitsView.append_column(self._unitFractionalColumn)
 +
 +              renderer = gtk.CellRendererText()
 +              renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
 +              #renderer.set_property("scale", 0.5)
 +              self._unitSymbolColumn = gtk.TreeViewColumn(_('Units'), renderer)
 +              self._unitSymbolColumn.set_property('resizable', True)
 +              self._unitSymbolColumn.add_attribute(renderer, 'text', self.UNITS_SYMBOL_IDX)
 +              self._unitSymbolColumn.set_clickable(True)
 +              self._unitSymbolColumn.connect("clicked", self._on_click_unit_column)
 +              self._unitsView.append_column(self._unitSymbolColumn)
 +
 +              self._unitModel = gtk.ListStore(
 +                      gobject.TYPE_STRING, # UNITS_NAME_IDX
 +                      gobject.TYPE_STRING, # UNITS_VALUE_IDX
 +                      gobject.TYPE_STRING, # UNITS_SYMBOL_IDX
 +                      gobject.TYPE_STRING, # UNITS_INTEGER_IDX
 +                      gobject.TYPE_STRING, # UNITS_FRACTION_IDX
 +              )
 +              self._sortedUnitModel = gtk.TreeModelSort(self._unitModel)
 +              columns = self._get_column_sort_stuff()
 +              for columnIndex, (column, sortDirection, col_cmp) in enumerate(columns):
 +                      self._sortedUnitModel.set_sort_func(columnIndex, col_cmp)
 +              self._unitsView.set_model(self._sortedUnitModel)
 +
 +              #Insert a column into the category list even though the heading will not be seen
 +              renderer = gtk.CellRendererText()
 +              self._categoryColumn = gtk.TreeViewColumn('Title', renderer)
 +              self._categoryColumn.set_property('resizable', 1)
 +              self._categoryColumn.add_attribute(renderer, 'text', 0)
 +              self._categoryView.append_column(self._categoryColumn)
 +
 +              self._categoryModel = gtk.ListStore(gobject.TYPE_STRING)
 +              self._categoryView.set_model(self._categoryModel)
 +              #colourize each row differently for easier reading
 +              self._categoryView.set_property('rules_hint', 1)
 +
 +              #Populate the catagories list
 +              for key in unit_data.UNIT_CATEGORIES:
 +                      row = (key, )
 +                      self._categoryModel.append(row)
 +
 +              #--------- connections to GUI ----------------
 +              self._mainWindow.connect("destroy", self._on_user_exit)
 +              self._mainWindow.connect("key-press-event", self._on_key_press)
 +              self._mainWindow.connect("window-state-event", self._on_window_state_change)
 +              self._categorySelectionButton.connect("clicked", self._on_category_selector_clicked)
 +              self._categoryView.connect("cursor-changed", self._on_click_category)
 +              self._findButton.connect("clicked", self._on_find_activate)
 +              self._findEntry.connect("activate", self._on_find_activate)
 +              self._findEntry.connect("changed", self._on_findEntry_changed)
 +              self._closeSearchButton.connect("clicked", self._on_toggle_search)
 +              self._previousUnitValue.connect("changed", self._on_previous_unit_value_changed)
 +              self._unitValue.connect("changed", self._on_unit_value_changed)
 +              self._unitValue.connect("key-press-event", self._on_browse_key_press)
 +              self._unitsView.connect("cursor-changed", self._on_click_unit)
 +              self._unitsView.connect("key-press-event", self._on_browse_key_press)
 +              if hildonize.GTK_MENU_USED:
 +                      widgets.get_widget("aboutMenuItem").connect("activate", self._on_about_clicked)
 +                      widgets.get_widget("searchMenuItem").connect("activate", self._on_toggle_search)
 +                      widgets.get_widget("exitMenuItem").connect("activate", self._on_user_exit)
 +
 +              for scrollingWidgetName in (
 +                      "unitsViewScrolledWindow",
 +              ):
 +                      scrollingWidget = widgets.get_widget(scrollingWidgetName)
 +                      assert scrollingWidget is not None, scrollingWidgetName
 +                      scroller = hildonize.hildonize_scrollwindow(scrollingWidget)
 +                      scroller.show_all()
 +
 +              # Simplify the UI
 +              if hildonize.IS_HILDON_SUPPORTED or constants.FORCE_HILDON_LIKE:
 +                      self._categoryView.get_parent().hide()
 +                      self._unitsView.set_headers_visible(False)
 +                      self._previousUnitName.get_parent().hide()
 +                      self._unitDescription.get_parent().get_parent().hide()
 +              else:
 +                      self._categorySelectionButton.hide()
 +
 +              menu = hildonize.hildonize_menu(
 +                      self._mainWindow,
 +                      widgets.get_widget("mainMenuBar"),
 +              )
 +              if not hildonize.GTK_MENU_USED:
 +                      button = gtk.Button("Search")
 +                      button.connect("clicked", self._on_toggle_search)
 +                      menu.append(button)
 +
 +                      button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, None)
 +                      button.set_label("Name")
 +                      menu.add_filter(button)
 +                      button.connect("clicked", self._on_click_menu_filter, self._unitNameColumn)
 +                      button.set_mode(False)
 +                      filterGroup = button
 +
 +                      button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, filterGroup)
 +                      button.set_label("Value")
 +                      menu.add_filter(button)
 +                      button.connect("clicked", self._on_click_menu_filter, self._unitIntegerColumn)
 +                      button.set_mode(False)
 +
 +                      button = hildonize.hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, filterGroup)
 +                      button.set_label("Unit")
 +                      menu.add_filter(button)
 +                      button.connect("clicked", self._on_click_menu_filter, self._unitSymbolColumn)
 +                      button.set_mode(False)
 +
 +                      menu.show_all()
 +
 +              if not hildonize.IS_HILDON_SUPPORTED:
 +                      _moduleLogger.info("No hildonization support")
 +
-               hildonize.set_application_title(
-                       self._mainWindow, "%s - Unit Conversion Utility" % constants.__pretty_app_name__
++              hildonize.set_application_name(
++                      "%s - Unit Conversion Utility" % constants.__pretty_app_name__
 +              )
 +              iconPath = pixmapspath + '/gonvert.png'
 +              if os.path.exists(iconPath):
 +                      self._mainWindow.set_icon(gtk.gdk.pixbuf_new_from_file(iconPath))
 +              else:
 +                      _moduleLogger.warn("Error: Could not find gonvert icon: %s" % iconPath)
 +
 +              self._load_settings()
 +              self._mainWindow.show()
 +
 +      def _load_settings(self):
 +              #Restore window size from previously saved settings if it exists and is valid.
 +              windowDatPath = "/".join((constants._data_path_, "window.dat"))
 +              if os.path.exists(windowDatPath):
 +                      saved_window = pickle.load(open(windowDatPath, "r"))
 +                      try:
 +                              a, b = saved_window['size']
 +                      except KeyError:
 +                              pass
 +                      else:
 +                              self._mainWindow.resize(a, b)
 +                      try:
 +                              isFullscreen = saved_window["isFullscreen"]
 +                      except KeyError:
 +                              pass
 +                      else:
 +                              if isFullscreen:
 +                                      self._mainWindow.fullscreen()
 +                      try:
 +                              isPortrait = saved_window["isPortrait"]
 +                      except KeyError:
 +                              pass
 +                      else:
 +                              if isPortrait ^ self.__isPortrait:
 +                                      if isPortrait:
 +                                              orientation = gtk.ORIENTATION_VERTICAL
 +                                      else:
 +                                              orientation = gtk.ORIENTATION_HORIZONTAL
 +                                      self.set_orientation(orientation)
 +
 +              #Restore selections from previously saved settings if it exists and is valid.
 +              categoryIndex = 0
 +              selectedCategoryName = unit_data.UNIT_CATEGORIES[0]
 +              selectionsDatPath = "/".join((constants._data_path_, "selections.dat"))
 +              if os.path.exists(selectionsDatPath):
 +                      selections = pickle.load(open(selectionsDatPath, 'r'))
 +                      try:
 +                              self._defaultUnitForCategory = selections['selected_units']
 +                      except KeyError:
 +                              pass
 +
 +                      try:
 +                              selectedCategoryName = selections['selected_category']
 +                      except KeyError:
 +                              pass
 +                      else:
 +                              try:
 +                                      categoryIndex = unit_data.UNIT_CATEGORIES.index(selectedCategoryName)
 +                              except ValueError:
 +                                      _moduleLogger.warn("Unknown category: %s" % selectedCategoryName)
 +
 +              self._categorySelectionButton.get_child().set_markup("<big>%s</big>" % selectedCategoryName)
 +              self._categoryView.set_cursor(categoryIndex, self._categoryColumn, False)
 +              self._categoryView.grab_focus()
 +
 +              self._select_default_unit()
 +
 +      def _save_settings(self):
 +              """
 +              This routine saves the selections to a file, and
 +              should therefore only be called when exiting the program.
 +
 +              Update selections dictionary which consists of the following keys:
 +              'self._selectedCategoryName': full name of selected category
 +              'self._defaultUnitForCategory': self._defaultUnitForCategory dictionary which contains:
 +              [categoryname: #1 displayed unit, #2 displayed unit]
 +              """
 +              #Determine the contents of the selected category row
 +              selected, iter = self._categoryView.get_selection().get_selected()
 +              self._selectedCategoryName = self._categoryModel.get_value(iter, 0)
 +
 +              selections = {
 +                      'selected_category': self._selectedCategoryName,
 +                      'selected_units': self._defaultUnitForCategory
 +              }
 +              selectionsDatPath = "/".join((constants._data_path_, "selections.dat"))
 +              pickle.dump(selections, open(selectionsDatPath, 'w'))
 +
 +              #Get last size of app and save it
 +              window_settings = {
 +                      'size': self._mainWindow.get_size(),
 +                      "isFullscreen": self._isFullScreen,
 +                      "isPortrait": self.__isPortrait,
 +              }
 +              windowDatPath = "/".join((constants._data_path_, "window.dat"))
 +              pickle.dump(window_settings, open(windowDatPath, 'w'))
 +
 +      def _refresh_columns(self):
 +              self._unitsView.remove_column(self._unitNameColumn)
 +              self._unitsView.remove_column(self._unitIntegerColumn)
 +              self._unitsView.remove_column(self._unitFractionalColumn)
 +              self._unitsView.remove_column(self._unitSymbolColumn)
 +
 +              self._unitsView.append_column(self._unitNameColumn)
 +              self._unitsView.append_column(self._unitIntegerColumn)
 +              self._unitsView.append_column(self._unitFractionalColumn)
 +              self._unitsView.append_column(self._unitSymbolColumn)
 +
 +      def _clear_find(self):
 +              # switch to "new find" state
 +              self._find_result = []
 +              self._findIndex = 0
 +
 +              # Clear our user message
 +              self._findLabel.set_text('')
 +
 +      def _find_first(self):
 +              assert len(self._find_result) == 0
 +              assert self._findIndex == 0
 +              findString = self._findEntry.get_text().strip().lower()
 +              if not findString:
 +                      return
 +
 +              # Gather info on all the matching units from all categories
 +              for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
 +                      units = unit_data.get_units(category)
 +                      for unitIndex, unit in enumerate(units):
 +                              loweredUnit = unit.lower()
 +                              if loweredUnit in findString or findString in loweredUnit:
 +                                      self._find_result.append((category, unit, catIndex, unitIndex))
 +
 +      def _update_find_selection(self):
 +              assert 0 < len(self._find_result)
 +
 +              #check if next find is in a new category (prevent category changes when unnecessary
 +              searchCategoryName = self._find_result[self._findIndex][0]
 +              if self._selectedCategoryName != searchCategoryName:
 +                      self._categorySelectionButton.get_child().set_markup("<big>%s</big>" % searchCategoryName)
 +                      self._categoryView.set_cursor(
 +                              self._find_result[self._findIndex][2], self._categoryColumn, False
 +                      )
 +
 +              self._unitsView.set_cursor(
 +                      self._find_result[self._findIndex][3], self._unitNameColumn, True
 +              )
 +
 +      def _find_next(self):
 +              if len(self._find_result) == 0:
 +                      self._find_first()
 +              else:
 +                      if self._findIndex == len(self._find_result)-1:
 +                              self._findIndex = 0
 +                      else:
 +                              self._findIndex += 1
 +
 +              if not self._find_result:
 +                      self._findLabel.set_text('Text not found')
 +              else:
 +                      self._update_find_selection()
 +                      resultsLeft = len(self._find_result) - self._findIndex - 1
 +                      self._findLabel.set_text(
 +                              '%s result(s) left' % (resultsLeft, )
 +                      )
 +
 +      def _find_previous(self):
 +              if len(self._find_result) == 0:
 +                      self._find_first()
 +              else:
 +                      if self._findIndex == 0:
 +                              self._findIndex = len(self._find_result)-1
 +                      else:
 +                              self._findIndex -= 1
 +
 +              if not self._find_result:
 +                      self._findLabel.set_text('Text not found')
 +              else:
 +                      self._update_find_selection()
 +                      resultsLeft = len(self._find_result) - self._findIndex - 1
 +                      self._findLabel.set_text(
 +                              '%s result(s) left' % (resultsLeft, )
 +                      )
 +
 +      def _toggle_find(self):
 +              if self._searchLayout.get_property("visible"):
 +                      self._searchLayout.hide()
 +                      self._unitsView.grab_focus()
 +              else:
 +                      self._searchLayout.show()
 +                      self._findEntry.grab_focus()
 +
 +      def _unit_model_cmp(self, sortedModel, leftItr, rightItr):
 +              leftUnitText = self._unitModel.get_value(leftItr, self.UNITS_NAME_IDX)
 +              rightUnitText = self._unitModel.get_value(rightItr, self.UNITS_NAME_IDX)
 +              return cmp(leftUnitText, rightUnitText)
 +
 +      def _symbol_model_cmp(self, sortedModel, leftItr, rightItr):
 +              leftSymbolText = self._unitModel.get_value(leftItr, self.UNITS_SYMBOL_IDX)
 +              rightSymbolText = self._unitModel.get_value(rightItr, self.UNITS_SYMBOL_IDX)
 +              return cmp(leftSymbolText, rightSymbolText)
 +
 +      def _value_model_cmp(self, sortedModel, leftItr, rightItr):
 +              #special sorting exceptions for ascii values (instead of float values)
 +              if self._selectedCategoryName == "Computer Numbers":
 +                      leftValue = self._unitModel.get_value(leftItr, self.UNITS_VALUE_IDX)
 +                      rightValue = self._unitModel.get_value(rightItr, self.UNITS_VALUE_IDX)
 +              else:
 +                      leftValueText = self._unitModel.get_value(leftItr, self.UNITS_VALUE_IDX)
 +                      leftValue = float(leftValueText) if leftValueText else 0.0
 +
 +                      rightValueText = self._unitModel.get_value(rightItr, self.UNITS_VALUE_IDX)
 +                      rightValue = float(rightValueText) if rightValueText else 0.0
 +              return cmp(leftValue, rightValue)
 +
 +      def _get_column_sort_stuff(self):
 +              columns = (
 +                      (self._unitNameColumn, "_unit_sort_direction", self._unit_model_cmp),
 +                      (self._unitIntegerColumn, "_value_sort_direction", self._value_model_cmp),
 +                      (self._unitFractionalColumn, "_value_sort_direction", self._value_model_cmp),
 +                      (self._unitSymbolColumn, "_units_sort_direction", self._symbol_model_cmp),
 +              )
 +              return columns
 +
 +      def _switch_category(self, category):
 +              self._selectedCategoryName = category
 +              self._unitDataInCategory = unit_data.UNIT_DESCRIPTIONS[self._selectedCategoryName]
 +
 +              #Fill up the units descriptions and clear the value cells
 +              self._clear_visible_unit_data()
 +              nameLength = 0
 +              for key in unit_data.get_units(self._selectedCategoryName):
 +                      row = key, '0.0', self._unitDataInCategory[key][1], '0.', '0'
 +                      self._unitModel.append(row)
 +                      nameLength = max(nameLength, len(key))
 +              self._sortedUnitModel.sort_column_changed()
 +
 +              if constants.FORCE_HILDON_LIKE:
 +                      maxCatCharWidth = int(nameLength * 0.75)
 +                      maxCharWidth = int(len("nibble | hexit | quadbit") * 0.75)
 +                      charWidth = min(maxCatCharWidth, maxCharWidth)
 +                      self._unitsNameRenderer.set_property("width-chars", charWidth)
 +
 +              self._select_default_unit()
 +
 +      def _clear_visible_unit_data(self):
 +              self._unitDescription.get_buffer().set_text("")
 +              self._unitName.set_text('')
 +              self._unitValue.set_text('')
 +              self._unitSymbol.set_text('')
 +
 +              self._previousUnitName.set_text('')
 +              self._previousUnitValue.set_text('')
 +              self._previousUnitSymbol.set_text('')
 +
 +              self._unitModel.clear()
 +
 +      def _select_default_unit(self):
 +              # Restore the previous historical settings of previously selected units
 +              # in this newly selected category
 +              defaultPrimary = unit_data.get_base_unit(self._selectedCategoryName)
 +              defaultSecondary = ""
 +              if self._selectedCategoryName in self._defaultUnitForCategory:
 +                      if self._defaultUnitForCategory[self._selectedCategoryName][0]:
 +                              defaultPrimary = self._defaultUnitForCategory[self._selectedCategoryName][0]
 +                      if self._defaultUnitForCategory[self._selectedCategoryName][1]:
 +                              defaultSecondary = self._defaultUnitForCategory[self._selectedCategoryName][1]
 +
 +              units = unit_data.get_units(self._selectedCategoryName)
 +
 +              #Restore oldest selection first.
 +              if defaultPrimary:
 +                      try:
 +                              unitIndex = units.index(defaultPrimary)
 +                      except ValueError:
 +                              unitIndex = 0
 +                      self._unitsView.set_cursor(unitIndex, self._unitNameColumn, True)
 +
 +              #Restore newest selection second.
 +              if defaultSecondary:
 +                      try:
 +                              unitIndex = units.index(defaultSecondary)
 +                      except ValueError:
 +                              unitIndex = 0
 +                      self._unitsView.set_cursor(unitIndex, self._unitNameColumn, True)
 +
 +              # select the text so user can start typing right away
 +              self._unitValue.grab_focus()
 +              self._unitValue.select_region(0, -1)
 +
 +      def _sanitize_value(self, userEntry):
 +              if self._selectedCategoryName == "Computer Numbers":
 +                      if userEntry == '':
 +                              value = '0'
 +                      else:
 +                              value = userEntry
 +              else:
 +                      if userEntry == '':
 +                              value = 0.0
 +                      else:
 +                              value = float(userEntry)
 +              return value
 +
 +      def _select_sort_column(self, col):
 +              #Determine which column requires sorting
 +              columns = self._get_column_sort_stuff()
 +              for columnIndex, (maybeCol, directionName, col_cmp) in enumerate(columns):
 +                      if col is maybeCol:
 +                              direction = getattr(self, directionName)
 +                              gtkDirection = gtk.SORT_ASCENDING if direction else gtk.SORT_DESCENDING
 +
 +                              # cause a sort
 +                              self._sortedUnitModel.set_sort_column_id(columnIndex, gtkDirection)
 +
 +                              # set the visual for sorting
 +                              col.set_sort_indicator(True)
 +                              col.set_sort_order(not direction)
 +
 +                              setattr(self, directionName, not direction)
 +                              break
 +                      else:
 +                              maybeCol.set_sort_indicator(False)
 +              else:
 +                      assert False, "Unknown column: %s" % (col.get_title(), )
 +
 +      def set_orientation(self, orientation):
 +              if orientation == gtk.ORIENTATION_VERTICAL:
 +                      hildonize.window_to_portrait(self._mainWindow)
 +                      self.__isPortrait = True
 +              elif orientation == gtk.ORIENTATION_HORIZONTAL:
 +                      hildonize.window_to_landscape(self._mainWindow)
 +                      self.__isPortrait = False
 +              else:
 +                      raise NotImplementedError(orientation)
 +
 +      def get_orientation(self):
 +              return gtk.ORIENTATION_VERTICAL if self.__isPortrait else gtk.ORIENTATION_HORIZONTAL
 +
 +      def _toggle_rotate(self):
 +              if self.__isPortrait:
 +                      self.set_orientation(gtk.ORIENTATION_HORIZONTAL)
 +              else:
 +                      self.set_orientation(gtk.ORIENTATION_VERTICAL)
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_key_press(self, widget, event, *args):
 +              """
 +              @note Hildon specific
 +              """
 +              RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
 +              if (
 +                      event.keyval == gtk.keysyms.F6 or
 +                      event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
 +              ):
 +                      if self._isFullScreen:
 +                              self._mainWindow.unfullscreen()
 +                      else:
 +                              self._mainWindow.fullscreen()
 +              elif event.keyval == gtk.keysyms.f and event.get_state() & gtk.gdk.CONTROL_MASK:
 +                      if not hildonize.GTK_MENU_USED:
 +                              self._toggle_find()
 +              elif event.keyval == gtk.keysyms.p and event.get_state() & gtk.gdk.CONTROL_MASK:
 +                      self._find_previous()
 +              elif event.keyval == gtk.keysyms.n and event.get_state() & gtk.gdk.CONTROL_MASK:
 +                      self._find_next()
 +              elif event.keyval == gtk.keysyms.o and event.get_state() & gtk.gdk.CONTROL_MASK:
 +                      self._toggle_rotate()
 +              elif (
 +                      event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
 +                      event.get_state() & gtk.gdk.CONTROL_MASK
 +              ):
 +                      self._mainWindow.destroy()
 +              elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
 +                      with open(constants._user_logpath_, "r") as f:
 +                              logLines = f.xreadlines()
 +                              log = "".join(logLines)
 +                              self._clipboard.set_text(str(log))
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_toggle_search(self, *args):
 +              self._toggle_find()
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_browse_key_press(self, widget, event, *args):
 +              if event.keyval == gtk.keysyms.uparrow or event.keyval == gtk.keysyms.Up:
 +                      index, column = self._unitsView.get_cursor()
 +                      newIndex = max(index[0]-1, 0)
 +                      path = (newIndex, )
 +                      self._unitsView.set_cursor(path, column, True)
 +                      self._unitsView.scroll_to_cell(path, column, False, 0, 0)
 +                      return True # override default behavior
 +              elif event.keyval == gtk.keysyms.downarrow or event.keyval == gtk.keysyms.Down:
 +                      index, column = self._unitsView.get_cursor()
 +                      newIndex = min(index[0]+1, len(self._unitModel)-1)
 +                      path = (newIndex, )
 +                      self._unitsView.set_cursor(path, column, True)
 +                      self._unitsView.scroll_to_cell(path, column, False, 0, 0)
 +                      return True # override default behavior
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_window_state_change(self, widget, event, *args):
 +              """
 +              @note Hildon specific
 +              """
 +              if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
 +                      self._isFullScreen = True
 +              else:
 +                      self._isFullScreen = False
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_findEntry_changed(self, *args):
 +              """
 +              Clear out find results since the user wants to look for something new
 +              """
 +              self._clear_find()
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_find_activate(self, *args):
 +              self._find_next()
 +              self._findButton.grab_focus()
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_click_menu_filter(self, button, col):
 +              self._select_sort_column(col)
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_click_unit_column(self, col):
 +              """
 +              Sort the contents of the col when the user clicks on the title.
 +              """
 +              self._select_sort_column(col)
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_category_selector_clicked(self, *args):
 +              currenntIndex = unit_data.UNIT_CATEGORIES.index(self._selectedCategoryName)
 +              newIndex = hildonize.touch_selector(
 +                      self._mainWindow,
 +                      "Categories",
 +                      unit_data.UNIT_CATEGORIES,
 +                      currenntIndex,
 +              )
 +
 +              selectedCategoryName = unit_data.UNIT_CATEGORIES[newIndex]
 +              self._categorySelectionButton.get_child().set_markup("<big>%s</big>" % selectedCategoryName)
 +              self._switch_category(selectedCategoryName)
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_click_category(self, *args):
 +              selected, iter = self._categoryView.get_selection().get_selected()
 +              if iter is None:
 +                      # User is typing in an invalid string, not selecting any category
 +                      return
 +              selectedCategory = self._categoryModel.get_value(iter, 0)
 +              self._switch_category(selectedCategory)
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_click_unit(self, *args):
 +              selected, iter = self._unitsView.get_selection().get_selected()
 +              selected_unit = selected.get_value(iter, self.UNITS_NAME_IDX)
 +              unit_spec = self._unitDataInCategory[selected_unit]
 +
 +              showSymbol = False
 +
 +              if self._unitName.get_text() != selected_unit:
 +                      self._previousUnitName.set_text(self._unitName.get_text())
 +                      self._previousUnitValue.set_text(self._unitValue.get_text())
 +                      self._previousUnitSymbol.set_text(self._unitSymbol.get_text())
 +                      if self._unitSymbol.get_text():
 +                              showSymbol = True
 +
 +              self._unitName.set_text(selected_unit)
 +              self._unitValue.set_text(selected.get_value(iter, self.UNITS_VALUE_IDX))
 +              buffer = self._unitDescription.get_buffer()
 +              buffer.set_text(unit_spec[2])
 +              self._unitSymbol.set_text(unit_spec[1]) # put units into label text
 +              if unit_spec[1]:
 +                      showSymbol = True
 +              else:
 +                      showSymbol = False
 +
 +              if showSymbol:
 +                      self._unitSymbol.show()
 +                      self._previousUnitSymbol.show()
 +              else:
 +                      self._unitSymbol.hide()
 +                      self._previousUnitSymbol.hide()
 +
 +              if self._unitValue.get_text() == '':
 +                      if self._selectedCategoryName == "Computer Numbers":
 +                              self._unitValue.set_text("0")
 +                      else:
 +                              self._unitValue.set_text("0.0")
 +
 +              self._defaultUnitForCategory[self._selectedCategoryName] = [
 +                      self._unitName.get_text(), self._previousUnitName.get_text()
 +              ]
 +
 +              # select the text so user can start typing right away
 +              self._unitValue.grab_focus()
 +              self._unitValue.select_region(0, -1)
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_unit_value_changed(self, *args):
 +              if self._unitName.get_text() == '':
 +                      return
 +              if not self._unitValue.is_focus():
 +                      return
 +
 +              #retrieve the conversion function and value from the selected unit
 +              value = self._sanitize_value(self._unitValue.get_text())
 +              func, arg = self._unitDataInCategory[self._unitName.get_text()][0]
 +              base = func.to_base(value, arg)
 +
 +              #point to the first row
 +              for row in self._unitModel:
 +                      func, arg = self._unitDataInCategory[row[self.UNITS_NAME_IDX]][0]
 +                      newValue = func.from_base(base, arg)
 +
 +                      newValueDisplay = str(newValue)
 +                      integerDisplay, fractionalDisplay = split_number(newValue)
 +
 +                      row[self.UNITS_VALUE_IDX] = newValueDisplay
 +                      row[self.UNITS_INTEGER_IDX] = integerDisplay
 +                      row[self.UNITS_FRACTION_IDX] = fractionalDisplay
 +
 +              # Update the secondary unit entry
 +              if self._previousUnitName.get_text() != '':
 +                      func, arg = self._unitDataInCategory[self._previousUnitName.get_text()][0]
 +                      self._previousUnitValue.set_text(str(func.from_base(base, arg, )))
 +
 +              self._sortedUnitModel.sort_column_changed()
 +              self._refresh_columns()
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_previous_unit_value_changed(self, *args):
 +              if self._previousUnitName.get_text() == '':
 +                      return
 +              if not self._previousUnitValue.is_focus():
 +                      return
 +
 +              #retrieve the conversion function and value from the selected unit
 +              value = self._sanitize_value(self._previousUnitValue.get_text())
 +              func, arg = self._unitDataInCategory[self._previousUnitName.get_text()][0]
 +              base = func.to_base(value, arg)
 +
 +              #point to the first row
 +              for row in self._unitModel:
 +                      func, arg = self._unitDataInCategory[row[self.UNITS_NAME_IDX]][0]
 +                      newValue = func.from_base(base, arg)
 +
 +                      newValueDisplay = str(newValue)
 +                      integerDisplay, fractionalDisplay = split_number(newValue)
 +
 +                      row[self.UNITS_VALUE_IDX] = newValueDisplay
 +                      row[self.UNITS_INTEGER_IDX] = integerDisplay
 +                      row[self.UNITS_FRACTION_IDX] = fractionalDisplay
 +
 +              # Update the primary unit entry
 +              func, arg = self._unitDataInCategory[self._unitName.get_text()][0]
 +              self._unitValue.set_text(str(func.from_base(base, arg, )))
 +
 +              self._sortedUnitModel.sort_column_changed()
 +              self._refresh_columns()
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_about_clicked(self, a):
 +              dlg = gtk.AboutDialog()
 +              dlg.set_name(constants.__pretty_app_name__)
 +              dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
 +              dlg.set_copyright("Copyright 2009 - GPL")
 +              dlg.set_comments("")
 +              dlg.set_website("http://unihedron.com/projects/gonvert/gonvert.php")
 +              dlg.set_authors(["Anthony Tekatch <anthony@unihedron.com>", "Ed Page <eopage@byu.net> (Blame him for the most recent bugs)"])
 +              dlg.run()
 +              dlg.destroy()
 +
 +      @gtk_toolbox.log_exception(_moduleLogger)
 +      def _on_user_exit(self, *args):
 +              try:
 +                      self._save_settings()
 +              except Exception:
 +                      pass
 +              finally:
 +                      gtk.main_quit()
 +
 +
 +def run_gonvert():
 +      gtk.gdk.threads_init()
 +      if hildonize.IS_HILDON_SUPPORTED:
 +              gtk.set_application_name(constants.__pretty_app_name__)
 +      handle = Gonvert()
 +      if not constants.PROFILE_STARTUP:
 +              gtk.main()
 +
 +
 +if __name__ == "__main__":
 +      logging.basicConfig(level = logging.DEBUG)
 +      try:
 +              os.makedirs(constants._data_path_)
 +      except OSError, e:
 +              if e.errno != 17:
 +                      raise
 +
 +      run_gonvert()
Simple merge
index 77d585a,339eb2a..339eb2a
mode 100755,100644..100755
@@@ -19,132 -22,7 +19,9 @@@ __email__ = "anthony@unihedron.com
  __version__ = constants.__version__
  __build__ = constants.__build__
  __changelog__ = """
 -REPLACEME
 +0.9.3
 +* Rotation support through Ctrl+o
 +* Switching from scrollbar to panning
- 0.9.2
- * Added search toggle to the menu
- * Maemo 5: Added sorting to the app menu
- 0.9.1
- * Added support for creating generic .deb files
- * Added an apothecary unit
- * Bug fix: Can directly enter numbers after selecting category
- * Bug fix: font of the category button was inconsistent
- * Bug fix: Improved up/down arrow keys
- 0.9.0
- * Added Radioactivity and Radiation dose categories.
- * Aligning the numbers by their decimal place
- * Added shortcuts for fullscreen
- * Switched to Find being brought up by CTRL+F
- * Added Find Previous and Find Next shortcuts (CTRL+P, CTRL+N)
- * Adjusted the sizing on various widgets
- * Removed unused UI features for polish
- * Bug fix: improved behavior when corner case values are inputted (like floats for base conversions)
- * Debugging: Added logging support
- * Marketting: Huge version bump to express a basic level of feature complete
- * Internal: Massive cleanup of code
- 0.2.23  - Added UK currency category and other UK measurements thanks to Dale Hair
- 0.2.22  - Restore previously used window size
- 0.2.21  - Category column widened. Maximize on start.
- 0.2.20  - correction in micron pressure conversion
- 0.2.19  - viscosity cP conversion correction
- 0.2.18        - addition of magnitudes per square arcsecond to Luminance category
- 0.2.17        - updated baud definitions
-       - fixed homepath location because new debian version changed
- 0.2.16        - fixed icon locating for display in about
-       - added alternate icon gonvert-icon_alernative.png (copy over gonvert.png)
- 0.2.15        - updated mainloop to main as discovered by Alexander Skwar
- 0.2.14        - added Calgary energy and volume suggestions per Kim Lux
- 0.2.13        - new more easily understandable icon
-       - nanotesla definition (nT).
-       - added shortlist feature.
- 0.2.12        - removed inoperable books feature.
-       - fixed up acre accuracy.
- 0.2.11        - miodified descriprion for silver, newton, sadzhens.
- 0.2.10        - \x90 changed to \u00C9 for Emile and similar for Reaumur utf-8 text.
-       - Added translation for "All" book text.
-       - The write units text is translatable.
-       - The pl_messages.po file has been updated
- 0.2.09        - Added utf-8 coding to all text strings in preparation for complete language translation.
- 0.2.08        - Added language translation for menus and labels.
- 0.2.07        - Added language translation changes and messages.pot.
- 0.2.06        - Fixed category list size to show preselected categorys on startup,
-         scroll window H&Vpolicy set to always.
- 0.2.05        - Spelling of Luminance category fixed.
- 0.2.04        - Modified unit clicking to force focus on value entry.
-         Modified Makefile to remove /share/share bug for desktop entry.
- 0.2.03        - Modified Makefile to allow better integration on other platforms.
- 0.2.01        - Added saved selections feature, creates ~/.gonvert/ and file. 
- 0.1.11        - fixed packaging for RPM
- 0.1.10        - added Current Loop category for PLCs and 4-20mA instrumentation.
- 0.1.9 - added kilobit, and more density units.
- 0.1.8 - Added Torque units
- 0.1.7 - Added many more pressure units
-       - Added thermal categories
-       - Added fuel consumption category
-       - Program extension to .pyw so that Windows startup without console
- 0.1.6 - add more frequency units
-       - fixed computer number bases nums was bad near "h" and "v"
-       - fixed error:
-         "GtkTextBuffer.insert_at_cursor() takes exactly 1 argument (2 given)"
-         thanks to Riccardo Galli
- 0.1.5 - put packages into /usr instead of /usr/local
-       - add gnome menu item back in
- 0.1.4 - remove dependency on gnome-config from Makefile, RPM, binary.
- 0.1.3 - touched up computer numbers units for better sorting
-       - limited up resizing of windows to prevent dissapearing areas
-       - fixed find bug that some users might notice (TreeViewColumn/None)
- 0.1.2 - Added description box when writing units
- 0.1.1 - Added help/about box
-       - fixed bug that sets focus on line 2480
-       - fixed temperature difference units labels
-       - all scroll bars only show when needed
-       - Added RPM distribution
- 0.1.0   - Major modifications for GTK2 (RedHat 8.0)
-       - addition of units column in display
-       - sorting for all units columns with sort pointer
- 0.0.15        - added Electromagnetic Radiation category
- 0.0.14        - fixed window close bug, attempt to fix libglade XML startup bug for
-           some machines
- 0.0.13        - changes for python2.2, had to remove gnome dependencies
- 0.0.12        - change contact information address
- 0.0.11        - addition of ppm to "find" utility
- 0.0.10        - addition of petabyte to computer data
- 0.0.9 - addition of cesium atom vibrations to Time category
- 0.0.8 - more accurate calculation of degrees F
- 0.0.7 - added 'Find unit' feature
-       - changed Category list to clist for ease of moveto (focus) after find
- 0.0.6 - added description for Amperes
-       - added DENSITY category
-       - added 4 new categories 101 new units
-       - added shoe size converter
-       - add a function to convert custom formulas (like area from diameter)
-         example: area = pi * (D/2)^2
-         base value = pi* (x/2)^2  #metres in diameter metres, cm, inch, foot.
- 0.0.5 - Tool for listing all categories and units to STDOUT.
-       - re-organization of project files.
-       - addition of suffixes between duodecillion and centillion.
-       - addition of Makefile to install onto Gnome based systems.
-       - sort Units or Value columns (ascending or descending)
-         by clicking on column.
- 0.0.4 - Prefixes and Suffixes addition of;
-         ppm, %, Marx brothers, various descriptions.
-       - addition of microgram to mass category.
-       - replaced base 63 with 62 from computer numbers since
-         only 62 characters can be represented.
-       - fixed error if second line has nothing it wouldn't get
-         updated.
- 0.0.3 - fix bug in labelling of base 36 (was base 37)
-         all numbering systems past 23 were at fault due
-         to improper nums string (fixed).
- 0.0.2 - Completion of second row data entry so that changes
-         to text are not cyclicly causing changes to all
-         values.
- 0.0.1 - Initial release.
  """