diff --git a/extensions/deviceicon/display.py b/extensions/deviceicon/display.py index e9a851a137..ca11c850ee 100644 --- a/extensions/deviceicon/display.py +++ b/extensions/deviceicon/display.py @@ -32,7 +32,7 @@ from jarabe.frame.frameinvoker import FrameWidgetInvoker from jarabe.model import brightness -from jarabe.model.screenshot import take_screenshot +from jarabe.view.screenshotpopup import ScreenshotPanel from jarabe import frame @@ -90,7 +90,7 @@ def __init__(self, text, icon_name): icon = Icon(pixel_size=style.SMALL_ICON_SIZE) icon.props.icon_name = icon_name icon.props.xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(), - style.COLOR_BUTTON_GREY.get_svg())) + style.COLOR_BUTTON_GREY.get_svg())) icon.show() label = Gtk.Label(text) @@ -228,7 +228,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 ee84dfad0b..20dbe2d8f3 100644 --- a/extensions/globalkey/screenshot.py +++ b/extensions/globalkey/screenshot.py @@ -14,10 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from jarabe.model.screenshot import take_screenshot +from jarabe.view.screenshotpopup import ScreenshotPanel BOUND_KEYS = ['1', 'Print'] def handle_key_press(key): - take_screenshot() + panel = ScreenshotPanel() diff --git a/src/jarabe/desktop/activitychooser.py b/src/jarabe/desktop/activitychooser.py index cfb30da0f1..b6c42f8b7b 100644 --- a/src/jarabe/desktop/activitychooser.py +++ b/src/jarabe/desktop/activitychooser.py @@ -33,6 +33,14 @@ class TitleBox(Gtk.Toolbar): + ''' + Title box at the top of the pop-up window. + Title and close button are added to the box and as needed + more widgets can be added using self.add_widget method. + This box is optional as the inherited class can remove this + block by setting the self._set_title_box to False. + ''' + def __init__(self): Gtk.Toolbar.__init__(self) @@ -52,9 +60,16 @@ def __init__(self): tool_item.show() def set_title(self, title): + ''' + setter function for 'title' property. + Args: + title (str): title for pop-up window + ''' self._label.set_markup('%s' % title) self._label.show() + title = GObject.Property(type=str, setter=set_title) + _AUTOSEARCH_TIMEOUT = 1000 @@ -149,6 +164,17 @@ def __init__(self): self.show() + def get_title_box(self): + ''' + Getter method for title-box + + Returns: + self._title_box (): Title or Tool Box + ''' + return self._title_box + + title_box = GObject.Property(type=str, getter=get_title_box) + def __close_button_clicked_cb(self, button): self.destroy() diff --git a/src/jarabe/view/Makefile.am b/src/jarabe/view/Makefile.am index da7fe1d8a0..c055d002eb 100644 --- a/src/jarabe/view/Makefile.am +++ b/src/jarabe/view/Makefile.am @@ -11,6 +11,7 @@ sugar_PYTHON = \ launcher.py \ palettes.py \ pulsingicon.py \ + screenshotpopup.py \ service.py \ tabbinghandler.py \ viewsource.py \ diff --git a/src/jarabe/view/screenshotpopup.py b/src/jarabe/view/screenshotpopup.py new file mode 100644 index 0000000000..82d0ff414d --- /dev/null +++ b/src/jarabe/view/screenshotpopup.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2016 Utkarsh Tiwari +# Copyright (C) 2019 Ibiam Chihurumnaya +# +# 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 + +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 sugar3.graphics.toolbutton import ToolButton + + +from jarabe.model.session import get_session_manager +from jarabe import config +from jarabe.model import shell +from jarabe.desktop.activitychooser import ActivityChooser + + +_logger = logging.getLogger('ScreenshotPanel') + +THUMBNAIL_SIZE = style.zoom( + Gdk.Screen.height() / 3), style.zoom(Gdk.Screen.height() / 4.5) + + +class ScreenshotPanel(ActivityChooser): + ''' + Generates a pop papel to allow user to save the + screenshot by the name of his choice + ''' + + __gtype_name__ = 'ScreenshotPanel' + + def __init__(self): + ActivityChooser.__init__(self) + + self._set_screensize() + # self.props.size = ((height / 6), + # (width / 5)) + self.get_title_box().props.title = _('Save Screenshot') + self._title_box.close_button.connect('clicked', self._close_cb) + self.set_keep_above(True) + + self.modify_bg(Gtk.StateType.NORMAL, + style.COLOR_BLACK.get_gdk_color()) + self.set_border_width(style.LINE_WIDTH) + self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + self.set_decorated(False) + self.set_resizable(False) + + 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._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() + + # Generates the thumbnail + self.screenshot_surface, self.file_path = take_screenshot() + preview_image, activity_title = generate_thumbnail( + self.screenshot_surface) + self._main_view.add(preview_image) + preview_image.show() + + self._vbox = Gtk.VBox() + self._hbox.pack_start(self._vbox, True, True, 0) + self._vbox.show() + + self._hbox = Gtk.HBox() + self._hbox.show() + + # Name entry box + self._name_view = Gtk.EventBox.new() + 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, 1, 0, 0) + halign.add(self._name_view) + halign.show() + + self._hbox.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._vbox.pack_start(self._hbox, True, True, 0) + + self._buttons_box = Gtk.HButtonBox() + self._buttons_box.set_layout(Gtk.ButtonBoxStyle.CENTER) + self._buttons_box.set_spacing(style.DEFAULT_SPACING) + + _ok = Gtk.Button() + _ok.set_image(Icon(icon_name='dialog-ok')) + _ok.set_label(_('Yes')) + _ok.connect('clicked', self.__ok_clicked_cb) + self._buttons_box.pack_start(_ok, True, True, 0) + _ok.show() + + _cancel = Gtk.Button() + _cancel.set_image(Icon(icon_name='dialog-cancel')) + _cancel.set_label(_('No')) + _cancel.connect('clicked', self.__cancel_clicked_cb) + self._buttons_box.pack_start(_cancel, True, True, 0) + _cancel.show() + + self._vbox.pack_start(self._buttons_box, True, True, 0) + self._buttons_box.show() + self._search_entry.grab_focus() + self.show() + + def _close_cb(self): + self.destroy() + + def __cancel_clicked_cb(self, widget): + self.destroy() + + def __ok_clicked_cb(self, widget): + self.save_screenshot(self._search_entry.get_text()) + self.destroy() + + 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() / 5 + self.set_size_request(width, height) + + def __key_press_event_cb(self, window, event): + # if the user clicked out of the window - fix SL #3188 + keyname = Gdk.keyval_name(event.keyval) + if keyname == 'Escape': + self.destroy() + + if keyname == 'Return': + self.save_screenshot(self._search_entry.get_text()) + self.destroy() + + if not self.is_active(): + self.present() + return False + + 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(screenshot_surface): + ''' + 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 = THUMBNAIL_SIZE + + cairo_context = cairo.Context(screenshot_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 = THUMBNAIL_SIZE + 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(screenshot_surface, dest_x, dest_y) + thumb_width, thumb_height = THUMBNAIL_SIZE + cairo_context.rectangle(dest_x, dest_y, thumb_width, thumb_height) + cairo_context.fill() + + bg_width, bg_height = THUMBNAIL_SIZE + 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 = THUMBNAIL_SIZE + 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())