From 52e195397cd0aa55b866f22518f33080f5d69e61 Mon Sep 17 00:00:00 2001 From: Utkarsh Tiwari Date: Wed, 6 Apr 2016 21:21:04 +0530 Subject: [PATCH] Screenshot popup alert This feature adds a screenshot alert to Sugar. On pressing +1 key or selecting 'Take a screenshot' option from bottom toolbar generates a popup alert where user can change the default name of that screenshot. The popup can be dismissed by clicking 'X' button or by pressing 'Escape' key. --- extensions/deviceicon/display.py | 4 +- extensions/globalkey/screenshot.py | 4 +- src/jarabe/Makefile.am | 3 +- src/jarabe/screenshotpanel/Makefile.am | 5 + src/jarabe/screenshotpanel/__init__.py | 20 ++ src/jarabe/screenshotpanel/gui.py | 341 +++++++++++++++++++++++++ src/jarabe/screenshotpanel/toolbar.py | 86 +++++++ 7 files changed, 458 insertions(+), 5 deletions(-) create mode 100644 src/jarabe/screenshotpanel/Makefile.am create mode 100644 src/jarabe/screenshotpanel/__init__.py create mode 100644 src/jarabe/screenshotpanel/gui.py create mode 100644 src/jarabe/screenshotpanel/toolbar.py diff --git a/extensions/deviceicon/display.py b/extensions/deviceicon/display.py index ebdd3d424f..31aeace2d2 100644 --- a/extensions/deviceicon/display.py +++ b/extensions/deviceicon/display.py @@ -34,7 +34,7 @@ from jarabe.frame.frameinvoker import FrameWidgetInvoker from jarabe.model import brightness -from jarabe.model.screenshot import take_screenshot +from jarabe.screenshotpanel.gui import ScreenshotPanel from jarabe import frame @@ -230,7 +230,7 @@ def __screenshot_cb(self, palette): def __take_screenshot_cb(self, frame_): if frame_.is_visible(): return True - take_screenshot() + panel = ScreenshotPanel() frame_.show() return False diff --git a/extensions/globalkey/screenshot.py b/extensions/globalkey/screenshot.py index 53b357d58b..df63d90dc9 100644 --- a/extensions/globalkey/screenshot.py +++ b/extensions/globalkey/screenshot.py @@ -15,10 +15,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from jarabe.model.screenshot import take_screenshot +from jarabe.screenshotpanel.gui import ScreenshotPanel BOUND_KEYS = ['1', 'Print'] def handle_key_press(key): - take_screenshot() + panel = ScreenshotPanel() diff --git a/src/jarabe/Makefile.am b/src/jarabe/Makefile.am index bebd7c92c3..00311e2942 100644 --- a/src/jarabe/Makefile.am +++ b/src/jarabe/Makefile.am @@ -1,9 +1,10 @@ SUBDIRS = \ - controlpanel \ + controlpanel \ desktop \ frame \ journal \ model \ + screenshotpanel \ view \ intro \ util \ diff --git a/src/jarabe/screenshotpanel/Makefile.am b/src/jarabe/screenshotpanel/Makefile.am new file mode 100644 index 0000000000..a20cf9e886 --- /dev/null +++ b/src/jarabe/screenshotpanel/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pythondir)/jarabe/screenshotpanel +sugar_PYTHON = \ + __init__.py \ + gui.py \ + toolbar.py \ No newline at end of file diff --git a/src/jarabe/screenshotpanel/__init__.py b/src/jarabe/screenshotpanel/__init__.py new file mode 100644 index 0000000000..d5fdd944a5 --- /dev/null +++ b/src/jarabe/screenshotpanel/__init__.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2016 Utkarsh Tiwari +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Contact information: +# Utkarsh Tiwari iamutkarshtiwari@gmail.com diff --git a/src/jarabe/screenshotpanel/gui.py b/src/jarabe/screenshotpanel/gui.py new file mode 100644 index 0000000000..9e54f06ce1 --- /dev/null +++ b/src/jarabe/screenshotpanel/gui.py @@ -0,0 +1,341 @@ +# Copyright (C) 2008 One Laptop Per Child +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import dbus +import cairo +import logging +import StringIO +import tempfile +from gettext import gettext as _ + +from gi.repository import GObject +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import Gio + +from sugar3 import env + +from sugar3.datastore import datastore +from sugar3.graphics.icon import Icon +from sugar3.graphics import style +from sugar3.graphics.alert import Alert, TimeoutAlert +from sugar3.graphics import iconentry + + +from jarabe.model.session import get_session_manager +from jarabe.screenshotpanel.toolbar import MainToolbar +from jarabe import config +from jarabe.model import shell + + +_logger = logging.getLogger('ScreenshotPanel') + + +class ScreenshotPanel(Gtk.Window): + ''' + Generates a pop papel to allow user to save the + screenshot by the name of his choice + ''' + + __gtype_name__ = 'ScreenshotPanel' + + + def __init__(self): + Gtk.Window.__init__(self) + + self.modify_bg(Gtk.StateType.NORMAL, + style.COLOR_BLACK.get_gdk_color()) + self._set_screensize() + self.set_border_width(style.LINE_WIDTH) + self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + self.set_decorated(False) + self.set_resizable(False) + self.set_modal(True) + self.set_can_focus(True) + + self.connect('key-press-event', self.__key_press_event_cb) + + self._toolbar = None + self._canvas = None + self._table = None + self._scrolledwindow = None + self._separator = None + self._section_view = None + self._section_toolbar = None + self._main_toolbar = None + + self._vbox = Gtk.VBox() + self._hbox = Gtk.HBox() + self._vbox.pack_start(self._hbox, True, True, 0) + self._hbox.show() + + self._main_view = Gtk.EventBox() + self._hbox.pack_start(self._main_view, True, True, 0) + self._main_view.modify_bg(Gtk.StateType.NORMAL, + style.COLOR_BLACK.get_gdk_color()) + self._main_view.show() + + self.add(self._vbox) + self._vbox.show() + self._setup_main() + self._show_main_view() + + # Generates the thumbnail + preview_image, activity_title = generate_thumbnail() + self._main_view.add(preview_image) + preview_image.show() + self.screenshot_surface, self.file_path = take_screenshot() + + self._vbox = Gtk.VBox() + self._hbox.pack_start(self._vbox, True, True, 0) + self._vbox.show() + + # Name label + name_label = Gtk.Label() + name_label.set_alignment(0.5, 1) + name_label.set_use_markup(True) + name_label.set_markup(""+_('Name')+"") + name_label.modify_bg(Gtk.StateType.NORMAL, + style.COLOR_BLACK.get_gdk_color()) + name_label.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse("yellow")) + self._vbox.pack_start(name_label, True, True, 0) + name_label.show() + + # Name entry box + self._name_view = Gtk.EventBox() + self._name_view.modify_bg(Gtk.StateType.NORMAL, + style.COLOR_BLACK.get_gdk_color()) + self._name_view.show() + + # Entry box + self._search_entry = Gtk.Entry() + halign = Gtk.Alignment.new(0.5, 0, 0, 0) + halign.add(self._name_view) + halign.show() + + self._vbox.pack_start(halign, True, True, 0) + self._name_view.add(self._search_entry) + self._search_entry.show() + self._search_entry.set_text(_(activity_title)) + self._search_entry.grab_focus() + self.show() + + def _set_cursor(self, cursor): + self.get_window().set_cursor(cursor) + Gdk.flush() + + def _set_screensize(self): + ''' + Sets the size of the popup based on + the screen resolution. + ''' + width = Gdk.Screen.width() / 4 + height = Gdk.Screen.height() / 4 + self.set_size_request(width, height) + + def _set_toolbar(self, toolbar): + ''' + Sets up the toolbar on the popup. + ''' + if self._toolbar: + self._vbox.remove(self._toolbar) + self._vbox.pack_start(toolbar, False, False, 0) + self._vbox.reorder_child(toolbar, 0) + self._toolbar = toolbar + if not self._separator: + self._separator = Gtk.HSeparator() + self._vbox.pack_start(self._separator, False, False, 0) + self._vbox.reorder_child(self._separator, 1) + self._separator.show() + + def _setup_main(self): + self._main_toolbar = MainToolbar() + self._main_toolbar.connect('stop-clicked', + self.__stop_clicked_cb) + #self._main_toolbar.connect('ok-clicked', self.__ok_clicked_cb) + + def _show_main_view(self): + self._set_toolbar(self._main_toolbar) + self._main_toolbar.show() + self._main_view.modify_bg(Gtk.StateType.NORMAL, + style.COLOR_BLACK.get_gdk_color()) + + def __key_press_event_cb(self, window, event): + if event.keyval == Gdk.KEY_Return: + self.save_screenshot(self._search_entry.get_text()) + self.destroy() + return True + + if event.keyval == Gdk.KEY_Escape: + if self._toolbar == self._main_toolbar: + self.__stop_clicked_cb(None) + self.destroy() + return True + + # if the user clicked out of the window - fix SL #3188 + if not self.is_active(): + self.present() + return False + + def __stop_clicked_cb(self, widget): + self.destroy() + + def __ok_clicked_cb(self, widget): + self.save_screenshot(self._search_entry.get_text()) + self.destroy() + + def save_screenshot(self, title): + settings = Gio.Settings('org.sugarlabs.user') + color = settings.get_string('color') + + jobject = datastore.create() + try: + jobject.metadata['title'] = title + jobject.metadata['keep'] = '0' + jobject.metadata['buddies'] = '' + jobject.metadata['preview'] = _get_preview_data(self.screenshot_surface) + jobject.metadata['icon-color'] = color + jobject.metadata['mime_type'] = 'image/png' + jobject.file_path = self.file_path + datastore.write(jobject, transfer_ownership=True) + finally: + jobject.destroy() + del jobject + + +def generate_thumbnail(): + ''' + Generates the thumbnail to be displayed + on the screenshot alert popup + ''' + window = Gdk.get_default_root_window() + width, height = window.get_width(), window.get_height() + thumb_width, thumb_height = style.zoom(200), style.zoom(160) + + thumb_surface = Gdk.Window.create_similar_surface( + window, cairo.CONTENT_COLOR, thumb_width, thumb_height) + + cairo_context = cairo.Context(thumb_surface) + thumb_scale_w = thumb_width * 1.0 / width + thumb_scale_h = thumb_height * 1.0 / height + cairo_context.scale(thumb_scale_w, thumb_scale_h) + Gdk.cairo_set_source_window(cairo_context, window, 0, 0) + cairo_context.paint() + + link_width, link_height = style.zoom(200), style.zoom(160) + link_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + link_width, link_height) + + cairo_context = cairo.Context(link_surface) + dest_x = style.zoom(0) + dest_y = style.zoom(0) + cairo_context.set_source_surface(thumb_surface, dest_x, dest_y) + thumb_width, thumb_height = style.zoom(200), style.zoom(160) + cairo_context.rectangle(dest_x, dest_y, thumb_width, thumb_height) + cairo_context.fill() + + bg_width, bg_height = style.zoom(200), style.zoom(160) + pixbuf_bg = Gdk.pixbuf_get_from_surface(link_surface, 0, 0, + bg_width, bg_height) + + preview_image = Gtk.Image() + preview_image.set_from_pixbuf(pixbuf_bg) + + # Set's the default title + content_title = None + shell_model = shell.get_model() + zoom_level = shell_model.zoom_level + + # TRANS: Nouns of what a screenshot contains + if zoom_level == shell_model.ZOOM_MESH: + content_title = _('Mesh') + elif zoom_level == shell_model.ZOOM_GROUP: + content_title = _('Group') + elif zoom_level == shell_model.ZOOM_HOME: + content_title = _('Home') + elif zoom_level == shell_model.ZOOM_ACTIVITY: + activity = shell_model.get_active_activity() + if activity is not None: + content_title = activity.get_title() + if content_title is None: + content_title = _('Activity') + + if content_title is None: + title = _('Screenshot') + else: + title = _('Screenshot of \"%s\"') % content_title + + return preview_image, title + + +def take_screenshot(): + ''' + Captures a screenshot and saves + it to a temp dir. + ''' + tmp_dir = os.path.join(env.get_profile_path(), 'data') + fd, file_path = tempfile.mkstemp(dir=tmp_dir) + os.close(fd) + + window = Gdk.get_default_root_window() + width, height = window.get_width(), window.get_height() + + screenshot_surface = Gdk.Window.create_similar_surface( + window, cairo.CONTENT_COLOR, width, height) + + cr = cairo.Context(screenshot_surface) + Gdk.cairo_set_source_window(cr, window, 0, 0) + cr.paint() + screenshot_surface.write_to_png(file_path) + + return screenshot_surface, file_path + + +def _get_preview_data(screenshot_surface): + screenshot_width = screenshot_surface.get_width() + screenshot_height = screenshot_surface.get_height() + + preview_width, preview_height = style.zoom(300), style.zoom(225) + preview_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + preview_width, preview_height) + cr = cairo.Context(preview_surface) + + scale_w = preview_width * 1.0 / screenshot_width + scale_h = preview_height * 1.0 / screenshot_height + scale = min(scale_w, scale_h) + + translate_x = int((preview_width - (screenshot_width * scale)) / 2) + translate_y = int((preview_height - (screenshot_height * scale)) / 2) + + cr.translate(translate_x, translate_y) + cr.scale(scale, scale) + + cr.set_source_rgba(1, 1, 1, 0) + cr.set_operator(cairo.OPERATOR_SOURCE) + cr.paint() + cr.set_source_surface(screenshot_surface) + cr.paint() + + preview_str = StringIO.StringIO() + preview_surface.write_to_png(preview_str) + + return dbus.ByteArray(preview_str.getvalue()) + + + + + diff --git a/src/jarabe/screenshotpanel/toolbar.py b/src/jarabe/screenshotpanel/toolbar.py new file mode 100644 index 0000000000..3ae18fe7ee --- /dev/null +++ b/src/jarabe/screenshotpanel/toolbar.py @@ -0,0 +1,86 @@ +# Copyright (C) 2007, 2008 One Laptop Per Child +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gi.repository import Gtk +import gettext +from gi.repository import GObject + +_ = lambda msg: gettext.dgettext('sugar', msg) + +from sugar3.graphics.icon import Icon +from sugar3.graphics.toolbutton import ToolButton +from sugar3.graphics import iconentry +from sugar3.graphics import style + + +class MainToolbar(Gtk.Toolbar): + """ Main toolbar of the screenshot panel + """ + __gtype_name__ = 'ScreenshotMainToolbar' + + __gsignals__ = { + 'stop-clicked': (GObject.SignalFlags.RUN_FIRST, + None, + ([])), + 'ok-clicked': (GObject.SignalFlags.RUN_FIRST, + None, + ([])), + } + + def __init__(self): + Gtk.Toolbar.__init__(self) + + self._add_separator() + + tool_item = Gtk.ToolItem() + self.insert(tool_item, -1) + tool_item.show() + + message = Gtk.Label() + message.set_markup(""+gettext.gettext('Save Screenshot')+"") + tool_item.add(message) + message.show() + + self._add_separator(True) + + self.stop = ToolButton(icon_name='dialog-cancel') + self.stop.set_tooltip(_('Cancel')) + self.stop.connect('clicked', self.__stop_clicked_cb) + self.insert(self.stop, -1) + self.stop.show() + + self.accept = ToolButton(icon_name='dialog-ok') + self.accept.set_tooltip(_('Done')) + self.accept.connect('clicked', self.__ok_clicked_cb) + self.insert(self.accept, -1) + self.accept.show() + + def _add_separator(self, expand=False): + separator = Gtk.SeparatorToolItem() + separator.props.draw = False + if expand: + separator.set_expand(True) + else: + separator.set_size_request(style.DEFAULT_SPACING, -1) + self.insert(separator, -1) + separator.show() + + def __stop_clicked_cb(self, button): + self.emit('stop-clicked') + + def __ok_clicked_cb(self, button): + self.emit('ok-clicked') +