Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSai Vineet <saivineet89@gmail.com>2014-03-11 17:57:52 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2014-03-13 20:13:57 (GMT)
commit0f015f654595452918bd77ba2e7ee8842bb8e365 (patch)
tree1357a4ece0d74000d76e8cbdaa6e9b1e3a7f5b0b
parentc41fd760f91fe7b91217d1aa8c5f581c4e637593 (diff)
Refactored code and divided code into sensible files
-rw-r--r--develop-activity/develop_app.py575
-rw-r--r--develop-activity/toolbars.py350
-rw-r--r--develop-activity/widgets.py272
3 files changed, 626 insertions, 571 deletions
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(
- _('<span weight="bold" size="larger">'
- 'Edit an installed activity</span>\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(
- _('<span weight="bold" size="larger">'
- 'Create a new activity</span>\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(
+ _('<span weight="bold" size="larger">'
+ 'Edit an installed activity</span>\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(
+ _('<span weight="bold" size="larger">'
+ 'Create a new activity</span>\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'