From 0f015f654595452918bd77ba2e7ee8842bb8e365 Mon Sep 17 00:00:00 2001 From: Sai Vineet Date: Tue, 11 Mar 2014 17:57:52 +0000 Subject: Refactored code and divided code into sensible files --- diff --git a/develop-activity/develop_app.py b/develop-activity/develop_app.py index 2642941..b55e7a7 100644 --- a/develop-activity/develop_app.py +++ b/develop-activity/develop_app.py @@ -28,15 +28,14 @@ from sugar3.graphics.toolbarbox import ToolbarBox from sugar3.graphics.toolbarbox import ToolbarButton from sugar3.graphics.radiotoolbutton import RadioToolButton from sugar3.activity.widgets import ActivityToolbarButton -from sugar3.activity.widgets import EditToolbar + from sugar3.activity.widgets import StopButton from sugar3.activity.bundlebuilder import XOPackager, Config, Builder from sugar3.activity import activity from sugar3.graphics.toolbutton import ToolButton -from sugar3.graphics.combobox import ComboBox from sugar3.graphics.alert import ConfirmationAlert from sugar3.graphics.alert import Alert -from sugar3.graphics import iconentry, notebook +from sugar3.graphics import notebook from sugar3.graphics.icon import Icon from sugar3.graphics import style from sugar3.datastore import datastore @@ -48,8 +47,9 @@ from sugar3.activity import activityfactory import logviewer import sourceview_editor S_WHERE = sourceview_editor.S_WHERE -import new_activity from symbols_tree import SymbolsTree +from toolbars import DevelopEditToolbar, DevelopSearchToolbar +from widgets import FileViewer, WelcomePage SEARCH_ICONS = {False: {S_WHERE.selection: "search-in-selection", S_WHERE.file: "system-search", @@ -62,24 +62,6 @@ SEARCH_ICONS = {False: {S_WHERE.selection: "search-in-selection", #CAP_ICONS = {False: "use-caps", True: "ignore-caps"} #REPLACE_ICONS = {False: "replace-and-find", True: "multi-replace"} -_EXCLUDE_EXTENSIONS = ('.pyc', '.pyo', '.so', '.o', '.a', '.la', '.mo', '~', - '.xo', '.tar', '.bz2', '.zip', '.gz') -_EXCLUDE_NAMES = ['.deps', '.libs'] - -try: - activities_path = os.environ['SUGAR_ACTIVITIES_PATH'] -except KeyError: - activities_path = os.path.join(os.path.expanduser("~"), "Activities") - -class SearchOptions: - - def __init__(self, template=None, **kw): - if template: - self.__dict__ = template.__dict__.copy() - else: - self.__dict__ = {} - self.__dict__.update(kw) - class DevelopActivity(activity.Activity): """Develop Activity as specified in activity.info""" @@ -549,552 +531,3 @@ class DevelopActivity(activity.Activity): self.refresh_files() self.editor.close_page() self.remove_alert(alert) - - -class WelcomePage(Gtk.VBox): - - __gsignals__ = { - 'open-activity': (GObject.SignalFlags.RUN_FIRST, - None, - ([str])), - 'show-alert': (GObject.SignalFlags.RUN_FIRST, - None, - ([str])), - } - - def __init__(self): - Gtk.VBox.__init__(self) - - edit_label = Gtk.Label( - _('' - 'Edit an installed activity\n\n' - 'You can modify an activity, and if there are errors the ' - 'activity can stop working. If you are not sure, clone the ' - 'activity to have a backup.')) - edit_label.set_use_markup(True) - edit_label.set_line_wrap(True) - self.pack_start(edit_label, expand=False, fill=True, padding=10) - - hbox_edit = Gtk.HBox() - hbox_edit.pack_start(Gtk.Label(_('Select the activity')), True, - True, 10) - activity_name_combo = ComboBox() - self._load_activities_installed_combo(activity_name_combo) - hbox_edit.pack_start(activity_name_combo, expand=False, fill=False, - padding=10) - edit_btn = Gtk.Button(_('Start')) - edit_btn.connect('clicked', self._pick_existing_activity, - activity_name_combo) - hbox_edit.pack_start(edit_btn, expand=False, fill=False, - padding=10) - align = Gtk.Alignment.new(0.5, 0.5, 0, 0) - align.add(hbox_edit) - self.pack_start(align, expand=False, fill=False, padding=10) - - new_project_label = Gtk.Label( - _('' - 'Create a new activity\n\n' - 'You can create something new, ' - 'just select the type of project.')) - new_project_label.set_use_markup(True) - new_project_label.set_line_wrap(True) - self.pack_start(new_project_label, expand=False, fill=True, padding=10) - - hbox_create = Gtk.HBox() - hbox_create.pack_start(Gtk.Label(_('Select the type')), - expand=False, fill=False, padding=10) - project_type_combo = ComboBox() - self._load_skeletons_combo(project_type_combo) - hbox_create.pack_start(project_type_combo, expand=False, fill=False, - padding=10) - align = Gtk.Alignment.new(0.5, 0.5, 0, 0) - align.add(hbox_create) - self.pack_start(align, expand=False, fill=False, padding=10) - - hbox_name = Gtk.HBox() - hbox_name.pack_start(Gtk.Label(_('Name the activity')), True, True, 0) - activity_name_entry = Gtk.Entry() - hbox_name.pack_start(activity_name_entry, expand=True, fill=True, - padding=10) - - create_btn = Gtk.Button(_('Start')) - create_btn.connect('clicked', self._create_new_activity, - activity_name_entry, project_type_combo) - hbox_name.pack_start(create_btn, expand=True, fill=True, - padding=10) - align = Gtk.Alignment.new(0.5, 0.5, 0, 0) - align.add(hbox_name) - self.pack_start(align, expand=False, fill=False, padding=10) - - self.show_all() - - def _load_activities_installed_combo(self, activities_combo): - for dir_name in sorted(os.listdir(activities_path)): - if dir_name.endswith('.activity'): - activity_name = dir_name[:- len('.activity')] - # search the icon - info_file_name = os.path.join(activities_path, dir_name, - 'activity/activity.info') - try: - info_file = open(info_file_name, 'r') - icon_name = None - for line in info_file.readlines(): - if line.strip().startswith('icon'): - icon_name = line.split()[-1] - info_file.close() - icon_file_name = None - if icon_name is not None: - icon_file_name = os.path.join( - activities_path, dir_name, 'activity', - '%s.svg' % icon_name) - activities_combo.append_item(0, activity_name, - file_name=icon_file_name) - except: - logging.error('Error trying to read information about %s', - activity_name) - - def _load_skeletons_combo(self, skeletons_combo): - skeletons_path = os.path.join(activity.get_bundle_path(), 'skeletons') - for dir_name in sorted(os.listdir(skeletons_path)): - skeletons_combo.append_item(0, dir_name) - - def _create_new_activity(self, button, name_entry, combo_skeletons): - """create and open a new activity in working dir - """ - if name_entry.get_text() == '': - self.emit('show-alert', - _('You must type the name for the new activity')) - return - if combo_skeletons.get_active() == -1: - self.emit('show-alert', _('You must select the project type')) - return - - activity_name = name_entry.get_text().strip() - skel_iter = combo_skeletons.get_active_iter() - skeleton = combo_skeletons.get_model().get_value(skel_iter, 1) - - activity_dir = new_activity.create_activity(activity_name, - activities_path, skeleton) - self.emit('open-activity', activity_dir) - - def _pick_existing_activity(self, button, combo_activities): - if combo_activities.get_active() == -1: - self.emit('show-alert', _('You must select the activity')) - else: - selected = combo_activities.get_active_iter() - activity_name = combo_activities.get_model().get_value(selected, 1) - logging.error('Activity selected %s', activity_name) - activity_dir = os.path.join(activities_path, - "%s.activity" % activity_name) - self.emit('open-activity', activity_dir) - - -class FileViewer(Gtk.ScrolledWindow): - __gtype_name__ = 'ActivityFileViewer' - - __gsignals__ = { - 'file-selected': (GObject.SignalFlags.RUN_FIRST, - None, - ([str])), - } - - def __init__(self): - Gtk.ScrolledWindow.__init__(self) - - self.props.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC - self.props.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC - self.set_size_request(style.GRID_CELL_SIZE * 3, -1) - - self._path = None - self._initial_filename = None - - self._tree_view = Gtk.TreeView() - self._tree_view.connect('cursor-changed', self.__cursor_changed_cb) - self.add(self._tree_view) - self._tree_view.show() - - selection = self._tree_view.get_selection() - selection.connect('changed', self.__selection_changed_cb) - - cell = Gtk.CellRendererText() - self._column = Gtk.TreeViewColumn() - self._column.pack_start(cell, True) - self._column.add_attribute(cell, 'text', 0) - self._tree_view.append_column(self._column) - self._tree_view.set_search_column(0) - # map between file_path and iter - self._opened_files = {} - - def load_activity(self, path, bundle): - self._search_initial_filename(path, bundle) - self._path = path - - self._tree_view.set_model(Gtk.TreeStore(str, str)) - self._model = self._tree_view.get_model() - self._add_dir_to_model(path) - - def _add_dir_to_model(self, dir_path, parent=None): - for f in os.listdir(dir_path): - if f.endswith(_EXCLUDE_EXTENSIONS) or f in _EXCLUDE_NAMES: - continue - - full_path = os.path.join(dir_path, f) - if os.path.isdir(full_path): - new_iter = self._model.append(parent, [f, full_path]) - self._add_dir_to_model(full_path, new_iter) - else: - current_iter = self._model.append(parent, [f, full_path]) - self._opened_files[full_path] = current_iter - if full_path == self._initial_filename: - selection = self._tree_view.get_selection() - selection.select_iter(current_iter) - - def __selection_changed_cb(self, selection): - model, tree_iter = selection.get_selected() - if tree_iter is None: - file_path = None - else: - file_path = model.get_value(tree_iter, 1) - self.emit('file-selected', file_path) - - def __cursor_changed_cb(self, tree_view): - selection = tree_view.get_selection() - store, iter_ = selection.get_selected() - if iter_ is None: - # Nothing selected. This happens at startup - return - if store.iter_has_child(iter_): - path = store.get_path(iter_) - if tree_view.row_expanded(path): - tree_view.collapse_row(path) - else: - tree_view.expand_row(path, False) - - def select_by_file_path(self, file_path): - if file_path in self._opened_files: - tree_iter = self._opened_files[file_path] - tree_selection = self._tree_view.get_selection() - tree_selection.unselect_all() - tree_selection.select_iter(tree_iter) - - def _search_initial_filename(self, activity_path, bundle): - command = bundle.get_command() - - if self._is_web_activity(bundle): - file_name = 'index.html' - - elif len(command.split(' ')) > 1: - name = command.split(' ')[1].split('.')[-1] - tmppath = command.split(' ')[1].replace('.', '/') - file_name = tmppath[0:-(len(name) + 1)] + '.py' - else: - file_name = command - - if file_name: - path = os.path.join(activity_path, file_name) - if os.path.exists(path): - logging.error('INITIAL_FILENAME %s', path) - self._initial_filename = path - self.emit('file-selected', path) - - def set_title(self, title): - self._column.set_title(title) - - def _is_web_activity(self, activity_bundle): - return activity_bundle.get_command() == 'sugar-activity-web' - - -class DevelopEditToolbar(EditToolbar): - - def __init__(self, _activity): - EditToolbar.__init__(self) - - self._activity = _activity - self._activity.editor.connect('changed', self._changed_cb) - self._changed_cb(None) - - self.undo.connect('clicked', self._undo_cb) - self.redo.connect('clicked', self._redo_cb) - self.copy.connect('clicked', self._copy_cb) - self.paste.connect('clicked', self._paste_cb) - - def _changed_cb(self, _buffer): - can_undo, can_redo = self._activity.editor.can_undo_redo() - self.undo.set_sensitive(can_undo) - self.redo.set_sensitive(can_redo) - - def _undo_cb(self, button): - self._activity.editor.undo() - self._changed_cb(None) - - def _redo_cb(self, button): - self._activity.editor.redo() - self._changed_cb(None) - - def _copy_cb(self, button): - self._activity.editor.copy() - - def _paste_cb(self, button): - self._activity.editor.paste() - - -class DevelopSearchToolbar(Gtk.Toolbar): - - def __init__(self, _activity): - GObject.GObject.__init__(self) - - self._activity = _activity - - # setup the search options - self.s_opts = SearchOptions( - where=S_WHERE.multifile, - use_regex=False, - ignore_caps=True, - replace_all=False, - #defaults to avoid creating - #a new SearchOptions object for normal searches - #should never be changed, just make a copy like: - #SearchOptions(self.s_opts, forward=False) - forward=True, - stay=False) - - self.safe_to_replace = False - - self._search_entry = iconentry.IconEntry() - self._search_entry.set_icon_from_name( - iconentry.ICON_ENTRY_PRIMARY, - SEARCH_ICONS[self.s_opts.use_regex][self.s_opts.where]) - - self._search_entry.add_clear_button() - self._search_entry.connect('activate', self._search_entry_activated_cb) - self._search_entry.connect('changed', self._search_entry_changed_cb) - self._add_widget(self._search_entry, expand=True) - - self._findprev = ToolButton('go-previous') - self._findprev.set_tooltip(_('Find previous')) - self.insert(self._findprev, -1) - self._findprev.show() - self._findprev.connect('clicked', self._findprev_cb) - - self._findnext = ToolButton('go-next') - self._findnext.set_tooltip(_('Find next')) - self.insert(self._findnext, -1) - self._findnext.show() - self._findnext.connect('clicked', self._findnext_cb) - - """ - self._settings = ToolButton(CAP_ICONS[self.s_opts.ignore_caps]) - self._settings.set_tooltip(_('Search settings')) - self.insert(self._settings, -1) - self._settings.show() - self._settings.connect('clicked', self._settings_cb) - - # Search settings menu - # This menu should attach to something else beside findnext - - #location is temporary. - palette = self._settings.get_palette() - sswo = self._set_where_options - ssho = self._set_how_options - ssco = self._set_cap_options - #TODO: move data structure to a member and the logic to a function - for name, function, options, icon in ( - (_('Ignore capitalization'), ssco, True, "ignore-caps"), - (_('Match capitalization'), ssco, False, "use-caps"), - (None, None, None, None), - (_('Search in selection'), sswo, S_WHERE.selection, - "search-in-selection"), - (_('Search in current file'), sswo, S_WHERE.file, - "system-search"), - (_('Search in all open files'), sswo, S_WHERE.multifile, - "multi-search"), - (None, None, None, None), - (_('Simple search'), ssho, False, "system-search"), - (_('Advanced search'), ssho, True, "regex"), - ): - if not name: - menuitem = Gtk.SeparatorMenuItem() - else: - menuitem = MenuItem(name, icon) - menuitem.connect('activate', function, options) - palette.menu.append(menuitem) - menuitem.show() - - # make expanded non-drawn visible separator to make the replace - #stuff right-align - separator = Gtk.SeparatorToolItem() - separator.props.draw = False - separator.set_expand(True) - self.insert(separator, -1) - separator.show() - - # replace entry - self._replace_entry = iconentry.IconEntry() - self._replace_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY, - 'system-replace') - self._replace_entry.connect('changed', self._replace_entry_changed_cb) - self._replace_entry.add_clear_button() - self._add_widget(self._replace_entry, expand=True) - - #replace button - self._replace_button = ToolButton(REPLACE_ICONS[ - self.s_opts.replace_all]) - self._replace_button.set_tooltip(_('Replace')) - self.insert(self._replace_button, -1) - self._replace_button.show() - self._replace_button.connect('clicked', self._replace_cb) - - palette = self._replace_button.get_palette() - ssro = self._set_replace_options - #TODO: move data structure to a member and the logic to a function - for name, function, options, icon in ( - (_('Replace one'), ssro, False, "replace-and-find"), - (_('Replace all'), ssro, True, "multi-replace"), - ): - if not name: - menuitem = Gtk.SeparatorMenuItem() - else: - menuitem = MenuItem(name, icon) - menuitem.connect('activate', function, options) - palette.menu.append(menuitem) - menuitem.show() - """ - - self._activity.editor.connect('changed', self._changed_cb) - - self._activity.connect('key_press_event', self._on_key_press_event) - - def _on_key_press_event(self, widget, event): - keyname = Gdk.keyval_name(event.keyval) - if "F5" <= keyname and keyname <= "F8": - if keyname == "F5": - self._go_to_search_entry_cb() - elif keyname == "F6": - self._findprev_cb() - elif keyname == "F7": - self._findnext_cb() - elif keyname == "F8": - self._replace_or_go_to_replace_entry_cb() - return True - - def _go_to_search_entry_cb(self): - entry = self._search_entry - text = self._activity.editor.get_selected() - entry.grab_focus() - if text: - entry.delete_text(0, -1) - entry.insert_text(text) - entry.select_region(0, -1) - else: - entry.delete_text(0, 0) - entry.set_position(-1) - #for some reason, grab_focus doesn't work otherwise - - def _replace_or_go_to_replace_entry_cb(self): - if self.safe_to_replace: - self._replace_cb() - else: - self._replace_entry.select_region(0, -1) - self._replace_entry.grab_focus() - - def _reset_search_icons(self): - self._search_entry.set_icon_from_name( - iconentry.ICON_ENTRY_PRIMARY, - SEARCH_ICONS[self.s_opts.use_regex][self.s_opts.where]) - #self._settings.set_icon(CAP_ICONS[self.s_opts.ignore_caps]) - #self._replace_button.set_icon(REPLACE_ICONS[self.s_opts.replace_all]) - self._reset_replace_sensitivity() - - def _reset_replace_sensitivity(self): - pass - """ - self._replace_button.set_sensitive( - self.s_opts.where == S_WHERE.selection or self.s_opts.replace_all) - """ - - def _set_where_options(self, menu, option): - self.s_opts.where = option # IGNORE:W0201 - self._reset_search_icons() - - def _set_how_options(self, menu, option): - self.s_opts.use_regex = option # IGNORE:W0201 - self._reset_search_icons() - - def _set_cap_options(self, menu, option): - self.s_opts.ignore_caps = option # IGNORE:W0201 - self._reset_search_icons() - - def _set_replace_options(self, menu, option): - self.s_opts.replace_all = option # IGNORE:W0201 - if option and self.s_opts.where == S_WHERE.multifile: - self.s_opts.where = S_WHERE.file # for safety: - #do not replace all in multifile except explicitly - self._reset_search_icons() - - def _changed_cb(self, _buffer): - self._reset_replace_sensitivity() - #if self.s_opts.where == S_WHERE.selection: - # self._set_where_options(None, S_WHERE.file) - - def _settings_cb(self, button): - self._set_cap_options(None, not self.s_opts.ignore_caps) - - def _replace_cb(self, button=None): - pass - """ - ftext = self._search_entry.props.text - rtext = self._replace_entry.props.text - __replaced, found = self._activity.editor.replace(ftext, rtext, - self.s_opts) - if found: - self._replace_button.set_sensitive(True) - """ - - def _search_entry_activated_cb(self, entry): - text = self._search_entry.props.text - if text: - self._findnext_cb(None) - - def _search_entry_changed_cb(self, entry): - self.safe_to_replace = False - text = self._search_entry.props.text - if not text: - self._findprev.set_sensitive(False) - self._findnext.set_sensitive(False) - else: - self._findprev.set_sensitive(True) - self._findnext.set_sensitive(True) - if not self.s_opts.use_regex: - #do not do partial searches for regex - if self._activity.editor.find_next(text): - #no multifile, or focus gets grabbed - pass - #self._replace_button.set_sensitive(True) - - def _replace_entry_changed_cb(self, entry): - if self._replace_entry.props.text: - self.safe_to_replace = True - - def _findprev_cb(self, button=None): - ftext = self._search_entry.props.text - if ftext: - if self._activity.editor.find_next(ftext, direction='backward'): - pass - #self._replace_button.set_sensitive(True) - - def _findnext_cb(self, button=None): - ftext = self._search_entry.props.text - if ftext: - if self._activity.editor.find_next(ftext, direction='forward'): - pass - #self._replace_button.set_sensitive(True) - - # bad paul! this function was copied from sugar's activity.py via Write - def _add_widget(self, widget, expand=False): - tool_item = Gtk.ToolItem() - tool_item.set_expand(expand) - - tool_item.add(widget) - widget.show() - - self.insert(tool_item, -1) - tool_item.show() diff --git a/develop-activity/toolbars.py b/develop-activity/toolbars.py new file mode 100644 index 0000000..7bee272 --- /dev/null +++ b/develop-activity/toolbars.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2006, Red Hat, Inc. +# Copyright (C) 2011, One Laptop Per Child +# Copyright (C) 2009, Tomeu Vizoso, Simon Schampijer +# +# 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. +# +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +from gi.repository import Gtk, Gdk +from gi.repository import GObject +from gettext import gettext as _ + +from sugar3.activity.widgets import EditToolbar +from sugar3.graphics import iconentry +from sugar3.graphics.toolbutton import ToolButton + +import sourceview_editor +S_WHERE = sourceview_editor.S_WHERE +SEARCH_ICONS = {False: {S_WHERE.selection: "search-in-selection", + S_WHERE.file: "system-search", + S_WHERE.multifile: "multi-search", + }, + True: {S_WHERE.selection: "regex-in-selection", + S_WHERE.file: "regex", + S_WHERE.multifile: "multi-regex", + }} + + +class DevelopViewToolbar(Gtk.Toolbar): + + def __init__(self, _activity): + self._activity = _activity + + # theme_toggler = ToggleToolButton() + + +class DevelopEditToolbar(EditToolbar): + + def __init__(self, _activity): + EditToolbar.__init__(self) + + self._activity = _activity + self._activity.editor.connect('changed', self._changed_cb) + self._changed_cb(None) + + self.undo.connect('clicked', self._undo_cb) + self.redo.connect('clicked', self._redo_cb) + self.copy.connect('clicked', self._copy_cb) + self.paste.connect('clicked', self._paste_cb) + + def _changed_cb(self, _buffer): + can_undo, can_redo = self._activity.editor.can_undo_redo() + self.undo.set_sensitive(can_undo) + self.redo.set_sensitive(can_redo) + + def _undo_cb(self, button): + self._activity.editor.undo() + self._changed_cb(None) + + def _redo_cb(self, button): + self._activity.editor.redo() + self._changed_cb(None) + + def _copy_cb(self, button): + self._activity.editor.copy() + + def _paste_cb(self, button): + self._activity.editor.paste() + + +class SearchOptions: + + def __init__(self, template=None, **kw): + if template: + self.__dict__ = template.__dict__.copy() + else: + self.__dict__ = {} + self.__dict__.update(kw) + + +class DevelopSearchToolbar(Gtk.Toolbar): + + def __init__(self, _activity): + GObject.GObject.__init__(self) + + self._activity = _activity + + # setup the search options + self.s_opts = SearchOptions( + where=S_WHERE.multifile, + use_regex=False, + ignore_caps=True, + replace_all=False, + #defaults to avoid creating + #a new SearchOptions object for normal searches + #should never be changed, just make a copy like: + #SearchOptions(self.s_opts, forward=False) + forward=True, + stay=False) + + self.safe_to_replace = False + + self._search_entry = iconentry.IconEntry() + self._search_entry.set_icon_from_name( + iconentry.ICON_ENTRY_PRIMARY, + SEARCH_ICONS[self.s_opts.use_regex][self.s_opts.where]) + + self._search_entry.add_clear_button() + self._search_entry.connect('activate', self._search_entry_activated_cb) + self._search_entry.connect('changed', self._search_entry_changed_cb) + self._add_widget(self._search_entry, expand=True) + + self._findprev = ToolButton('go-previous') + self._findprev.set_tooltip(_('Find previous')) + self.insert(self._findprev, -1) + self._findprev.show() + self._findprev.connect('clicked', self._findprev_cb) + + self._findnext = ToolButton('go-next') + self._findnext.set_tooltip(_('Find next')) + self.insert(self._findnext, -1) + self._findnext.show() + self._findnext.connect('clicked', self._findnext_cb) + + """ + self._settings = ToolButton(CAP_ICONS[self.s_opts.ignore_caps]) + self._settings.set_tooltip(_('Search settings')) + self.insert(self._settings, -1) + self._settings.show() + self._settings.connect('clicked', self._settings_cb) + + # Search settings menu + # This menu should attach to something else beside findnext - + #location is temporary. + palette = self._settings.get_palette() + sswo = self._set_where_options + ssho = self._set_how_options + ssco = self._set_cap_options + #TODO: move data structure to a member and the logic to a function + for name, function, options, icon in ( + (_('Ignore capitalization'), ssco, True, "ignore-caps"), + (_('Match capitalization'), ssco, False, "use-caps"), + (None, None, None, None), + (_('Search in selection'), sswo, S_WHERE.selection, + "search-in-selection"), + (_('Search in current file'), sswo, S_WHERE.file, + "system-search"), + (_('Search in all open files'), sswo, S_WHERE.multifile, + "multi-search"), + (None, None, None, None), + (_('Simple search'), ssho, False, "system-search"), + (_('Advanced search'), ssho, True, "regex"), + ): + if not name: + menuitem = Gtk.SeparatorMenuItem() + else: + menuitem = MenuItem(name, icon) + menuitem.connect('activate', function, options) + palette.menu.append(menuitem) + menuitem.show() + + # make expanded non-drawn visible separator to make the replace + #stuff right-align + separator = Gtk.SeparatorToolItem() + separator.props.draw = False + separator.set_expand(True) + self.insert(separator, -1) + separator.show() + + # replace entry + self._replace_entry = iconentry.IconEntry() + self._replace_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY, + 'system-replace') + self._replace_entry.connect('changed', self._replace_entry_changed_cb) + self._replace_entry.add_clear_button() + self._add_widget(self._replace_entry, expand=True) + + #replace button + self._replace_button = ToolButton(REPLACE_ICONS[ + self.s_opts.replace_all]) + self._replace_button.set_tooltip(_('Replace')) + self.insert(self._replace_button, -1) + self._replace_button.show() + self._replace_button.connect('clicked', self._replace_cb) + + palette = self._replace_button.get_palette() + ssro = self._set_replace_options + #TODO: move data structure to a member and the logic to a function + for name, function, options, icon in ( + (_('Replace one'), ssro, False, "replace-and-find"), + (_('Replace all'), ssro, True, "multi-replace"), + ): + if not name: + menuitem = Gtk.SeparatorMenuItem() + else: + menuitem = MenuItem(name, icon) + menuitem.connect('activate', function, options) + palette.menu.append(menuitem) + menuitem.show() + """ + + self._activity.editor.connect('changed', self._changed_cb) + + self._activity.connect('key_press_event', self._on_key_press_event) + + def _on_key_press_event(self, widget, event): + keyname = Gdk.keyval_name(event.keyval) + if "F5" <= keyname and keyname <= "F8": + if keyname == "F5": + self._go_to_search_entry_cb() + elif keyname == "F6": + self._findprev_cb() + elif keyname == "F7": + self._findnext_cb() + elif keyname == "F8": + self._replace_or_go_to_replace_entry_cb() + return True + + def _go_to_search_entry_cb(self): + entry = self._search_entry + text = self._activity.editor.get_selected() + entry.grab_focus() + if text: + entry.delete_text(0, -1) + entry.insert_text(text) + entry.select_region(0, -1) + else: + entry.delete_text(0, 0) + entry.set_position(-1) + #for some reason, grab_focus doesn't work otherwise + + def _replace_or_go_to_replace_entry_cb(self): + if self.safe_to_replace: + self._replace_cb() + else: + self._replace_entry.select_region(0, -1) + self._replace_entry.grab_focus() + + def _reset_search_icons(self): + self._search_entry.set_icon_from_name( + iconentry.ICON_ENTRY_PRIMARY, + SEARCH_ICONS[self.s_opts.use_regex][self.s_opts.where]) + #self._settings.set_icon(CAP_ICONS[self.s_opts.ignore_caps]) + #self._replace_button.set_icon(REPLACE_ICONS[self.s_opts.replace_all]) + self._reset_replace_sensitivity() + + def _reset_replace_sensitivity(self): + pass + """ + self._replace_button.set_sensitive( + self.s_opts.where == S_WHERE.selection or self.s_opts.replace_all) + """ + + def _set_where_options(self, menu, option): + self.s_opts.where = option # IGNORE:W0201 + self._reset_search_icons() + + def _set_how_options(self, menu, option): + self.s_opts.use_regex = option # IGNORE:W0201 + self._reset_search_icons() + + def _set_cap_options(self, menu, option): + self.s_opts.ignore_caps = option # IGNORE:W0201 + self._reset_search_icons() + + def _set_replace_options(self, menu, option): + self.s_opts.replace_all = option # IGNORE:W0201 + if option and self.s_opts.where == S_WHERE.multifile: + self.s_opts.where = S_WHERE.file # for safety: + #do not replace all in multifile except explicitly + self._reset_search_icons() + + def _changed_cb(self, _buffer): + self._reset_replace_sensitivity() + #if self.s_opts.where == S_WHERE.selection: + # self._set_where_options(None, S_WHERE.file) + + def _settings_cb(self, button): + self._set_cap_options(None, not self.s_opts.ignore_caps) + + def _replace_cb(self, button=None): + pass + """ + ftext = self._search_entry.props.text + rtext = self._replace_entry.props.text + __replaced, found = self._activity.editor.replace(ftext, rtext, + self.s_opts) + if found: + self._replace_button.set_sensitive(True) + """ + + def _search_entry_activated_cb(self, entry): + text = self._search_entry.props.text + if text: + self._findnext_cb(None) + + def _search_entry_changed_cb(self, entry): + self.safe_to_replace = False + text = self._search_entry.props.text + if not text: + self._findprev.set_sensitive(False) + self._findnext.set_sensitive(False) + else: + self._findprev.set_sensitive(True) + self._findnext.set_sensitive(True) + if not self.s_opts.use_regex: + #do not do partial searches for regex + if self._activity.editor.find_next(text): + #no multifile, or focus gets grabbed + pass + #self._replace_button.set_sensitive(True) + + def _replace_entry_changed_cb(self, entry): + if self._replace_entry.props.text: + self.safe_to_replace = True + + def _findprev_cb(self, button=None): + ftext = self._search_entry.props.text + if ftext: + if self._activity.editor.find_next(ftext, direction='backward'): + pass + #self._replace_button.set_sensitive(True) + + def _findnext_cb(self, button=None): + ftext = self._search_entry.props.text + if ftext: + if self._activity.editor.find_next(ftext, direction='forward'): + pass + #self._replace_button.set_sensitive(True) + + # bad paul! this function was copied from sugar's activity.py via Write + def _add_widget(self, widget, expand=False): + tool_item = Gtk.ToolItem() + tool_item.set_expand(expand) + + tool_item.add(widget) + widget.show() + + self.insert(tool_item, -1) + tool_item.show() diff --git a/develop-activity/widgets.py b/develop-activity/widgets.py index 27382c4..a84c015 100644 --- a/develop-activity/widgets.py +++ b/develop-activity/widgets.py @@ -17,11 +17,30 @@ # 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 logging +import os +import os.path +from gettext import gettext as _ + from gi.repository import Gtk from gi.repository import GObject +from sugar3.activity import activity +from sugar3.graphics import style +from sugar3.graphics.combobox import ComboBox from sugar3.graphics.icon import Icon +import new_activity + +_EXCLUDE_EXTENSIONS = ('.pyc', '.pyo', '.so', '.o', '.a', '.la', '.mo', '~', + '.xo', '.tar', '.bz2', '.zip', '.gz') +_EXCLUDE_NAMES = ['.deps', '.libs'] + +try: + activities_path = os.environ['SUGAR_ACTIVITIES_PATH'] +except KeyError: + activities_path = os.path.join(os.path.expanduser("~"), "Activities") + class TabLabel(Gtk.HBox): __gtype_name__ = 'BrowseTabLabel' @@ -72,3 +91,256 @@ class TabLabel(Gtk.HBox): def __button_clicked_cb(self, button): self.emit('tab-close', self._child) + + +class WelcomePage(Gtk.VBox): + + __gsignals__ = { + 'open-activity': (GObject.SignalFlags.RUN_FIRST, + None, + ([str])), + 'show-alert': (GObject.SignalFlags.RUN_FIRST, + None, + ([str])), + } + + def __init__(self): + Gtk.VBox.__init__(self) + + edit_label = Gtk.Label( + _('' + 'Edit an installed activity\n\n' + 'You can modify an activity, and if there are errors the ' + 'activity can stop working. If you are not sure, clone the ' + 'activity to have a backup.')) + edit_label.set_use_markup(True) + edit_label.set_line_wrap(True) + self.pack_start(edit_label, expand=False, fill=True, padding=10) + + hbox_edit = Gtk.HBox() + hbox_edit.pack_start(Gtk.Label(_('Select the activity')), True, + True, 10) + activity_name_combo = ComboBox() + self._load_activities_installed_combo(activity_name_combo) + hbox_edit.pack_start(activity_name_combo, expand=False, fill=False, + padding=10) + edit_btn = Gtk.Button(_('Start')) + edit_btn.connect('clicked', self._pick_existing_activity, + activity_name_combo) + hbox_edit.pack_start(edit_btn, expand=False, fill=False, + padding=10) + align = Gtk.Alignment.new(0.5, 0.5, 0, 0) + align.add(hbox_edit) + self.pack_start(align, expand=False, fill=False, padding=10) + + new_project_label = Gtk.Label( + _('' + 'Create a new activity\n\n' + 'You can create something new, ' + 'just select the type of project.')) + new_project_label.set_use_markup(True) + new_project_label.set_line_wrap(True) + self.pack_start(new_project_label, expand=False, fill=True, padding=10) + + hbox_create = Gtk.HBox() + hbox_create.pack_start(Gtk.Label(_('Select the type')), + expand=False, fill=False, padding=10) + project_type_combo = ComboBox() + self._load_skeletons_combo(project_type_combo) + hbox_create.pack_start(project_type_combo, expand=False, fill=False, + padding=10) + align = Gtk.Alignment.new(0.5, 0.5, 0, 0) + align.add(hbox_create) + self.pack_start(align, expand=False, fill=False, padding=10) + + hbox_name = Gtk.HBox() + hbox_name.pack_start(Gtk.Label(_('Name the activity')), True, True, 0) + activity_name_entry = Gtk.Entry() + hbox_name.pack_start(activity_name_entry, expand=True, fill=True, + padding=10) + + create_btn = Gtk.Button(_('Start')) + create_btn.connect('clicked', self._create_new_activity, + activity_name_entry, project_type_combo) + hbox_name.pack_start(create_btn, expand=True, fill=True, + padding=10) + align = Gtk.Alignment.new(0.5, 0.5, 0, 0) + align.add(hbox_name) + self.pack_start(align, expand=False, fill=False, padding=10) + + self.show_all() + + def _load_activities_installed_combo(self, activities_combo): + for dir_name in sorted(os.listdir(activities_path)): + if dir_name.endswith('.activity'): + activity_name = dir_name[:- len('.activity')] + # search the icon + info_file_name = os.path.join(activities_path, dir_name, + 'activity/activity.info') + try: + info_file = open(info_file_name, 'r') + icon_name = None + for line in info_file.readlines(): + if line.strip().startswith('icon'): + icon_name = line.split()[-1] + info_file.close() + icon_file_name = None + if icon_name is not None: + icon_file_name = os.path.join( + activities_path, dir_name, 'activity', + '%s.svg' % icon_name) + activities_combo.append_item(0, activity_name, + file_name=icon_file_name) + except: + logging.error('Error trying to read information about %s', + activity_name) + + def _load_skeletons_combo(self, skeletons_combo): + skeletons_path = os.path.join(activity.get_bundle_path(), 'skeletons') + for dir_name in sorted(os.listdir(skeletons_path)): + skeletons_combo.append_item(0, dir_name) + + def _create_new_activity(self, button, name_entry, combo_skeletons): + """create and open a new activity in working dir + """ + if name_entry.get_text() == '': + self.emit('show-alert', + _('You must type the name for the new activity')) + return + if combo_skeletons.get_active() == -1: + self.emit('show-alert', _('You must select the project type')) + return + + activity_name = name_entry.get_text().strip() + skel_iter = combo_skeletons.get_active_iter() + skeleton = combo_skeletons.get_model().get_value(skel_iter, 1) + + activity_dir = new_activity.create_activity(activity_name, + activities_path, skeleton) + self.emit('open-activity', activity_dir) + + def _pick_existing_activity(self, button, combo_activities): + if combo_activities.get_active() == -1: + self.emit('show-alert', _('You must select the activity')) + else: + selected = combo_activities.get_active_iter() + activity_name = combo_activities.get_model().get_value(selected, 1) + logging.error('Activity selected %s', activity_name) + activity_dir = os.path.join(activities_path, + "%s.activity" % activity_name) + self.emit('open-activity', activity_dir) + + +class FileViewer(Gtk.ScrolledWindow): + __gtype_name__ = 'ActivityFileViewer' + + __gsignals__ = { + 'file-selected': (GObject.SignalFlags.RUN_FIRST, + None, + ([str])), + } + + def __init__(self): + Gtk.ScrolledWindow.__init__(self) + + self.props.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC + self.props.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC + self.set_size_request(style.GRID_CELL_SIZE * 3, -1) + + self._path = None + self._initial_filename = None + + self._tree_view = Gtk.TreeView() + self._tree_view.connect('cursor-changed', self.__cursor_changed_cb) + self.add(self._tree_view) + self._tree_view.show() + + selection = self._tree_view.get_selection() + selection.connect('changed', self.__selection_changed_cb) + + cell = Gtk.CellRendererText() + self._column = Gtk.TreeViewColumn() + self._column.pack_start(cell, True) + self._column.add_attribute(cell, 'text', 0) + self._tree_view.append_column(self._column) + self._tree_view.set_search_column(0) + # map between file_path and iter + self._opened_files = {} + + def load_activity(self, path, bundle): + self._search_initial_filename(path, bundle) + self._path = path + + self._tree_view.set_model(Gtk.TreeStore(str, str)) + self._model = self._tree_view.get_model() + self._add_dir_to_model(path) + + def _add_dir_to_model(self, dir_path, parent=None): + for f in os.listdir(dir_path): + if f.endswith(_EXCLUDE_EXTENSIONS) or f in _EXCLUDE_NAMES: + continue + + full_path = os.path.join(dir_path, f) + if os.path.isdir(full_path): + new_iter = self._model.append(parent, [f, full_path]) + self._add_dir_to_model(full_path, new_iter) + else: + current_iter = self._model.append(parent, [f, full_path]) + self._opened_files[full_path] = current_iter + if full_path == self._initial_filename: + selection = self._tree_view.get_selection() + selection.select_iter(current_iter) + + def __selection_changed_cb(self, selection): + model, tree_iter = selection.get_selected() + if tree_iter is None: + file_path = None + else: + file_path = model.get_value(tree_iter, 1) + self.emit('file-selected', file_path) + + def __cursor_changed_cb(self, tree_view): + selection = tree_view.get_selection() + store, iter_ = selection.get_selected() + if iter_ is None: + # Nothing selected. This happens at startup + return + if store.iter_has_child(iter_): + path = store.get_path(iter_) + if tree_view.row_expanded(path): + tree_view.collapse_row(path) + else: + tree_view.expand_row(path, False) + + def select_by_file_path(self, file_path): + if file_path in self._opened_files: + tree_iter = self._opened_files[file_path] + tree_selection = self._tree_view.get_selection() + tree_selection.unselect_all() + tree_selection.select_iter(tree_iter) + + def _search_initial_filename(self, activity_path, bundle): + command = bundle.get_command() + + if self._is_web_activity(bundle): + file_name = 'index.html' + + elif len(command.split(' ')) > 1: + name = command.split(' ')[1].split('.')[-1] + tmppath = command.split(' ')[1].replace('.', '/') + file_name = tmppath[0:-(len(name) + 1)] + '.py' + else: + file_name = command + + if file_name: + path = os.path.join(activity_path, file_name) + if os.path.exists(path): + logging.error('INITIAL_FILENAME %s', path) + self._initial_filename = path + self.emit('file-selected', path) + + def set_title(self, title): + self._column.set_title(title) + + def _is_web_activity(self, activity_bundle): + return activity_bundle.get_command() == 'sugar-activity-web' -- cgit v0.9.1