diff options
author | Walter Bender <walter.bender@gmail.com> | 2013-03-12 15:20:00 (GMT) |
---|---|---|
committer | Walter Bender <walter.bender@gmail.com> | 2013-03-12 15:20:00 (GMT) |
commit | 7d88e037f8b30b6703a2e6ea5b6a4e64e4bb5804 (patch) | |
tree | dad825c4dc2b97a8a4df86b65176d28eb8ea8dab | |
parent | 022db68254762057dcf5b7aa8f4fe32d7791e558 (diff) |
backport changes from gtk3 version
-rw-r--r-- | PortfolioActivity.py | 671 |
1 files changed, 422 insertions, 249 deletions
diff --git a/PortfolioActivity.py b/PortfolioActivity.py index e83cf95..5cccee5 100644 --- a/PortfolioActivity.py +++ b/PortfolioActivity.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#Copyright (c) 2011, 2012 Walter Bender +#Copyright (c) 2011-2013 Walter Bender # 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 @@ -43,12 +43,13 @@ from sugar.graphics.alert import Alert from sprites import Sprites, Sprite from exportpdf import save_pdf -from utils import get_path, lighter_color, svg_str_to_pixbuf, svg_rectangle, \ - play_audio_from_file, get_pixbuf_from_journal, genblank, get_hardware, \ - pixbuf_to_base64, base64_to_pixbuf, get_pixbuf_from_file - -from toolbar_utils import radio_factory, button_factory, separator_factory, \ - combo_factory, label_factory +from utils import (get_path, lighter_color, svg_str_to_pixbuf, svg_rectangle, + play_audio_from_file, get_pixbuf_from_journal, genblank, + get_hardware, pixbuf_to_base64, base64_to_pixbuf, + get_pixbuf_from_file, parse_comments, get_tablet_mode) + +from toolbar_utils import (radio_factory, button_factory, separator_factory, + combo_factory, label_factory) from grecord import Grecord from gettext import gettext as _ @@ -56,11 +57,8 @@ from gettext import gettext as _ import logging _logger = logging.getLogger("portfolio-activity") -try: - from sugar.graphics import style - GRID_CELL_SIZE = style.GRID_CELL_SIZE -except ImportError: - GRID_CELL_SIZE = 0 +from sugar.graphics import style +GRID_CELL_SIZE = style.GRID_CELL_SIZE try: _OLD_SUGAR_SYSTEM = False @@ -88,11 +86,16 @@ IFACE = SERVICE PATH = '/org/sugarlabs/PortfolioActivity' # Size and position of title, preview image, and description -PREVIEWW = 600 -PREVIEWH = 450 -PREVIEWY = 80 -TITLEH = 60 -DESCRIPTIONY = 550 +TITLE = [[GRID_CELL_SIZE, 10, 1200 - GRID_CELL_SIZE * 2, 100], + [GRID_CELL_SIZE, 10, 900 - GRID_CELL_SIZE * 2, 100]] +PREVIEW = [[GRID_CELL_SIZE, 110, 560, 420], + [180, 110, 560, 420]] +DESC = [[560 + GRID_CELL_SIZE, 110, 560, 420], + [GRID_CELL_SIZE, 530, 900 - GRID_CELL_SIZE * 2, 300]] +NEW_COMMENT = [[GRID_CELL_SIZE, 530, 1200 - GRID_CELL_SIZE * 2, 100], + [GRID_CELL_SIZE, 840, 900 - GRID_CELL_SIZE * 2, 100]] +COMMENTS = [[GRID_CELL_SIZE, 640, 1200 - GRID_CELL_SIZE * 2, 250], + [GRID_CELL_SIZE, 950, 900 - GRID_CELL_SIZE * 2, 240]] TWO = 0 TEN = 1 @@ -103,10 +106,6 @@ UNIT_DICTIONARY = {TWO: (UNITS[TWO], 2), TEN: (UNITS[TEN], 10), THIRTY: (UNITS[THIRTY], 30), SIXTY: (UNITS[SIXTY], 60)} -XO1 = 'xo1' -XO15 = 'xo1.5' -XO175 = 'xo1.75' -UNKNOWN = 'unknown' # sprite layers DRAG = 6 @@ -117,6 +116,8 @@ MIDDLE = 2 BOTTOM = 1 HIDE = 0 +OSK_SHIFT = 200 + def _get_screen_dpi(): ''' Return screen DPI ''' @@ -128,7 +129,7 @@ def _get_screen_dpi(): class Slide(): ''' A container for a slide ''' - def __init__(self, owner, uid, colors, title, preview, desc): + def __init__(self, owner, uid, colors, title, preview, desc, comment): self.active = True self.owner = owner self.uid = uid @@ -137,6 +138,7 @@ class Slide(): self.preview = preview self.preview2 = None # larger version for fullscreen mode self.description = desc + self.comment = comment # A list of dictionaries self.sound = None self.dirty = False self.fav = True @@ -160,25 +162,15 @@ class PortfolioActivity(activity.Activity): self.datapath = get_path(activity, 'instance') self._buddies = [profile.get_nick_name()] self._colors = profile.get_color().to_string().split(',') + self._my_colors = self._colors[:] # Save original colors self.initiating = None # sharing (True) or joining (False) - - self._set_screen_dpi() - + self._tablet_mode = get_tablet_mode() self._playing = False self._first_time = True - self._width = gtk.gdk.screen_width() - self._height = gtk.gdk.screen_height() - self._scale = gtk.gdk.screen_height() / 900. - - self._titlewh = [self._width, TITLEH * self._scale] - self._titlexy = [0, 0] - self._previewwh = [PREVIEWW * self._scale, PREVIEWH * self._scale] - self._previewxy = [(self._width - self._previewwh[0]) / 2, - PREVIEWY * self._scale] - self._descriptionwh = [self._width, - self._height - DESCRIPTIONY * self._scale - 55] - self._descriptionxy = [0, DESCRIPTIONY * self._scale] + self._set_scale_and_orientation() + self._set_screen_dpi() + self._set_xy_wh() if hasattr(self, 'get_window') and \ hasattr(self.get_window(), 'get_cursor'): @@ -204,20 +196,73 @@ class PortfolioActivity(activity.Activity): self._keypress = None self._selected_spr = None - self._dead_key = '' self._saved_string = '' self._startpos = [0, 0] self._dragpos = [0, 0] self._setup_presence_service() + def _set_xy_wh(self): + orientation = self._orientation + self._title_wh = [TITLE[orientation][2] * self._scale, + TITLE[orientation][3] * self._scale] + self._title_xy = [TITLE[orientation][0] * self._scale, + TITLE[orientation][1] * self._scale] + self._title_xy[0] = int((self._width - self._title_wh[0]) / 2.) + self._preview_wh = [PREVIEW[orientation][2] * self._scale, + PREVIEW[orientation][3] * self._scale] + self._preview_xy = [PREVIEW[orientation][0] * self._scale, + PREVIEW[orientation][1] * self._scale] + if orientation == 0: + self._preview_xy[0] = self._title_xy[0] + else: + self._preview_xy[0] = int((self._width - self._preview_wh[0]) / 2.) + self._desc_wh = [DESC[orientation][2] * self._scale, + DESC[orientation][3] * self._scale] + if orientation == 0: + self._desc_wh[0] = \ + self._width - self._preview_wh[0] - 2 * self._title_xy[0] + else: + self._desc_wh[0] = self._title_wh[0] + self._desc_xy = [DESC[orientation][0] * self._scale, + DESC[orientation][1] * self._scale] + if orientation == 0: + self._desc_xy[0] = self._preview_wh[0] + self._title_xy[0] + else: + self._desc_xy[0] = self._title_xy[0] + self._new_comment_wh = [NEW_COMMENT[orientation][2] * self._scale, + NEW_COMMENT[orientation][3] * self._scale] + self._new_comment_xy = [NEW_COMMENT[orientation][0] * self._scale, + NEW_COMMENT[orientation][1] * self._scale] + self._new_comment_xy[0] = self._title_xy[0] + self._new_comment_xy[1] = self._desc_xy[1] + self._desc_wh[1] + self._comment_wh = [COMMENTS[orientation][2] * self._scale, + COMMENTS[orientation][3] * self._scale] + self._comment_xy = [COMMENTS[orientation][0] * self._scale, + COMMENTS[orientation][1] * self._scale] + self._comment_xy[0] = self._title_xy[0] + self._comment_xy[1] = self._new_comment_xy[1] + self._new_comment_wh[1] + def _set_screen_dpi(self): dpi = _get_screen_dpi() - font_map_default = pangocairo.cairo_font_map_get_default() + font_map_default = PangoCairo.font_map_get_default() font_map_default.set_resolution(dpi) - def _tablet_mode(self): - return False # FIXME: Sugar scrolls the window for me??? + def _fixed_resize_cb(self, widget=None, rect=None): + ''' If a toolbar opens or closes, we need to resize the vbox + holding out scrolling window. ''' + self.vbox.set_size_request(rect.width, rect.height) + + ''' + self._width = gtk.gdk.screen_width() + self._height = gtk.gdk.screen_height() + self._scale = gtk.gdk.screen_height() / 900. + ''' + + def _set_screen_dpi(self): + dpi = _get_screen_dpi() + font_map_default = pangocairo.cairo_font_map_get_default() + font_map_default.set_resolution(dpi) def _fixed_resize_cb(self, widget=None, rect=None): ''' If a toolbar opens or closes, we need to resize the vbox @@ -233,9 +278,8 @@ class PortfolioActivity(activity.Activity): self.set_canvas(self.fixed) self.vbox = gtk.VBox(False, 0) - self.vbox.set_size_request( - gtk.gdk.screen_width(), - gtk.gdk.screen_height() - style.GRID_CELL_SIZE) + self.vbox.set_size_request(gtk.gdk.screen_width(), + gtk.gdk.screen_height()) self.fixed.put(self.vbox, 0, 0) self.vbox.show() @@ -243,7 +287,6 @@ class PortfolioActivity(activity.Activity): self._canvas.set_size_request(int(gtk.gdk.screen_width()), int(gtk.gdk.screen_height())) self._canvas.show() - # self.set_canvas(self._canvas) self.show_all() self.vbox.pack_end(self._canvas, True, True) self.vbox.show() @@ -259,32 +302,39 @@ class PortfolioActivity(activity.Activity): self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.connect('motion-notify-event', self._mouse_move_cb) self._canvas.connect('key-press-event', self._keypress_cb) - self._canvas.connect('configure-event', self._configure_cb) + gtk.gdk.screen_get_default().connect('size-changed', self._configure_cb) self._canvas.grab_focus() - def _configure_cb(self, win, event): - # landscape or portrait? + def _set_scale_and_orientation(self): self._width = gtk.gdk.screen_width() self._height = gtk.gdk.screen_height() if self._width > self._height: self._scale = gtk.gdk.screen_height() / 900. + self._orientation = 0 else: - self._scale = gtk.gdk.screen_width() / 1200. + self._scale = gtk.gdk.screen_height() / 1200. + self._orientation = 1 + def _configure_cb(self, win, event): self._my_canvas.hide() self._title.hide() self._description.hide() - self._titlewh = [self._width, TITLEH * self._scale] - self._titlexy = [0, 0] - self._previewwh = [PREVIEWW * self._scale, PREVIEWH * self._scale] - self._previewxy = [(self._width - self._previewwh[0]) / 2, - PREVIEWY * self._scale] - self._descriptionwh = [self._width, - self._height - DESCRIPTIONY * self._scale - 55] - self._descriptionxy = [0, DESCRIPTIONY * self._scale] + self._comment.hide() + self._new_comment.hide() + + self._set_scale_and_orientation() + + self.vbox.set_size_request(gtk.gdk.screen_width(), + gtk.gdk.screen_height()) + self.vbox.show() + self._canvas.set_size_request(int(gtk.gdk.screen_width()), + int(gtk.gdk.screen_height())) + self._canvas.show() + self._set_xy_wh() self._configured_sprites() # Some sprites are sized to screen + self._my_canvas.set_layer(BOTTOM) self._clear_screen() if self._thumbnail_mode: self._thumbs_cb() @@ -301,17 +351,17 @@ class PortfolioActivity(activity.Activity): self._colors[1] = tmp if self._hw[0:2] == 'xo': - self._titlef = 18 - self._descriptionf = 12 + self.title_size = 18 + self.desc_size = 12 else: - self._titlef = 36 - self._descriptionf = 24 + self.title_size = int(36 * self._scale) + self.desc_size = int(24 * self._scale) # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) if self._nobjects == 0: - star_size = 55 + star_size = GRID_CELL_SIZE else: star_size = int(150. / int(ceil(sqrt(self._nobjects)))) self._fav_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( @@ -323,16 +373,17 @@ class PortfolioActivity(activity.Activity): self.record_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'media-audio.svg'), 55, 55) + 'media-audio.svg'), GRID_CELL_SIZE, GRID_CELL_SIZE) self.recording_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'media-audio-recording.svg'), 55, 55) + 'media-audio-recording.svg'), GRID_CELL_SIZE, + GRID_CELL_SIZE) self.playback_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'speaker-100.svg'), 55, 55) + 'speaker-100.svg'), GRID_CELL_SIZE, GRID_CELL_SIZE) self.playing_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'speaker-0.svg'), 55, 55) + 'speaker-0.svg'), GRID_CELL_SIZE, GRID_CELL_SIZE) self._record_button = Sprite(self._sprites, 0, 0, self.record_pixbuf) self._record_button.set_layer(DRAG) @@ -345,16 +396,18 @@ class PortfolioActivity(activity.Activity): self.prev_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'go-previous.svg'), 55, 55) + 'go-previous.svg'), GRID_CELL_SIZE, GRID_CELL_SIZE) self.next_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'go-next.svg'), 55, 55) + 'go-next.svg'), GRID_CELL_SIZE, GRID_CELL_SIZE) self.prev_off_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'go-previous-inactive.svg'), 55, 55) + 'go-previous-inactive.svg'), GRID_CELL_SIZE, + GRID_CELL_SIZE) self.next_off_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'icons', - 'go-next-inactive.svg'), 55, 55) + 'go-next-inactive.svg'), GRID_CELL_SIZE, + GRID_CELL_SIZE) self._prev = Sprite(self._sprites, 0, 0, self.prev_off_pixbuf) self._prev.set_layer(DRAG) @@ -368,15 +421,15 @@ class PortfolioActivity(activity.Activity): 0, 0, gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'help.png'), - int(self._previewwh[0]), - int(self._previewwh[1]))) + int(self._preview_wh[0]), + int(self._preview_wh[1]))) self._help.hide() self._preview = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf(genblank( - int(self._previewwh[0]), - int(self._previewwh[1]), + int(self._preview_wh[0]), + int(self._preview_wh[1]), self._colors))) self._configured_sprites() # Some sprites are sized to screen @@ -393,34 +446,64 @@ class PortfolioActivity(activity.Activity): ''' Some sprites are sized or positioned based on screen configuration ''' - self._preview.move((int(self._previewxy[0]), - int(self._previewxy[1]))) - self._help.move((int(self._previewxy[0]), - int(self._previewxy[1]))) - self._record_button.move((self._width - 55, self._titlewh[1])) - self._playback_button.move((self._width - 55, self._titlewh[1] + 55)) - self._prev.move((0, int((self._height - 55) / 2))) - self._next.move((self._width - 55, int((self._height - 55) / 2))) + self._preview.move((int(self._preview_xy[0]), + int(self._preview_xy[1]))) + self._help.move((int(self._preview_xy[0]), + int(self._preview_xy[1]))) + self._record_button.move((self._width - GRID_CELL_SIZE, + self._title_wh[1])) + self._playback_button.move((self._width - GRID_CELL_SIZE, + self._title_wh[1] + GRID_CELL_SIZE)) + self._prev.move((0, int((self._height - GRID_CELL_SIZE) / 2))) + self._next.move((self._width - GRID_CELL_SIZE, + int((self._height - GRID_CELL_SIZE) / 2))) self._title = Sprite(self._sprites, - int(self._titlexy[0]), - int(self._titlexy[1]), + int(self._title_xy[0]), + int(self._title_xy[1]), svg_str_to_pixbuf( - genblank(self._titlewh[0], self._titlewh[1], self._colors))) - self._title.set_label_attributes(int(self._titlef * self._scale), - rescale=False) + genblank(self._title_wh[0], self._title_wh[1], self._colors))) + self._title.set_label_attributes(self.title_size, rescale=False) self._title.type = 'title' self._description = Sprite(self._sprites, - int(self._descriptionxy[0]), - int(self._descriptionxy[1]), + int(self._desc_xy[0]), + int(self._desc_xy[1]), svg_str_to_pixbuf( - genblank(int(self._descriptionwh[0]), - int(self._descriptionwh[1]), + genblank(int(self._desc_wh[0]), + int(self._desc_wh[1]), self._colors))) - self._description.set_label_attributes( - int(self._descriptionf * self._scale), vert_align='top') + self._description.set_label_attributes(self.desc_size, + horiz_align="left", + rescale=False, vert_align="top") + m = int(self.desc_size / 2) + self._description.set_margins(l=m, t=m, r=m, b=m) self._description.type = 'description' + self._comment = Sprite(self._sprites, + int(self._comment_xy[0]), + int(self._comment_xy[1]), + svg_str_to_pixbuf( + genblank(int(self._comment_wh[0]), + int(self._comment_wh[1]), + self._colors))) + self._comment.set_label_attributes(int(self.desc_size * 0.67), + vert_align="top", + horiz_align="left", + rescale=False) + self._comment.set_margins(l=m, t=m, r=m, b=m) + self._new_comment = Sprite(self._sprites, + int(self._new_comment_xy[0]), + int(self._new_comment_xy[1]), + svg_str_to_pixbuf( + genblank(int(self._new_comment_wh[0]), + int(self._new_comment_wh[1]), + self._colors))) + self._new_comment.set_label_attributes(self.desc_size, + horiz_align="left", + vert_align="top", rescale=False) + self._new_comment.type = 'comment' + self._new_comment.set_label(_('Enter comments here.')) + self._my_canvas = Sprite( self._sprites, 0, 0, svg_str_to_pixbuf(genblank( self._width, self._height, (self._colors[0], @@ -573,6 +656,12 @@ class PortfolioActivity(activity.Activity): title = dsobj.metadata['title'] if 'description' in dsobj.metadata: desc = dsobj.metadata['description'] + if 'comments' in dsobj.metadata: + try: + comment = json.loads(dsobj.metadata['comments']) + _logger.debug(comment) + except: + comment = [] if 'mime_type' in dsobj.metadata and \ dsobj.metadata['mime_type'][0:5] == 'image': preview = get_pixbuf_from_file(dsobj.file_path, @@ -585,15 +674,17 @@ class PortfolioActivity(activity.Activity): if slide is None: self._slides.append(Slide(owner, - dsobj.object_id, - self._colors, - title, - preview, - desc)) + dsobj.object_id, + self._colors, + title, + preview, + desc, + comment)) else: slide.title = title slide.preview = preview slide.description = desc + slide.comment = comment slide.active = True slide.fav = True if slide.star is not None: @@ -698,7 +789,11 @@ class PortfolioActivity(activity.Activity): self._title.hide() self._preview.hide() self._description.hide() - self.invalt(0, 0, self._width, self._height) + self._comment.hide() + self._new_comment.hide() + + # Why do we need this? + # self.invalt(0, 0, self._width, self._height) # Reset drag settings self._press = None @@ -707,19 +802,24 @@ class PortfolioActivity(activity.Activity): self._total_drag = [0, 0] self.last_spr_moved = None + def _show_slide(self, direction=1): ''' Display a title, preview image, and decription for slide i. Play an audio note if there is one recorded for this object. ''' self._clear_screen() - if len(self._slides) == 0: + if self._nobjects == 0: self._prev.set_image(self.prev_off_pixbuf) self._next.set_image(self.next_off_pixbuf) self._description.set_label( _('Do you have any items in your Journal starred?')) self._help.set_layer(TOP) self._description.set_layer(MIDDLE) + self._prev.hide() + self._next.hide() + self._record_button.hide() + self._playback_button.hide() return slide = self._slides[self.i] @@ -738,22 +838,24 @@ class PortfolioActivity(activity.Activity): return slide = self._slides[self.i] - if self.i == 0: + if self.i == 0: self._prev.set_image(self.prev_off_pixbuf) else: self._prev.set_image(self.prev_pixbuf) - if self.i == len(self._slides) - 1: + if self.i > self._nobjects - 2: self._next.set_image(self.next_off_pixbuf) else: self._next.set_image(self.next_pixbuf) + self._prev.set_layer(DRAG) + self._next.set_layer(DRAG) pixbuf = slide.preview if pixbuf is not None: self._preview.set_shape(pixbuf.scale_simple( - int(PREVIEWW * self._scale), - int(PREVIEWH * self._scale), - gtk.gdk.INTERP_NEAREST)) + int(PREVIEW[self._orientation][2] * self._scale), + int(PREVIEW[self._orientation][3] * self._scale), + GdkPixbuf.InterpType.NEAREST)) self._preview.set_layer(MIDDLE) else: if self._preview is not None: @@ -762,9 +864,17 @@ class PortfolioActivity(activity.Activity): self._title.set_label(slide.title) self._title.set_layer(MIDDLE) - self._description.set_label(slide.description) + if len(slide.description) == 0: + self._description.set_label(_('This project is about...')) + else: + self._description.set_label(slide.description) self._description.set_layer(MIDDLE) + self._comment.set_label(parse_comments(slide.comment)) + self._comment.set_layer(MIDDLE) + + self._new_comment.set_layer(MIDDLE) + if self.initiating is None or self.initiating: if slide.sound is None: slide.sound = self._search_for_audio_note(slide.uid) @@ -787,9 +897,7 @@ class PortfolioActivity(activity.Activity): def _slides_cb(self, button=None): if self._thumbnail_mode: self._thumbnail_mode = False - self.i = self._current_slide - self._prev.set_layer(DRAG) - self._next.set_layer(DRAG) + self.i = self._current_slide self._record_button.set_layer(DRAG) self._playback_button.set_layer(DRAG) self._show_slide() @@ -851,8 +959,8 @@ class PortfolioActivity(activity.Activity): slide.thumb = None if slide.thumb is None: if slide.preview is not None: - pixbuf_thumb = slide.preview.scale_simple(int(w), int(h), - gtk.gdk.INTERP_TILES) + pixbuf_thumb = slide.preview.scale_simple( + int(w), int(h), GdkPixbuf.InterpType.TILES) else: pixbuf_thumb = svg_str_to_pixbuf(genblank(int(w), int(h), self._colors)) @@ -866,20 +974,22 @@ class PortfolioActivity(activity.Activity): slide.star.set_layer(STAR) slide.star.move((x, y)) - def _expose_cb(self, win, event): - ''' Callback to handle window expose events ''' - self.do_expose_event(event) + def _draw_cb(self, win, context): + ''' Callback to handle window draw events ''' + self.do_draw_event(context) return True - # Handle the expose-event by drawing - def do_expose_event(self, event): + # Handle the draw-event by drawing + def do_draw_event(self, context): + + allocation = self.get_allocation() # Create the cairo context - cr = self.canvas.window.cairo_create() + cr = self.canvas.props.window.cairo_create() - # Restrict Cairo to the exposed area; avoid extra work - cr.rectangle(event.area.x, event.area.y, - event.area.width, event.area.height) + # Restrict Cairo to the drawn area; avoid extra work + cr.rectangle(allocation.x, allocation.y, + allocation.width, allocation.height) cr.clip() # Refresh sprite list @@ -896,25 +1006,17 @@ class PortfolioActivity(activity.Activity): os.remove(os.path.join(self.datapath, 'output.ogg')) def do_fullscreen_cb(self, button): - ''' Hide the Sugar toolbars. ''' + ''' Hide the sugar3 toolbars. ''' self.fullscreen() - def invalt(self, x, y, w, h): - ''' Mark a region for refresh ''' - self._canvas.window.invalidate_rect( - gtk.gdk.Rectangle(int(x), int(y), int(w), int(h)), False) - - def _add_text_no_changed_cb(self, widget=None, event=None): - pass - def _text_focus_out_cb(self, widget=None, event=None): bounds = self.text_buffer.get_bounds() - text = self.text_buffer.get_text(bounds[0], bounds[1]) + text = self.text_buffer.get_text(bounds[0], bounds[1], True) self._selected_spr.set_label(text) self._saved_string = self._selected_spr.labels[0] def _button_press_cb(self, win, event): - ''' The mouse button was pressed. Is it on asprite? ''' + ''' The mouse button was pressed. Is it on a sprite? ''' x, y = map(int, event.get_coords()) self._dragpos = [x, y] @@ -926,8 +1028,8 @@ class PortfolioActivity(activity.Activity): self._press = None self._release = None - # Are we clicking on a title or description? - if spr.type == 'title' or spr.type == 'description': + # Are we clicking on a title or description or comment? + if spr.type in ['title', 'description', 'comment']: if spr == self._selected_spr: return True elif self._selected_spr is not None: @@ -935,21 +1037,27 @@ class PortfolioActivity(activity.Activity): self._selected_spr = spr self._saved_string = spr.labels[0] if spr.type == 'description': - if self.initiating is not None and not self.initiating: - label = '%s\n[%s] ' % (self._selected_spr.labels[0], - profile.get_nick_name()) - else: + if self.initiating is None or self.initiating: label = self._selected_spr.labels[0] + self._selected_spr.set_label(label) + else: + self._selected_spr = None + return True self._selected_spr.set_label(label) if not hasattr(self, 'desc_entry'): - self.desc_entry = gtk.TextView() - self.desc_entry.set_justification(gtk.JUSTIFY_CENTER) - self.desc_entry.set_pixels_above_lines(4) - font_desc = pango.FontDescription('Sans') - font_desc.set_size(int( - self._descriptionf * pango.SCALE * self._scale)) + self.desc_entry = Gtk.TextView() + self.desc_entry.set_wrap_mode(Gtk.WrapMode.WORD) + self.desc_entry.set_pixels_above_lines(0) + self.desc_entry.set_size_request(self._desc_wh[0], + self._desc_wh[1]) + rgba = Gdk.RGBA() + rgba.red, rgba.green, rgba.blue = rgb(self._colors[1]) + rgba.alpha = 1. + self.desc_entry.override_background_color( + Gtk.StateFlags.NORMAL, rgba) + font_desc = Pango.font_description_from_string( + str(self.desc_size)) self.desc_entry.modify_font(font_desc) - self.desc_buffer = self.desc_entry.get_buffer() self.fixed.put(self.desc_entry, 0, 0) self.text_entry = self.desc_entry self.text_buffer = self.desc_entry.get_buffer() @@ -960,25 +1068,56 @@ class PortfolioActivity(activity.Activity): self._selected_spr.set_label(label) else: self._selected_spr = None + return True if not hasattr(self, 'title_entry'): - self.title_entry = gtk.TextView() - self.title_entry.set_justification(gtk.JUSTIFY_CENTER) - self.title_entry.set_pixels_above_lines(4) - font_desc = pango.FontDescription('Sans') - font_desc.set_size(int( - self._titlef * pango.SCALE * self._scale)) + self.title_entry = Gtk.TextView() + self.title_entry.set_justification(Gtk.Justification.CENTER) + self.title_entry.set_pixels_above_lines(1) + rgba = Gdk.RGBA() + rgba.red, rgba.green, rgba.blue = rgb(self._colors[1]) + rgba.alpha = 1. + self.title_entry.override_background_color( + Gtk.StateFlags.NORMAL, rgba) + font_desc = Pango.font_description_from_string( + str(self.title_size)) self.title_entry.modify_font(font_desc) - self.title_buffer = self.title_entry.get_buffer() self.fixed.put(self.title_entry, 0, 0) self.text_entry = self.title_entry self.text_buffer = self.title_entry.get_buffer() + self.text_buffer.connect('insert-text', self._insert_text_cb) self.title_entry.show() - self.text_buffer.set_text('') + elif spr.type == 'comment': + self._selected_spr.set_label('') + self._saved_string = spr.labels[0] + if not hasattr(self, 'comment_entry'): + self.comment_entry = Gtk.TextView() + self.comment_entry.set_wrap_mode(Gtk.WrapMode.WORD) + self.comment_entry.set_pixels_above_lines(0) + self.comment_entry.set_size_request( + self._new_comment_wh[0], self._new_comment_wh[1]) + rgba = Gdk.RGBA() + rgba.red, rgba.green, rgba.blue = rgb(self._colors[1]) + rgba.alpha = 1. + self.comment_entry.override_background_color( + Gtk.StateFlags.NORMAL, rgba) + font_desc = Pango.font_description_from_string( + str(self.desc_size)) + self.comment_entry.modify_font(font_desc) + self.fixed.put(self.comment_entry, 0, 0) + self.text_entry = self.comment_entry + self.text_buffer = self.comment_entry.get_buffer() + self.text_buffer.connect('insert-text', self._insert_text_cb) + self.comment_entry.show() self.text_buffer.set_text(self._saved_string) - spr.set_label('') # Clear the label while the text_entry is visible + # Clear the label while the text_entry is visible + spr.set_label('') w = spr.label_safe_width() h = spr.label_safe_height() + + if spr.type == 'comment': + if self._tablet_mode: + self._OSK_shift(spr, -OSK_SHIFT) bx, by = spr.get_xy() mx, my = spr.label_left_top() self.text_entry.set_size_request(w, h) @@ -1031,16 +1170,24 @@ class PortfolioActivity(activity.Activity): self.last_spr_moved = spr self._press = spr self._press.set_layer(DRAG) - slide.star.set_layer(DRAG+1) + slide.star.set_layer(DRAG + 1) return False + def _OSK_shift(self, spr, dy): + ''' Move some sprites when OSK appears/disappears ''' + dy *= self._scale + spr.move_relative((0, dy)) + self._title.move_relative((0, dy)) + self._preview.move_relative((0, dy)) + self._description.move_relative((0, dy)) + def _mouse_move_cb(self, win, event): ''' Drag a thumbnail with the mouse. ''' spr = self._press if spr is None: self._dragpos = [0, 0] return False - # win.grab_focus() + win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self._dragpos[0] dy = y - self._dragpos[1] @@ -1056,7 +1203,7 @@ class PortfolioActivity(activity.Activity): def _button_release_cb(self, win, event): ''' Button event is used to swap slides or goto next slide. ''' - # win.grab_focus() + win.grab_focus() self._dragpos = [0, 0] x, y = map(int, event.get_coords()) @@ -1111,6 +1258,10 @@ class PortfolioActivity(activity.Activity): self._release = None return False + def _insert_text_cb(self, textbuffer, textiter, text, length): + if '\12' in text: + self._unselect() + def _swap_slides(self, i, j): ''' Swap order and x, y position of two slides ''' tmp = self._slides[i] @@ -1137,6 +1288,11 @@ class PortfolioActivity(activity.Activity): if self._grecord is None: _logger.debug('setting up grecord') self._grecord = Grecord(self) + if self.i < 0 or self.i > len(self._slides) - 1: + _logger.debug('bad slide index %d' % (self.i)) + return + else: + _logger.debug('slide #%d' % (self.i)) if self._recording: # Was recording, so stop (and save?) _logger.debug('recording...True. Preparing to save.') self._grecord.stop_recording_audio() @@ -1148,9 +1304,9 @@ class PortfolioActivity(activity.Activity): self._playback_button.type = 'play' self._playback_button.set_layer(DRAG) # Autosave if there was not already a recording - slide = self._slides[self.i] _logger.debug('Autosaving recording') self._notify_successful_save(title=_('Save recording')) + self._transcoding_wait_counter = 0 gobject.timeout_add(100, self._wait_for_transcoding_to_finish) else: # Wasn't recording, so start _logger.debug('recording...False. Start recording.') @@ -1161,16 +1317,26 @@ class PortfolioActivity(activity.Activity): self._recording = True def _wait_for_transcoding_to_finish(self, button=None): - while not self._grecord.transcoding_complete(): - time.sleep(1) - if self._alert is not None: - self.remove_alert(self._alert) - self._alert = None - self._save_recording() + self._transcoding_wait_counter += 1 + if self._transcoding_wait_counter < 60 and \ + not self._grecord.transcoding_complete(): + gobject.timeout_add(1000, self._wait_for_transcoding_to_finish) + else: + if self._alert is not None: + self.remove_alert(self._alert) + self._alert = None + self._save_recording() + return False def _playback_recording_cb(self, button=None): ''' Play back current recording ''' _logger.debug('Playback current recording from output.ogg...') + if self.i < 0 or self.i > len(self._slides) - 1: + _logger.debug('bad slide index %d' % (self.i)) + return + if self._slides[self.i].sound is None: + _logger.debug('slide %d has no sound' % (self.i)) + return self._playback_button.set_image(self.playing_pixbuf) self._playback_button.set_layer(DRAG) self._playback_button.type = 'playing' @@ -1236,6 +1402,7 @@ class PortfolioActivity(activity.Activity): self._slides.index(slide))) jobject = datastore.get(slide.uid) jobject.metadata['description'] = slide.description + jobject.metadata['comments'] = json.dumps(slide.comment) jobject.metadata['title'] = slide.title datastore.write(jobject, update_mtime=False, @@ -1243,7 +1410,7 @@ class PortfolioActivity(activity.Activity): error_handler=self.datastore_write_error_cb) def datastore_write_cb(self): - pass + self._unselect() def datastore_write_error_cb(self, error): _logger.error('datastore_write_error_cb: %r' % error) @@ -1258,9 +1425,9 @@ class PortfolioActivity(activity.Activity): def _keypress_cb(self, area, event): ''' Keyboard ''' - keyname = gtk.gdk.keyval_name(event.keyval) - keyunicode = gtk.gdk.keyval_to_unicode(event.keyval) - if event.get_state() & gtk.gdk.MOD1_MASK: + keyname = Gdk.keyval_name(event.keyval) + keyunicode = Gdk.keyval_to_unicode(event.keyval) + if event.get_state() & Gdk.ModifierType.MOD1_MASK: alt_mask = True alt_flag = 'T' else: @@ -1289,6 +1456,10 @@ class PortfolioActivity(activity.Activity): def _unselect(self): if hasattr(self, 'text_entry'): + if self._selected_spr is not None: + if self._selected_spr.type == 'comment': + if self._tablet_mode: + self._OSK_shift(self._selected_spr, OSK_SHIFT) self.text_entry.hide() if self._selected_spr is not None: @@ -1298,13 +1469,27 @@ class PortfolioActivity(activity.Activity): if self.initiating is not None and self.initiating: self._send_event('t:%s' % (self._data_dumper( [slide.uid, slide.title]))) - else: + slide.dirty = True + elif self._selected_spr.type == 'description': slide.description = self._selected_spr.labels[0] if self.initiating is not None: - self._send_event('d:%s' % (self._data_dumper( - [slide.uid, slide.description]))) - _logger.debug('marking %d as dirty' % (self.i)) - slide.dirty = True + self._send_event('d:%s' % ( + self._data_dumper([slide.uid, slide.description]))) + slide.dirty = True + elif self._selected_spr.type == 'comment': + message = self._selected_spr.labels[0] + if message != '': + slide.comment.append({'from':profile.get_nick_name(), + 'message':message, + # Use my colors in case of sharing + 'icon':[self._my_colors[0], + self._my_colors[1]]}) + if self.initiating is not None: + self._send_event('c:%s' % (self._data_dumper( + [slide.uid, slide.comment]))) + self._comment.set_label(parse_comments(slide.comment)) + self._selected_spr.set_label('') + slide.dirty = True self._selected_spr = None self._saved_string = '' @@ -1312,18 +1497,14 @@ class PortfolioActivity(activity.Activity): ''' No longer waiting, so restore standard cursor. ''' if not hasattr(self, 'get_window'): return - if hasattr(self.get_window(), 'get_cursor'): - self.get_window().set_cursor(self.old_cursor) - else: - self.get_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) + self.get_window().set_cursor(self.old_cursor) def _waiting_cursor(self): ''' Waiting, so set watch cursor. ''' if not hasattr(self, 'get_window'): return - if hasattr(self.get_window(), 'get_cursor'): - self.old_cursor = self.get_window().get_cursor() - self.get_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) + self.old_cursor = self.get_window().get_cursor() + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) # Serialize @@ -1331,26 +1512,22 @@ class PortfolioActivity(activity.Activity): ''' Dump data for sharing.''' _logger.debug('dumping %s' % (slide.uid)) if slide.preview is None: - data = [slide.uid, slide.title, None, slide.description] + data = [slide.uid, slide.title, None, slide.description, + slide.comment] else: data = [slide.uid, slide.title, pixbuf_to_base64(activity, slide.preview, width=300, height=225), - slide.description] + slide.description, slide.comment] return self._data_dumper(data) def _data_dumper(self, data): - if _OLD_SUGAR_SYSTEM: - return json.write(data) - else: - io = StringIO() - jdump(data, io) - return io.getvalue() + return json.dumps(data) def _load(self, data): ''' Load slide data from a sharer. ''' self._restore_cursor() - uid, title, base64, description = self._data_loader(data) + uid, title, base64, description, comment = self._data_loader(data) if self._uid_to_slide(uid) is None: _logger.debug('loading %s' % (uid)) if base64 is None: @@ -1362,7 +1539,8 @@ class PortfolioActivity(activity.Activity): self._colors, title, preview, - description)) + description, + comment)) else: _logger.debug('updating description for %s' % (uid)) slide = self._uid_to_slide(uid) @@ -1372,23 +1550,23 @@ class PortfolioActivity(activity.Activity): else: slide.preview = base64_to_pixbuf(activity, base64) slide.description = description + slide.comment = comment slide.active = True if not slide.fav: slide.fav = True if slide.star is not None: slide.star.set_shape(self._fav_pixbuf) slide.star.type = 'star' + + self._nobjects += 1 + if not self._thumbnail_mode: self._thumb_button.set_active(True) else: self._show_thumbs() def _data_loader(self, data): - if _OLD_SUGAR_SYSTEM: - return json.read(data) - else: - io = StringIO(data) - return jload(io) + return json.loads(data) # When portfolio is shared, only sharer sends out slides, joiners # send back comments. @@ -1406,18 +1584,18 @@ class PortfolioActivity(activity.Activity): def _shared_cb(self, activity): ''' Either set up initial share...''' - if self._shared_activity is None: + if self.get_shared_activity() is None: _logger.error('Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()') + shared_activity is null in _shared_cb()') return self.initiating = True self.waiting = False _logger.debug('I am sharing...') - self.conn = self._shared_activity.telepathy_conn - self.tubes_chan = self._shared_activity.telepathy_tubes_chan - self.text_chan = self._shared_activity.telepathy_text_chan + self.conn = self.shared_activity.telepathy_conn + self.tubes_chan = self.shared_activity.telepathy_tubes_chan + self.text_chan = self.shared_activity.telepathy_text_chan self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) @@ -1428,17 +1606,17 @@ class PortfolioActivity(activity.Activity): def _joined_cb(self, activity): ''' ...or join an exisiting share. ''' - if self._shared_activity is None: + if self.get_shared_activity() is None: _logger.error('Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()') + shared_activity is null in _shared_cb()') return self.initiating = False _logger.debug('I joined a shared activity.') - self.conn = self._shared_activity.telepathy_conn - self.tubes_chan = self._shared_activity.telepathy_tubes_chan - self.text_chan = self._shared_activity.telepathy_text_chan + self.conn = self.shared_activity.telepathy_conn + self.tubes_chan = self.shared_activity.telepathy_tubes_chan + self.text_chan = self.shared_activity.telepathy_text_chan self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(\ 'NewTube', self._new_tube_cb) @@ -1453,6 +1631,9 @@ class PortfolioActivity(activity.Activity): for slide in self._slides: slide.active = False self._clear_screen() + self.i = 0 + self._current_slide = 0 + self._nobjects = 0 self._help.hide() self._description.set_layer(TOP) self._description.set_label(_('Please wait.')) @@ -1491,8 +1672,9 @@ class PortfolioActivity(activity.Activity): def event_received_cb(self, text): ''' Data is passed as tuples: cmd:text ''' dispatch_table = {'s': self._load, - 'c': self._update_colors, + 'C': self._update_colors, 'd': self._update_description, + 'c': self._update_comment, 't': self._update_title, 'S': self._update_star, 'R': self._reset, @@ -1530,32 +1712,32 @@ class PortfolioActivity(activity.Activity): def _update_colors(self, data): colors = self._data_loader(data) - if colors[0] != self._colors[0] or \ - colors[1] != self._colors[1]: - self._colors = colors[:] - self._my_canvas.set_image(svg_str_to_pixbuf( - genblank(self._width, self._height, [self._colors[0], - self._colors[0]]))) - self._description.set_image(svg_str_to_pixbuf( - genblank( - int(self._descriptionwh[0]), - int(self._descriptionwh[1]), - self._colors))) - self._title.set_image(svg_str_to_pixbuf( - genblank(int(self._titlewh[0]), - int(self._titlewh[1]), - self._colors))) - - def _update_description(self, data): - uid, text = self._data_loader(data) + colors[0] = str(colors[0]) + colors[1] = str(colors[1]) + self._my_canvas.set_image(svg_str_to_pixbuf( + genblank(self._width, self._height, [colors[0], colors[0]]))) + self._title.set_image( + svg_str_to_pixbuf(genblank( + int(self._title_wh[0]), int(self._title_wh[1]), colors))) + self._description.set_image( + svg_str_to_pixbuf(genblank( + int(self._desc_wh[0]), int(self._desc_wh[1]), colors))) + self._comment.set_image( + svg_str_to_pixbuf(genblank( + int(self._comment_wh[0]), int(self._comment_wh[1]), + colors))) + # Don't update new_comment colors + + def _update_comment(self, data): + uid, comment = self._data_loader(data) slide = self._uid_to_slide(uid) if slide is None: _logger.debug('slide %s not found' % (uid)) return - _logger.debug('updating description %s' % (uid)) - slide.description = text + _logger.debug('updating comment %s' % (uid)) + slide.comment = comment if self.i == self._slides.index(slide): - self._description.set_label(text) + self._comment.set_label(parse_comments(slide.comment)) if self.initiating: slide.dirty = True @@ -1572,13 +1754,26 @@ class PortfolioActivity(activity.Activity): if self.initiating: slide.dirty = True + def _update_description(self, data): + uid, text = self._data_loader(data) + slide = self._uid_to_slide(uid) + if slide is None: + _logger.debug('slide %s not found' % (uid)) + return + _logger.debug('updating title %s' % (uid)) + slide.description = text + if self.i == self._slides.index(slide): + self._description.set_label(text) + if self.initiating: + slide.dirty = True + def _share_nick(self): _logger.debug('sharing nick') self._send_event('j:%s' % (profile.get_nick_name())) def _share_colors(self): _logger.debug('sharing colors') - self._send_event('c:%s' % (self._data_dumper(self._colors))) + self._send_event('C:%s' % (self._data_dumper(self._colors))) def _share_slides(self): for slide in self._slides: @@ -1619,25 +1814,3 @@ class ChatTube(ExportedGObject): @signal(dbus_interface=IFACE, signature='s') def SendText(self, text): self.stack = text - - -def check_output(command, warning): - ''' Workaround for old systems without subprocess.check_output''' - if hasattr(subprocess, 'check_output'): - try: - output = subprocess.check_output(command) - except subprocess.CalledProcessError: - log.warning(warning) - return None - else: - import commands - - cmd = '' - for c in command: - cmd += c - cmd += ' ' - (status, output) = commands.getstatusoutput(cmd) - if status != 0: - log.warning(warning) - return None - return output |