From 1e066f5535bf6eaf13cdc79215d1640faf1c73c9 Mon Sep 17 00:00:00 2001 From: Ignacio Rodriguez Date: Sun, 22 Dec 2013 21:54:45 +0000 Subject: Show dialog for change bundle icon. --- diff --git a/IconDialog.py b/IconDialog.py new file mode 100644 index 0000000..637889c --- /dev/null +++ b/IconDialog.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Ignacio Rodríguez +# +# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import shutil +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GdkPixbuf +from jarabe.journal.model import get_documents_path +from sugar3.graphics.toolbutton import ToolButton +from sugar3.graphics.toolbarbox import ToolbarBox +from sugar3.graphics import style +from gettext import gettext as _ +import os + +SUGAR_ARTWORK = [_('Actions'), _('Emblems'), _('Documents')] + + +def get_document_icons(): + icons = os.listdir(get_documents_path()) + icons_ = [] + for icon in icons: + if icon.endswith('.svg'): + icons_.append(icon[:-4]) + + return icons_ + +SUGAR_ICONS = { + _('Actions'): ['media-playlist-repeat-insensitive', + 'media-playlist-shuffle-insensitive', + 'format-justify-left', + 'cell-height', + 'media-playback-stop-insensitive', + 'select-all', 'format-columns-triple', 'column-insert', + 'go-right', 'cell-format', 'format-justify-right', 'row-insert', + 'entry-search', 'invite', 'format-text-underline', 'entry-stop', + 'view-return', 'transfer-from-text-uri-list', 'cell-size', 'column-remove', + 'insert-image', 'edit-clear', 'view-radial', 'view-lastedit', + 'media-seek-forward-insensitive', 'row-remove', 'zoom-home', + 'zoom-best-fit', 'media-playlist-repeat', 'media-eject-insensitive', + 'view-fullscreen', 'format-text-leading', 'transfer-from-text-x-generic', + 'select-none', 'toolbar-view', 'media-playback-pause', 'format-text-bold', + 'media-playback-start-insensitive', 'go-home', 'view-freeform', 'go-next', + 'transfer-from-image-x-generic', 'media-seek-backward', 'list-add', + 'edit-description', 'toolbar-colors', 'cell-width', + 'transfer-from-audio-x-generic', 'zoom-in', 'zoom-groups', + 'media-seek-forward', 'go-up', 'view-list', 'format-justify-center', + 'transfer-from', 'media-playback-pause-insensitive', 'media-playback-stop', + 'go-previous', 'go-left', 'transfer-from-video-x-generic', + 'media-playlist-shuffle', 'zoom-out', 'toolbar-edit', 'go-next-paired', + 'system-logout', 'view-source', 'tray-hide', 'edit-copy', 'insert-table', + 'view-size', 'format-justify-fill', 'go-down', 'format-columns-single', + 'transfer-to-text-uri-list', 'activity-stop', + 'transfer-to-audio-x-generic', 'view-box', 'zoom-original', + 'edit-undo', 'document-send', 'view-refresh', + 'document-save', 'system-shutdown', 'entry-refresh', 'dialog-cancel', + 'system-search', 'transfer-to-image-x-generic', + 'transfer-from-application-octet-stream', + 'media-seek-backward-insensitive', 'dialog-ok', 'edit-redo', + 'view-created', 'activity-start', 'format-text-size', 'view-triangle', + 'entry-cancel', 'media-eject', 'edit-paste', 'tray-show', + 'transfer-to-video-x-generic', 'transfer-to', 'view-details', + 'system-restart', 'zoom-activity', 'media-record', + 'transfer-to-text-x-generic', 'zoom-to-width', 'format-columns-double', + 'format-text-italic', 'tray-favourite', 'list-remove', + 'transfer-to-application-octet-stream', 'view-spiral', + 'media-record-insensitive', 'edit-delete', 'toolbar-help', + 'edit-duplicate', 'media-playback-start', 'zoom-neighborhood', + 'go-previous-paired'], + _('Emblems'): ['emblem-busy', 'emblem-charging', 'emblem-downloads', + 'emblem-favorite', 'emblem-locked', 'emblem-notification', + 'emblem-outofrange', 'emblem-question', 'emblem-view-source', + 'emblem-warning'], + _('Documents'): get_document_icons() +} + + + +class IconDialog(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self) + + self.theme = Gtk.IconTheme.get_default() + self.theme.append_search_path(get_documents_path()) + + self._icon = None + grid = Gtk.Grid() + + self.x, self.y = (Gdk.Screen.width() / 1.5, Gdk.Screen.height() / 1.5) + self.set_size_request(self.x, self.y) + + self.icons = None + toolbox = self.build_toolbar() + self.icons = self.build_scroll() + + grid.attach(toolbox, 0, 1, 1, 1) + grid.attach(self.icons, 0, 2, 1, 1) + + self.set_decorated(False) + self.set_skip_pager_hint(True) + self.set_skip_taskbar_hint(True) + self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + self.set_resizable(False) + self.set_modal(True) + + self.add(grid) + self.show_all() + + def build_toolbar(self): + toolbox = ToolbarBox() + + label = Gtk.Label("\t" + _('Select an icon')) + label.modify_fg(Gtk.StateType.NORMAL, + Gdk.color_parse('white')) + + item = Gtk.ToolItem() + item.add(label) + + close = ToolButton('entry-cancel') + close.connect('clicked', lambda x: self.destroy()) + + separator = Gtk.SeparatorToolItem() + separator.props.draw = False + separator.set_expand(True) + + toolbox.toolbar.insert(item, -1) + toolbox.toolbar.insert(separator, -1) + toolbox.toolbar.insert(close, -1) + + return toolbox + + def get_icon(self): + return self._icon + + def build_scroll(self): + scroll = Gtk.ScrolledWindow() + scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + + grid = Gtk.Grid() + current = 0 + for icon in SUGAR_ARTWORK: + expander = self.build_icons(icon) + grid.attach(expander, 0, current, 1, 1) + current += 1 + + scroll.set_size_request(self.x, self.y) + scroll.add_with_viewport(grid) + return scroll + + def build_icons(self, category): + icons = SUGAR_ICONS[category] + + if len(icons) < 1: + return Gtk.EventBox() + + store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str) + + + icon_view = Gtk.IconView.new_with_model(store) + icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE) + icon_view.connect('selection-changed', self.set_icon, store) + icon_view.set_pixbuf_column(0) + icon_view.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse('#D5D5D5')) + + for icon in icons: + info = self.theme.lookup_icon(icon, 55, + Gtk.IconLookupFlags.FORCE_SVG) + if not info: + continue + icon_path = info.get_filename() + if category == _('Documents'): + icon_path = os.path.join(get_documents_path(), icon + ".svg") + + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( + icon_path, 55, 55) + store.insert(-1, [pixbuf, icon, icon_path]) + + expand = Gtk.Expander() + expand.set_label(category) + expand.add(icon_view) + return expand + + def set_icon(self, widget, model): + try: + iter_ = model.get_iter(widget.get_selected_items()[0]) + except: + return + + icon_path = model.get(iter_, 2)[0] + self._icon = icon_path + self.destroy() + + def get_icon(self): + return self._icon diff --git a/pippy_app.py b/pippy_app.py index 0349d86..0b58007 100644 --- a/pippy_app.py +++ b/pippy_app.py @@ -48,6 +48,7 @@ from sugar3.activity.widgets import StopButton from sugar3.activity.activity import get_bundle_path from sugar3.activity.activity import get_bundle_name from sugar3.graphics.alert import NotifyAlert +from sugar3.graphics.alert import ConfirmationAlert from sugar3.graphics import style from sugar3.graphics.toggletoolbutton import ToggleToolButton @@ -60,6 +61,7 @@ import groupthink.sugar_tools import groupthink.gtk_tools from FileDialog import FileDialog +from IconDialog import IconDialog text_buffer = None # magic prefix to use utf-8 source encoding @@ -340,7 +342,6 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): def _select_func_cb(self, path): text_buffer = self.source_tabs.get_text_buffer() if text_buffer.get_modified(): - from sugar3.graphics.alert import ConfirmationAlert alert = ConfirmationAlert() alert.props.title = _('Example selection Warning') alert.props.msg = _('You have modified the currently selected file. \ @@ -481,35 +482,55 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): self.add_alert(alert) return - self.stopbutton_cb(None) # try stopping old code first. - self._reset_vte() - self._vte.feed(_("Creating activity bundle...")) - self._vte.feed("\r\n") - TMPDIR = 'instance' - app_temp = mkdtemp('.activity', 'Pippy', - os.path.join(self.get_activity_root(), TMPDIR)) - sourcefile = os.path.join(app_temp, 'xyzzy.py') - # invoke ourself to build the activity bundle. - self._logger.debug('writing out source file: %s' % sourcefile) + alert_icon = ConfirmationAlert() + alert_icon.props.title = _('Activity icon') + alert_icon.props.msg = _('You want to select activity icon?') - # write out application code - self._write_text_buffer(sourcefile) + def internal_callback(window=None, event=None): + icon = "%s/activity/activity-default.svg" % (get_bundle_path()) + if window: + icon = window.get_icon() - try: - # FIXME: vte invocation was raising errors. Switched to subprocess - output = subprocess.check_output( - ["/usr/bin/python", - "%s/pippy_app.py" % get_bundle_path(), - '-p', '%s/library' % get_bundle_path(), - '-d', app_temp, title, sourcefile]) - self._vte.feed(output) - self._vte.feed("\r\n") - self.bundle_cb(title, app_temp) - except subprocess.CalledProcessError: - rmtree(app_temp, ignore_errors=True) # clean up! - self._vte.feed(_('Save as Activity Error')) + self.stopbutton_cb(None) # try stopping old code first. + self._reset_vte() + self._vte.feed(_("Creating activity bundle...")) self._vte.feed("\r\n") - raise + TMPDIR = 'instance' + app_temp = mkdtemp('.activity', 'Pippy', + os.path.join(self.get_activity_root(), TMPDIR)) + sourcefile = os.path.join(app_temp, 'xyzzy.py') + # invoke ourself to build the activity bundle. + self._logger.debug('writing out source file: %s' % sourcefile) + + # write out application code + self._write_text_buffer(sourcefile) + + try: + # FIXME: vte invocation was raising errors. Switched to subprocess + output = subprocess.check_output( + ["/usr/bin/python", + "%s/pippy_app.py" % get_bundle_path(), + '-p', '%s/library' % get_bundle_path(), + '-d', app_temp, title, sourcefile, icon]) + self._vte.feed(output) + self._vte.feed("\r\n") + self.bundle_cb(title, app_temp) + except subprocess.CalledProcessError: + rmtree(app_temp, ignore_errors=True) # clean up! + self._vte.feed(_('Save as Activity Error')) + self._vte.feed("\r\n") + raise + + def alert_response(alert, response_id): + if response_id == Gtk.ResponseType.OK: + dialog = IconDialog() + dialog.connect('destroy', internal_callback) + else: + internal_callback() + self.remove_alert(alert) + + alert_icon.connect('response', alert_response) + self.add_alert(alert_icon) def _export_example_cb(self, __): # get the name of this pippy program. @@ -533,7 +554,6 @@ name before attempting to save it as an example.') local_data = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'data') local_file = os.path.join(local_data, title) if os.path.exists(local_file): - from sugar3.graphics.alert import ConfirmationAlert alert = ConfirmationAlert() alert.props.title = _('Save as Example Warning') alert.props.msg = _('This example already exists. \ @@ -541,11 +561,11 @@ Do you want to overwrite it?') alert.connect('response', self.confirmation_alert_cb, local_file) self.add_alert(alert) else: - self.write_file(local_file) - self._reset_vte() - self._vte.feed(_("Saved as example.")) - self._vte.feed("\r\n") - self.add_to_example_list(local_file) + self.write_file(local_file) + self._reset_vte() + self._vte.feed(_("Saved as example.")) + self._vte.feed("\r\n") + self.add_to_example_list(local_file) def child_exited_cb(self, *args): """Called whenever a child exits. If there's a handler, run it.""" @@ -670,64 +690,6 @@ stroke="&stroke_color;" stroke-linecap="round" stroke-width="3"/> """ -PIPPY_DEFAULT_ICON = \ -""" - - - - -]> - - - - - - -""" ############# ACTIVITY META-INFORMATION ############### # this is used by Pippy to generate a bundle for itself. @@ -749,7 +711,7 @@ def pippy_activity_extra_files(): for name in files: fn = os.path.join(root, name).replace(bp + '/', '') extra[fn] = open(os.path.join(root, name), 'r').read() - extra['activity/activity-default.svg'] = PIPPY_DEFAULT_ICON + extra['activity/activity-custom.svg'] = PIPPY_CUSTOM_ICON return extra @@ -797,18 +759,20 @@ def main(): from sugar3.activity import bundlebuilder import sys - parser = OptionParser(usage='%prog [options] [title] [sourcefile]') + parser = OptionParser(usage='%prog [options] [title] [sourcefile] [icon]') parser.add_option('-d', '--dir', dest='dir', default='.', metavar='DIR', help='Put generated bundle in the specified directory.') parser.add_option('-p', '--pythonpath', dest='path', action='append', default=[], metavar='DIR', help='Append directory to python search path.') + (options, args) = parser.parse_args() - if len(args) != 2: - parser.error('The title and sourcefile arguments are required.') + if len(args) < 3: + parser.error('The title, sourcefile and icon arguments are required.') title = args[0] sourcefile = args[1] + icon_path = args[2] pytitle = re.sub(r'[^A-Za-z0-9_]', '', title) if re.match(r'[0-9]', pytitle) is not None: pytitle = '_' + pytitle # first character cannot be numeric @@ -819,12 +783,15 @@ def main(): if not sourcedir: sourcedir = '.' module, ext = os.path.splitext(basename) + f = open(icon_path, 'r') + icon = f.read() + f.close() # things we look for: bundle_info = { 'version': 1, 'extra_files': {}, 'news': 'No news.', - 'icon': PIPPY_DEFAULT_ICON, + 'icon': icon, 'class': 'activity.VteActivity', 'bundle_id': ('org.sugarlabs.pippy.%s%d' % (generate_unique_id(), -- cgit v0.9.1