+++ /dev/null
-<?xml version="1.0"?>
-<glade-interface>
- <!-- interface-requires gtk+ 2.16 -->
- <!-- interface-naming-policy toplevel-contextual -->
- <widget class="GtkWindow" id="mainWindow">
- <property name="title" translatable="yes">Cluttered Calc</property>
- <property name="default_width">800</property>
- <property name="default_height">480</property>
- <child>
- <widget class="GtkVBox" id="mainLayout">
- <property name="visible">True</property>
- <child>
- <widget class="GtkMenuBar" id="mainMenubar">
- <property name="visible">True</property>
- <child>
- <widget class="GtkMenuItem" id="fileMenuItem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_File</property>
- <property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="fileMenu">
- <property name="visible">True</property>
- <child>
- <widget class="GtkImageMenuItem" id="quitMenuItem">
- <property name="label">gtk-quit</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- <signal name="activate" handler="on_calculator_quit"/>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="editMenuItem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Edit</property>
- <property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="editMenuIte">
- <property name="visible">True</property>
- <child>
- <widget class="GtkImageMenuItem" id="copyMenuItem">
- <property name="label">gtk-copy</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="copyEquationMenuItem">
- <property name="label" translatable="yes">Copy Equation</property>
- <property name="visible">True</property>
- <property name="use_stock">False</property>
- <child internal-child="image">
- <widget class="GtkImage" id="image1">
- <property name="visible">True</property>
- <property name="stock">gtk-copy</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="pasteMenuItem">
- <property name="label">gtk-paste</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- <signal name="activate" handler="on_paste"/>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="deleteMenuItem">
- <property name="label">Clear _History</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">False</property>
- <signal name="activate" handler="on_clear_history"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image2">
- <property name="visible">True</property>
- <property name="stock">gtk-delete</property>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="helpMenuItem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Help</property>
- <property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="helpMenu">
- <property name="visible">True</property>
- <child>
- <widget class="GtkImageMenuItem" id="aboutMenuItem">
- <property name="label">gtk-about</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- <signal name="activate" handler="on_about"/>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="calculatorLayout">
- <property name="visible">True</property>
- <child>
- <widget class="GtkVBox" id="historyLayout">
- <property name="visible">True</property>
- <child>
- <widget class="GtkScrolledWindow" id="scrollingHistory">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="window_placement">bottom-left</property>
- <property name="window_placement_set">True</property>
- <child>
- <widget class="GtkTreeView" id="historyView">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- <property name="reorderable">True</property>
- <property name="rules_hint">True</property>
- <property name="enable_search">False</property>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkEventBox" id="errorEventBox">
- <property name="visible">True</property>
- <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
- <child>
- <widget class="GtkHBox" id="errorBox">
- <property name="visible">True</property>
- <child>
- <widget class="GtkImage" id="errorImage">
- <property name="visible">True</property>
- <property name="stock">gtk-dialog-error</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="errorDescription">
- <property name="visible">True</property>
- <property name="use_markup">True</property>
- <property name="ellipsize">end</property>
- <property name="single_line_mode">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkImage" id="errorClose">
- <property name="visible">True</property>
- <property name="stock">gtk-close</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkEntry" id="entryView">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">False</property>
- <property name="invisible_char">●</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkVBox" id="functionLayout">
- <property name="visible">True</property>
- <child>
- <widget class="GtkButton" id="keyboardSelectionButton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="pluginKeyboard">
- <property name="visible">True</property>
- <child>
- <placeholder/>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHSeparator" id="hseparator1">
- <property name="visible">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="mainKeyboard">
- <property name="visible">True</property>
- <child>
- <placeholder/>
- </child>
- </widget>
- <packing>
- <property name="position">3</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
-</glade-interface>
+++ /dev/null
-#!/usr/bin/env python
-
-import os
-
-import plugin_utils
-import history
-
-
-PLUGIN_SEARCH_PATHS = [
- os.path.join(os.path.dirname(__file__), "plugins/"),
-]
-
-
-OPERATIONS = {}
-
-CONSTANTS = {}
-
-
-class CliEntry(object):
-
- def __init__(self):
- self.value = ""
-
- def set_value(self, value):
- self.value = value
-
- def get_value(self):
- return self.value
-
- def clear(self):
- self.value = ""
-
-
-def parse_command(userInput):
- return OPERATIONS[userInput.strip()]
-
-
-def ambiguous_parse(calc, userInput):
- try:
- Node = parse_command(userInput)
- calc.apply_operation(Node)
- return True
- except KeyError:
- return False
-
-
-def repl():
- entry = CliEntry()
- stack = history.CalcHistory()
- rpnCalc = history.RpnCalcHistory(
- stack,
- entry, history.ErrorWarning(),
- CONSTANTS, OPERATIONS
- )
- while True:
- userInput = raw_input(">")
- isUsed = ambiguous_parse(rpnCalc, userInput)
- if not isUsed:
- entry.set_value(userInput)
- rpnCalc.push_entry()
-
- if 0 < len(stack):
- node = stack.peek()
- print "\t= %s" % str(node)
- print "\t~= %s" % str(node.simplify(**CONSTANTS))
-
-
-def main():
- constantPlugins = plugin_utils.ConstantPluginManager()
- constantPlugins.add_path(*PLUGIN_SEARCH_PATHS)
- constantPlugins.enable_plugin(constantPlugins.lookup_plugin("Builtin"))
- CONSTANTS.update(constantPlugins.constants)
-
- operatorPlugins = plugin_utils.OperatorPluginManager()
- operatorPlugins.add_path(*PLUGIN_SEARCH_PATHS)
- operatorPlugins.enable_plugin(operatorPlugins.lookup_plugin("Builtin"))
- OPERATIONS.update(operatorPlugins.operators)
-
- repl()
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#!/usr/bin/python
-
-"""
-Some useful things on Maemo
-@li http://maemo.org/api_refs/4.1/libosso-2.16-1/group__Statesave.html
-@li http://maemo.org/api_refs/4.1/libosso-2.16-1/group__Autosave.html
-"""
-
-
-from __future__ import with_statement
-
-
-import sys
-import gc
-import os
-import string
-import logging
-import warnings
-
-import gtk
-import gtk.glade
-
-import hildonize
-import gtk_toolbox
-
-import constants
-from libraries import gtkpie
-from libraries import gtkpieboard
-import util.misc as misc_utils
-import plugin_utils
-import history
-import gtkhistory
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-PLUGIN_SEARCH_PATHS = [
- os.path.join(os.path.dirname(__file__), "plugins/"),
-]
-
-PROFILE_STARTUP = False
-
-
-class ValueEntry(object):
-
- def __init__(self, widget):
- self.__widget = widget
- self.__actualEntryDisplay = ""
-
- def get_value(self):
- value = self.__actualEntryDisplay.strip()
- if any(
- 0 < value.find(whitespace)
- for whitespace in string.whitespace
- ):
- self.clear()
- raise ValueError('Invalid input "%s"' % value)
- return value
-
- def set_value(self, value):
- value = value.strip()
- if any(
- 0 < value.find(whitespace)
- for whitespace in string.whitespace
- ):
- raise ValueError('Invalid input "%s"' % value)
- self.__actualEntryDisplay = value
- self.__widget.set_text(value)
-
- def append(self, value):
- value = value.strip()
- if any(
- 0 < value.find(whitespace)
- for whitespace in string.whitespace
- ):
- raise ValueError('Invalid input "%s"' % value)
- self.set_value(self.get_value() + value)
-
- def pop(self):
- value = self.get_value()[0:-1]
- self.set_value(value)
-
- def clear(self):
- self.set_value("")
-
- value = property(get_value, set_value, clear)
-
-
-class Calculator(object):
-
- _glade_files = [
- '/usr/lib/ejpi/ejpi.glade',
- os.path.join(os.path.dirname(__file__), "ejpi.glade"),
- os.path.join(os.path.dirname(__file__), "../lib/ejpi.glade"),
- ]
-
- _plugin_search_paths = [
- "/usr/lib/ejpi/plugins/",
- os.path.join(os.path.dirname(__file__), "plugins/"),
- ]
-
- _user_data = constants._data_path_
- _user_settings = "%s/settings.ini" % _user_data
- _user_history = "%s/history.stack" % _user_data
-
- MIN_BUTTON_SIZE = min(800, 480) // 6 - 20
-
- def __init__(self):
- self.__constantPlugins = plugin_utils.ConstantPluginManager()
- self.__constantPlugins.add_path(*self._plugin_search_paths)
- for pluginName in ["Builtin", "Trigonometry", "Computer", "Alphabet"]:
- try:
- pluginId = self.__constantPlugins.lookup_plugin(pluginName)
- self.__constantPlugins.enable_plugin(pluginId)
- except:
- warnings.warn("Failed to load plugin %s" % pluginName)
-
- self.__operatorPlugins = plugin_utils.OperatorPluginManager()
- self.__operatorPlugins.add_path(*self._plugin_search_paths)
- for pluginName in ["Builtin", "Trigonometry", "Computer", "Alphabet"]:
- try:
- pluginId = self.__operatorPlugins.lookup_plugin(pluginName)
- self.__operatorPlugins.enable_plugin(pluginId)
- except:
- warnings.warn("Failed to load plugin %s" % pluginName)
-
- self.__keyboardPlugins = plugin_utils.KeyboardPluginManager()
- self.__keyboardPlugins.add_path(*self._plugin_search_paths)
- self.__activeKeyboards = []
-
- for path in self._glade_files:
- if os.path.isfile(path):
- self._widgetTree = gtk.glade.XML(path)
- break
- else:
- self.display_error_message("Cannot find ejpi.glade")
- gtk.main_quit()
- return
- try:
- os.makedirs(self._user_data)
- except OSError, e:
- if e.errno != 17:
- raise
-
- self._clipboard = gtk.clipboard_get()
- self._window = self._widgetTree.get_widget("mainWindow")
-
- self._app = None
- self._isFullScreen = False
- self._app = hildonize.get_app_class()()
- self._window = hildonize.hildonize_window(self._app, self._window)
-
- menu = hildonize.hildonize_menu(
- self._window,
- self._widgetTree.get_widget("mainMenubar"),
- )
-
- for scrollingWidgetName in (
- "scrollingHistory",
- ):
- scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
- assert scrollingWidget is not None, scrollingWidgetName
- hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
-
- self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
- self.__userEntry = ValueEntry(self._widgetTree.get_widget("entryView"))
- self.__stackView = self._widgetTree.get_widget("historyView")
- self.__pluginButton = self._widgetTree.get_widget("keyboardSelectionButton")
-
- self.__historyStore = gtkhistory.GtkCalcHistory(self.__stackView)
- self.__history = history.RpnCalcHistory(
- self.__historyStore,
- self.__userEntry, self.__errorDisplay,
- self.__constantPlugins.constants, self.__operatorPlugins.operators
- )
- self.__load_history()
-
- # Basic keyboard stuff
- self.__sliceStyle = gtkpie.generate_pie_style(gtk.Button())
- self.__handler = gtkpieboard.KeyboardHandler(self._on_entry_direct)
- self.__handler.register_command_handler("push", self._on_push)
- self.__handler.register_command_handler("unpush", self._on_unpush)
- self.__handler.register_command_handler("backspace", self._on_entry_backspace)
- self.__handler.register_command_handler("clear", self._on_entry_clear)
-
- # Main keyboard
- builtinKeyboardId = self.__keyboardPlugins.lookup_plugin("Builtin")
- self.__keyboardPlugins.enable_plugin(builtinKeyboardId)
- self.__builtinPlugin = self.__keyboardPlugins.keyboards["Builtin"].construct_keyboard()
- self.__builtinKeyboard = self.__builtinPlugin.setup(self.__history, self.__sliceStyle, self.__handler)
- self._widgetTree.get_widget("mainKeyboard").pack_start(self.__builtinKeyboard)
- for child in self.__builtinKeyboard.get_children():
- child.set_size_request(self.MIN_BUTTON_SIZE, self.MIN_BUTTON_SIZE)
-
- # Plugins
- self.enable_plugin(self.__keyboardPlugins.lookup_plugin("Trigonometry"))
- self.enable_plugin(self.__keyboardPlugins.lookup_plugin("Computer"))
- self.enable_plugin(self.__keyboardPlugins.lookup_plugin("Alphabet"))
- self._set_plugin_kb(0)
-
- # Callbacks
- if not hildonize.IS_FREMANTLE_SUPPORTED:
- # Menus aren't used in the Fremantle version
- callbackMapping = {
- "on_calculator_quit": self._on_close,
- "on_paste": self._on_paste,
- "on_clear_history": self._on_clear_all,
- "on_about": self._on_about_activate,
- }
- self._widgetTree.signal_autoconnect(callbackMapping)
- self._widgetTree.get_widget("copyMenuItem").connect("activate", self._on_copy)
- self._widgetTree.get_widget("copyEquationMenuItem").connect("activate", self._on_copy_equation)
- self._window.connect("key-press-event", self._on_key_press)
- self._window.connect("window-state-event", self._on_window_state_change)
- self._widgetTree.get_widget("entryView").connect("activate", self._on_push)
- self.__pluginButton.connect("clicked", self._on_kb_plugin_selection_button)
-
- hildonize.set_application_name("%s" % constants.__pretty_app_name__)
- self._window.connect("destroy", self._on_close)
- self._window.show_all()
-
- if not hildonize.IS_HILDON_SUPPORTED:
- _moduleLogger.warning("No hildonization support")
-
- try:
- import osso
- except ImportError:
- osso = None
- self._osso = None
- self._deviceState = None
- if osso is not None:
- self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
- self._deviceState = osso.DeviceState(self._osso)
- self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
- else:
- _moduleLogger.warning("No OSSO support")
-
- def display_error_message(self, msg):
- error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
-
- def close(dialog, response, editor):
- editor.about_dialog = None
- dialog.destroy()
- error_dialog.connect("response", close, self)
- error_dialog.run()
-
- def enable_plugin(self, pluginId):
- self.__keyboardPlugins.enable_plugin(pluginId)
- pluginData = self.__keyboardPlugins.plugin_info(pluginId)
- pluginName = pluginData[0]
- plugin = self.__keyboardPlugins.keyboards[pluginName].construct_keyboard()
- pluginKeyboard = plugin.setup(self.__history, self.__sliceStyle, self.__handler)
- for child in pluginKeyboard.get_children():
- child.set_size_request(self.MIN_BUTTON_SIZE, self.MIN_BUTTON_SIZE)
-
- self.__activeKeyboards.append({
- "pluginName": pluginName,
- "plugin": plugin,
- "pluginKeyboard": pluginKeyboard,
- })
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_kb_plugin_selection_button(self, *args):
- pluginNames = [plugin["pluginName"] for plugin in self.__activeKeyboards]
- oldIndex = pluginNames.index(self.__pluginButton.get_label())
- newIndex = hildonize.touch_selector(self._window, "Keyboards", pluginNames, oldIndex)
- self._set_plugin_kb(newIndex)
-
- def _set_plugin_kb(self, pluginIndex):
- plugin = self.__activeKeyboards[pluginIndex]
- self.__pluginButton.set_label(plugin["pluginName"])
-
- pluginParent = self._widgetTree.get_widget("pluginKeyboard")
- oldPluginChildren = pluginParent.get_children()
- if oldPluginChildren:
- assert len(oldPluginChildren) == 1, "%r" % (oldPluginChildren, )
- pluginParent.remove(oldPluginChildren[0])
- oldPluginChildren[0].hide()
- pluginKeyboard = plugin["pluginKeyboard"]
- pluginParent.pack_start(pluginKeyboard)
-
- pluginKeyboard.show_all()
-
- def __load_history(self):
- serialized = []
- try:
- with open(self._user_history, "rU") as f:
- serialized = (
- (part.strip() for part in line.split(" "))
- for line in f.readlines()
- )
- except IOError, e:
- if e.errno != 2:
- raise
- self.__history.deserialize_stack(serialized)
-
- def __save_history(self):
- serialized = self.__history.serialize_stack()
- with open(self._user_history, "w") as f:
- for lineData in serialized:
- line = " ".join(data for data in lineData)
- f.write("%s\n" % line)
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
- """
- For system_inactivity, we have no background tasks to pause
-
- @note Hildon specific
- """
- if memory_low:
- gc.collect()
-
- if save_unsaved_data or shutdown:
- self.__save_history()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_window_state_change(self, widget, event, *args):
- if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
- self._isFullScreen = True
- else:
- self._isFullScreen = False
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_close(self, *args, **kwds):
- try:
- self.__save_history()
-
- try:
- self._deviceState.close()
- except AttributeError:
- pass # Either None or close was removed (in Fremantle)
- try:
- self._osso.close()
- except AttributeError:
- pass # Either None or close was removed (in Fremantle)
- finally:
- gtk.main_quit()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_copy(self, *args):
- equationNode = self.__history.history.peek()
- result = str(equationNode.evaluate())
- self._clipboard.set_text(result)
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_copy_equation(self, *args):
- equationNode = self.__history.history.peek()
- equation = str(equationNode)
- self._clipboard.set_text(equation)
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_paste(self, *args):
- contents = self._clipboard.wait_for_text()
- self.__userEntry.append(contents)
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_key_press(self, widget, event, *args):
- 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._window.unfullscreen()
- else:
- self._window.fullscreen()
- elif event.keyval == ord("c") and event.get_state() & gtk.gdk.CONTROL_MASK:
- equationNode = self.__history.history.peek()
- result = str(equationNode.evaluate())
- self._clipboard.set_text(result)
- elif event.keyval == ord("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))
- elif event.keyval == gtk.keysyms.BackSpace and event.get_state() & gtk.gdk.CONTROL_MASK:
- self.__historyStore.unpush()
- elif event.keyval == gtk.keysyms.BackSpace:
- self.__userEntry.pop()
- elif event.keyval in RETURN_TYPES:
- self.__history.push_entry()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_push(self, *args):
- self.__history.push_entry()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_unpush(self, *args):
- self.__historyStore.unpush()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_entry_direct(self, keys, modifiers):
- if "shift" in modifiers:
- keys = keys.upper()
- self.__userEntry.append(keys)
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_entry_backspace(self, *args):
- self.__userEntry.pop()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_entry_clear(self, *args):
- self.__userEntry.clear()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_clear_all(self, *args):
- self.__history.clear()
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_about_activate(self, *args):
- dlg = gtk.AboutDialog()
- dlg.set_name(constants.__pretty_app_name__)
- dlg.set_version(constants.__version__)
- dlg.set_copyright("Copyright 2008 - LGPL")
- dlg.set_comments("""
-ejpi A Touch Screen Optimized RPN Calculator for Maemo and Linux.
-
-RPN: Stack based math, its fun
-Buttons: Try both pressing and hold/drag
-History: Try dragging things around, deleting them, etc
-""")
- dlg.set_website("http://ejpi.garage.maemo.org")
- dlg.set_authors(["Ed Page <eopage@byu.net>"])
- dlg.run()
- dlg.destroy()
-
-
-def run_doctest():
- import doctest
-
- failureCount, testCount = doctest.testmod()
- if not failureCount:
- print "Tests Successful"
- sys.exit(0)
- else:
- sys.exit(1)
-
-
-def run():
- gtk.gdk.threads_init()
-
- gtkpie.IMAGES.add_path(os.path.join(os.path.dirname(__file__), "libraries/images"), )
- if hildonize.IS_HILDON_SUPPORTED:
- gtk.set_application_name(constants.__pretty_app_name__)
- handle = Calculator()
- if not PROFILE_STARTUP:
- gtk.main()
-
-
-class DummyOptions(object):
-
- def __init__(self):
- self.test = False
-
-
-if __name__ == "__main__":
- logging.basicConfig(level=logging.DEBUG)
- if len(sys.argv) > 1:
- try:
- import optparse
- except ImportError:
- optparse = None
-
- if optparse is not None:
- parser = optparse.OptionParser()
- parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
- (commandOptions, commandArgs) = parser.parse_args()
- else:
- commandOptions = DummyOptions()
- commandArgs = []
-
- if commandOptions.test:
- run_doctest()
- else:
- run()
import sys
import os
import simplejson
+import string
import logging
from PyQt4 import QtGui
import maeqt
from util import misc as misc_utils
+from libraries import qtpie
+from libraries import qtpieboard
+import plugin_utils
+import history
import qhistory
IS_MAEMO = True
+PLUGIN_SEARCH_PATHS = [
+ os.path.join(os.path.dirname(__file__), "plugins/"),
+]
+
+
class Calculator(object):
def __init__(self, app):
self._hiddenCategories = set()
self._hiddenUnits = {}
self._clipboard = QtGui.QApplication.clipboard()
-
self._mainWindow = None
self._fullscreenAction = QtGui.QAction(None)
self._close_windows()
+class QErrorDisplay(object):
+
+ def __init__(self):
+ self._messages = []
+
+ icon = QtGui.QIcon.fromTheme("gtk-dialog-error")
+ self._severityIcon = icon.pixmap(32, 32)
+ self._severityLabel = QtGui.QLabel()
+ self._severityLabel.setPixmap(self._severityIcon)
+
+ self._message = QtGui.QLabel()
+ self._message.setText("Boo")
+
+ icon = QtGui.QIcon.fromTheme("gtk-close")
+ self._closeIcon = icon.pixmap(32, 32)
+ self._closeLabel = QtGui.QLabel()
+ self._closeLabel.setPixmap(self._closeIcon)
+
+ self._controlLayout = QtGui.QHBoxLayout()
+ self._controlLayout.addWidget(self._severityLabel)
+ self._controlLayout.addWidget(self._message)
+ self._controlLayout.addWidget(self._closeLabel)
+
+ self._topLevelLayout = QtGui.QHBoxLayout()
+
+ @property
+ def toplevel(self):
+ return self._topLevelLayout
+
+ def push_message(self, message):
+ self._messages.append(message)
+ if 1 == len(self._messages):
+ self._show_message(message)
+
+ def push_exception(self):
+ userMessage = str(sys.exc_info()[1])
+ self.push_message(userMessage)
+ _moduleLogger.exception(userMessage)
+
+ def pop_message(self):
+ del self._messages[0]
+ if 0 == len(self._messages):
+ self._hide_message()
+ else:
+ self._message.setText(self._messages[0])
+
+ def _on_close(self, *args):
+ self.pop_message()
+
+ def _show_message(self, message):
+ self._message.set_text(message)
+ self._topLevelLayout.addLayout(self._controlLayout)
+
+ def _hide_message(self):
+ self._message.set_text("")
+ self._topLevelLayout.removeItem(self._controlLayout)
+
+
+class QValueEntry(object):
+
+ def __init__(self):
+ self._widget = QtGui.QLineEdit("")
+ self._widget.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
+ self._actualEntryDisplay = ""
+
+ @property
+ def toplevel(self):
+ return self._widget
+
+ def get_value(self):
+ value = self._actualEntryDisplay.strip()
+ if any(
+ 0 < value.find(whitespace)
+ for whitespace in string.whitespace
+ ):
+ self.clear()
+ raise ValueError('Invalid input "%s"' % value)
+ return value
+
+ def set_value(self, value):
+ value = value.strip()
+ if any(
+ 0 < value.find(whitespace)
+ for whitespace in string.whitespace
+ ):
+ raise ValueError('Invalid input "%s"' % value)
+ self._actualEntryDisplay = value
+ self._widget.setText(value)
+
+ def append(self, value):
+ value = value.strip()
+ if any(
+ 0 < value.find(whitespace)
+ for whitespace in string.whitespace
+ ):
+ raise ValueError('Invalid input "%s"' % value)
+ self.set_value(self.get_value() + value)
+
+ def pop(self):
+ value = self.get_value()[0:-1]
+ self.set_value(value)
+
+ def clear(self):
+ self.set_value("")
+
+ value = property(get_value, set_value, clear)
+
+
class MainWindow(object):
+ _plugin_search_paths = [
+ "/opt/epi/lib/plugins/",
+ "/usr/lib/ejpi/plugins/",
+ os.path.join(os.path.dirname(__file__), "plugins/"),
+ ]
+
+ _user_history = "%s/history.stack" % constants._data_path_
+
def __init__(self, parent, app):
self._app = app
self._historyView = qhistory.QCalcHistory()
- self._layout = QtGui.QVBoxLayout()
- self._layout.addWidget(self._historyView.toplevel)
+ self._errorDisplay = QErrorDisplay()
+
+ self._userEntry = QValueEntry()
+
+ self._controlLayout = QtGui.QVBoxLayout()
+ self._controlLayout.addLayout(self._errorDisplay.toplevel)
+ self._controlLayout.addWidget(self._historyView.toplevel)
+ self._controlLayout.addWidget(self._userEntry.toplevel)
+
+ self._pluginKeyboardSpot = QtGui.QVBoxLayout()
+ self._inputLayout = QtGui.QVBoxLayout()
+ self._inputLayout.addLayout(self._pluginKeyboardSpot)
+
+ self._layout = QtGui.QHBoxLayout()
+ self._layout.addLayout(self._controlLayout)
+ self._layout.addLayout(self._inputLayout)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(self._layout)
self._window.addAction(self._app.logAction)
+ self._constantPlugins = plugin_utils.ConstantPluginManager()
+ self._constantPlugins.add_path(*self._plugin_search_paths)
+ for pluginName in ["Builtin", "Trigonometry", "Computer", "Alphabet"]:
+ try:
+ pluginId = self._constantPlugins.lookup_plugin(pluginName)
+ self._constantPlugins.enable_plugin(pluginId)
+ except:
+ _moduleLogger.info("Failed to load plugin %s" % pluginName)
+
+ self._operatorPlugins = plugin_utils.OperatorPluginManager()
+ self._operatorPlugins.add_path(*self._plugin_search_paths)
+ for pluginName in ["Builtin", "Trigonometry", "Computer", "Alphabet"]:
+ try:
+ pluginId = self._operatorPlugins.lookup_plugin(pluginName)
+ self._operatorPlugins.enable_plugin(pluginId)
+ except:
+ _moduleLogger.info("Failed to load plugin %s" % pluginName)
+
+ self._keyboardPlugins = plugin_utils.KeyboardPluginManager()
+ self._keyboardPlugins.add_path(*self._plugin_search_paths)
+ self._activeKeyboards = []
+
+ self._history = history.RpnCalcHistory(
+ self._historyView,
+ self._userEntry, self._errorDisplay,
+ self._constantPlugins.constants, self._operatorPlugins.operators
+ )
+ self._load_history()
+
+ # Basic keyboard stuff
+ self._handler = qtpieboard.KeyboardHandler(self._on_entry_direct)
+ self._handler.register_command_handler("push", self._on_push)
+ self._handler.register_command_handler("unpush", self._on_unpush)
+ self._handler.register_command_handler("backspace", self._on_entry_backspace)
+ self._handler.register_command_handler("clear", self._on_entry_clear)
+
+ # Main keyboard
+ builtinKeyboardId = self._keyboardPlugins.lookup_plugin("Builtin")
+ self._keyboardPlugins.enable_plugin(builtinKeyboardId)
+ self._builtinPlugin = self._keyboardPlugins.keyboards["Builtin"].construct_keyboard()
+ self._builtinKeyboard = self._builtinPlugin.setup(self._history, self._handler)
+ self._inputLayout.addLayout(self._builtinKeyboard.toplevel)
+
+ # Plugins
+ self.enable_plugin(self._keyboardPlugins.lookup_plugin("Trigonometry"))
+ self.enable_plugin(self._keyboardPlugins.lookup_plugin("Computer"))
+ self.enable_plugin(self._keyboardPlugins.lookup_plugin("Alphabet"))
+ self._set_plugin_kb(0)
+
self.set_fullscreen(self._app.fullscreenAction.isChecked())
self._window.show()
for child in self.walk_children():
child.set_fullscreen(isFullscreen)
+ def enable_plugin(self, pluginId):
+ self._keyboardPlugins.enable_plugin(pluginId)
+ pluginData = self._keyboardPlugins.plugin_info(pluginId)
+ pluginName = pluginData[0]
+ plugin = self._keyboardPlugins.keyboards[pluginName].construct_keyboard()
+ pluginKeyboard = plugin.setup(self._history, self._handler)
+
+ self._activeKeyboards.append({
+ "pluginName": pluginName,
+ "plugin": plugin,
+ "pluginKeyboard": pluginKeyboard,
+ })
+
+ def _set_plugin_kb(self, pluginIndex):
+ plugin = self._activeKeyboards[pluginIndex]
+ # @todo self._pluginButton.set_label(plugin["pluginName"])
+
+ for i in xrange(self._pluginKeyboardSpot.count()):
+ self._pluginKeyboardSpot.removeItem(self._pluginKeyboardSpot.itemAt(i))
+ pluginKeyboard = plugin["pluginKeyboard"]
+ self._pluginKeyboardSpot.addItem(pluginKeyboard.toplevel)
+
+ def _load_history(self):
+ serialized = []
+ try:
+ with open(self._user_history, "rU") as f:
+ serialized = (
+ (part.strip() for part in line.split(" "))
+ for line in f.readlines()
+ )
+ except IOError, e:
+ if e.errno != 2:
+ raise
+ self._history.deserialize_stack(serialized)
+
+ def _save_history(self):
+ serialized = self._history.serialize_stack()
+ with open(self._user_history, "w") as f:
+ for lineData in serialized:
+ line = " ".join(data for data in lineData)
+ f.write("%s\n" % line)
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_entry_direct(self, keys, modifiers):
+ if "shift" in modifiers:
+ keys = keys.upper()
+ self._userEntry.append(keys)
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_push(self, *args):
+ self._history.push_entry()
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_unpush(self, *args):
+ self._historyStore.unpush()
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_entry_backspace(self, *args):
+ self._userEntry.pop()
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_entry_clear(self, *args):
+ self._userEntry.clear()
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_clear_all(self, *args):
+ self._history.clear()
+
@misc_utils.log_exception(_moduleLogger)
def _on_close_window(self, checked = True):
+ self._save_history()
self.close()
def run():
app = QtGui.QApplication([])
handle = Calculator(app)
+ qtpie.init_pies()
return app.exec_()
+++ /dev/null
-#!/usr/bin/python
-
-from __future__ import with_statement
-
-import os
-import errno
-import sys
-import time
-import itertools
-import functools
-import contextlib
-import logging
-import threading
-import Queue
-
-import gobject
-import gtk
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-def get_screen_orientation():
- width, height = gtk.gdk.get_default_root_window().get_size()
- if width < height:
- return gtk.ORIENTATION_VERTICAL
- else:
- return gtk.ORIENTATION_HORIZONTAL
-
-
-def orientation_change_connect(handler, *args):
- """
- @param handler(orientation, *args) -> None(?)
- """
- initialScreenOrientation = get_screen_orientation()
- orientationAndArgs = list(itertools.chain((initialScreenOrientation, ), args))
-
- def _on_screen_size_changed(screen):
- newScreenOrientation = get_screen_orientation()
- if newScreenOrientation != orientationAndArgs[0]:
- orientationAndArgs[0] = newScreenOrientation
- handler(*orientationAndArgs)
-
- rootScreen = gtk.gdk.get_default_root_window()
- return gtk.connect(rootScreen, "size-changed", _on_screen_size_changed)
-
-
-@contextlib.contextmanager
-def flock(path, timeout=-1):
- WAIT_FOREVER = -1
- DELAY = 0.1
- timeSpent = 0
-
- acquired = False
-
- while timeSpent <= timeout or timeout == WAIT_FOREVER:
- try:
- fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
- acquired = True
- break
- except OSError, e:
- if e.errno != errno.EEXIST:
- raise
- time.sleep(DELAY)
- timeSpent += DELAY
-
- assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout)
-
- try:
- yield fd
- finally:
- os.unlink(path)
-
-
-@contextlib.contextmanager
-def gtk_lock():
- gtk.gdk.threads_enter()
- try:
- yield
- finally:
- gtk.gdk.threads_leave()
-
-
-def find_parent_window(widget):
- while True:
- parent = widget.get_parent()
- if isinstance(parent, gtk.Window):
- return parent
- widget = parent
-
-
-def make_idler(func):
- """
- Decorator that makes a generator-function into a function that will continue execution on next call
- """
- a = []
-
- @functools.wraps(func)
- def decorated_func(*args, **kwds):
- if not a:
- a.append(func(*args, **kwds))
- try:
- a[0].next()
- return True
- except StopIteration:
- del a[:]
- return False
-
- return decorated_func
-
-
-def asynchronous_gtk_message(original_func):
- """
- @note Idea came from http://www.aclevername.com/articles/python-webgui/
- """
-
- def execute(allArgs):
- args, kwargs = allArgs
- with gtk_lock():
- original_func(*args, **kwargs)
- return False
-
- @functools.wraps(original_func)
- def delayed_func(*args, **kwargs):
- gobject.idle_add(execute, (args, kwargs))
-
- return delayed_func
-
-
-def synchronous_gtk_message(original_func):
- """
- @note Idea came from http://www.aclevername.com/articles/python-webgui/
- """
-
- @functools.wraps(original_func)
- def immediate_func(*args, **kwargs):
- with gtk_lock():
- return original_func(*args, **kwargs)
-
- return immediate_func
-
-
-def autostart(func):
- """
- >>> @autostart
- ... def grep_sink(pattern):
- ... print "Looking for %s" % pattern
- ... while True:
- ... line = yield
- ... if pattern in line:
- ... print line,
- >>> g = grep_sink("python")
- Looking for python
- >>> g.send("Yeah but no but yeah but no")
- >>> g.send("A series of tubes")
- >>> g.send("python generators rock!")
- python generators rock!
- >>> g.close()
- """
-
- @functools.wraps(func)
- def start(*args, **kwargs):
- cr = func(*args, **kwargs)
- cr.next()
- return cr
-
- return start
-
-
-@autostart
-def printer_sink(format = "%s"):
- """
- >>> pr = printer_sink("%r")
- >>> pr.send("Hello")
- 'Hello'
- >>> pr.send("5")
- '5'
- >>> pr.send(5)
- 5
- >>> p = printer_sink()
- >>> p.send("Hello")
- Hello
- >>> p.send("World")
- World
- >>> # p.throw(RuntimeError, "Goodbye")
- >>> # p.send("Meh")
- >>> # p.close()
- """
- while True:
- item = yield
- print format % (item, )
-
-
-@autostart
-def null_sink():
- """
- Good for uses like with cochain to pick up any slack
- """
- while True:
- item = yield
-
-
-@autostart
-def comap(function, target):
- """
- >>> p = printer_sink()
- >>> cm = comap(lambda x: x+1, p)
- >>> cm.send((0, ))
- 1
- >>> cm.send((1.0, ))
- 2.0
- >>> cm.send((-2, ))
- -1
- """
- while True:
- try:
- item = yield
- mappedItem = function(*item)
- target.send(mappedItem)
- except Exception, e:
- _moduleLogger.exception("Forwarding exception!")
- target.throw(e.__class__, str(e))
-
-
-def _flush_queue(queue):
- while not queue.empty():
- yield queue.get()
-
-
-@autostart
-def queue_sink(queue):
- """
- >>> q = Queue.Queue()
- >>> qs = queue_sink(q)
- >>> qs.send("Hello")
- >>> qs.send("World")
- >>> qs.throw(RuntimeError, "Goodbye")
- >>> qs.send("Meh")
- >>> qs.close()
- >>> print [i for i in _flush_queue(q)]
- [(None, 'Hello'), (None, 'World'), (<type 'exceptions.RuntimeError'>, 'Goodbye'), (None, 'Meh'), (<type 'exceptions.GeneratorExit'>, None)]
- """
- while True:
- try:
- item = yield
- queue.put((None, item))
- except Exception, e:
- queue.put((e.__class__, str(e)))
- except GeneratorExit:
- queue.put((GeneratorExit, None))
- raise
-
-
-def decode_item(item, target):
- if item[0] is None:
- target.send(item[1])
- return False
- elif item[0] is GeneratorExit:
- target.close()
- return True
- else:
- target.throw(item[0], item[1])
- return False
-
-
-def nonqueue_source(queue, target):
- isDone = False
- while not isDone:
- item = queue.get()
- isDone = decode_item(item, target)
- while not queue.empty():
- queue.get_nowait()
-
-
-def threaded_stage(target, thread_factory = threading.Thread):
- messages = Queue.Queue()
-
- run_source = functools.partial(nonqueue_source, messages, target)
- thread = thread_factory(target=run_source)
- thread.setDaemon(True)
- thread.start()
-
- # Sink running in current thread
- return queue_sink(messages)
-
-
-def log_exception(logger):
-
- def log_exception_decorator(func):
-
- @functools.wraps(func)
- def wrapper(*args, **kwds):
- try:
- return func(*args, **kwds)
- except Exception:
- logger.exception(func.__name__)
-
- return wrapper
-
- return log_exception_decorator
-
-
-class LoginWindow(object):
-
- def __init__(self, widgetTree):
- """
- @note Thread agnostic
- """
- self._dialog = widgetTree.get_widget("loginDialog")
- self._parentWindow = widgetTree.get_widget("mainWindow")
- self._serviceCombo = widgetTree.get_widget("serviceCombo")
- self._usernameEntry = widgetTree.get_widget("usernameentry")
- self._passwordEntry = widgetTree.get_widget("passwordentry")
-
- self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
- self._serviceCombo.set_model(self._serviceList)
- cell = gtk.CellRendererText()
- self._serviceCombo.pack_start(cell, True)
- self._serviceCombo.add_attribute(cell, 'text', 1)
- self._serviceCombo.set_active(0)
-
- widgetTree.get_widget("loginbutton").connect("clicked", self._on_loginbutton_clicked)
- widgetTree.get_widget("logins_close_button").connect("clicked", self._on_loginclose_clicked)
-
- def request_credentials(self,
- parentWindow = None,
- defaultCredentials = ("", "")
- ):
- """
- @note UI Thread
- """
- if parentWindow is None:
- parentWindow = self._parentWindow
-
- self._serviceCombo.hide()
- self._serviceList.clear()
-
- self._usernameEntry.set_text(defaultCredentials[0])
- self._passwordEntry.set_text(defaultCredentials[1])
-
- try:
- self._dialog.set_transient_for(parentWindow)
- self._dialog.set_default_response(gtk.RESPONSE_OK)
- response = self._dialog.run()
- if response != gtk.RESPONSE_OK:
- raise RuntimeError("Login Cancelled")
-
- username = self._usernameEntry.get_text()
- password = self._passwordEntry.get_text()
- self._passwordEntry.set_text("")
- finally:
- self._dialog.hide()
-
- return username, password
-
- def request_credentials_from(self,
- services,
- parentWindow = None,
- defaultCredentials = ("", "")
- ):
- """
- @note UI Thread
- """
- if parentWindow is None:
- parentWindow = self._parentWindow
-
- self._serviceList.clear()
- for serviceIdserviceName in services:
- self._serviceList.append(serviceIdserviceName)
- self._serviceCombo.set_active(0)
- self._serviceCombo.show()
-
- self._usernameEntry.set_text(defaultCredentials[0])
- self._passwordEntry.set_text(defaultCredentials[1])
-
- try:
- self._dialog.set_transient_for(parentWindow)
- self._dialog.set_default_response(gtk.RESPONSE_OK)
- response = self._dialog.run()
- if response != gtk.RESPONSE_OK:
- raise RuntimeError("Login Cancelled")
-
- username = self._usernameEntry.get_text()
- password = self._passwordEntry.get_text()
- finally:
- self._dialog.hide()
-
- itr = self._serviceCombo.get_active_iter()
- serviceId = int(self._serviceList.get_value(itr, 0))
- self._serviceList.clear()
- return serviceId, username, password
-
- def _on_loginbutton_clicked(self, *args):
- self._dialog.response(gtk.RESPONSE_OK)
-
- def _on_loginclose_clicked(self, *args):
- self._dialog.response(gtk.RESPONSE_CANCEL)
-
-
-def safecall(f, errorDisplay=None, default=None, exception=Exception):
- '''
- Returns modified f. When the modified f is called and throws an
- exception, the default value is returned
- '''
- def _safecall(*args, **argv):
- try:
- return f(*args,**argv)
- except exception, e:
- if errorDisplay is not None:
- errorDisplay.push_exception(e)
- return default
- return _safecall
-
-
-class ErrorDisplay(object):
-
- def __init__(self, widgetTree):
- super(ErrorDisplay, self).__init__()
- self.__errorBox = widgetTree.get_widget("errorEventBox")
- self.__errorDescription = widgetTree.get_widget("errorDescription")
- self.__errorClose = widgetTree.get_widget("errorClose")
- self.__parentBox = self.__errorBox.get_parent()
-
- self.__errorBox.connect("button_release_event", self._on_close)
-
- self.__messages = []
- self.__parentBox.remove(self.__errorBox)
-
- def push_message_with_lock(self, message):
- with gtk_lock():
- self.push_message(message)
-
- def push_message(self, message):
- self.__messages.append(message)
- if 1 == len(self.__messages):
- self.__show_message(message)
-
- def push_exception_with_lock(self):
- with gtk_lock():
- self.push_exception()
-
- def push_exception(self):
- userMessage = str(sys.exc_info()[1])
- self.push_message(userMessage)
- _moduleLogger.exception(userMessage)
-
- def pop_message(self):
- del self.__messages[0]
- if 0 == len(self.__messages):
- self.__hide_message()
- else:
- self.__errorDescription.set_text(self.__messages[0])
-
- def _on_close(self, *args):
- self.pop_message()
-
- def __show_message(self, message):
- self.__errorDescription.set_text(message)
- self.__parentBox.pack_start(self.__errorBox, False, False)
- self.__parentBox.reorder_child(self.__errorBox, 1)
-
- def __hide_message(self):
- self.__errorDescription.set_text("")
- self.__parentBox.remove(self.__errorBox)
-
-
-class DummyErrorDisplay(object):
-
- def __init__(self):
- super(DummyErrorDisplay, self).__init__()
-
- self.__messages = []
-
- def push_message_with_lock(self, message):
- self.push_message(message)
-
- def push_message(self, message):
- if 0 < len(self.__messages):
- self.__messages.append(message)
- else:
- self.__show_message(message)
-
- def push_exception(self, exception = None):
- userMessage = str(sys.exc_value)
- _moduleLogger.exception(userMessage)
-
- def pop_message(self):
- if 0 < len(self.__messages):
- self.__show_message(self.__messages[0])
- del self.__messages[0]
-
- def __show_message(self, message):
- _moduleLogger.debug(message)
-
-
-class MessageBox(gtk.MessageDialog):
-
- def __init__(self, message):
- parent = None
- gtk.MessageDialog.__init__(
- self,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK,
- message,
- )
- self.set_default_response(gtk.RESPONSE_OK)
- self.connect('response', self._handle_clicked)
-
- def _handle_clicked(self, *args):
- self.destroy()
-
-
-class MessageBox2(gtk.MessageDialog):
-
- def __init__(self, message):
- parent = None
- gtk.MessageDialog.__init__(
- self,
- parent,
- gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK,
- message,
- )
- self.set_default_response(gtk.RESPONSE_OK)
- self.connect('response', self._handle_clicked)
-
- def _handle_clicked(self, *args):
- self.destroy()
-
-
-class PopupCalendar(object):
-
- def __init__(self, parent, displayDate, title = ""):
- self._displayDate = displayDate
-
- self._calendar = gtk.Calendar()
- self._calendar.select_month(self._displayDate.month, self._displayDate.year)
- self._calendar.select_day(self._displayDate.day)
- self._calendar.set_display_options(
- gtk.CALENDAR_SHOW_HEADING |
- gtk.CALENDAR_SHOW_DAY_NAMES |
- gtk.CALENDAR_NO_MONTH_CHANGE |
- 0
- )
- self._calendar.connect("day-selected", self._on_day_selected)
-
- self._popupWindow = gtk.Window()
- self._popupWindow.set_title(title)
- self._popupWindow.add(self._calendar)
- self._popupWindow.set_transient_for(parent)
- self._popupWindow.set_modal(True)
- self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
- self._popupWindow.set_skip_pager_hint(True)
- self._popupWindow.set_skip_taskbar_hint(True)
-
- def run(self):
- self._popupWindow.show_all()
-
- def _on_day_selected(self, *args):
- try:
- self._calendar.select_month(self._displayDate.month, self._displayDate.year)
- self._calendar.select_day(self._displayDate.day)
- except Exception, e:
- _moduleLogger.exception(e)
-
-
-class QuickAddView(object):
-
- def __init__(self, widgetTree, errorDisplay, signalSink, prefix):
- self._errorDisplay = errorDisplay
- self._manager = None
- self._signalSink = signalSink
-
- self._clipboard = gtk.clipboard_get()
-
- self._taskNameEntry = widgetTree.get_widget(prefix+"-nameEntry")
- self._addTaskButton = widgetTree.get_widget(prefix+"-addButton")
- self._pasteTaskNameButton = widgetTree.get_widget(prefix+"-pasteNameButton")
- self._clearTaskNameButton = widgetTree.get_widget(prefix+"-clearNameButton")
- self._onAddId = None
- self._onAddClickedId = None
- self._onAddReleasedId = None
- self._addToEditTimerId = None
- self._onClearId = None
- self._onPasteId = None
-
- def enable(self, manager):
- self._manager = manager
-
- self._onAddId = self._addTaskButton.connect("clicked", self._on_add)
- self._onAddClickedId = self._addTaskButton.connect("pressed", self._on_add_pressed)
- self._onAddReleasedId = self._addTaskButton.connect("released", self._on_add_released)
- self._onPasteId = self._pasteTaskNameButton.connect("clicked", self._on_paste)
- self._onClearId = self._clearTaskNameButton.connect("clicked", self._on_clear)
-
- def disable(self):
- self._manager = None
-
- self._addTaskButton.disconnect(self._onAddId)
- self._addTaskButton.disconnect(self._onAddClickedId)
- self._addTaskButton.disconnect(self._onAddReleasedId)
- self._pasteTaskNameButton.disconnect(self._onPasteId)
- self._clearTaskNameButton.disconnect(self._onClearId)
-
- def set_addability(self, addability):
- self._addTaskButton.set_sensitive(addability)
-
- def _on_add(self, *args):
- try:
- name = self._taskNameEntry.get_text()
- self._taskNameEntry.set_text("")
-
- self._signalSink.stage.send(("add", name))
- except Exception, e:
- self._errorDisplay.push_exception()
-
- def _on_add_edit(self, *args):
- try:
- name = self._taskNameEntry.get_text()
- self._taskNameEntry.set_text("")
-
- self._signalSink.stage.send(("add-edit", name))
- except Exception, e:
- self._errorDisplay.push_exception()
-
- def _on_add_pressed(self, widget):
- try:
- self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit)
- except Exception, e:
- self._errorDisplay.push_exception()
-
- def _on_add_released(self, widget):
- try:
- if self._addToEditTimerId is not None:
- gobject.source_remove(self._addToEditTimerId)
- self._addToEditTimerId = None
- except Exception, e:
- self._errorDisplay.push_exception()
-
- def _on_paste(self, *args):
- try:
- entry = self._taskNameEntry.get_text()
- addedText = self._clipboard.wait_for_text()
- if addedText:
- entry += addedText
- self._taskNameEntry.set_text(entry)
- except Exception, e:
- self._errorDisplay.push_exception()
-
- def _on_clear(self, *args):
- try:
- self._taskNameEntry.set_text("")
- except Exception, e:
- self._errorDisplay.push_exception()
-
-
-class TapOrHold(object):
-
- def __init__(self, widget):
- self._widget = widget
- self._isTap = True
- self._isPointerInside = True
- self._holdTimeoutId = None
- self._tapTimeoutId = None
- self._taps = 0
-
- self._bpeId = None
- self._breId = None
- self._eneId = None
- self._lneId = None
-
- def enable(self):
- self._bpeId = self._widget.connect("button-press-event", self._on_button_press)
- self._breId = self._widget.connect("button-release-event", self._on_button_release)
- self._eneId = self._widget.connect("enter-notify-event", self._on_enter)
- self._lneId = self._widget.connect("leave-notify-event", self._on_leave)
-
- def disable(self):
- self._widget.disconnect(self._bpeId)
- self._widget.disconnect(self._breId)
- self._widget.disconnect(self._eneId)
- self._widget.disconnect(self._lneId)
-
- def on_tap(self, taps):
- print "TAP", taps
-
- def on_hold(self, taps):
- print "HOLD", taps
-
- def on_holding(self):
- print "HOLDING"
-
- def on_cancel(self):
- print "CANCEL"
-
- def _on_button_press(self, *args):
- # Hack to handle weird notebook behavior
- self._isPointerInside = True
- self._isTap = True
-
- if self._tapTimeoutId is not None:
- gobject.source_remove(self._tapTimeoutId)
- self._tapTimeoutId = None
-
- # Handle double taps
- if self._holdTimeoutId is None:
- self._tapTimeoutId = None
-
- self._taps = 1
- self._holdTimeoutId = gobject.timeout_add(1000, self._on_hold_timeout)
- else:
- self._taps = 2
-
- def _on_button_release(self, *args):
- assert self._tapTimeoutId is None
- # Handle release after timeout if user hasn't double-clicked
- self._tapTimeoutId = gobject.timeout_add(100, self._on_tap_timeout)
-
- def _on_actual_press(self, *args):
- if self._holdTimeoutId is not None:
- gobject.source_remove(self._holdTimeoutId)
- self._holdTimeoutId = None
-
- if self._isPointerInside:
- if self._isTap:
- self.on_tap(self._taps)
- else:
- self.on_hold(self._taps)
- else:
- self.on_cancel()
-
- def _on_tap_timeout(self, *args):
- self._tapTimeoutId = None
- self._on_actual_press()
- return False
-
- def _on_hold_timeout(self, *args):
- self._holdTimeoutId = None
- self._isTap = False
- self.on_holding()
- return False
-
- def _on_enter(self, *args):
- self._isPointerInside = True
-
- def _on_leave(self, *args):
- self._isPointerInside = False
-
-
-if __name__ == "__main__":
- if True:
- win = gtk.Window()
- win.set_title("Tap'N'Hold")
- eventBox = gtk.EventBox()
- win.add(eventBox)
-
- context = ContextHandler(eventBox, coroutines.printer_sink())
- context.enable()
- win.connect("destroy", lambda w: gtk.main_quit())
-
- win.show_all()
-
- if False:
- import datetime
- cal = PopupCalendar(None, datetime.datetime.now())
- cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())
- cal.run()
-
- gtk.main()
+++ /dev/null
-#!/usr/bin/env python
-
-"""
-http://www.grigoriev.ru/svgmath/ (MathML->SVG in Python)
-http://helm.cs.unibo.it/mml-widget/ (MathML widget in C++)
-"""
-
-import logging
-
-import gobject
-import pango
-import gtk
-
-
-import util.misc as misc_utils
-import hildonize
-import history
-import operation
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class GtkCalcHistory(history.AbstractHistory):
-
- BUTTON_IDX = 0
- VIEW_DATA_IDX = 1
- VIEW_RESULT_IDX = 2
- DATA_IDX = 3
- RESULT_IDX = 4
-
- def __init__(self, view):
- super(GtkCalcHistory, self).__init__()
- self.__prettyRenderer = operation.render_number()
- self._historyView = view
-
- # stock-id, display, value
- self.__historyStore = gtk.ListStore(
- gobject.TYPE_STRING, # stock id for pixbuf
- gobject.TYPE_STRING, # view of data
- gobject.TYPE_STRING, # view of result
- object, # data
- object, # result
- )
- self._historyView.set_model(self.__historyStore)
-
- # create the TreeViewColumns to display the data
- self.__closeColumn = gtk.TreeViewColumn('')
- self._historyView.append_column(self.__closeColumn)
-
- self.__historyColumn = gtk.TreeViewColumn('History')
- self.__historyColumn.set_sort_column_id(0)
- self.__historyColumn.set_expand(True)
- self._historyView.append_column(self.__historyColumn)
-
- self.__resultColumn = gtk.TreeViewColumn('')
- self.__resultColumn.set_sort_column_id(0)
- self._historyView.append_column(self.__resultColumn)
-
- # create a CellRenderers to render the data
- self.__closeCell = gtk.CellRendererPixbuf()
- hildonize.set_pix_cell_thumb_selectable(self.__closeCell)
- self.__closeColumn.pack_start(self.__closeCell, False)
- self.__closeColumn.set_attributes(self.__closeCell, stock_id=0)
-
- self.__expressionCell = gtk.CellRendererText()
- self.__expressionCell.set_property("ellipsize", pango.ELLIPSIZE_MIDDLE)
- self.__historyColumn.pack_start(self.__expressionCell, True)
- self.__historyColumn.set_attributes(self.__expressionCell, text=1)
-
- self.__valueCell = gtk.CellRendererText()
- self.__valueCell.set_property("ellipsize", pango.ELLIPSIZE_NONE)
- self.__resultColumn.pack_end(self.__valueCell, False)
- self.__resultColumn.set_attributes(self.__valueCell, text=2)
-
- self._historyView.set_reorderable(True)
- self._historyView.get_selection().set_mode(gtk.SELECTION_SINGLE)
- self._historyView.connect("row-activated", self._on_close_activated)
-
- def push(self, node):
- simpleNode = node.simplify()
- self.__historyStore.append([
- gtk.STOCK_CLOSE,
- operation.render_operation(self.__prettyRenderer, node),
- operation.render_operation(self.__prettyRenderer, simpleNode),
- node,
- simpleNode
- ])
- selection = self._historyView.get_selection()
- selectionPath = (len(self.__historyStore)-1, )
- selection.select_path(selectionPath)
- self._historyView.scroll_to_cell(selectionPath)
-
- def pop(self):
- if len(self.__historyStore) == 0:
- raise IndexError("Not enough items in the history for the operation")
-
- row = self.__historyStore[-1]
- data = row[self.DATA_IDX]
- del self.__historyStore[-1]
-
- return data
-
- def peek(self):
- if len(self.__historyStore) == 0:
- raise IndexError("Not enough items in the history for the operation")
- row = self.__historyStore[-1]
- data = row[self.DATA_IDX]
- return data
-
- def clear(self):
- self.__historyStore.clear()
-
- def __len__(self):
- return len(self.__historyStore)
-
- def __iter__(self):
- for row in iter(self.__historyStore):
- data = row[self.DATA_IDX]
- yield data
-
- @misc_utils.log_exception(_moduleLogger)
- def _on_close_activated(self, treeView, path, viewColumn):
- if viewColumn is self.__closeColumn:
- del self.__historyStore[path[0]]
- elif viewColumn is self.__resultColumn:
- row = self.__historyStore[path[0]]
- data = row[self.RESULT_IDX]
- self.push(data)
- elif viewColumn is self.__historyColumn:
- row = self.__historyStore[path[0]]
- data = row[self.DATA_IDX]
- self.push(data)
- else:
- assert False
+++ /dev/null
-#!/usr/bin/env python
-
-"""
-Open Issues
- @bug not all of a message is shown
- @bug Buttons are too small
-"""
-
-
-import gobject
-import gtk
-import dbus
-
-
-class _NullHildonModule(object):
- pass
-
-
-try:
- import hildon as _hildon
- hildon = _hildon # Dumb but gets around pyflakiness
-except (ImportError, OSError):
- hildon = _NullHildonModule
-
-
-IS_HILDON_SUPPORTED = hildon is not _NullHildonModule
-
-
-class _NullHildonProgram(object):
-
- def add_window(self, window):
- pass
-
-
-def _hildon_get_app_class():
- return hildon.Program
-
-
-def _null_get_app_class():
- return _NullHildonProgram
-
-
-try:
- hildon.Program
- get_app_class = _hildon_get_app_class
-except AttributeError:
- get_app_class = _null_get_app_class
-
-
-def _hildon_set_application_name(name):
- gtk.set_application_name(name)
-
-
-def _null_set_application_name(name):
- pass
-
-
-try:
- gtk.set_application_name
- set_application_name = _hildon_set_application_name
-except AttributeError:
- set_application_name = _null_set_application_name
-
-
-def _fremantle_hildonize_window(app, window):
- oldWindow = window
- newWindow = hildon.StackableWindow()
- if oldWindow.get_child() is not None:
- oldWindow.get_child().reparent(newWindow)
- app.add_window(newWindow)
- return newWindow
-
-
-def _hildon_hildonize_window(app, window):
- oldWindow = window
- newWindow = hildon.Window()
- if oldWindow.get_child() is not None:
- oldWindow.get_child().reparent(newWindow)
- app.add_window(newWindow)
- return newWindow
-
-
-def _null_hildonize_window(app, window):
- return window
-
-
-try:
- hildon.StackableWindow
- hildonize_window = _fremantle_hildonize_window
-except AttributeError:
- try:
- hildon.Window
- hildonize_window = _hildon_hildonize_window
- except AttributeError:
- hildonize_window = _null_hildonize_window
-
-
-def _fremantle_hildonize_menu(window, gtkMenu):
- appMenu = hildon.AppMenu()
- window.set_app_menu(appMenu)
- gtkMenu.get_parent().remove(gtkMenu)
- return appMenu
-
-
-def _hildon_hildonize_menu(window, gtkMenu):
- hildonMenu = gtk.Menu()
- for child in gtkMenu.get_children():
- child.reparent(hildonMenu)
- window.set_menu(hildonMenu)
- gtkMenu.destroy()
- return hildonMenu
-
-
-def _null_hildonize_menu(window, gtkMenu):
- return gtkMenu
-
-
-try:
- hildon.AppMenu
- GTK_MENU_USED = False
- IS_FREMANTLE_SUPPORTED = True
- hildonize_menu = _fremantle_hildonize_menu
-except AttributeError:
- GTK_MENU_USED = True
- IS_FREMANTLE_SUPPORTED = False
- if IS_HILDON_SUPPORTED:
- hildonize_menu = _hildon_hildonize_menu
- else:
- hildonize_menu = _null_hildonize_menu
-
-
-def _hildon_set_button_auto_selectable(button):
- button.set_theme_size(hildon.HILDON_SIZE_AUTO_HEIGHT)
-
-
-def _null_set_button_auto_selectable(button):
- pass
-
-
-try:
- hildon.HILDON_SIZE_AUTO_HEIGHT
- gtk.Button.set_theme_size
- set_button_auto_selectable = _hildon_set_button_auto_selectable
-except AttributeError:
- set_button_auto_selectable = _null_set_button_auto_selectable
-
-
-def _hildon_set_button_finger_selectable(button):
- button.set_theme_size(hildon.HILDON_SIZE_FINGER_HEIGHT)
-
-
-def _null_set_button_finger_selectable(button):
- pass
-
-
-try:
- hildon.HILDON_SIZE_FINGER_HEIGHT
- gtk.Button.set_theme_size
- set_button_finger_selectable = _hildon_set_button_finger_selectable
-except AttributeError:
- set_button_finger_selectable = _null_set_button_finger_selectable
-
-
-def _hildon_set_button_thumb_selectable(button):
- button.set_theme_size(hildon.HILDON_SIZE_THUMB_HEIGHT)
-
-
-def _null_set_button_thumb_selectable(button):
- pass
-
-
-try:
- hildon.HILDON_SIZE_THUMB_HEIGHT
- gtk.Button.set_theme_size
- set_button_thumb_selectable = _hildon_set_button_thumb_selectable
-except AttributeError:
- set_button_thumb_selectable = _null_set_button_thumb_selectable
-
-
-def _hildon_set_cell_thumb_selectable(renderer):
- renderer.set_property("scale", 1.5)
-
-
-def _null_set_cell_thumb_selectable(renderer):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- set_cell_thumb_selectable = _hildon_set_cell_thumb_selectable
-else:
- set_cell_thumb_selectable = _null_set_cell_thumb_selectable
-
-
-def _hildon_set_pix_cell_thumb_selectable(renderer):
- renderer.set_property("stock-size", 48)
-
-
-def _null_set_pix_cell_thumb_selectable(renderer):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- set_pix_cell_thumb_selectable = _hildon_set_pix_cell_thumb_selectable
-else:
- set_pix_cell_thumb_selectable = _null_set_pix_cell_thumb_selectable
-
-
-def _fremantle_show_information_banner(parent, message):
- hildon.hildon_banner_show_information(parent, "", message)
-
-
-def _hildon_show_information_banner(parent, message):
- hildon.hildon_banner_show_information(parent, None, message)
-
-
-def _null_show_information_banner(parent, message):
- pass
-
-
-if IS_FREMANTLE_SUPPORTED:
- show_information_banner = _fremantle_show_information_banner
-else:
- try:
- hildon.hildon_banner_show_information
- show_information_banner = _hildon_show_information_banner
- except AttributeError:
- show_information_banner = _null_show_information_banner
-
-
-def _fremantle_show_busy_banner_start(parent, message):
- hildon.hildon_gtk_window_set_progress_indicator(parent, True)
- return parent
-
-
-def _fremantle_show_busy_banner_end(banner):
- hildon.hildon_gtk_window_set_progress_indicator(banner, False)
-
-
-def _hildon_show_busy_banner_start(parent, message):
- return hildon.hildon_banner_show_animation(parent, None, message)
-
-
-def _hildon_show_busy_banner_end(banner):
- banner.destroy()
-
-
-def _null_show_busy_banner_start(parent, message):
- return None
-
-
-def _null_show_busy_banner_end(banner):
- assert banner is None
-
-
-try:
- hildon.hildon_gtk_window_set_progress_indicator
- show_busy_banner_start = _fremantle_show_busy_banner_start
- show_busy_banner_end = _fremantle_show_busy_banner_end
-except AttributeError:
- try:
- hildon.hildon_banner_show_animation
- show_busy_banner_start = _hildon_show_busy_banner_start
- show_busy_banner_end = _hildon_show_busy_banner_end
- except AttributeError:
- show_busy_banner_start = _null_show_busy_banner_start
- show_busy_banner_end = _null_show_busy_banner_end
-
-
-def _hildon_hildonize_text_entry(textEntry):
- textEntry.set_property('hildon-input-mode', 7)
-
-
-def _null_hildonize_text_entry(textEntry):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- hildonize_text_entry = _hildon_hildonize_text_entry
-else:
- hildonize_text_entry = _null_hildonize_text_entry
-
-
-def _hildon_window_to_portrait(window):
- # gtk documentation is unclear whether this does a "=" or a "|="
- flags = hildon.PORTRAIT_MODE_SUPPORT | hildon.PORTRAIT_MODE_REQUEST
- hildon.hildon_gtk_window_set_portrait_flags(window, flags)
-
-
-def _hildon_window_to_landscape(window):
- # gtk documentation is unclear whether this does a "=" or a "&= ~"
- flags = hildon.PORTRAIT_MODE_SUPPORT
- hildon.hildon_gtk_window_set_portrait_flags(window, flags)
-
-
-def _null_window_to_portrait(window):
- pass
-
-
-def _null_window_to_landscape(window):
- pass
-
-
-try:
- hildon.PORTRAIT_MODE_SUPPORT
- hildon.PORTRAIT_MODE_REQUEST
- hildon.hildon_gtk_window_set_portrait_flags
-
- window_to_portrait = _hildon_window_to_portrait
- window_to_landscape = _hildon_window_to_landscape
-except AttributeError:
- window_to_portrait = _null_window_to_portrait
- window_to_landscape = _null_window_to_landscape
-
-
-def get_device_orientation():
- bus = dbus.SystemBus()
- try:
- rawMceRequest = bus.get_object("com.nokia.mce", "/com/nokia/mce/request")
- mceRequest = dbus.Interface(rawMceRequest, dbus_interface="com.nokia.mce.request")
- orientation, standState, faceState, xAxis, yAxis, zAxis = mceRequest.get_device_orientation()
- except dbus.exception.DBusException:
- # catching for documentation purposes that when a system doesn't
- # support this, this is what to expect
- raise
-
- if orientation == "":
- return gtk.ORIENTATION_HORIZONTAL
- elif orientation == "":
- return gtk.ORIENTATION_VERTICAL
- else:
- raise RuntimeError("Unknown orientation: %s" % orientation)
-
-
-def _hildon_hildonize_password_entry(textEntry):
- textEntry.set_property('hildon-input-mode', 7 | (1 << 29))
-
-
-def _null_hildonize_password_entry(textEntry):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- hildonize_password_entry = _hildon_hildonize_password_entry
-else:
- hildonize_password_entry = _null_hildonize_password_entry
-
-
-def _hildon_hildonize_combo_entry(comboEntry):
- comboEntry.set_property('hildon-input-mode', 1 << 4)
-
-
-def _null_hildonize_combo_entry(textEntry):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- hildonize_combo_entry = _hildon_hildonize_combo_entry
-else:
- hildonize_combo_entry = _null_hildonize_combo_entry
-
-
-def _null_create_seekbar():
- adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1)
- seek = gtk.HScale(adjustment)
- seek.set_draw_value(False)
- return seek
-
-
-def _fremantle_create_seekbar():
- seek = hildon.Seekbar()
- seek.set_range(0.0, 100)
- seek.set_draw_value(False)
- seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
- return seek
-
-
-try:
- hildon.Seekbar
- create_seekbar = _fremantle_create_seekbar
-except AttributeError:
- create_seekbar = _null_create_seekbar
-
-
-def _fremantle_hildonize_scrollwindow(scrolledWindow):
- pannableWindow = hildon.PannableArea()
-
- child = scrolledWindow.get_child()
- scrolledWindow.remove(child)
- pannableWindow.add(child)
-
- parent = scrolledWindow.get_parent()
- if parent is not None:
- parent.remove(scrolledWindow)
- parent.add(pannableWindow)
-
- return pannableWindow
-
-
-def _hildon_hildonize_scrollwindow(scrolledWindow):
- hildon.hildon_helper_set_thumb_scrollbar(scrolledWindow, True)
- return scrolledWindow
-
-
-def _null_hildonize_scrollwindow(scrolledWindow):
- return scrolledWindow
-
-
-try:
- hildon.PannableArea
- hildonize_scrollwindow = _fremantle_hildonize_scrollwindow
- hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
-except AttributeError:
- try:
- hildon.hildon_helper_set_thumb_scrollbar
- hildonize_scrollwindow = _hildon_hildonize_scrollwindow
- hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
- except AttributeError:
- hildonize_scrollwindow = _null_hildonize_scrollwindow
- hildonize_scrollwindow_with_viewport = _null_hildonize_scrollwindow
-
-
-def _hildon_request_number(parent, title, range, default):
- spinner = hildon.NumberEditor(*range)
- spinner.set_value(default)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(spinner)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- return spinner.get_value()
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-def _null_request_number(parent, title, range, default):
- adjustment = gtk.Adjustment(default, range[0], range[1], 1, 5, 0)
- spinner = gtk.SpinButton(adjustment, 0, 0)
- spinner.set_wrap(False)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(spinner)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- return spinner.get_value_as_int()
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-try:
- hildon.NumberEditor # TODO deprecated in fremantle
- request_number = _hildon_request_number
-except AttributeError:
- request_number = _null_request_number
-
-
-def _hildon_touch_selector(parent, title, items, defaultIndex):
- model = gtk.ListStore(gobject.TYPE_STRING)
- for item in items:
- model.append((item, ))
-
- selector = hildon.TouchSelector()
- selector.append_text_column(model, True)
- selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
- selector.set_active(0, defaultIndex)
-
- dialog = hildon.PickerDialog(parent)
- dialog.set_selector(selector)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- return selector.get_active(0)
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-def _on_null_touch_selector_activated(treeView, path, column, dialog, pathData):
- dialog.response(gtk.RESPONSE_OK)
- pathData[0] = path
-
-
-def _null_touch_selector(parent, title, items, defaultIndex = -1):
- parentSize = parent.get_size()
-
- model = gtk.ListStore(gobject.TYPE_STRING)
- for item in items:
- model.append((item, ))
-
- cell = gtk.CellRendererText()
- set_cell_thumb_selectable(cell)
- column = gtk.TreeViewColumn(title)
- column.pack_start(cell, expand=True)
- column.add_attribute(cell, "text", 0)
-
- treeView = gtk.TreeView()
- treeView.set_model(model)
- treeView.append_column(column)
- selection = treeView.get_selection()
- selection.set_mode(gtk.SELECTION_SINGLE)
- if 0 < defaultIndex:
- selection.select_path((defaultIndex, ))
-
- scrolledWin = gtk.ScrolledWindow()
- scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scrolledWin.add(treeView)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(scrolledWin)
- dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
-
- scrolledWin = hildonize_scrollwindow(scrolledWin)
- pathData = [None]
- treeView.connect("row-activated", _on_null_touch_selector_activated, dialog, pathData)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- if pathData[0] is None:
- raise RuntimeError("No selection made")
- return pathData[0][0]
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-try:
- hildon.PickerDialog
- hildon.TouchSelector
- touch_selector = _hildon_touch_selector
-except AttributeError:
- touch_selector = _null_touch_selector
-
-
-def _hildon_touch_selector_entry(parent, title, items, defaultItem):
- # Got a segfault when using append_text_column with TouchSelectorEntry, so using this way
- try:
- selector = hildon.TouchSelectorEntry(text=True)
- except TypeError:
- selector = hildon.hildon_touch_selector_entry_new_text()
- defaultIndex = -1
- for i, item in enumerate(items):
- selector.append_text(item)
- if item == defaultItem:
- defaultIndex = i
-
- dialog = hildon.PickerDialog(parent)
- dialog.set_selector(selector)
-
- if 0 < defaultIndex:
- selector.set_active(0, defaultIndex)
- else:
- selector.get_entry().set_text(defaultItem)
-
- try:
- dialog.show_all()
- response = dialog.run()
- finally:
- dialog.hide()
-
- if response == gtk.RESPONSE_OK:
- return selector.get_entry().get_text()
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
-
-
-def _on_null_touch_selector_entry_entry_changed(entry, result, selection, defaultIndex):
- custom = entry.get_text().strip()
- if custom:
- result[0] = custom
- selection.unselect_all()
- else:
- result[0] = None
- selection.select_path((defaultIndex, ))
-
-
-def _on_null_touch_selector_entry_entry_activated(customEntry, dialog, result):
- dialog.response(gtk.RESPONSE_OK)
- result[0] = customEntry.get_text()
-
-
-def _on_null_touch_selector_entry_tree_activated(treeView, path, column, dialog, result):
- dialog.response(gtk.RESPONSE_OK)
- model = treeView.get_model()
- itr = model.get_iter(path)
- if itr is not None:
- result[0] = model.get_value(itr, 0)
-
-
-def _null_touch_selector_entry(parent, title, items, defaultItem):
- parentSize = parent.get_size()
-
- model = gtk.ListStore(gobject.TYPE_STRING)
- defaultIndex = -1
- for i, item in enumerate(items):
- model.append((item, ))
- if item == defaultItem:
- defaultIndex = i
-
- cell = gtk.CellRendererText()
- set_cell_thumb_selectable(cell)
- column = gtk.TreeViewColumn(title)
- column.pack_start(cell, expand=True)
- column.add_attribute(cell, "text", 0)
-
- treeView = gtk.TreeView()
- treeView.set_model(model)
- treeView.append_column(column)
- selection = treeView.get_selection()
- selection.set_mode(gtk.SELECTION_SINGLE)
-
- scrolledWin = gtk.ScrolledWindow()
- scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scrolledWin.add(treeView)
-
- customEntry = gtk.Entry()
-
- layout = gtk.VBox()
- layout.pack_start(customEntry, expand=False)
- layout.pack_start(scrolledWin)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(layout)
- dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
-
- scrolledWin = hildonize_scrollwindow(scrolledWin)
-
- result = [None]
- if 0 < defaultIndex:
- selection.select_path((defaultIndex, ))
- result[0] = defaultItem
- else:
- customEntry.set_text(defaultItem)
-
- customEntry.connect("activate", _on_null_touch_selector_entry_entry_activated, dialog, result)
- customEntry.connect("changed", _on_null_touch_selector_entry_entry_changed, result, selection, defaultIndex)
- treeView.connect("row-activated", _on_null_touch_selector_entry_tree_activated, dialog, result)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- _, itr = selection.get_selected()
- if itr is not None:
- return model.get_value(itr, 0)
- else:
- enteredText = customEntry.get_text().strip()
- if enteredText:
- return enteredText
- elif result[0] is not None:
- return result[0]
- else:
- raise RuntimeError("No selection made")
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-try:
- hildon.PickerDialog
- hildon.TouchSelectorEntry
- touch_selector_entry = _hildon_touch_selector_entry
-except AttributeError:
- touch_selector_entry = _null_touch_selector_entry
-
-
-if __name__ == "__main__":
- app = get_app_class()()
-
- label = gtk.Label("Hello World from a Label!")
-
- win = gtk.Window()
- win.add(label)
- win = hildonize_window(app, win)
- if False and IS_FREMANTLE_SUPPORTED:
- appMenu = hildon.AppMenu()
- for i in xrange(5):
- b = gtk.Button(str(i))
- appMenu.append(b)
- win.set_app_menu(appMenu)
- win.show_all()
- appMenu.show_all()
- gtk.main()
- elif False:
- print touch_selector(win, "Test", ["A", "B", "C", "D"], 2)
- elif False:
- print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "C")
- print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "Blah")
- elif False:
- import pprint
- name, value = "", ""
- goodLocals = [
- (name, value) for (name, value) in locals().iteritems()
- if not name.startswith("_")
- ]
- pprint.pprint(goodLocals)
- elif False:
- import time
- show_information_banner(win, "Hello World")
- time.sleep(5)
- elif False:
- import time
- banner = show_busy_banner_start(win, "Hello World")
- time.sleep(5)
- show_busy_banner_end(banner)
+++ /dev/null
-#!/usr/bin/env python
-
-"""
-@todo Handle sizing in a better manner http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/sec-custom-widgets.html
-"""
-
-
-from __future__ import division
-
-import os
-import weakref
-import math
-import copy
-import warnings
-
-import gobject
-import gtk
-import cairo
-import pango
-
-try:
- import rsvg
-except ImportError:
- rsvg = None
-
-
-def deg_to_rad(deg):
- return (2 * math.pi * deg) / 360.0
-
-
-def rad_to_deg(rad):
- return (360.0 * rad) / (2 * math.pi)
-
-
-def normalize_radian_angle(radAng):
- """
- Restricts @param radAng to the range [0..2pi)
- """
- twoPi = 2 * math.pi
-
- while radAng < 0:
- radAng += twoPi
- while twoPi <= radAng:
- radAng -= twoPi
-
- return radAng
-
-
-def delta_to_rtheta(dx, dy):
- distance = math.sqrt(dx**2 + dy**2)
-
- angleInRads = math.atan2(-dy, dx)
- if angleInRads < 0:
- angleInRads = 2*math.pi + angleInRads
- return distance, angleInRads
-
-
-class FontCache(object):
-
- def __init__(self):
- self.__fontCache = {}
-
- def get_font(self, s):
- if s in self.__fontCache:
- return self.__fontCache[s]
-
- descr = pango.FontDescription(s)
- self.__fontCache[s] = descr
-
- return descr
-
-
-FONTS = FontCache()
-
-
-class ImageCache(object):
-
- def __init__(self):
- self.__imageCache = {}
- self.__imagePaths = [
- os.path.join(os.path.dirname(__file__), "images"),
- ]
-
- def add_path(self, path):
- self.__imagePaths.append(path)
-
- def get_image(self, s):
- if s in self.__imageCache:
- return self.__imageCache[s]
-
- image = None
-
- if s.lower().endswith(".png"):
- for path in self.__imagePaths:
- imagePath = os.path.join(path, s)
- try:
- image = cairo.ImageSurface.create_from_png(imagePath)
- break
- except:
- warnings.warn("Unable to load image %s" % imagePath)
- elif s.lower().endswith(".svg") and rsvg is not None:
- for path in self.__imagePaths:
- imagePath = os.path.join(path, s)
- try:
- image = rsvg.Handle(file=imagePath)
- except:
- warnings.warn("Unable to load image %s" % imagePath)
- else:
- print "Don't know how to load image file type:", s
-
- if image is not None:
- self.__imageCache[s] = image
-
- return image
-
-
-IMAGES = ImageCache()
-
-
-def convert_color(gtkColor):
- r = gtkColor.red / 65535
- g = gtkColor.green / 65535
- b = gtkColor.blue / 65535
- return r, g, b
-
-
-def generate_pie_style(widget):
- # GTK states:
- # * gtk.STATE_NORMAL - The state of a sensitive widget that is not active and does not have the focus
- # * gtk.STATE_ACTIVE - The state of a sensitive widget when it is active e.g. a button that is pressed but not yet released
- # * gtk.STATE_PRELIGHT - The state of a sensitive widget that has the focus e.g. a button that has the mouse pointer over it.
- # * gtk.STATE_SELECTED - The state of a widget that is selected e.g. selected text in a gtk.Entry widget
- # * gtk.STATE_INSENSITIVE - The state of a widget that is insensitive and will not respond to any events e.g. cannot be activated, selected or prelit.
-
- widget.ensure_style()
- gtkStyle = widget.get_style()
- sliceStyle = dict(
- (gtkStyleState, {
- "text": convert_color(gtkStyle.text[gtkStyleState]),
- "fill": convert_color(gtkStyle.bg[gtkStyleState]),
- "stroke": None,
- })
- for gtkStyleState in (
- gtk.STATE_NORMAL, gtk.STATE_ACTIVE, gtk.STATE_PRELIGHT, gtk.STATE_SELECTED, gtk.STATE_INSENSITIVE
- )
- )
-
- return sliceStyle
-
-
-class PieSlice(object):
-
- SLICE_CENTER = 0
- SLICE_EAST = 1
- SLICE_SOUTH_EAST = 2
- SLICE_SOUTH = 3
- SLICE_SOUTH_WEST = 4
- SLICE_WEST = 5
- SLICE_NORTH_WEST = 6
- SLICE_NORTH = 7
- SLICE_NORTH_EAST = 8
-
- MAX_ANGULAR_SLICES = 8
-
- SLICE_DIRECTIONS = [
- SLICE_CENTER,
- SLICE_EAST,
- SLICE_SOUTH_EAST,
- SLICE_SOUTH,
- SLICE_SOUTH_WEST,
- SLICE_WEST,
- SLICE_NORTH_WEST,
- SLICE_NORTH,
- SLICE_NORTH_EAST,
- ]
-
- SLICE_DIRECTION_NAMES = [
- "CENTER",
- "EAST",
- "SOUTH_EAST",
- "SOUTH",
- "SOUTH_WEST",
- "WEST",
- "NORTH_WEST",
- "NORTH",
- "NORTH_EAST",
- ]
-
- def __init__(self, handler = (lambda p, s, d: None)):
- self._direction = self.SLICE_CENTER
- self._pie = None
- self._style = None
- self._handler = handler
-
- def menu_init(self, pie, direction):
- self._direction = direction
- self._pie = weakref.ref(pie)
- self._style = pie.sliceStyle
-
- def calculate_minimum_radius(self, context, textLayout):
- return 0
-
- def draw_fg(self, styleState, isSelected, context, textLayout):
- if isSelected:
- styleState = gtk.STATE_ACTIVE
- self._draw_fg(styleState, context, textLayout)
-
- def draw_bg(self, styleState, isSelected, context, textLayout):
- if isSelected:
- styleState = gtk.STATE_ACTIVE
- self._draw_bg(styleState, context, textLayout)
-
- def _draw_fg(self, styleState, context, textLayout):
- pass
-
- def _draw_bg(self, styleState, context, textLayout):
- centerPosition = self._pie().centerPosition
- radius = max(self._pie().radius, self.calculate_minimum_radius(context, textLayout))
- outerRadius = self._pie().outerRadius
-
- fillColor = self._style[styleState]["fill"]
- if not fillColor:
- return
-
- if self._direction == self.SLICE_CENTER:
- context.arc(
- centerPosition[0],
- centerPosition[1],
- radius,
- 0,
- 2 * math.pi
- )
-
- context.set_source_rgb(*fillColor)
- context.fill()
- else:
- sliceCenterAngle = self.quadrant_to_theta(self._direction)
- sliceArcWidth = 2*math.pi / self.MAX_ANGULAR_SLICES
- sliceStartAngle = sliceCenterAngle - sliceArcWidth/2
- sliceEndAngle = sliceCenterAngle + sliceArcWidth/2
-
- context.arc(
- centerPosition[0],
- centerPosition[1],
- radius,
- sliceStartAngle,
- sliceEndAngle,
- )
- context.arc_negative(
- centerPosition[0],
- centerPosition[1],
- outerRadius,
- sliceEndAngle,
- sliceStartAngle,
- )
- context.close_path()
-
- context.set_source_rgb(*fillColor)
- context.fill()
-
- def activate(self):
- self._handler(self._pie(), self, self._direction)
-
- @classmethod
- def rtheta_to_quadrant(cls, distance, angleInRads, innerRadius):
- if distance < innerRadius:
- quadrant = 0
- else:
- gradians = angleInRads / (2*math.pi)
- preciseQuadrant = gradians * cls.MAX_ANGULAR_SLICES + cls.MAX_ANGULAR_SLICES / (2 * 2*math.pi)
- quadrantWithWrap = int(preciseQuadrant)
- quadrant = quadrantWithWrap % cls.MAX_ANGULAR_SLICES
- quadrant += 1
-
- return quadrant
-
- @classmethod
- def quadrant_to_theta(cls, quadrant):
- assert quadrant != 0
- quadrant -= 1
-
- gradians = quadrant / cls.MAX_ANGULAR_SLICES
- radians = gradians * 2*math.pi
-
- return radians
-
-
-class NullPieSlice(PieSlice):
-
- def draw_bg(self, styleState, isSelected, context, textLayout):
- super(NullPieSlice, self).draw_bg(styleState, False, context, textLayout)
-
-
-class LabelPieSlice(PieSlice):
-
- def _align_label(self, labelWidth, labelHeight):
- centerPosition = self._pie().centerPosition
- if self._direction == PieSlice.SLICE_CENTER:
- labelX = centerPosition[0] - labelWidth/2
- labelY = centerPosition[1] - labelHeight/2
- else:
- if self._direction in (PieSlice.SLICE_NORTH_WEST, PieSlice.SLICE_WEST, PieSlice.SLICE_SOUTH_WEST):
- outerX = 0
- labelX = outerX
- elif self._direction in (PieSlice.SLICE_SOUTH, PieSlice.SLICE_NORTH):
- outerX = centerPosition[0]
- labelX = outerX - labelWidth/2
- elif self._direction in (PieSlice.SLICE_NORTH_EAST, PieSlice.SLICE_EAST, PieSlice.SLICE_SOUTH_EAST):
- outerX = centerPosition[0] * 2
- labelX = outerX - labelWidth
- else:
- assert False, "Direction %d is incorrect" % self._direction
-
- if self._direction in (PieSlice.SLICE_NORTH_EAST, PieSlice.SLICE_NORTH, PieSlice.SLICE_NORTH_WEST):
- outerY = 0
- labelY = outerY
- elif self._direction in (PieSlice.SLICE_EAST, PieSlice.SLICE_WEST):
- outerY = centerPosition[1]
- labelY = outerY - labelHeight/2
- elif self._direction in (PieSlice.SLICE_SOUTH_EAST, PieSlice.SLICE_SOUTH, PieSlice.SLICE_SOUTH_WEST):
- outerY = centerPosition[1] * 2
- labelY = outerY - labelHeight
- else:
- assert False, "Direction %d is incorrect" % self._direction
-
- return int(labelX), int(labelY)
-
-
-class TextLabelPieSlice(LabelPieSlice):
-
- def __init__(self, text, fontName = 'Helvetica 12', handler = (lambda p, s, d: None)):
- super(TextLabelPieSlice, self).__init__(handler = handler)
- self.__text = text
- self.__fontName = fontName
-
- def calculate_minimum_radius(self, context, textLayout):
- font = FONTS.get_font(self.__fontName)
- textLayout.set_font_description(font)
- textLayout.set_markup(self.__text)
-
- labelWidth, labelHeight = textLayout.get_pixel_size()
- return min(labelWidth, labelHeight) / 2
-
- def _draw_fg(self, styleState, context, textLayout):
- super(TextLabelPieSlice, self)._draw_fg(styleState, context, textLayout)
-
- textColor = self._style[styleState]["text"]
- font = FONTS.get_font(self.__fontName)
-
- context.set_source_rgb(*textColor)
- textLayout.set_font_description(font)
- textLayout.set_markup(self.__text)
- labelWidth, labelHeight = textLayout.get_pixel_size()
- labelX, labelY = self._align_label(labelWidth, labelHeight)
-
- context.move_to(
- labelX,
- labelY,
- )
-
- context.show_layout(textLayout)
-
-
-class ImageLabelPieSlice(LabelPieSlice):
-
- def __init__(self, imagePath, handler = (lambda p, s, d: None)):
- super(ImageLabelPieSlice, self).__init__(handler = handler)
- self.__imagePath = imagePath
-
- def calculate_minimum_radius(self, context, textLayout):
- image = IMAGES.get_image(self.__imagePath)
- if image is None:
- return
- labelWidth, labelHeight = image.get_width(), image.get_height()
- return min(labelWidth, labelHeight) / 2
-
- def _draw_fg(self, styleState, context, textLayout):
- super(ImageLabelPieSlice, self)._draw_fg(styleState, context, textLayout)
-
- image = IMAGES.get_image(self.__imagePath)
- if image is None:
- return
-
- labelWidth, labelHeight = image.get_width(), image.get_height()
- labelX, labelY = self._align_label(labelWidth, labelHeight)
-
- context.set_source_surface(
- image,
- labelX,
- labelY,
- )
-
- context.paint()
-
-
-class PieMenu(gtk.DrawingArea):
-
- def __init__(self, style = None, **kwds):
- super(PieMenu, self).__init__()
-
- self.sliceStyle = style
- self.centerPosition = 0, 0
- self.radius = 20
- self.outerRadius = self.radius * 2
-
- self.connect("expose_event", self._on_expose)
- self.connect("motion_notify_event", self._on_motion_notify)
- self.connect("leave_notify_event", self._on_leave_notify)
- self.connect("proximity_in_event", self._on_motion_notify)
- self.connect("proximity_out_event", self._on_leave_notify)
- self.connect("button_press_event", self._on_button_press)
- self.connect("button_release_event", self._on_button_release)
-
- self.set_events(
- gtk.gdk.EXPOSURE_MASK |
- gtk.gdk.POINTER_MOTION_MASK |
- gtk.gdk.POINTER_MOTION_HINT_MASK |
- gtk.gdk.BUTTON_MOTION_MASK |
- gtk.gdk.BUTTON_PRESS_MASK |
- gtk.gdk.BUTTON_RELEASE_MASK |
- gtk.gdk.PROXIMITY_IN_MASK |
- gtk.gdk.PROXIMITY_OUT_MASK |
- gtk.gdk.LEAVE_NOTIFY_MASK
- )
-
- self.__activeSlice = None
- self.__slices = {}
- for direction in PieSlice.SLICE_DIRECTIONS:
- self.add_slice(NullPieSlice(), direction)
-
- self.__clickPosition = 0, 0
- self.__styleState = gtk.STATE_NORMAL
-
- def add_slice(self, slice, direction):
- assert direction in PieSlice.SLICE_DIRECTIONS
-
- slice.menu_init(self, direction)
- self.__slices[direction] = slice
-
- if direction == PieSlice.SLICE_CENTER:
- self.__activeSlice = self.__slices[PieSlice.SLICE_CENTER]
-
- def __update_state(self, mousePosition):
- rect = self.get_allocation()
- newStyleState = self.__styleState
-
- if (
- 0 <= mousePosition[0] and mousePosition[1] < rect.width and
- 0 <= mousePosition[1] and mousePosition[1] < rect.height
- ):
- if self.__clickPosition == (0, 0):
- newStyleState = gtk.STATE_PRELIGHT
- else:
- if self.__clickPosition != (0, 0):
- newStyleState = gtk.STATE_PRELIGHT
-
- if newStyleState != self.__styleState:
- self.__generate_draw_event()
- self.__styleState = newStyleState
-
- def __process_mouse_position(self, mousePosition):
- self.__update_state(mousePosition)
- if self.__clickPosition == (0, 0):
- return
-
- delta = (
- mousePosition[0] - self.centerPosition[0],
- - (mousePosition[1] - self.centerPosition[1])
- )
- distance, angleInRads = delta_to_rtheta(delta[0], delta[1])
- quadrant = PieSlice.rtheta_to_quadrant(distance, angleInRads, self.radius)
- self.__select_slice(self.__slices[quadrant])
-
- def __select_slice(self, newSlice):
- if newSlice is self.__activeSlice:
- return
-
- oldSlice = self.__activeSlice
- self.__activeSlice = newSlice
- self.__generate_draw_event()
-
- def __generate_draw_event(self):
- if self.window is None:
- return
- self.queue_draw()
-
- def _on_expose(self, widget, event):
- cairoContext = self.window.cairo_create()
- pangoContext = self.create_pango_context()
- textLayout = pango.Layout(pangoContext)
-
- rect = self.get_allocation()
- position = 0, 0
- dimensions = rect.width, rect.height
-
- self.centerPosition = position[0] + dimensions[0] / 2, position[1] + dimensions[1] / 2
- self.outerRadius = max(*dimensions) # be larger than the view
- self.radius = self.outerRadius / (3*2) # fit inside the middle cell
-
- # Draw Background
- cairoContext.rectangle(
- position[0],
- position[1],
- dimensions[0],
- dimensions[1],
- )
- cairoContext.set_source_rgb(*self.sliceStyle[self.__styleState]["fill"])
- cairoContext.fill()
-
- isSelected = self.__clickPosition != (0, 0)
- self.__activeSlice.draw_bg(self.__styleState, isSelected, cairoContext, textLayout)
-
- # Draw Foreground
- for slice in self.__slices.itervalues():
- isSelected = (slice is self.__activeSlice)
- if not isSelected:
- slice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
-
- isSelected = self.__clickPosition != (0, 0)
- self.__activeSlice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
-
- def _on_leave_notify(self, widget, event):
- newStyleState = gtk.STATE_NORMAL
- if newStyleState != self.__styleState:
- self.__generate_draw_event()
- self.__styleState = newStyleState
-
- mousePosition = event.get_coords()
- self.__process_mouse_position(mousePosition)
-
- def _on_motion_notify(self, widget, event):
- mousePosition = event.get_coords()
- self.__process_mouse_position(mousePosition)
-
- def _on_button_press(self, widget, event):
- self.__clickPosition = event.get_coords()
-
- self._on_motion_notify(widget, event)
- self.__generate_draw_event()
-
- def _on_button_release(self, widget, event):
- self._on_motion_notify(widget, event)
-
- self.__activeSlice.activate()
- self.__activeSlice = self.__slices[PieSlice.SLICE_CENTER]
- self.__clickPosition = 0, 0
-
- self.__generate_draw_event()
-
-
-gobject.type_register(PieMenu)
-
-
-class FakeEvent(object):
-
- def __init__(self, x, y, isHint):
- self.x = x
- self.y = y
- self.is_hint = isHint
-
- def get_coords(self):
- return self.x, self.y
-
-
-class PiePopup(gtk.DrawingArea):
-
- def __init__(self, style = None, **kwds):
- super(PiePopup, self).__init__()
-
- self.showAllSlices = True
- self.sliceStyle = style
- self.centerPosition = 0, 0
- self.radius = 20
- self.outerRadius = self.radius * 2
-
- self.connect("expose_event", self._on_expose)
- self.connect("motion_notify_event", self._on_motion_notify)
- self.connect("proximity_in_event", self._on_motion_notify)
- self.connect("proximity_out_event", self._on_leave_notify)
- self.connect("leave_notify_event", self._on_leave_notify)
- self.connect("button_press_event", self._on_button_press)
- self.connect("button_release_event", self._on_button_release)
-
- self.set_events(
- gtk.gdk.EXPOSURE_MASK |
- gtk.gdk.POINTER_MOTION_MASK |
- gtk.gdk.POINTER_MOTION_HINT_MASK |
- gtk.gdk.BUTTON_MOTION_MASK |
- gtk.gdk.BUTTON_PRESS_MASK |
- gtk.gdk.BUTTON_RELEASE_MASK |
- gtk.gdk.PROXIMITY_IN_MASK |
- gtk.gdk.PROXIMITY_OUT_MASK |
- gtk.gdk.LEAVE_NOTIFY_MASK
- )
-
- self.__popped = False
- self.__styleState = gtk.STATE_NORMAL
- self.__activeSlice = None
- self.__slices = {}
- self.__localSlices = {}
-
- self.__clickPosition = 0, 0
- self.__popupTimeDelay = None
-
- self.__pie = None
- self.__pie = PieMenu(self.sliceStyle)
- self.__pie.connect("button_release_event", self._on_button_release)
- self.__pie.show()
-
- self.__popupWindow = gtk.Window(type = gtk.WINDOW_POPUP)
- self.__popupWindow.set_title("")
- self.__popupWindow.add(self.__pie)
-
- self.add_slice(NullPieSlice(), PieSlice.SLICE_CENTER)
-
- def add_slice(self, slice, direction):
- assert direction in PieSlice.SLICE_DIRECTIONS
-
- self.__pie.add_slice(copy.copy(slice), direction)
-
- if self.showAllSlices or direction == PieSlice.SLICE_CENTER:
- self.__localSlices[direction] = slice
- self.__localSlices[direction].menu_init(self, direction)
- if direction == PieSlice.SLICE_CENTER:
- self.__activeSlice = self.__localSlices[PieSlice.SLICE_CENTER]
-
- def __update_state(self, mousePosition):
- rect = self.get_allocation()
- newStyleState = self.__styleState
-
- if (
- 0 <= mousePosition[0] and mousePosition[0] < rect.width and
- 0 <= mousePosition[1] and mousePosition[1] < rect.height
- ):
- if self.__clickPosition == (0, 0):
- newStyleState = gtk.STATE_PRELIGHT
- else:
- if self.__clickPosition != (0, 0):
- newStyleState = gtk.STATE_PRELIGHT
-
- if newStyleState != self.__styleState:
- self.__styleState = newStyleState
- self.__generate_draw_event()
-
- def __generate_draw_event(self):
- self.queue_draw()
-
- def _on_expose(self, widget, event):
- rect = self.get_allocation()
- position = 0, 0
- dimensions = rect.width, rect.height
-
- # update sizing information
- self.centerPosition = position[0] + dimensions[0] / 2, position[1] + dimensions[1] / 2
- self.outerRadius = max(*dimensions) # be larger than the view
- self.radius = self.outerRadius / (3*2) # fit inside the middle cell
-
- # Draw Background
- cairoContext = self.window.cairo_create()
- cairoContext.rectangle(
- position[0],
- position[1],
- dimensions[0],
- dimensions[1],
- )
- cairoContext.set_source_rgb(*self.sliceStyle[self.__styleState]["fill"])
- cairoContext.fill()
-
- # Draw Foreground
- pangoContext = self.create_pango_context()
- textLayout = pango.Layout(pangoContext)
- for slice in self.__localSlices.itervalues():
- isSelected = (slice is self.__activeSlice)
- if not isSelected:
- slice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
-
- isSelected = self.__clickPosition != (0, 0)
- self.__activeSlice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
-
- def _on_leave_notify(self, widget, event):
- newStyleState = gtk.STATE_NORMAL
- if newStyleState != self.__styleState:
- self.__styleState = newStyleState
- self.__generate_draw_event()
-
- self._on_motion_notify(widget, event)
-
- def _on_motion_notify(self, widget, event):
- self.__update_state(event.get_coords())
- if not self.__popped:
- return
-
- mousePosition = event.get_root_coords()
- piePosition = self.__popupWindow.get_position()
- event.x = mousePosition[0] - piePosition[0]
- event.y = mousePosition[1] - piePosition[1]
- self.__pie._on_motion_notify(self.__pie, event)
-
- def _on_button_press(self, widget, event):
- if len(self.__localSlices) == 0:
- return
-
- if self.__popupTimeDelay is not None:
- # This press is for a double click for which we do not get a release
- # self._on_button_release(widget, event)
- return
-
- self.__clickPosition = event.get_root_coords()
- self.__generate_draw_event()
- self.__popupTimeDelay = gobject.timeout_add(150, self._on_delayed_popup)
-
- def _on_delayed_popup(self):
- self.__popup(self.__clickPosition)
- gobject.source_remove(self.__popupTimeDelay)
- self.__popupTimeDelay = None
- return False
-
- def _on_button_release(self, widget, event):
- if len(self.__localSlices) == 0:
- return
-
- if self.__popupTimeDelay is None:
- mousePosition = event.get_root_coords()
- piePosition = self.__popupWindow.get_position()
- eventX = mousePosition[0] - piePosition[0]
- eventY = mousePosition[1] - piePosition[1]
- pieRelease = FakeEvent(eventX, eventY, False)
- self.__pie._on_button_release(self.__pie, pieRelease)
-
- self.__unpop()
- else:
- gobject.source_remove(self.__popupTimeDelay)
- self.__popupTimeDelay = None
- self.__activeSlice.activate()
-
- self.__clickPosition = 0, 0
- self.__generate_draw_event()
-
- def __popup(self, position):
- assert not self.__popped
- self.__popped = True
-
- width, height = 256, 256
- popupX, popupY = position[0] - width/2, position[1] - height/2
-
- self.__popupWindow.move(int(popupX), int(popupY))
- self.__popupWindow.resize(width, height)
- pieClick = FakeEvent(width/2, height/2, False)
- self.__pie._on_button_press(self.__pie, pieClick)
- self.__pie.grab_focus()
-
- self.__popupWindow.show()
-
- def __unpop(self):
- assert self.__popped
- self.__popped = False
-
- piePosition = self.__popupWindow.get_position()
- self.grab_focus()
-
- self.__popupWindow.hide()
-
-
-gobject.type_register(PiePopup)
-
-
-def pie_main(isPop):
- win = gtk.Window()
- win.set_title("Pie Menu Test")
-
- sliceStyle = generate_pie_style(win)
- if isPop:
- target = PiePopup(sliceStyle)
- else:
- target = PieMenu(sliceStyle)
-
- def handler(pie, slice, direction):
- print pie, slice, direction
- target.add_slice(TextLabelPieSlice("C", handler=handler), PieSlice.SLICE_CENTER)
- target.add_slice(TextLabelPieSlice("N", handler=handler), PieSlice.SLICE_NORTH)
- target.add_slice(TextLabelPieSlice("S", handler=handler), PieSlice.SLICE_SOUTH)
- target.add_slice(TextLabelPieSlice("E", handler=handler), PieSlice.SLICE_EAST)
- target.add_slice(TextLabelPieSlice("W", handler=handler), PieSlice.SLICE_WEST)
-
- win.add(target)
- win.resize(300, 300)
- win.connect("destroy", lambda w: gtk.main_quit())
- win.show_all()
-
-
-if __name__ == "__main__":
- pie_main(False)
- pie_main(True)
- gtk.main()
+++ /dev/null
-#!/usr/bin/env python
-
-
-from __future__ import division
-
-import copy
-import warnings
-
-import gobject
-import gtk
-
-import gtkpie
-
-
-class PieKeyboard(gtk.Table):
-
- def __init__(self, style, rows, columns, alternateStyles=True):
- super(PieKeyboard, self).__init__(rows, columns, homogeneous=True)
-
- self.__cells = {}
- for row in xrange(rows):
- for column in xrange(columns):
- popup = gtkpie.PiePopup(
- self._alternate_style(row, column, style) if alternateStyles else style
- )
- self.attach(popup, column, column+1, row, row+1)
- self.__cells[(row, column)] = popup
-
- def add_slice(self, row, column, slice, direction):
- pie = self.__cells[(row, column)]
- pie.add_slice(slice, direction)
-
- def add_slices(self, row, column, slices):
- pie = self.__cells[(row, column)]
- for direction, slice in slices.iteritems():
- pie.add_slice(slice, direction)
-
- def get_pie(self, row, column):
- return self.__cells[(row, column)]
-
- @classmethod
- def _alternate_style(cls, row, column, style):
- i = row + column
- isEven = (i % 2) == 0
-
- if not isEven:
- return style
-
- altStyle = copy.copy(style)
- selected = altStyle[True]
- notSelected = altStyle[False]
- altStyle[False] = selected
- altStyle[True] = notSelected
- return altStyle
-
-
-class KeyboardModifier(object):
-
- def __init__(self, name):
- self.name = name
- self.lock = False
- self.once = False
-
- @property
- def isActive(self):
- return self.lock or self.once
-
- def on_toggle_lock(self, *args, **kwds):
- self.lock = not self.lock
-
- def on_toggle_once(self, *args, **kwds):
- self.once = not self.once
-
- def reset_once(self):
- self.once = False
-
-
-gobject.type_register(PieKeyboard)
-
-
-def parse_keyboard_data(text):
- return eval(text)
-
-
-def load_keyboard(keyboardName, dataTree, keyboard, keyboardHandler):
- for (row, column), pieData in dataTree.iteritems():
- showAllSlices = pieData["showAllSlices"]
- keyboard.get_pie(row, column).showAllSlices = showAllSlices
- for direction, directionName in enumerate(gtkpie.PieSlice.SLICE_DIRECTION_NAMES):
- if directionName not in pieData:
- continue
- sliceName = "%s-(%d, %d)-%s" % (keyboardName, row, column, directionName)
-
- sliceData = pieData[directionName]
- sliceAction = sliceData["action"]
- sliceType = sliceData["type"]
- if sliceType == "text":
- text = sliceData["text"]
- # font = sliceData["font"] # @TODO
- slice = gtkpie.TextLabelPieSlice(text, handler=keyboardHandler)
- elif sliceType == "image":
- path = sliceData["path"]
- slice = gtkpie.ImageLabelPieSlice(path, handler=keyboardHandler)
-
- slice.name = sliceName
- keyboard.add_slice(row, column, slice, direction)
- keyboardHandler.map_slice_action(slice, sliceAction)
-
-
-class KeyboardHandler(object):
-
- def __init__(self, keyhandler):
- self.__keyhandler = keyhandler
- self.__commandHandlers = {}
- self.__modifiers = {}
- self.__sliceActions = {}
-
- self.register_modifier("Shift")
- self.register_modifier("Super")
- self.register_modifier("Control")
- self.register_modifier("Alt")
-
- def register_command_handler(self, command, handler):
- #@todo Make this handle multiple handlers or switch to gobject events
- self.__commandHandlers["[%s]" % command] = handler
-
- def unregister_command_handler(self, command):
- #@todo Make this handle multiple handlers or switch to gobject events
- del self.__commandHandlers["[%s]" % command]
-
- def register_modifier(self, modifierName):
- mod = KeyboardModifier(modifierName)
- self.register_command_handler(modifierName, mod.on_toggle_lock)
- self.__modifiers["<%s>" % modifierName] = mod
-
- def unregister_modifier(self, modifierName):
- self.unregister_command_handler(modifierName)
- del self.__modifiers["<%s>" % modifierName]
-
- def map_slice_action(self, slice, action):
- self.__sliceActions[slice.name] = action
-
- def __call__(self, pie, slice, direction):
- try:
- action = self.__sliceActions[slice.name]
- except KeyError:
- return
-
- activeModifiers = [
- mod.name
- for mod in self.__modifiers.itervalues()
- if mod.isActive
- ]
-
- needResetOnce = False
- if action.startswith("[") and action.endswith("]"):
- commandName = action[1:-1]
- if action in self.__commandHandlers:
- self.__commandHandlers[action](commandName, activeModifiers)
- needResetOnce = True
- else:
- warnings.warn("Unknown command: [%s]" % commandName)
- elif action.startswith("<") and action.endswith(">"):
- modName = action[1:-1]
- for mod in self.__modifiers.itervalues():
- if mod.name == modName:
- mod.on_toggle_once()
- break
- else:
- warnings.warn("Unknown modifier: <%s>" % modName)
- else:
- self.__keyhandler(action, activeModifiers)
- needResetOnce = True
-
- if needResetOnce:
- for mod in self.__modifiers.itervalues():
- mod.reset_once()
#!/usr/bin/env python
import math
+import logging
from PyQt4 import QtGui
from PyQt4 import QtCore
+try:
+ from util import misc as misc_utils
+except ImportError:
+ class misc_utils(object):
+
+ @staticmethod
+ def log_exception(logger):
+
+ def wrapper(func):
+ return func
+ return wrapper
+
+
+_moduleLogger = logging.getLogger(__name__)
+
_TWOPI = 2 * math.pi
def sizeHint(self):
return self._artist.pieSize()
+ @misc_utils.log_exception(_moduleLogger)
def showEvent(self, showEvent):
mask = self._artist.show(self.palette())
self.setMask(mask)
QtGui.QWidget.showEvent(self, showEvent)
+ @misc_utils.log_exception(_moduleLogger)
def hideEvent(self, hideEvent):
self._artist.hide()
self._selectionIndex = PieFiling.SELECTION_NONE
QtGui.QWidget.hideEvent(self, hideEvent)
+ @misc_utils.log_exception(_moduleLogger)
def paintEvent(self, paintEvent):
canvas = self._artist.paint(self._selectionIndex)
def sizeHint(self):
return self._buttonArtist.pieSize()
+ @misc_utils.log_exception(_moduleLogger)
def mousePressEvent(self, mouseEvent):
self._popup_child(mouseEvent.globalPos())
lastSelection = self._selectionIndex
self.highlighted.emit(self._selectionIndex)
self._display.selectAt(self._selectionIndex)
+ @misc_utils.log_exception(_moduleLogger)
def mouseMoveEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self.highlighted.emit(self._selectionIndex)
self._display.selectAt(self._selectionIndex)
+ @misc_utils.log_exception(_moduleLogger)
def mouseReleaseEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self._activate_at(self._selectionIndex)
self._hide_child()
+ @misc_utils.log_exception(_moduleLogger)
def keyPressEvent(self, keyEvent):
if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
self._popup_child(QtGui.QCursor.pos())
else:
QtGui.QWidget.keyPressEvent(self, keyEvent)
+ @misc_utils.log_exception(_moduleLogger)
def showEvent(self, showEvent):
self._buttonArtist.show(self.palette())
self._cachedCenterPosition = self.rect().center()
QtGui.QWidget.showEvent(self, showEvent)
+ @misc_utils.log_exception(_moduleLogger)
def hideEvent(self, hideEvent):
self._display.hide()
self._select_at(PieFiling.SELECTION_NONE)
QtGui.QWidget.hideEvent(self, hideEvent)
+ @misc_utils.log_exception(_moduleLogger)
def paintEvent(self, paintEvent):
if self._poppedUp:
canvas = self._buttonArtist.paint(PieFiling.SELECTION_CENTER)
QtGui.QWidget.paintEvent(self, paintEvent)
+ def __iter__(self):
+ return iter(self._filing)
+
+ def __len__(self):
+ return len(self._filing)
+
def _popup_child(self, position):
self._poppedUp = True
self.aboutToShow.emit()
def sizeHint(self):
return self._artist.pieSize()
+ @misc_utils.log_exception(_moduleLogger)
def mousePressEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self.highlighted.emit(self._selectionIndex)
self.update()
+ @misc_utils.log_exception(_moduleLogger)
def mouseMoveEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self.highlighted.emit(self._selectionIndex)
self.update()
+ @misc_utils.log_exception(_moduleLogger)
def mouseReleaseEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self._activate_at(self._selectionIndex)
self.update()
+ @misc_utils.log_exception(_moduleLogger)
def keyPressEvent(self, keyEvent):
if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
if self._selectionIndex != len(self._filing) - 1:
else:
QtGui.QWidget.keyPressEvent(self, keyEvent)
+ @misc_utils.log_exception(_moduleLogger)
def showEvent(self, showEvent):
self.aboutToShow.emit()
self._cachedCenterPosition = self.rect().center()
QtGui.QWidget.showEvent(self, showEvent)
+ @misc_utils.log_exception(_moduleLogger)
def hideEvent(self, hideEvent):
self._artist.hide()
self._selectionIndex = PieFiling.SELECTION_NONE
QtGui.QWidget.hideEvent(self, hideEvent)
+ @misc_utils.log_exception(_moduleLogger)
def paintEvent(self, paintEvent):
canvas = self._artist.paint(self._selectionIndex)
QtGui.QWidget.paintEvent(self, paintEvent)
+ def __iter__(self):
+ return iter(self._filing)
+
+ def __len__(self):
+ return len(self._filing)
+
def _select_at(self, index):
self._selectionIndex = index
self.hide()
+def init_pies():
+ PieFiling.NULL_CENTER.setEnabled(False)
+
+
def _print(msg):
print msg
if __name__ == "__main__":
app = QtGui.QApplication([])
- PieFiling.NULL_CENTER.setEnabled(False)
+ init_pies()
if False:
pie = QPieMenu()
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import division
+
+import os
+import warnings
+
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+
+import qtpie
+
+
+class PieKeyboard(object):
+
+ SLICE_CENTER = -1
+ SLICE_NORTH = 0
+ SLICE_NORTH_WEST = 1
+ SLICE_WEST = 2
+ SLICE_SOUTH_WEST = 3
+ SLICE_SOUTH = 4
+ SLICE_SOUTH_EAST = 5
+ SLICE_EAST = 6
+ SLICE_NORTH_EAST = 7
+
+ MAX_ANGULAR_SLICES = 8
+
+ SLICE_DIRECTIONS = [
+ SLICE_CENTER,
+ SLICE_NORTH,
+ SLICE_NORTH_WEST,
+ SLICE_WEST,
+ SLICE_SOUTH_WEST,
+ SLICE_SOUTH,
+ SLICE_SOUTH_EAST,
+ SLICE_EAST,
+ SLICE_NORTH_EAST,
+ ]
+
+ SLICE_DIRECTION_NAMES = [
+ "CENTER",
+ "NORTH",
+ "NORTH_WEST",
+ "WEST",
+ "SOUTH_WEST",
+ "SOUTH",
+ "SOUTH_EAST",
+ "EAST",
+ "NORTH_EAST",
+ ]
+
+ def __init__(self, rows, columns):
+ self._layout = QtGui.QGridLayout()
+
+ self.__cells = {}
+
+ @property
+ def toplevel(self):
+ return self._layout
+
+ def add_pie(self, row, column, pieButton):
+ assert len(pieButton) == 8
+ self._layout.addWidget(pieButton, row, column)
+ self.__cells[(row, column)] = pieButton
+
+ def get_pie(self, row, column):
+ return self.__cells[(row, column)]
+
+
+class KeyboardModifier(object):
+
+ def __init__(self, name):
+ self.name = name
+ self.lock = False
+ self.once = False
+
+ @property
+ def isActive(self):
+ return self.lock or self.once
+
+ def on_toggle_lock(self, *args, **kwds):
+ self.lock = not self.lock
+
+ def on_toggle_once(self, *args, **kwds):
+ self.once = not self.once
+
+ def reset_once(self):
+ self.once = False
+
+
+def parse_keyboard_data(text):
+ return eval(text)
+
+
+def _enumerate_pie_slices(pieData, iconPaths):
+ for direction, directionName in zip(
+ PieKeyboard.SLICE_DIRECTIONS, PieKeyboard.SLICE_DIRECTION_NAMES
+ ):
+ if directionName in pieData:
+ sliceData = pieData[directionName]
+
+ action = QtGui.QAction(None)
+ try:
+ action.setText(sliceData["text"])
+ except KeyError:
+ pass
+ try:
+ relativeIconPath = sliceData["path"]
+ except KeyError:
+ pass
+ else:
+ for iconPath in iconPaths:
+ absIconPath = os.path.join(iconPath, relativeIconPath)
+ if os.path.exists(absIconPath):
+ action.setIcon(QtGui.QIcon(absIconPath))
+ break
+ pieItem = qtpie.QActionPieItem(action)
+ else:
+ pieItem = qtpie.PieFiling.NULL_CENTER
+ action = ""
+ yield direction, pieItem, action
+
+
+def load_keyboard(keyboardName, dataTree, keyboard, keyboardHandler, iconPaths):
+ for (row, column), pieData in dataTree.iteritems():
+ pieItems = list(_enumerate_pie_slices(pieData, iconPaths))
+ assert pieItems[0][0] == PieKeyboard.SLICE_CENTER, pieItems[0]
+ _, center, centerAction = pieItems.pop(0)
+
+ pieButton = qtpie.QPieButton(center)
+ pieButton.set_center(center)
+ for direction, pieItem, action in pieItems:
+ pieButton.insertItem(pieItem)
+ keyboard.add_pie(row, column, pieButton)
+
+
+class KeyboardHandler(object):
+
+ def __init__(self, keyhandler):
+ self.__keyhandler = keyhandler
+ self.__commandHandlers = {}
+ self.__modifiers = {}
+ self.__sliceActions = {}
+
+ self.register_modifier("Shift")
+ self.register_modifier("Super")
+ self.register_modifier("Control")
+ self.register_modifier("Alt")
+
+ def register_command_handler(self, command, handler):
+ #@todo Make this handle multiple handlers or switch to gobject events
+ self.__commandHandlers["[%s]" % command] = handler
+
+ def unregister_command_handler(self, command):
+ #@todo Make this handle multiple handlers or switch to gobject events
+ del self.__commandHandlers["[%s]" % command]
+
+ def register_modifier(self, modifierName):
+ mod = KeyboardModifier(modifierName)
+ self.register_command_handler(modifierName, mod.on_toggle_lock)
+ self.__modifiers["<%s>" % modifierName] = mod
+
+ def unregister_modifier(self, modifierName):
+ self.unregister_command_handler(modifierName)
+ del self.__modifiers["<%s>" % modifierName]
+
+ def map_slice_action(self, slice, action):
+ self.__sliceActions[slice.name] = action
+
+ def __call__(self, pie, slice, direction):
+ try:
+ action = self.__sliceActions[slice.name]
+ except KeyError:
+ return
+
+ activeModifiers = [
+ mod.name
+ for mod in self.__modifiers.itervalues()
+ if mod.isActive
+ ]
+
+ needResetOnce = False
+ if action.startswith("[") and action.endswith("]"):
+ commandName = action[1:-1]
+ if action in self.__commandHandlers:
+ self.__commandHandlers[action](commandName, activeModifiers)
+ needResetOnce = True
+ else:
+ warnings.warn("Unknown command: [%s]" % commandName)
+ elif action.startswith("<") and action.endswith(">"):
+ modName = action[1:-1]
+ for mod in self.__modifiers.itervalues():
+ if mod.name == modName:
+ mod.on_toggle_once()
+ break
+ else:
+ warnings.warn("Unknown modifier: <%s>" % modName)
+ else:
+ self.__keyhandler(action, activeModifiers)
+ needResetOnce = True
+
+ if needResetOnce:
+ for mod in self.__modifiers.itervalues():
+ mod.reset_once()
import inspect
import ConfigParser
-from libraries import gtkpieboard
+from libraries import qtpieboard
from util import io
import operation
self.factory = factory
self.__handler = None
- def setup(self, calcStack, style, boardHandler):
+ def setup(self, calcStack, boardHandler):
self.__handler = boardHandler
with open(self.factory.mapFile, "r") as mapfile:
- boardTree = gtkpieboard.parse_keyboard_data("\n".join(mapfile.readlines()))
+ boardTree = qtpieboard.parse_keyboard_data("\n".join(mapfile.readlines()))
rows, columns = boardTree["dimensions"]
keyboardName = boardTree["name"]
keyTree = boardTree["keys"]
- keyboard = gtkpieboard.PieKeyboard(style, rows, columns)
- gtkpieboard.load_keyboard(keyboardName, keyTree, keyboard, self.__handler)
+ keyboard = qtpieboard.PieKeyboard(rows, columns)
+ qtpieboard.load_keyboard(keyboardName, keyTree, keyboard, self.__handler, self.factory.iconPaths)
for commandName, operator in self.factory.commands.iteritems():
handler = CommandStackHandler(calcStack, commandName, operator)
self.name = pluginName
self.mapFile = keyboardMapFile
self.commands = {}
+ self.iconPaths = [os.path.join(os.path.dirname(keyboardMapFile), "images")]
def register_operation(self, commandName, operator):
self.commands[commandName] = operator
def push(self, node):
simpleNode = node.simplify()
- row = RowData(node, simpleNode)
+ row = RowData(self._prettyRenderer, node, simpleNode)
self._historyStore.push(row)
- selection = self._historyView.get_selection()
- selectionPath = (len(self._historyStore)-1, )
- selection.select_path(selectionPath)
- self._historyView.scroll_to_cell(selectionPath)
+ # @todo Scroll to bottom
def pop(self):
if len(self._historyStore) == 0: