#!/usr/bin/python
"""
-@todo Add preference file
- @li enable/disable plugins
- @li plugin search path
- @li Number format
- @li Current tab
-@todo Expand operations to support
- @li mathml then to cairo?
- @li cairo directly?
-@todo Expanded copy/paste (Unusure how far to go)
- @li Copy formula, value, serialized, mathml, latex?
- @li Paste serialized, value?
-
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
import gc
import os
import string
+import logging
import warnings
import gtk
import gtk.glade
-try:
- import hildon
-except ImportError:
- hildon = None
+import hildonize
from libraries import gtkpie
from libraries import gtkpieboard
import plugin_utils
import history
import gtkhistory
+import gtk_toolbox
+import constants
+
+_moduleLogger = logging.getLogger("ejpi_glade")
PLUGIN_SEARCH_PATHS = [
os.path.join(os.path.dirname(__file__), "plugins/"),
]
+PROFILE_STARTUP = False
+
class ValueEntry(object):
value = property(get_value, set_value, clear)
-class ErrorDisplay(history.ErrorReporting):
-
- 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(self, message):
- if 0 < len(self.__messages):
- self.__messages.append(message)
- else:
- self.__show_message(message)
-
- def pop_message(self):
- if 0 < len(self.__messages):
- self.__show_message(self.__messages[0])
- del self.__messages[0]
- else:
- self.__hide_message()
-
- 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 Calculator(object):
- __pretty_app_name__ = "e**(j pi) + 1 = 0"
- __app_name__ = "ejpi"
- __version__ = "0.9.3"
- __app_magic__ = 0xdeadbeef
-
_glade_files = [
'/usr/lib/ejpi/ejpi.glade',
os.path.join(os.path.dirname(__file__), "ejpi.glade"),
os.path.join(os.path.dirname(__file__), "plugins/"),
]
- _user_data = os.path.expanduser("~/.%s/" % __app_name__)
+ _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)
self.__keyboardPlugins = plugin_utils.KeyboardPluginManager()
self.__keyboardPlugins.add_path(*self._plugin_search_paths)
- self.__activeKeyboards = {}
+ self.__activeKeyboards = []
for path in self._glade_files:
if os.path.isfile(path):
else:
self.display_error_message("Cannot find ejpi.glade")
gtk.main_quit()
+ return
try:
os.makedirs(self._user_data)
except OSError, e:
raise
self._clipboard = gtk.clipboard_get()
- self.__window = self._widgetTree.get_widget("mainWindow")
+ self._window = self._widgetTree.get_widget("mainWindow")
- global hildon
self._app = None
self._isFullScreen = False
- if hildon is not None:
- self._app = hildon.Program()
- self.__window = hildon.Window()
- self._widgetTree.get_widget("mainLayout").reparent(self.__window)
- self._app.add_window(self.__window)
- hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('scrollingHistory'), True)
-
- gtkMenu = self._widgetTree.get_widget("mainMenubar")
- menu = gtk.Menu()
- for child in gtkMenu.get_children():
- child.reparent(menu)
- self.__window.set_menu(menu)
- gtkMenu.destroy()
-
- self.__window.connect("key-press-event", self._on_key_press)
- self.__window.connect("window-state-event", self._on_window_state_change)
- else:
- pass # warnings.warn("No Hildon", UserWarning, 2)
+ 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"),
+ []
+ )
- self.__errorDisplay = ErrorDisplay(self._widgetTree)
+ 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.__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("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("functionLayout").pack_start(self.__builtinKeyboard)
- self._widgetTree.get_widget("functionLayout").reorder_child(self.__builtinKeyboard, 0)
+ 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"))
-
- 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)
-
- if self.__window:
- if hildon is None:
- self.__window.set_title("%s" % self.__pretty_app_name__)
- self.__window.connect("destroy", self._on_close)
- self.__window.show_all()
+ 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_title(self._window, "%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(Calculator.__app_name__, Calculator.__version__, False)
- device = osso.DeviceState(self._osso)
- device.set_device_state_callback(self._on_device_state_change, 0)
+ 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:
- pass # warnings.warn("No OSSO", UserWarning, 2)
+ _moduleLogger.warning("No OSSO support")
def display_error_message(self, msg):
error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
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)
- keyboardTabs = self._widgetTree.get_widget("pluginKeyboards")
- keyboardTabs.append_page(pluginKeyboard, gtk.Label(pluginName))
- keyboardPageNum = keyboardTabs.page_num(pluginKeyboard)
- assert keyboardPageNum not in self.__activeKeyboards
- self.__activeKeyboards[keyboardPageNum] = {
+ self.__activeKeyboards.append({
"pluginName": pluginName,
"plugin": plugin,
"pluginKeyboard": pluginKeyboard,
- }
+ })
+
+ @gtk_toolbox.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 = []
line = " ".join(data for data in lineData)
f.write("%s\n" % line)
+ @gtk_toolbox.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
if save_unsaved_data or shutdown:
self.__save_history()
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_window_state_change(self, widget, event, *args):
- """
- @note Hildon specific
- """
if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
self._isFullScreen = True
else:
self._isFullScreen = False
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_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()
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_copy(self, *args):
+ equationNode = self.__history.history.peek()
+ result = str(equationNode.evaluate())
+ self._clipboard.set_text(result)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_copy_equation(self, *args):
+ equationNode = self.__history.history.peek()
+ equation = str(equationNode)
+ self._clipboard.set_text(equation)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_paste(self, *args):
contents = self._clipboard.wait_for_text()
self.__userEntry.append(contents)
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_key_press(self, widget, event, *args):
- """
- @note Hildon specific
- """
- if event.keyval == gtk.keysyms.F6:
+ 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()
+ self._window.unfullscreen()
else:
- self.__window.fullscreen()
-
+ self._window.fullscreen()
+ 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))
+
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_push(self, *args):
self.__history.push_entry()
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_unpush(self, *args):
self.__historyStore.unpush()
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_entry_direct(self, keys, modifiers):
if "shift" in modifiers:
keys = keys.upper()
self.__userEntry.append(keys)
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_entry_backspace(self, *args):
self.__userEntry.pop()
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_entry_clear(self, *args):
self.__userEntry.clear()
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_clear_all(self, *args):
self.__history.clear()
+ @gtk_toolbox.log_exception(_moduleLogger)
def _on_about_activate(self, *args):
dlg = gtk.AboutDialog()
- dlg.set_name(self.__pretty_app_name__)
- dlg.set_version(self.__version__)
+ dlg.set_name(constants.__pretty_app_name__)
+ dlg.set_version(constants.__version__)
dlg.set_copyright("Copyright 2008 - LGPL")
- dlg.set_comments("")
- dlg.set_website("")
- dlg.set_authors([""])
+ 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()
gtk.gdk.threads_init()
gtkpie.IMAGES.add_path(os.path.join(os.path.dirname(__file__), "libraries/images"), )
- if hildon is not None:
- gtk.set_application_name(Calculator.__pretty_app_name__)
+ if hildonize.IS_HILDON_SUPPORTED:
+ gtk.set_application_name(constants.__pretty_app_name__)
handle = Calculator()
- gtk.main()
+ if not PROFILE_STARTUP:
+ gtk.main()
class DummyOptions(object):
if __name__ == "__main__":
+ logging.basicConfig(level=logging.DEBUG)
if len(sys.argv) > 1:
try:
import optparse