From aa3a853c9d06d0321e8924cbfbc42c8411f571f5 Mon Sep 17 00:00:00 2001 From: monsta Date: Wed, 9 Sep 2015 12:35:05 +0300 Subject: mate-screenshot: move stuff to data/ and src/ subdirs --- mate-screenshot/Makefile.am | 94 +- mate-screenshot/data/Makefile.am | 40 + mate-screenshot/data/mate-screenshot.1 | 58 + .../data/mate-screenshot.appdata.xml.in | 27 + mate-screenshot/data/mate-screenshot.convert | 6 + mate-screenshot/data/mate-screenshot.desktop.in | 15 + mate-screenshot/data/mate-screenshot.ui | 251 ++++ .../data/org.mate.screenshot.gschema.xml.in | 29 + mate-screenshot/mate-screenshot.1 | 58 - mate-screenshot/mate-screenshot.appdata.xml.in | 27 - mate-screenshot/mate-screenshot.c | 1372 -------------------- mate-screenshot/mate-screenshot.convert | 6 - mate-screenshot/mate-screenshot.desktop.in | 15 - mate-screenshot/mate-screenshot.ui | 251 ---- mate-screenshot/org.mate.screenshot.gschema.xml.in | 29 - mate-screenshot/screenshot-dialog.c | 422 ------ mate-screenshot/screenshot-dialog.h | 42 - mate-screenshot/screenshot-save.c | 285 ---- mate-screenshot/screenshot-save.h | 34 - mate-screenshot/screenshot-shadow.c | 235 ---- mate-screenshot/screenshot-shadow.h | 28 - mate-screenshot/screenshot-utils.c | 1040 --------------- mate-screenshot/screenshot-utils.h | 50 - mate-screenshot/screenshot-xfer.c | 393 ------ mate-screenshot/screenshot-xfer.h | 45 - mate-screenshot/src/Makefile.am | 44 + mate-screenshot/src/mate-screenshot.c | 1372 ++++++++++++++++++++ mate-screenshot/src/screenshot-dialog.c | 422 ++++++ mate-screenshot/src/screenshot-dialog.h | 42 + mate-screenshot/src/screenshot-save.c | 285 ++++ mate-screenshot/src/screenshot-save.h | 34 + mate-screenshot/src/screenshot-shadow.c | 235 ++++ mate-screenshot/src/screenshot-shadow.h | 28 + mate-screenshot/src/screenshot-utils.c | 1040 +++++++++++++++ mate-screenshot/src/screenshot-utils.h | 50 + mate-screenshot/src/screenshot-xfer.c | 393 ++++++ mate-screenshot/src/screenshot-xfer.h | 45 + 37 files changed, 4417 insertions(+), 4425 deletions(-) create mode 100644 mate-screenshot/data/Makefile.am create mode 100644 mate-screenshot/data/mate-screenshot.1 create mode 100644 mate-screenshot/data/mate-screenshot.appdata.xml.in create mode 100644 mate-screenshot/data/mate-screenshot.convert create mode 100644 mate-screenshot/data/mate-screenshot.desktop.in create mode 100644 mate-screenshot/data/mate-screenshot.ui create mode 100644 mate-screenshot/data/org.mate.screenshot.gschema.xml.in delete mode 100644 mate-screenshot/mate-screenshot.1 delete mode 100644 mate-screenshot/mate-screenshot.appdata.xml.in delete mode 100644 mate-screenshot/mate-screenshot.c delete mode 100644 mate-screenshot/mate-screenshot.convert delete mode 100644 mate-screenshot/mate-screenshot.desktop.in delete mode 100644 mate-screenshot/mate-screenshot.ui delete mode 100644 mate-screenshot/org.mate.screenshot.gschema.xml.in delete mode 100644 mate-screenshot/screenshot-dialog.c delete mode 100644 mate-screenshot/screenshot-dialog.h delete mode 100644 mate-screenshot/screenshot-save.c delete mode 100644 mate-screenshot/screenshot-save.h delete mode 100644 mate-screenshot/screenshot-shadow.c delete mode 100644 mate-screenshot/screenshot-shadow.h delete mode 100644 mate-screenshot/screenshot-utils.c delete mode 100644 mate-screenshot/screenshot-utils.h delete mode 100644 mate-screenshot/screenshot-xfer.c delete mode 100644 mate-screenshot/screenshot-xfer.h create mode 100644 mate-screenshot/src/Makefile.am create mode 100644 mate-screenshot/src/mate-screenshot.c create mode 100644 mate-screenshot/src/screenshot-dialog.c create mode 100644 mate-screenshot/src/screenshot-dialog.h create mode 100644 mate-screenshot/src/screenshot-save.c create mode 100644 mate-screenshot/src/screenshot-save.h create mode 100644 mate-screenshot/src/screenshot-shadow.c create mode 100644 mate-screenshot/src/screenshot-shadow.h create mode 100644 mate-screenshot/src/screenshot-utils.c create mode 100644 mate-screenshot/src/screenshot-utils.h create mode 100644 mate-screenshot/src/screenshot-xfer.c create mode 100644 mate-screenshot/src/screenshot-xfer.h (limited to 'mate-screenshot') diff --git a/mate-screenshot/Makefile.am b/mate-screenshot/Makefile.am index 7cce5d0c..29d83f25 100644 --- a/mate-screenshot/Makefile.am +++ b/mate-screenshot/Makefile.am @@ -1,94 +1,2 @@ -NULL = +SUBDIRS = data src -AM_CPPFLAGS = \ - -I. \ - -I$(srcdir) \ - -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ - -DUIDIR=\""$(uidir)"\" \ - $(DISABLE_DEPRECATED) \ - $(NULL) - -bin_PROGRAMS = \ - mate-screenshot \ - $(NULL) - -mate_screenshot_SOURCES = \ - mate-screenshot.c \ - screenshot-dialog.c \ - screenshot-dialog.h \ - screenshot-shadow.c \ - screenshot-shadow.h \ - screenshot-utils.c \ - screenshot-utils.h \ - screenshot-save.c \ - screenshot-save.h \ - screenshot-xfer.c \ - screenshot-xfer.h \ - $(NULL) - -mate_screenshot_CFLAGS = \ - $(GLIB_CFLAGS) \ - $(GIO_CFLAGS) \ - $(LIBCANBERRA_GTK_CFLAGS) \ - $(GTHREAD_CFLAGS) \ - $(GTK_CFLAGS) \ - $(NULL) - -mate_screenshot_LDFLAGS = -export-dynamic - -mate_screenshot_LDADD = \ - $(XSHAPE_LIBS) \ - $(GLIB_LIBS) \ - $(GIO_LIBS) \ - $(LIBCANBERRA_GTK_LIBS) \ - $(GTHREAD_LIBS) \ - $(GTK_LIBS) \ - -lm \ - $(NULL) - -mate_screenshotdir = $(datadir)/applications -mate_screenshot_in_files = mate-screenshot.desktop.in -mate_screenshot_DATA = $(mate_screenshot_in_files:.desktop.in=.desktop) -@INTLTOOL_DESKTOP_RULE@ - -man_MANS = mate-screenshot.1 - -uidir = $(datadir)/mate-screenshot -ui_DATA = \ - mate-screenshot.ui \ - $(NULL) - -@INTLTOOL_XML_RULE@ -appdatadir = $(datadir)/appdata -appdata_in_files = mate-screenshot.appdata.xml.in -appdata_DATA = $(appdata_in_files:.xml.in=.xml) - -gsettings_SCHEMAS = org.mate.screenshot.gschema.xml -@GSETTINGS_RULES@ - -convertdir = $(datadir)/MateConf/gsettings -convert_DATA = mate-screenshot.convert - -EXTRA_DIST = \ - $(appdata_in_files) \ - $(mate_screenshot_in_files) \ - $(man_MANS) \ - $(ui_DATA) \ - $(convert_DATA) \ - $(NULL) - -CLEANFILES = \ - $(BUILT_SOURCES) \ - $(gsettings_SCHEMAS) \ - $(mate_screenshot_DATA) \ - $(sys_DATA) \ - $(appdata_DATA) - -dist-hook: - cd $(distdir) ; rm -f $(CLEANFILES) - -install-exec-local: - rm -f $(DESTDIR)$(bindir)/mate-panel-screenshot - ln -s mate-screenshot $(DESTDIR)$(bindir)/mate-panel-screenshot - rm -f $(DESTDIR)$(mandir)/man1/mate-panel-screenshot.1 - ln -s mate-screenshot.1 $(DESTDIR)$(mandir)/man1/mate-panel-screenshot.1 diff --git a/mate-screenshot/data/Makefile.am b/mate-screenshot/data/Makefile.am new file mode 100644 index 00000000..b75f3162 --- /dev/null +++ b/mate-screenshot/data/Makefile.am @@ -0,0 +1,40 @@ +mate_screenshotdir = $(datadir)/applications +mate_screenshot_in_files = mate-screenshot.desktop.in +mate_screenshot_DATA = $(mate_screenshot_in_files:.desktop.in=.desktop) +@INTLTOOL_DESKTOP_RULE@ + +man_MANS = mate-screenshot.1 + +uidir = $(datadir)/mate-screenshot +ui_DATA = mate-screenshot.ui + +@INTLTOOL_XML_RULE@ +appdatadir = $(datadir)/appdata +appdata_in_files = mate-screenshot.appdata.xml.in +appdata_DATA = $(appdata_in_files:.xml.in=.xml) + +gsettings_SCHEMAS = org.mate.screenshot.gschema.xml +@GSETTINGS_RULES@ + +convertdir = $(datadir)/MateConf/gsettings +convert_DATA = mate-screenshot.convert + +EXTRA_DIST = \ + $(appdata_in_files) \ + $(mate_screenshot_in_files) \ + $(man_MANS) \ + $(ui_DATA) \ + $(convert_DATA) + +CLEANFILES = \ + $(gsettings_SCHEMAS) \ + $(mate_screenshot_DATA) \ + $(appdata_DATA) + +dist-hook: + cd $(distdir) ; rm -f $(CLEANFILES) + +install-exec-local: + rm -f $(DESTDIR)$(mandir)/man1/mate-panel-screenshot.1 + ln -s mate-screenshot.1 $(DESTDIR)$(mandir)/man1/mate-panel-screenshot.1 + diff --git a/mate-screenshot/data/mate-screenshot.1 b/mate-screenshot/data/mate-screenshot.1 new file mode 100644 index 00000000..8728c2dc --- /dev/null +++ b/mate-screenshot/data/mate-screenshot.1 @@ -0,0 +1,58 @@ +.TH "MATE-SCREENSHOT" "1" "June 28, 2009" "" "" +.SH NAME +mate-screenshot \- capture the screen, a window, or an user\-defined area and save the snapshot image to a file. +.SH SYNOPSIS +.sp +\fBmate-screenshot\fR [ \fB\-w\fR ] [ \fB\-a\fR ] [ \fB\-b\fR ] [ \fB\-B\fR ] [ \fB\-d \fISECONDS\fB \fR ] [ \fB\-e \fIEFFECT\fB \fR ] [ \fB\-i\fR ] [ \fB\-\-display \fIDISPLAY\fB \fR ] +.SH "DESCRIPTION" +.PP +\fBmate-screenshot\fR is a MATE utility for taking +screenshots of the entire screen, a window or an user\-defined area of the screen, with optional beutifying +border effects. +.SH "OPTIONS" +.TP +\fB\-w, \-\-window\fR +Grab the current active window instead of the entire +screen. +.TP +\fB\-a, \-\-area\fR +Grab an area of the screen instead of the entire screen. +.TP +\fB\-b, \-\-include-border\fR +Include the window border within the screenshot. +.TP +\fB\-B, \-\-remove-border\fR +Remove the window border from the screenshot. +.TP +\fB\-d, \-\-delay=\fISECONDS\fB,\fR +Take the screenshot after the specified delay [in seconds]. +.TP +\fB\-e, \-\-effect=\fIEFFECT\fB,\fR +Add an effect to the outside of the screenshot border. +\fIEFFECT\fR can be ``shadow'' +(adding drop shadow), ``border'' (adding rectangular +space around the screenshot) or ``none'' (no effect). +Default is ``none''. +.TP +\fB\-i, \-\-interactive\fR +Interactively set options in a dialog. +.TP +\fB\-\-display=\fIDISPLAY\fB\fR +X display to use. +.TP +\fB\-?, \-h, \-\-help\fR +Show a summary of the available options. +.PP +In addition, the usual GTK+ and MATE command line options apply. +See the output of \-\-help for details. +.PP +.SH "SEE ALSO" +.PP +mate-options(7), gtk-options(7) +.SH "AUTHOR" +.PP +This manual page was written by Christian Marillat for +the Debian GNU/Linux system (but may be used by others). +.PP +Updated by Theppitak Karoonboonyanan +, Tom Feiner and Cosimo Cecchi diff --git a/mate-screenshot/data/mate-screenshot.appdata.xml.in b/mate-screenshot/data/mate-screenshot.appdata.xml.in new file mode 100644 index 00000000..9d1200c1 --- /dev/null +++ b/mate-screenshot/data/mate-screenshot.appdata.xml.in @@ -0,0 +1,27 @@ + + + + mate-screenshot.desktop + CC0-1.0 + GPL-2.0+ + MATE Screenshot + <_summary>A screenshot utility for MATE Desktop + <_description> +

+ MATE Screenshot is a simple utility that lets you capture screenshots + of your desktop or of application windows. You can select to copy them + to the system clipboard or save them in Portable Network Graphics (.png) + image format. +

+ + + + + https://alexpl.fedorapeople.org/AppData/mate-screenshot/screens/mate-screenshot_01.png + + + + http://www.mate-desktop.org + mate-dev@ml.mate-desktop.org + MATE +
diff --git a/mate-screenshot/data/mate-screenshot.convert b/mate-screenshot/data/mate-screenshot.convert new file mode 100644 index 00000000..741f7d44 --- /dev/null +++ b/mate-screenshot/data/mate-screenshot.convert @@ -0,0 +1,6 @@ +[org.mate.screenshot] +delay = /apps/mate-screenshot/delay +last-save-directory = /apps/mate-screenshot/last_save_directory +include-border = /apps/mate-screenshot/include_border +include-pointer = /apps/mate-screenshot/include_pointer +border-effect = /apps/mate-screenshot/border_effect diff --git a/mate-screenshot/data/mate-screenshot.desktop.in b/mate-screenshot/data/mate-screenshot.desktop.in new file mode 100644 index 00000000..fedc1321 --- /dev/null +++ b/mate-screenshot/data/mate-screenshot.desktop.in @@ -0,0 +1,15 @@ +[Desktop Entry] +_Name=Take Screenshot +_Comment=Save images of your desktop or individual windows +Exec=mate-screenshot --interactive +Terminal=false +Type=Application +Icon=applets-screenshooter +StartupNotify=true +Categories=GTK;Utility; +Keywords=MATE;screenshot;snapshot;desktop;window;image; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-utils +X-MATE-Bugzilla-Component=screenshot +X-MATE-Bugzilla-OtherBinaries=mate-panel-screenshot diff --git a/mate-screenshot/data/mate-screenshot.ui b/mate-screenshot/data/mate-screenshot.ui new file mode 100644 index 00000000..80382ed4 --- /dev/null +++ b/mate-screenshot/data/mate-screenshot.ui @@ -0,0 +1,251 @@ + + + + + 5 + Save Screenshot + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + + + + True + False + 2 + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-help + True + GTK_RELIEF_NORMAL + True + + + + + True + True + True + True + C_opy to Clipboard + GTK_RELIEF_NORMAL + True + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + True + True + True + True + gtk-save + True + GTK_RELIEF_NORMAL + True + + + + + 0 + False + True + GTK_PACK_END + + + + + 5 + True + False + 18 + + + True + False + 12 + + + True + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + True + 0 + 0 + GTK_SHADOW_IN + 0 + 0 + 1 + True + + + True + + + + + + + 0 + False + True + + + + + True + 2 + 2 + False + 6 + 12 + + + True + _Name: + True + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + filename_entry + + + 0 + 1 + 0 + 1 + fill + + + + + + True + Save in _folder: + True + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + True + True + True + True + 0 + + True + * + True + 32 + + + 1 + 2 + 0 + 1 + + + + + + True + False + 0 + + + + + + 1 + 2 + 1 + 2 + fill + fill + + + + + 0 + True + True + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + help_button + cancel_button + ok_button + copy_button + + + diff --git a/mate-screenshot/data/org.mate.screenshot.gschema.xml.in b/mate-screenshot/data/org.mate.screenshot.gschema.xml.in new file mode 100644 index 00000000..c926d737 --- /dev/null +++ b/mate-screenshot/data/org.mate.screenshot.gschema.xml.in @@ -0,0 +1,29 @@ + + + + 0 + Screenshot delay + The number of seconds to wait before taking the screenshot. + + + '' + Screenshot directory + The directory the last screenshot was saved in. + + + true + Include Border + Include the window manager border along with the screenshot + + + true + Include Pointer + Include the pointer in the screenshot + + + 'none' + Border Effect + Effect to add to the outside of a border. Possible values are "shadow", "none", and "border". + + + diff --git a/mate-screenshot/mate-screenshot.1 b/mate-screenshot/mate-screenshot.1 deleted file mode 100644 index 8728c2dc..00000000 --- a/mate-screenshot/mate-screenshot.1 +++ /dev/null @@ -1,58 +0,0 @@ -.TH "MATE-SCREENSHOT" "1" "June 28, 2009" "" "" -.SH NAME -mate-screenshot \- capture the screen, a window, or an user\-defined area and save the snapshot image to a file. -.SH SYNOPSIS -.sp -\fBmate-screenshot\fR [ \fB\-w\fR ] [ \fB\-a\fR ] [ \fB\-b\fR ] [ \fB\-B\fR ] [ \fB\-d \fISECONDS\fB \fR ] [ \fB\-e \fIEFFECT\fB \fR ] [ \fB\-i\fR ] [ \fB\-\-display \fIDISPLAY\fB \fR ] -.SH "DESCRIPTION" -.PP -\fBmate-screenshot\fR is a MATE utility for taking -screenshots of the entire screen, a window or an user\-defined area of the screen, with optional beutifying -border effects. -.SH "OPTIONS" -.TP -\fB\-w, \-\-window\fR -Grab the current active window instead of the entire -screen. -.TP -\fB\-a, \-\-area\fR -Grab an area of the screen instead of the entire screen. -.TP -\fB\-b, \-\-include-border\fR -Include the window border within the screenshot. -.TP -\fB\-B, \-\-remove-border\fR -Remove the window border from the screenshot. -.TP -\fB\-d, \-\-delay=\fISECONDS\fB,\fR -Take the screenshot after the specified delay [in seconds]. -.TP -\fB\-e, \-\-effect=\fIEFFECT\fB,\fR -Add an effect to the outside of the screenshot border. -\fIEFFECT\fR can be ``shadow'' -(adding drop shadow), ``border'' (adding rectangular -space around the screenshot) or ``none'' (no effect). -Default is ``none''. -.TP -\fB\-i, \-\-interactive\fR -Interactively set options in a dialog. -.TP -\fB\-\-display=\fIDISPLAY\fB\fR -X display to use. -.TP -\fB\-?, \-h, \-\-help\fR -Show a summary of the available options. -.PP -In addition, the usual GTK+ and MATE command line options apply. -See the output of \-\-help for details. -.PP -.SH "SEE ALSO" -.PP -mate-options(7), gtk-options(7) -.SH "AUTHOR" -.PP -This manual page was written by Christian Marillat for -the Debian GNU/Linux system (but may be used by others). -.PP -Updated by Theppitak Karoonboonyanan -, Tom Feiner and Cosimo Cecchi diff --git a/mate-screenshot/mate-screenshot.appdata.xml.in b/mate-screenshot/mate-screenshot.appdata.xml.in deleted file mode 100644 index 9d1200c1..00000000 --- a/mate-screenshot/mate-screenshot.appdata.xml.in +++ /dev/null @@ -1,27 +0,0 @@ - - - - mate-screenshot.desktop - CC0-1.0 - GPL-2.0+ - MATE Screenshot - <_summary>A screenshot utility for MATE Desktop - <_description> -

- MATE Screenshot is a simple utility that lets you capture screenshots - of your desktop or of application windows. You can select to copy them - to the system clipboard or save them in Portable Network Graphics (.png) - image format. -

