diff options
author | Sebastian <icarito@sugarlabs.org> | 2011-04-13 00:54:55 (GMT) |
---|---|---|
committer | Sebastian <icarito@sugarlabs.org> | 2011-04-13 00:54:55 (GMT) |
commit | eb6bc090d76a0acb87df7aea6292c3e2a14cc719 (patch) | |
tree | 6ed6426114d1b5937c8fb79944aebea4d06648d7 | |
parent | 1666527af2e8109f4cab35b3e0d3607f8cfcc58b (diff) |
-rw-r--r-- | ABOUT | 1 | ||||
-rw-r--r-- | about.py | 314 | ||||
-rw-r--r-- | activity.py | 5 | ||||
-rw-r--r-- | icons/activity-about.svg | 100 |
4 files changed, 420 insertions, 0 deletions
@@ -0,0 +1 @@ +The "Hello world" Activity will print out "Hello world" on the screen. It is one of the simplest Activities possible in Sugar. Therefore, it can be used as a starting point to examine the activity code and create your own Sugar activity. diff --git a/about.py b/about.py new file mode 100644 index 0000000..6dd47a8 --- /dev/null +++ b/about.py @@ -0,0 +1,314 @@ +from gettext import gettext as _ +import gtk +import gobject +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics import style +from sugar.graphics.icon import Icon +from sugar.bundle.activitybundle import ActivityBundle +from sugar.activity.activity import get_bundle_path +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.toolcombobox import ToolComboBox +from sugar.graphics.combobox import ComboBox +import os + +def get_info(): + return { 10: { 'title':_('About this activity'), + 'widget':AboutThis}, + 20: { 'title':_('How to contribute'), + 'widget':AboutHacking}, + 30: { 'title':_('License'), + 'widget':AboutLicense}, + 40: { 'title':_('Credits'), + 'widget':AboutCredits}, + } + +class AboutPanel(gtk.EventBox): + exists = False + def __init__(self, *args, **kwargs): + super(AboutPanel, self).__init__(*args, **kwargs) + + self.scrollwindow = gtk.ScrolledWindow() + self.scrollwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + self.add(self.scrollwindow) + self.scrollwindow.show() + + self.content_vbox = gtk.VBox() + self.viewport = gtk.Viewport() + self.viewport.add(self.content_vbox) + self.viewport.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color()) + self.scrollwindow.add(self.viewport) + self.show_all() + + def add_section_from_file(self, filename, title=None): + text = None + filename = os.path.join(get_bundle_path(), filename) + lang = os.environ['LANG'] + if lang.endswith("UTF-8"): + lang = lang[:-6] + + try_file = filename + "." + lang + if os.path.isfile(try_file): + filename = try_file + else: + try_file = filename + "." + lang.split("_")[0] + if os.path.isfile(try_file): + filename = try_file + + try: + fd = open(filename) + # remove 0x0c page breaks which can't be rendered in text views + text = fd.read().replace('\x0c', '') + fd.close() + except IOError: + return False + + if text: + self.add_section(text, title) + return True + + def add_section(self, text, title=None): + if title: + vbox = gtk.VBox() + separator = gtk.HSeparator() + vbox.pack_start(separator, expand=False) + separator.show() + + label= gtk.Label(title) + label.set_alignment(0, 0) + vbox.pack_start(label, expand=False) + label.show() + self.content_vbox.pack_start(vbox, expand=False) + + vbox = gtk.VBox() + vbox.set_border_width(style.DEFAULT_SPACING * 2) + + # This is a workaround in sweets + #view = gtk.Label(text) + #view.set_line_wrap(True) + #view.set_alignment(0, 0) + + # FIXME: This is the prefered method, scrolling goes crazy in sweets + view = gtk.TextView() + view.unset_flags(gtk.CAN_FOCUS) + view.set_cursor_visible(False) + view.set_editable(False) + view.get_buffer().set_text(text) + view.set_wrap_mode(gtk.WRAP_WORD) + + vbox.pack_start(view) + self.content_vbox.pack_start(vbox, expand=False, fill=False) + view.show() + +class AboutThis(AboutPanel): + def __init__(self, *args, **kwargs): + super(AboutThis, self).__init__(*args, **kwargs) + bundle_name = ActivityBundle(get_bundle_path()).get_bundle_name() + title = _("About %s Activity" % bundle_name) + self.exists += self.add_section_from_file("ABOUT", title) + title = _('News') + self.exists += self.add_section_from_file("NEWS", title) + title = _('Read me') + self.exists += self.add_section_from_file("README", title) + if not self.exists: + self.add_section(_("There is currently no ABOUT file for this activity."), title) + +class AboutLicense(AboutPanel): + def __init__(self, *args, **kwargs): + super(AboutLicense, self).__init__(*args, **kwargs) + title = _("Know your rights") + self.exists += self.add_section_from_file("COPYING", title) + +class AboutCredits(AboutPanel): + def __init__(self, *args, **kwargs): + super(AboutCredits, self).__init__(*args, **kwargs) + title = _("Authors") + self.exists += self.add_section_from_file("AUTHORS", title) + title = _("Mantainers") + self.exists += self.add_section_from_file("MANTAINERS", title) + +class AboutHacking(AboutPanel): + def __init__(self, *args, **kwargs): + super(AboutHacking, self).__init__(*args, **kwargs) + title = _("Contributing code") + self.exists += self.add_section_from_file("HACKING", title) + self.exists += self.add_section_from_file("DEVELOPING", title) + title = _("Pending development tasks") + self.exists += self.add_section_from_file("TODO", title) + +class AboutButton(ToolButton): + def __init__(self, activity, **kwargs): + ToolButton.__init__(self, 'activity-about', **kwargs) + self.props.tooltip = _('About this activity') + self.props.accelerator = '<Ctrl>H' + self.connect('clicked', self.__about_button_clicked_cb, activity) + + def __about_button_clicked_cb(self, button, activity): + about_dialog = _AboutDialog() + about_dialog.set_transient_for(self.get_toplevel()) + about_dialog.show_all() + +class _AboutDialog(gtk.Window): + def __init__(self): + super(_AboutDialog, self).__init__() + + offset = style.GRID_CELL_SIZE + width = gtk.gdk.screen_width() - offset * 2 + height = gtk.gdk.screen_height() - offset * 2 + self.set_size_request(width, height) + self.set_border_width(style.LINE_WIDTH) + + self.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color()) + vbox = gtk.VBox() + self.add(vbox) + vbox.show() + + self.set_position(gtk.WIN_POS_CENTER_ALWAYS) + self.set_decorated(False) + self.set_resizable(False) + self.set_modal(True) + + self.toolbar = _DialogToolbar() + self.toolbar.connect('stop-clicked', self._stop_clicked_cb) + self.toolbar.connect('infopanel-changed', self._info_changed_cb) + vbox.pack_start(self.toolbar, False) + + bundle = ActivityBundle(get_bundle_path()) + icon = Icon(pixel_size=style.XLARGE_ICON_SIZE*2, file=bundle.get_icon()) + icon_button = gtk.Button() + icon_button.set_image(icon) + icon_button.unset_flags(gtk.CAN_FOCUS) + + container = gtk.VButtonBox() + container.add(icon_button) + container.set_layout(gtk.BUTTONBOX_START) + + hbox = gtk.HBox() + hbox.set_border_width(style.DEFAULT_SPACING * 2) + hbox.set_spacing(style.DEFAULT_SPACING) + hbox.pack_start(container, expand=False, fill=False) + vbox.pack_start(hbox, expand=True, fill=True) + + self.content_vbox = gtk.VBox() + self.content_vbox.set_border_width(style.DEFAULT_SPACING) + hbox.pack_start(self.content_vbox, expand=True, fill=True) + + self._notebook = gtk.Notebook() + self._notebook.set_show_tabs(False) + + self.panel = get_info() + for key in sorted(self.panel.keys()): + if self.panel[key]['widget']: + page = self.panel[key]['widget']() + if page.exists: + self._notebook.append_page(page) + self.toolbar.info_combo.append_item(key, self.panel[key]['title']) + self.toolbar.panel[key]=self.panel[key] + self.content_vbox.pack_start(self._notebook) + self.toolbar.info_combo.set_active(0) + + self.connect('realize', self._realize_cb) + + def _stop_clicked_cb(self, source): + self.destroy() + + def _info_changed_cb(self, source): + active = self.toolbar.info_combo.get_active() + self._notebook.set_current_page(active) + + def _realize_cb(self, source): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self.window.set_accept_focus(True) + +class _DialogToolbar(gtk.Toolbar): + panel = {} + __gsignals__ = { + 'stop-clicked' : (gobject.SIGNAL_RUN_LAST, None, ()), + 'infopanel-changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([])), + } + def __init__(self): + super(_DialogToolbar, self).__init__() + + self._add_separator() + icon_button = Icon(pixel_size=style.STANDARD_ICON_SIZE, + icon_name='activity-about') + #icon_button.connect('clicked', self._icon_button_clicked_cb) + self._add_widget(icon_button) + icon_button.unset_flags(gtk.CAN_FOCUS) + + self._add_separator() + self.info_combo = ComboBox() + self.info_combo.props.sensitive = True + self._info_changed_cb_id = self.info_combo.connect('changed', + self._info_changed_cb) + combotool = ToolComboBox(self.info_combo) + self.insert(combotool, -1) + combotool.show() + + #self.panel = get_info() + #for key in sorted(self.panel.keys()): + # self.info_combo.append_item(key, self.panel[key]['title']) + #self.info_combo.set_active(0) + + self._back = ToolButton('go-previous-paired') + self._back.props.sensitive = False + self._back.connect('clicked', self.__go_back_cb) + self.insert(self._back, -1) + self._back.show() + + self._forward = ToolButton('go-next-paired') + #self._forward.props.sensitive = False + self._forward.connect('clicked', self.__go_forward_cb) + self.insert(self._forward, -1) + self._forward.show() + + self._add_separator(expand=True) + + stop = ToolButton(icon_name='dialog-cancel') + stop.set_tooltip(_('Done')) + stop.connect('clicked', self._stop_clicked_cb) + self.add(stop) + + def __go_back_cb(self, widget): + active = max(0, self.info_combo.get_active() - 1) + self.info_combo.set_active(active) + self._info_changed_cb(widget) + self.__set_nav_buttons(active) + + def __go_forward_cb(self, widget): + active = min(len(self.panel.keys())-1, self.info_combo.get_active() + 1) + self.info_combo.set_active(active) + self._info_changed_cb(widget) + self.__set_nav_buttons(active) + + def __set_nav_buttons(self,active): + if active==0: + self._back.props.sensitive = False + else: + self._back.props.sensitive = True + + if active==len(self.panel.keys())-1: + self._forward.props.sensitive = False + else: + self._forward.props.sensitive = True + + def _info_changed_cb(self, widget): + self.__set_nav_buttons(widget.get_active()) + self.emit('infopanel-changed') + + def _add_separator(self, expand=False): + separator = gtk.SeparatorToolItem() + separator.set_expand(expand) + separator.set_draw(False) + self.add(separator) + + def _add_widget(self, widget): + tool_item = gtk.ToolItem() + tool_item.add(widget) + self.add(tool_item) + + def _stop_clicked_cb(self, button): + self.emit('stop-clicked') + + diff --git a/activity.py b/activity.py index 5b06254..fe3da0b 100644 --- a/activity.py +++ b/activity.py @@ -29,6 +29,7 @@ from sugar.activity.widgets import TitleEntry from sugar.activity.widgets import StopButton from sugar.activity.widgets import ShareButton from sugar.activity.widgets import KeepButton +from about import AboutButton class HelloWorldActivity(activity.Activity): """HelloWorldActivity class as specified in activity.info""" @@ -66,6 +67,10 @@ class HelloWorldActivity(activity.Activity): toolbar_box.toolbar.insert(separator, -1) separator.show() + about_button = AboutButton(self) + toolbar_box.toolbar.insert(about_button, -1) + about_button.show() + stop_button = StopButton(self) toolbar_box.toolbar.insert(stop_button, -1) stop_button.show() diff --git a/icons/activity-about.svg b/icons/activity-about.svg new file mode 100644 index 0000000..2d2c139 --- /dev/null +++ b/icons/activity-about.svg @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + height="55px" + viewBox="0 0 55 55" + width="55px" + id="svg2" + version="1.1" + inkscape:version="0.47 r22583" + sodipodi:docname="activity-about.svg"> + <metadata + id="metadata14"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs12"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 27.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="55 : 27.5 : 1" + inkscape:persp3d-origin="27.5 : 18.333333 : 1" + id="perspective16" /> + <inkscape:perspective + id="perspective2956" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + </defs> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1024" + inkscape:window-height="548" + id="namedview10" + showgrid="true" + inkscape:zoom="5.8969072" + inkscape:cx="27.5" + inkscape:cy="30.043706" + inkscape:window-x="0" + inkscape:window-y="28" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" + showguides="false"> + <inkscape:grid + type="xygrid" + id="grid2818" /> + </sodipodi:namedview> + <g + transform="translate(0,-0.125)" + id="g2920" + style="fill:none;stroke:#ffffff;stroke-opacity:1"> + <path + style="fill:none;stroke:#ffffff;stroke-width:3.50000000000000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + d="m 50,27.625 c 0,12.3431 -10.1569,22.5 -22.5,22.5 C 15.1569,50.125 5,39.9681 5,27.625 5,15.2819 15.1569,5.125 27.5,5.125 c 12.3431,0 22.5,10.1569 22.5,22.5 z" + id="path2922" /> + </g> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:#ff0000;font-family:Bitstream Vera Sans" + x="70.381355" + y="23.771187" + id="text2822"><tspan + sodipodi:role="line" + id="tspan2824" + x="70.381355" + y="23.771187" /></text> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#ffffff;font-family:Century Schoolbook L;-inkscape-font-specification:Century Schoolbook L Bold" + x="20.459162" + y="42.070423" + id="text2826" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2828" + x="20.459162" + y="42.070423">i</tspan></text> +</svg> |