diff options
Diffstat (limited to 'src/sugar/tutorius/creator.py')
-rw-r--r-- | src/sugar/tutorius/creator.py | 436 |
1 files changed, 0 insertions, 436 deletions
diff --git a/src/sugar/tutorius/creator.py b/src/sugar/tutorius/creator.py deleted file mode 100644 index d6826dc..0000000 --- a/src/sugar/tutorius/creator.py +++ /dev/null @@ -1,436 +0,0 @@ -""" -This package contains UI classes related to tutorial authoring. -This includes visual display of tools to edit and create tutorials from within -the activity itself. -""" -# Copyright (C) 2009, Tutorius.org -# Copyright (C) 2009, Simon Poirier <simpoir@gmail.com> -# -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 1 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import gtk.gdk -import gobject -from gettext import gettext as T - -from sugar.graphics.toolbutton import ToolButton - -from sugar.tutorius import overlayer, gtkutils, actions, bundler, properties, addon -from sugar.tutorius import filters -from sugar.tutorius.services import ObjectStore -from sugar.tutorius.linear_creator import LinearCreator -from sugar.tutorius.tutorial import Tutorial - -class Creator(object): - """ - Class acting as a bridge between the creator, serialization and core - classes. This contains most of the UI part of the editor. - """ - def __init__(self, activity, tutorial=None): - """ - Instanciate a tutorial creator for the activity. - - @param activity to bind the creator to - @param tutorial an existing tutorial to edit, or None to create one - """ - self._activity = activity - if not tutorial: - self._tutorial = LinearCreator() - else: - self._tutorial = tutorial - - self._action_panel = None - self._current_filter = None - self._intro_mask = None - self._intro_handle = None - self._state_bubble = overlayer.TextBubble(self._tutorial.state_name) - allocation = self._activity.get_allocation() - self._width = allocation.width - self._height = allocation.height - self._selected_widget = None - self._eventmenu = None - - self._hlmask = overlayer.Rectangle(None, (1.0, 0.0, 0.0, 0.5)) - self._activity._overlayer.put(self._hlmask, 0, 0) - - self._activity._overlayer.put(self._state_bubble, - self._width/2-self._state_bubble.allocation.width/2, 0) - - dlg_width = 300 - dlg_height = 70 - sw = gtk.gdk.screen_width() - sh = gtk.gdk.screen_height() - self._tooldialog = gtk.Window() - self._tooldialog.set_title("Tutorius tools") - self._tooldialog.set_transient_for(self._activity) - self._tooldialog.set_decorated(True) - self._tooldialog.set_resizable(False) - self._tooldialog.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY) - self._tooldialog.set_destroy_with_parent(True) - self._tooldialog.set_deletable(False) - self._tooldialog.set_size_request(dlg_width, dlg_height) - - toolbar = gtk.Toolbar() - for tool in addon.list_addons(): - meta = addon.get_addon_meta(tool) - toolitem = ToolButton(meta['icon']) - toolitem.set_tooltip(meta['display_name']) - toolitem.connect("clicked", self._add_action_cb, tool) - toolbar.insert(toolitem, -1) - toolitem = ToolButton("go-next") - toolitem.connect("clicked", self._add_step_cb) - toolitem.set_tooltip("Add Step") - toolbar.insert(toolitem, -1) - toolitem = ToolButton("stop") - toolitem.connect("clicked", self._cleanup_cb) - toolitem.set_tooltip("End Tutorial") - toolbar.insert(toolitem, -1) - self._tooldialog.add(toolbar) - self._tooldialog.show_all() - # simpoir: I suspect the realized widget is a tiny bit larger than - # it should be, thus the -10. - self._tooldialog.move(sw-10-dlg_width, sh-dlg_height) - - self._propedit = EditToolBox(self._activity) - - def _evfilt_cb(self, menuitem, event_name, *args): - """ - This will get called once the user has selected a menu item from the - event filter popup menu. This should add the correct event filter - to the FSM and increment states. - """ - self.introspecting = False - eventfilter = addon.create('GtkWidgetEventFilter', - next_state=None, - object_id=self._selected_widget, - event_name=event_name) - # undo actions so they don't persist through step editing - for action in self._tutorial.current_actions: - action.exit_editmode() - self._tutorial.event(eventfilter) - self._state_bubble.label = self._tutorial.state_name - self._hlmask.covered = None - self._propedit.action = None - self._activity.queue_draw() - - def _intro_cb(self, widget, evt): - """ - Callback for capture of widget events, when in introspect mode. - """ - if evt.type == gtk.gdk.BUTTON_PRESS: - # widget has focus, let's hilight it - win = gtk.gdk.display_get_default().get_window_at_pointer() - click_wdg = win[0].get_user_data() - if not click_wdg.is_ancestor(self._activity._overlayer): - # as popups are not (yet) supported, it would break - # badly if we were to play with a widget not in the - # hierarchy. - return - for hole in self._intro_mask.pass_thru: - self._intro_mask.mask(hole) - self._intro_mask.unmask(click_wdg) - self._selected_widget = gtkutils.raddr_lookup(click_wdg) - - if self._eventmenu: - self._eventmenu.destroy() - self._eventmenu = gtk.Menu() - menuitem = gtk.MenuItem(label=type(click_wdg).__name__) - menuitem.set_sensitive(False) - self._eventmenu.append(menuitem) - self._eventmenu.append(gtk.MenuItem()) - - for item in gobject.signal_list_names(click_wdg): - menuitem = gtk.MenuItem(label=item) - menuitem.connect("activate", self._evfilt_cb, item) - self._eventmenu.append(menuitem) - self._eventmenu.show_all() - self._eventmenu.popup(None, None, None, evt.button, evt.time) - self._activity.queue_draw() - - def set_intropecting(self, value): - """ - Set whether creator is in UI introspection mode. Setting this will - connect necessary handlers. - @param value True to setup introspection handlers. - """ - if bool(value) ^ bool(self._intro_mask): - if value: - self._intro_mask = overlayer.Mask(catch_events=True) - self._intro_handle = self._intro_mask.connect_after( - "button-press-event", self._intro_cb) - self._activity._overlayer.put(self._intro_mask, 0, 0) - else: - self._intro_mask.catch_events = False - self._intro_mask.disconnect(self._intro_handle) - self._intro_handle = None - self._activity._overlayer.remove(self._intro_mask) - self._intro_mask = None - - def get_introspecting(self): - """ - Whether creator is in UI introspection (catch all event) mode. - @return True if introspection handlers are connected, or False if not. - """ - return bool(self._intro_mask) - - introspecting = property(fset=set_intropecting, fget=get_introspecting) - - def _add_action_cb(self, widget, actiontype): - """Callback for the action creation toolbar tool""" - action = addon.create(actiontype) - if isinstance(action, actions.Action): - action.enter_editmode() - self._tutorial.action(action) - # FIXME: replace following with event catching - action._drag._eventbox.connect_after( - "button-release-event", self._action_refresh_cb, action) - else: - addonname = type(action).__name__ - meta = addon.get_addon_meta(addonname) - had_introspect = False - for propname in meta['mandatory_props']: - prop = getattr(type(action), propname) - if isinstance(prop, properties.TUAMProperty): - had_introspect = True - self.introspecting = True - elif isinstance(prop, properties.TStringProperty): - dlg = TextInputDialog(title="Mandatory property", - field=propname) - setattr(action, propname, dlg.pop()) - else: - raise NotImplementedError() - - # FIXME: hack to reuse previous introspection code - if not had_introspect: - self._tutorial.event(action) - - - def _action_refresh_cb(self, widget, evt, action): - """ - Callback for refreshing properties values and notifying the - property dialog of the new values. - """ - action.exit_editmode() - action.enter_editmode() - self._activity.queue_draw() - # TODO: replace following with event catching - action._drag._eventbox.connect_after( - "button-release-event", self._action_refresh_cb, action) - self._propedit.action = action - - def _add_step_cb(self, widget): - """Callback for the "add step" tool""" - self.introspecting = True - - def _cleanup_cb(self, *args): - """ - Quit editing and cleanup interface artifacts. - """ - self.introspecting = False - eventfilter = filters.EventFilter(None) - # undo actions so they don't persist through step editing - for action in self._tutorial.current_actions: - action.exit_editmode() - self._tutorial.event(eventfilter) - - dlg = TextInputDialog(text=T("Enter a tutorial title."), - field=T("Title")) - tutorialName = "" - while not tutorialName: tutorialName = dlg.pop() - dlg.destroy() - - # prepare tutorial for serialization - tuto = Tutorial(tutorialName, self._tutorial.fsm) - bundle = bundler.TutorialBundler() - bundle.write_metadata_file(tuto) - bundle.write_fsm(self._tutorial.fsm) - - # remove UI remains - self._hlmask.covered = None - self._activity._overlayer.remove(self._hlmask) - self._activity._overlayer.remove(self._state_bubble) - self._hlmask.destroy() - self._hlmask = None - self._tooldialog.destroy() - self._propedit.destroy() - self._activity.queue_draw() - del self._activity._creator - - def launch(*args, **kwargs): - """ - Launch and attach a creator to the currently running activity. - """ - activity = ObjectStore().activity - if not hasattr(activity, "_creator"): - activity._creator = Creator(activity) - launch = staticmethod(launch) - -class EditToolBox(gtk.Window): - """Helper toolbox class for managing action properties""" - def __init__(self, parent, action=None): - """ - Create the property edition toolbox and display it. - - @param parent the parent window of this toolbox, usually an activity - @param action the action to introspect/edit - """ - gtk.Window.__init__(self) - self._action = None - self.__parent = parent # private avoid gtk clash - - self.set_title("Action Properties") - self.set_transient_for(parent) - self.set_decorated(True) - self.set_resizable(False) - self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY) - self.set_destroy_with_parent(True) - self.set_deletable(False) - self.set_size_request(200, 400) - - self._vbox = gtk.VBox() - self.add(self._vbox) - propwin = gtk.ScrolledWindow() - propwin.props.hscrollbar_policy = gtk.POLICY_AUTOMATIC - propwin.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC - self._vbox.pack_start(propwin) - self._propbox = gtk.VBox(spacing=10) - propwin.add(self._propbox) - - self.action = action - - sw = gtk.gdk.screen_width() - sh = gtk.gdk.screen_height() - - self.show_all() - self.move(sw-10-200, (sh-400)/2) - - def refresh(self): - """Refresh property values from the selected action.""" - if self._action is None: - return - props = self._action._props.keys() - for propnum in xrange(len(props)): - row = self._propbox.get_children()[propnum] - propname = props[propnum] - prop = getattr(type(self._action), propname) - propval = getattr(self._action, propname) - if isinstance(prop, properties.TStringProperty): - propwdg = row.get_children()[1] - propwdg.get_buffer().set_text(propval) - elif isinstance(prop, properties.TIntProperty): - propwdg = row.get_children()[1] - propwdg.set_value(propval) - elif isinstance(prop, properties.TArrayProperty): - propwdg = row.get_children()[1] - for i in xrange(len(propval)): - entry = propwdg.get_children()[i] - entry.set_text(str(propval[i])) - else: - propwdg = row.get_children()[1] - propwdg.set_text(str(propval)) - - def set_action(self, action): - """Setter for the action property.""" - if self._action is action: - self.refresh() - return - parent = self._propbox.get_parent() - parent.remove(self._propbox) - self._propbox = gtk.VBox(spacing=10) - parent.add(self._propbox) - - self._action = action - if action is None: - return - for propname in action._props.keys(): - row = gtk.HBox() - row.pack_start(gtk.Label(T(propname)), False, False, 10) - prop = getattr(type(action), propname) - propval = getattr(action, propname) - if isinstance(prop, properties.TStringProperty): - propwdg = gtk.TextView() - propwdg.get_buffer().set_text(propval) - propwdg.connect_after("focus-out-event", \ - self._str_prop_changed, action, propname) - elif isinstance(prop, properties.TIntProperty): - adjustment = gtk.Adjustment(value=propval, - lower=prop.lower_limit.limit, - upper=prop.upper_limit.limit, - step_incr=1) - propwdg = gtk.SpinButton(adjustment=adjustment) - propwdg.connect_after("focus-out-event", \ - self._int_prop_changed, action, prop) - elif isinstance(prop, properties.TArrayProperty): - propwdg = gtk.HBox() - for i in xrange(len(propval)): - entry = gtk.Entry() - propwdg.pack_start(entry) - entry.connect_after("focus-out-event", \ - self._list_prop_changed, action, propname, i) - else: - propwdg = gtk.Entry() - propwdg.set_text(str(propval)) - row.pack_end(propwdg) - self._propbox.pack_start(row, expand=False) - self._vbox.show_all() - self.refresh() - - def get_action(self): - """Getter for the action property""" - return self._action - action = property(fset=set_action, fget=get_action, doc=\ - "Action to be edited through introspection.") - - def _list_prop_changed(self, widget, evt, action, propname, idx): - try: - getattr(action, propname)[idx] = int(widget.get_text()) - except ValueError: - widget.set_text(str(getattr(action, propname)[idx])) - self.__parent._creator._action_refresh_cb(None, None, action) - def _str_prop_changed(self, widget, evt, action, propname): - buf = widget.get_buffer() - setattr(action, propname, buf.get_text(buf.get_start_iter(), buf.get_end_iter())) - self.__parent._creator._action_refresh_cb(None, None, action) - def _int_prop_changed(self, widget, evt, action, prop): - setattr(action, propname, widget.get_value_as_int()) - self.__parent._creator._action_refresh_cb(None, None, action) - -class TextInputDialog(gtk.MessageDialog): - def __init__(self, text, field): - gtk.MessageDialog.__init__(self, None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - gtk.MESSAGE_QUESTION, - gtk.BUTTONS_OK, - None) - self.set_markup(text) - self.entry = gtk.Entry() - self.entry.connect("activate", self._dialog_done_cb, gtk.RESPONSE_OK) - hbox = gtk.HBox() - lbl = gtk.Label(field) - hbox.pack_start(lbl, False) - hbox.pack_end(self.entry) - self.vbox.pack_end(hbox, True, True) - self.show_all() - - def pop(self): - self.run() - self.hide() - text = self.entry.get_text() - return text - - def _dialog_done_cb(self, entry, response): - self.response(response) - -# vim:set ts=4 sts=4 sw=4 et: |