class Picker(Gtk.Grid): def __init__(self, icon, label): Gtk.Grid.__init__(self) self._button = EventIcon(pixel_size=style.LARGE_ICON_SIZE, icon_name=icon) self.attach(self._button, 0, 0, 1, 1) self._button.hide() self._label = Gtk.Label(label.replace(' ', '\n')) self._label.props.justify = Gtk.Justification.CENTER self.attach(self._label, 0, 1, 1, 1) self._label.hide() def show_all(self): self._button.show() self._label.show() self.show() def hide_all(self): self._button.hide() self._label.hide() self.hide() def connect(self, callback, arg): self._button.connect('activate', callback, arg) def set_color(self, color): self._button.xo_color = color def set_icon(self, icon): self._button.set_icon_name(icon)
class Picker(Gtk.Grid): def __init__(self, icon, label): Gtk.Grid.__init__(self) self._button = EventIcon(pixel_size=style.LARGE_ICON_SIZE, icon_name=icon) self.attach(self._button, 0, 0, 1, 1) self._button.hide() self._label = Gtk.Label(label) self.attach(self._label, 0, 1, 1, 1) self._label.hide() def show_all(self): self._button.show() self._label.show() self.show() def hide_all(self): self._button.hide() self._label.hide() self.hide() def connect(self, callback, arg): self._button.connect('button-press-event', callback, arg) def set_color(self, color): self._button.xo_color = color def set_icon(self, icon): self._button.set_icon_name(icon)
class Picker(Gtk.Grid): def __init__(self, icon, label): Gtk.Grid.__init__(self) self._button = EventIcon(pixel_size=style.LARGE_ICON_SIZE, icon_name=icon) self.attach(self._button, 0, 0, 1, 1) self._button.hide() self._label = Gtk.Label(label) self.attach(self._label, 0, 1, 1, 1) self._label.hide() def show_all(self): self._button.show() self._label.show() self.show() def hide_all(self): self._button.hide() self._label.hide() self.hide() def connect(self, callback, arg): self._button.connect('button-press-event', callback, arg) def set_color(self, color): self._button.xo_color = color def set_icon(self, icon): self._button.set_icon_name(icon)
class ReflectionGrid(Gtk.EventBox): def __init__(self, parent): Gtk.EventBox.__init__(self) self._reflection = parent self._collapse = True self._collapse_id = None self.modify_bg(Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color()) self._title_color = self._reflection.activity.fg_color.get_html() self._grid = Gtk.Grid() self.add(self._grid) self._grid.show() self._grid.set_row_spacing(style.DEFAULT_PADDING) self._grid.set_column_spacing(style.DEFAULT_SPACING) self._grid.set_column_homogeneous(True) self._grid.set_border_width(style.DEFAULT_PADDING) row = 0 self._expand_button = EventIcon(icon_name='expand', pixel_size=BUTTON_SIZE) self._collapse_id = self._expand_button.connect( 'button-press-event', self._expand_cb) self._expand_button.set_tooltip(_('Expand')) self._grid.attach(self._expand_button, 0, row, 1, 1) self._expand_button.show() self._title_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._title = Gtk.TextView() self._title.set_size_request(ENTRY_WIDTH, -1) self._title.set_wrap_mode(Gtk.WrapMode.WORD) self._title_tag = self._title.get_buffer().create_tag( 'title', foreground=self._title_color, weight=Pango.Weight.BOLD, size=12288) iter_text = self._title.get_buffer().get_iter_at_offset(0) self._title.get_buffer().insert_with_tags( iter_text, self._reflection.data['title'], self._title_tag) if self._reflection.activity.initiating: self._title.connect('focus-out-event', self._title_focus_out_cb) else: self._title.set_editable(False) self._title_align.add(self._title) self._title.show() self._grid.attach(self._title_align, 1, row, 5, 1) self._title_align.show() delete_button = EventIcon(icon_name='delete', pixel_size=BUTTON_SIZE) delete_button.set_tooltip(_('Delete')) delete_button.connect('button-press-event', self.__delete_cb) self._grid.attach(delete_button, 6, row, 1, 1) delete_button.show() ''' Notification that a new comment has been shared. ''' self.notify_button = EventIcon(icon_name='chat', pixel_size=BUTTON_SIZE) self._grid.attach(self.notify_button, 6, row, 1, 1) row += 1 self._time_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._time = Gtk.Label() self._time.set_size_request(ENTRY_WIDTH, -1) self._time.set_justify(Gtk.Justification.LEFT) self._time.set_use_markup(True) try: time_string = util.timestamp_to_elapsed_string( int(self._reflection.data['modification_time'])) except Exception as e: logging.error('Could not convert modification time %s: %s' % (self._reflection.data['modification_time'], e)) self._reflection.data['modification_time'] = \ self._reflection.data['creation_time'] time_string = util.timestamp_to_elapsed_string( int(self._reflection.data['modification_time'])) self._time.set_markup( '<span foreground="#808080"><small><b>%s</b></small></span>' % time_string) self._time_align.add(self._time) self._time.show() self._grid.attach(self._time_align, 1, row, 5, 1) self._time_align.show() row += 1 label = '' if 'tags' in self._reflection.data: for tag in self._reflection.data['tags']: if len(label) > 0: label += ', ' label += tag if self._reflection.activity.initiating and label == '': label = _('Add a #tag') self._tag_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._tag_view = Gtk.TextView() self._tag_view.set_size_request(ENTRY_WIDTH, -1) self._tag_view.set_wrap_mode(Gtk.WrapMode.WORD) self._tag_view.get_buffer().set_text(label) if self._reflection.activity.initiating: self._tag_view.connect('focus-in-event', self._tag_focus_in_cb, _('Add a #tag')) self._tag_view.connect('focus-out-event', self._tags_focus_out_cb) else: self._tag_view.set_editable(False) self._tag_align.add(self._tag_view) self._tag_view.show() self._grid.attach(self._tag_align, 1, row, 5, 1) if self._reflection.activity.initiating: self._new_tag = EventIcon(icon_name='ok', pixel_size=BUTTON_SIZE) self._new_tag.connect('button-press-event', self._tag_button_cb) self._grid.attach(self._new_tag, 6, row, 1, 1) row += 1 self._activities_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._make_activities_grid() self._grid.attach(self._activities_align, 1, row, 5, 1) self._activities_align.show() if self._reflection.activity.initiating: self._new_activity = EventIcon(icon_name='add-item', pixel_size=BUTTON_SIZE) self._new_activity.set_tooltip(_('Add new activity')) self._new_activity.connect('button-press-event', self._activity_button_cb) self._grid.attach(self._new_activity, 6, row, 1, 1) self._new_activity.show() row += 1 self._stars_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) grid = Gtk.Grid() if 'stars' in self._reflection.data: stars = self._reflection.data['stars'] else: stars = 0 self._star_icons = [] for i in range(NUMBER_OF_STARS): if i < stars: icon_name = 'star-filled' else: icon_name = 'star-empty' self._star_icons.append( EventIcon(icon_name=icon_name, pixel_size=STAR_SIZE)) if self._reflection.activity.initiating: self._star_icons[-1].connect('button-press-event', self._star_button_cb, i) grid.attach(self._star_icons[-1], i, 0, 1, 1) self._star_icons[-1].show() self._stars_align.add(grid) grid.show() self._grid.attach(self._stars_align, 1, row, 5, 1) row += 1 self._content_aligns = [] first_text = True first_image = True self._content_we_always_show = [] if 'content' in self._reflection.data: for i, item in enumerate(self._reflection.data['content']): # Add edit and delete buttons align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) obj = None if 'text' in item: obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) obj.get_buffer().set_text(item['text']) if self._reflection.activity.initiating: obj.connect('focus-in-event', self._text_focus_in_cb) obj.connect('focus-out-event', self._text_focus_out_cb, i) else: obj.set_editable(False) if first_text: self._content_we_always_show.append(align) first_text = False elif 'image' in item: try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( item['image'], PICTURE_WIDTH, PICTURE_HEIGHT) obj = Gtk.Image.new_from_pixbuf(pixbuf) if first_image: self._content_we_always_show.append(align) first_image = False except BaseException: logging.error('could not open %s' % item['image']) if obj is not None: align.add(obj) obj.show() self._grid.attach(align, 1, row, 5, 1) self._content_aligns.append(align) row += 1 self._row = row if self._reflection.activity.initiating: self._new_entry = Gtk.Entry() self._new_entry.props.placeholder_text = _('Write a reflection') self._new_entry.connect('activate', self._entry_activate_cb) self._grid.attach(self._new_entry, 1, row, 5, 1) self._content_we_always_show.append(self._new_entry) self._new_image = EventIcon(icon_name='add-picture', pixel_size=BUTTON_SIZE) self._new_image.set_tooltip(_('Add new image')) self._new_image.connect('button-press-event', self._image_button_cb) self._grid.attach(self._new_image, 6, row, 1, 1) self._content_we_always_show.append(self._new_image) for align in self._content_we_always_show: align.show() row += 1 self._comment_row = row self._comment_aligns = [] if 'comments' in self._reflection.data: for comment in self._reflection.data['comments']: obj = Gtk.TextView() obj.set_editable(False) obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) nick_tag = obj.get_buffer().create_tag( 'nick', foreground=comment['color'], weight=Pango.Weight.BOLD) iter_text = obj.get_buffer().get_iter_at_offset(0) obj.get_buffer().insert_with_tags(iter_text, comment['nick'] + ': ', nick_tag) iter_text = obj.get_buffer().get_end_iter() obj.get_buffer().insert(iter_text, comment['comment']) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.attach(align, 1, self._comment_row, 5, 1) self._comment_aligns.append(align) self._comment_row += 1 self._new_comment = Gtk.Entry() self._new_comment.props.placeholder_text = _('Make a comment') self._new_comment.connect('activate', self._comment_activate_cb) self._grid.attach(self._new_comment, 1, self._comment_row, 5, 1) def _star_button_cb(self, button, event, n): self.update_stars(n) if self._reflection.activity.sharing: self._reflection.activity.send_event( STAR_CMD, { "obj_id": self._reflection.data["obj_id"], "stars": n }) def update_stars(self, n): if 'stars' in self._reflection.data: oldn = self._reflection.data['stars'] else: oldn = 0 if n < oldn: # Erase stars, including one that was clicked for i in range(NUMBER_OF_STARS): if i < n: icon_name = 'star-filled' else: icon_name = 'star-empty' self._star_icons[i].set_icon_name(icon_name) self._reflection.data['stars'] = n else: # Add stars, including one that was clicked for i in range(NUMBER_OF_STARS): if i <= n: icon_name = 'star-filled' else: icon_name = 'star-empty' self._star_icons[i].set_icon_name(icon_name) self._reflection.data['stars'] = n + 1 self._reflection.set_modification_time() def _text_focus_in_cb(self, widget, event): rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 0.9, 0.9, 0.9 rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _text_focus_out_cb(self, widget, event, entry): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._reflection.data['content'][entry]['text'] = text rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 1., 1., 1. rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _tag_button_cb(self, button, event): bounds = self._tag_view.get_buffer().get_bounds() text = self._tag_view.get_buffer().get_text(bounds[0], bounds[1], True) self._process_tags(self._tag_view.get_buffer(), text) def _tag_focus_in_cb(self, widget, event, prompt=None): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) if text == prompt: widget.get_buffer().set_text('') rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 0.9, 0.9, 0.9 rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _tags_focus_out_cb(self, widget, event): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._process_tags(widget.get_buffer(), text) rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 1., 1., 1. rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _process_tags(self, text_buffer, text): ''' process tag data from textview ''' self._reflection.data['tags'] = [] label = '' tags = text.split() for tag in tags: if len(label) > 0: label += ', ' tag = tag.rstrip(',') tag = tag.rstrip(';') if tag[0] == '#': self._reflection.data['tags'].append(tag) label += tag else: self._reflection.data['tags'].append('#' + tag) label += '#' + tag text_buffer.set_text(label.replace('\12', '')) if self._reflection.activity.sharing: data = json.dumps(self._reflection.data['tags']) self._reflection.activity.send_event( TAG_CMD, { "obj_id": self._refelection.data["ob_id"], "reflection": data }) self._reflection.set_modification_time() # Update journal entry dsobj = datastore.get(self._reflection.data['obj_id']) logging.error('setting tags to %s' % label) dsobj.metadata['tags'] = label datastore.write(dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def add_tags(self, data): ''' process encoded tag data from share ''' tags = json.loads(data) self._reflection.data['tags'] = tags[:] label = '' for tag in tags: if len(label) > 0: label += ', ' label += tag self._tag_view.get_buffer().set_text(label) def _title_focus_out_cb(self, widget, event): ''' process title text from textview ''' bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._reflection.data['title'] = text if self._reflection.activity.sharing: self._reflection.activity.send_event( TITLE_CMD, { "obj_id": self._reflection.data["obj_id"], "title": text }) self._reflection.set_modification_time() # Update journal entry dsobj = datastore.get(self._reflection.data['obj_id']) dsobj.metadata['title'] = text datastore.write(dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def datastore_write_cb(self): logging.debug('ds write cb') def datastore_write_error_cb(self, error): logging.error('datastore_write_error_cb: %r' % error) def update_title(self, text): ''' process title text from share ''' self._reflection.data['title'] = text self._title.get_buffer().set_text('') iter_text = self._title.get_buffer().get_iter_at_offset(0) self._title.get_buffer().insert_with_tags(iter_text, text, self._title_tag) def _comment_activate_cb(self, entry): text = entry.props.text if 'comments' not in self._reflection.data: self._reflection.data['comments'] = [] data = { 'nick': profile.get_nick_name(), 'color': self._reflection.activity.fg_color.get_html(), 'comment': text } self._reflection.data['comments'].append(data) self.add_new_comment(data) # Send the comment if self._reflection.activity.sharing: send_data = data.copy() send_data["obj_id"] = self._reflection.data["obj_id"] self._reflection.activity.send_event(COMMENT_CMD, send_data) entry.set_text('') # Update journal entry dsobj = datastore.get(self._reflection.data['obj_id']) if 'comments' in dsobj.metadata: data = json.loads(dsobj.metadata['comments']) else: data = [] data.append({ 'from': profile.get_nick_name(), 'message': text, 'icon-color': profile.get_color().to_string() }) dsobj.metadata['comments'] = json.dumps(data) datastore.write(dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def add_new_comment(self, comment): obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) nick_tag = obj.get_buffer().create_tag('nick', foreground=comment['color'], weight=Pango.Weight.BOLD) iter_text = obj.get_buffer().get_iter_at_offset(0) obj.get_buffer().insert_with_tags(iter_text, comment['nick'] + ': ', nick_tag) iter_text = obj.get_buffer().get_end_iter() obj.get_buffer().insert(iter_text, comment['comment']) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._comment_row) self._grid.attach(align, 1, self._comment_row, 5, 1) self._comment_row += 1 align.show() def _entry_activate_cb(self, entry): text = entry.props.text if 'content' not in self._reflection.data: self._reflection.data['content'] = [] self._reflection.data['content'].append({'text': text}) self._reflection.set_modification_time() self.add_new_reflection(text) # Send the reflection if self._reflection.activity.sharing: self._reflection.activity.send_event( REFLECTION_CMD, { "obj_id": self._reflection.data["obj_id"], "reflection": text }) entry.set_text('') def add_new_reflection(self, text): i = len(self._reflection.data['content']) obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) obj.get_buffer().set_text(text) obj.connect('focus-in-event', self._text_focus_in_cb) obj.connect('focus-out-event', self._text_focus_out_cb, i - 1) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._row) self._grid.attach(align, 1, self._row, 5, 1) self._row += 1 align.show() def _activity_button_cb(self, button, event): self._reflection.activity.busy_cursor() GObject.idle_add(self._choose_activity) def _choose_activity(self): if not hasattr(self, '_activity_sw'): grid = Gtk.Grid() self._reflection.activity.load_overlay_area(grid) grid.show() bundle_icons = utils.get_bundle_icons() x = 0 y = 0 for bundle_id in bundle_icons.keys(): icon_path = bundle_icons[bundle_id] if icon_path is None: continue pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( icon_path, style.GRID_CELL_SIZE, style.GRID_CELL_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) button = Gtk.ToolButton() button.set_icon_widget(image) image.show() button.connect('clicked', self._insert_activity, bundle_id) grid.attach(button, x, y, 1, 1) button.show() x += 1 if x > 6: y += 1 x = 0 self._reflection.activity.show_overlay_area() self._reflection.activity.reset_cursor() def _insert_activity(self, widget, bundle_id): ''' Add activity from UI ''' self._reflection.activity.hide_overlay_area() self.add_activity(bundle_id) if self._reflection.activity.sharing: self._reflection.activity.send_event( ACTIVITY_CMD, { "obj_id": self._reflection.data["obj_id"], "bundle_id": bundle_id }) def add_activity(self, bundle_id): ''' Add activity from sharer ''' if 'activities' not in self._reflection.data: self._reflection.data['activities'] = [] self._reflection.data['activities'].append( utils.bundle_id_to_icon(bundle_id)) self._reflection.set_modification_time() self._activities_align.remove(self._activities_grid) self._make_activities_grid() def _make_activities_grid(self): column = 0 self._activities_grid = Gtk.Grid() self._activities = [] if 'activities' in self._reflection.data: for icon_path in self._reflection.data['activities']: if icon_path is None: continue try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( icon_path, BUTTON_SIZE, BUTTON_SIZE) except Exception as e: logging.error('Could not find icon %s: %s' % (icon_path, e)) continue self._activities.append(Gtk.Image.new_from_pixbuf(pixbuf)) self._activities_grid.attach(self._activities[-1], column, 0, 1, 1) self._activities[-1].show() column += 1 else: label = Gtk.Label('Add an activity') self._activities_grid.attach(label, 0, 0, 5, 1) label.show() self._activities_align.add(self._activities_grid) self._activities_grid.show() def _image_button_cb(self, button, event): self._reflection.activity.busy_cursor() GObject.idle_add(self._choose_image) def _choose_image(self): from sugar3.graphics.objectchooser import ObjectChooser try: from sugar3.graphics.objectchooser import FILTER_TYPE_GENERIC_MIME except BaseException: FILTER_TYPE_GENERIC_MIME = 'generic_mime' from sugar3 import mime chooser = None name = None if hasattr(mime, 'GENERIC_TYPE_IMAGE'): # See #2398 if 'image/svg+xml' not in \ mime.get_generic_type(mime.GENERIC_TYPE_IMAGE).mime_types: mime.get_generic_type( mime.GENERIC_TYPE_IMAGE).mime_types.append('image/svg+xml') try: chooser = ObjectChooser(parent=self._reflection.activity, what_filter=mime.GENERIC_TYPE_IMAGE, filter_type=FILTER_TYPE_GENERIC_MIME, show_preview=True) except BaseException: chooser = ObjectChooser(parent=self._reflection.activity, what_filter=mime.GENERIC_TYPE_IMAGE) else: try: chooser = ObjectChooser(parent=self, what_filter=None) except TypeError: chooser = ObjectChooser( None, self._reflection.activity, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT) if chooser is not None: try: result = chooser.run() if result == Gtk.ResponseType.ACCEPT: jobject = chooser.get_selected_object() if jobject and jobject.file_path: name = jobject.metadata['title'] mime_type = jobject.metadata['mime_type'] _logger.debug('result of choose: %s (%s)' % (name, str(mime_type))) finally: chooser.destroy() del chooser if name is not None: pixbuf = self.add_new_picture(jobject.file_path) self._reflection.set_modification_time() if self._reflection.activity.sharing and pixbuf is not None: self._reflection.activity.send_event( PICTURE_CMD, { "basename": os.path.basename(jobject.file_path), "data": utils.pixbuf_to_base64(pixbuf) }) self._reflection.activity.send_event( IMAGE_REFLECTION_CMD, { "obj_id": self._reflection.data["obj_id"], "basename": os.path.basename(jobject.file_path) }) self._reflection.activity.reset_cursor() def add_new_picture(self, path): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( path, PICTURE_WIDTH, PICTURE_HEIGHT) obj = Gtk.Image.new_from_pixbuf(pixbuf) except BaseException: logging.error('could not open %s' % path) return None align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._row) self._grid.attach(align, 1, self._row, 5, 1) self._row += 1 align.show() if 'content' not in self._reflection.data: self._reflection.data['content'] = [] self._reflection.data['content'].append({'image': path}) if self._reflection.activity.sharing: return pixbuf def _expand_cb(self, button, event): self._grid.set_row_spacing(style.DEFAULT_SPACING) if self._collapse_id is not None: button.disconnect(self._collapse_id) button.set_icon_name('collapse') button.set_tooltip(_('Collapse')) self._collapse_id = button.connect('button-press-event', self._collapse_cb) self._tag_align.show() if hasattr(self, '_new_tag'): self._new_tag.show() self._stars_align.show() for align in self._content_aligns: align.show() for align in self._comment_aligns: align.show() self._new_comment.show() def _collapse_cb(self, button, event): self._grid.set_row_spacing(0) if self._collapse_id is not None: button.disconnect(self._collapse_id) button.set_icon_name('expand') button.set_tooltip(_('Expand')) self._collapse_id = button.connect('button-press-event', self._expand_cb) self._tag_align.hide() if hasattr(self, '_new_tag'): self._new_tag.hide() self._stars_align.hide() for align in self._content_aligns: if align not in self._content_we_always_show: align.hide() for align in self._comment_aligns: align.hide() self._new_comment.hide() def __delete_cb(self, button, event): self._reflection.activity.delete_item(self._reflection.data['obj_id']) self.hide()
class ReflectionGrid(Gtk.EventBox): def __init__(self, parent): Gtk.EventBox.__init__(self) self._reflection = parent self._collapse = True self._collapse_id = None self.modify_bg( Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color()) self._title_color = self._reflection.activity.fg_color.get_html() self._grid = Gtk.Grid() self.add(self._grid) self._grid.show() self._grid.set_row_spacing(style.DEFAULT_PADDING) self._grid.set_column_spacing(style.DEFAULT_SPACING) self._grid.set_column_homogeneous(True) self._grid.set_border_width(style.DEFAULT_PADDING) row = 0 self._expand_button = EventIcon(icon_name='expand', pixel_size=BUTTON_SIZE) self._collapse_id = self._expand_button.connect('button-press-event', self._expand_cb) self._expand_button.set_tooltip(_('Expand')) self._grid.attach(self._expand_button, 0, row, 1, 1) self._expand_button.show() self._title_align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) self._title = Gtk.TextView() self._title.set_size_request(ENTRY_WIDTH, -1) self._title.set_wrap_mode(Gtk.WrapMode.WORD) self._title_tag = self._title.get_buffer().create_tag( 'title', foreground=self._title_color, weight=Pango.Weight.BOLD, size=12288) iter_text = self._title.get_buffer().get_iter_at_offset(0) self._title.get_buffer().insert_with_tags( iter_text, self._reflection.data['title'], self._title_tag) if self._reflection.activity.initiating: self._title.connect('focus-out-event', self._title_focus_out_cb) else: self._title.set_editable(False) self._title_align.add(self._title) self._title.show() self._grid.attach(self._title_align, 1, row, 5, 1) self._title_align.show() delete_button = EventIcon(icon_name='delete', pixel_size=BUTTON_SIZE) delete_button.set_tooltip(_('Delete')) delete_button.connect('button-press-event', self.__delete_cb) self._grid.attach(delete_button, 6, row, 1, 1) delete_button.show() ''' Notification that a new comment has been shared. ''' self.notify_button = EventIcon(icon_name='chat', pixel_size=BUTTON_SIZE) self._grid.attach(self.notify_button, 6, row, 1, 1) row += 1 self._time_align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) self._time = Gtk.Label() self._time.set_size_request(ENTRY_WIDTH, -1) self._time.set_justify(Gtk.Justification.LEFT) self._time.set_use_markup(True) try: time_string = util.timestamp_to_elapsed_string( int(self._reflection.data['modification_time'])) except Exception as e: logging.error('Could not convert modification time %s: %s' % (self._reflection.data['modification_time'], e)) self._reflection.data['modification_time'] = \ self._reflection.data['creation_time'] time_string = util.timestamp_to_elapsed_string( int(self._reflection.data['modification_time'])) self._time.set_markup( '<span foreground="#808080"><small><b>%s</b></small></span>' % time_string) self._time_align.add(self._time) self._time.show() self._grid.attach(self._time_align, 1, row, 5, 1) self._time_align.show() row += 1 label = '' if 'tags' in self._reflection.data: for tag in self._reflection.data['tags']: if len(label) > 0: label += ', ' label += tag if self._reflection.activity.initiating and label == '': label = _('Add a #tag') self._tag_align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) self._tag_view = Gtk.TextView() self._tag_view.set_size_request(ENTRY_WIDTH, -1) self._tag_view.set_wrap_mode(Gtk.WrapMode.WORD) self._tag_view.get_buffer().set_text(label) if self._reflection.activity.initiating: self._tag_view.connect('focus-in-event', self._tag_focus_in_cb, _('Add a #tag')) self._tag_view.connect('focus-out-event', self._tags_focus_out_cb) else: self._tag_view.set_editable(False) self._tag_align.add(self._tag_view) self._tag_view.show() self._grid.attach(self._tag_align, 1, row, 5, 1) if self._reflection.activity.initiating: self._new_tag = EventIcon(icon_name='ok', pixel_size=BUTTON_SIZE) self._new_tag.connect('button-press-event', self._tag_button_cb) self._grid.attach(self._new_tag, 6, row, 1, 1) row += 1 self._activities_align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) self._make_activities_grid() self._grid.attach(self._activities_align, 1, row, 5, 1) self._activities_align.show() if self._reflection.activity.initiating: self._new_activity = EventIcon(icon_name='add-item', pixel_size=BUTTON_SIZE) self._new_activity.set_tooltip(_('Add new activity')) self._new_activity.connect('button-press-event', self._activity_button_cb) self._grid.attach(self._new_activity, 6, row, 1, 1) self._new_activity.show() row += 1 self._stars_align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) grid = Gtk.Grid() if 'stars' in self._reflection.data: stars = self._reflection.data['stars'] else: stars = 0 self._star_icons = [] for i in range(NUMBER_OF_STARS): if i < stars: icon_name = 'star-filled' else: icon_name = 'star-empty' self._star_icons.append(EventIcon(icon_name=icon_name, pixel_size=STAR_SIZE)) if self._reflection.activity.initiating: self._star_icons[-1].connect('button-press-event', self._star_button_cb, i) grid.attach(self._star_icons[-1], i, 0, 1, 1) self._star_icons[-1].show() self._stars_align.add(grid) grid.show() self._grid.attach(self._stars_align, 1, row, 5, 1) row += 1 self._content_aligns = [] first_text = True first_image = True self._content_we_always_show = [] if 'content' in self._reflection.data: for i, item in enumerate(self._reflection.data['content']): # Add edit and delete buttons align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) obj = None if 'text' in item: obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) obj.get_buffer().set_text(item['text']) if self._reflection.activity.initiating: obj.connect('focus-in-event', self._text_focus_in_cb) obj.connect( 'focus-out-event', self._text_focus_out_cb, i) else: obj.set_editable(False) if first_text: self._content_we_always_show.append(align) first_text = False elif 'image' in item: try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( item['image'], PICTURE_WIDTH, PICTURE_HEIGHT) obj = Gtk.Image.new_from_pixbuf(pixbuf) if first_image: self._content_we_always_show.append(align) first_image = False except: logging.error('could not open %s' % item['image']) if obj is not None: align.add(obj) obj.show() self._grid.attach(align, 1, row, 5, 1) self._content_aligns.append(align) row += 1 self._row = row if self._reflection.activity.initiating: self._new_entry = Gtk.Entry() self._new_entry.props.placeholder_text = _('Write a reflection') self._new_entry.connect('activate', self._entry_activate_cb) self._grid.attach(self._new_entry, 1, row, 5, 1) self._content_we_always_show.append(self._new_entry) self._new_image = EventIcon(icon_name='add-picture', pixel_size=BUTTON_SIZE) self._new_image.set_tooltip(_('Add new image')) self._new_image.connect('button-press-event', self._image_button_cb) self._grid.attach(self._new_image, 6, row, 1, 1) self._content_we_always_show.append(self._new_image) for align in self._content_we_always_show: align.show() row += 1 self._comment_row = row self._comment_aligns = [] if 'comments' in self._reflection.data: for comment in self._reflection.data['comments']: obj = Gtk.TextView() obj.set_editable(False) obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) nick_tag = obj.get_buffer().create_tag( 'nick', foreground=comment['color'], weight=Pango.Weight.BOLD) iter_text = obj.get_buffer().get_iter_at_offset(0) obj.get_buffer().insert_with_tags( iter_text, comment['nick'] + ': ', nick_tag) iter_text = obj.get_buffer().get_end_iter() obj.get_buffer().insert(iter_text, comment['comment']) align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.attach(align, 1, self._comment_row, 5, 1) self._comment_aligns.append(align) self._comment_row += 1 self._new_comment = Gtk.Entry() self._new_comment.props.placeholder_text = _('Make a comment') self._new_comment.connect('activate', self._comment_activate_cb) self._grid.attach(self._new_comment, 1, self._comment_row, 5, 1) def _star_button_cb(self, button, event, n): self.update_stars(n) if self._reflection.activity.sharing: self._reflection.activity.send_event(STAR_CMD, {"obj_id": self._reflection.data["obj_id"], "stars": n}) def update_stars(self, n): if 'stars' in self._reflection.data: oldn = self._reflection.data['stars'] else: oldn = 0 if n < oldn: # Erase stars, including one that was clicked for i in range(NUMBER_OF_STARS): if i < n: icon_name = 'star-filled' else: icon_name = 'star-empty' self._star_icons[i].set_icon_name(icon_name) self._reflection.data['stars'] = n else: # Add stars, including one that was clicked for i in range(NUMBER_OF_STARS): if i <= n: icon_name = 'star-filled' else: icon_name = 'star-empty' self._star_icons[i].set_icon_name(icon_name) self._reflection.data['stars'] = n + 1 self._reflection.set_modification_time() def _text_focus_in_cb(self, widget, event): rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 0.9, 0.9, 0.9 rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _text_focus_out_cb(self, widget, event, entry): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._reflection.data['content'][entry]['text'] = text rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 1., 1., 1. rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _tag_button_cb(self, button, event): bounds = self._tag_view.get_buffer().get_bounds() text = self._tag_view.get_buffer().get_text(bounds[0], bounds[1], True) self._process_tags(self._tag_view.get_buffer(), text) def _tag_focus_in_cb(self, widget, event, prompt=None): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) if text == prompt: widget.get_buffer().set_text('') rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 0.9, 0.9, 0.9 rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _tags_focus_out_cb(self, widget, event): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._process_tags(widget.get_buffer(), text) rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 1., 1., 1. rgba.alpha = 1. widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _process_tags(self, text_buffer, text): ''' process tag data from textview ''' self._reflection.data['tags'] = [] label = '' tags = text.split() for tag in tags: if len(label) > 0: label += ', ' tag = tag.rstrip(',') tag = tag.rstrip(';') if tag[0] == '#': self._reflection.data['tags'].append(tag) label += tag else: self._reflection.data['tags'].append('#' + tag) label += '#' + tag text_buffer.set_text(label.replace('\12', '')) if self._reflection.activity.sharing: data = json.dumps(self._reflection.data['tags']) self._reflection.activity.send_event(TAG_CMD, {"obj_id": self._refelection.data["ob_id"], "reflection": data}) self._reflection.set_modification_time() # Update journal entry dsobj = datastore.get(self._reflection.data['obj_id']) logging.error('setting tags to %s' % label) dsobj.metadata['tags'] = label datastore.write(dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def add_tags(self, data): ''' process encoded tag data from share ''' tags = json.loads(data) self._reflection.data['tags'] = tags[:] label = '' for tag in tags: if len(label) > 0: label += ', ' label += tag self._tag_view.get_buffer().set_text(label) def _title_focus_out_cb(self, widget, event): ''' process title text from textview ''' bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._reflection.data['title'] = text if self._reflection.activity.sharing: self._reflection.activity.send_event(TITLE_CMD, {"obj_id": self._reflection.data["obj_id"], "title": text}) self._reflection.set_modification_time() # Update journal entry dsobj = datastore.get(self._reflection.data['obj_id']) dsobj.metadata['title'] = text datastore.write(dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def datastore_write_cb(self): logging.debug('ds write cb') def datastore_write_error_cb(self, error): logging.error('datastore_write_error_cb: %r' % error) def update_title(self, text): ''' process title text from share ''' self._reflection.data['title'] = text self._title.get_buffer().set_text('') iter_text = self._title.get_buffer().get_iter_at_offset(0) self._title.get_buffer().insert_with_tags( iter_text, text, self._title_tag) def _comment_activate_cb(self, entry): text = entry.props.text if not 'comments' in self._reflection.data: self._reflection.data['comments'] = [] data = {'nick': profile.get_nick_name(), 'color': self._reflection.activity.fg_color.get_html(), 'comment': text} self._reflection.data['comments'].append(data) self.add_new_comment(data) # Send the comment if self._reflection.activity.sharing: send_data = data.copy() send_data["obj_id"] = self._reflection.data["obj_id"] self._reflection.activity.send_event(COMMENT_CMD, send_data) entry.set_text('') # Update journal entry dsobj = datastore.get(self._reflection.data['obj_id']) if 'comments' in dsobj.metadata: data = json.loads(dsobj.metadata['comments']) else: data = [] data.append({'from': profile.get_nick_name(), 'message': text, 'icon-color': profile.get_color().to_string()}) dsobj.metadata['comments'] = json.dumps(data) datastore.write(dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def add_new_comment(self, comment): obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) nick_tag = obj.get_buffer().create_tag( 'nick', foreground=comment['color'], weight=Pango.Weight.BOLD) iter_text = obj.get_buffer().get_iter_at_offset(0) obj.get_buffer().insert_with_tags( iter_text, comment['nick'] + ': ', nick_tag) iter_text = obj.get_buffer().get_end_iter() obj.get_buffer().insert(iter_text, comment['comment']) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._comment_row) self._grid.attach(align, 1, self._comment_row, 5, 1) self._comment_row += 1 align.show() def _entry_activate_cb(self, entry): text = entry.props.text if not 'content' in self._reflection.data: self._reflection.data['content'] = [] self._reflection.data['content'].append({'text': text}) self._reflection.set_modification_time() self.add_new_reflection(text) # Send the reflection if self._reflection.activity.sharing: self._reflection.activity.send_event(REFLECTION_CMD, {"obj_id": self._reflection.data["obj_id"], "reflection": text}) entry.set_text('') def add_new_reflection(self, text): i = len(self._reflection.data['content']) obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) obj.get_buffer().set_text(text) obj.connect('focus-in-event', self._text_focus_in_cb) obj.connect('focus-out-event', self._text_focus_out_cb, i - 1) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._row) self._grid.attach(align, 1, self._row, 5, 1) self._row += 1 align.show() def _activity_button_cb(self, button, event): self._reflection.activity.busy_cursor() GObject.idle_add(self._choose_activity) def _choose_activity(self): if not hasattr(self, '_activity_sw'): grid = Gtk.Grid() self._reflection.activity.load_overlay_area(grid) grid.show() bundle_icons = utils.get_bundle_icons() x = 0 y = 0 for bundle_id in bundle_icons.keys(): icon_path = bundle_icons[bundle_id] if icon_path is None: continue pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( icon_path, style.GRID_CELL_SIZE, style.GRID_CELL_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) button = Gtk.ToolButton() button.set_icon_widget(image) image.show() button.connect('clicked', self._insert_activity, bundle_id) grid.attach(button, x, y, 1, 1) button.show() x += 1 if x > 6: y += 1 x = 0 self._reflection.activity.show_overlay_area() self._reflection.activity.reset_cursor() def _insert_activity(self, widget, bundle_id): ''' Add activity from UI ''' self._reflection.activity.hide_overlay_area() self.add_activity(bundle_id) if self._reflection.activity.sharing: self._reflection.activity.send_event(ACTIVITY_CMD, {"obj_id": self._reflection.data["obj_id"], "bundle_id": bundle_id}) def add_activity(self, bundle_id): ''' Add activity from sharer ''' if not 'activities' in self._reflection.data: self._reflection.data['activities'] = [] self._reflection.data['activities'].append( utils.bundle_id_to_icon(bundle_id)) self._reflection.set_modification_time() self._activities_align.remove(self._activities_grid) self._make_activities_grid() def _make_activities_grid(self): column = 0 self._activities_grid = Gtk.Grid() self._activities = [] if 'activities' in self._reflection.data: for icon_path in self._reflection.data['activities']: if icon_path is None: continue try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( icon_path, BUTTON_SIZE, BUTTON_SIZE) except Exception as e: logging.error('Could not find icon %s: %s' % (icon_path, e)) continue self._activities.append(Gtk.Image.new_from_pixbuf(pixbuf)) self._activities_grid.attach( self._activities[-1], column, 0, 1, 1) self._activities[-1].show() column += 1 else: label = Gtk.Label('Add an activity') self._activities_grid.attach(label, 0, 0, 5, 1) label.show() self._activities_align.add(self._activities_grid) self._activities_grid.show() def _image_button_cb(self, button, event): self._reflection.activity.busy_cursor() GObject.idle_add(self._choose_image) def _choose_image(self): from sugar3.graphics.objectchooser import ObjectChooser try: from sugar3.graphics.objectchooser import FILTER_TYPE_GENERIC_MIME except: FILTER_TYPE_GENERIC_MIME = 'generic_mime' from sugar3 import mime chooser = None name = None if hasattr(mime, 'GENERIC_TYPE_IMAGE'): # See #2398 if 'image/svg+xml' not in \ mime.get_generic_type(mime.GENERIC_TYPE_IMAGE).mime_types: mime.get_generic_type( mime.GENERIC_TYPE_IMAGE).mime_types.append('image/svg+xml') try: chooser = ObjectChooser(parent=self._reflection.activity, what_filter=mime.GENERIC_TYPE_IMAGE, filter_type=FILTER_TYPE_GENERIC_MIME, show_preview=True) except: chooser = ObjectChooser(parent=self._reflection.activity, what_filter=mime.GENERIC_TYPE_IMAGE) else: try: chooser = ObjectChooser(parent=self, what_filter=None) except TypeError: chooser = ObjectChooser( None, self._reflection.activity, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT) if chooser is not None: try: result = chooser.run() if result == Gtk.ResponseType.ACCEPT: jobject = chooser.get_selected_object() if jobject and jobject.file_path: name = jobject.metadata['title'] mime_type = jobject.metadata['mime_type'] _logger.debug('result of choose: %s (%s)' % (name, str(mime_type))) finally: chooser.destroy() del chooser if name is not None: pixbuf = self.add_new_picture(jobject.file_path) self._reflection.set_modification_time() if self._reflection.activity.sharing and pixbuf is not None: self._reflection.activity.send_event(PICTURE_CMD, {"basename": os.path.basename(jobject.file_path), "data": utils.pixbuf_to_base64(pixbuf)}) self._reflection.activity.send_event(IMAGE_REFLECTION_CMD, {"obj_id": self._reflection.data["obj_id"], "basename": os.path.basename(jobject.file_path)}) self._reflection.activity.reset_cursor() def add_new_picture(self, path): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( path, PICTURE_WIDTH, PICTURE_HEIGHT) obj = Gtk.Image.new_from_pixbuf(pixbuf) except: logging.error('could not open %s' % jobject.file_path) return None align = Gtk.Alignment.new( xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._row) self._grid.attach(align, 1, self._row, 5, 1) self._row += 1 align.show() if not 'content' in self._reflection.data: self._reflection.data['content'] = [] self._reflection.data['content'].append({'image': path}) if self._reflection.activity.sharing: return pixbuf def _expand_cb(self, button, event): self._grid.set_row_spacing(style.DEFAULT_SPACING) if self._collapse_id is not None: button.disconnect(self._collapse_id) button.set_icon_name('collapse') button.set_tooltip(_('Collapse')) self._collapse_id = button.connect('button-press-event', self._collapse_cb) self._tag_align.show() if hasattr(self, '_new_tag'): self._new_tag.show() self._stars_align.show() for align in self._content_aligns: align.show() for align in self._comment_aligns: align.show() self._new_comment.show() def _collapse_cb(self, button, event): self._grid.set_row_spacing(0) if self._collapse_id is not None: button.disconnect(self._collapse_id) button.set_icon_name('expand') button.set_tooltip(_('Expand')) self._collapse_id = button.connect('button-press-event', self._expand_cb) self._tag_align.hide() if hasattr(self, '_new_tag'): self._new_tag.hide() self._stars_align.hide() for align in self._content_aligns: if not align in self._content_we_always_show: align.hide() for align in self._comment_aligns: align.hide() self._new_comment.hide() def __delete_cb(self, button, event): self._reflection.activity.delete_item(self._reflection.data['obj_id']) self.hide()
class ReflectionGrid(Gtk.EventBox): def __init__(self, parent): Gtk.EventBox.__init__(self) self._reflection = parent self._collapse = True self._collapse_id = None self.modify_bg(Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color()) self._title_color = self._reflection.activity.fg_color.get_html() self._grid = Gtk.Grid() self.add(self._grid) self._grid.show() self._grid.set_row_spacing(style.DEFAULT_PADDING) self._grid.set_column_spacing(style.DEFAULT_SPACING) self._grid.set_column_homogeneous(True) self._grid.set_border_width(style.DEFAULT_PADDING) row = 0 self._expand_button = EventIcon(icon_name="expand", pixel_size=BUTTON_SIZE) self._collapse_id = self._expand_button.connect("button-press-event", self._expand_cb) self._grid.attach(self._expand_button, 0, row, 1, 1) self._expand_button.show() self._title_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._title = Gtk.TextView() self._title.set_size_request(ENTRY_WIDTH, -1) self._title.set_wrap_mode(Gtk.WrapMode.WORD) self._title_tag = self._title.get_buffer().create_tag( "title", foreground=self._title_color, weight=Pango.Weight.BOLD, size=12288 ) iter_text = self._title.get_buffer().get_iter_at_offset(0) self._title.get_buffer().insert_with_tags(iter_text, self._reflection.data["title"], self._title_tag) if self._reflection.activity.initiating: self._title.connect("focus-out-event", self._title_focus_out_cb) else: self._title.set_editable(False) self._title_align.add(self._title) self._title.show() self._grid.attach(self._title_align, 1, row, 5, 1) self._title_align.show() """ Notification that a new comment has been shared. """ self.notify_button = EventIcon(icon_name="chat", pixel_size=BUTTON_SIZE) self._grid.attach(self.notify_button, 6, row, 1, 1) row += 1 self._time_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._time = Gtk.Label() self._time.set_size_request(ENTRY_WIDTH, -1) self._time.set_justify(Gtk.Justification.LEFT) self._time.set_use_markup(True) try: time_string = util.timestamp_to_elapsed_string(int(self._reflection.data["modification_time"])) except Exception as e: logging.error( "Could not convert modification time %s: %s" % (self._reflection.data["modification_time"], e) ) self._reflection.data["modification_time"] = self._reflection.data["creation_time"] time_string = util.timestamp_to_elapsed_string(int(self._reflection.data["modification_time"])) self._time.set_markup('<span foreground="#808080"><small><b>%s</b></small></span>' % time_string) self._time_align.add(self._time) self._time.show() self._grid.attach(self._time_align, 1, row, 5, 1) self._time_align.show() row += 1 label = "" if "tags" in self._reflection.data: for tag in self._reflection.data["tags"]: if len(label) > 0: label += ", " label += tag if self._reflection.activity.initiating and label == "": label = _("Add a #tag") self._tag_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._tag_view = Gtk.TextView() self._tag_view.set_size_request(ENTRY_WIDTH, -1) self._tag_view.set_wrap_mode(Gtk.WrapMode.WORD) self._tag_view.get_buffer().set_text(label) if self._reflection.activity.initiating: self._tag_view.connect("focus-in-event", self._tag_focus_in_cb, _("Add a #tag")) self._tag_view.connect("focus-out-event", self._tags_focus_out_cb) else: self._tag_view.set_editable(False) self._tag_align.add(self._tag_view) self._tag_view.show() self._grid.attach(self._tag_align, 1, row, 5, 1) if self._reflection.activity.initiating: self._new_tag = EventIcon(icon_name="ok", pixel_size=BUTTON_SIZE) self._new_tag.connect("button-press-event", self._tag_button_cb) self._grid.attach(self._new_tag, 6, row, 1, 1) row += 1 self._activities_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) self._make_activities_grid() self._grid.attach(self._activities_align, 1, row, 5, 1) self._activities_align.show() if self._reflection.activity.initiating: self._new_activity = EventIcon(icon_name="add-item", pixel_size=BUTTON_SIZE) self._new_activity.connect("button-press-event", self._activity_button_cb) self._grid.attach(self._new_activity, 6, row, 1, 1) self._new_activity.show() row += 1 self._stars_align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) grid = Gtk.Grid() if "stars" in self._reflection.data: stars = self._reflection.data["stars"] else: stars = 0 self._star_icons = [] for i in range(NUMBER_OF_STARS): if i < stars: icon_name = "star-filled" else: icon_name = "star-empty" self._star_icons.append(EventIcon(icon_name=icon_name, pixel_size=STAR_SIZE)) if self._reflection.activity.initiating: self._star_icons[-1].connect("button-press-event", self._star_button_cb, i) grid.attach(self._star_icons[-1], i, 0, 1, 1) self._star_icons[-1].show() self._stars_align.add(grid) grid.show() self._grid.attach(self._stars_align, 1, row, 5, 1) row += 1 self._content_aligns = [] first_text = True first_image = True self._content_we_always_show = [] if "content" in self._reflection.data: for i, item in enumerate(self._reflection.data["content"]): # Add edit and delete buttons align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) obj = None if "text" in item: obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) obj.get_buffer().set_text(item["text"]) if self._reflection.activity.initiating: obj.connect("focus-in-event", self._text_focus_in_cb) obj.connect("focus-out-event", self._text_focus_out_cb, i) else: obj.set_editable(False) if first_text: self._content_we_always_show.append(align) first_text = False elif "image" in item: try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(item["image"], PICTURE_WIDTH, PICTURE_HEIGHT) obj = Gtk.Image.new_from_pixbuf(pixbuf) if first_image: self._content_we_always_show.append(align) first_image = False except: logging.error("could not open %s" % item["image"]) if obj is not None: align.add(obj) obj.show() self._grid.attach(align, 1, row, 5, 1) self._content_aligns.append(align) row += 1 self._row = row if self._reflection.activity.initiating: self._new_entry = Gtk.Entry() self._new_entry.props.placeholder_text = _("Write a reflection") self._new_entry.connect("activate", self._entry_activate_cb) self._grid.attach(self._new_entry, 1, row, 5, 1) self._content_we_always_show.append(self._new_entry) self._new_image = EventIcon(icon_name="add-picture", pixel_size=BUTTON_SIZE) self._new_image.connect("button-press-event", self._image_button_cb) self._grid.attach(self._new_image, 6, row, 1, 1) self._content_we_always_show.append(self._new_image) for align in self._content_we_always_show: align.show() row += 1 self._comment_row = row self._comment_aligns = [] if "comments" in self._reflection.data: for comment in self._reflection.data["comments"]: obj = Gtk.TextView() obj.set_editable(False) obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) nick_tag = obj.get_buffer().create_tag("nick", foreground=comment["color"], weight=Pango.Weight.BOLD) iter_text = obj.get_buffer().get_iter_at_offset(0) obj.get_buffer().insert_with_tags(iter_text, comment["nick"] + ": ", nick_tag) iter_text = obj.get_buffer().get_end_iter() obj.get_buffer().insert(iter_text, comment["comment"]) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.attach(align, 1, self._comment_row, 5, 1) self._comment_aligns.append(align) self._comment_row += 1 self._new_comment = Gtk.Entry() self._new_comment.props.placeholder_text = _("Make a comment") self._new_comment.connect("activate", self._comment_activate_cb) self._grid.attach(self._new_comment, 1, self._comment_row, 5, 1) def _star_button_cb(self, button, event, n): self.update_stars(n) if self._reflection.activity.sharing: self._reflection.activity.send_event("%s|%s|%d" % (STAR_CMD, self._reflection.data["obj_id"], n)) def update_stars(self, n): if "stars" in self._reflection.data: oldn = self._reflection.data["stars"] else: oldn = 0 if n < oldn: # Erase stars, including one that was clicked for i in range(NUMBER_OF_STARS): if i < n: icon_name = "star-filled" else: icon_name = "star-empty" self._star_icons[i].set_icon_name(icon_name) self._reflection.data["stars"] = n else: # Add stars, including one that was clicked for i in range(NUMBER_OF_STARS): if i <= n: icon_name = "star-filled" else: icon_name = "star-empty" self._star_icons[i].set_icon_name(icon_name) self._reflection.data["stars"] = n + 1 self._reflection.set_modification_time() def _text_focus_in_cb(self, widget, event): rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 0.9, 0.9, 0.9 rgba.alpha = 1.0 widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _text_focus_out_cb(self, widget, event, entry): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._reflection.data["content"][entry]["text"] = text rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 1.0, 1.0, 1.0 rgba.alpha = 1.0 widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _tag_button_cb(self, button, event): bounds = self._tag_view.get_buffer().get_bounds() text = self._tag_view.get_buffer().get_text(bounds[0], bounds[1], True) self._process_tags(self._tag_view.get_buffer(), text) def _tag_focus_in_cb(self, widget, event, prompt=None): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) if text == prompt: widget.get_buffer().set_text("") rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 0.9, 0.9, 0.9 rgba.alpha = 1.0 widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _tags_focus_out_cb(self, widget, event): bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._process_tags(widget.get_buffer(), text) rgba = Gdk.RGBA() rgba.red, rgba.green, rgba.blue = 1.0, 1.0, 1.0 rgba.alpha = 1.0 widget.override_background_color(Gtk.StateFlags.NORMAL, rgba) def _process_tags(self, text_buffer, text): """ process tag data from textview """ self._reflection.data["tags"] = [] label = "" tags = text.split() for tag in tags: if len(label) > 0: label += ", " tag = tag.rstrip(",") tag = tag.rstrip(";") if tag[0] == "#": self._reflection.data["tags"].append(tag) label += tag else: self._reflection.data["tags"].append("#" + tag) label += "#" + tag text_buffer.set_text(label.replace("\12", "")) if self._reflection.activity.sharing: data = json.dumps(self._reflection.data["tags"]) self._reflection.activity.send_event("%s|%s|%s" % (TAG_CMD, self._reflection.data["obj_id"], data)) self._reflection.set_modification_time() # Update journal entry dsobj = datastore.get(self._reflection.data["obj_id"]) logging.error("setting tags to %s" % label) dsobj.metadata["tags"] = label datastore.write( dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb, ) def add_tags(self, data): """ process encoded tag data from share """ tags = json.loads(data) self._reflection.data["tags"] = tags[:] label = "" for tag in tags: if len(label) > 0: label += ", " label += tag self._tag_view.get_buffer().set_text(label) def _title_focus_out_cb(self, widget, event): """ process title text from textview """ bounds = widget.get_buffer().get_bounds() text = widget.get_buffer().get_text(bounds[0], bounds[1], True) self._reflection.data["title"] = text if self._reflection.activity.sharing: self._reflection.activity.send_event("%s|%s|%s" % (TITLE_CMD, self._reflection.data["obj_id"], text)) self._reflection.set_modification_time() # Update journal entry dsobj = datastore.get(self._reflection.data["obj_id"]) dsobj.metadata["title"] = text datastore.write( dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb, ) def datastore_write_cb(self): logging.debug("ds write cb") def datastore_write_error_cb(self, error): logging.error("datastore_write_error_cb: %r" % error) def update_title(self, text): """ process title text from share """ self._reflection.data["title"] = text self._title.get_buffer().set_text("") iter_text = self._title.get_buffer().get_iter_at_offset(0) self._title.get_buffer().insert_with_tags(iter_text, text, self._title_tag) def _comment_activate_cb(self, entry): text = entry.props.text if not "comments" in self._reflection.data: self._reflection.data["comments"] = [] data = { "nick": profile.get_nick_name(), "color": self._reflection.activity.fg_color.get_html(), "comment": text, } self._reflection.data["comments"].append(data) self.add_new_comment(data) # Send the comment if self._reflection.activity.sharing: self._reflection.activity.send_event( "%s|%s|%s|%s|%s" % (COMMENT_CMD, self._reflection.data["obj_id"], data["nick"], data["color"], data["comment"]) ) entry.set_text("") # Update journal entry dsobj = datastore.get(self._reflection.data["obj_id"]) if "comments" in dsobj.metadata: data = json.loads(dsobj.metadata["comments"]) else: data = [] data.append({"from": profile.get_nick_name(), "message": text, "icon-color": profile.get_color().to_string()}) dsobj.metadata["comments"] = json.dumps(data) datastore.write( dsobj, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb, ) def add_new_comment(self, comment): obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) nick_tag = obj.get_buffer().create_tag("nick", foreground=comment["color"], weight=Pango.Weight.BOLD) iter_text = obj.get_buffer().get_iter_at_offset(0) obj.get_buffer().insert_with_tags(iter_text, comment["nick"] + ": ", nick_tag) iter_text = obj.get_buffer().get_end_iter() obj.get_buffer().insert(iter_text, comment["comment"]) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._comment_row) self._grid.attach(align, 1, self._comment_row, 5, 1) self._comment_row += 1 align.show() def _entry_activate_cb(self, entry): text = entry.props.text if not "content" in self._reflection.data: self._reflection.data["content"] = [] self._reflection.data["content"].append({"text": text}) self._reflection.set_modification_time() self.add_new_reflection(text) # Send the reflection if self._reflection.activity.sharing: self._reflection.activity.send_event("%s|%s|%s" % (REFLECTION_CMD, self._reflection.data["obj_id"], text)) entry.set_text("") def add_new_reflection(self, text): i = len(self._reflection.data["content"]) obj = Gtk.TextView() obj.set_size_request(ENTRY_WIDTH, -1) obj.set_wrap_mode(Gtk.WrapMode.WORD) obj.get_buffer().set_text(text) obj.connect("focus-in-event", self._text_focus_in_cb) obj.connect("focus-out-event", self._text_focus_out_cb, i - 1) align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._row) self._grid.attach(align, 1, self._row, 5, 1) self._row += 1 align.show() def _activity_button_cb(self, button, event): self._reflection.activity.busy_cursor() GObject.idle_add(self._choose_activity) def _choose_activity(self): if not hasattr(self, "_activity_sw"): grid = Gtk.Grid() self._reflection.activity.load_overlay_area(grid) grid.show() bundle_icons = utils.get_bundle_icons() x = 0 y = 0 for bundle_id in bundle_icons.keys(): icon_path = bundle_icons[bundle_id] if icon_path is None: continue pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, style.GRID_CELL_SIZE, style.GRID_CELL_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) button = Gtk.ToolButton() button.set_icon_widget(image) image.show() button.connect("clicked", self._insert_activity, bundle_id) grid.attach(button, x, y, 1, 1) button.show() x += 1 if x > 6: y += 1 x = 0 self._reflection.activity.show_overlay_area() self._reflection.activity.reset_cursor() def _insert_activity(self, widget, bundle_id): """ Add activity from UI """ self._reflection.activity.hide_overlay_area() self.add_activity(bundle_id) if self._reflection.activity.sharing: self._reflection.activity.send_event( "%s|%s|%s" % (ACTIVITY_CMD, self._reflection.data["obj_id"], bundle_id) ) def add_activity(self, bundle_id): """ Add activity from sharer """ if not "activities" in self._reflection.data: self._reflection.data["activities"] = [] self._reflection.data["activities"].append(utils.bundle_id_to_icon(bundle_id)) self._reflection.set_modification_time() self._activities_align.remove(self._activities_grid) self._make_activities_grid() def _make_activities_grid(self): column = 0 self._activities_grid = Gtk.Grid() self._activities = [] if "activities" in self._reflection.data: for icon_path in self._reflection.data["activities"]: if icon_path is None: continue try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, BUTTON_SIZE, BUTTON_SIZE) except Exception as e: logging.error("Could not find icon %s: %s" % (icon_path, e)) continue self._activities.append(Gtk.Image.new_from_pixbuf(pixbuf)) self._activities_grid.attach(self._activities[-1], column, 0, 1, 1) self._activities[-1].show() column += 1 else: label = Gtk.Label("Add an activity") self._activities_grid.attach(label, 0, 0, 5, 1) label.show() self._activities_align.add(self._activities_grid) self._activities_grid.show() def _image_button_cb(self, button, event): self._reflection.activity.busy_cursor() GObject.idle_add(self._choose_image) def _choose_image(self): from sugar3.graphics.objectchooser import ObjectChooser try: from sugar3.graphics.objectchooser import FILTER_TYPE_GENERIC_MIME except: FILTER_TYPE_GENERIC_MIME = "generic_mime" from sugar3 import mime chooser = None name = None if hasattr(mime, "GENERIC_TYPE_IMAGE"): # See #2398 if "image/svg+xml" not in mime.get_generic_type(mime.GENERIC_TYPE_IMAGE).mime_types: mime.get_generic_type(mime.GENERIC_TYPE_IMAGE).mime_types.append("image/svg+xml") try: chooser = ObjectChooser( parent=self._reflection.activity, what_filter=mime.GENERIC_TYPE_IMAGE, filter_type=FILTER_TYPE_GENERIC_MIME, show_preview=True, ) except: chooser = ObjectChooser(parent=self._reflection.activity, what_filter=mime.GENERIC_TYPE_IMAGE) else: try: chooser = ObjectChooser(parent=self, what_filter=None) except TypeError: chooser = ObjectChooser( None, self._reflection.activity, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT ) if chooser is not None: try: result = chooser.run() if result == Gtk.ResponseType.ACCEPT: jobject = chooser.get_selected_object() if jobject and jobject.file_path: name = jobject.metadata["title"] mime_type = jobject.metadata["mime_type"] _logger.debug("result of choose: %s (%s)" % (name, str(mime_type))) finally: chooser.destroy() del chooser if name is not None: pixbuf = self.add_new_picture(jobject.file_path) self._reflection.set_modification_time() if self._reflection.activity.sharing and pixbuf is not None: self._reflection.activity.send_event( "%s|%s|%s" % (PICTURE_CMD, os.path.basename(jobject.file_path), utils.pixbuf_to_base64(pixbuf)) ) self._reflection.activity.send_event( "%s|%s|%s" % (IMAGE_REFLECTION_CMD, self._reflection.data["obj_id"], os.path.basename(jobject.file_path)) ) self._reflection.activity.reset_cursor() def add_new_picture(self, path): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, PICTURE_WIDTH, PICTURE_HEIGHT) obj = Gtk.Image.new_from_pixbuf(pixbuf) except: logging.error("could not open %s" % jobject.file_path) return None align = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0) align.add(obj) obj.show() self._grid.insert_row(self._row) self._grid.attach(align, 1, self._row, 5, 1) self._row += 1 align.show() if not "content" in self._reflection.data: self._reflection.data["content"] = [] self._reflection.data["content"].append({"image": path}) if self._reflection.activity.sharing: return pixbuf def _expand_cb(self, button, event): self._grid.set_row_spacing(style.DEFAULT_SPACING) if self._collapse_id is not None: button.disconnect(self._collapse_id) button.set_icon_name("collapse") self._collapse_id = button.connect("button-press-event", self._collapse_cb) self._tag_align.show() if hasattr(self, "_new_tag"): self._new_tag.show() self._stars_align.show() for align in self._content_aligns: align.show() for align in self._comment_aligns: align.show() self._new_comment.show() def _collapse_cb(self, button, event): self._grid.set_row_spacing(0) if self._collapse_id is not None: button.disconnect(self._collapse_id) button.set_icon_name("expand") self._collapse_id = button.connect("button-press-event", self._expand_cb) self._tag_align.hide() if hasattr(self, "_new_tag"): self._new_tag.hide() self._stars_align.hide() for align in self._content_aligns: if not align in self._content_we_always_show: align.hide() for align in self._comment_aligns: align.hide() self._new_comment.hide()