diff options
author | Daniel Francis <francis@sugarlabs.org> | 2012-07-01 17:01:31 (GMT) |
---|---|---|
committer | Rafael Ortiz <rafael@activitycentral.com> | 2012-07-03 05:29:31 (GMT) |
commit | 6b9ff449e17cc7ac7327138234a72b1066202c18 (patch) | |
tree | e7746f077b65a16c2af61eb42e129ccef3d63ed6 | |
parent | 4e2df4e10931eac04393c3cc8abe99953b17dcc6 (diff) |
Migrating from the early toolbarbox-toolkit modules to Sugar Toolkit
This little patch is the second step in the Gtk3 migration.
Here I remove some few unnecesary files always when it was possible, and use Sugar Toolkit.
The purpose is to prevent a headache at the time of porting to Gtk3 the activity, and of course remove some useless kilobytes and not having written the same code twice.
I also modified the .gitignore file, I didn't type 'git add .gitignore' previous than my commit so I didn't expect it would appear in my patch, I think git has its own life :) , but anyway, is better having a correct .gitignore .
Signed-off-by: Daniel Francis <francis@sugarlabs.org>
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | activity.py | 86 | ||||
-rw-r--r-- | brain.py | 4 | ||||
-rw-r--r-- | combobox.py (renamed from toolkit/combobox.py) | 0 | ||||
-rw-r--r-- | shared_activity.py | 115 | ||||
-rw-r--r-- | toolitem.py (renamed from toolkit/toolitem.py) | 2 | ||||
-rw-r--r-- | toolkit/__init__.py | 16 | ||||
-rw-r--r-- | toolkit/activity.py | 331 | ||||
-rw-r--r-- | toolkit/activity_widgets.py | 397 | ||||
-rw-r--r-- | toolkit/chooser.py | 69 | ||||
-rw-r--r-- | toolkit/internals/__init__.py | 16 | ||||
-rw-r--r-- | toolkit/internals/palettewindow.py | 976 | ||||
-rw-r--r-- | toolkit/json.py | 35 | ||||
-rw-r--r-- | toolkit/pixbuf.py | 116 | ||||
-rw-r--r-- | toolkit/radiopalette.py | 109 | ||||
-rw-r--r-- | toolkit/scrolledbox.py | 191 | ||||
-rw-r--r-- | toolkit/tarball.py | 125 | ||||
-rw-r--r-- | toolkit/temposlider.py | 211 | ||||
-rw-r--r-- | toolkit/toolbarbox.py | 333 |
19 files changed, 201 insertions, 2937 deletions
@@ -1,2 +1,4 @@ -dist -.0sugar +*.pyc +*.pyo +*.mo +*~ diff --git a/activity.py b/activity.py index a044423..a3f01ac 100644 --- a/activity.py +++ b/activity.py @@ -31,16 +31,17 @@ import pango import cjson from gettext import gettext as _ -from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.toolbarbox import ToolbarButton from sugar.graphics.toggletoolbutton import ToggleToolButton from sugar.graphics.radiotoolbutton import RadioToolButton -from toolkit.toolitem import ToolWidget -from toolkit.combobox import ComboBox -from toolkit.toolbarbox import ToolbarBox -from toolkit.activity import SharedActivity -from toolkit.activity_widgets import * +from combobox import ComboBox +from sugar.graphics.toolbarbox import ToolbarBox +from shared_activity import SharedActivity +from sugar.activity.widgets import ActivityToolbarButton +from sugar.activity.widgets import StopButton +from toolitem import ToolWidget import eye import glasses import mouth @@ -59,13 +60,39 @@ MODE_TYPE = 1 MODE_BOT = 2 MODE_CHAT = 3 +_NEW_INSTANCE = 0 +_NEW_INSTANCE = 1 +_PRE_INSTANCE = 2 +_POST_INSTANCE = 3 + + +class CursorFactory: + + __shared_state = {"cursors": {}} + + def __init__(self): + self.__dict__ = self.__shared_state + + def get_cursor(self, cur_type): + if not cur_type in self.cursors: + cur = gtk.gdk.Cursor(cur_type) + self.cursors[cur_type] = cur + return self.cursors[cur_type] + class SpeakActivity(SharedActivity): def __init__(self, handle): self.notebook = gtk.Notebook() + self.notebook.connect_after('map', self.__map_canvasactivity_cb) SharedActivity.__init__(self, self.notebook, SERVICE, handle) + self._cursor = None + self.set_cursor(gtk.gdk.LEFT_PTR) + self.__resume_filename = None + self.__postponed_share = [] + self.__on_save_instance = [] + self.__state = _NEW_INSTANCE self._mode = MODE_TYPE self.numeyesadj = None @@ -176,6 +203,53 @@ class SpeakActivity(SharedActivity): toolbox.show_all() self.toolbar_box = toolbox + def set_cursor(self, cursor): + if not isinstance(cursor, gtk.gdk.Cursor): + cursor = CursorFactory().get_cursor(cursor) + + if self._cursor != cursor: + self._cursor = cursor + self.window.set_cursor(self._cursor) + + def __map_canvasactivity_cb(self, widget): + logging.debug('Activity.__map_canvasactivity_cb state=%s' % \ + self.__state) + + if self.__state == _NEW_INSTANCE: + self.__instance() + elif self.__state == _NEW_INSTANCE: + self.__state = _PRE_INSTANCE + elif self.__state == _PRE_INSTANCE: + self.__instance() + + return False + + def __instance(self): + logging.debug('Activity.__instance') + + if self.__resume_filename: + self.resume_instance(self.__resume_filename) + else: + self.new_instance() + + for i in self.__postponed_share: + self.share_instance(*i) + self.__postponed_share = [] + + self.__state = _POST_INSTANCE + + def read_file(self, file_path): + self.__resume_filename = file_path + if self.__state == _NEW_INSTANCE: + self.__state = _PRE_INSTANCE + elif self.__state == _PRE_INSTANCE: + self.__instance() + + def write_file(self, file_path): + for cb, args in self.__on_save_instance: + cb(*args) + self.save_instance(file_path) + def new_instance(self): self.voices.connect('changed', self.__changed_voices_cb) self.pitchadj.connect("value_changed", self.pitch_adjusted_cb, @@ -26,7 +26,7 @@ from gettext import gettext as _ import logging logger = logging.getLogger('speak') -from toolkit.combobox import ComboBox +from combobox import ComboBox import aiml import voice @@ -86,7 +86,7 @@ def load(activity, voice, sorry=None): if voice == _kernel_voice: return False - old_cursor = activity.get_cursor() + old_cursor = activity._cursor activity.set_cursor(gtk.gdk.WATCH) def load_brain(): diff --git a/toolkit/combobox.py b/combobox.py index d021106..d021106 100644 --- a/toolkit/combobox.py +++ b/combobox.py diff --git a/shared_activity.py b/shared_activity.py new file mode 100644 index 0000000..a537958 --- /dev/null +++ b/shared_activity.py @@ -0,0 +1,115 @@ +# Copyright (C) 2009, Aleksey Lim +# +# 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 2 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 + +"""Extend sugar-toolkit activity class""" + +import logging +import telepathy + +from sugar.activity import activity +from sugar.presence.sugartubeconn import SugarTubeConnection + + +class SharedActivity(activity.Activity): + """Basic activity class with sharing features""" + + def __init__(self, canvas, service, handle): + """ + Initialise the Activity. + + canvas -- gtk.Widget + root widget for activity content + + service -- string + dbus service for activity + + handle -- sugar.activity.activityhandle.ActivityHandle + instance providing the activity id and access to the + presence service which *may* provide sharing for this + application + + """ + activity.Activity.__init__(self, handle) + self.set_canvas(canvas) + self.service = service + + self.connect('shared', self._shared_cb) + + # Owner.props.key + if self._shared_activity: + # We are joining the activity + self.connect('joined', self._joined_cb) + if self.get_shared(): + # We've already joined + self._joined_cb() + + def _shared_cb(self, activity): + logging.debug('My activity was shared') + self.__initiator = True + self._sharing_setup() + + logging.debug('This is my activity: making a tube...') + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + self.service, {}) + + def _joined_cb(self, activity): + if not self._shared_activity: + return + + logging.debug('Joined an existing shared activity') + + self.__initiator = False + self._sharing_setup() + + logging.debug('This is not my activity: waiting for a tube...') + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + def _sharing_setup(self): + if self._shared_activity is None: + logging.error('Failed to share or join activity') + return + self._conn = self._shared_activity.telepathy_conn + self._tubes_chan = self._shared_activity.telepathy_tubes_chan + self._text_chan = self._shared_activity.telepathy_text_chan + + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( + 'NewTube', self._new_tube_cb) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + logging.error('ListTubes() failed: %s', e) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + logging.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and + service == self.service): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES] \ + .AcceptDBusTube(id) + + tube_conn = SugarTubeConnection(self._conn, + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, + group_iface=self._text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + + self._share(tube_conn, self.__initiator) diff --git a/toolkit/toolitem.py b/toolitem.py index e490c22..1f4ee49 100644 --- a/toolkit/toolitem.py +++ b/toolitem.py @@ -22,8 +22,6 @@ import gobject from sugar.graphics import style -from toolkit.combobox import ComboBox - class ToolWidget(gtk.ToolItem): diff --git a/toolkit/__init__.py b/toolkit/__init__.py deleted file mode 100644 index 17a92ac..0000000 --- a/toolkit/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. diff --git a/toolkit/activity.py b/toolkit/activity.py deleted file mode 100644 index 1512610..0000000 --- a/toolkit/activity.py +++ /dev/null @@ -1,331 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# 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 2 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 - -"""Extend sugar-toolkit activity class""" - -import gtk -import logging -import telepathy -import gobject - -from sugar.activity import activity -from sugar.presence.sugartubeconn import SugarTubeConnection -from sugar.graphics.alert import ConfirmationAlert, NotifyAlert - - -_NEW_INSTANCE = 0 -_NEW_INSTANCE = 1 -_PRE_INSTANCE = 2 -_POST_INSTANCE = 3 - - -class CursorFactory: - - __shared_state = {"cursors": {}} - - def __init__(self): - self.__dict__ = self.__shared_state - - def get_cursor(self, cur_type): - if not self.cursors.has_key(cur_type): - cur = gtk.gdk.Cursor(cur_type) - self.cursors[cur_type] = cur - return self.cursors[cur_type] - - -class Activity(activity.Activity): - - """Basic activity class""" - - def new_instance(self): - """ - New instance was created. - - Will be invoked after __init__() instead of resume_instance(). - Subclass should implement this method to catch creation stage. - """ - pass - - def resume_instance(self, filepath): - """ - Instance was resumed. - - Will be invoked after __init__() instead of new_instance(). - Subclass should implement this method to catch resuming stage. - - """ - pass - - def save_instance(self, filepath): - """ - Save activity instance. - - Subclass should implement this method to save activity data. - """ - raise NotImplementedError - - def on_save_instance(self, cb, *args): - """ Register callback which will be invoked before save_instance """ - self.__on_save_instance.append((cb, args)) - - def share_instance(self, connection, is_initiator): - """ - Activity was shared/joined. - - connection -- SugarTubeConnection object - wich represents telepathy connection - - is_initiator -- boolean - if True activity was shared and - (current activity is an initiator of sharing) - otherwise activity was joined(to existed sharing session) - - Will be invoked after __init__() and {new,resume}_instance(). - Subclass should implement this method to catch sharing stage. - """ - pass - - def set_toolbar_box(self, toolbox): - if hasattr(activity.Activity, 'set_toolbar_box'): - activity.Activity.set_toolbar_box(self, toolbox) - else: - self.set_toolbox(toolbox) - - def get_toolbar_box(self): - if hasattr(activity.Activity, 'get_toolbar_box'): - return activity.Activity.get_toolbar_box(self) - else: - return self.get_toolbox() - - toolbar_box = property(get_toolbar_box, set_toolbar_box) - - def get_shared_activity(self): - if hasattr(activity.Activity, 'get_shared_activity'): - return activity.Activity.get_shared_activity(self) - else: - return self._shared_activity - - def notify_alert(self, title, msg): - """Raise standard notify alert""" - alert = NotifyAlert(title=title, msg=msg) - - def response(alert, response_id, self): - self.remove_alert(alert) - - alert.connect('response', response, self) - alert.show_all() - self.add_alert(alert) - - def confirmation_alert(self, title, msg, cb, *cb_args): - """Raise standard confirmation alert""" - alert = ConfirmationAlert(title=title, msg=msg) - - def response(alert, response_id, self, cb, *cb_args): - self.remove_alert(alert) - if response_id is gtk.RESPONSE_OK: - cb(*cb_args) - - alert.connect('response', response, self, cb, *cb_args) - alert.show_all() - self.add_alert(alert) - - def get_cursor(self): - return self._cursor - - def set_cursor(self, cursor): - if not isinstance(cursor, gtk.gdk.Cursor): - cursor = CursorFactory().get_cursor(cursor) - - if self._cursor != cursor: - self._cursor = cursor - self.window.set_cursor(self._cursor) - - def __init__(self, canvas, handle): - """ - Initialise the Activity. - - canvas -- gtk.Widget - root widget for activity content - - handle -- sugar.activity.activityhandle.ActivityHandle - instance providing the activity id and access to the - - """ - activity.Activity.__init__(self, handle) - - if handle.object_id: - self.__state = _NEW_INSTANCE - else: - self.__state = _NEW_INSTANCE - - self.__resume_filename = None - self.__postponed_share = [] - self.__on_save_instance = [] - - self._cursor = None - self.set_cursor(gtk.gdk.LEFT_PTR) - - # XXX do it after(possible) read_file() invoking - # have to rely on calling read_file() from map_cb in sugar-toolkit - canvas.connect_after('map', self.__map_canvasactivity_cb) - self.set_canvas(canvas) - - def __instance(self): - logging.debug('Activity.__instance') - - if self.__resume_filename: - self.resume_instance(self.__resume_filename) - else: - self.new_instance() - - for i in self.__postponed_share: - self.share_instance(*i) - self.__postponed_share = [] - - self.__state = _POST_INSTANCE - - def read_file(self, filepath): - """Subclass should not override this method""" - logging.debug('Activity.read_file state=%s' % self.__state) - - self.__resume_filename = filepath - - if self.__state == _NEW_INSTANCE: - self.__state = _PRE_INSTANCE - elif self.__state == _PRE_INSTANCE: - self.__instance(); - - def write_file(self, filepath): - """Subclass should not override this method""" - for cb, args in self.__on_save_instance: - cb(*args) - self.save_instance(filepath) - - def __map_canvasactivity_cb(self, widget): - logging.debug('Activity.__map_canvasactivity_cb state=%s' % \ - self.__state) - - if self.__state == _NEW_INSTANCE: - self.__instance() - elif self.__state == _NEW_INSTANCE: - self.__state = _PRE_INSTANCE - elif self.__state == _PRE_INSTANCE: - self.__instance(); - - return False - - def _share(self, tube_conn, initiator): - logging.debug('Activity._share state=%s' % self.__state) - - if self.__state == _NEW_INSTANCE: - self.__postponed_share.append((tube_conn, initiator)) - self.__state = _PRE_INSTANCE - elif self.__state == _PRE_INSTANCE: - self.__postponed_share.append((tube_conn, initiator)) - self.__instance(); - elif self.__state == _POST_INSTANCE: - self.share_instance(tube_conn, initiator) - - -class SharedActivity(Activity): - """Basic activity class with sharing features""" - - def __init__(self, canvas, service, handle): - """ - Initialise the Activity. - - canvas -- gtk.Widget - root widget for activity content - - service -- string - dbus service for activity - - handle -- sugar.activity.activityhandle.ActivityHandle - instance providing the activity id and access to the - presence service which *may* provide sharing for this - application - - """ - Activity.__init__(self, canvas, handle) - self.service = service - - self.connect('shared', self._shared_cb) - - # Owner.props.key - if self._shared_activity: - # We are joining the activity - self.connect('joined', self._joined_cb) - if self.get_shared(): - # We've already joined - self._joined_cb() - - def _shared_cb(self, activity): - logging.debug('My activity was shared') - self.__initiator = True - self._sharing_setup() - - logging.debug('This is my activity: making a tube...') - id = self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( - self.service, {}) - - def _joined_cb(self, activity): - if not self._shared_activity: - return - - logging.debug('Joined an existing shared activity') - - self.__initiator = False - self._sharing_setup() - - logging.debug('This is not my activity: waiting for a tube...') - self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( - reply_handler=self._list_tubes_reply_cb, - error_handler=self._list_tubes_error_cb) - - def _sharing_setup(self): - if self._shared_activity is None: - logging.error('Failed to share or join activity') - return - self._conn = self._shared_activity.telepathy_conn - self._tubes_chan = self._shared_activity.telepathy_tubes_chan - self._text_chan = self._shared_activity.telepathy_text_chan - - self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( - 'NewTube', self._new_tube_cb) - - def _list_tubes_reply_cb(self, tubes): - for tube_info in tubes: - self._new_tube_cb(*tube_info) - - def _list_tubes_error_cb(self, e): - logging.error('ListTubes() failed: %s', e) - - def _new_tube_cb(self, id, initiator, type, service, params, state): - logging.debug('New tube: ID=%d initator=%d type=%d service=%s ' - 'params=%r state=%d', id, initiator, type, service, - params, state) - - if (type == telepathy.TUBE_TYPE_DBUS and - service == self.service): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES] \ - .AcceptDBusTube(id) - - tube_conn = SugarTubeConnection(self._conn, - self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, - group_iface=self._text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - - self._share(tube_conn, self.__initiator) diff --git a/toolkit/activity_widgets.py b/toolkit/activity_widgets.py deleted file mode 100644 index 9195af5..0000000 --- a/toolkit/activity_widgets.py +++ /dev/null @@ -1,397 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim, Simon Schampijer -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -import gtk -import gobject -import gettext - -from sugar import profile -from sugar.graphics.toolbutton import ToolButton -from sugar.graphics.radiotoolbutton import RadioToolButton -from sugar.graphics.toolbox import Toolbox -from sugar.graphics.xocolor import XoColor -from sugar.graphics.icon import Icon -from sugar.bundle.activitybundle import ActivityBundle - -from toolkit.toolbarbox import ToolbarButton -from toolkit.radiopalette import RadioPalette -from toolkit.radiopalette import RadioMenuButton -from sugar.graphics import style - -_ = lambda msg: gettext.dgettext('sugar-toolkit', msg) - - -def _create_activity_icon(metadata): - if metadata.get('icon-color', ''): - color = XoColor(metadata['icon-color']) - else: - color = profile.get_color() - - from sugar.activity.activity import get_bundle_path - bundle = ActivityBundle(get_bundle_path()) - icon = Icon(file=bundle.get_icon(), xo_color=color) - - return icon - - -class ActivityButton(ToolButton): - - def __init__(self, activity, **kwargs): - ToolButton.__init__(self, **kwargs) - - icon = _create_activity_icon(activity.metadata) - self.set_icon_widget(icon) - icon.show() - - self.props.tooltip = activity.metadata['title'] - activity.metadata.connect('updated', self.__jobject_updated_cb) - - def __jobject_updated_cb(self, jobject): - self.props.tooltip = jobject['title'] - - -class ActivityToolbarButton(ToolbarButton): - - def __init__(self, activity, **kwargs): - toolbar = ActivityToolbar(activity, orientation_left=True) - toolbar.stop.hide() - - ToolbarButton.__init__(self, page=toolbar, **kwargs) - - icon = _create_activity_icon(activity.metadata) - self.set_icon_widget(icon) - icon.show() - - -class StopButton(ToolButton): - - def __init__(self, activity, **kwargs): - ToolButton.__init__(self, 'activity-stop', **kwargs) - self.props.tooltip = _('Stop') - self.props.accelerator = '<Ctrl>Q' - self.connect('clicked', self.__stop_button_clicked_cb, activity) - - def __stop_button_clicked_cb(self, button, activity): - activity.close() - - -class UndoButton(ToolButton): - - def __init__(self, **kwargs): - ToolButton.__init__(self, 'edit-undo', **kwargs) - self.props.tooltip = _('Undo') - self.props.accelerator = '<Ctrl>Z' - - -class RedoButton(ToolButton): - - def __init__(self, **kwargs): - ToolButton.__init__(self, 'edit-redo', **kwargs) - self.props.tooltip = _('Redo') - - -class CopyButton(ToolButton): - - def __init__(self, **kwargs): - ToolButton.__init__(self, 'edit-copy', **kwargs) - self.props.tooltip = _('Copy') - - -class PasteButton(ToolButton): - - def __init__(self, **kwargs): - ToolButton.__init__(self, 'edit-paste', **kwargs) - self.props.tooltip = _('Paste') - - -class ShareButton(RadioMenuButton): - - def __init__(self, activity, **kwargs): - palette = RadioPalette() - - self.private = RadioToolButton( - icon_name='zoom-home') - palette.append(self.private, _('Private')) - - self.neighborhood = RadioToolButton( - icon_name='zoom-neighborhood', - group=self.private) - self._neighborhood_handle = self.neighborhood.connect( - 'clicked', self.__neighborhood_clicked_cb, activity) - palette.append(self.neighborhood, _('My Neighborhood')) - - activity.connect('shared', self.__update_share_cb) - activity.connect('joined', self.__update_share_cb) - - RadioMenuButton.__init__(self, **kwargs) - self.props.palette = palette - if activity.props.max_participants == 1: - self.props.sensitive = False - - def __neighborhood_clicked_cb(self, button, activity): - activity.share() - - def __update_share_cb(self, activity): - self.neighborhood.handler_block(self._neighborhood_handle) - try: - if activity.get_shared(): - self.private.props.sensitive = False - self.neighborhood.props.sensitive = False - self.neighborhood.props.active = True - else: - self.private.props.sensitive = True - self.neighborhood.props.sensitive = True - self.private.props.active = True - finally: - self.neighborhood.handler_unblock(self._neighborhood_handle) - - -class TitleEntry(gtk.ToolItem): - - def __init__(self, activity, **kwargs): - gtk.ToolItem.__init__(self) - self.set_expand(False) - self._update_title_sid = None - - self.entry = gtk.Entry(**kwargs) - self.entry.set_size_request(int(gtk.gdk.screen_width() / 3), -1) - self.entry.set_text(activity.metadata['title']) - self.entry.connect('changed', self.__title_changed_cb, activity) - self.entry.show() - self.add(self.entry) - - activity.metadata.connect('updated', self.__jobject_updated_cb) - - def modify_bg(self, state, color): - gtk.ToolItem.modify_bg(self, state, color) - self.entry.modify_bg(state, color) - - def __jobject_updated_cb(self, jobject): - self.entry.set_text(jobject['title']) - - def __title_changed_cb(self, entry, activity): - if not self._update_title_sid: - self._update_title_sid = gobject.timeout_add_seconds( - 1, self.__update_title_cb, activity) - - def __update_title_cb(self, activity): - title = self.entry.get_text() - - activity.metadata['title'] = title - activity.metadata['title_set_by_user'] = '1' - activity.save() - - shared_activity = activity.get_shared_activity() - if shared_activity is not None: - shared_activity.props.name = title - - self._update_title_sid = None - return False - - -class DescriptionItem(gtk.ToolItem): - - def __init__(self, activity, **kwargs): - gtk.ToolItem.__init__(self) - - description_button = ToolButton('edit-description') - description_button.show() - description_button.set_tooltip(_('Description')) - self._palette = description_button.get_palette() - - description_box = gtk.HBox() - sw = gtk.ScrolledWindow() - sw.set_size_request(int(gtk.gdk.screen_width() / 2), - 2 * style.GRID_CELL_SIZE) - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self._text_view = gtk.TextView() - self._text_view.set_left_margin(style.DEFAULT_PADDING) - self._text_view.set_right_margin(style.DEFAULT_PADDING) - text_buffer = gtk.TextBuffer() - if 'description' in activity.metadata: - text_buffer.set_text(activity.metadata['description']) - self._text_view.set_buffer(text_buffer) - self._text_view.connect('focus-out-event', - self.__description_changed_cb, activity) - sw.add(self._text_view) - description_box.pack_start(sw, False, True, 0) - self._palette.set_content(description_box) - description_box.show_all() - - self.add(description_button) - description_button.connect('clicked', - self.__description_button_clicked_cb) - - activity.metadata.connect('updated', self.__jobject_updated_cb) - - def _get_text_from_buffer(self): - buf = self._text_view.get_buffer() - start_iter = buf.get_start_iter() - end_iter = buf.get_end_iter() - return buf.get_text(start_iter, end_iter, False) - - def __jobject_updated_cb(self, jobject): - if self._text_view.has_focus(): - return - if 'description' not in jobject: - return - if self._get_text_from_buffer() == jobject['description']: - return - buf = self._text_view.get_buffer() - buf.set_text(jobject['description']) - - def __description_button_clicked_cb(self, button): - self._palette.popup(immediate=True, state=1) - - def __description_changed_cb(self, widget, event, activity): - description = self._get_text_from_buffer() - if 'description' in activity.metadata and \ - description == activity.metadata['description']: - return - - activity.metadata['description'] = description - activity.save() - return False - - -class ActivityToolbar(gtk.Toolbar): - """The Activity toolbar with the Journal entry title, sharing, - and Stop buttons - - All activities should have this toolbar. It is easiest to add it to your - Activity by using the ActivityToolbox. - """ - - def __init__(self, activity, orientation_left=False): - gtk.Toolbar.__init__(self) - - self._activity = activity - - if activity.metadata: - title_button = TitleEntry(activity) - title_button.show() - self.insert(title_button, -1) - self.title = title_button.entry - - if orientation_left == False: - separator = gtk.SeparatorToolItem() - separator.props.draw = False - separator.set_expand(True) - self.insert(separator, -1) - separator.show() - - if activity.metadata: - description_item = DescriptionItem(activity) - description_item.show() - self.insert(description_item, -1) - - self.share = ShareButton(activity) - self.share.show() - self.insert(self.share, -1) - - self.stop = StopButton(activity) - self.insert(self.stop, -1) - self.stop.show() - - -class EditToolbar(gtk.Toolbar): - """Provides the standard edit toolbar for Activities. - - Members: - undo -- the undo button - redo -- the redo button - copy -- the copy button - paste -- the paste button - separator -- A separator between undo/redo and copy/paste - - This class only provides the 'edit' buttons in a standard layout, - your activity will need to either hide buttons which make no sense for your - Activity, or you need to connect the button events to your own callbacks: - - ## Example from Read.activity: - # Create the edit toolbar: - self._edit_toolbar = EditToolbar(self._view) - # Hide undo and redo, they're not needed - self._edit_toolbar.undo.props.visible = False - self._edit_toolbar.redo.props.visible = False - # Hide the separator too: - self._edit_toolbar.separator.props.visible = False - - # As long as nothing is selected, copy needs to be insensitive: - self._edit_toolbar.copy.set_sensitive(False) - # When the user clicks the button, call _edit_toolbar_copy_cb() - self._edit_toolbar.copy.connect('clicked', self._edit_toolbar_copy_cb) - - # Add the edit toolbar: - toolbox.add_toolbar(_('Edit'), self._edit_toolbar) - # And make it visible: - self._edit_toolbar.show() - """ - - def __init__(self): - gtk.Toolbar.__init__(self) - - self.undo = UndoButton() - self.insert(self.undo, -1) - self.undo.show() - - self.redo = RedoButton() - self.insert(self.redo, -1) - self.redo.show() - - self.separator = gtk.SeparatorToolItem() - self.separator.set_draw(True) - self.insert(self.separator, -1) - self.separator.show() - - self.copy = CopyButton() - self.insert(self.copy, -1) - self.copy.show() - - self.paste = PasteButton() - self.insert(self.paste, -1) - self.paste.show() - - -class ActivityToolbox(Toolbox): - """Creates the Toolbox for the Activity - - By default, the toolbox contains only the ActivityToolbar. After creating - the toolbox, you can add your activity specific toolbars, for example the - EditToolbar. - - To add the ActivityToolbox to your Activity in MyActivity.__init__() do: - - # Create the Toolbar with the ActivityToolbar: - toolbox = activity.ActivityToolbox(self) - ... your code, inserting all other toolbars you need, like EditToolbar - - # Add the toolbox to the activity frame: - self.set_toolbox(toolbox) - # And make it visible: - toolbox.show() - """ - - def __init__(self, activity): - Toolbox.__init__(self) - - self._activity_toolbar = ActivityToolbar(activity) - self.add_toolbar(_('Activity'), self._activity_toolbar) - self._activity_toolbar.show() - - def get_activity_toolbar(self): - return self._activity_toolbar diff --git a/toolkit/chooser.py b/toolkit/chooser.py deleted file mode 100644 index e957fd7..0000000 --- a/toolkit/chooser.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# 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 2 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 - -"""Object chooser method""" - -import gtk -import logging - -from sugar import mime -from sugar.graphics.objectchooser import ObjectChooser - -TEXT = hasattr(mime, 'GENERIC_TYPE_TEXT') and mime.GENERIC_TYPE_TEXT or None -IMAGE = hasattr(mime, 'GENERIC_TYPE_IMAGE') and mime.GENERIC_TYPE_IMAGE or None -AUDIO = hasattr(mime, 'GENERIC_TYPE_AUDIO') and mime.GENERIC_TYPE_AUDIO or None -VIDEO = hasattr(mime, 'GENERIC_TYPE_VIDEO') and mime.GENERIC_TYPE_VIDEO or None -LINK = hasattr(mime, 'GENERIC_TYPE_LINK') and mime.GENERIC_TYPE_LINK or None - - -def pick(cb=None, default=None, parent=None, what=None): - """ - Opens object chooser. - - Method returns: - - * cb(jobject), if object was choosen and cb is not None - * jobject, if object was choosen and cb is None - * default, otherwise - - NOTE: 'what' makes sense only for sugar >= 0.84 - """ - what = what and {'what_filter': what} or {} - chooser = ObjectChooser(parent=parent, **what) - - jobject = None - out = None - - try: - if chooser.run() == gtk.RESPONSE_ACCEPT: - jobject = chooser.get_selected_object() - logging.debug('ObjectChooser: %r' % jobject) - - if jobject and jobject.file_path: - if cb: - out = cb(jobject) - else: - out = jobject - finally: - if jobject and id(jobject) != id(out): - jobject.destroy() - chooser.destroy() - del chooser - - if out: - return out - else: - return default diff --git a/toolkit/internals/__init__.py b/toolkit/internals/__init__.py deleted file mode 100644 index 17a92ac..0000000 --- a/toolkit/internals/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. diff --git a/toolkit/internals/palettewindow.py b/toolkit/internals/palettewindow.py deleted file mode 100644 index d8c4326..0000000 --- a/toolkit/internals/palettewindow.py +++ /dev/null @@ -1,976 +0,0 @@ -# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com> -# Copyright (C) 2008, One Laptop Per Child -# Copyright (C) 2009, Tomeu Vizoso -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -""" -STABLE. -""" - -import logging - -import gtk -import gobject -import hippo - -from sugar.graphics import palettegroup -from sugar.graphics import animator -from sugar.graphics import style - - -def _calculate_gap(a, b): - """Helper function to find the gap position and size of widget a""" - # Test for each side if the palette and invoker are - # adjacent to each other. - gap = True - - if a.y + a.height == b.y: - gap_side = gtk.POS_BOTTOM - elif a.x + a.width == b.x: - gap_side = gtk.POS_RIGHT - elif a.x == b.x + b.width: - gap_side = gtk.POS_LEFT - elif a.y == b.y + b.height: - gap_side = gtk.POS_TOP - else: - gap = False - - if gap: - if gap_side == gtk.POS_BOTTOM or gap_side == gtk.POS_TOP: - gap_start = min(a.width, max(0, b.x - a.x)) - gap_size = max(0, min(a.width, - (b.x + b.width) - a.x) - gap_start) - elif gap_side == gtk.POS_RIGHT or gap_side == gtk.POS_LEFT: - gap_start = min(a.height, max(0, b.y - a.y)) - gap_size = max(0, min(a.height, - (b.y + b.height) - a.y) - gap_start) - - if gap and gap_size > 0: - return (gap_side, gap_start, gap_size) - else: - return False - - -class MouseSpeedDetector(gobject.GObject): - - __gsignals__ = { - 'motion-slow': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'motion-fast': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - } - - _MOTION_SLOW = 1 - _MOTION_FAST = 2 - - def __init__(self, parent, delay, thresh): - """Create MouseSpeedDetector object, - delay in msec - threshold in pixels (per tick of 'delay' msec)""" - - gobject.GObject.__init__(self) - - self._threshold = thresh - self._parent = parent - self._delay = delay - self._state = None - self._timeout_hid = None - self._mouse_pos = None - - def start(self): - self.stop() - - self._mouse_pos = self._get_mouse_position() - self._timeout_hid = gobject.timeout_add(self._delay, self._timer_cb) - - def stop(self): - if self._timeout_hid is not None: - gobject.source_remove(self._timeout_hid) - self._state = None - - def _get_mouse_position(self): - display = gtk.gdk.display_get_default() - screen_, x, y, mask_ = display.get_pointer() - return (x, y) - - def _detect_motion(self): - oldx, oldy = self._mouse_pos - (x, y) = self._get_mouse_position() - self._mouse_pos = (x, y) - - dist2 = (oldx - x)**2 + (oldy - y)**2 - if dist2 > self._threshold**2: - return True - else: - return False - - def _timer_cb(self): - motion = self._detect_motion() - if motion and self._state != self._MOTION_FAST: - self.emit('motion-fast') - self._state = self._MOTION_FAST - elif not motion and self._state != self._MOTION_SLOW: - self.emit('motion-slow') - self._state = self._MOTION_SLOW - - return True - - -class PaletteWindow(gtk.Window): - - __gsignals__ = { - 'popup': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'popdown': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'activate': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - } - - def __init__(self, **kwargs): - self._group_id = None - self._invoker = None - self._invoker_hids = [] - self._cursor_x = 0 - self._cursor_y = 0 - self._alignment = None - self._up = False - self._old_alloc = None - - self._popup_anim = animator.Animator(.5, 10) - self._popup_anim.add(_PopupAnimation(self)) - - self._popdown_anim = animator.Animator(0.6, 10) - self._popdown_anim.add(_PopdownAnimation(self)) - - gobject.GObject.__init__(self, **kwargs) - - self.set_decorated(False) - self.set_resizable(False) - # Just assume xthickness and ythickness are the same - self.set_border_width(self.get_style().xthickness) - - accel_group = gtk.AccelGroup() - self.set_data('sugar-accel-group', accel_group) - self.add_accel_group(accel_group) - - self.set_group_id("default") - - self.connect('show', self.__show_cb) - self.connect('hide', self.__hide_cb) - self.connect('realize', self.__realize_cb) - self.connect('destroy', self.__destroy_cb) - self.connect('enter-notify-event', self.__enter_notify_event_cb) - self.connect('leave-notify-event', self.__leave_notify_event_cb) - - self._mouse_detector = MouseSpeedDetector(self, 200, 5) - self._mouse_detector.connect('motion-slow', self._mouse_slow_cb) - - def __destroy_cb(self, palette): - self.set_group_id(None) - - def set_invoker(self, invoker): - for hid in self._invoker_hids[:]: - self._invoker.disconnect(hid) - self._invoker_hids.remove(hid) - - self._invoker = invoker - if invoker is not None: - self._invoker_hids.append(self._invoker.connect( - 'mouse-enter', self._invoker_mouse_enter_cb)) - self._invoker_hids.append(self._invoker.connect( - 'mouse-leave', self._invoker_mouse_leave_cb)) - self._invoker_hids.append(self._invoker.connect( - 'right-click', self._invoker_right_click_cb)) - - def get_invoker(self): - return self._invoker - - invoker = gobject.property(type=object, - getter=get_invoker, - setter=set_invoker) - - def __realize_cb(self, widget): - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) - - def _mouse_slow_cb(self, widget): - self._mouse_detector.stop() - self._palette_do_popup() - - def _palette_do_popup(self): - immediate = False - - if self.is_up(): - self._popdown_anim.stop() - return - - if self._group_id: - group = palettegroup.get_group(self._group_id) - if group and group.is_up(): - immediate = True - group.popdown() - - self.popup(immediate=immediate) - - def is_up(self): - return self._up - - def set_group_id(self, group_id): - if self._group_id: - group = palettegroup.get_group(self._group_id) - group.remove(self) - if group_id: - self._group_id = group_id - group = palettegroup.get_group(group_id) - group.add(self) - - def get_group_id(self): - return self._group_id - - group_id = gobject.property(type=str, - getter=get_group_id, - setter=set_group_id) - - def do_size_request(self, requisition): - gtk.Window.do_size_request(self, requisition) - requisition.width = max(requisition.width, style.GRID_CELL_SIZE * 2) - - def do_size_allocate(self, allocation): - gtk.Window.do_size_allocate(self, allocation) - - if self._old_alloc is None or \ - self._old_alloc.x != allocation.x or \ - self._old_alloc.y != allocation.y or \ - self._old_alloc.width != allocation.width or \ - self._old_alloc.height != allocation.height: - self.queue_draw() - - # We need to store old allocation because when size_allocate - # is called widget.allocation is already updated. - # gtk.Window resizing is different from normal containers: - # the X window is resized, widget.allocation is updated from - # the configure request handler and finally size_allocate is called. - self._old_alloc = allocation - - def do_expose_event(self, event): - # We want to draw a border with a beautiful gap - if self._invoker is not None and self._invoker.has_rectangle_gap(): - invoker = self._invoker.get_rect() - palette = self.get_rect() - - gap = _calculate_gap(palette, invoker) - else: - gap = False - - allocation = self.get_allocation() - wstyle = self.get_style() - - if gap: - wstyle.paint_box_gap(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_IN, event.area, self, "palette", - 0, 0, allocation.width, allocation.height, - gap[0], gap[1], gap[2]) - else: - wstyle.paint_box(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_IN, event.area, self, "palette", - 0, 0, allocation.width, allocation.height) - - # Fall trough to the container expose handler. - # (Leaving out the window expose handler which redraws everything) - gtk.Bin.do_expose_event(self, event) - - def update_position(self): - invoker = self._invoker - if invoker is None or self._alignment is None: - logging.error('Cannot update the palette position.') - return - - rect = self.size_request() - position = invoker.get_position_for_alignment(self._alignment, rect) - if position is None: - position = invoker.get_position(rect) - - self.move(position.x, position.y) - - def get_full_size_request(self): - return self.size_request() - - def popup(self, immediate=False): - if self._invoker is not None: - full_size_request = self.get_full_size_request() - self._alignment = self._invoker.get_alignment(full_size_request) - - self.update_position() - self.set_transient_for(self._invoker.get_toplevel()) - - self._popdown_anim.stop() - - if not immediate: - self._popup_anim.start() - else: - self._popup_anim.stop() - self.show() - # we have to invoke update_position() twice - # since WM could ignore first move() request - self.update_position() - - def popdown(self, immediate=False): - logging.debug('PaletteWindow.popdown immediate %r', immediate) - - self._popup_anim.stop() - self._mouse_detector.stop() - - if not immediate: - self._popdown_anim.start() - else: - self._popdown_anim.stop() - self.size_request() - self.hide() - - def on_invoker_enter(self): - self._popdown_anim.stop() - self._mouse_detector.start() - - def on_invoker_leave(self): - self._mouse_detector.stop() - self.popdown() - - def on_enter(self, event): - self._popdown_anim.stop() - - def on_leave(self, event): - self.popdown() - - def _invoker_mouse_enter_cb(self, invoker): - self.on_invoker_enter() - - def _invoker_mouse_leave_cb(self, invoker): - self.on_invoker_leave() - - def _invoker_right_click_cb(self, invoker): - self.popup(immediate=True) - - def __enter_notify_event_cb(self, widget, event): - if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ - event.mode == gtk.gdk.CROSSING_NORMAL: - self.on_enter(event) - - def __leave_notify_event_cb(self, widget, event): - if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ - event.mode == gtk.gdk.CROSSING_NORMAL: - self.on_leave(event) - - def __show_cb(self, widget): - if self._invoker is not None: - self._invoker.notify_popup() - - self._up = True - self.emit('popup') - - def __hide_cb(self, widget): - logging.debug('__hide_cb') - - if self._invoker: - self._invoker.notify_popdown() - - self._up = False - self.emit('popdown') - - def get_rect(self): - win_x, win_y = self.window.get_origin() - rectangle = self.get_allocation() - - x = win_x + rectangle.x - y = win_y + rectangle.y - width, height = self.size_request() - - return gtk.gdk.Rectangle(x, y, width, height) - - def get_palette_state(self): - return self._palette_state - - def _set_palette_state(self, state): - self._palette_state = state - - def set_palette_state(self, state): - self._set_palette_state(state) - - palette_state = property(get_palette_state) - - -class _PopupAnimation(animator.Animation): - - def __init__(self, palette): - animator.Animation.__init__(self, 0.0, 1.0) - self._palette = palette - - def next_frame(self, current): - if current == 1.0: - self._palette.popup(immediate=True) - - -class _PopdownAnimation(animator.Animation): - - def __init__(self, palette): - animator.Animation.__init__(self, 0.0, 1.0) - self._palette = palette - - def next_frame(self, current): - if current == 1.0: - self._palette.popdown(immediate=True) - - -class Invoker(gobject.GObject): - - __gsignals__ = { - 'mouse-enter': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'mouse-leave': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'right-click': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'focus-out': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - } - - ANCHORED = 0 - AT_CURSOR = 1 - - BOTTOM = [(0.0, 0.0, 0.0, 1.0), (-1.0, 0.0, 1.0, 1.0)] - RIGHT = [(0.0, 0.0, 1.0, 0.0), (0.0, -1.0, 1.0, 1.0)] - TOP = [(0.0, -1.0, 0.0, 0.0), (-1.0, -1.0, 1.0, 0.0)] - LEFT = [(-1.0, 0.0, 0.0, 0.0), (-1.0, -1.0, 0.0, 1.0)] - - def __init__(self): - gobject.GObject.__init__(self) - - self.parent = None - - self._screen_area = gtk.gdk.Rectangle(0, 0, gtk.gdk.screen_width(), - gtk.gdk.screen_height()) - self._position_hint = self.ANCHORED - self._cursor_x = -1 - self._cursor_y = -1 - self._palette = None - - def attach(self, parent): - self.parent = parent - - def detach(self): - self.parent = None - if self._palette is not None: - self._palette.destroy() - self._palette = None - - def _get_position_for_alignment(self, alignment, palette_dim): - palette_halign = alignment[0] - palette_valign = alignment[1] - invoker_halign = alignment[2] - invoker_valign = alignment[3] - - if self._cursor_x == -1 or self._cursor_y == -1: - display = gtk.gdk.display_get_default() - screen_, x, y, mask_ = display.get_pointer() - self._cursor_x = x - self._cursor_y = y - - if self._position_hint is self.ANCHORED: - rect = self.get_rect() - else: - dist = style.PALETTE_CURSOR_DISTANCE - rect = gtk.gdk.Rectangle(self._cursor_x - dist, - self._cursor_y - dist, - dist * 2, dist * 2) - - palette_width, palette_height = palette_dim - - x = rect.x + rect.width * invoker_halign + \ - palette_width * palette_halign - - y = rect.y + rect.height * invoker_valign + \ - palette_height * palette_valign - - return gtk.gdk.Rectangle(int(x), int(y), - palette_width, palette_height) - - def _in_screen(self, rect): - return rect.x >= self._screen_area.x and \ - rect.y >= self._screen_area.y and \ - rect.x + rect.width <= self._screen_area.width and \ - rect.y + rect.height <= self._screen_area.height - - def _get_area_in_screen(self, rect): - """Return area of rectangle visible in the screen""" - - x1 = max(rect.x, self._screen_area.x) - y1 = max(rect.y, self._screen_area.y) - x2 = min(rect.x + rect.width, - self._screen_area.x + self._screen_area.width) - y2 = min(rect.y + rect.height, - self._screen_area.y + self._screen_area.height) - - return (x2 - x1) * (y2 - y1) - - def _get_alignments(self): - if self._position_hint is self.AT_CURSOR: - return [(0.0, 0.0, 1.0, 1.0), - (0.0, -1.0, 1.0, 0.0), - (-1.0, -1.0, 0.0, 0.0), - (-1.0, 0.0, 0.0, 1.0)] - else: - return self.BOTTOM + self.RIGHT + self.TOP + self.LEFT - - def get_position_for_alignment(self, alignment, palette_dim): - rect = self._get_position_for_alignment(alignment, palette_dim) - if self._in_screen(rect): - return rect - else: - return None - - def get_position(self, palette_dim): - alignment = self.get_alignment(palette_dim) - rect = self._get_position_for_alignment(alignment, palette_dim) - - # In case our efforts to find an optimum place inside the screen - # failed, just make sure the palette fits inside the screen if at all - # possible. - rect.x = max(0, rect.x) - rect.y = max(0, rect.y) - - rect.x = min(rect.x, self._screen_area.width - rect.width) - rect.y = min(rect.y, self._screen_area.height - rect.height) - - return rect - - def get_alignment(self, palette_dim): - best_alignment = None - best_area = -1 - for alignment in self._get_alignments(): - pos = self._get_position_for_alignment(alignment, palette_dim) - if self._in_screen(pos): - return alignment - - area = self._get_area_in_screen(pos) - if area > best_area: - best_alignment = alignment - best_area = area - - # Palette horiz/vert alignment - ph = best_alignment[0] - pv = best_alignment[1] - - # Invoker horiz/vert alignment - ih = best_alignment[2] - iv = best_alignment[3] - - rect = self.get_rect() - screen_area = self._screen_area - - if best_alignment in self.LEFT or best_alignment in self.RIGHT: - dtop = rect.y - screen_area.y - dbottom = screen_area.y + screen_area.height - rect.y - rect.width - - iv = 0 - - # Set palette_valign to align to screen on the top - if dtop > dbottom: - pv = -float(dtop) / palette_dim[1] - - # Set palette_valign to align to screen on the bottom - else: - pv = -float(palette_dim[1] - dbottom - rect.height) \ - / palette_dim[1] - - elif best_alignment in self.TOP or best_alignment in self.BOTTOM: - dleft = rect.x - screen_area.x - dright = screen_area.x + screen_area.width - rect.x - rect.width - - ih = 0 - - # Set palette_halign to align to screen on left - if dleft > dright: - ph = -float(dleft) / palette_dim[0] - - # Set palette_halign to align to screen on right - else: - ph = -float(palette_dim[0] - dright - rect.width) \ - / palette_dim[0] - - return (ph, pv, ih, iv) - - def has_rectangle_gap(self): - return False - - def draw_rectangle(self, event, palette): - pass - - def notify_popup(self): - pass - - def notify_popdown(self): - self._cursor_x = -1 - self._cursor_y = -1 - - def _ensure_palette_exists(self): - if self.parent and self.palette is None: - palette = self.parent.create_palette() - if palette is not None: - self.palette = palette - - def notify_mouse_enter(self): - self._ensure_palette_exists() - self.emit('mouse-enter') - - def notify_mouse_leave(self): - self.emit('mouse-leave') - - def notify_right_click(self): - self._ensure_palette_exists() - self.emit('right-click') - - def get_palette(self): - return self._palette - - def set_palette(self, palette): - if self._palette is not None: - self._palette.popdown(immediate=True) - - if self._palette: - self._palette.props.invoker = None - - self._palette = palette - - if self._palette: - self._palette.props.invoker = self - - palette = gobject.property( - type=object, setter=set_palette, getter=get_palette) - - -class WidgetInvoker(Invoker): - - def __init__(self, parent=None, widget=None): - Invoker.__init__(self) - - self._widget = None - self._enter_hid = None - self._leave_hid = None - self._release_hid = None - - if parent or widget: - self.attach_widget(parent, widget) - - def attach_widget(self, parent, widget=None): - if widget: - self._widget = widget - else: - self._widget = parent - - self.notify('widget') - - self._enter_hid = self._widget.connect('enter-notify-event', - self.__enter_notify_event_cb) - self._leave_hid = self._widget.connect('leave-notify-event', - self.__leave_notify_event_cb) - self._release_hid = self._widget.connect('button-release-event', - self.__button_release_event_cb) - - self.attach(parent) - - def detach(self): - Invoker.detach(self) - self._widget.disconnect(self._enter_hid) - self._widget.disconnect(self._leave_hid) - self._widget.disconnect(self._release_hid) - - def get_rect(self): - allocation = self._widget.get_allocation() - if self._widget.window is not None: - x, y = self._widget.window.get_origin() - else: - logging.warning( - "Trying to position palette with invoker that's not realized.") - x = 0 - y = 0 - - if self._widget.flags() & gtk.NO_WINDOW: - x += allocation.x - y += allocation.y - - width = allocation.width - height = allocation.height - - return gtk.gdk.Rectangle(x, y, width, height) - - def has_rectangle_gap(self): - return True - - def draw_rectangle(self, event, palette): - if self._widget.flags() & gtk.NO_WINDOW: - x, y = self._widget.allocation.x, self._widget.allocation.y - else: - x = y = 0 - - wstyle = self._widget.get_style() - gap = _calculate_gap(self.get_rect(), palette.get_rect()) - - if gap: - wstyle.paint_box_gap(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_IN, event.area, self._widget, - "palette-invoker", x, y, - self._widget.allocation.width, - self._widget.allocation.height, - gap[0], gap[1], gap[2]) - else: - wstyle.paint_box(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_IN, event.area, self._widget, - "palette-invoker", x, y, - self._widget.allocation.width, - self._widget.allocation.height) - - def __enter_notify_event_cb(self, widget, event): - self.notify_mouse_enter() - - def __leave_notify_event_cb(self, widget, event): - self.notify_mouse_leave() - - def __button_release_event_cb(self, widget, event): - if event.button == 3: - self.notify_right_click() - return True - else: - return False - - def get_toplevel(self): - return self._widget.get_toplevel() - - def notify_popup(self): - Invoker.notify_popup(self) - self._widget.queue_draw() - - def notify_popdown(self): - Invoker.notify_popdown(self) - self._widget.queue_draw() - - def _get_widget(self): - return self._widget - widget = gobject.property(type=object, getter=_get_widget, setter=None) - - -class CanvasInvoker(Invoker): - - def __init__(self, parent=None): - Invoker.__init__(self) - - self._position_hint = self.AT_CURSOR - self._motion_hid = None - self._release_hid = None - self._item = None - - if parent: - self.attach(parent) - - def attach(self, parent): - Invoker.attach(self, parent) - - self._item = parent - self._motion_hid = self._item.connect('motion-notify-event', - self.__motion_notify_event_cb) - self._release_hid = self._item.connect('button-release-event', - self.__button_release_event_cb) - - def detach(self): - Invoker.detach(self) - self._item.disconnect(self._motion_hid) - self._item.disconnect(self._release_hid) - - def get_default_position(self): - return self.AT_CURSOR - - def get_rect(self): - context = self._item.get_context() - if context: - x, y = context.translate_to_screen(self._item) - width, height = self._item.get_allocation() - return gtk.gdk.Rectangle(x, y, width, height) - else: - return gtk.gdk.Rectangle() - - def __motion_notify_event_cb(self, button, event): - if event.detail == hippo.MOTION_DETAIL_ENTER: - self.notify_mouse_enter() - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - self.notify_mouse_leave() - - return False - - def __button_release_event_cb(self, button, event): - if event.button == 3: - self.notify_right_click() - return True - else: - return False - - def get_toplevel(self): - return hippo.get_canvas_for_item(self._item).get_toplevel() - - -class ToolInvoker(WidgetInvoker): - - def __init__(self, parent=None): - WidgetInvoker.__init__(self) - - if parent: - self.attach_tool(parent) - - def attach_tool(self, widget): - self.attach_widget(widget, widget.child) - - def _get_alignments(self): - parent = self._widget.get_parent() - if parent is None: - return WidgetInvoker._get_alignments() - - if parent.get_orientation() is gtk.ORIENTATION_HORIZONTAL: - return self.BOTTOM + self.TOP - else: - return self.LEFT + self.RIGHT - - -class CellRendererInvoker(Invoker): - - def __init__(self): - Invoker.__init__(self) - - self._position_hint = self.AT_CURSOR - self._tree_view = None - self._cell_renderer = None - self._motion_hid = None - self._leave_hid = None - self._release_hid = None - self.path = None - - def attach_cell_renderer(self, tree_view, cell_renderer): - self._tree_view = tree_view - self._cell_renderer = cell_renderer - - self._motion_hid = tree_view.connect('motion-notify-event', - self.__motion_notify_event_cb) - self._leave_hid = tree_view.connect('leave-notify-event', - self.__leave_notify_event_cb) - self._release_hid = tree_view.connect('button-release-event', - self.__button_release_event_cb) - - self.attach(cell_renderer) - - def detach(self): - Invoker.detach(self) - self._tree_view.disconnect(self._motion_hid) - self._tree_view.disconnect(self._leave_hid) - self._tree_view.disconnect(self._release_hid) - - def get_rect(self): - allocation = self._tree_view.get_allocation() - if self._tree_view.window is not None: - x, y = self._tree_view.window.get_origin() - else: - logging.warning( - "Trying to position palette with invoker that's not realized.") - x = 0 - y = 0 - - if self._tree_view.flags() & gtk.NO_WINDOW: - x += allocation.x - y += allocation.y - - width = allocation.width - height = allocation.height - - return gtk.gdk.Rectangle(x, y, width, height) - - def __motion_notify_event_cb(self, widget, event): - if event.window != widget.get_bin_window(): - return - if self._point_in_cell_renderer(event.x, event.y): - - tree_view = self._tree_view - path, column_, x_, y_ = tree_view.get_path_at_pos(int(event.x), - int(event.y)) - if path != self.path: - if self.path is not None: - self._redraw_path(self.path) - if path is not None: - self._redraw_path(path) - if self.palette is not None: - self.palette.popdown(immediate=True) - self.palette = None - self.path = path - - self.notify_mouse_enter() - else: - if self.path is not None: - self._redraw_path(self.path) - self.path = None - self.notify_mouse_leave() - - def _redraw_path(self, path): - for column in self._tree_view.get_columns(): - if self._cell_renderer in column.get_cell_renderers(): - break - area = self._tree_view.get_background_area(path, column) - x, y = \ - self._tree_view.convert_bin_window_to_widget_coords(area.x, area.y) - self._tree_view.queue_draw_area(x, y, area.width, area.height) - - def __leave_notify_event_cb(self, widget, event): - self.notify_mouse_leave() - - def __button_release_event_cb(self, widget, event): - if event.button == 1 and self._point_in_cell_renderer(event.x, - event.y): - tree_view = self._tree_view - path, column_, x_, y_ = tree_view.get_path_at_pos(int(event.x), - int(event.y)) - self._cell_renderer.emit('clicked', path) - # So the treeview receives it and knows a drag isn't going on - return False - if event.button == 3 and self._point_in_cell_renderer(event.x, - event.y): - self.notify_right_click() - return True - else: - return False - - def _point_in_cell_renderer(self, event_x, event_y): - pos = self._tree_view.get_path_at_pos(int(event_x), int(event_y)) - if pos is None: - return False - - path_, column, x, y_ = pos - - for cell_renderer in column.get_cell_renderers(): - if cell_renderer == self._cell_renderer: - cell_x, cell_width = column.cell_get_position(cell_renderer) - if x > cell_x and x < (cell_x + cell_width): - return True - return False - - return False - - def get_toplevel(self): - return self._tree_view.get_toplevel() - - def notify_popup(self): - Invoker.notify_popup(self) - - def notify_popdown(self): - Invoker.notify_popdown(self) - self.palette = None - - def get_default_position(self): - return self.AT_CURSOR diff --git a/toolkit/json.py b/toolkit/json.py deleted file mode 100644 index a8cbcbd..0000000 --- a/toolkit/json.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# 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 2 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 - -""" -Unify usage of simplejson in Python 2.5/2.6 - -In Python 2.5 it imports simplejson module, in 2.6 native json module. - -Usage: - - import toolkit.json as json - - # and using regular simplejson interface with module json - json.dumps([]) - -""" - -try: - from json import * - dumps -except (ImportError, NameError): - from simplejson import * diff --git a/toolkit/pixbuf.py b/toolkit/pixbuf.py deleted file mode 100644 index c3bb7d1..0000000 --- a/toolkit/pixbuf.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# 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 2 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 - -"""gtk.gdk.Pixbuf extensions""" - -import re -import cStringIO -import gtk -import rsvg -import cairo -import logging - -from sugar.graphics import style -from sugar.graphics.xocolor import XoColor, is_valid -from sugar.util import LRU - - -def to_file(pixbuf): - """Convert pixbuf object to file object""" - - def push(pixbuf, buffer): - buffer.write(pixbuf) - - buffer = cStringIO.StringIO() - pixbuf.save_to_callback(push, 'png', user_data=buffer) - buffer.seek(0) - - return buffer - -def to_str(pixbuf): - """Convert pixbuf object to string""" - return to_file(pixbuf).getvalue() - -def from_str(str): - """Convert string to pixbuf object""" - - loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') - - try: - loader.write(str) - except Exception, e: - logging.error('pixbuf.from_str: %s' % e) - return None - finally: - loader.close() - - return loader.get_pixbuf() - - -def at_size_with_ratio(pixbuf, width, height, type=gtk.gdk.INTERP_BILINEAR): - image_width = pixbuf.get_width() - image_height = pixbuf.get_height() - - ratio_width = float(width) / image_width - ratio_height = float(height) / image_height - ratio = min(ratio_width, ratio_height) - - if ratio_width != ratio: - ratio_width = ratio - width = int(image_width * ratio) - elif ratio_height != ratio: - ratio_height = ratio - height = int(image_height * ratio) - - return pixbuf.scale_simple(width, height, type) - -def from_svg_at_size(filename=None, width=None, height=None, handle=None, - keep_ratio=True): - """Scale and load SVG into pixbuf""" - - if not handle: - handle = rsvg.Handle(filename) - - dimensions = handle.get_dimension_data() - icon_width = dimensions[0] - icon_height = dimensions[1] - - if icon_width != width or icon_height != height: - ratio_width = float(width) / icon_width - ratio_height = float(height) / icon_height - - if keep_ratio: - ratio = min(ratio_width, ratio_height) - if ratio_width != ratio: - ratio_width = ratio - width = int(icon_width * ratio) - elif ratio_height != ratio: - ratio_height = ratio - height = int(icon_height * ratio) - else: - ratio_width = 1 - ratio_height = 1 - - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - context = cairo.Context(surface) - context.scale(ratio_width, ratio_height) - handle.render_cairo(context) - - loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') - surface.write_to_png(loader) - loader.close() - - return loader.get_pixbuf() diff --git a/toolkit/radiopalette.py b/toolkit/radiopalette.py deleted file mode 100644 index 9c902b1..0000000 --- a/toolkit/radiopalette.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -import gtk - -from sugar.graphics.toolbutton import ToolButton -from sugar.graphics.palette import Palette - - -class RadioMenuButton(ToolButton): - - def __init__(self, **kwargs): - ToolButton.__init__(self, **kwargs) - self.selected_button = None - - if self.props.palette: - self.__palette_cb(None, None) - - self.connect('clicked', self.__clicked_cb) - self.connect('notify::palette', self.__palette_cb) - - def _do_clicked(self): - if self.palette is None: - return - if self.palette.is_up() and \ - self.palette.palette_state == Palette.SECONDARY: - self.palette.popdown(immediate=True) - else: - self.palette.popup(immediate=True) - self.palette.props.invoker.emit('right-click') - - def __palette_cb(self, widget, pspec): - if not isinstance(self.props.palette, RadioPalette): - return - self.props.palette.update_button() - - def __clicked_cb(self, button): - self._do_clicked() - - -class RadioToolsButton(RadioMenuButton): - - def __init__(self, **kwargs): - RadioMenuButton.__init__(self, **kwargs) - - def _do_clicked(self): - if not self.selected_button: - return - self.selected_button.emit('clicked') - - -class RadioPalette(Palette): - - def __init__(self, **kwargs): - Palette.__init__(self, **kwargs) - - self.button_box = gtk.HBox() - self.button_box.show() - self.set_content(self.button_box) - - def append(self, button, label): - children = self.button_box.get_children() - - if button.palette is not None: - raise RuntimeError("Palette's button should not have sub-palettes") - - button.show() - button.connect('clicked', self.__clicked_cb) - self.button_box.pack_start(button, fill=False) - button.palette_label = label - - if not children: - self.__clicked_cb(button) - - def update_button(self): - for i in self.button_box.get_children(): - self.__clicked_cb(i) - - def __clicked_cb(self, button): - if not button.get_active(): - return - - self.set_primary_text(button.palette_label) - self.popdown(immediate=True) - - if self.props.invoker is not None: - parent = self.props.invoker.parent - else: - parent = None - if not isinstance(parent, RadioMenuButton): - return - - parent.props.label = button.palette_label - parent.set_icon(button.props.icon_name) - parent.selected_button = button diff --git a/toolkit/scrolledbox.py b/toolkit/scrolledbox.py deleted file mode 100644 index ead071e..0000000 --- a/toolkit/scrolledbox.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# 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 2 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 - -from sugar.graphics.icon import Icon - -class ScrollButton(gtk.ToolButton): - def __init__(self, icon_name): - gtk.ToolButton.__init__(self) - - icon = Icon(icon_name = icon_name, - icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR) - # The alignment is a hack to work around gtk.ToolButton code - # that sets the icon_size when the icon_widget is a gtk.Image - alignment = gtk.Alignment(0.5, 0.5) - alignment.add(icon) - self.set_icon_widget(alignment) - -class ScrolledBox(gtk.EventBox): - def __init__(self, orientation, - arrows_policy=gtk.POLICY_AUTOMATIC, - scroll_policy=gtk.POLICY_AUTOMATIC): - - gtk.EventBox.__init__(self) - self.orientation = orientation - self._viewport = None - self._abox = None - self._aviewport = None - self._aviewport_sig = None - self._arrows_policy = arrows_policy - self._scroll_policy = scroll_policy - self._left = None - self._right = None - - if orientation == gtk.ORIENTATION_HORIZONTAL: - box = gtk.HBox() - else: - box = gtk.VBox() - if self._arrows_policy == gtk.POLICY_AUTOMATIC: - box.connect("size-allocate", self._box_allocate_cb) - self.add(box) - - if self._arrows_policy != gtk.POLICY_NEVER: - if orientation == gtk.ORIENTATION_HORIZONTAL: - self._left = ScrollButton('go-left') - else: - self._left = ScrollButton('go-up') - self._left.connect('clicked', self._scroll_cb, - gtk.gdk.SCROLL_LEFT) - box.pack_start(self._left, False, False, 0) - - self._scrolled = gtk.ScrolledWindow() - if orientation == gtk.ORIENTATION_HORIZONTAL: - self._scrolled.set_policy(scroll_policy, gtk.POLICY_NEVER) - else: - self._scrolled.set_policy(gtk.POLICY_NEVER, scroll_policy) - self._scrolled.connect('scroll-event', self._scroll_event_cb) - box.pack_start(self._scrolled, True, True, 0) - - if orientation == gtk.ORIENTATION_HORIZONTAL: - self._adj = self._scrolled.get_hadjustment() - else: - self._adj = self._scrolled.get_vadjustment() - self._adj.connect('changed', self._scroll_changed_cb) - self._adj.connect('value-changed', self._scroll_changed_cb) - - if self._arrows_policy != gtk.POLICY_NEVER: - if orientation == gtk.ORIENTATION_HORIZONTAL: - self._right = ScrollButton('go-right') - else: - self._right = ScrollButton('go-down') - self._right.connect('clicked', self._scroll_cb, - gtk.gdk.SCROLL_RIGHT) - box.pack_start(self._right, False, False, 0) - - def modify_fg(self, state, bg): - gtk.EventBox.modify_fg(self, state, bg) - self._viewport.get_parent().modify_fg(state, bg) - - def modify_bg(self, state, bg): - gtk.EventBox.modify_bg(self, state, bg) - self._viewport.get_parent().modify_bg(state, bg) - - def set_viewport(self, widget): - if widget == self._viewport: return - if self._viewport and self._aviewport_sig: - self._viewport.disconnect(self._aviewport_sig) - self._viewport = widget - - if self._arrows_policy == gtk.POLICY_AUTOMATIC: - self._aviewport_sig = self._viewport.connect('size-allocate', - self._viewport_allocate_cb) - - self._scrolled.add_with_viewport(widget) - - def get_viewport_allocation(self): - alloc = self._scrolled.get_allocation() - alloc.x -= self._adj.get_value() - return alloc - - def get_adjustment(self): - return self._adj - - def _box_allocate_cb(self, w, a): - self._abox = a - self._update_arrows() - - def _viewport_allocate_cb(self, w, a): - self._aviewport = a - self._update_arrows() - - def _update_arrows(self): - if not self._abox or not self._aviewport: return - - if self.orientation == gtk.ORIENTATION_HORIZONTAL: - show_flag = self._abox.width < self._aviewport.width - else: - show_flag = self._abox.height < self._aviewport.height - - if show_flag: - self._left.show() - self._right.show() - else: - self._left.hide() - self._right.hide() - - def _scroll_event_cb(self, widget, event): - if self.orientation == gtk.ORIENTATION_HORIZONTAL: - if event.direction == gtk.gdk.SCROLL_UP: - event.direction = gtk.gdk.SCROLL_LEFT - if event.direction == gtk.gdk.SCROLL_DOWN: - event.direction = gtk.gdk.SCROLL_RIGHT - else: - if event.direction == gtk.gdk.SCROLL_LEFT: - event.direction = gtk.gdk.SCROLL_UP - if event.direction == gtk.gdk.SCROLL_RIGHT: - event.direction = gtk.gdk.SCROLL_DOWN - - if self._scroll_policy == gtk.POLICY_NEVER: - self._scroll_cb(None, event.direction) - - return False - - def _scroll_cb(self, widget, direction): - if direction in (gtk.gdk.SCROLL_LEFT, gtk.gdk.SCROLL_UP): - val = max(self._adj.get_property('lower'), self._adj.get_value() - - self._adj.get_property('page_increment')) - else: - val = min(self._adj.get_property('upper') - - self._adj.get_property('page_size'), - self._adj.get_value() - + self._adj.get_property('page_increment')) - - self._adj.set_value(val) - - def _scroll_changed_cb(self, widget): - val = self._adj.get_value() - if self._left: - if val == 0: - self._left.set_sensitive(False) - else: - self._left.set_sensitive(True) - - if self._right: - if val >= self._adj.get_property('upper') - \ - self._adj.get_property('page_size'): - self._right.set_sensitive(False) - else: - self._right.set_sensitive(True) - -class HScrolledBox(ScrolledBox): - def __init__(self, **kwargs): - ScrolledBox.__init__(self, gtk.ORIENTATION_HORIZONTAL, **kwargs) - -class VScrolledBox(ScrolledBox): - def __init__(self, **kwargs): - ScrolledBox.__init__(self, gtk.ORIENTATION_VERTICAL, **kwargs) diff --git a/toolkit/tarball.py b/toolkit/tarball.py deleted file mode 100644 index 0a4a1b2..0000000 --- a/toolkit/tarball.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# 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 2 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 - -"""Simplify tarfile module usage""" - -import os -import time -import tarfile -import cStringIO -import gtk -import zipfile -import tempfile -import shutil - - -class TarballError(Exception): - """Base Tarball exception.""" - pass - - -class BadDataTypeError(TarballError): - """Exception for unsupported data type in read/write methods.""" - pass - - -class Tarball: - """ - Wrap standart tarfile module to simplify read/write operations. - In read mode Tarball can load zip files as well. - - Write usage: - - # create Tarball object - # to see all supported modes use - # http://docs.python.org/library/tarfile.html#tarfile.open - tar = Tarball(tarfile, 'w') - - # write string to file in tarball - tar.write('name within tarball', 'string to write') - - # save and close tarball file - tar.close() - - Read usage: - - # create Tarball object - tar = Tarball(tarfile) - - # read content of file in tarball to string - str_content = tar.read('name within tarball') - """ - - def __init__(self, name=None, mode='r', mtime=None): - if not mode.startswith('r') or tarfile.is_tarfile(name): - self.__tar = tarfile.TarFile(name=name, mode=mode) - else: - # convert for tar - - if not zipfile.is_zipfile(name): - raise tarfile.ReadError() - - try: - tmp_dir = tempfile.mkdtemp() - tmp_fd, tmp_name = tempfile.mkstemp() - tmp_fo = os.fdopen(tmp_fd, 'w') - - zip = zipfile.ZipFile(name) - zip.extractall(tmp_dir) - - tar = tarfile.TarFile(fileobj=tmp_fo, mode='w') - tar.add(tmp_dir, arcname='') - tar.close() - - self.__tar = tarfile.TarFile(name=tmp_name, mode=mode) - finally: - tmp_fo.close() - os.unlink(tmp_name) - shutil.rmtree(tmp_dir) - - if mtime: - self.mtime = mtime - else: - self.mtime = time.time() - - def close(self): - """Save(if 'r' mode was given) and close tarball file.""" - self.__tar.close() - - def getnames(self): - """Return names of members sorted by creation order.""" - return self.__tar.getnames() - - def read(self, arcname): - """Returns sring with content of given file from tarball.""" - file_o = self.__tar.extractfile(arcname.encode('utf8')) - if not file_o: - return None - out = file_o.read() - file_o.close() - return out - - def write(self, arcname, data, mode=0644): - """ - Stores given object to file in tarball. - Raises BadDataTypeError exception If data type isn't supported. - """ - info = tarfile.TarInfo(arcname.encode('utf8')) - info.mode = mode - info.mtime = self.mtime - info.size = len(data) - - self.__tar.addfile(info, cStringIO.StringIO(data)) diff --git a/toolkit/temposlider.py b/toolkit/temposlider.py deleted file mode 100644 index 8fcf8cb..0000000 --- a/toolkit/temposlider.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright (C) 2006-2008, TamTam Team -# -# 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 2 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 - -# Widget was copy&pasted from TamTam activities - -import gtk -import rsvg -import cairo - -from sugar.graphics import style - -class TempoSlider(gtk.HBox): - def __init__(self, min_value, max_value): - gtk.HBox.__init__(self) - - self._pixbuf = [None] * 8 - self._image = gtk.Image() - self._image.show() - - # used to store tempo updates while the slider is active - self._delayed = 0 - self._active = False - - self.adjustment = gtk.Adjustment(min_value, min_value, max_value, - (max_value - min_value) / 8, (max_value - min_value) / 8, 0) - self._adjustment_h = self.adjustment.connect('value-changed', - self._changed_cb) - - slider = gtk.HScale(adjustment = self.adjustment) - slider.show() - slider.set_draw_value(False) - slider.connect("button-press-event", self._press_cb) - slider.connect("button-release-event", self._release_cb) - - self.pack_start(slider, True, True) - self.pack_end(self._image, False, False) - - def set_value(self, tempo, quiet = False): - if self._active: - self._delayed = tempo - elif quiet: - self.adjustment.handler_block(self._adjustment_h) - self.adjustment.set_value(tempo) - self._update(tempo) - self.adjustment.handler_unblock(self._adjustment_h) - else: - self.adjustment.set_value(tempo) - - def _changed_cb(self, widget): - self._update(widget.get_value()) - - def _update(self, tempo): - def map_range(value, ilower, iupper, olower, oupper): - if value == iupper: - return oupper - return olower + int((oupper-olower+1) * (value-ilower) / - float(iupper-ilower)) - - img = map_range(tempo, self.adjustment.lower, - self.adjustment.upper, 0, 7) - - if not self._pixbuf[img]: - svg = rsvg.Handle(data=IMAGE[img]) - self._pixbuf[img] = _from_svg_at_size(handle=svg, - width=style.STANDARD_ICON_SIZE, - height=style.STANDARD_ICON_SIZE) - - self._image.set_from_pixbuf(self._pixbuf[img]) - - def _press_cb(self, widget, event): - self._active = True - - def _release_cb(self, widget, event): - self._active = False - if self._delayed != 0: - self.set_value(self._delayed, True) - self._delayed = 0 - -def _from_svg_at_size(filename=None, width=None, height=None, handle=None, - keep_ratio=True): - """ import from pixbuf.py """ - - if not handle: - handle = rsvg.Handle(filename) - - dimensions = handle.get_dimension_data() - icon_width = dimensions[0] - icon_height = dimensions[1] - - if icon_width != width or icon_height != height: - ratio_width = float(width) / icon_width - ratio_height = float(height) / icon_height - - if keep_ratio: - ratio = min(ratio_width, ratio_height) - if ratio_width != ratio: - ratio_width = ratio - width = int(icon_width * ratio) - elif ratio_height != ratio: - ratio_height = ratio - height = int(icon_height * ratio) - else: - ratio_width = 1 - ratio_height = 1 - - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - context = cairo.Context(surface) - context.scale(ratio_width, ratio_height) - handle.render_cairo(context) - - loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') - surface.write_to_png(loader) - loader.close() - - return loader.get_pixbuf() - -IMAGE = [None] * 8 - -IMAGE[0] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M23.5,6.5c3,3,7,7,9,11c-7,5-4,6-3,26c-1,1-8,1-9,0c0,0,2,1,2-1 - c0-3-2-7-2-11c0-2,1-4,1-6c0-3-2-1-2-3c0-3,3-8,3-11c0-2-1-1-2-2v-3H23.5z"/> -</svg> -""" - -IMAGE[1] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M27.5,44.5v-3C28.5,42.5,28.5,43.5,27.5,44.5z M26.5,10.5 - c2,2,2,6,2,8c0,4-3,11-3,13s4,7,7,10c-2,2-4,3-5,5h-6c1-1,2-3,2-5c0-3-2-9-3-14c0,0,0-1-1,0v-6c0-3,3-8,3-11c0-1-2-2-2-6h3 - C23.5,5.5,26.5,9.5,26.5,10.5z"/> -</svg> -""" - -IMAGE[2] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M30.5,17.5c0,3-2,2-2,4c0,3,4,14,7,21c-1,0-3,1-5,1c1-1,2,0,2-3 - c0-2-4-7-6-10c-3,3-5,8-7,13c-1,0-3-1-4-1c3-3,7-14,7-18s-1-3-4-4c3-2,4-8,4-14h3C23.5,9.5,30.5,14.5,30.5,17.5z"/> -</svg> -""" - -IMAGE[3] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M34.5,22.5c-1-1-2-4-5-6c-1,2,0,3,0,6c0,2-3,4-3,7c0,2,4,2,4,4 - c0,3-1,4-2,5c0-1,0-3-1-4c-1,3-2,7-3,10c-4-3,0-6,0-9s-3-11-4-17l-4,4c1-5,8.25-11.12,7.25-16.12c0.68,0.68,3.029,0,2.87,2.12 - C26.5,10.25,33.62,17.75,34.5,22.5z"/> -</svg> -""" - -IMAGE[4] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M24.5,13.5c2,1,5,3,5,6c0,2-2,3-2,5c0,9,11,4,11,13c-1,0-3-2-4-3 - c-3-1-9,1-10-3c-2,3-5,7-7,11c-3,0-3-1-4-1c0-2,3-3,4-6s4-8,4-10c0-3-1-3-2-5c-1,0-2,1-3,2c0-1,2-3,2-4c1-2,3-5,2-8c0,0,1-1,4-2 - C25.5,9.5,25.5,11.5,24.5,13.5z"/> -</svg> -""" - -IMAGE[5] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M22.5,10.5c3,2,7,5,7,7c0,3-4,8-4,10c0,3,1,3,1,5h5l2-2l2,2v4 - c-1,0-3-2-5-2c-3,0-5,1-8,1c-1,3-2,7-2,10h-5c1-1,3-3,3-4c1-5,1-11,1-18l-1-1c-1,1-1.75,2.88-2.75,2.88c0,0-0.25-0.63-0.25-1.63 - c4-4,2-8.25,2-13.25c0-1,0.25-2.5,0.38-5.38L22.5,5.5C23.12,6.5,22.5,8.5,22.5,10.5z"/> -<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#333333" stroke="#333333" stroke-linecap="round" stroke-linejoin="round" points=" - 25,20 25.25,16.75 26.5,17.88 "/> -</svg> -""" - -IMAGE[6] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M20.5,7.5c1,1,1,3,1,4c10,4,8,6,8,14c0,2,6,9,10,13c-1,2-2,4-4,5 - c1.62-8.88-8.75-13.88-12-15c-1,1-1,0-1,2c0,3,2,5,3,7c-1,1-3,2-6,2c0-1,2-1,2-4c0-2-4-4-4-6c0-3,3-4,5-6c-3-8-8-2-11-6h6 - c0-1,1,0,1-3c0-2-1-1-2-2l1-5H20.5z"/> -</svg> -""" - -IMAGE[7] = """<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve"> -<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M20.5,12.5c0.67,0.4,0.4,1.9,1.75,2.25s1.05-0.38,1.5-0.37 - c4.971,0,10.95-0.88,11.75,7.12c-1-2-3-4-5-5l-4,1c1,2,4,4,5,7c1,1,1,4,1,6c3,3,8-1,11,6c-2.88-0.82-4.25-2.62-12.75-2.75 - c-1.561-0.02-2.34-1.561-3.75-1.87c-3.42-0.76-4.67-0.38-5.5-0.38c-3,0-8,7-11,7c-2,0-3-1-3-2c4,2,8-4,9-7c2-1,5-1,8-3c-2-4-6-5-8-3 - l-6-6l2-2c1,1,1,2,1,4c1,0,4.12,0.38,6.12-0.62L16.5,17.5v-5H20.5z"/> -</svg> -""" diff --git a/toolkit/toolbarbox.py b/toolkit/toolbarbox.py deleted file mode 100644 index 7172b8b..0000000 --- a/toolkit/toolbarbox.py +++ /dev/null @@ -1,333 +0,0 @@ -# Copyright (C) 2009, Aleksey Lim -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -import gtk -import gobject - -from sugar.graphics import style -from sugar.graphics.toolbutton import ToolButton -from sugar.graphics import palettegroup - -from toolkit.internals.palettewindow import PaletteWindow -from toolkit.internals.palettewindow import ToolInvoker - - -_ARROW_SIZE = style.zoom(24) -_LINE_WIDTH = 2 - -class ToolbarButton(ToolButton): - - def __init__(self, page=None, **kwargs): - ToolButton.__init__(self, **kwargs) - - self.page_widget = None - - self.set_page(page) - - self.connect('clicked', - lambda widget: self.set_expanded(not self.is_expanded())) - self.connect('size-allocate', self.__size_allocate_cb) - - def get_toolbar_box(self): - if not hasattr(self.parent, 'owner'): - return None - return self.parent.owner - - toolbar_box = property(get_toolbar_box) - - def get_page(self): - if self.page_widget is None: - return None - return _get_embedded_page(self.page_widget) - - def set_page(self, page): - if page is None: - self.page_widget = None - return - - self.page_widget, alignment_ = _embed_page(_Box, page) - w_, h = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR) - page.show() - - if self.props.palette is None: - self.props.palette = _ToolbarPalette(invoker=ToolInvoker(self)) - self._move_page_to_palette() - - page = gobject.property(type=object, getter=get_page, setter=set_page) - - def is_in_palette(self): - return self.page is not None and \ - self.page_widget.parent == self.props.palette - - def is_expanded(self): - return self.page is not None and \ - not self.is_in_palette() - - def popdown(self): - if self.props.palette is not None: - self.props.palette.popdown(immediate=True) - - def set_expanded(self, expanded): - self.popdown() - - if self.page is None or self.is_expanded() == expanded: - return - - if not expanded: - self._move_page_to_palette() - return - - box = self.toolbar_box - - if box.expanded_button is not None: - if box.expanded_button.window is not None: - # need to redraw it to erase arrow - box.expanded_button.window.invalidate_rect(None, True) - box.expanded_button.set_expanded(False) - box.expanded_button = self - - self._unparent() - - self.modify_bg(gtk.STATE_NORMAL, box.background) - _setup_page(self.page_widget, box.background, box.props.padding) - box.pack_start(self.page_widget) - - def _move_page_to_palette(self): - if self.is_in_palette(): - return - - self._unparent() - - if isinstance(self.props.palette, _ToolbarPalette): - self.props.palette.add(self.page_widget) - - def _unparent(self): - if self.page_widget.parent is None: - return - self.page_widget.parent.remove(self.page_widget) - - def do_expose_event(self, event): - if not self.is_expanded() or self.props.palette is not None and \ - self.props.palette.is_up(): - ToolButton.do_expose_event(self, event) - _paint_arrow(self, event, gtk.ARROW_DOWN) - return - - alloc = self.allocation - - self.get_style().paint_box(event.window, - gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self, - 'palette-invoker', alloc.x, 0, - alloc.width, alloc.height + _LINE_WIDTH) - - if self.child.state != gtk.STATE_PRELIGHT: - self.get_style().paint_box(event.window, - gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None, - alloc.x + _LINE_WIDTH, _LINE_WIDTH, - alloc.width - _LINE_WIDTH * 2, alloc.height) - - gtk.ToolButton.do_expose_event(self, event) - _paint_arrow(self, event, gtk.ARROW_UP) - - def __size_allocate_cb(self, button, allocation): - if self.page_widget is not None: - self.page_widget.set_size_request(-1, allocation.height) - - -class ToolbarBox(gtk.VBox): - - def __init__(self, padding=style.TOOLBOX_HORIZONTAL_PADDING): - gtk.VBox.__init__(self) - self._expanded_button_index = -1 - self.background = None - - self._toolbar = gtk.Toolbar() - self._toolbar.owner = self - self._toolbar.connect('remove', self.__remove_cb) - - self._toolbar_widget, self._toolbar_alignment = \ - _embed_page(gtk.EventBox, self._toolbar) - self.pack_start(self._toolbar_widget) - - self.props.padding = padding - self.modify_bg(gtk.STATE_NORMAL, - style.COLOR_TOOLBAR_GREY.get_gdk_color()) - - def get_toolbar(self): - return self._toolbar - - toolbar = property(get_toolbar) - - def get_expanded_button(self): - if self._expanded_button_index == -1: - return None - return self.toolbar.get_nth_item(self._expanded_button_index) - - def set_expanded_button(self, button): - if not button in self.toolbar: - self._expanded_button_index = -1 - return - self._expanded_button_index = self.toolbar.get_item_index(button) - - expanded_button = property(get_expanded_button, set_expanded_button) - - def get_padding(self): - return self._toolbar_alignment.props.left_padding - - def set_padding(self, pad): - self._toolbar_alignment.set_padding(0, 0, pad, pad) - - padding = gobject.property(type=object, - getter=get_padding, setter=set_padding) - - def modify_bg(self, state, color): - if state == gtk.STATE_NORMAL: - self.background = color - self._toolbar_widget.modify_bg(state, color) - self.toolbar.modify_bg(state, color) - - def __remove_cb(self, sender, button): - if not isinstance(button, ToolbarButton): - return - button.popdown() - if button == self.expanded_button: - self.remove(button.page_widget) - self._expanded_button_index = -1 - - -class _ToolbarPalette(PaletteWindow): - - def __init__(self, **kwargs): - PaletteWindow.__init__(self, **kwargs) - self.set_border_width(0) - self._has_focus = False - - group = palettegroup.get_group('default') - group.connect('popdown', self.__group_popdown_cb) - self.set_group_id('toolbarbox') - - def get_expanded_button(self): - return self.invoker.parent - - expanded_button = property(get_expanded_button) - - def on_invoker_enter(self): - PaletteWindow.on_invoker_enter(self) - self._set_focus(True) - - def on_invoker_leave(self): - PaletteWindow.on_invoker_leave(self) - self._set_focus(False) - - def on_enter(self, event): - PaletteWindow.on_enter(self, event) - self._set_focus(True) - - def on_leave(self, event): - PaletteWindow.on_enter(self, event) - self._set_focus(False) - - def _set_focus(self, new_focus): - self._has_focus = new_focus - if not self._has_focus: - group = palettegroup.get_group('default') - if not group.is_up(): - self.popdown() - - def do_size_request(self, requisition): - gtk.Window.do_size_request(self, requisition) - requisition.width = max(requisition.width, - gtk.gdk.screen_width()) - - def popup(self, immediate=False): - button = self.expanded_button - if button.is_expanded(): - return - box = button.toolbar_box - _setup_page(button.page_widget, style.COLOR_BLACK.get_gdk_color(), - box.props.padding) - PaletteWindow.popup(self, immediate) - - def __group_popdown_cb(self, group): - if not self._has_focus: - self.popdown(immediate=True) - - -class _Box(gtk.EventBox): - - def __init__(self): - gtk.EventBox.__init__(self) - self.connect('expose-event', self.do_expose_event) - self.set_app_paintable(True) - - def do_expose_event(self, widget, event): - if self.parent.expanded_button is None: - return - alloc = self.parent.expanded_button.allocation - self.get_style().paint_box(event.window, - gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self, - 'palette-invoker', -_LINE_WIDTH, 0, - self.allocation.width + _LINE_WIDTH * 2, - self.allocation.height + _LINE_WIDTH) - self.get_style().paint_box(event.window, - gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None, - alloc.x + _LINE_WIDTH, 0, - alloc.width - _LINE_WIDTH * 2, _LINE_WIDTH) - - -def _setup_page(page_widget, color, hpad): - vpad = _LINE_WIDTH - page_widget.child.set_padding(vpad, vpad, hpad, hpad) - - page = _get_embedded_page(page_widget) - page.modify_bg(gtk.STATE_NORMAL, color) - if isinstance(page, gtk.Container): - for i in page.get_children(): - i.modify_bg(gtk.STATE_INSENSITIVE, color) - - page_widget.modify_bg(gtk.STATE_NORMAL, color) - page_widget.modify_bg(gtk.STATE_PRELIGHT, color) - - -def _embed_page(box_class, page): - page.show() - - alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0) - alignment.add(page) - alignment.show() - - page_widget = box_class() - page_widget.modify_bg(gtk.STATE_ACTIVE, - style.COLOR_BUTTON_GREY.get_gdk_color()) - page_widget.add(alignment) - page_widget.show() - - return (page_widget, alignment) - - -def _get_embedded_page(page_widget): - return page_widget.child.child - - -def _paint_arrow(widget, event, arrow_type): - alloc = widget.allocation - x = alloc.x + alloc.width / 2 - _ARROW_SIZE / 2 - y = alloc.y + alloc.height - int(_ARROW_SIZE * .85) - - widget.get_style().paint_arrow(event.window, - gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, widget, - None, arrow_type, True, x, y, _ARROW_SIZE, _ARROW_SIZE) |