- - - - - https://alexpl.fedorapeople.org/AppData/mate-screenshot/screens/mate-screenshot_01.png - - - - http://www.mate-desktop.org - mate-dev@ml.mate-desktop.org - MATE -
diff --git a/mate-screenshot/mate-screenshot.c b/mate-screenshot/mate-screenshot.c deleted file mode 100644 index 3e121f00..00000000 --- a/mate-screenshot/mate-screenshot.c +++ /dev/null @@ -1,1372 +0,0 @@ -/* mate-screenshot.c - Take a screenshot of the desktop - * - * Copyright (C) 2001 Jonathan Blandford - * Copyright (C) 2006 Emmanuele Bassi - * Copyright (C) 2008 Cosimo Cecchi - * - * 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 - */ - -/* THERE ARE NO FEATURE REQUESTS ALLOWED */ -/* IF YOU WANT YOUR OWN FEATURE -- WRITE THE DAMN THING YOURSELF (-: */ -/* MAYBE I LIED... -jrb */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "screenshot-shadow.h" -#include "screenshot-utils.h" -#include "screenshot-save.h" -#include "screenshot-dialog.h" -#include "screenshot-xfer.h" - -#define SCREENSHOOTER_ICON "applets-screenshooter" - -#define MATE_SCREENSHOT_SCHEMA "org.mate.screenshot" -#define INCLUDE_BORDER_KEY "include-border" -#define INCLUDE_POINTER_KEY "include-pointer" -#define LAST_SAVE_DIRECTORY_KEY "last-save-directory" -#define BORDER_EFFECT_KEY "border-effect" -#define DELAY_KEY "delay" -#define CAJA_PREFERENCES_SCHEMA "org.mate.caja.preferences" - -enum -{ - COLUMN_NICK, - COLUMN_LABEL, - COLUMN_ID, - - N_COLUMNS -}; - -typedef enum { - SCREENSHOT_EFFECT_NONE, - SCREENSHOT_EFFECT_SHADOW, - SCREENSHOT_EFFECT_BORDER -} ScreenshotEffectType; - -typedef enum -{ - TEST_LAST_DIR = 0, - TEST_DESKTOP = 1, - TEST_TMP = 2, -} TestType; - -typedef struct -{ - char *base_uris[3]; - char *retval; - int iteration; - TestType type; - GdkWindow *window; - GdkRectangle *rectangle; -} AsyncExistenceJob; - -static GdkPixbuf *screenshot = NULL; - -/* Global variables*/ -static char *last_save_dir = NULL; -static char *window_title = NULL; -static char *temporary_file = NULL; -static gboolean save_immediately = FALSE; -static GSettings *settings = NULL; - -/* Options */ -static gboolean take_window_shot = FALSE; -static gboolean take_area_shot = FALSE; -static gboolean include_border = FALSE; -static gboolean include_pointer = TRUE; -static char *border_effect = NULL; -static guint delay = 0; - -/* some local prototypes */ -static void display_help (GtkWindow *parent); -static void save_done_notification (gpointer data); -static char *get_desktop_dir (void); -static void save_options (void); - -static GtkWidget *border_check = NULL; -static GtkWidget *effect_combo = NULL; -static GtkWidget *effect_label = NULL; -static GtkWidget *effects_vbox = NULL; -static GtkWidget *delay_hbox = NULL; - -static void -display_help (GtkWindow *parent) -{ - GError *error = NULL; - - gtk_show_uri (gtk_window_get_screen (parent), - "help:mate-user-guide/goseditmainmenu-53", - gtk_get_current_event_time (), &error); - - if (error) - { - screenshot_show_gerror_dialog (parent, - _("Error loading the help page"), - error); - g_error_free (error); - } -} - -static void -interactive_dialog_response_cb (GtkDialog *dialog, - gint response, - gpointer user_data) -{ - switch (response) - { - case GTK_RESPONSE_HELP: - g_signal_stop_emission_by_name (dialog, "response"); - display_help (GTK_WINDOW (dialog)); - break; - default: - gtk_widget_hide (GTK_WIDGET (dialog)); - break; - } -} - -#define TARGET_TOGGLE_DESKTOP 0 -#define TARGET_TOGGLE_WINDOW 1 -#define TARGET_TOGGLE_AREA 2 - -static void -target_toggled_cb (GtkToggleButton *button, - gpointer data) -{ - int target_toggle = GPOINTER_TO_INT (data); - - if (gtk_toggle_button_get_active (button)) - { - take_window_shot = (target_toggle == TARGET_TOGGLE_WINDOW); - take_area_shot = (target_toggle == TARGET_TOGGLE_AREA); - - gtk_widget_set_sensitive (border_check, take_window_shot); - gtk_widget_set_sensitive (effect_combo, take_window_shot); - gtk_widget_set_sensitive (effect_label, take_window_shot); - - gtk_widget_set_sensitive (delay_hbox, !take_area_shot); - gtk_widget_set_sensitive (effects_vbox, !take_area_shot); - } -} - -static void -delay_spin_value_changed_cb (GtkSpinButton *button) -{ - delay = gtk_spin_button_get_value_as_int (button); -} - -static void -include_border_toggled_cb (GtkToggleButton *button, - gpointer data) -{ - include_border = gtk_toggle_button_get_active (button); -} - -static void -include_pointer_toggled_cb (GtkToggleButton *button, - gpointer data) -{ - include_pointer = gtk_toggle_button_get_active (button); -} - -static void -effect_combo_changed_cb (GtkComboBox *combo, - gpointer user_data) -{ - GtkTreeIter iter; - - if (gtk_combo_box_get_active_iter (combo, &iter)) - { - GtkTreeModel *model; - gchar *effect; - - model = gtk_combo_box_get_model (combo); - gtk_tree_model_get (model, &iter, COLUMN_NICK, &effect, -1); - - g_assert (effect != NULL); - - g_free (border_effect); - border_effect = effect; /* gets free'd later */ - } -} - -static gint -key_press_cb (GtkWidget* widget, GdkEventKey* event, gpointer data) -{ - if (event->keyval == GDK_KEY_F1) - { - display_help (GTK_WINDOW (widget)); - return TRUE; - } - - return FALSE; -} - -typedef struct { - ScreenshotEffectType id; - const gchar *label; - const gchar *nick; -} ScreenshotEffect; - -/* Translators: - * these are the names of the effects available which will be - * displayed inside a combo box in interactive mode for the user - * to chooser. - */ -static const ScreenshotEffect effects[] = { - { SCREENSHOT_EFFECT_NONE, N_("None"), "none" }, - { SCREENSHOT_EFFECT_SHADOW, N_("Drop shadow"), "shadow" }, - { SCREENSHOT_EFFECT_BORDER, N_("Border"), "border" } -}; - -static guint n_effects = G_N_ELEMENTS (effects); - -static GtkWidget * -create_effects_combo (void) -{ - GtkWidget *retval; - GtkListStore *model; - GtkCellRenderer *renderer; - gint i; - - model = gtk_list_store_new (N_COLUMNS, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_UINT); - - for (i = 0; i < n_effects; i++) - { - GtkTreeIter iter; - - gtk_list_store_insert (model, &iter, i); - gtk_list_store_set (model, &iter, - COLUMN_ID, effects[i].id, - COLUMN_LABEL, gettext (effects[i].label), - COLUMN_NICK, effects[i].nick, - -1); - } - - retval = gtk_combo_box_new (); - gtk_combo_box_set_model (GTK_COMBO_BOX (retval), - GTK_TREE_MODEL (model)); - g_object_unref (model); - - switch (border_effect[0]) - { - case 's': /* shadow */ - gtk_combo_box_set_active (GTK_COMBO_BOX (retval), - SCREENSHOT_EFFECT_SHADOW); - break; - case 'b': /* border */ - gtk_combo_box_set_active (GTK_COMBO_BOX (retval), - SCREENSHOT_EFFECT_BORDER); - break; - case 'n': /* none */ - gtk_combo_box_set_active (GTK_COMBO_BOX (retval), - SCREENSHOT_EFFECT_NONE); - break; - default: - break; - } - - renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (retval), renderer, TRUE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (retval), renderer, - "text", COLUMN_LABEL, - NULL); - - g_signal_connect (retval, "changed", - G_CALLBACK (effect_combo_changed_cb), - NULL); - - return retval; -} - -static void -create_effects_frame (GtkWidget *outer_vbox, - const gchar *frame_title) -{ - GtkWidget *main_vbox, *vbox, *hbox; - GtkWidget *align; - GtkWidget *label; - GtkWidget *check; - GtkWidget *combo; - gchar *title; - - main_vbox = gtk_vbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (outer_vbox), main_vbox, FALSE, FALSE, 0); - gtk_widget_show (main_vbox); - effects_vbox = main_vbox; - - title = g_strconcat ("", frame_title, "", NULL); - label = gtk_label_new (title); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0); - gtk_widget_show (label); - g_free (title); - - hbox = gtk_hbox_new (FALSE, 12); - gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - align = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); - gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0); - gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0); - gtk_widget_show (align); - - vbox = gtk_vbox_new (FALSE, 6); - gtk_container_add (GTK_CONTAINER (align), vbox); - gtk_widget_show (vbox); - - /** Include pointer **/ - check = gtk_check_button_new_with_mnemonic (_("Include _pointer")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), include_pointer); - g_signal_connect (check, "toggled", - G_CALLBACK (include_pointer_toggled_cb), - NULL); - gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); - gtk_widget_show (check); - - /** Include window border **/ - check = gtk_check_button_new_with_mnemonic (_("Include the window _border")); - gtk_widget_set_sensitive (check, take_window_shot); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), include_border); - g_signal_connect (check, "toggled", - G_CALLBACK (include_border_toggled_cb), - NULL); - gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); - gtk_widget_show (check); - border_check = check; - - /** Effects **/ - hbox = gtk_hbox_new (FALSE, 12); - gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - label = gtk_label_new_with_mnemonic (_("Apply _effect:")); - gtk_widget_set_sensitive (label, take_window_shot); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); - gtk_widget_show (label); - effect_label = label; - - combo = create_effects_combo (); - gtk_widget_set_sensitive (combo, take_window_shot); - gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); - gtk_widget_show (combo); - effect_combo = combo; -} - -static void -create_screenshot_frame (GtkWidget *outer_vbox, - const gchar *frame_title) -{ - GtkWidget *main_vbox, *vbox, *hbox; - GtkWidget *align; - GtkWidget *radio; - GtkWidget *image; - GtkWidget *spin; - GtkWidget *label; - GtkAdjustment *adjust; - GSList *group; - gchar *title; - - main_vbox = gtk_vbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (outer_vbox), main_vbox, FALSE, FALSE, 0); - gtk_widget_show (main_vbox); - - title = g_strconcat ("", frame_title, "", NULL); - label = gtk_label_new (title); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0); - gtk_widget_show (label); - g_free (title); - - hbox = gtk_hbox_new (FALSE, 12); - gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - align = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); - gtk_widget_set_size_request (align, 48, -1); - gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0); - gtk_widget_show (align); - - image = gtk_image_new_from_stock (SCREENSHOOTER_ICON, - GTK_ICON_SIZE_DIALOG); - gtk_container_add (GTK_CONTAINER (align), image); - gtk_widget_show (image); - - vbox = gtk_vbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); - gtk_widget_show (vbox); - - /** Grab whole desktop **/ - group = NULL; - radio = gtk_radio_button_new_with_mnemonic (group, - _("Grab the whole _desktop")); - if (take_window_shot || take_area_shot) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), FALSE); - g_signal_connect (radio, "toggled", - G_CALLBACK (target_toggled_cb), - GINT_TO_POINTER (TARGET_TOGGLE_DESKTOP)); - gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, FALSE, 0); - group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)); - gtk_widget_show (radio); - - /** Grab current window **/ - radio = gtk_radio_button_new_with_mnemonic (group, - _("Grab the current _window")); - if (take_window_shot) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); - g_signal_connect (radio, "toggled", - G_CALLBACK (target_toggled_cb), - GINT_TO_POINTER (TARGET_TOGGLE_WINDOW)); - gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, FALSE, 0); - group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)); - gtk_widget_show (radio); - - /** Grab area of the desktop **/ - radio = gtk_radio_button_new_with_mnemonic (group, - _("Select _area to grab")); - if (take_area_shot) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); - g_signal_connect (radio, "toggled", - G_CALLBACK (target_toggled_cb), - GINT_TO_POINTER (TARGET_TOGGLE_AREA)); - gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, FALSE, 0); - gtk_widget_show (radio); - - /** Grab after delay **/ - delay_hbox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (vbox), delay_hbox, FALSE, FALSE, 0); - gtk_widget_show (delay_hbox); - - /* translators: this is the first part of the "grab after a - * delay of seconds". - */ - label = gtk_label_new_with_mnemonic (_("Grab _after a delay of")); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (delay_hbox), label, FALSE, FALSE, 0); - gtk_widget_show (label); - - adjust = GTK_ADJUSTMENT (gtk_adjustment_new ((gdouble) delay, - 0.0, 99.0, - 1.0, 1.0, - 0.0)); - spin = gtk_spin_button_new (adjust, 1.0, 0); - g_signal_connect (spin, "value-changed", - G_CALLBACK (delay_spin_value_changed_cb), - NULL); - gtk_box_pack_start (GTK_BOX (delay_hbox), spin, FALSE, FALSE, 0); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin); - gtk_widget_show (spin); - - /* translators: this is the last part of the "grab after a - * delay of seconds". - */ - label = gtk_label_new (_("seconds")); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_end (GTK_BOX (delay_hbox), label, FALSE, FALSE, 0); - gtk_widget_show (label); -} - -static GtkWidget * -create_interactive_dialog (void) -{ - GtkWidget *retval; - GtkWidget *main_vbox; - GtkWidget *content_area; - - retval = gtk_dialog_new (); - gtk_window_set_resizable (GTK_WINDOW (retval), FALSE); - gtk_container_set_border_width (GTK_CONTAINER (retval), 5); - content_area = gtk_dialog_get_content_area (GTK_DIALOG (retval)); - gtk_box_set_spacing (GTK_BOX (content_area), 2); - gtk_window_set_title (GTK_WINDOW (retval), _("Take Screenshot")); - - /* main container */ - main_vbox = gtk_vbox_new (FALSE, 18); - gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 5); - gtk_box_pack_start (GTK_BOX (content_area), main_vbox, TRUE, TRUE, 0); - gtk_widget_show (main_vbox); - - create_screenshot_frame (main_vbox, _("Take Screenshot")); - create_effects_frame (main_vbox, _("Effects")); - - gtk_dialog_add_buttons (GTK_DIALOG (retval), - GTK_STOCK_HELP, GTK_RESPONSE_HELP, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - _("Take _Screenshot"), GTK_RESPONSE_OK, - NULL); - - gtk_dialog_set_default_response (GTK_DIALOG (retval), GTK_RESPONSE_OK); - - /* we need to block on "response" and keep showing the interactive - * dialog in case the user did choose "help" - */ - g_signal_connect (retval, "response", - G_CALLBACK (interactive_dialog_response_cb), - NULL); - - g_signal_connect (G_OBJECT (retval), "key-press-event", - G_CALLBACK(key_press_cb), - NULL); - - return retval; -} - -static void -save_folder_to_settings (ScreenshotDialog *dialog) -{ - char *folder; - - folder = screenshot_dialog_get_folder (dialog); - g_settings_set_string (settings, - LAST_SAVE_DIRECTORY_KEY, folder); - - g_free (folder); -} - -static void -set_recent_entry (ScreenshotDialog *dialog) -{ - char *uri, *app_exec = NULL; - GtkRecentManager *recent; - GtkRecentData recent_data; - GAppInfo *app; - const char *exec_name = NULL; - static char * groups[2] = { "Graphics", NULL }; - - app = g_app_info_get_default_for_type ("image/png", TRUE); - - if (!app) { - /* return early, as this would be an useless recent entry anyway. */ - return; - } - - uri = screenshot_dialog_get_uri (dialog); - recent = gtk_recent_manager_get_default (); - - exec_name = g_app_info_get_executable (app); - app_exec = g_strjoin (" ", exec_name, "%u", NULL); - - recent_data.display_name = NULL; - recent_data.description = NULL; - recent_data.mime_type = "image/png"; - recent_data.app_name = "MATE Screenshot"; - recent_data.app_exec = app_exec; - recent_data.groups = groups; - recent_data.is_private = FALSE; - - gtk_recent_manager_add_full (recent, uri, &recent_data); - - g_object_unref (app); - g_free (app_exec); - g_free (uri); -} - -static void -error_dialog_response_cb (GtkDialog *d, - gint response, - ScreenshotDialog *dialog) -{ - gtk_widget_destroy (GTK_WIDGET (d)); - - screenshot_dialog_focus_entry (dialog); -} - -static void -save_callback (TransferResult result, - char *error_message, - gpointer data) -{ - ScreenshotDialog *dialog = data; - GtkWidget *toplevel; - - toplevel = screenshot_dialog_get_toplevel (dialog); - screenshot_dialog_set_busy (dialog, FALSE); - - if (result == TRANSFER_OK) - { - save_folder_to_settings (dialog); - set_recent_entry (dialog); - gtk_widget_destroy (toplevel); - - /* we're done, stop the mainloop now */ - gtk_main_quit (); - } - else if (result == TRANSFER_OVERWRITE || - result == TRANSFER_CANCELLED) - { - /* user has canceled the overwrite dialog or the transfer itself, let him - * choose another name. - */ - screenshot_dialog_focus_entry (dialog); - } - else /* result == TRANSFER_ERROR */ - { - /* we had an error, display a dialog to the user and let him choose - * another name/location to save the screenshot. - */ - GtkWidget *error_dialog; - char *uri; - - uri = screenshot_dialog_get_uri (dialog); - error_dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - _("Error while saving screenshot")); - /* translators: first %s is the file path, second %s is the VFS error */ - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), - _("Impossible to save the screenshot " - "to %s.\n Error was %s.\n Please choose another " - "location and retry."), uri, error_message); - gtk_widget_show (error_dialog); - g_signal_connect (error_dialog, - "response", - G_CALLBACK (error_dialog_response_cb), - dialog); - - g_free (uri); - } - -} - -static void -try_to_save (ScreenshotDialog *dialog, - const char *target) -{ - GFile *source_file, *target_file; - - g_assert (temporary_file); - - screenshot_dialog_set_busy (dialog, TRUE); - - source_file = g_file_new_for_path (temporary_file); - target_file = g_file_new_for_uri (target); - - screenshot_xfer_uri (source_file, - target_file, - screenshot_dialog_get_toplevel (dialog), - save_callback, dialog); - - /* screenshot_xfer_uri () holds a ref, so we can unref now */ - g_object_unref (source_file); - g_object_unref (target_file); -} - -static void -save_done_notification (gpointer data) -{ - ScreenshotDialog *dialog = data; - - temporary_file = g_strdup (screenshot_save_get_filename ()); - screenshot_dialog_enable_dnd (dialog); - - if (save_immediately) - { - GtkWidget *toplevel; - - toplevel = screenshot_dialog_get_toplevel (dialog); - gtk_dialog_response (GTK_DIALOG (toplevel), GTK_RESPONSE_OK); - } -} - -static void -screenshot_dialog_response_cb (GtkDialog *d, - gint response_id, - ScreenshotDialog *dialog) -{ - char *uri; - - if (response_id == GTK_RESPONSE_HELP) - { - display_help (GTK_WINDOW (d)); - } - else if (response_id == GTK_RESPONSE_OK) - { - uri = screenshot_dialog_get_uri (dialog); - if (temporary_file == NULL) - { - save_immediately = TRUE; - screenshot_dialog_set_busy (dialog, TRUE); - } - else - { - /* we've saved the temporary file, lets try to copy it to the - * correct location. - */ - try_to_save (dialog, uri); - } - g_free (uri); - } - else if (response_id == SCREENSHOT_RESPONSE_COPY) - { - GtkClipboard *clipboard; - GdkPixbuf *screenshot; - - clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (d)), - GDK_SELECTION_CLIPBOARD); - screenshot = screenshot_dialog_get_screenshot (dialog); - gtk_clipboard_set_image (clipboard, screenshot); - } - else /* dialog was canceled */ - { - gtk_widget_destroy (GTK_WIDGET (d)); - gtk_main_quit (); - } -} - - -static void -run_dialog (ScreenshotDialog *dialog) -{ - GtkWidget *toplevel; - - toplevel = screenshot_dialog_get_toplevel (dialog); - - gtk_widget_show (toplevel); - - g_signal_connect (toplevel, - "response", - G_CALLBACK (screenshot_dialog_response_cb), - dialog); -} - -static void -play_sound_effect (GdkWindow *window) -{ - ca_context *c; - ca_proplist *p = NULL; - int res; - - c = ca_gtk_context_get (); - - res = ca_proplist_create (&p); - if (res < 0) - goto done; - - res = ca_proplist_sets (p, CA_PROP_EVENT_ID, "screen-capture"); - if (res < 0) - goto done; - - res = ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Screenshot taken")); - if (res < 0) - goto done; - - if (window != NULL) - { - res = ca_proplist_setf (p, - CA_PROP_WINDOW_X11_XID, - "%lu", - (unsigned long) GDK_WINDOW_XID (window)); - if (res < 0) - goto done; - } - - ca_context_play_full (c, 0, p, NULL, NULL); - - done: - if (p != NULL) - ca_proplist_destroy (p); - -} - -static void -finish_prepare_screenshot (char *initial_uri, GdkWindow *window, GdkRectangle *rectangle) -{ - ScreenshotDialog *dialog; - gboolean include_mask = (!take_window_shot && !take_area_shot); - - /* always disable window border for full-desktop or selected-area screenshots */ - if (!take_window_shot) - screenshot = screenshot_get_pixbuf (window, rectangle, include_pointer, FALSE, include_mask); - else - { - screenshot = screenshot_get_pixbuf (window, rectangle, include_pointer, include_border, include_mask); - - switch (border_effect[0]) - { - case 's': /* shadow */ - screenshot_add_shadow (&screenshot); - break; - case 'b': /* border */ - screenshot_add_border (&screenshot); - break; - case 'n': /* none */ - default: - break; - } - } - - /* release now the lock, it was acquired when we were finding the window */ - screenshot_release_lock (); - - if (screenshot == NULL) - { - screenshot_show_error_dialog (NULL, - _("Unable to take a screenshot of the current window"), - NULL); - exit (1); - } - - play_sound_effect (window); - - dialog = screenshot_dialog_new (screenshot, initial_uri, take_window_shot); - g_free (initial_uri); - - screenshot_save_start (screenshot, save_done_notification, dialog); - - run_dialog (dialog); -} - -static void -async_existence_job_free (AsyncExistenceJob *job) -{ - if (!job) - return; - - g_free (job->base_uris[1]); - g_free (job->base_uris[2]); - - if (job->rectangle != NULL) - g_slice_free (GdkRectangle, job->rectangle); - - g_slice_free (AsyncExistenceJob, job); -} - -static gboolean -check_file_done (gpointer user_data) -{ - AsyncExistenceJob *job = user_data; - - finish_prepare_screenshot (job->retval, job->window, job->rectangle); - - async_existence_job_free (job); - - return FALSE; -} - -static char * -build_uri (AsyncExistenceJob *job) -{ - char *retval, *file_name; - char *timestamp; - GDateTime *d; - - d = g_date_time_new_now_local (); - /* Translators: This is a strftime format string. - * It is used to display the time in 24-hours format (eg, like - * in France: 20:10). */ - timestamp = g_date_time_format (d, _("%Y-%m-%d %H:%M:%S")); - g_date_time_unref (d); - - if (job->iteration == 0) - { - /* translators: this is the name of the file that gets made up - * with the screenshot if the entire screen is taken */ - file_name = g_strdup_printf (_("Screenshot at %s.png"), timestamp); - } - else - { - /* translators: this is the name of the file that gets - * made up with the screenshot if the entire screen is - * taken */ - file_name = g_strdup_printf (_("Screenshot at %s - %d.png"), timestamp, job->iteration); - } - - retval = g_build_filename (job->base_uris[job->type], file_name, NULL); - g_free (file_name); - g_free (timestamp); - - return retval; -} - -static gboolean -try_check_file (GIOSchedulerJob *io_job, - GCancellable *cancellable, - gpointer data) -{ - AsyncExistenceJob *job = data; - GFile *file; - GFileInfo *info; - GError *error; - char *uri; - -retry: - error = NULL; - uri = build_uri (job); - file = g_file_new_for_uri (uri); - - info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, - G_FILE_QUERY_INFO_NONE, cancellable, &error); - if (info != NULL) - { - /* file already exists, iterate again */ - g_object_unref (info); - g_object_unref (file); - g_free (uri); - - (job->iteration)++; - - goto retry; - } - else - { - /* see the error to check whether the location is not accessible - * or the file does not exist. - */ - if (error->code == G_IO_ERROR_NOT_FOUND) - { - GFile *parent; - - /* if the parent directory doesn't exist as well, forget the saved - * directory and treat this as a generic error. - */ - - parent = g_file_get_parent (file); - - if (!g_file_query_exists (parent, NULL)) - { - (job->type)++; - job->iteration = 0; - - g_object_unref (file); - g_object_unref (parent); - goto retry; - } - else - { - job->retval = uri; - - g_object_unref (parent); - goto out; - } - } - else - { - /* another kind of error, assume this location is not - * accessible. - */ - g_free (uri); - if (job->type == TEST_TMP) - { - job->retval = NULL; - goto out; - } - else - { - (job->type)++; - job->iteration = 0; - - g_error_free (error); - g_object_unref (file); - goto retry; - } - } - } - -out: - g_error_free (error); - g_object_unref (file); - - g_io_scheduler_job_send_to_mainloop_async (io_job, - check_file_done, - job, - NULL); - return FALSE; -} - -static GdkWindow * -find_current_window (char **window_title) -{ - GdkWindow *window; - - if (!screenshot_grab_lock ()) - exit (0); - - if (take_window_shot) - { - window = screenshot_find_current_window (); - if (!window) - { - take_window_shot = FALSE; - window = gdk_get_default_root_window (); - } - else - { - gchar *tmp, *sanitized; - - tmp = screenshot_get_window_title (window); - sanitized = screenshot_sanitize_filename (tmp); - g_free (tmp); - *window_title = sanitized; - } - } - else - { - window = gdk_get_default_root_window (); - } - - return window; -} - -static void -push_check_file_job (GdkRectangle *rectangle) -{ - AsyncExistenceJob *job; - - job = g_slice_new0 (AsyncExistenceJob); - job->base_uris[0] = last_save_dir; - /* we'll have to free these two */ - job->base_uris[1] = get_desktop_dir (); - job->base_uris[2] = g_strconcat ("file://", g_get_tmp_dir (), NULL); - job->iteration = 0; - job->type = TEST_LAST_DIR; - job->window = find_current_window (&window_title); - - if (rectangle != NULL) - { - job->rectangle = g_slice_new0 (GdkRectangle); - job->rectangle->x = rectangle->x; - job->rectangle->y = rectangle->y; - job->rectangle->width = rectangle->width; - job->rectangle->height = rectangle->height; - } - - /* Check if the area selection was cancelled */ - if (job->rectangle && - (job->rectangle->width == 0 || job->rectangle->height == 0)) - { - async_existence_job_free (job); - gtk_main_quit (); - return; - } - - g_io_scheduler_push_job (try_check_file, - job, - NULL, - 0, NULL); - -} - -static void -rectangle_found_cb (GdkRectangle *rectangle) -{ - push_check_file_job (rectangle); -} - -static void -prepare_screenshot (void) -{ - if (take_area_shot) - screenshot_select_area_async (rectangle_found_cb); - else - push_check_file_job (NULL); -} - -static gboolean -prepare_screenshot_timeout (gpointer data) -{ - prepare_screenshot (); - save_options (); - - return FALSE; -} - - -static gchar * -get_desktop_dir (void) -{ - gboolean desktop_is_home_dir = FALSE; - gchar *desktop_dir; - gboolean schema_exists = FALSE; - - /* Check if caja schema is installed before trying to read settings */ - GSettingsSchema *schema = g_settings_schema_source_lookup (g_settings_schema_source_get_default (), - CAJA_PREFERENCES_SCHEMA, - FALSE); - - if (schema != NULL) { - GSettings *caja_prefs; - - caja_prefs = g_settings_new (CAJA_PREFERENCES_SCHEMA); - desktop_is_home_dir = g_settings_get_boolean (caja_prefs, "desktop-is-home-dir"); - - g_object_unref (caja_prefs); - g_settings_schema_unref (schema); - } - - if (desktop_is_home_dir) - desktop_dir = g_strconcat ("file://", g_get_home_dir (), NULL); - else - desktop_dir = g_strconcat ("file://", g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP), NULL); - - return desktop_dir; -} - -/* Taken from mate-vfs-utils.c */ -static char * -expand_initial_tilde (const char *path) -{ - char *slash_after_user_name, *user_name; - struct passwd *passwd_file_entry; - - if (path[1] == '/' || path[1] == '\0') { - return g_strconcat (g_get_home_dir (), &path[1], NULL); - } - - slash_after_user_name = strchr (&path[1], '/'); - if (slash_after_user_name == NULL) { - user_name = g_strdup (&path[1]); - } else { - user_name = g_strndup (&path[1], - slash_after_user_name - &path[1]); - } - passwd_file_entry = getpwnam (user_name); - g_free (user_name); - - if (passwd_file_entry == NULL || passwd_file_entry->pw_dir == NULL) { - return g_strdup (path); - } - - return g_strconcat (passwd_file_entry->pw_dir, - slash_after_user_name, - NULL); -} - -/* Load options */ -static void -load_options (void) -{ - /* Find various dirs */ - last_save_dir = g_settings_get_string (settings, - LAST_SAVE_DIRECTORY_KEY); - if (!last_save_dir || !last_save_dir[0]) - { - last_save_dir = get_desktop_dir (); - } - else if (last_save_dir[0] == '~') - { - char *tmp = expand_initial_tilde (last_save_dir); - g_free (last_save_dir); - last_save_dir = tmp; - } - - include_border = g_settings_get_boolean (settings, - INCLUDE_BORDER_KEY); - - include_pointer = g_settings_get_boolean (settings, - INCLUDE_POINTER_KEY); - - border_effect = g_settings_get_string (settings, - BORDER_EFFECT_KEY); - if (!border_effect) - border_effect = g_strdup ("none"); - - delay = g_settings_get_int (settings, DELAY_KEY); -} - -static void -save_options (void) -{ - g_settings_set_boolean (settings, - INCLUDE_BORDER_KEY, include_border); - g_settings_set_boolean (settings, - INCLUDE_POINTER_KEY, include_pointer); - g_settings_set_int (settings, DELAY_KEY, delay); - g_settings_set_string (settings, - BORDER_EFFECT_KEY, border_effect); -} - - -static void -register_screenshooter_icon (GtkIconFactory * factory) -{ - GtkIconSource *source; - GtkIconSet *icon_set; - - source = gtk_icon_source_new (); - gtk_icon_source_set_icon_name (source, SCREENSHOOTER_ICON); - - icon_set = gtk_icon_set_new (); - gtk_icon_set_add_source (icon_set, source); - - gtk_icon_factory_add (factory, SCREENSHOOTER_ICON, icon_set); - gtk_icon_set_unref (icon_set); - gtk_icon_source_free (source); -} - -static void -screenshooter_init_stock_icons (void) -{ - GtkIconFactory *factory; - - factory = gtk_icon_factory_new (); - gtk_icon_factory_add_default (factory); - - register_screenshooter_icon (factory); - g_object_unref (factory); -} - -/* main */ -int -main (int argc, char *argv[]) -{ - GOptionContext *context; - gboolean window_arg = FALSE; - gboolean area_arg = FALSE; - gboolean include_border_arg = FALSE; - gboolean disable_border_arg = FALSE; - gboolean interactive_arg = FALSE; - gchar *border_effect_arg = NULL; - guint delay_arg = 0; - GError *error = NULL; - - const GOptionEntry entries[] = { - { "window", 'w', 0, G_OPTION_ARG_NONE, &window_arg, N_("Grab a window instead of the entire screen"), NULL }, - { "area", 'a', 0, G_OPTION_ARG_NONE, &area_arg, N_("Grab an area of the screen instead of the entire screen"), NULL }, - { "include-border", 'b', 0, G_OPTION_ARG_NONE, &include_border_arg, N_("Include the window border with the screenshot"), NULL }, - { "remove-border", 'B', 0, G_OPTION_ARG_NONE, &disable_border_arg, N_("Remove the window border from the screenshot"), NULL }, - { "delay", 'd', 0, G_OPTION_ARG_INT, &delay_arg, N_("Take screenshot after specified delay [in seconds]"), N_("seconds") }, - { "border-effect", 'e', 0, G_OPTION_ARG_STRING, &border_effect_arg, N_("Effect to add to the border (shadow, border or none)"), N_("effect") }, - { "interactive", 'i', 0, G_OPTION_ARG_NONE, &interactive_arg, N_("Interactively set options"), NULL }, - { NULL }, - }; - - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - context = g_option_context_new (_("Take a picture of the screen")); - g_option_context_set_ignore_unknown_options (context, FALSE); - g_option_context_set_help_enabled (context, TRUE); - g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); - g_option_context_add_group (context, gtk_get_option_group (TRUE)); - - g_option_context_parse (context, &argc, &argv, &error); - - if (error) { - g_critical ("Unable to parse arguments: %s", error->message); - g_error_free (error); - g_option_context_free (context); - exit (1); - } - - g_option_context_free (context); - - if (window_arg && area_arg) { - g_printerr (_("Conflicting options: --window and --area should not be " - "used at the same time.\n")); - exit (1); - } - - gtk_window_set_default_icon_name (SCREENSHOOTER_ICON); - screenshooter_init_stock_icons (); - - settings = g_settings_new (MATE_SCREENSHOT_SCHEMA); - load_options (); - /* allow the command line to override options */ - if (window_arg) - take_window_shot = TRUE; - - if (area_arg) - take_area_shot = TRUE; - - if (include_border_arg) - include_border = TRUE; - - if (disable_border_arg) - include_border = FALSE; - - if (border_effect_arg) - { - g_free (border_effect); - border_effect = border_effect_arg; - } - - if (delay_arg > 0) - delay = delay_arg; - - /* interactive mode overrides everything */ - if (interactive_arg) - { - GtkWidget *dialog; - gint response; - - dialog = create_interactive_dialog (); - response = gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - - switch (response) - { - case GTK_RESPONSE_DELETE_EVENT: - case GTK_RESPONSE_CANCEL: - return EXIT_SUCCESS; - case GTK_RESPONSE_OK: - break; - default: - g_assert_not_reached (); - break; - } - } - - if (((delay > 0 && interactive_arg) || delay_arg > 0) && - !take_area_shot) - { - g_timeout_add (delay * 1000, - prepare_screenshot_timeout, - NULL); - } - else - { - if (interactive_arg) - { - /* HACK: give time to the dialog to actually disappear. - * We don't have any way to tell when the compositor has finished - * re-drawing. - */ - g_timeout_add (200, - prepare_screenshot_timeout, NULL); - } - else - g_idle_add (prepare_screenshot_timeout, NULL); - } - - gtk_main (); - - return EXIT_SUCCESS; -} diff --git a/mate-screenshot/mate-screenshot.convert b/mate-screenshot/mate-screenshot.convert deleted file mode 100644 index 741f7d44..00000000 --- a/mate-screenshot/mate-screenshot.convert +++ /dev/null @@ -1,6 +0,0 @@ -[org.mate.screenshot] -delay = /apps/mate-screenshot/delay -last-save-directory = /apps/mate-screenshot/last_save_directory -include-border = /apps/mate-screenshot/include_border -include-pointer = /apps/mate-screenshot/include_pointer -border-effect = /apps/mate-screenshot/border_effect diff --git a/mate-screenshot/mate-screenshot.desktop.in b/mate-screenshot/mate-screenshot.desktop.in deleted file mode 100644 index fedc1321..00000000 --- a/mate-screenshot/mate-screenshot.desktop.in +++ /dev/null @@ -1,15 +0,0 @@ -[Desktop Entry] -_Name=Take Screenshot -_Comment=Save images of your desktop or individual windows -Exec=mate-screenshot --interactive -Terminal=false -Type=Application -Icon=applets-screenshooter -StartupNotify=true -Categories=GTK;Utility; -Keywords=MATE;screenshot;snapshot;desktop;window;image; -OnlyShowIn=MATE; -X-MATE-Bugzilla-Bugzilla=MATE -X-MATE-Bugzilla-Product=mate-utils -X-MATE-Bugzilla-Component=screenshot -X-MATE-Bugzilla-OtherBinaries=mate-panel-screenshot diff --git a/mate-screenshot/mate-screenshot.ui b/mate-screenshot/mate-screenshot.ui deleted file mode 100644 index 80382ed4..00000000 --- a/mate-screenshot/mate-screenshot.ui +++ /dev/null @@ -1,251 +0,0 @@ - - - - - 5 - Save Screenshot - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - False - False - True - False - False - GDK_WINDOW_TYPE_HINT_DIALOG - GDK_GRAVITY_NORTH_WEST - - - - True - False - 2 - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-help - True - GTK_RELIEF_NORMAL - True - - - - - True - True - True - True - C_opy to Clipboard - GTK_RELIEF_NORMAL - True - - - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - - - - True - True - True - True - gtk-save - True - GTK_RELIEF_NORMAL - True - - - - - 0 - False - True - GTK_PACK_END - - - - - 5 - True - False - 18 - - - True - False - 12 - - - True - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - - - True - 0 - 0 - GTK_SHADOW_IN - 0 - 0 - 1 - True - - - True - - - - - - - 0 - False - True - - - - - True - 2 - 2 - False - 6 - 12 - - - True - _Name: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - filename_entry - - - 0 - 1 - 0 - 1 - fill - - - - - - True - Save in _folder: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - True - True - True - True - 0 - - True - * - True - 32 - - - 1 - 2 - 0 - 1 - - - - - - True - False - 0 - - - - - - 1 - 2 - 1 - 2 - fill - fill - - - - - 0 - True - True - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - help_button - cancel_button - ok_button - copy_button - - - diff --git a/mate-screenshot/org.mate.screenshot.gschema.xml.in b/mate-screenshot/org.mate.screenshot.gschema.xml.in deleted file mode 100644 index c926d737..00000000 --- a/mate-screenshot/org.mate.screenshot.gschema.xml.in +++ /dev/null @@ -1,29 +0,0 @@ - - - - 0 - Screenshot delay - The number of seconds to wait before taking the screenshot. - - - '' - Screenshot directory - The directory the last screenshot was saved in. - - - true - Include Border - Include the window manager border along with the screenshot - - - true - Include Pointer - Include the pointer in the screenshot - - - 'none' - Border Effect - Effect to add to the outside of a border. Possible values are "shadow", "none", and "border". - - - diff --git a/mate-screenshot/screenshot-dialog.c b/mate-screenshot/screenshot-dialog.c deleted file mode 100644 index 7c5fae6d..00000000 --- a/mate-screenshot/screenshot-dialog.c +++ /dev/null @@ -1,422 +0,0 @@ -/* screenshot-dialog.c - main MATE Screenshot dialog - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -#include -#include -#include - -#include "screenshot-dialog.h" -#include "screenshot-save.h" -#include -#include -#include - -enum { - TYPE_IMAGE_PNG, - TYPE_TEXT_URI_LIST, - - LAST_TYPE -}; - -static GtkTargetEntry drag_types[] = -{ - { "image/png", 0, TYPE_IMAGE_PNG }, - { "text/uri-list", 0, TYPE_TEXT_URI_LIST }, -}; - -struct ScreenshotDialog -{ - GtkBuilder *ui; - GdkPixbuf *screenshot; - GdkPixbuf *preview_image; - GtkWidget *save_widget; - GtkWidget *filename_entry; - gint drag_x; - gint drag_y; -}; - -static gboolean -on_toplevel_key_press_event (GtkWidget *widget, - GdkEventKey *key) -{ - if (key->keyval == GDK_KEY_F1) - { - gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_HELP); - return TRUE; - } - - return FALSE; -} - -static void -#if GTK_CHECK_VERSION (3, 0, 0) -on_preview_draw (GtkWidget *drawing_area, cairo_t *cr, gpointer data) -#else -on_preview_expose_event (GtkWidget *drawing_area, GdkEventExpose *event, gpointer data) -#endif -{ - ScreenshotDialog *dialog = data; - GdkPixbuf *pixbuf = NULL; - gboolean free_pixbuf = FALSE; -#if !GTK_CHECK_VERSION (3, 0, 0) - cairo_t *cr; -#endif - - /* Stolen from GtkImage. I really should just make the drawing area an - * image some day */ - if (gtk_widget_get_state (drawing_area) != GTK_STATE_NORMAL) - { - GtkIconSource *source; - - source = gtk_icon_source_new (); - gtk_icon_source_set_pixbuf (source, dialog->preview_image); - gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_icon_source_set_size_wildcarded (source, FALSE); - - pixbuf = gtk_style_render_icon (gtk_widget_get_style (drawing_area), - source, - gtk_widget_get_direction (drawing_area), - gtk_widget_get_state (drawing_area), - (GtkIconSize) -1, - drawing_area, - "gtk-image"); - free_pixbuf = TRUE; - gtk_icon_source_free (source); - } - else - { - pixbuf = g_object_ref (dialog->preview_image); - } - -#if !GTK_CHECK_VERSION (3, 0, 0) - cr = gdk_cairo_create (gtk_widget_get_window (drawing_area)); - gdk_cairo_region (cr, event->region); - cairo_clip (cr); -#endif - - gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); - cairo_paint (cr); - -#if !GTK_CHECK_VERSION (3, 0, 0) - cairo_destroy (cr); -#endif - - g_object_unref (pixbuf); -} - -static gboolean -on_preview_button_press_event (GtkWidget *drawing_area, - GdkEventButton *event, - gpointer data) -{ - ScreenshotDialog *dialog = data; - - dialog->drag_x = (int) event->x; - dialog->drag_y = (int) event->y; - - return FALSE; -} - -static gboolean -on_preview_button_release_event (GtkWidget *drawing_area, - GdkEventButton *event, - gpointer data) -{ - ScreenshotDialog *dialog = data; - - dialog->drag_x = 0; - dialog->drag_y = 0; - - return FALSE; -} - -static void -on_preview_configure_event (GtkWidget *drawing_area, - GdkEventConfigure *event, - gpointer data) -{ - ScreenshotDialog *dialog = data; - - if (dialog->preview_image) - g_object_unref (G_OBJECT (dialog->preview_image)); - - dialog->preview_image = gdk_pixbuf_scale_simple (dialog->screenshot, - event->width, - event->height, - GDK_INTERP_BILINEAR); -} - -static void -drag_data_get (GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time, - ScreenshotDialog *dialog) -{ - if (info == TYPE_TEXT_URI_LIST) - { - gchar **uris; - - uris = g_new (gchar *, 2); - uris[0] = g_strconcat ("file://", - screenshot_save_get_filename (), - NULL); - uris[1] = NULL; - - gtk_selection_data_set_uris (selection_data, uris); - g_strfreev (uris); - } - else if (info == TYPE_IMAGE_PNG) - { - gtk_selection_data_set_pixbuf (selection_data, dialog->screenshot); - } - else - { - g_warning ("Unknown type %d", info); - } -} - -static void -drag_begin (GtkWidget *widget, - GdkDragContext *context, - ScreenshotDialog *dialog) -{ - gtk_drag_set_icon_pixbuf (context, dialog->preview_image, - dialog->drag_x, dialog->drag_y); -} - - -ScreenshotDialog * -screenshot_dialog_new (GdkPixbuf *screenshot, - char *initial_uri, - gboolean take_window_shot) -{ - ScreenshotDialog *dialog; - GtkWidget *toplevel; - GtkWidget *preview_darea; - GtkWidget *aspect_frame; - GtkWidget *file_chooser_box; - gint width, height; - char *current_folder; - char *current_name; - char *ext; - gint pos; - GFile *tmp_file; - GFile *parent_file; - GError *error = NULL; - guint res; - - tmp_file = g_file_new_for_uri (initial_uri); - parent_file = g_file_get_parent (tmp_file); - - current_name = g_file_get_basename (tmp_file); - current_folder = g_file_get_uri (parent_file); - g_object_unref (tmp_file); - g_object_unref (parent_file); - - dialog = g_new0 (ScreenshotDialog, 1); - - dialog->ui = gtk_builder_new (); - res = gtk_builder_add_from_file (dialog->ui, UIDIR "/mate-screenshot.ui", &error); - dialog->screenshot = screenshot; - - if (res == 0) - { - GtkWidget *dialog; - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - _("Error loading UI definition file for the screenshot program: \n%s\n\n" - "Please check your installation of mate-utils."), error->message); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - - g_error_free (error); - exit (1); - } - - gtk_builder_set_translation_domain (dialog->ui, GETTEXT_PACKAGE); - - width = gdk_pixbuf_get_width (screenshot); - height = gdk_pixbuf_get_height (screenshot); - - width /= 5; - height /= 5; - - toplevel = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "toplevel")); - aspect_frame = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "aspect_frame")); - preview_darea = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "preview_darea")); - dialog->filename_entry = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "filename_entry")); - file_chooser_box = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "file_chooser_box")); - - dialog->save_widget = gtk_file_chooser_button_new (_("Select a folder"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); - gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog->save_widget), FALSE); - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog->save_widget), current_folder); - gtk_entry_set_text (GTK_ENTRY (dialog->filename_entry), current_name); - - gtk_box_pack_start (GTK_BOX (file_chooser_box), dialog->save_widget, TRUE, TRUE, 0); - g_free (current_folder); - - gtk_widget_set_size_request (preview_darea, width, height); - gtk_aspect_frame_set (GTK_ASPECT_FRAME (aspect_frame), 0.0, 0.5, - gdk_pixbuf_get_width (screenshot)/ - (gfloat) gdk_pixbuf_get_height (screenshot), - FALSE); - g_signal_connect (toplevel, "key_press_event", G_CALLBACK (on_toplevel_key_press_event), dialog); -#if GTK_CHECK_VERSION (3, 0, 0) - g_signal_connect (preview_darea, "draw", G_CALLBACK (on_preview_draw), dialog); -#else - g_signal_connect (preview_darea, "expose_event", G_CALLBACK (on_preview_expose_event), dialog); -#endif - g_signal_connect (preview_darea, "button_press_event", G_CALLBACK (on_preview_button_press_event), dialog); - g_signal_connect (preview_darea, "button_release_event", G_CALLBACK (on_preview_button_release_event), dialog); - g_signal_connect (preview_darea, "configure_event", G_CALLBACK (on_preview_configure_event), dialog); - - if (take_window_shot) - gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_NONE); - else - gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN); - - /* setup dnd */ - g_signal_connect (G_OBJECT (preview_darea), "drag_begin", - G_CALLBACK (drag_begin), dialog); - g_signal_connect (G_OBJECT (preview_darea), "drag_data_get", - G_CALLBACK (drag_data_get), dialog); - - gtk_widget_show_all (toplevel); - - /* select the name of the file but leave out the extension if there's any; - * the dialog must be realized for select_region to work - */ - ext = g_utf8_strrchr (current_name, -1, '.'); - if (ext) - pos = g_utf8_strlen (current_name, -1) - g_utf8_strlen (ext, -1); - else - pos = -1; - - gtk_editable_select_region (GTK_EDITABLE (dialog->filename_entry), - 0, - pos); - - g_free (current_name); - - return dialog; -} - -void -screenshot_dialog_focus_entry (ScreenshotDialog *dialog) -{ - gtk_widget_grab_focus (dialog->filename_entry); -} - -void -screenshot_dialog_enable_dnd (ScreenshotDialog *dialog) -{ - GtkWidget *preview_darea; - - g_return_if_fail (dialog != NULL); - - preview_darea = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "preview_darea")); - gtk_drag_source_set (preview_darea, - GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, - drag_types, G_N_ELEMENTS (drag_types), - GDK_ACTION_COPY); -} - -GtkWidget * -screenshot_dialog_get_toplevel (ScreenshotDialog *dialog) -{ - return GTK_WIDGET (gtk_builder_get_object (dialog->ui, "toplevel")); -} - -char * -screenshot_dialog_get_uri (ScreenshotDialog *dialog) -{ - gchar *folder; - const gchar *file_name; - gchar *uri, *file, *tmp; - GError *error; - - folder = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog->save_widget)); - file_name = gtk_entry_get_text (GTK_ENTRY (dialog->filename_entry)); - - error = NULL; - tmp = g_filename_from_utf8 (file_name, -1, NULL, NULL, &error); - if (error) - { - g_warning ("Unable to convert `%s' to valid UTF-8: %s\n" - "Falling back to default file.", - file_name, - error->message); - g_error_free (error); - tmp = g_strdup (_("Screenshot.png")); - } - - file = g_uri_escape_string (tmp, NULL, FALSE); - uri = g_build_filename (folder, file, NULL); - - g_free (folder); - g_free (tmp); - g_free (file); - - return uri; -} - -char * -screenshot_dialog_get_folder (ScreenshotDialog *dialog) -{ - return gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog->save_widget)); -} - -GdkPixbuf * -screenshot_dialog_get_screenshot (ScreenshotDialog *dialog) -{ - return dialog->screenshot; -} - -void -screenshot_dialog_set_busy (ScreenshotDialog *dialog, - gboolean busy) -{ - GtkWidget *toplevel; - - toplevel = screenshot_dialog_get_toplevel (dialog); - - if (busy) - { - GdkCursor *cursor; - /* Change cursor to busy */ - cursor = gdk_cursor_new (GDK_WATCH); - gdk_window_set_cursor (gtk_widget_get_window (toplevel), cursor); -#if GTK_CHECK_VERSION (3, 0, 0) - g_object_unref (cursor); -#else - gdk_cursor_unref (cursor); -#endif - } - else - { - gdk_window_set_cursor (gtk_widget_get_window (toplevel), NULL); - } - - gtk_widget_set_sensitive (toplevel, ! busy); - - gdk_flush (); -} diff --git a/mate-screenshot/screenshot-dialog.h b/mate-screenshot/screenshot-dialog.h deleted file mode 100644 index 0cd5e1da..00000000 --- a/mate-screenshot/screenshot-dialog.h +++ /dev/null @@ -1,42 +0,0 @@ -/* screenshot-dialog.h - main MATE Screenshot dialog - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -#ifndef __SCREENSHOT_DIALOG_H__ -#define __SCREENSHOT_DIALOG_H__ - -#include - -typedef struct ScreenshotDialog ScreenshotDialog; - -/* Keep in sync with the value defined in the UI file */ -#define SCREENSHOT_RESPONSE_COPY 1 - -ScreenshotDialog *screenshot_dialog_new (GdkPixbuf *screenshot, - char *initial_uri, - gboolean take_window_shot); -void screenshot_dialog_enable_dnd (ScreenshotDialog *dialog); -GtkWidget *screenshot_dialog_get_toplevel (ScreenshotDialog *dialog); -char *screenshot_dialog_get_uri (ScreenshotDialog *dialog); -char *screenshot_dialog_get_folder (ScreenshotDialog *dialog); -GdkPixbuf *screenshot_dialog_get_screenshot (ScreenshotDialog *dialog); -void screenshot_dialog_set_busy (ScreenshotDialog *dialog, - gboolean busy); -void screenshot_dialog_focus_entry (ScreenshotDialog *dialog); - -#endif /* __SCREENSHOT_DIALOG_H__ */ diff --git a/mate-screenshot/screenshot-save.c b/mate-screenshot/screenshot-save.c deleted file mode 100644 index 5b870eec..00000000 --- a/mate-screenshot/screenshot-save.c +++ /dev/null @@ -1,285 +0,0 @@ -/* screenshot-save.c - image saving functions for MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "screenshot-save.h" - -static char *parent_dir = NULL; -static char *tmp_filename = NULL; - -static SaveFunction save_callback = NULL; -static gpointer save_user_data = NULL; - -/* Strategy for saving: - * - * We keep another process around to handle saving the image. This is - * done for two reasons. One, the saving takes a non-zero amount of - * time (about a quarter of a second on my box.) This will make it - * more interactive. The second reason is to make the child - * responsible for cleaning up the temp dir. If the parent process is - * killed or segfaults, the child process can clean up the temp dir. - */ -static void -clean_up_temporary_dir (gboolean gui_on_error) -{ - char *message; - gboolean error_occurred = FALSE; - if (g_file_test (tmp_filename, G_FILE_TEST_EXISTS)) - error_occurred = unlink (tmp_filename); - if (g_file_test (parent_dir, G_FILE_TEST_EXISTS)) - error_occurred = rmdir (parent_dir) || error_occurred; - - if (error_occurred) - { - message = g_strdup_printf (_("Unable to clear the temporary folder:\n%s"), - tmp_filename); - if (gui_on_error) - { - GtkWidget *dialog; - - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", message); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - } - else - { - g_warning ("%s", message); - } - g_free (message); - } - g_free (tmp_filename); - g_free (parent_dir); -} - -static void -child_done_notification (GPid pid, - gint status, - gpointer data) -{ - /* This should never be called. */ - - /* We expect the child to die after the parent. If the child dies - * than it either segfaulted, or was randomly killed. In either - * case, we can't reasonably continue. */ - GtkWidget *dialog; - - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - _("The child save process unexpectedly exited. We are unable to write the screenshot to disk.")); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - - clean_up_temporary_dir (TRUE); - - exit (1); -} - -static gboolean -read_pipe_from_child (GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - if (condition & G_IO_IN) - { - gchar *message = NULL; - gchar *error_message = NULL; - GtkWidget *dialog; - GIOStatus status; - - status = g_io_channel_read_line (source, &error_message, NULL, NULL, NULL); - - if (status == G_IO_STATUS_NORMAL) - { - message = g_strdup_printf ("Unable to save the screenshot to disk:\n\n%s", error_message); - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", message); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - exit (1); - } - } - - (*save_callback) (save_user_data); - - return FALSE; -} - -static char * -make_temp_directory (void) -{ - gint result, i; - gchar *dir_name; - - i = 0; - do - { - gchar *tmp_dir = g_strdup_printf ("mate-screenshot.%u.%d", - (unsigned int) getpid (), - i++); - - dir_name = g_build_filename (g_get_tmp_dir (), - tmp_dir, - NULL); - g_free (tmp_dir); - - result = g_mkdir_with_parents (dir_name, 0777); - if (result < 0) - { - g_free (dir_name); - - if (errno != EEXIST) - return NULL; - else - continue; - } - else - return dir_name; - } - while (TRUE); -} - -static void -signal_handler (int sig) -{ - clean_up_temporary_dir (FALSE); - - signal (sig, SIG_DFL); - kill (getpid (), sig); -} - -void -screenshot_save_start (GdkPixbuf *pixbuf, - SaveFunction callback, - gpointer user_data) -{ - GPid pid; - int parent_exit_notification[2]; - int pipe_from_child[2]; - - pipe (parent_exit_notification); - pipe (pipe_from_child); - - parent_dir = make_temp_directory (); - if (parent_dir == NULL) - return; - - tmp_filename = g_build_filename (parent_dir, - _("Screenshot.png"), - NULL); - save_callback = callback; - save_user_data = user_data; - - pid = fork (); - if (pid == 0) - { - GError *error = NULL; - char c; - - signal (SIGINT, signal_handler); - signal (SIGTERM, signal_handler); - - close (parent_exit_notification [1]); - close (pipe_from_child [0]); - - if (! gdk_pixbuf_save (pixbuf, tmp_filename, - "png", &error, - "tEXt::Software", "mate-screenshot", - NULL)) - { - if (error && error->message) - write (pipe_from_child[1], - error->message, - strlen (error->message)); - else -#define ERROR_MESSAGE _("Unknown error saving screenshot to disk") - write (pipe_from_child[1], - ERROR_MESSAGE, - strlen (ERROR_MESSAGE)); - } - /* By closing the pipe, we let the main process know that we're - * done saving it. */ - close (pipe_from_child[1]); - read (parent_exit_notification[0], &c, 1); - - clean_up_temporary_dir (FALSE); - _exit (0); - } - else if (pid > 0) - { - GIOChannel *channel; - - close (parent_exit_notification[0]); - close (pipe_from_child[1]); - - channel = g_io_channel_unix_new (pipe_from_child[0]); - g_io_add_watch (channel, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - read_pipe_from_child, - NULL); - g_io_channel_unref (channel); - g_child_watch_add (pid, child_done_notification, NULL); - } - else - /* George awesomely wrote code originally to handle the - * could-not-fork case synchronously. I'm not copying it, as I'm - * guessing that the system is pretty hosed if that's the case. - * However, he gets major kudos for trying. (-: - */ - g_assert_not_reached (); -} - -const char * -screenshot_save_get_filename (void) -{ - return tmp_filename; -} - -gchar * -screenshot_sanitize_filename (const char *filename) -{ - char *retval, *p; - - g_assert (filename); - g_assert (g_utf8_validate (filename, -1, NULL)); - - retval = g_uri_escape_string (filename, - "/", - TRUE); - - for (p = retval; *p != '\000'; p = g_utf8_next_char (p)) - { - if (*p == G_DIR_SEPARATOR) - *p = '-'; - } - - return retval; -} diff --git a/mate-screenshot/screenshot-save.h b/mate-screenshot/screenshot-save.h deleted file mode 100644 index 9df442d2..00000000 --- a/mate-screenshot/screenshot-save.h +++ /dev/null @@ -1,34 +0,0 @@ -/* screenshot-save.h - image saving functions for MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -#ifndef __SCREENSHOT_SAVE_H__ -#define __SCREENSHOT_SAVE_H__ - -#include - -typedef void (*SaveFunction) (gpointer data); - -void screenshot_save_start (GdkPixbuf *pixbuf, - SaveFunction callback, - gpointer user_data); -const char *screenshot_save_get_filename (void); -gchar *screenshot_sanitize_filename (const char *filename); - - -#endif /* __SCREENSHOT_SAVE_H__ */ diff --git a/mate-screenshot/screenshot-shadow.c b/mate-screenshot/screenshot-shadow.c deleted file mode 100644 index 052bb9f3..00000000 --- a/mate-screenshot/screenshot-shadow.c +++ /dev/null @@ -1,235 +0,0 @@ -/* screenshot-shadow.c - part of MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -/* Shadow code from anders */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "screenshot-shadow.h" -#include - -#define BLUR_RADIUS 5 -#define SHADOW_OFFSET (BLUR_RADIUS * 4 / 5) -#define SHADOW_OPACITY 0.5 - -#define OUTLINE_RADIUS 1 -#define OUTLINE_OFFSET 0 -#define OUTLINE_OPACITY 1.0 - -#define dist(x0, y0, x1, y1) sqrt(((x0) - (x1))*((x0) - (x1)) + ((y0) - (y1))*((y0) - (y1))) - -typedef struct { - int size; - double *data; -} ConvFilter; - -static double -gaussian (double x, double y, double r) -{ - return ((1 / (2 * M_PI * r)) * - exp ((- (x * x + y * y)) / (2 * r * r))); -} - -static ConvFilter * -create_blur_filter (int radius) -{ - ConvFilter *filter; - int x, y; - double sum; - - filter = g_new0 (ConvFilter, 1); - filter->size = radius * 2 + 1; - filter->data = g_new (double, filter->size * filter->size); - - sum = 0.0; - - for (y = 0 ; y < filter->size; y++) - { - for (x = 0 ; x < filter->size; x++) - { - sum += filter->data[y * filter->size + x] = gaussian (x - (filter->size >> 1), - y - (filter->size >> 1), - radius); - } - } - - for (y = 0; y < filter->size; y++) - { - for (x = 0; x < filter->size; x++) - { - filter->data[y * filter->size + x] /= sum; - } - } - - return filter; - -} - -static ConvFilter * -create_outline_filter (int radius) -{ - ConvFilter *filter; - double *iter; - - filter = g_new0 (ConvFilter, 1); - filter->size = radius * 2 + 1; - filter->data = g_new (double, filter->size * filter->size); - - for (iter = filter->data; - iter < filter->data + (filter->size * filter->size); - iter++) - { - *iter = 1.0; - } - - return filter; -} - -static GdkPixbuf * -create_effect (GdkPixbuf *src, - ConvFilter const *filter, - int radius, - int offset, - double opacity) -{ - GdkPixbuf *dest; - int x, y, i, j; - int src_x, src_y; - int suma; - int dest_width, dest_height; - int src_width, src_height; - int src_rowstride, dest_rowstride; - gboolean src_has_alpha; - - guchar *src_pixels, *dest_pixels; - - src_has_alpha = gdk_pixbuf_get_has_alpha (src); - - src_width = gdk_pixbuf_get_width (src); - src_height = gdk_pixbuf_get_height (src); - dest_width = src_width + 2 * radius + offset; - dest_height = src_height + 2 * radius + offset; - - dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), - TRUE, - gdk_pixbuf_get_bits_per_sample (src), - dest_width, dest_height); - - gdk_pixbuf_fill (dest, 0); - - src_pixels = gdk_pixbuf_get_pixels (src); - src_rowstride = gdk_pixbuf_get_rowstride (src); - - dest_pixels = gdk_pixbuf_get_pixels (dest); - dest_rowstride = gdk_pixbuf_get_rowstride (dest); - - for (y = 0; y < dest_height; y++) - { - for (x = 0; x < dest_width; x++) - { - suma = 0; - - src_x = x - radius; - src_y = y - radius; - - /* We don't need to compute effect here, since this pixel will be - * discarded when compositing */ - if (src_x >= 0 && src_x < src_width && - src_y >= 0 && src_y < src_height && - (!src_has_alpha || - src_pixels [src_y * src_rowstride + src_x * 4 + 3] == 0xFF)) - continue; - - for (i = 0; i < filter->size; i++) - { - for (j = 0; j < filter->size; j++) - { - src_y = -(radius + offset) + y - (filter->size >> 1) + i; - src_x = -(radius + offset) + x - (filter->size >> 1) + j; - - if (src_y < 0 || src_y >= src_height || - src_x < 0 || src_x >= src_width) - continue; - - suma += ( src_has_alpha ? - src_pixels [src_y * src_rowstride + src_x * 4 + 3] : - 0xFF ) * filter->data [i * filter->size + j]; - } - } - - dest_pixels [y * dest_rowstride + x * 4 + 3] = CLAMP (suma * opacity, 0x00, 0xFF); - } - } - - return dest; -} - -void -screenshot_add_shadow (GdkPixbuf **src) -{ - GdkPixbuf *dest; - static ConvFilter *filter = NULL; - - if (!filter) - filter = create_blur_filter (BLUR_RADIUS); - - dest = create_effect (*src, filter, - BLUR_RADIUS, - SHADOW_OFFSET, SHADOW_OPACITY); - - if (dest == NULL) - return; - - gdk_pixbuf_composite (*src, dest, - BLUR_RADIUS, BLUR_RADIUS, - gdk_pixbuf_get_width (*src), - gdk_pixbuf_get_height (*src), - BLUR_RADIUS, BLUR_RADIUS, 1.0, 1.0, - GDK_INTERP_BILINEAR, 255); - g_object_unref (*src); - *src = dest; -} - -void -screenshot_add_border (GdkPixbuf **src) -{ - GdkPixbuf *dest; - static ConvFilter *filter = NULL; - - if (!filter) - filter = create_outline_filter (OUTLINE_RADIUS); - - dest = create_effect (*src, filter, - OUTLINE_RADIUS, - OUTLINE_OFFSET, OUTLINE_OPACITY); - - if (dest == NULL) - return; - - gdk_pixbuf_composite (*src, dest, - OUTLINE_RADIUS, OUTLINE_RADIUS, - gdk_pixbuf_get_width (*src), - gdk_pixbuf_get_height (*src), - OUTLINE_RADIUS, OUTLINE_RADIUS, 1.0, 1.0, - GDK_INTERP_BILINEAR, 255); - g_object_unref (*src); - *src = dest; -} diff --git a/mate-screenshot/screenshot-shadow.h b/mate-screenshot/screenshot-shadow.h deleted file mode 100644 index c4162a5f..00000000 --- a/mate-screenshot/screenshot-shadow.h +++ /dev/null @@ -1,28 +0,0 @@ -/* screenshot-shadow.h - part of MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -#ifndef __SCREENSHOT_SHADOW_H__ -#define __SCREENSHOT_SHADOW_H__ - -#include - -void screenshot_add_shadow (GdkPixbuf **src); -void screenshot_add_border (GdkPixbuf **src); - -#endif /* __SCREENSHOT_SHADOW_H__ */ diff --git a/mate-screenshot/screenshot-utils.c b/mate-screenshot/screenshot-utils.c deleted file mode 100644 index eaeeca28..00000000 --- a/mate-screenshot/screenshot-utils.c +++ /dev/null @@ -1,1040 +0,0 @@ -/* screenshot-utils.c - common functions for MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * Copyright (C) 2008 Cosimo Cecchi - * - * 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, - */ - -#include "config.h" -#include "screenshot-utils.h" - -#include -#include -#include -#include - -#ifdef HAVE_X11_EXTENSIONS_SHAPE_H -#include -#endif - -#if GTK_CHECK_VERSION (3, 0, 0) -#define GdkRegion cairo_region_t -#define gdk_region_new cairo_region_create -#define gdk_region_destroy cairo_region_destroy -#define gdk_region_rectangle cairo_region_create_rectangle -#define gdk_region_offset cairo_region_translate -#define gdk_region_intersect cairo_region_intersect -#define gdk_region_subtract cairo_region_subtract -#define gdk_region_union_with_rect cairo_region_union_rectangle -#define gdk_cursor_unref g_object_unref -#endif - -static GtkWidget *selection_window; - -#define SELECTION_NAME "_MATE_PANEL_SCREENSHOT" - -static char * -get_utf8_property (GdkWindow *window, - GdkAtom atom) -{ - gboolean res; - GdkAtom utf8_string; - GdkAtom type; - int actual_format, actual_length; - guchar *data; - char *retval; - - utf8_string = gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("UTF8_STRING")); - res = gdk_property_get (window, atom, utf8_string, - 0, G_MAXLONG, FALSE, - &type, - &actual_format, &actual_length, - &data); - if (!res) - return NULL; - - if (type != utf8_string || actual_format != 8 || actual_length == 0) - { - g_free (data); - return NULL; - } - - if (!g_utf8_validate ((gchar *) data, actual_length, NULL)) - { - char *atom_name = gdk_atom_name (atom); - - g_warning ("Property `%s' (format: %d, length: %d) contained " - "invalid UTF-8", - atom_name, - actual_format, - actual_length); - - g_free (atom_name); - g_free (data); - - return NULL; - } - - retval = g_strndup ((gchar *) data, actual_length); - - g_free (data); - - return retval; -} - -/* To make sure there is only one screenshot taken at a time, - * (Imagine key repeat for the print screen key) we hold a selection - * until we are done taking the screenshot - */ -gboolean -screenshot_grab_lock (void) -{ - GdkAtom selection_atom; - gboolean result = FALSE; - - selection_atom = gdk_atom_intern (SELECTION_NAME, FALSE); - gdk_x11_grab_server (); - - if (gdk_selection_owner_get (selection_atom) != NULL) - goto out; - - selection_window = gtk_invisible_new (); - gtk_widget_show (selection_window); - - if (!gtk_selection_owner_set (selection_window, - gdk_atom_intern (SELECTION_NAME, FALSE), - GDK_CURRENT_TIME)) - { - gtk_widget_destroy (selection_window); - selection_window = NULL; - goto out; - } - - result = TRUE; - - out: - gdk_x11_ungrab_server (); - gdk_flush (); - - return result; -} - -void -screenshot_release_lock (void) -{ - if (selection_window) - { - gtk_widget_destroy (selection_window); - selection_window = NULL; - } - - gdk_flush (); -} - -static GdkWindow * -screenshot_find_active_window (void) -{ - GdkWindow *window; - GdkScreen *default_screen; - - default_screen = gdk_screen_get_default (); - window = gdk_screen_get_active_window (default_screen); - - return window; -} - -static gboolean -screenshot_window_is_desktop (GdkWindow *window) -{ - GdkWindow *root_window = gdk_get_default_root_window (); - GdkWindowTypeHint window_type_hint; - - if (window == root_window) - return TRUE; - - window_type_hint = gdk_window_get_type_hint (window); - if (window_type_hint == GDK_WINDOW_TYPE_HINT_DESKTOP) - return TRUE; - - return FALSE; - -} - -#define MAXIMUM_WM_REPARENTING_DEPTH 4 - -static GdkWindow * -look_for_hint_helper (GdkWindow *window, - GdkAtom property, - int depth) -{ - gboolean res; - GdkAtom actual_type; - int actual_format, actual_length; - guchar *data; - - res = gdk_property_get (window, property, GDK_NONE, - 0, 1, FALSE, - &actual_type, - &actual_format, &actual_length, - &data); - - if (res == TRUE && - data != NULL && - actual_format == 32 && - data[0] == 1) - { - g_free (data); - - return window; - } - - if (depth < MAXIMUM_WM_REPARENTING_DEPTH) - { - GList *children, *l; - - children = gdk_window_get_children (window); - if (children != NULL) - { - for (l = children; l; l = l->next) - { - window = look_for_hint_helper (l->data, property, depth + 1); - if (window) - break; - } - - g_list_free (children); - - if (window) - return window; - } - } - - return NULL; -} - -static GdkWindow * -look_for_hint (GdkWindow *window, - GdkAtom property) -{ - GdkWindow *retval; - - retval = look_for_hint_helper (window, property, 0); - - return retval; -} - -GdkWindow * -screenshot_find_current_window () -{ - GdkWindow *current_window; - - current_window = screenshot_find_active_window (); - - /* If there's no active window, we fall back to returning the - * window that the cursor is in. - */ - if (!current_window) - current_window = gdk_window_at_pointer (NULL, NULL); - - if (current_window) - { - if (screenshot_window_is_desktop (current_window)) - /* if the current window is the desktop (e.g. caja), we - * return NULL, as getting the whole screen makes more sense. - */ - return NULL; - - /* Once we have a window, we take the toplevel ancestor. */ - current_window = gdk_window_get_toplevel (current_window); - } - - return current_window; -} - -typedef struct { - GdkRectangle rect; - gboolean button_pressed; - GtkWidget *window; -} select_area_filter_data; - -static gboolean -select_area_button_press (GtkWidget *window, - GdkEventButton *event, - select_area_filter_data *data) -{ - if (data->button_pressed) - return TRUE; - - data->button_pressed = TRUE; - data->rect.x = event->x_root; - data->rect.y = event->y_root; - - return TRUE; -} - -static gboolean -select_area_motion_notify (GtkWidget *window, - GdkEventMotion *event, - select_area_filter_data *data) -{ - GdkRectangle draw_rect; - - if (!data->button_pressed) - return TRUE; - - draw_rect.width = ABS (data->rect.x - event->x_root); - draw_rect.height = ABS (data->rect.y - event->y_root); - draw_rect.x = MIN (data->rect.x, event->x_root); - draw_rect.y = MIN (data->rect.y, event->y_root); - - if (draw_rect.width <= 0 || draw_rect.height <= 0) - { - gtk_window_move (GTK_WINDOW (window), -100, -100); - gtk_window_resize (GTK_WINDOW (window), 10, 10); - return TRUE; - } - - gtk_window_move (GTK_WINDOW (window), draw_rect.x, draw_rect.y); - gtk_window_resize (GTK_WINDOW (window), draw_rect.width, draw_rect.height); - - /* We (ab)use app-paintable to indicate if we have an RGBA window */ - if (!gtk_widget_get_app_paintable (window)) - { - GdkWindow *gdkwindow = gtk_widget_get_window (window); - - /* Shape the window to make only the outline visible */ - if (draw_rect.width > 2 && draw_rect.height > 2) - { -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_region_t *region, *region2; - cairo_rectangle_int_t region_rect = { -#else - GdkRegion *region, *region2; - GdkRectangle region_rect = { -#endif - 0, 0, - draw_rect.width - 2, draw_rect.height - 2 - }; - -#if GTK_CHECK_VERSION (3, 0, 0) - region = cairo_region_create_rectangle (®ion_rect); -#else - region = gdk_region_rectangle (®ion_rect); -#endif - region_rect.x++; - region_rect.y++; - region_rect.width -= 2; - region_rect.height -= 2; -#if GTK_CHECK_VERSION (3, 0, 0) - region2 = cairo_region_create_rectangle (®ion_rect); - cairo_region_subtract (region, region2); -#else - region2 = gdk_region_rectangle (®ion_rect); - gdk_region_subtract (region, region2); -#endif - - gdk_window_shape_combine_region (gdkwindow, region, 0, 0); - - gdk_region_destroy (region); - gdk_region_destroy (region2); - } - else - gdk_window_shape_combine_region (gdkwindow, NULL, 0, 0); - } - - return TRUE; -} - -static gboolean -select_area_button_release (GtkWidget *window, - GdkEventButton *event, - select_area_filter_data *data) -{ - if (!data->button_pressed) - return TRUE; - - data->rect.width = ABS (data->rect.x - event->x_root); - data->rect.height = ABS (data->rect.y - event->y_root); - data->rect.x = MIN (data->rect.x, event->x_root); - data->rect.y = MIN (data->rect.y, event->y_root); - - gtk_main_quit (); - - return TRUE; -} - -static gboolean -select_area_key_press (GtkWidget *window, - GdkEventKey *event, - select_area_filter_data *data) -{ - if (event->keyval == GDK_KEY_Escape) - { - data->rect.x = 0; - data->rect.y = 0; - data->rect.width = 0; - data->rect.height = 0; - gtk_main_quit (); - } - - return TRUE; -} - - -static gboolean -#if GTK_CHECK_VERSION (3, 0, 0) -draw (GtkWidget *window, cairo_t *cr, gpointer unused) -#else -expose (GtkWidget *window, GdkEventExpose *event, gpointer unused) -#endif -{ - GtkAllocation allocation; - GtkStyle *style; -#if !GTK_CHECK_VERSION (3, 0, 0) - cairo_t *cr; - - cr = gdk_cairo_create (event->window); - gdk_cairo_region (cr, event->region); - cairo_clip (cr); -#endif - - style = gtk_widget_get_style (window); - - if (gtk_widget_get_app_paintable (window)) - { - cairo_set_line_width (cr, 1.0); - - gtk_widget_get_allocation (window, &allocation); - - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba (cr, 0, 0, 0, 0); - cairo_paint (cr); - - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]); - cairo_paint_with_alpha (cr, 0.25); - - cairo_rectangle (cr, - allocation.x + 0.5, allocation.y + 0.5, - allocation.width - 1, allocation.height - 1); - cairo_stroke (cr); - } - else - { - gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]); - cairo_paint (cr); - } - -#if !GTK_CHECK_VERSION (3, 0, 0) - cairo_destroy (cr); -#endif - - return TRUE; -} - -static GtkWidget * -create_select_window (void) -{ - GtkWidget *window; - GdkScreen *screen; -#if GTK_CHECK_VERSION (3, 0, 0) - GdkVisual *visual; -#endif - - screen = gdk_screen_get_default (); -#if GTK_CHECK_VERSION (3, 0, 0) - visual = gdk_screen_get_rgba_visual (screen); -#endif - - window = gtk_window_new (GTK_WINDOW_POPUP); - if (gdk_screen_is_composited (screen) && -#if GTK_CHECK_VERSION (3, 0, 0) - visual) -#else - gdk_screen_get_rgba_colormap (screen)) -#endif - { -#if GTK_CHECK_VERSION (3, 0, 0) - gtk_widget_set_visual (window, visual); -#else - gtk_widget_set_colormap (window, gdk_screen_get_rgba_colormap (screen)); -#endif - gtk_widget_set_app_paintable (window, TRUE); - } -#if GTK_CHECK_VERSION (3, 0, 0) - g_signal_connect (window, "draw", G_CALLBACK (draw), NULL); -#else - g_signal_connect (window, "expose-event", G_CALLBACK (expose), NULL); -#endif - - gtk_window_move (GTK_WINDOW (window), -100, -100); - gtk_window_resize (GTK_WINDOW (window), 10, 10); - gtk_widget_show (window); - return window; -} - -typedef struct { - GdkRectangle rectangle; - SelectAreaCallback callback; -} CallbackData; - -static gboolean -emit_select_callback_in_idle (gpointer user_data) -{ - CallbackData *data = user_data; - - data->callback (&data->rectangle); - - g_slice_free (CallbackData, data); - - return FALSE; -} - -void -screenshot_select_area_async (SelectAreaCallback callback) -{ - GdkCursor *cursor; - select_area_filter_data data; - GdkRectangle *rectangle; - CallbackData *cb_data; - - data.rect.x = 0; - data.rect.y = 0; - data.rect.width = 0; - data.rect.height = 0; - data.button_pressed = FALSE; - data.window = create_select_window(); - - cb_data = g_slice_new0 (CallbackData); - cb_data->callback = callback; - - g_signal_connect (data.window, "key-press-event", G_CALLBACK (select_area_key_press), &data); - g_signal_connect (data.window, "button-press-event", G_CALLBACK (select_area_button_press), &data); - g_signal_connect (data.window, "button-release-event", G_CALLBACK (select_area_button_release), &data); - g_signal_connect (data.window, "motion-notify-event", G_CALLBACK (select_area_motion_notify), &data); - - cursor = gdk_cursor_new (GDK_CROSSHAIR); - - if (gdk_pointer_grab (gtk_widget_get_window (data.window), FALSE, - GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, - NULL, cursor, - GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) - { - gdk_cursor_unref (cursor); - goto out; - } - - if (gdk_keyboard_grab (gtk_widget_get_window (data.window), FALSE, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) - { - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_cursor_unref (cursor); - goto out; - } - - gtk_main (); - - gdk_keyboard_ungrab (GDK_CURRENT_TIME); - gdk_pointer_ungrab (GDK_CURRENT_TIME); - - gtk_widget_destroy (data.window); - gdk_cursor_unref (cursor); - - gdk_flush (); - - out: - cb_data->rectangle = data.rect; - - /* FIXME: we should actually be emitting the callback When - * the compositor has finished re-drawing, but there seems to be no easy - * way to know that. - */ - g_timeout_add (200, emit_select_callback_in_idle, cb_data); -} - -static Window -find_wm_window (Window xid) -{ - Window root, parent, *children; - unsigned int nchildren; - - do - { - if (XQueryTree (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xid, &root, - &parent, &children, &nchildren) == 0) - { - g_warning ("Couldn't find window manager window"); - return None; - } - - if (root == parent) - return xid; - - xid = parent; - } - while (TRUE); -} - -#if GTK_CHECK_VERSION (3, 0, 0) -static cairo_region_t * -#else -static GdkRegion * -#endif -make_region_with_monitors (GdkScreen *screen) -{ -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_region_t *region; -#else - GdkRegion *region; -#endif - int num_monitors; - int i; - - num_monitors = gdk_screen_get_n_monitors (screen); - -#if GTK_CHECK_VERSION (3, 0, 0) - region = cairo_region_create (); -#else - region = gdk_region_new (); -#endif - - for (i = 0; i < num_monitors; i++) - { - GdkRectangle rect; - - gdk_screen_get_monitor_geometry (screen, i, &rect); -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_region_union_rectangle (region, &rect); -#else - gdk_region_union_with_rect (region, &rect); -#endif - } - - return region; -} - -static void -blank_rectangle_in_pixbuf (GdkPixbuf *pixbuf, GdkRectangle *rect) -{ - int x, y; - int x2, y2; - guchar *pixels; - int rowstride; - int n_channels; - guchar *row; - gboolean has_alpha; - - g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB); - - x2 = rect->x + rect->width; - y2 = rect->y + rect->height; - - pixels = gdk_pixbuf_get_pixels (pixbuf); - rowstride = gdk_pixbuf_get_rowstride (pixbuf); - has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); - n_channels = gdk_pixbuf_get_n_channels (pixbuf); - - for (y = rect->y; y < y2; y++) - { - guchar *p; - - row = pixels + y * rowstride; - p = row + rect->x * n_channels; - - for (x = rect->x; x < x2; x++) - { - *p++ = 0; - *p++ = 0; - *p++ = 0; - - if (has_alpha) - *p++ = 255; /* opaque black */ - } - } -} - -static void -#if GTK_CHECK_VERSION (3, 0, 0) -blank_region_in_pixbuf (GdkPixbuf *pixbuf, cairo_region_t *region) -{ - int n_rects; - int i; - int width, height; - cairo_rectangle_int_t pixbuf_rect; - - n_rects = cairo_region_num_rectangles (region); -#else -blank_region_in_pixbuf (GdkPixbuf *pixbuf, GdkRegion *region) -{ - GdkRectangle *rects; - int n_rects; - int i; - int width, height; - GdkRectangle pixbuf_rect; - - gdk_region_get_rectangles (region, &rects, &n_rects); -#endif - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - - pixbuf_rect.x = 0; - pixbuf_rect.y = 0; - pixbuf_rect.width = width; - pixbuf_rect.height = height; - - for (i = 0; i < n_rects; i++) - { -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_rectangle_int_t rect, dest; - - cairo_region_get_rectangle (region, i, &rect); - if (gdk_rectangle_intersect (&rect, &pixbuf_rect, &dest)) -#else - GdkRectangle dest; - - if (gdk_rectangle_intersect (rects + i, &pixbuf_rect, &dest)) -#endif - blank_rectangle_in_pixbuf (pixbuf, &dest); - - } -#if !GTK_CHECK_VERSION (3, 0, 0) - g_free (rects); -#endif -} - -/* When there are multiple monitors with different resolutions, the visible area - * within the root window may not be rectangular (it may have an L-shape, for - * example). In that case, mask out the areas of the root window which would - * not be visible in the monitors, so that screenshot do not end up with content - * that the user won't ever see. - */ -static void -mask_monitors (GdkPixbuf *pixbuf, GdkWindow *root_window) -{ - GdkScreen *screen; -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_region_t *region_with_monitors; - cairo_region_t *invisible_region; - cairo_rectangle_int_t rect; -#else - GdkRegion *region_with_monitors; - GdkRegion *invisible_region; - GdkRectangle rect; -#endif - - screen = gdk_window_get_screen (root_window); - - region_with_monitors = make_region_with_monitors (screen); - - rect.x = 0; - rect.y = 0; - rect.width = gdk_screen_get_width (screen); - rect.height = gdk_screen_get_height (screen); - -#if GTK_CHECK_VERSION (3, 0, 0) - invisible_region = cairo_region_create_rectangle (&rect); - cairo_region_subtract (invisible_region, region_with_monitors); - - blank_region_in_pixbuf (pixbuf, invisible_region); - - cairo_region_destroy (region_with_monitors); - cairo_region_destroy (invisible_region); -#else - invisible_region = gdk_region_rectangle (&rect); - gdk_region_subtract (invisible_region, region_with_monitors); - - blank_region_in_pixbuf (pixbuf, invisible_region); - - gdk_region_destroy (region_with_monitors); - gdk_region_destroy (invisible_region); -#endif -} - -GdkPixbuf * -screenshot_get_pixbuf (GdkWindow *window, - GdkRectangle *rectangle, - gboolean include_pointer, - gboolean include_border, - gboolean include_mask) -{ - GdkWindow *root; - GdkPixbuf *screenshot; - gint x_real_orig, y_real_orig, x_orig, y_orig; - gint width, real_width, height, real_height; - - /* If the screenshot should include the border, we look for the WM window. */ - - if (include_border) - { - Window xid, wm; - - xid = GDK_WINDOW_XID (window); - wm = find_wm_window (xid); - - if (wm != None) - window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), wm); - - /* fallback to no border if we can't find the WM window. */ - } - - root = gdk_get_default_root_window (); - - real_width = gdk_window_get_width(window); - real_height = gdk_window_get_height(window); - - gdk_window_get_origin (window, &x_real_orig, &y_real_orig); - - x_orig = x_real_orig; - y_orig = y_real_orig; - width = real_width; - height = real_height; - - if (x_orig < 0) - { - width = width + x_orig; - x_orig = 0; - } - - if (y_orig < 0) - { - height = height + y_orig; - y_orig = 0; - } - - if (x_orig + width > gdk_screen_width ()) - width = gdk_screen_width () - x_orig; - - if (y_orig + height > gdk_screen_height ()) - height = gdk_screen_height () - y_orig; - - if (rectangle) - { - x_orig = rectangle->x - x_orig; - y_orig = rectangle->y - y_orig; - width = rectangle->width; - height = rectangle->height; - } - -#if GTK_CHECK_VERSION (3, 0, 0) - screenshot = gdk_pixbuf_get_from_window (root, - x_orig, y_orig, - width, height); -#else - screenshot = gdk_pixbuf_get_from_drawable (NULL, root, NULL, - x_orig, y_orig, 0, 0, - width, height); -#endif - - /* - * Masking currently only works properly with full-screen shots - */ - if (include_mask) - mask_monitors (screenshot, root); - -#ifdef HAVE_X11_EXTENSIONS_SHAPE_H - if (include_border) - { - XRectangle *rectangles; - GdkPixbuf *tmp; - int rectangle_count, rectangle_order, i; - - /* we must use XShape to avoid showing what's under the rounder corners - * of the WM decoration. - */ - - rectangles = XShapeGetRectangles (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - GDK_WINDOW_XID (window), - ShapeBounding, - &rectangle_count, - &rectangle_order); - if (rectangles && rectangle_count > 0 && window != root) - { - gboolean has_alpha = gdk_pixbuf_get_has_alpha (screenshot); - - tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); - gdk_pixbuf_fill (tmp, 0); - - for (i = 0; i < rectangle_count; i++) - { - gint rec_x, rec_y; - gint rec_width, rec_height; - gint y; - - rec_x = rectangles[i].x; - rec_y = rectangles[i].y; - rec_width = rectangles[i].width; - rec_height = rectangles[i].height; - - if (x_real_orig < 0) - { - rec_x += x_real_orig; - rec_x = MAX(rec_x, 0); - rec_width += x_real_orig; - } - - if (y_real_orig < 0) - { - rec_y += y_real_orig; - rec_y = MAX(rec_y, 0); - rec_height += y_real_orig; - } - - if (x_orig + rec_x + rec_width > gdk_screen_width ()) - rec_width = gdk_screen_width () - x_orig - rec_x; - - if (y_orig + rec_y + rec_height > gdk_screen_height ()) - rec_height = gdk_screen_height () - y_orig - rec_y; - - for (y = rec_y; y < rec_y + rec_height; y++) - { - guchar *src_pixels, *dest_pixels; - gint x; - - src_pixels = gdk_pixbuf_get_pixels (screenshot) - + y * gdk_pixbuf_get_rowstride(screenshot) - + rec_x * (has_alpha ? 4 : 3); - dest_pixels = gdk_pixbuf_get_pixels (tmp) - + y * gdk_pixbuf_get_rowstride (tmp) - + rec_x * 4; - - for (x = 0; x < rec_width; x++) - { - *dest_pixels++ = *src_pixels++; - *dest_pixels++ = *src_pixels++; - *dest_pixels++ = *src_pixels++; - - if (has_alpha) - *dest_pixels++ = *src_pixels++; - else - *dest_pixels++ = 255; - } - } - } - - g_object_unref (screenshot); - screenshot = tmp; - } - } -#endif /* HAVE_X11_EXTENSIONS_SHAPE_H */ - - /* if we have a selected area, there were by definition no cursor in the - * screenshot */ - if (include_pointer && !rectangle) - { - GdkCursor *cursor; - GdkPixbuf *cursor_pixbuf; - - cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_LEFT_PTR); - cursor_pixbuf = gdk_cursor_get_image (cursor); - - if (cursor_pixbuf != NULL) - { - GdkRectangle r1, r2; - gint cx, cy, xhot, yhot; - - gdk_window_get_pointer (window, &cx, &cy, NULL); - sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"), "%d", &xhot); - sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"), "%d", &yhot); - - /* in r1 we have the window coordinates */ - r1.x = x_real_orig; - r1.y = y_real_orig; - r1.width = real_width; - r1.height = real_height; - - /* in r2 we have the cursor window coordinates */ - r2.x = cx + x_real_orig; - r2.y = cy + y_real_orig; - r2.width = gdk_pixbuf_get_width (cursor_pixbuf); - r2.height = gdk_pixbuf_get_height (cursor_pixbuf); - - /* see if the pointer is inside the window */ - if (gdk_rectangle_intersect (&r1, &r2, &r2)) - { - gdk_pixbuf_composite (cursor_pixbuf, screenshot, - cx - xhot, cy - yhot, - r2.width, r2.height, - cx - xhot, cy - yhot, - 1.0, 1.0, - GDK_INTERP_BILINEAR, - 255); - } - - g_object_unref (cursor_pixbuf); - gdk_cursor_unref (cursor); - } - } - - return screenshot; -} - -gchar * -screenshot_get_window_title (GdkWindow *win) -{ - gchar *name; - - win = gdk_window_get_toplevel (win); - win = look_for_hint (win, gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("WM_STATE"))); - - name = get_utf8_property (win, gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("_NET_WM_NAME"))); - if (name) - return name; - - /* TODO: maybe we should also look at WM_NAME and WM_CLASS? */ - - return g_strdup (_("Untitled Window")); -} - -void -screenshot_show_error_dialog (GtkWindow *parent, - const gchar *message, - const gchar *detail) -{ - GtkWidget *dialog; - - g_return_if_fail ((parent == NULL) || (GTK_IS_WINDOW (parent))); - g_return_if_fail (message != NULL); - - dialog = gtk_message_dialog_new (parent, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", message); - gtk_window_set_title (GTK_WINDOW (dialog), ""); - - if (detail) - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), - "%s", detail); - - if (parent && gtk_window_get_group (parent)) - gtk_window_group_add_window (gtk_window_get_group (parent), GTK_WINDOW (dialog)); - - gtk_dialog_run (GTK_DIALOG (dialog)); - - gtk_widget_destroy (dialog); -} - -void -screenshot_show_gerror_dialog (GtkWindow *parent, - const gchar *message, - GError *error) -{ - g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); - g_return_if_fail (message != NULL); - g_return_if_fail (error != NULL); - - screenshot_show_error_dialog (parent, message, error->message); -} diff --git a/mate-screenshot/screenshot-utils.h b/mate-screenshot/screenshot-utils.h deleted file mode 100644 index b9d13f7b..00000000 --- a/mate-screenshot/screenshot-utils.h +++ /dev/null @@ -1,50 +0,0 @@ -/* screenshot-utils.h - common functions for MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * - * 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, - */ - -#ifndef __SCREENSHOT_UTILS_H__ -#define __SCREENSHOT_UTILS_H__ - -#include -#include - -G_BEGIN_DECLS - -typedef void (* SelectAreaCallback) (GdkRectangle *rectangle); - -gboolean screenshot_grab_lock (void); -void screenshot_release_lock (void); -gchar *screenshot_get_window_title (GdkWindow *win); -GdkWindow *screenshot_find_current_window (void); -void screenshot_select_area_async (SelectAreaCallback callback); -GdkPixbuf *screenshot_get_pixbuf (GdkWindow *win, - GdkRectangle *rectangle, - gboolean include_pointer, - gboolean include_border, - gboolean include_mask); - -void screenshot_show_error_dialog (GtkWindow *parent, - const gchar *message, - const gchar *detail); -void screenshot_show_gerror_dialog (GtkWindow *parent, - const gchar *message, - GError *error); - -G_END_DECLS - -#endif /* __SCREENSHOT_UTILS_H__ */ diff --git a/mate-screenshot/screenshot-xfer.c b/mate-screenshot/screenshot-xfer.c deleted file mode 100644 index a4cfeac1..00000000 --- a/mate-screenshot/screenshot-xfer.c +++ /dev/null @@ -1,393 +0,0 @@ -/* screenshot-xfer.c - file transfer functions for MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * Copyright (C) 2008 Cosimo Cecchi - * - * 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, - */ - -#include "config.h" - -#include "screenshot-xfer.h" - -#include -#include - -typedef struct -{ - GtkWidget *dialog; - GtkWidget *progress_bar; - GCancellable *cancellable; -} TransferDialog; - -typedef struct -{ - GFile *source; - GFile *dest; - GFileCopyFlags flags; - TransferCallback callback; - gpointer callback_data; - GCancellable *cancellable; - GtkWidget *parent; - TransferDialog *dialog; - TransferResult result; - GIOSchedulerJob *io_job; - char *error; - gint dialog_timeout_id; - goffset total_bytes; - goffset current_bytes; -} TransferJob; - -typedef struct -{ - int resp; - GtkWidget *parent; - char *basename; -} ErrorDialogData; - -static gboolean -do_run_overwrite_confirm_dialog (gpointer _data) -{ - ErrorDialogData *data = _data; - GtkWidget *dialog; - gint response; - - /* we need to ask the user if they want to overwrite this file */ - dialog = gtk_message_dialog_new (GTK_WINDOW (data->parent), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - _("File already exists")); - - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), - _("The file \"%s\" already exists. " - "Would you like to replace it?"), - data->basename); - gtk_dialog_add_button (GTK_DIALOG (dialog), - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL); - gtk_dialog_add_button (GTK_DIALOG (dialog), - _("_Replace"), - GTK_RESPONSE_OK); - - response = gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - - data->resp = response; - - return FALSE; -} - -static void -transfer_dialog_response_cb (GtkDialog *d, - gint response, - GCancellable *cancellable) -{ - if (response == GTK_RESPONSE_CANCEL) - { - if (!g_cancellable_is_cancelled (cancellable)) - { - g_cancellable_cancel (cancellable); - } - } -} - -static gboolean -transfer_progress_dialog_new (TransferJob *job) -{ - TransferDialog *dialog; - GtkWidget *gdialog; - GtkWidget *widget; - - dialog = g_new0 (TransferDialog, 1); - - gdialog = gtk_message_dialog_new (GTK_WINDOW (job->parent), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_OTHER, - GTK_BUTTONS_CANCEL, - _("Saving file...")); - widget = gtk_progress_bar_new (); - gtk_box_pack_start (GTK_BOX (gtk_message_dialog_get_message_area GTK_MESSAGE_DIALOG (gdialog)), - widget, FALSE, 0, 0); - gtk_widget_show (widget); - dialog->progress_bar = widget; - dialog->dialog = gdialog; - - g_signal_connect (gdialog, - "response", - G_CALLBACK (transfer_dialog_response_cb), - job->cancellable); - - job->dialog = dialog; - gtk_widget_show (gdialog); - - return FALSE; -} - -static void -transfer_progress_dialog_start (TransferJob *job) -{ - /* sends to the mainloop and schedules show */ - if (job->dialog_timeout_id == 0) - job->dialog_timeout_id = g_timeout_add_seconds (1, - (GSourceFunc) transfer_progress_dialog_new, - job); -} - -static int -run_overwrite_confirm_dialog (TransferJob *job) -{ - ErrorDialogData *data; - gboolean need_timeout; - int response; - char *basename; - - basename = g_file_get_basename (job->dest); - - data = g_slice_new0 (ErrorDialogData); - data->parent = job->parent; - data->basename = basename; - - need_timeout = (job->dialog_timeout_id > 0); - - if (need_timeout) - { - g_source_remove (job->dialog_timeout_id); - job->dialog_timeout_id = 0; - } - - g_io_scheduler_job_send_to_mainloop (job->io_job, - do_run_overwrite_confirm_dialog, - data, - NULL); - response = data->resp; - - if (need_timeout) - transfer_progress_dialog_start (job); - - g_free (basename); - g_slice_free (ErrorDialogData, data); - - return response; -} - -static gboolean -transfer_progress_dialog_update (TransferJob *job) -{ - TransferDialog *dialog = job->dialog; - double fraction; - - fraction = ((double) job->current_bytes) / ((double) job->total_bytes); - - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress_bar), - fraction); - - return FALSE; -} - -static gboolean -transfer_job_done (gpointer user_data) -{ - TransferJob *job = user_data; - TransferDialog *dialog; - - dialog = job->dialog; - - if (job->dialog_timeout_id) - { - g_source_remove (job->dialog_timeout_id); - job->dialog_timeout_id = 0; - } - if (dialog) - gtk_widget_destroy (dialog->dialog); - - if (job->callback) - { - (job->callback) (job->result, - job->error, - job->callback_data); - } - - g_object_unref (job->source); - g_object_unref (job->dest); - g_object_unref (job->cancellable); - - g_free (dialog); - g_free (job->error); - g_slice_free (TransferJob, job); - - return FALSE; -} - -static void -transfer_progress_cb (goffset current_num_bytes, - goffset total_num_bytes, - TransferJob *job) -{ - job->current_bytes = current_num_bytes; - - if (!job->dialog) - return; - - g_io_scheduler_job_send_to_mainloop_async (job->io_job, - (GSourceFunc) transfer_progress_dialog_update, - job, - NULL); -} - -static goffset -get_file_size (GFile *file) -{ - GFileInfo *file_info; - goffset size; - - file_info = g_file_query_info (file, - G_FILE_ATTRIBUTE_STANDARD_SIZE, - 0, NULL, NULL); - if (file_info != NULL) - { - size = g_file_info_get_size (file_info); - g_object_unref (file_info); - } - else - { - /* this should never fail as the source file is always local and in /tmp, - * but you never know. - */ - size = -1; - } - - return size; -} - -static gboolean -transfer_file (GIOSchedulerJob *io_job, - GCancellable *cancellable, - gpointer user_data) -{ - TransferJob *job = user_data; - GError *error; - - job->io_job = io_job; - job->total_bytes = get_file_size (job->source); - if (job->total_bytes == -1) - { - /* we can't access the source file, abort early */ - error = NULL; - job->result = TRANSFER_ERROR; - job->error = g_strdup (_("Can't access source file")); - - goto out; - } - - transfer_progress_dialog_start (job); - -retry: - error = NULL; - if (!g_file_copy (job->source, - job->dest, - job->flags, - job->cancellable, - (GFileProgressCallback) transfer_progress_cb, - job, - &error)) - { - if (error->code == G_IO_ERROR_EXISTS) - { - int response; - /* ask the user if he wants to overwrite, otherwise - * return and report what happened. - */ - response = run_overwrite_confirm_dialog (job); - if (response == GTK_RESPONSE_OK) - { - job->flags |= G_FILE_COPY_OVERWRITE; - g_error_free (error); - - goto retry; - } - else - { - g_cancellable_cancel (job->cancellable); - job->result = TRANSFER_OVERWRITE; - goto out; - } - } - else if (error->code == G_IO_ERROR_CANCELLED) - { - job->result = TRANSFER_CANCELLED; - - goto out; - } - else - { - /* other vfs error, abort the transfer and report - * the error. - */ - g_cancellable_cancel (job->cancellable); - job->result = TRANSFER_ERROR; - job->error = g_strdup (error->message); - - goto out; - } - } - else - { - /* success */ - job->result = TRANSFER_OK; - - goto out; - } - -out: - if (error) - g_error_free (error); - - g_io_scheduler_job_send_to_mainloop_async (io_job, - transfer_job_done, - job, - NULL); - return FALSE; -} - -void -screenshot_xfer_uri (GFile *source_file, - GFile *target_file, - GtkWidget *parent, - TransferCallback done_callback, - gpointer done_callback_data) -{ - TransferJob *job; - GCancellable *cancellable; - - cancellable = g_cancellable_new (); - - job = g_slice_new0 (TransferJob); - job->parent = parent; - job->source = g_object_ref (source_file); - job->dest = g_object_ref (target_file); - job->flags = 0; - job->error = NULL; - job->dialog = NULL; - job->callback = done_callback; - job->callback_data = done_callback_data; - job->cancellable = cancellable; - - g_io_scheduler_push_job (transfer_file, - job, - NULL, 0, - job->cancellable); -} - diff --git a/mate-screenshot/screenshot-xfer.h b/mate-screenshot/screenshot-xfer.h deleted file mode 100644 index 52262f11..00000000 --- a/mate-screenshot/screenshot-xfer.h +++ /dev/null @@ -1,45 +0,0 @@ -/* screenshot-xfer.h - file transfer functions for MATE Screenshot - * - * Copyright (C) 2001-2006 Jonathan Blandford - * Copyright (C) 2008 Cosimo Cecchi - * - * 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, - */ - -#ifndef __SCREENSHOT_XFER_H__ -#define __SCREENSHOT_XFER_H__ - -#include -#include - -typedef enum -{ - TRANSFER_OK, - TRANSFER_OVERWRITE, - TRANSFER_CANCELLED, - TRANSFER_ERROR -} TransferResult; - -typedef void (* TransferCallback) (TransferResult result, - char *error_message, - gpointer data); - -void screenshot_xfer_uri (GFile *source_file, - GFile *target_file, - GtkWidget *parent, - TransferCallback done_callback, - gpointer done_callback_data); - -#endif /* __SCREENSHOT_XFER_H__ */ diff --git a/mate-screenshot/src/Makefile.am b/mate-screenshot/src/Makefile.am new file mode 100644 index 00000000..fecac8cd --- /dev/null +++ b/mate-screenshot/src/Makefile.am @@ -0,0 +1,44 @@ +AM_CPPFLAGS = \ + -I. \ + -I$(srcdir) \ + -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + -DUIDIR=\""$(uidir)"\" \ + $(DISABLE_DEPRECATED) + +bin_PROGRAMS = mate-screenshot + +mate_screenshot_SOURCES = \ + mate-screenshot.c \ + screenshot-dialog.c \ + screenshot-dialog.h \ + screenshot-shadow.c \ + screenshot-shadow.h \ + screenshot-utils.c \ + screenshot-utils.h \ + screenshot-save.c \ + screenshot-save.h \ + screenshot-xfer.c \ + screenshot-xfer.h + +mate_screenshot_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(GIO_CFLAGS) \ + $(LIBCANBERRA_GTK_CFLAGS) \ + $(GTHREAD_CFLAGS) \ + $(GTK_CFLAGS) + +mate_screenshot_LDFLAGS = -export-dynamic + +mate_screenshot_LDADD = \ + $(XSHAPE_LIBS) \ + $(GLIB_LIBS) \ + $(GIO_LIBS) \ + $(LIBCANBERRA_GTK_LIBS) \ + $(GTHREAD_LIBS) \ + $(GTK_LIBS) \ + -lm + +install-exec-local: + rm -f $(DESTDIR)$(bindir)/mate-panel-screenshot + ln -s mate-screenshot $(DESTDIR)$(bindir)/mate-panel-screenshot + diff --git a/mate-screenshot/src/mate-screenshot.c b/mate-screenshot/src/mate-screenshot.c new file mode 100644 index 00000000..3e121f00 --- /dev/null +++ b/mate-screenshot/src/mate-screenshot.c @@ -0,0 +1,1372 @@ +/* mate-screenshot.c - Take a screenshot of the desktop + * + * Copyright (C) 2001 Jonathan Blandford + * Copyright (C) 2006 Emmanuele Bassi + * Copyright (C) 2008 Cosimo Cecchi + * + * 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 + */ + +/* THERE ARE NO FEATURE REQUESTS ALLOWED */ +/* IF YOU WANT YOUR OWN FEATURE -- WRITE THE DAMN THING YOURSELF (-: */ +/* MAYBE I LIED... -jrb */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "screenshot-shadow.h" +#include "screenshot-utils.h" +#include "screenshot-save.h" +#include "screenshot-dialog.h" +#include "screenshot-xfer.h" + +#define SCREENSHOOTER_ICON "applets-screenshooter" + +#define MATE_SCREENSHOT_SCHEMA "org.mate.screenshot" +#define INCLUDE_BORDER_KEY "include-border" +#define INCLUDE_POINTER_KEY "include-pointer" +#define LAST_SAVE_DIRECTORY_KEY "last-save-directory" +#define BORDER_EFFECT_KEY "border-effect" +#define DELAY_KEY "delay" +#define CAJA_PREFERENCES_SCHEMA "org.mate.caja.preferences" + +enum +{ + COLUMN_NICK, + COLUMN_LABEL, + COLUMN_ID, + + N_COLUMNS +}; + +typedef enum { + SCREENSHOT_EFFECT_NONE, + SCREENSHOT_EFFECT_SHADOW, + SCREENSHOT_EFFECT_BORDER +} ScreenshotEffectType; + +typedef enum +{ + TEST_LAST_DIR = 0, + TEST_DESKTOP = 1, + TEST_TMP = 2, +} TestType; + +typedef struct +{ + char *base_uris[3]; + char *retval; + int iteration; + TestType type; + GdkWindow *window; + GdkRectangle *rectangle; +} AsyncExistenceJob; + +static GdkPixbuf *screenshot = NULL; + +/* Global variables*/ +static char *last_save_dir = NULL; +static char *window_title = NULL; +static char *temporary_file = NULL; +static gboolean save_immediately = FALSE; +static GSettings *settings = NULL; + +/* Options */ +static gboolean take_window_shot = FALSE; +static gboolean take_area_shot = FALSE; +static gboolean include_border = FALSE; +static gboolean include_pointer = TRUE; +static char *border_effect = NULL; +static guint delay = 0; + +/* some local prototypes */ +static void display_help (GtkWindow *parent); +static void save_done_notification (gpointer data); +static char *get_desktop_dir (void); +static void save_options (void); + +static GtkWidget *border_check = NULL; +static GtkWidget *effect_combo = NULL; +static GtkWidget *effect_label = NULL; +static GtkWidget *effects_vbox = NULL; +static GtkWidget *delay_hbox = NULL; + +static void +display_help (GtkWindow *parent) +{ + GError *error = NULL; + + gtk_show_uri (gtk_window_get_screen (parent), + "help:mate-user-guide/goseditmainmenu-53", + gtk_get_current_event_time (), &error); + + if (error) + { + screenshot_show_gerror_dialog (parent, + _("Error loading the help page"), + error); + g_error_free (error); + } +} + +static void +interactive_dialog_response_cb (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + switch (response) + { + case GTK_RESPONSE_HELP: + g_signal_stop_emission_by_name (dialog, "response"); + display_help (GTK_WINDOW (dialog)); + break; + default: + gtk_widget_hide (GTK_WIDGET (dialog)); + break; + } +} + +#define TARGET_TOGGLE_DESKTOP 0 +#define TARGET_TOGGLE_WINDOW 1 +#define TARGET_TOGGLE_AREA 2 + +static void +target_toggled_cb (GtkToggleButton *button, + gpointer data) +{ + int target_toggle = GPOINTER_TO_INT (data); + + if (gtk_toggle_button_get_active (button)) + { + take_window_shot = (target_toggle == TARGET_TOGGLE_WINDOW); + take_area_shot = (target_toggle == TARGET_TOGGLE_AREA); + + gtk_widget_set_sensitive (border_check, take_window_shot); + gtk_widget_set_sensitive (effect_combo, take_window_shot); + gtk_widget_set_sensitive (effect_label, take_window_shot); + + gtk_widget_set_sensitive (delay_hbox, !take_area_shot); + gtk_widget_set_sensitive (effects_vbox, !take_area_shot); + } +} + +static void +delay_spin_value_changed_cb (GtkSpinButton *button) +{ + delay = gtk_spin_button_get_value_as_int (button); +} + +static void +include_border_toggled_cb (GtkToggleButton *button, + gpointer data) +{ + include_border = gtk_toggle_button_get_active (button); +} + +static void +include_pointer_toggled_cb (GtkToggleButton *button, + gpointer data) +{ + include_pointer = gtk_toggle_button_get_active (button); +} + +static void +effect_combo_changed_cb (GtkComboBox *combo, + gpointer user_data) +{ + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + { + GtkTreeModel *model; + gchar *effect; + + model = gtk_combo_box_get_model (combo); + gtk_tree_model_get (model, &iter, COLUMN_NICK, &effect, -1); + + g_assert (effect != NULL); + + g_free (border_effect); + border_effect = effect; /* gets free'd later */ + } +} + +static gint +key_press_cb (GtkWidget* widget, GdkEventKey* event, gpointer data) +{ + if (event->keyval == GDK_KEY_F1) + { + display_help (GTK_WINDOW (widget)); + return TRUE; + } + + return FALSE; +} + +typedef struct { + ScreenshotEffectType id; + const gchar *label; + const gchar *nick; +} ScreenshotEffect; + +/* Translators: + * these are the names of the effects available which will be + * displayed inside a combo box in interactive mode for the user + * to chooser. + */ +static const ScreenshotEffect effects[] = { + { SCREENSHOT_EFFECT_NONE, N_("None"), "none" }, + { SCREENSHOT_EFFECT_SHADOW, N_("Drop shadow"), "shadow" }, + { SCREENSHOT_EFFECT_BORDER, N_("Border"), "border" } +}; + +static guint n_effects = G_N_ELEMENTS (effects); + +static GtkWidget * +create_effects_combo (void) +{ + GtkWidget *retval; + GtkListStore *model; + GtkCellRenderer *renderer; + gint i; + + model = gtk_list_store_new (N_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_UINT); + + for (i = 0; i < n_effects; i++) + { + GtkTreeIter iter; + + gtk_list_store_insert (model, &iter, i); + gtk_list_store_set (model, &iter, + COLUMN_ID, effects[i].id, + COLUMN_LABEL, gettext (effects[i].label), + COLUMN_NICK, effects[i].nick, + -1); + } + + retval = gtk_combo_box_new (); + gtk_combo_box_set_model (GTK_COMBO_BOX (retval), + GTK_TREE_MODEL (model)); + g_object_unref (model); + + switch (border_effect[0]) + { + case 's': /* shadow */ + gtk_combo_box_set_active (GTK_COMBO_BOX (retval), + SCREENSHOT_EFFECT_SHADOW); + break; + case 'b': /* border */ + gtk_combo_box_set_active (GTK_COMBO_BOX (retval), + SCREENSHOT_EFFECT_BORDER); + break; + case 'n': /* none */ + gtk_combo_box_set_active (GTK_COMBO_BOX (retval), + SCREENSHOT_EFFECT_NONE); + break; + default: + break; + } + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (retval), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (retval), renderer, + "text", COLUMN_LABEL, + NULL); + + g_signal_connect (retval, "changed", + G_CALLBACK (effect_combo_changed_cb), + NULL); + + return retval; +} + +static void +create_effects_frame (GtkWidget *outer_vbox, + const gchar *frame_title) +{ + GtkWidget *main_vbox, *vbox, *hbox; + GtkWidget *align; + GtkWidget *label; + GtkWidget *check; + GtkWidget *combo; + gchar *title; + + main_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (outer_vbox), main_vbox, FALSE, FALSE, 0); + gtk_widget_show (main_vbox); + effects_vbox = main_vbox; + + title = g_strconcat ("", frame_title, "", NULL); + label = gtk_label_new (title); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + g_free (title); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + align = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0); + gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0); + gtk_widget_show (align); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (align), vbox); + gtk_widget_show (vbox); + + /** Include pointer **/ + check = gtk_check_button_new_with_mnemonic (_("Include _pointer")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), include_pointer); + g_signal_connect (check, "toggled", + G_CALLBACK (include_pointer_toggled_cb), + NULL); + gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); + gtk_widget_show (check); + + /** Include window border **/ + check = gtk_check_button_new_with_mnemonic (_("Include the window _border")); + gtk_widget_set_sensitive (check, take_window_shot); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), include_border); + g_signal_connect (check, "toggled", + G_CALLBACK (include_border_toggled_cb), + NULL); + gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); + gtk_widget_show (check); + border_check = check; + + /** Effects **/ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + label = gtk_label_new_with_mnemonic (_("Apply _effect:")); + gtk_widget_set_sensitive (label, take_window_shot); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + effect_label = label; + + combo = create_effects_combo (); + gtk_widget_set_sensitive (combo, take_window_shot); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + gtk_widget_show (combo); + effect_combo = combo; +} + +static void +create_screenshot_frame (GtkWidget *outer_vbox, + const gchar *frame_title) +{ + GtkWidget *main_vbox, *vbox, *hbox; + GtkWidget *align; + GtkWidget *radio; + GtkWidget *image; + GtkWidget *spin; + GtkWidget *label; + GtkAdjustment *adjust; + GSList *group; + gchar *title; + + main_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (outer_vbox), main_vbox, FALSE, FALSE, 0); + gtk_widget_show (main_vbox); + + title = g_strconcat ("", frame_title, "", NULL); + label = gtk_label_new (title); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + g_free (title); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + align = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); + gtk_widget_set_size_request (align, 48, -1); + gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0); + gtk_widget_show (align); + + image = gtk_image_new_from_stock (SCREENSHOOTER_ICON, + GTK_ICON_SIZE_DIALOG); + gtk_container_add (GTK_CONTAINER (align), image); + gtk_widget_show (image); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + /** Grab whole desktop **/ + group = NULL; + radio = gtk_radio_button_new_with_mnemonic (group, + _("Grab the whole _desktop")); + if (take_window_shot || take_area_shot) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), FALSE); + g_signal_connect (radio, "toggled", + G_CALLBACK (target_toggled_cb), + GINT_TO_POINTER (TARGET_TOGGLE_DESKTOP)); + gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, FALSE, 0); + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)); + gtk_widget_show (radio); + + /** Grab current window **/ + radio = gtk_radio_button_new_with_mnemonic (group, + _("Grab the current _window")); + if (take_window_shot) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + g_signal_connect (radio, "toggled", + G_CALLBACK (target_toggled_cb), + GINT_TO_POINTER (TARGET_TOGGLE_WINDOW)); + gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, FALSE, 0); + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)); + gtk_widget_show (radio); + + /** Grab area of the desktop **/ + radio = gtk_radio_button_new_with_mnemonic (group, + _("Select _area to grab")); + if (take_area_shot) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + g_signal_connect (radio, "toggled", + G_CALLBACK (target_toggled_cb), + GINT_TO_POINTER (TARGET_TOGGLE_AREA)); + gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, FALSE, 0); + gtk_widget_show (radio); + + /** Grab after delay **/ + delay_hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), delay_hbox, FALSE, FALSE, 0); + gtk_widget_show (delay_hbox); + + /* translators: this is the first part of the "grab after a + * delay of seconds". + */ + label = gtk_label_new_with_mnemonic (_("Grab _after a delay of")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (delay_hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adjust = GTK_ADJUSTMENT (gtk_adjustment_new ((gdouble) delay, + 0.0, 99.0, + 1.0, 1.0, + 0.0)); + spin = gtk_spin_button_new (adjust, 1.0, 0); + g_signal_connect (spin, "value-changed", + G_CALLBACK (delay_spin_value_changed_cb), + NULL); + gtk_box_pack_start (GTK_BOX (delay_hbox), spin, FALSE, FALSE, 0); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin); + gtk_widget_show (spin); + + /* translators: this is the last part of the "grab after a + * delay of seconds". + */ + label = gtk_label_new (_("seconds")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_end (GTK_BOX (delay_hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); +} + +static GtkWidget * +create_interactive_dialog (void) +{ + GtkWidget *retval; + GtkWidget *main_vbox; + GtkWidget *content_area; + + retval = gtk_dialog_new (); + gtk_window_set_resizable (GTK_WINDOW (retval), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (retval), 5); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (retval)); + gtk_box_set_spacing (GTK_BOX (content_area), 2); + gtk_window_set_title (GTK_WINDOW (retval), _("Take Screenshot")); + + /* main container */ + main_vbox = gtk_vbox_new (FALSE, 18); + gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 5); + gtk_box_pack_start (GTK_BOX (content_area), main_vbox, TRUE, TRUE, 0); + gtk_widget_show (main_vbox); + + create_screenshot_frame (main_vbox, _("Take Screenshot")); + create_effects_frame (main_vbox, _("Effects")); + + gtk_dialog_add_buttons (GTK_DIALOG (retval), + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Take _Screenshot"), GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (retval), GTK_RESPONSE_OK); + + /* we need to block on "response" and keep showing the interactive + * dialog in case the user did choose "help" + */ + g_signal_connect (retval, "response", + G_CALLBACK (interactive_dialog_response_cb), + NULL); + + g_signal_connect (G_OBJECT (retval), "key-press-event", + G_CALLBACK(key_press_cb), + NULL); + + return retval; +} + +static void +save_folder_to_settings (ScreenshotDialog *dialog) +{ + char *folder; + + folder = screenshot_dialog_get_folder (dialog); + g_settings_set_string (settings, + LAST_SAVE_DIRECTORY_KEY, folder); + + g_free (folder); +} + +static void +set_recent_entry (ScreenshotDialog *dialog) +{ + char *uri, *app_exec = NULL; + GtkRecentManager *recent; + GtkRecentData recent_data; + GAppInfo *app; + const char *exec_name = NULL; + static char * groups[2] = { "Graphics", NULL }; + + app = g_app_info_get_default_for_type ("image/png", TRUE); + + if (!app) { + /* return early, as this would be an useless recent entry anyway. */ + return; + } + + uri = screenshot_dialog_get_uri (dialog); + recent = gtk_recent_manager_get_default (); + + exec_name = g_app_info_get_executable (app); + app_exec = g_strjoin (" ", exec_name, "%u", NULL); + + recent_data.display_name = NULL; + recent_data.description = NULL; + recent_data.mime_type = "image/png"; + recent_data.app_name = "MATE Screenshot"; + recent_data.app_exec = app_exec; + recent_data.groups = groups; + recent_data.is_private = FALSE; + + gtk_recent_manager_add_full (recent, uri, &recent_data); + + g_object_unref (app); + g_free (app_exec); + g_free (uri); +} + +static void +error_dialog_response_cb (GtkDialog *d, + gint response, + ScreenshotDialog *dialog) +{ + gtk_widget_destroy (GTK_WIDGET (d)); + + screenshot_dialog_focus_entry (dialog); +} + +static void +save_callback (TransferResult result, + char *error_message, + gpointer data) +{ + ScreenshotDialog *dialog = data; + GtkWidget *toplevel; + + toplevel = screenshot_dialog_get_toplevel (dialog); + screenshot_dialog_set_busy (dialog, FALSE); + + if (result == TRANSFER_OK) + { + save_folder_to_settings (dialog); + set_recent_entry (dialog); + gtk_widget_destroy (toplevel); + + /* we're done, stop the mainloop now */ + gtk_main_quit (); + } + else if (result == TRANSFER_OVERWRITE || + result == TRANSFER_CANCELLED) + { + /* user has canceled the overwrite dialog or the transfer itself, let him + * choose another name. + */ + screenshot_dialog_focus_entry (dialog); + } + else /* result == TRANSFER_ERROR */ + { + /* we had an error, display a dialog to the user and let him choose + * another name/location to save the screenshot. + */ + GtkWidget *error_dialog; + char *uri; + + uri = screenshot_dialog_get_uri (dialog); + error_dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Error while saving screenshot")); + /* translators: first %s is the file path, second %s is the VFS error */ + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), + _("Impossible to save the screenshot " + "to %s.\n Error was %s.\n Please choose another " + "location and retry."), uri, error_message); + gtk_widget_show (error_dialog); + g_signal_connect (error_dialog, + "response", + G_CALLBACK (error_dialog_response_cb), + dialog); + + g_free (uri); + } + +} + +static void +try_to_save (ScreenshotDialog *dialog, + const char *target) +{ + GFile *source_file, *target_file; + + g_assert (temporary_file); + + screenshot_dialog_set_busy (dialog, TRUE); + + source_file = g_file_new_for_path (temporary_file); + target_file = g_file_new_for_uri (target); + + screenshot_xfer_uri (source_file, + target_file, + screenshot_dialog_get_toplevel (dialog), + save_callback, dialog); + + /* screenshot_xfer_uri () holds a ref, so we can unref now */ + g_object_unref (source_file); + g_object_unref (target_file); +} + +static void +save_done_notification (gpointer data) +{ + ScreenshotDialog *dialog = data; + + temporary_file = g_strdup (screenshot_save_get_filename ()); + screenshot_dialog_enable_dnd (dialog); + + if (save_immediately) + { + GtkWidget *toplevel; + + toplevel = screenshot_dialog_get_toplevel (dialog); + gtk_dialog_response (GTK_DIALOG (toplevel), GTK_RESPONSE_OK); + } +} + +static void +screenshot_dialog_response_cb (GtkDialog *d, + gint response_id, + ScreenshotDialog *dialog) +{ + char *uri; + + if (response_id == GTK_RESPONSE_HELP) + { + display_help (GTK_WINDOW (d)); + } + else if (response_id == GTK_RESPONSE_OK) + { + uri = screenshot_dialog_get_uri (dialog); + if (temporary_file == NULL) + { + save_immediately = TRUE; + screenshot_dialog_set_busy (dialog, TRUE); + } + else + { + /* we've saved the temporary file, lets try to copy it to the + * correct location. + */ + try_to_save (dialog, uri); + } + g_free (uri); + } + else if (response_id == SCREENSHOT_RESPONSE_COPY) + { + GtkClipboard *clipboard; + GdkPixbuf *screenshot; + + clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (d)), + GDK_SELECTION_CLIPBOARD); + screenshot = screenshot_dialog_get_screenshot (dialog); + gtk_clipboard_set_image (clipboard, screenshot); + } + else /* dialog was canceled */ + { + gtk_widget_destroy (GTK_WIDGET (d)); + gtk_main_quit (); + } +} + + +static void +run_dialog (ScreenshotDialog *dialog) +{ + GtkWidget *toplevel; + + toplevel = screenshot_dialog_get_toplevel (dialog); + + gtk_widget_show (toplevel); + + g_signal_connect (toplevel, + "response", + G_CALLBACK (screenshot_dialog_response_cb), + dialog); +} + +static void +play_sound_effect (GdkWindow *window) +{ + ca_context *c; + ca_proplist *p = NULL; + int res; + + c = ca_gtk_context_get (); + + res = ca_proplist_create (&p); + if (res < 0) + goto done; + + res = ca_proplist_sets (p, CA_PROP_EVENT_ID, "screen-capture"); + if (res < 0) + goto done; + + res = ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Screenshot taken")); + if (res < 0) + goto done; + + if (window != NULL) + { + res = ca_proplist_setf (p, + CA_PROP_WINDOW_X11_XID, + "%lu", + (unsigned long) GDK_WINDOW_XID (window)); + if (res < 0) + goto done; + } + + ca_context_play_full (c, 0, p, NULL, NULL); + + done: + if (p != NULL) + ca_proplist_destroy (p); + +} + +static void +finish_prepare_screenshot (char *initial_uri, GdkWindow *window, GdkRectangle *rectangle) +{ + ScreenshotDialog *dialog; + gboolean include_mask = (!take_window_shot && !take_area_shot); + + /* always disable window border for full-desktop or selected-area screenshots */ + if (!take_window_shot) + screenshot = screenshot_get_pixbuf (window, rectangle, include_pointer, FALSE, include_mask); + else + { + screenshot = screenshot_get_pixbuf (window, rectangle, include_pointer, include_border, include_mask); + + switch (border_effect[0]) + { + case 's': /* shadow */ + screenshot_add_shadow (&screenshot); + break; + case 'b': /* border */ + screenshot_add_border (&screenshot); + break; + case 'n': /* none */ + default: + break; + } + } + + /* release now the lock, it was acquired when we were finding the window */ + screenshot_release_lock (); + + if (screenshot == NULL) + { + screenshot_show_error_dialog (NULL, + _("Unable to take a screenshot of the current window"), + NULL); + exit (1); + } + + play_sound_effect (window); + + dialog = screenshot_dialog_new (screenshot, initial_uri, take_window_shot); + g_free (initial_uri); + + screenshot_save_start (screenshot, save_done_notification, dialog); + + run_dialog (dialog); +} + +static void +async_existence_job_free (AsyncExistenceJob *job) +{ + if (!job) + return; + + g_free (job->base_uris[1]); + g_free (job->base_uris[2]); + + if (job->rectangle != NULL) + g_slice_free (GdkRectangle, job->rectangle); + + g_slice_free (AsyncExistenceJob, job); +} + +static gboolean +check_file_done (gpointer user_data) +{ + AsyncExistenceJob *job = user_data; + + finish_prepare_screenshot (job->retval, job->window, job->rectangle); + + async_existence_job_free (job); + + return FALSE; +} + +static char * +build_uri (AsyncExistenceJob *job) +{ + char *retval, *file_name; + char *timestamp; + GDateTime *d; + + d = g_date_time_new_now_local (); + /* Translators: This is a strftime format string. + * It is used to display the time in 24-hours format (eg, like + * in France: 20:10). */ + timestamp = g_date_time_format (d, _("%Y-%m-%d %H:%M:%S")); + g_date_time_unref (d); + + if (job->iteration == 0) + { + /* translators: this is the name of the file that gets made up + * with the screenshot if the entire screen is taken */ + file_name = g_strdup_printf (_("Screenshot at %s.png"), timestamp); + } + else + { + /* translators: this is the name of the file that gets + * made up with the screenshot if the entire screen is + * taken */ + file_name = g_strdup_printf (_("Screenshot at %s - %d.png"), timestamp, job->iteration); + } + + retval = g_build_filename (job->base_uris[job->type], file_name, NULL); + g_free (file_name); + g_free (timestamp); + + return retval; +} + +static gboolean +try_check_file (GIOSchedulerJob *io_job, + GCancellable *cancellable, + gpointer data) +{ + AsyncExistenceJob *job = data; + GFile *file; + GFileInfo *info; + GError *error; + char *uri; + +retry: + error = NULL; + uri = build_uri (job); + file = g_file_new_for_uri (uri); + + info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, cancellable, &error); + if (info != NULL) + { + /* file already exists, iterate again */ + g_object_unref (info); + g_object_unref (file); + g_free (uri); + + (job->iteration)++; + + goto retry; + } + else + { + /* see the error to check whether the location is not accessible + * or the file does not exist. + */ + if (error->code == G_IO_ERROR_NOT_FOUND) + { + GFile *parent; + + /* if the parent directory doesn't exist as well, forget the saved + * directory and treat this as a generic error. + */ + + parent = g_file_get_parent (file); + + if (!g_file_query_exists (parent, NULL)) + { + (job->type)++; + job->iteration = 0; + + g_object_unref (file); + g_object_unref (parent); + goto retry; + } + else + { + job->retval = uri; + + g_object_unref (parent); + goto out; + } + } + else + { + /* another kind of error, assume this location is not + * accessible. + */ + g_free (uri); + if (job->type == TEST_TMP) + { + job->retval = NULL; + goto out; + } + else + { + (job->type)++; + job->iteration = 0; + + g_error_free (error); + g_object_unref (file); + goto retry; + } + } + } + +out: + g_error_free (error); + g_object_unref (file); + + g_io_scheduler_job_send_to_mainloop_async (io_job, + check_file_done, + job, + NULL); + return FALSE; +} + +static GdkWindow * +find_current_window (char **window_title) +{ + GdkWindow *window; + + if (!screenshot_grab_lock ()) + exit (0); + + if (take_window_shot) + { + window = screenshot_find_current_window (); + if (!window) + { + take_window_shot = FALSE; + window = gdk_get_default_root_window (); + } + else + { + gchar *tmp, *sanitized; + + tmp = screenshot_get_window_title (window); + sanitized = screenshot_sanitize_filename (tmp); + g_free (tmp); + *window_title = sanitized; + } + } + else + { + window = gdk_get_default_root_window (); + } + + return window; +} + +static void +push_check_file_job (GdkRectangle *rectangle) +{ + AsyncExistenceJob *job; + + job = g_slice_new0 (AsyncExistenceJob); + job->base_uris[0] = last_save_dir; + /* we'll have to free these two */ + job->base_uris[1] = get_desktop_dir (); + job->base_uris[2] = g_strconcat ("file://", g_get_tmp_dir (), NULL); + job->iteration = 0; + job->type = TEST_LAST_DIR; + job->window = find_current_window (&window_title); + + if (rectangle != NULL) + { + job->rectangle = g_slice_new0 (GdkRectangle); + job->rectangle->x = rectangle->x; + job->rectangle->y = rectangle->y; + job->rectangle->width = rectangle->width; + job->rectangle->height = rectangle->height; + } + + /* Check if the area selection was cancelled */ + if (job->rectangle && + (job->rectangle->width == 0 || job->rectangle->height == 0)) + { + async_existence_job_free (job); + gtk_main_quit (); + return; + } + + g_io_scheduler_push_job (try_check_file, + job, + NULL, + 0, NULL); + +} + +static void +rectangle_found_cb (GdkRectangle *rectangle) +{ + push_check_file_job (rectangle); +} + +static void +prepare_screenshot (void) +{ + if (take_area_shot) + screenshot_select_area_async (rectangle_found_cb); + else + push_check_file_job (NULL); +} + +static gboolean +prepare_screenshot_timeout (gpointer data) +{ + prepare_screenshot (); + save_options (); + + return FALSE; +} + + +static gchar * +get_desktop_dir (void) +{ + gboolean desktop_is_home_dir = FALSE; + gchar *desktop_dir; + gboolean schema_exists = FALSE; + + /* Check if caja schema is installed before trying to read settings */ + GSettingsSchema *schema = g_settings_schema_source_lookup (g_settings_schema_source_get_default (), + CAJA_PREFERENCES_SCHEMA, + FALSE); + + if (schema != NULL) { + GSettings *caja_prefs; + + caja_prefs = g_settings_new (CAJA_PREFERENCES_SCHEMA); + desktop_is_home_dir = g_settings_get_boolean (caja_prefs, "desktop-is-home-dir"); + + g_object_unref (caja_prefs); + g_settings_schema_unref (schema); + } + + if (desktop_is_home_dir) + desktop_dir = g_strconcat ("file://", g_get_home_dir (), NULL); + else + desktop_dir = g_strconcat ("file://", g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP), NULL); + + return desktop_dir; +} + +/* Taken from mate-vfs-utils.c */ +static char * +expand_initial_tilde (const char *path) +{ + char *slash_after_user_name, *user_name; + struct passwd *passwd_file_entry; + + if (path[1] == '/' || path[1] == '\0') { + return g_strconcat (g_get_home_dir (), &path[1], NULL); + } + + slash_after_user_name = strchr (&path[1], '/'); + if (slash_after_user_name == NULL) { + user_name = g_strdup (&path[1]); + } else { + user_name = g_strndup (&path[1], + slash_after_user_name - &path[1]); + } + passwd_file_entry = getpwnam (user_name); + g_free (user_name); + + if (passwd_file_entry == NULL || passwd_file_entry->pw_dir == NULL) { + return g_strdup (path); + } + + return g_strconcat (passwd_file_entry->pw_dir, + slash_after_user_name, + NULL); +} + +/* Load options */ +static void +load_options (void) +{ + /* Find various dirs */ + last_save_dir = g_settings_get_string (settings, + LAST_SAVE_DIRECTORY_KEY); + if (!last_save_dir || !last_save_dir[0]) + { + last_save_dir = get_desktop_dir (); + } + else if (last_save_dir[0] == '~') + { + char *tmp = expand_initial_tilde (last_save_dir); + g_free (last_save_dir); + last_save_dir = tmp; + } + + include_border = g_settings_get_boolean (settings, + INCLUDE_BORDER_KEY); + + include_pointer = g_settings_get_boolean (settings, + INCLUDE_POINTER_KEY); + + border_effect = g_settings_get_string (settings, + BORDER_EFFECT_KEY); + if (!border_effect) + border_effect = g_strdup ("none"); + + delay = g_settings_get_int (settings, DELAY_KEY); +} + +static void +save_options (void) +{ + g_settings_set_boolean (settings, + INCLUDE_BORDER_KEY, include_border); + g_settings_set_boolean (settings, + INCLUDE_POINTER_KEY, include_pointer); + g_settings_set_int (settings, DELAY_KEY, delay); + g_settings_set_string (settings, + BORDER_EFFECT_KEY, border_effect); +} + + +static void +register_screenshooter_icon (GtkIconFactory * factory) +{ + GtkIconSource *source; + GtkIconSet *icon_set; + + source = gtk_icon_source_new (); + gtk_icon_source_set_icon_name (source, SCREENSHOOTER_ICON); + + icon_set = gtk_icon_set_new (); + gtk_icon_set_add_source (icon_set, source); + + gtk_icon_factory_add (factory, SCREENSHOOTER_ICON, icon_set); + gtk_icon_set_unref (icon_set); + gtk_icon_source_free (source); +} + +static void +screenshooter_init_stock_icons (void) +{ + GtkIconFactory *factory; + + factory = gtk_icon_factory_new (); + gtk_icon_factory_add_default (factory); + + register_screenshooter_icon (factory); + g_object_unref (factory); +} + +/* main */ +int +main (int argc, char *argv[]) +{ + GOptionContext *context; + gboolean window_arg = FALSE; + gboolean area_arg = FALSE; + gboolean include_border_arg = FALSE; + gboolean disable_border_arg = FALSE; + gboolean interactive_arg = FALSE; + gchar *border_effect_arg = NULL; + guint delay_arg = 0; + GError *error = NULL; + + const GOptionEntry entries[] = { + { "window", 'w', 0, G_OPTION_ARG_NONE, &window_arg, N_("Grab a window instead of the entire screen"), NULL }, + { "area", 'a', 0, G_OPTION_ARG_NONE, &area_arg, N_("Grab an area of the screen instead of the entire screen"), NULL }, + { "include-border", 'b', 0, G_OPTION_ARG_NONE, &include_border_arg, N_("Include the window border with the screenshot"), NULL }, + { "remove-border", 'B', 0, G_OPTION_ARG_NONE, &disable_border_arg, N_("Remove the window border from the screenshot"), NULL }, + { "delay", 'd', 0, G_OPTION_ARG_INT, &delay_arg, N_("Take screenshot after specified delay [in seconds]"), N_("seconds") }, + { "border-effect", 'e', 0, G_OPTION_ARG_STRING, &border_effect_arg, N_("Effect to add to the border (shadow, border or none)"), N_("effect") }, + { "interactive", 'i', 0, G_OPTION_ARG_NONE, &interactive_arg, N_("Interactively set options"), NULL }, + { NULL }, + }; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + context = g_option_context_new (_("Take a picture of the screen")); + g_option_context_set_ignore_unknown_options (context, FALSE); + g_option_context_set_help_enabled (context, TRUE); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + + g_option_context_parse (context, &argc, &argv, &error); + + if (error) { + g_critical ("Unable to parse arguments: %s", error->message); + g_error_free (error); + g_option_context_free (context); + exit (1); + } + + g_option_context_free (context); + + if (window_arg && area_arg) { + g_printerr (_("Conflicting options: --window and --area should not be " + "used at the same time.\n")); + exit (1); + } + + gtk_window_set_default_icon_name (SCREENSHOOTER_ICON); + screenshooter_init_stock_icons (); + + settings = g_settings_new (MATE_SCREENSHOT_SCHEMA); + load_options (); + /* allow the command line to override options */ + if (window_arg) + take_window_shot = TRUE; + + if (area_arg) + take_area_shot = TRUE; + + if (include_border_arg) + include_border = TRUE; + + if (disable_border_arg) + include_border = FALSE; + + if (border_effect_arg) + { + g_free (border_effect); + border_effect = border_effect_arg; + } + + if (delay_arg > 0) + delay = delay_arg; + + /* interactive mode overrides everything */ + if (interactive_arg) + { + GtkWidget *dialog; + gint response; + + dialog = create_interactive_dialog (); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + switch (response) + { + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CANCEL: + return EXIT_SUCCESS; + case GTK_RESPONSE_OK: + break; + default: + g_assert_not_reached (); + break; + } + } + + if (((delay > 0 && interactive_arg) || delay_arg > 0) && + !take_area_shot) + { + g_timeout_add (delay * 1000, + prepare_screenshot_timeout, + NULL); + } + else + { + if (interactive_arg) + { + /* HACK: give time to the dialog to actually disappear. + * We don't have any way to tell when the compositor has finished + * re-drawing. + */ + g_timeout_add (200, + prepare_screenshot_timeout, NULL); + } + else + g_idle_add (prepare_screenshot_timeout, NULL); + } + + gtk_main (); + + return EXIT_SUCCESS; +} diff --git a/mate-screenshot/src/screenshot-dialog.c b/mate-screenshot/src/screenshot-dialog.c new file mode 100644 index 00000000..7c5fae6d --- /dev/null +++ b/mate-screenshot/src/screenshot-dialog.c @@ -0,0 +1,422 @@ +/* screenshot-dialog.c - main MATE Screenshot dialog + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +#include +#include +#include + +#include "screenshot-dialog.h" +#include "screenshot-save.h" +#include +#include +#include + +enum { + TYPE_IMAGE_PNG, + TYPE_TEXT_URI_LIST, + + LAST_TYPE +}; + +static GtkTargetEntry drag_types[] = +{ + { "image/png", 0, TYPE_IMAGE_PNG }, + { "text/uri-list", 0, TYPE_TEXT_URI_LIST }, +}; + +struct ScreenshotDialog +{ + GtkBuilder *ui; + GdkPixbuf *screenshot; + GdkPixbuf *preview_image; + GtkWidget *save_widget; + GtkWidget *filename_entry; + gint drag_x; + gint drag_y; +}; + +static gboolean +on_toplevel_key_press_event (GtkWidget *widget, + GdkEventKey *key) +{ + if (key->keyval == GDK_KEY_F1) + { + gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_HELP); + return TRUE; + } + + return FALSE; +} + +static void +#if GTK_CHECK_VERSION (3, 0, 0) +on_preview_draw (GtkWidget *drawing_area, cairo_t *cr, gpointer data) +#else +on_preview_expose_event (GtkWidget *drawing_area, GdkEventExpose *event, gpointer data) +#endif +{ + ScreenshotDialog *dialog = data; + GdkPixbuf *pixbuf = NULL; + gboolean free_pixbuf = FALSE; +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_t *cr; +#endif + + /* Stolen from GtkImage. I really should just make the drawing area an + * image some day */ + if (gtk_widget_get_state (drawing_area) != GTK_STATE_NORMAL) + { + GtkIconSource *source; + + source = gtk_icon_source_new (); + gtk_icon_source_set_pixbuf (source, dialog->preview_image); + gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_icon_source_set_size_wildcarded (source, FALSE); + + pixbuf = gtk_style_render_icon (gtk_widget_get_style (drawing_area), + source, + gtk_widget_get_direction (drawing_area), + gtk_widget_get_state (drawing_area), + (GtkIconSize) -1, + drawing_area, + "gtk-image"); + free_pixbuf = TRUE; + gtk_icon_source_free (source); + } + else + { + pixbuf = g_object_ref (dialog->preview_image); + } + +#if !GTK_CHECK_VERSION (3, 0, 0) + cr = gdk_cairo_create (gtk_widget_get_window (drawing_area)); + gdk_cairo_region (cr, event->region); + cairo_clip (cr); +#endif + + gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); + cairo_paint (cr); + +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_destroy (cr); +#endif + + g_object_unref (pixbuf); +} + +static gboolean +on_preview_button_press_event (GtkWidget *drawing_area, + GdkEventButton *event, + gpointer data) +{ + ScreenshotDialog *dialog = data; + + dialog->drag_x = (int) event->x; + dialog->drag_y = (int) event->y; + + return FALSE; +} + +static gboolean +on_preview_button_release_event (GtkWidget *drawing_area, + GdkEventButton *event, + gpointer data) +{ + ScreenshotDialog *dialog = data; + + dialog->drag_x = 0; + dialog->drag_y = 0; + + return FALSE; +} + +static void +on_preview_configure_event (GtkWidget *drawing_area, + GdkEventConfigure *event, + gpointer data) +{ + ScreenshotDialog *dialog = data; + + if (dialog->preview_image) + g_object_unref (G_OBJECT (dialog->preview_image)); + + dialog->preview_image = gdk_pixbuf_scale_simple (dialog->screenshot, + event->width, + event->height, + GDK_INTERP_BILINEAR); +} + +static void +drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + ScreenshotDialog *dialog) +{ + if (info == TYPE_TEXT_URI_LIST) + { + gchar **uris; + + uris = g_new (gchar *, 2); + uris[0] = g_strconcat ("file://", + screenshot_save_get_filename (), + NULL); + uris[1] = NULL; + + gtk_selection_data_set_uris (selection_data, uris); + g_strfreev (uris); + } + else if (info == TYPE_IMAGE_PNG) + { + gtk_selection_data_set_pixbuf (selection_data, dialog->screenshot); + } + else + { + g_warning ("Unknown type %d", info); + } +} + +static void +drag_begin (GtkWidget *widget, + GdkDragContext *context, + ScreenshotDialog *dialog) +{ + gtk_drag_set_icon_pixbuf (context, dialog->preview_image, + dialog->drag_x, dialog->drag_y); +} + + +ScreenshotDialog * +screenshot_dialog_new (GdkPixbuf *screenshot, + char *initial_uri, + gboolean take_window_shot) +{ + ScreenshotDialog *dialog; + GtkWidget *toplevel; + GtkWidget *preview_darea; + GtkWidget *aspect_frame; + GtkWidget *file_chooser_box; + gint width, height; + char *current_folder; + char *current_name; + char *ext; + gint pos; + GFile *tmp_file; + GFile *parent_file; + GError *error = NULL; + guint res; + + tmp_file = g_file_new_for_uri (initial_uri); + parent_file = g_file_get_parent (tmp_file); + + current_name = g_file_get_basename (tmp_file); + current_folder = g_file_get_uri (parent_file); + g_object_unref (tmp_file); + g_object_unref (parent_file); + + dialog = g_new0 (ScreenshotDialog, 1); + + dialog->ui = gtk_builder_new (); + res = gtk_builder_add_from_file (dialog->ui, UIDIR "/mate-screenshot.ui", &error); + dialog->screenshot = screenshot; + + if (res == 0) + { + GtkWidget *dialog; + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Error loading UI definition file for the screenshot program: \n%s\n\n" + "Please check your installation of mate-utils."), error->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_error_free (error); + exit (1); + } + + gtk_builder_set_translation_domain (dialog->ui, GETTEXT_PACKAGE); + + width = gdk_pixbuf_get_width (screenshot); + height = gdk_pixbuf_get_height (screenshot); + + width /= 5; + height /= 5; + + toplevel = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "toplevel")); + aspect_frame = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "aspect_frame")); + preview_darea = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "preview_darea")); + dialog->filename_entry = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "filename_entry")); + file_chooser_box = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "file_chooser_box")); + + dialog->save_widget = gtk_file_chooser_button_new (_("Select a folder"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog->save_widget), FALSE); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog->save_widget), current_folder); + gtk_entry_set_text (GTK_ENTRY (dialog->filename_entry), current_name); + + gtk_box_pack_start (GTK_BOX (file_chooser_box), dialog->save_widget, TRUE, TRUE, 0); + g_free (current_folder); + + gtk_widget_set_size_request (preview_darea, width, height); + gtk_aspect_frame_set (GTK_ASPECT_FRAME (aspect_frame), 0.0, 0.5, + gdk_pixbuf_get_width (screenshot)/ + (gfloat) gdk_pixbuf_get_height (screenshot), + FALSE); + g_signal_connect (toplevel, "key_press_event", G_CALLBACK (on_toplevel_key_press_event), dialog); +#if GTK_CHECK_VERSION (3, 0, 0) + g_signal_connect (preview_darea, "draw", G_CALLBACK (on_preview_draw), dialog); +#else + g_signal_connect (preview_darea, "expose_event", G_CALLBACK (on_preview_expose_event), dialog); +#endif + g_signal_connect (preview_darea, "button_press_event", G_CALLBACK (on_preview_button_press_event), dialog); + g_signal_connect (preview_darea, "button_release_event", G_CALLBACK (on_preview_button_release_event), dialog); + g_signal_connect (preview_darea, "configure_event", G_CALLBACK (on_preview_configure_event), dialog); + + if (take_window_shot) + gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_NONE); + else + gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN); + + /* setup dnd */ + g_signal_connect (G_OBJECT (preview_darea), "drag_begin", + G_CALLBACK (drag_begin), dialog); + g_signal_connect (G_OBJECT (preview_darea), "drag_data_get", + G_CALLBACK (drag_data_get), dialog); + + gtk_widget_show_all (toplevel); + + /* select the name of the file but leave out the extension if there's any; + * the dialog must be realized for select_region to work + */ + ext = g_utf8_strrchr (current_name, -1, '.'); + if (ext) + pos = g_utf8_strlen (current_name, -1) - g_utf8_strlen (ext, -1); + else + pos = -1; + + gtk_editable_select_region (GTK_EDITABLE (dialog->filename_entry), + 0, + pos); + + g_free (current_name); + + return dialog; +} + +void +screenshot_dialog_focus_entry (ScreenshotDialog *dialog) +{ + gtk_widget_grab_focus (dialog->filename_entry); +} + +void +screenshot_dialog_enable_dnd (ScreenshotDialog *dialog) +{ + GtkWidget *preview_darea; + + g_return_if_fail (dialog != NULL); + + preview_darea = GTK_WIDGET (gtk_builder_get_object (dialog->ui, "preview_darea")); + gtk_drag_source_set (preview_darea, + GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, + drag_types, G_N_ELEMENTS (drag_types), + GDK_ACTION_COPY); +} + +GtkWidget * +screenshot_dialog_get_toplevel (ScreenshotDialog *dialog) +{ + return GTK_WIDGET (gtk_builder_get_object (dialog->ui, "toplevel")); +} + +char * +screenshot_dialog_get_uri (ScreenshotDialog *dialog) +{ + gchar *folder; + const gchar *file_name; + gchar *uri, *file, *tmp; + GError *error; + + folder = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog->save_widget)); + file_name = gtk_entry_get_text (GTK_ENTRY (dialog->filename_entry)); + + error = NULL; + tmp = g_filename_from_utf8 (file_name, -1, NULL, NULL, &error); + if (error) + { + g_warning ("Unable to convert `%s' to valid UTF-8: %s\n" + "Falling back to default file.", + file_name, + error->message); + g_error_free (error); + tmp = g_strdup (_("Screenshot.png")); + } + + file = g_uri_escape_string (tmp, NULL, FALSE); + uri = g_build_filename (folder, file, NULL); + + g_free (folder); + g_free (tmp); + g_free (file); + + return uri; +} + +char * +screenshot_dialog_get_folder (ScreenshotDialog *dialog) +{ + return gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog->save_widget)); +} + +GdkPixbuf * +screenshot_dialog_get_screenshot (ScreenshotDialog *dialog) +{ + return dialog->screenshot; +} + +void +screenshot_dialog_set_busy (ScreenshotDialog *dialog, + gboolean busy) +{ + GtkWidget *toplevel; + + toplevel = screenshot_dialog_get_toplevel (dialog); + + if (busy) + { + GdkCursor *cursor; + /* Change cursor to busy */ + cursor = gdk_cursor_new (GDK_WATCH); + gdk_window_set_cursor (gtk_widget_get_window (toplevel), cursor); +#if GTK_CHECK_VERSION (3, 0, 0) + g_object_unref (cursor); +#else + gdk_cursor_unref (cursor); +#endif + } + else + { + gdk_window_set_cursor (gtk_widget_get_window (toplevel), NULL); + } + + gtk_widget_set_sensitive (toplevel, ! busy); + + gdk_flush (); +} diff --git a/mate-screenshot/src/screenshot-dialog.h b/mate-screenshot/src/screenshot-dialog.h new file mode 100644 index 00000000..0cd5e1da --- /dev/null +++ b/mate-screenshot/src/screenshot-dialog.h @@ -0,0 +1,42 @@ +/* screenshot-dialog.h - main MATE Screenshot dialog + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +#ifndef __SCREENSHOT_DIALOG_H__ +#define __SCREENSHOT_DIALOG_H__ + +#include + +typedef struct ScreenshotDialog ScreenshotDialog; + +/* Keep in sync with the value defined in the UI file */ +#define SCREENSHOT_RESPONSE_COPY 1 + +ScreenshotDialog *screenshot_dialog_new (GdkPixbuf *screenshot, + char *initial_uri, + gboolean take_window_shot); +void screenshot_dialog_enable_dnd (ScreenshotDialog *dialog); +GtkWidget *screenshot_dialog_get_toplevel (ScreenshotDialog *dialog); +char *screenshot_dialog_get_uri (ScreenshotDialog *dialog); +char *screenshot_dialog_get_folder (ScreenshotDialog *dialog); +GdkPixbuf *screenshot_dialog_get_screenshot (ScreenshotDialog *dialog); +void screenshot_dialog_set_busy (ScreenshotDialog *dialog, + gboolean busy); +void screenshot_dialog_focus_entry (ScreenshotDialog *dialog); + +#endif /* __SCREENSHOT_DIALOG_H__ */ diff --git a/mate-screenshot/src/screenshot-save.c b/mate-screenshot/src/screenshot-save.c new file mode 100644 index 00000000..5b870eec --- /dev/null +++ b/mate-screenshot/src/screenshot-save.c @@ -0,0 +1,285 @@ +/* screenshot-save.c - image saving functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "screenshot-save.h" + +static char *parent_dir = NULL; +static char *tmp_filename = NULL; + +static SaveFunction save_callback = NULL; +static gpointer save_user_data = NULL; + +/* Strategy for saving: + * + * We keep another process around to handle saving the image. This is + * done for two reasons. One, the saving takes a non-zero amount of + * time (about a quarter of a second on my box.) This will make it + * more interactive. The second reason is to make the child + * responsible for cleaning up the temp dir. If the parent process is + * killed or segfaults, the child process can clean up the temp dir. + */ +static void +clean_up_temporary_dir (gboolean gui_on_error) +{ + char *message; + gboolean error_occurred = FALSE; + if (g_file_test (tmp_filename, G_FILE_TEST_EXISTS)) + error_occurred = unlink (tmp_filename); + if (g_file_test (parent_dir, G_FILE_TEST_EXISTS)) + error_occurred = rmdir (parent_dir) || error_occurred; + + if (error_occurred) + { + message = g_strdup_printf (_("Unable to clear the temporary folder:\n%s"), + tmp_filename); + if (gui_on_error) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + else + { + g_warning ("%s", message); + } + g_free (message); + } + g_free (tmp_filename); + g_free (parent_dir); +} + +static void +child_done_notification (GPid pid, + gint status, + gpointer data) +{ + /* This should never be called. */ + + /* We expect the child to die after the parent. If the child dies + * than it either segfaulted, or was randomly killed. In either + * case, we can't reasonably continue. */ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("The child save process unexpectedly exited. We are unable to write the screenshot to disk.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + clean_up_temporary_dir (TRUE); + + exit (1); +} + +static gboolean +read_pipe_from_child (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + if (condition & G_IO_IN) + { + gchar *message = NULL; + gchar *error_message = NULL; + GtkWidget *dialog; + GIOStatus status; + + status = g_io_channel_read_line (source, &error_message, NULL, NULL, NULL); + + if (status == G_IO_STATUS_NORMAL) + { + message = g_strdup_printf ("Unable to save the screenshot to disk:\n\n%s", error_message); + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + exit (1); + } + } + + (*save_callback) (save_user_data); + + return FALSE; +} + +static char * +make_temp_directory (void) +{ + gint result, i; + gchar *dir_name; + + i = 0; + do + { + gchar *tmp_dir = g_strdup_printf ("mate-screenshot.%u.%d", + (unsigned int) getpid (), + i++); + + dir_name = g_build_filename (g_get_tmp_dir (), + tmp_dir, + NULL); + g_free (tmp_dir); + + result = g_mkdir_with_parents (dir_name, 0777); + if (result < 0) + { + g_free (dir_name); + + if (errno != EEXIST) + return NULL; + else + continue; + } + else + return dir_name; + } + while (TRUE); +} + +static void +signal_handler (int sig) +{ + clean_up_temporary_dir (FALSE); + + signal (sig, SIG_DFL); + kill (getpid (), sig); +} + +void +screenshot_save_start (GdkPixbuf *pixbuf, + SaveFunction callback, + gpointer user_data) +{ + GPid pid; + int parent_exit_notification[2]; + int pipe_from_child[2]; + + pipe (parent_exit_notification); + pipe (pipe_from_child); + + parent_dir = make_temp_directory (); + if (parent_dir == NULL) + return; + + tmp_filename = g_build_filename (parent_dir, + _("Screenshot.png"), + NULL); + save_callback = callback; + save_user_data = user_data; + + pid = fork (); + if (pid == 0) + { + GError *error = NULL; + char c; + + signal (SIGINT, signal_handler); + signal (SIGTERM, signal_handler); + + close (parent_exit_notification [1]); + close (pipe_from_child [0]); + + if (! gdk_pixbuf_save (pixbuf, tmp_filename, + "png", &error, + "tEXt::Software", "mate-screenshot", + NULL)) + { + if (error && error->message) + write (pipe_from_child[1], + error->message, + strlen (error->message)); + else +#define ERROR_MESSAGE _("Unknown error saving screenshot to disk") + write (pipe_from_child[1], + ERROR_MESSAGE, + strlen (ERROR_MESSAGE)); + } + /* By closing the pipe, we let the main process know that we're + * done saving it. */ + close (pipe_from_child[1]); + read (parent_exit_notification[0], &c, 1); + + clean_up_temporary_dir (FALSE); + _exit (0); + } + else if (pid > 0) + { + GIOChannel *channel; + + close (parent_exit_notification[0]); + close (pipe_from_child[1]); + + channel = g_io_channel_unix_new (pipe_from_child[0]); + g_io_add_watch (channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + read_pipe_from_child, + NULL); + g_io_channel_unref (channel); + g_child_watch_add (pid, child_done_notification, NULL); + } + else + /* George awesomely wrote code originally to handle the + * could-not-fork case synchronously. I'm not copying it, as I'm + * guessing that the system is pretty hosed if that's the case. + * However, he gets major kudos for trying. (-: + */ + g_assert_not_reached (); +} + +const char * +screenshot_save_get_filename (void) +{ + return tmp_filename; +} + +gchar * +screenshot_sanitize_filename (const char *filename) +{ + char *retval, *p; + + g_assert (filename); + g_assert (g_utf8_validate (filename, -1, NULL)); + + retval = g_uri_escape_string (filename, + "/", + TRUE); + + for (p = retval; *p != '\000'; p = g_utf8_next_char (p)) + { + if (*p == G_DIR_SEPARATOR) + *p = '-'; + } + + return retval; +} diff --git a/mate-screenshot/src/screenshot-save.h b/mate-screenshot/src/screenshot-save.h new file mode 100644 index 00000000..9df442d2 --- /dev/null +++ b/mate-screenshot/src/screenshot-save.h @@ -0,0 +1,34 @@ +/* screenshot-save.h - image saving functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +#ifndef __SCREENSHOT_SAVE_H__ +#define __SCREENSHOT_SAVE_H__ + +#include + +typedef void (*SaveFunction) (gpointer data); + +void screenshot_save_start (GdkPixbuf *pixbuf, + SaveFunction callback, + gpointer user_data); +const char *screenshot_save_get_filename (void); +gchar *screenshot_sanitize_filename (const char *filename); + + +#endif /* __SCREENSHOT_SAVE_H__ */ diff --git a/mate-screenshot/src/screenshot-shadow.c b/mate-screenshot/src/screenshot-shadow.c new file mode 100644 index 00000000..052bb9f3 --- /dev/null +++ b/mate-screenshot/src/screenshot-shadow.c @@ -0,0 +1,235 @@ +/* screenshot-shadow.c - part of MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +/* Shadow code from anders */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "screenshot-shadow.h" +#include + +#define BLUR_RADIUS 5 +#define SHADOW_OFFSET (BLUR_RADIUS * 4 / 5) +#define SHADOW_OPACITY 0.5 + +#define OUTLINE_RADIUS 1 +#define OUTLINE_OFFSET 0 +#define OUTLINE_OPACITY 1.0 + +#define dist(x0, y0, x1, y1) sqrt(((x0) - (x1))*((x0) - (x1)) + ((y0) - (y1))*((y0) - (y1))) + +typedef struct { + int size; + double *data; +} ConvFilter; + +static double +gaussian (double x, double y, double r) +{ + return ((1 / (2 * M_PI * r)) * + exp ((- (x * x + y * y)) / (2 * r * r))); +} + +static ConvFilter * +create_blur_filter (int radius) +{ + ConvFilter *filter; + int x, y; + double sum; + + filter = g_new0 (ConvFilter, 1); + filter->size = radius * 2 + 1; + filter->data = g_new (double, filter->size * filter->size); + + sum = 0.0; + + for (y = 0 ; y < filter->size; y++) + { + for (x = 0 ; x < filter->size; x++) + { + sum += filter->data[y * filter->size + x] = gaussian (x - (filter->size >> 1), + y - (filter->size >> 1), + radius); + } + } + + for (y = 0; y < filter->size; y++) + { + for (x = 0; x < filter->size; x++) + { + filter->data[y * filter->size + x] /= sum; + } + } + + return filter; + +} + +static ConvFilter * +create_outline_filter (int radius) +{ + ConvFilter *filter; + double *iter; + + filter = g_new0 (ConvFilter, 1); + filter->size = radius * 2 + 1; + filter->data = g_new (double, filter->size * filter->size); + + for (iter = filter->data; + iter < filter->data + (filter->size * filter->size); + iter++) + { + *iter = 1.0; + } + + return filter; +} + +static GdkPixbuf * +create_effect (GdkPixbuf *src, + ConvFilter const *filter, + int radius, + int offset, + double opacity) +{ + GdkPixbuf *dest; + int x, y, i, j; + int src_x, src_y; + int suma; + int dest_width, dest_height; + int src_width, src_height; + int src_rowstride, dest_rowstride; + gboolean src_has_alpha; + + guchar *src_pixels, *dest_pixels; + + src_has_alpha = gdk_pixbuf_get_has_alpha (src); + + src_width = gdk_pixbuf_get_width (src); + src_height = gdk_pixbuf_get_height (src); + dest_width = src_width + 2 * radius + offset; + dest_height = src_height + 2 * radius + offset; + + dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), + TRUE, + gdk_pixbuf_get_bits_per_sample (src), + dest_width, dest_height); + + gdk_pixbuf_fill (dest, 0); + + src_pixels = gdk_pixbuf_get_pixels (src); + src_rowstride = gdk_pixbuf_get_rowstride (src); + + dest_pixels = gdk_pixbuf_get_pixels (dest); + dest_rowstride = gdk_pixbuf_get_rowstride (dest); + + for (y = 0; y < dest_height; y++) + { + for (x = 0; x < dest_width; x++) + { + suma = 0; + + src_x = x - radius; + src_y = y - radius; + + /* We don't need to compute effect here, since this pixel will be + * discarded when compositing */ + if (src_x >= 0 && src_x < src_width && + src_y >= 0 && src_y < src_height && + (!src_has_alpha || + src_pixels [src_y * src_rowstride + src_x * 4 + 3] == 0xFF)) + continue; + + for (i = 0; i < filter->size; i++) + { + for (j = 0; j < filter->size; j++) + { + src_y = -(radius + offset) + y - (filter->size >> 1) + i; + src_x = -(radius + offset) + x - (filter->size >> 1) + j; + + if (src_y < 0 || src_y >= src_height || + src_x < 0 || src_x >= src_width) + continue; + + suma += ( src_has_alpha ? + src_pixels [src_y * src_rowstride + src_x * 4 + 3] : + 0xFF ) * filter->data [i * filter->size + j]; + } + } + + dest_pixels [y * dest_rowstride + x * 4 + 3] = CLAMP (suma * opacity, 0x00, 0xFF); + } + } + + return dest; +} + +void +screenshot_add_shadow (GdkPixbuf **src) +{ + GdkPixbuf *dest; + static ConvFilter *filter = NULL; + + if (!filter) + filter = create_blur_filter (BLUR_RADIUS); + + dest = create_effect (*src, filter, + BLUR_RADIUS, + SHADOW_OFFSET, SHADOW_OPACITY); + + if (dest == NULL) + return; + + gdk_pixbuf_composite (*src, dest, + BLUR_RADIUS, BLUR_RADIUS, + gdk_pixbuf_get_width (*src), + gdk_pixbuf_get_height (*src), + BLUR_RADIUS, BLUR_RADIUS, 1.0, 1.0, + GDK_INTERP_BILINEAR, 255); + g_object_unref (*src); + *src = dest; +} + +void +screenshot_add_border (GdkPixbuf **src) +{ + GdkPixbuf *dest; + static ConvFilter *filter = NULL; + + if (!filter) + filter = create_outline_filter (OUTLINE_RADIUS); + + dest = create_effect (*src, filter, + OUTLINE_RADIUS, + OUTLINE_OFFSET, OUTLINE_OPACITY); + + if (dest == NULL) + return; + + gdk_pixbuf_composite (*src, dest, + OUTLINE_RADIUS, OUTLINE_RADIUS, + gdk_pixbuf_get_width (*src), + gdk_pixbuf_get_height (*src), + OUTLINE_RADIUS, OUTLINE_RADIUS, 1.0, 1.0, + GDK_INTERP_BILINEAR, 255); + g_object_unref (*src); + *src = dest; +} diff --git a/mate-screenshot/src/screenshot-shadow.h b/mate-screenshot/src/screenshot-shadow.h new file mode 100644 index 00000000..c4162a5f --- /dev/null +++ b/mate-screenshot/src/screenshot-shadow.h @@ -0,0 +1,28 @@ +/* screenshot-shadow.h - part of MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +#ifndef __SCREENSHOT_SHADOW_H__ +#define __SCREENSHOT_SHADOW_H__ + +#include + +void screenshot_add_shadow (GdkPixbuf **src); +void screenshot_add_border (GdkPixbuf **src); + +#endif /* __SCREENSHOT_SHADOW_H__ */ diff --git a/mate-screenshot/src/screenshot-utils.c b/mate-screenshot/src/screenshot-utils.c new file mode 100644 index 00000000..eaeeca28 --- /dev/null +++ b/mate-screenshot/src/screenshot-utils.c @@ -0,0 +1,1040 @@ +/* screenshot-utils.c - common functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * Copyright (C) 2008 Cosimo Cecchi + * + * 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, + */ + +#include "config.h" +#include "screenshot-utils.h" + +#include +#include +#include +#include + +#ifdef HAVE_X11_EXTENSIONS_SHAPE_H +#include +#endif + +#if GTK_CHECK_VERSION (3, 0, 0) +#define GdkRegion cairo_region_t +#define gdk_region_new cairo_region_create +#define gdk_region_destroy cairo_region_destroy +#define gdk_region_rectangle cairo_region_create_rectangle +#define gdk_region_offset cairo_region_translate +#define gdk_region_intersect cairo_region_intersect +#define gdk_region_subtract cairo_region_subtract +#define gdk_region_union_with_rect cairo_region_union_rectangle +#define gdk_cursor_unref g_object_unref +#endif + +static GtkWidget *selection_window; + +#define SELECTION_NAME "_MATE_PANEL_SCREENSHOT" + +static char * +get_utf8_property (GdkWindow *window, + GdkAtom atom) +{ + gboolean res; + GdkAtom utf8_string; + GdkAtom type; + int actual_format, actual_length; + guchar *data; + char *retval; + + utf8_string = gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("UTF8_STRING")); + res = gdk_property_get (window, atom, utf8_string, + 0, G_MAXLONG, FALSE, + &type, + &actual_format, &actual_length, + &data); + if (!res) + return NULL; + + if (type != utf8_string || actual_format != 8 || actual_length == 0) + { + g_free (data); + return NULL; + } + + if (!g_utf8_validate ((gchar *) data, actual_length, NULL)) + { + char *atom_name = gdk_atom_name (atom); + + g_warning ("Property `%s' (format: %d, length: %d) contained " + "invalid UTF-8", + atom_name, + actual_format, + actual_length); + + g_free (atom_name); + g_free (data); + + return NULL; + } + + retval = g_strndup ((gchar *) data, actual_length); + + g_free (data); + + return retval; +} + +/* To make sure there is only one screenshot taken at a time, + * (Imagine key repeat for the print screen key) we hold a selection + * until we are done taking the screenshot + */ +gboolean +screenshot_grab_lock (void) +{ + GdkAtom selection_atom; + gboolean result = FALSE; + + selection_atom = gdk_atom_intern (SELECTION_NAME, FALSE); + gdk_x11_grab_server (); + + if (gdk_selection_owner_get (selection_atom) != NULL) + goto out; + + selection_window = gtk_invisible_new (); + gtk_widget_show (selection_window); + + if (!gtk_selection_owner_set (selection_window, + gdk_atom_intern (SELECTION_NAME, FALSE), + GDK_CURRENT_TIME)) + { + gtk_widget_destroy (selection_window); + selection_window = NULL; + goto out; + } + + result = TRUE; + + out: + gdk_x11_ungrab_server (); + gdk_flush (); + + return result; +} + +void +screenshot_release_lock (void) +{ + if (selection_window) + { + gtk_widget_destroy (selection_window); + selection_window = NULL; + } + + gdk_flush (); +} + +static GdkWindow * +screenshot_find_active_window (void) +{ + GdkWindow *window; + GdkScreen *default_screen; + + default_screen = gdk_screen_get_default (); + window = gdk_screen_get_active_window (default_screen); + + return window; +} + +static gboolean +screenshot_window_is_desktop (GdkWindow *window) +{ + GdkWindow *root_window = gdk_get_default_root_window (); + GdkWindowTypeHint window_type_hint; + + if (window == root_window) + return TRUE; + + window_type_hint = gdk_window_get_type_hint (window); + if (window_type_hint == GDK_WINDOW_TYPE_HINT_DESKTOP) + return TRUE; + + return FALSE; + +} + +#define MAXIMUM_WM_REPARENTING_DEPTH 4 + +static GdkWindow * +look_for_hint_helper (GdkWindow *window, + GdkAtom property, + int depth) +{ + gboolean res; + GdkAtom actual_type; + int actual_format, actual_length; + guchar *data; + + res = gdk_property_get (window, property, GDK_NONE, + 0, 1, FALSE, + &actual_type, + &actual_format, &actual_length, + &data); + + if (res == TRUE && + data != NULL && + actual_format == 32 && + data[0] == 1) + { + g_free (data); + + return window; + } + + if (depth < MAXIMUM_WM_REPARENTING_DEPTH) + { + GList *children, *l; + + children = gdk_window_get_children (window); + if (children != NULL) + { + for (l = children; l; l = l->next) + { + window = look_for_hint_helper (l->data, property, depth + 1); + if (window) + break; + } + + g_list_free (children); + + if (window) + return window; + } + } + + return NULL; +} + +static GdkWindow * +look_for_hint (GdkWindow *window, + GdkAtom property) +{ + GdkWindow *retval; + + retval = look_for_hint_helper (window, property, 0); + + return retval; +} + +GdkWindow * +screenshot_find_current_window () +{ + GdkWindow *current_window; + + current_window = screenshot_find_active_window (); + + /* If there's no active window, we fall back to returning the + * window that the cursor is in. + */ + if (!current_window) + current_window = gdk_window_at_pointer (NULL, NULL); + + if (current_window) + { + if (screenshot_window_is_desktop (current_window)) + /* if the current window is the desktop (e.g. caja), we + * return NULL, as getting the whole screen makes more sense. + */ + return NULL; + + /* Once we have a window, we take the toplevel ancestor. */ + current_window = gdk_window_get_toplevel (current_window); + } + + return current_window; +} + +typedef struct { + GdkRectangle rect; + gboolean button_pressed; + GtkWidget *window; +} select_area_filter_data; + +static gboolean +select_area_button_press (GtkWidget *window, + GdkEventButton *event, + select_area_filter_data *data) +{ + if (data->button_pressed) + return TRUE; + + data->button_pressed = TRUE; + data->rect.x = event->x_root; + data->rect.y = event->y_root; + + return TRUE; +} + +static gboolean +select_area_motion_notify (GtkWidget *window, + GdkEventMotion *event, + select_area_filter_data *data) +{ + GdkRectangle draw_rect; + + if (!data->button_pressed) + return TRUE; + + draw_rect.width = ABS (data->rect.x - event->x_root); + draw_rect.height = ABS (data->rect.y - event->y_root); + draw_rect.x = MIN (data->rect.x, event->x_root); + draw_rect.y = MIN (data->rect.y, event->y_root); + + if (draw_rect.width <= 0 || draw_rect.height <= 0) + { + gtk_window_move (GTK_WINDOW (window), -100, -100); + gtk_window_resize (GTK_WINDOW (window), 10, 10); + return TRUE; + } + + gtk_window_move (GTK_WINDOW (window), draw_rect.x, draw_rect.y); + gtk_window_resize (GTK_WINDOW (window), draw_rect.width, draw_rect.height); + + /* We (ab)use app-paintable to indicate if we have an RGBA window */ + if (!gtk_widget_get_app_paintable (window)) + { + GdkWindow *gdkwindow = gtk_widget_get_window (window); + + /* Shape the window to make only the outline visible */ + if (draw_rect.width > 2 && draw_rect.height > 2) + { +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_t *region, *region2; + cairo_rectangle_int_t region_rect = { +#else + GdkRegion *region, *region2; + GdkRectangle region_rect = { +#endif + 0, 0, + draw_rect.width - 2, draw_rect.height - 2 + }; + +#if GTK_CHECK_VERSION (3, 0, 0) + region = cairo_region_create_rectangle (®ion_rect); +#else + region = gdk_region_rectangle (®ion_rect); +#endif + region_rect.x++; + region_rect.y++; + region_rect.width -= 2; + region_rect.height -= 2; +#if GTK_CHECK_VERSION (3, 0, 0) + region2 = cairo_region_create_rectangle (®ion_rect); + cairo_region_subtract (region, region2); +#else + region2 = gdk_region_rectangle (®ion_rect); + gdk_region_subtract (region, region2); +#endif + + gdk_window_shape_combine_region (gdkwindow, region, 0, 0); + + gdk_region_destroy (region); + gdk_region_destroy (region2); + } + else + gdk_window_shape_combine_region (gdkwindow, NULL, 0, 0); + } + + return TRUE; +} + +static gboolean +select_area_button_release (GtkWidget *window, + GdkEventButton *event, + select_area_filter_data *data) +{ + if (!data->button_pressed) + return TRUE; + + data->rect.width = ABS (data->rect.x - event->x_root); + data->rect.height = ABS (data->rect.y - event->y_root); + data->rect.x = MIN (data->rect.x, event->x_root); + data->rect.y = MIN (data->rect.y, event->y_root); + + gtk_main_quit (); + + return TRUE; +} + +static gboolean +select_area_key_press (GtkWidget *window, + GdkEventKey *event, + select_area_filter_data *data) +{ + if (event->keyval == GDK_KEY_Escape) + { + data->rect.x = 0; + data->rect.y = 0; + data->rect.width = 0; + data->rect.height = 0; + gtk_main_quit (); + } + + return TRUE; +} + + +static gboolean +#if GTK_CHECK_VERSION (3, 0, 0) +draw (GtkWidget *window, cairo_t *cr, gpointer unused) +#else +expose (GtkWidget *window, GdkEventExpose *event, gpointer unused) +#endif +{ + GtkAllocation allocation; + GtkStyle *style; +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_t *cr; + + cr = gdk_cairo_create (event->window); + gdk_cairo_region (cr, event->region); + cairo_clip (cr); +#endif + + style = gtk_widget_get_style (window); + + if (gtk_widget_get_app_paintable (window)) + { + cairo_set_line_width (cr, 1.0); + + gtk_widget_get_allocation (window, &allocation); + + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 0, 0, 0, 0); + cairo_paint (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]); + cairo_paint_with_alpha (cr, 0.25); + + cairo_rectangle (cr, + allocation.x + 0.5, allocation.y + 0.5, + allocation.width - 1, allocation.height - 1); + cairo_stroke (cr); + } + else + { + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]); + cairo_paint (cr); + } + +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_destroy (cr); +#endif + + return TRUE; +} + +static GtkWidget * +create_select_window (void) +{ + GtkWidget *window; + GdkScreen *screen; +#if GTK_CHECK_VERSION (3, 0, 0) + GdkVisual *visual; +#endif + + screen = gdk_screen_get_default (); +#if GTK_CHECK_VERSION (3, 0, 0) + visual = gdk_screen_get_rgba_visual (screen); +#endif + + window = gtk_window_new (GTK_WINDOW_POPUP); + if (gdk_screen_is_composited (screen) && +#if GTK_CHECK_VERSION (3, 0, 0) + visual) +#else + gdk_screen_get_rgba_colormap (screen)) +#endif + { +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_visual (window, visual); +#else + gtk_widget_set_colormap (window, gdk_screen_get_rgba_colormap (screen)); +#endif + gtk_widget_set_app_paintable (window, TRUE); + } +#if GTK_CHECK_VERSION (3, 0, 0) + g_signal_connect (window, "draw", G_CALLBACK (draw), NULL); +#else + g_signal_connect (window, "expose-event", G_CALLBACK (expose), NULL); +#endif + + gtk_window_move (GTK_WINDOW (window), -100, -100); + gtk_window_resize (GTK_WINDOW (window), 10, 10); + gtk_widget_show (window); + return window; +} + +typedef struct { + GdkRectangle rectangle; + SelectAreaCallback callback; +} CallbackData; + +static gboolean +emit_select_callback_in_idle (gpointer user_data) +{ + CallbackData *data = user_data; + + data->callback (&data->rectangle); + + g_slice_free (CallbackData, data); + + return FALSE; +} + +void +screenshot_select_area_async (SelectAreaCallback callback) +{ + GdkCursor *cursor; + select_area_filter_data data; + GdkRectangle *rectangle; + CallbackData *cb_data; + + data.rect.x = 0; + data.rect.y = 0; + data.rect.width = 0; + data.rect.height = 0; + data.button_pressed = FALSE; + data.window = create_select_window(); + + cb_data = g_slice_new0 (CallbackData); + cb_data->callback = callback; + + g_signal_connect (data.window, "key-press-event", G_CALLBACK (select_area_key_press), &data); + g_signal_connect (data.window, "button-press-event", G_CALLBACK (select_area_button_press), &data); + g_signal_connect (data.window, "button-release-event", G_CALLBACK (select_area_button_release), &data); + g_signal_connect (data.window, "motion-notify-event", G_CALLBACK (select_area_motion_notify), &data); + + cursor = gdk_cursor_new (GDK_CROSSHAIR); + + if (gdk_pointer_grab (gtk_widget_get_window (data.window), FALSE, + GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, + NULL, cursor, + GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) + { + gdk_cursor_unref (cursor); + goto out; + } + + if (gdk_keyboard_grab (gtk_widget_get_window (data.window), FALSE, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) + { + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_cursor_unref (cursor); + goto out; + } + + gtk_main (); + + gdk_keyboard_ungrab (GDK_CURRENT_TIME); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + + gtk_widget_destroy (data.window); + gdk_cursor_unref (cursor); + + gdk_flush (); + + out: + cb_data->rectangle = data.rect; + + /* FIXME: we should actually be emitting the callback When + * the compositor has finished re-drawing, but there seems to be no easy + * way to know that. + */ + g_timeout_add (200, emit_select_callback_in_idle, cb_data); +} + +static Window +find_wm_window (Window xid) +{ + Window root, parent, *children; + unsigned int nchildren; + + do + { + if (XQueryTree (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xid, &root, + &parent, &children, &nchildren) == 0) + { + g_warning ("Couldn't find window manager window"); + return None; + } + + if (root == parent) + return xid; + + xid = parent; + } + while (TRUE); +} + +#if GTK_CHECK_VERSION (3, 0, 0) +static cairo_region_t * +#else +static GdkRegion * +#endif +make_region_with_monitors (GdkScreen *screen) +{ +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_t *region; +#else + GdkRegion *region; +#endif + int num_monitors; + int i; + + num_monitors = gdk_screen_get_n_monitors (screen); + +#if GTK_CHECK_VERSION (3, 0, 0) + region = cairo_region_create (); +#else + region = gdk_region_new (); +#endif + + for (i = 0; i < num_monitors; i++) + { + GdkRectangle rect; + + gdk_screen_get_monitor_geometry (screen, i, &rect); +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_union_rectangle (region, &rect); +#else + gdk_region_union_with_rect (region, &rect); +#endif + } + + return region; +} + +static void +blank_rectangle_in_pixbuf (GdkPixbuf *pixbuf, GdkRectangle *rect) +{ + int x, y; + int x2, y2; + guchar *pixels; + int rowstride; + int n_channels; + guchar *row; + gboolean has_alpha; + + g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB); + + x2 = rect->x + rect->width; + y2 = rect->y + rect->height; + + pixels = gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + n_channels = gdk_pixbuf_get_n_channels (pixbuf); + + for (y = rect->y; y < y2; y++) + { + guchar *p; + + row = pixels + y * rowstride; + p = row + rect->x * n_channels; + + for (x = rect->x; x < x2; x++) + { + *p++ = 0; + *p++ = 0; + *p++ = 0; + + if (has_alpha) + *p++ = 255; /* opaque black */ + } + } +} + +static void +#if GTK_CHECK_VERSION (3, 0, 0) +blank_region_in_pixbuf (GdkPixbuf *pixbuf, cairo_region_t *region) +{ + int n_rects; + int i; + int width, height; + cairo_rectangle_int_t pixbuf_rect; + + n_rects = cairo_region_num_rectangles (region); +#else +blank_region_in_pixbuf (GdkPixbuf *pixbuf, GdkRegion *region) +{ + GdkRectangle *rects; + int n_rects; + int i; + int width, height; + GdkRectangle pixbuf_rect; + + gdk_region_get_rectangles (region, &rects, &n_rects); +#endif + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + pixbuf_rect.x = 0; + pixbuf_rect.y = 0; + pixbuf_rect.width = width; + pixbuf_rect.height = height; + + for (i = 0; i < n_rects; i++) + { +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_rectangle_int_t rect, dest; + + cairo_region_get_rectangle (region, i, &rect); + if (gdk_rectangle_intersect (&rect, &pixbuf_rect, &dest)) +#else + GdkRectangle dest; + + if (gdk_rectangle_intersect (rects + i, &pixbuf_rect, &dest)) +#endif + blank_rectangle_in_pixbuf (pixbuf, &dest); + + } +#if !GTK_CHECK_VERSION (3, 0, 0) + g_free (rects); +#endif +} + +/* When there are multiple monitors with different resolutions, the visible area + * within the root window may not be rectangular (it may have an L-shape, for + * example). In that case, mask out the areas of the root window which would + * not be visible in the monitors, so that screenshot do not end up with content + * that the user won't ever see. + */ +static void +mask_monitors (GdkPixbuf *pixbuf, GdkWindow *root_window) +{ + GdkScreen *screen; +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_t *region_with_monitors; + cairo_region_t *invisible_region; + cairo_rectangle_int_t rect; +#else + GdkRegion *region_with_monitors; + GdkRegion *invisible_region; + GdkRectangle rect; +#endif + + screen = gdk_window_get_screen (root_window); + + region_with_monitors = make_region_with_monitors (screen); + + rect.x = 0; + rect.y = 0; + rect.width = gdk_screen_get_width (screen); + rect.height = gdk_screen_get_height (screen); + +#if GTK_CHECK_VERSION (3, 0, 0) + invisible_region = cairo_region_create_rectangle (&rect); + cairo_region_subtract (invisible_region, region_with_monitors); + + blank_region_in_pixbuf (pixbuf, invisible_region); + + cairo_region_destroy (region_with_monitors); + cairo_region_destroy (invisible_region); +#else + invisible_region = gdk_region_rectangle (&rect); + gdk_region_subtract (invisible_region, region_with_monitors); + + blank_region_in_pixbuf (pixbuf, invisible_region); + + gdk_region_destroy (region_with_monitors); + gdk_region_destroy (invisible_region); +#endif +} + +GdkPixbuf * +screenshot_get_pixbuf (GdkWindow *window, + GdkRectangle *rectangle, + gboolean include_pointer, + gboolean include_border, + gboolean include_mask) +{ + GdkWindow *root; + GdkPixbuf *screenshot; + gint x_real_orig, y_real_orig, x_orig, y_orig; + gint width, real_width, height, real_height; + + /* If the screenshot should include the border, we look for the WM window. */ + + if (include_border) + { + Window xid, wm; + + xid = GDK_WINDOW_XID (window); + wm = find_wm_window (xid); + + if (wm != None) + window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), wm); + + /* fallback to no border if we can't find the WM window. */ + } + + root = gdk_get_default_root_window (); + + real_width = gdk_window_get_width(window); + real_height = gdk_window_get_height(window); + + gdk_window_get_origin (window, &x_real_orig, &y_real_orig); + + x_orig = x_real_orig; + y_orig = y_real_orig; + width = real_width; + height = real_height; + + if (x_orig < 0) + { + width = width + x_orig; + x_orig = 0; + } + + if (y_orig < 0) + { + height = height + y_orig; + y_orig = 0; + } + + if (x_orig + width > gdk_screen_width ()) + width = gdk_screen_width () - x_orig; + + if (y_orig + height > gdk_screen_height ()) + height = gdk_screen_height () - y_orig; + + if (rectangle) + { + x_orig = rectangle->x - x_orig; + y_orig = rectangle->y - y_orig; + width = rectangle->width; + height = rectangle->height; + } + +#if GTK_CHECK_VERSION (3, 0, 0) + screenshot = gdk_pixbuf_get_from_window (root, + x_orig, y_orig, + width, height); +#else + screenshot = gdk_pixbuf_get_from_drawable (NULL, root, NULL, + x_orig, y_orig, 0, 0, + width, height); +#endif + + /* + * Masking currently only works properly with full-screen shots + */ + if (include_mask) + mask_monitors (screenshot, root); + +#ifdef HAVE_X11_EXTENSIONS_SHAPE_H + if (include_border) + { + XRectangle *rectangles; + GdkPixbuf *tmp; + int rectangle_count, rectangle_order, i; + + /* we must use XShape to avoid showing what's under the rounder corners + * of the WM decoration. + */ + + rectangles = XShapeGetRectangles (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + GDK_WINDOW_XID (window), + ShapeBounding, + &rectangle_count, + &rectangle_order); + if (rectangles && rectangle_count > 0 && window != root) + { + gboolean has_alpha = gdk_pixbuf_get_has_alpha (screenshot); + + tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + gdk_pixbuf_fill (tmp, 0); + + for (i = 0; i < rectangle_count; i++) + { + gint rec_x, rec_y; + gint rec_width, rec_height; + gint y; + + rec_x = rectangles[i].x; + rec_y = rectangles[i].y; + rec_width = rectangles[i].width; + rec_height = rectangles[i].height; + + if (x_real_orig < 0) + { + rec_x += x_real_orig; + rec_x = MAX(rec_x, 0); + rec_width += x_real_orig; + } + + if (y_real_orig < 0) + { + rec_y += y_real_orig; + rec_y = MAX(rec_y, 0); + rec_height += y_real_orig; + } + + if (x_orig + rec_x + rec_width > gdk_screen_width ()) + rec_width = gdk_screen_width () - x_orig - rec_x; + + if (y_orig + rec_y + rec_height > gdk_screen_height ()) + rec_height = gdk_screen_height () - y_orig - rec_y; + + for (y = rec_y; y < rec_y + rec_height; y++) + { + guchar *src_pixels, *dest_pixels; + gint x; + + src_pixels = gdk_pixbuf_get_pixels (screenshot) + + y * gdk_pixbuf_get_rowstride(screenshot) + + rec_x * (has_alpha ? 4 : 3); + dest_pixels = gdk_pixbuf_get_pixels (tmp) + + y * gdk_pixbuf_get_rowstride (tmp) + + rec_x * 4; + + for (x = 0; x < rec_width; x++) + { + *dest_pixels++ = *src_pixels++; + *dest_pixels++ = *src_pixels++; + *dest_pixels++ = *src_pixels++; + + if (has_alpha) + *dest_pixels++ = *src_pixels++; + else + *dest_pixels++ = 255; + } + } + } + + g_object_unref (screenshot); + screenshot = tmp; + } + } +#endif /* HAVE_X11_EXTENSIONS_SHAPE_H */ + + /* if we have a selected area, there were by definition no cursor in the + * screenshot */ + if (include_pointer && !rectangle) + { + GdkCursor *cursor; + GdkPixbuf *cursor_pixbuf; + + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_LEFT_PTR); + cursor_pixbuf = gdk_cursor_get_image (cursor); + + if (cursor_pixbuf != NULL) + { + GdkRectangle r1, r2; + gint cx, cy, xhot, yhot; + + gdk_window_get_pointer (window, &cx, &cy, NULL); + sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"), "%d", &xhot); + sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"), "%d", &yhot); + + /* in r1 we have the window coordinates */ + r1.x = x_real_orig; + r1.y = y_real_orig; + r1.width = real_width; + r1.height = real_height; + + /* in r2 we have the cursor window coordinates */ + r2.x = cx + x_real_orig; + r2.y = cy + y_real_orig; + r2.width = gdk_pixbuf_get_width (cursor_pixbuf); + r2.height = gdk_pixbuf_get_height (cursor_pixbuf); + + /* see if the pointer is inside the window */ + if (gdk_rectangle_intersect (&r1, &r2, &r2)) + { + gdk_pixbuf_composite (cursor_pixbuf, screenshot, + cx - xhot, cy - yhot, + r2.width, r2.height, + cx - xhot, cy - yhot, + 1.0, 1.0, + GDK_INTERP_BILINEAR, + 255); + } + + g_object_unref (cursor_pixbuf); + gdk_cursor_unref (cursor); + } + } + + return screenshot; +} + +gchar * +screenshot_get_window_title (GdkWindow *win) +{ + gchar *name; + + win = gdk_window_get_toplevel (win); + win = look_for_hint (win, gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("WM_STATE"))); + + name = get_utf8_property (win, gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("_NET_WM_NAME"))); + if (name) + return name; + + /* TODO: maybe we should also look at WM_NAME and WM_CLASS? */ + + return g_strdup (_("Untitled Window")); +} + +void +screenshot_show_error_dialog (GtkWindow *parent, + const gchar *message, + const gchar *detail) +{ + GtkWidget *dialog; + + g_return_if_fail ((parent == NULL) || (GTK_IS_WINDOW (parent))); + g_return_if_fail (message != NULL); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_window_set_title (GTK_WINDOW (dialog), ""); + + if (detail) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", detail); + + if (parent && gtk_window_get_group (parent)) + gtk_window_group_add_window (gtk_window_get_group (parent), GTK_WINDOW (dialog)); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); +} + +void +screenshot_show_gerror_dialog (GtkWindow *parent, + const gchar *message, + GError *error) +{ + g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); + g_return_if_fail (message != NULL); + g_return_if_fail (error != NULL); + + screenshot_show_error_dialog (parent, message, error->message); +} diff --git a/mate-screenshot/src/screenshot-utils.h b/mate-screenshot/src/screenshot-utils.h new file mode 100644 index 00000000..b9d13f7b --- /dev/null +++ b/mate-screenshot/src/screenshot-utils.h @@ -0,0 +1,50 @@ +/* screenshot-utils.h - common functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * + * 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, + */ + +#ifndef __SCREENSHOT_UTILS_H__ +#define __SCREENSHOT_UTILS_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef void (* SelectAreaCallback) (GdkRectangle *rectangle); + +gboolean screenshot_grab_lock (void); +void screenshot_release_lock (void); +gchar *screenshot_get_window_title (GdkWindow *win); +GdkWindow *screenshot_find_current_window (void); +void screenshot_select_area_async (SelectAreaCallback callback); +GdkPixbuf *screenshot_get_pixbuf (GdkWindow *win, + GdkRectangle *rectangle, + gboolean include_pointer, + gboolean include_border, + gboolean include_mask); + +void screenshot_show_error_dialog (GtkWindow *parent, + const gchar *message, + const gchar *detail); +void screenshot_show_gerror_dialog (GtkWindow *parent, + const gchar *message, + GError *error); + +G_END_DECLS + +#endif /* __SCREENSHOT_UTILS_H__ */ diff --git a/mate-screenshot/src/screenshot-xfer.c b/mate-screenshot/src/screenshot-xfer.c new file mode 100644 index 00000000..a4cfeac1 --- /dev/null +++ b/mate-screenshot/src/screenshot-xfer.c @@ -0,0 +1,393 @@ +/* screenshot-xfer.c - file transfer functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * Copyright (C) 2008 Cosimo Cecchi + * + * 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, + */ + +#include "config.h" + +#include "screenshot-xfer.h" + +#include +#include + +typedef struct +{ + GtkWidget *dialog; + GtkWidget *progress_bar; + GCancellable *cancellable; +} TransferDialog; + +typedef struct +{ + GFile *source; + GFile *dest; + GFileCopyFlags flags; + TransferCallback callback; + gpointer callback_data; + GCancellable *cancellable; + GtkWidget *parent; + TransferDialog *dialog; + TransferResult result; + GIOSchedulerJob *io_job; + char *error; + gint dialog_timeout_id; + goffset total_bytes; + goffset current_bytes; +} TransferJob; + +typedef struct +{ + int resp; + GtkWidget *parent; + char *basename; +} ErrorDialogData; + +static gboolean +do_run_overwrite_confirm_dialog (gpointer _data) +{ + ErrorDialogData *data = _data; + GtkWidget *dialog; + gint response; + + /* we need to ask the user if they want to overwrite this file */ + dialog = gtk_message_dialog_new (GTK_WINDOW (data->parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("File already exists")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The file \"%s\" already exists. " + "Would you like to replace it?"), + data->basename); + gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Replace"), + GTK_RESPONSE_OK); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + data->resp = response; + + return FALSE; +} + +static void +transfer_dialog_response_cb (GtkDialog *d, + gint response, + GCancellable *cancellable) +{ + if (response == GTK_RESPONSE_CANCEL) + { + if (!g_cancellable_is_cancelled (cancellable)) + { + g_cancellable_cancel (cancellable); + } + } +} + +static gboolean +transfer_progress_dialog_new (TransferJob *job) +{ + TransferDialog *dialog; + GtkWidget *gdialog; + GtkWidget *widget; + + dialog = g_new0 (TransferDialog, 1); + + gdialog = gtk_message_dialog_new (GTK_WINDOW (job->parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_OTHER, + GTK_BUTTONS_CANCEL, + _("Saving file...")); + widget = gtk_progress_bar_new (); + gtk_box_pack_start (GTK_BOX (gtk_message_dialog_get_message_area GTK_MESSAGE_DIALOG (gdialog)), + widget, FALSE, 0, 0); + gtk_widget_show (widget); + dialog->progress_bar = widget; + dialog->dialog = gdialog; + + g_signal_connect (gdialog, + "response", + G_CALLBACK (transfer_dialog_response_cb), + job->cancellable); + + job->dialog = dialog; + gtk_widget_show (gdialog); + + return FALSE; +} + +static void +transfer_progress_dialog_start (TransferJob *job) +{ + /* sends to the mainloop and schedules show */ + if (job->dialog_timeout_id == 0) + job->dialog_timeout_id = g_timeout_add_seconds (1, + (GSourceFunc) transfer_progress_dialog_new, + job); +} + +static int +run_overwrite_confirm_dialog (TransferJob *job) +{ + ErrorDialogData *data; + gboolean need_timeout; + int response; + char *basename; + + basename = g_file_get_basename (job->dest); + + data = g_slice_new0 (ErrorDialogData); + data->parent = job->parent; + data->basename = basename; + + need_timeout = (job->dialog_timeout_id > 0); + + if (need_timeout) + { + g_source_remove (job->dialog_timeout_id); + job->dialog_timeout_id = 0; + } + + g_io_scheduler_job_send_to_mainloop (job->io_job, + do_run_overwrite_confirm_dialog, + data, + NULL); + response = data->resp; + + if (need_timeout) + transfer_progress_dialog_start (job); + + g_free (basename); + g_slice_free (ErrorDialogData, data); + + return response; +} + +static gboolean +transfer_progress_dialog_update (TransferJob *job) +{ + TransferDialog *dialog = job->dialog; + double fraction; + + fraction = ((double) job->current_bytes) / ((double) job->total_bytes); + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress_bar), + fraction); + + return FALSE; +} + +static gboolean +transfer_job_done (gpointer user_data) +{ + TransferJob *job = user_data; + TransferDialog *dialog; + + dialog = job->dialog; + + if (job->dialog_timeout_id) + { + g_source_remove (job->dialog_timeout_id); + job->dialog_timeout_id = 0; + } + if (dialog) + gtk_widget_destroy (dialog->dialog); + + if (job->callback) + { + (job->callback) (job->result, + job->error, + job->callback_data); + } + + g_object_unref (job->source); + g_object_unref (job->dest); + g_object_unref (job->cancellable); + + g_free (dialog); + g_free (job->error); + g_slice_free (TransferJob, job); + + return FALSE; +} + +static void +transfer_progress_cb (goffset current_num_bytes, + goffset total_num_bytes, + TransferJob *job) +{ + job->current_bytes = current_num_bytes; + + if (!job->dialog) + return; + + g_io_scheduler_job_send_to_mainloop_async (job->io_job, + (GSourceFunc) transfer_progress_dialog_update, + job, + NULL); +} + +static goffset +get_file_size (GFile *file) +{ + GFileInfo *file_info; + goffset size; + + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + 0, NULL, NULL); + if (file_info != NULL) + { + size = g_file_info_get_size (file_info); + g_object_unref (file_info); + } + else + { + /* this should never fail as the source file is always local and in /tmp, + * but you never know. + */ + size = -1; + } + + return size; +} + +static gboolean +transfer_file (GIOSchedulerJob *io_job, + GCancellable *cancellable, + gpointer user_data) +{ + TransferJob *job = user_data; + GError *error; + + job->io_job = io_job; + job->total_bytes = get_file_size (job->source); + if (job->total_bytes == -1) + { + /* we can't access the source file, abort early */ + error = NULL; + job->result = TRANSFER_ERROR; + job->error = g_strdup (_("Can't access source file")); + + goto out; + } + + transfer_progress_dialog_start (job); + +retry: + error = NULL; + if (!g_file_copy (job->source, + job->dest, + job->flags, + job->cancellable, + (GFileProgressCallback) transfer_progress_cb, + job, + &error)) + { + if (error->code == G_IO_ERROR_EXISTS) + { + int response; + /* ask the user if he wants to overwrite, otherwise + * return and report what happened. + */ + response = run_overwrite_confirm_dialog (job); + if (response == GTK_RESPONSE_OK) + { + job->flags |= G_FILE_COPY_OVERWRITE; + g_error_free (error); + + goto retry; + } + else + { + g_cancellable_cancel (job->cancellable); + job->result = TRANSFER_OVERWRITE; + goto out; + } + } + else if (error->code == G_IO_ERROR_CANCELLED) + { + job->result = TRANSFER_CANCELLED; + + goto out; + } + else + { + /* other vfs error, abort the transfer and report + * the error. + */ + g_cancellable_cancel (job->cancellable); + job->result = TRANSFER_ERROR; + job->error = g_strdup (error->message); + + goto out; + } + } + else + { + /* success */ + job->result = TRANSFER_OK; + + goto out; + } + +out: + if (error) + g_error_free (error); + + g_io_scheduler_job_send_to_mainloop_async (io_job, + transfer_job_done, + job, + NULL); + return FALSE; +} + +void +screenshot_xfer_uri (GFile *source_file, + GFile *target_file, + GtkWidget *parent, + TransferCallback done_callback, + gpointer done_callback_data) +{ + TransferJob *job; + GCancellable *cancellable; + + cancellable = g_cancellable_new (); + + job = g_slice_new0 (TransferJob); + job->parent = parent; + job->source = g_object_ref (source_file); + job->dest = g_object_ref (target_file); + job->flags = 0; + job->error = NULL; + job->dialog = NULL; + job->callback = done_callback; + job->callback_data = done_callback_data; + job->cancellable = cancellable; + + g_io_scheduler_push_job (transfer_file, + job, + NULL, 0, + job->cancellable); +} + diff --git a/mate-screenshot/src/screenshot-xfer.h b/mate-screenshot/src/screenshot-xfer.h new file mode 100644 index 00000000..52262f11 --- /dev/null +++ b/mate-screenshot/src/screenshot-xfer.h @@ -0,0 +1,45 @@ +/* screenshot-xfer.h - file transfer functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford + * Copyright (C) 2008 Cosimo Cecchi + * + * 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, + */ + +#ifndef __SCREENSHOT_XFER_H__ +#define __SCREENSHOT_XFER_H__ + +#include +#include + +typedef enum +{ + TRANSFER_OK, + TRANSFER_OVERWRITE, + TRANSFER_CANCELLED, + TRANSFER_ERROR +} TransferResult; + +typedef void (* TransferCallback) (TransferResult result, + char *error_message, + gpointer data); + +void screenshot_xfer_uri (GFile *source_file, + GFile *target_file, + GtkWidget *parent, + TransferCallback done_callback, + gpointer done_callback_data); + +#endif /* __SCREENSHOT_XFER_H__ */ -- cgit v1.2.1