summaryrefslogtreecommitdiff
path: root/mate-screenshot
diff options
context:
space:
mode:
authorStefano Karapetsas <[email protected]>2011-12-14 10:13:54 +0100
committerStefano Karapetsas <[email protected]>2011-12-14 10:13:54 +0100
commitef0467789bfc8406b57ba553e4d59f4d6c3f9be8 (patch)
tree09d541636a16cb38448fe6183289ebdc3080c1bf /mate-screenshot
downloadmate-utils-ef0467789bfc8406b57ba553e4d59f4d6c3f9be8.tar.bz2
mate-utils-ef0467789bfc8406b57ba553e4d59f4d6c3f9be8.tar.xz
Moved from Mate-Extra repository
Diffstat (limited to 'mate-screenshot')
-rw-r--r--mate-screenshot/ChangeLog672
-rw-r--r--mate-screenshot/Makefile.am90
-rw-r--r--mate-screenshot/mate-screenshot.159
-rw-r--r--mate-screenshot/mate-screenshot.c1390
-rw-r--r--mate-screenshot/mate-screenshot.desktop.in14
-rw-r--r--mate-screenshot/mate-screenshot.schemas.in81
-rw-r--r--mate-screenshot/mate-screenshot.ui254
-rw-r--r--mate-screenshot/screenshot-dialog.c402
-rw-r--r--mate-screenshot/screenshot-dialog.h42
-rw-r--r--mate-screenshot/screenshot-save.c285
-rw-r--r--mate-screenshot/screenshot-save.h34
-rw-r--r--mate-screenshot/screenshot-shadow.c231
-rw-r--r--mate-screenshot/screenshot-shadow.h28
-rw-r--r--mate-screenshot/screenshot-utils.c936
-rw-r--r--mate-screenshot/screenshot-utils.h50
-rw-r--r--mate-screenshot/screenshot-xfer.c393
-rw-r--r--mate-screenshot/screenshot-xfer.h45
17 files changed, 5006 insertions, 0 deletions
diff --git a/mate-screenshot/ChangeLog b/mate-screenshot/ChangeLog
new file mode 100644
index 00000000..ab881461
--- /dev/null
+++ b/mate-screenshot/ChangeLog
@@ -0,0 +1,672 @@
+2009-04-04 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c (target_toggled_cb), (create_effects_frame),
+ (create_screenshot_frame): disable the effects section altogether
+ when taking a screenshot of an user selected area. Use better
+ wording for the user selection feature (#577861).
+
+2009-03-04 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c (target_toggled_cb),
+ (create_screenshot_frame), (main): disable the delay option
+ when taking a screenshot of an user selected area (#573939).
+
+2009-02-27 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c (target_toggled_cb),
+ (create_screenshot_frame), (finish_prepare_screenshot),
+ (async_existence_job_free), (check_file_done), (find_rectangle),
+ (prepare_screenshot), (main):
+ * screenshot-utils.c (select_area_button_press),
+ (select_area_button_release), (select_area_motion_notify),
+ (select_area_filter), (screenshot_select_area),
+ (screenshot_get_pixbuf):
+ * screenshot-utils.h: add support for taking a screenshot of an
+ user-defined selection.
+ Patch by Vincent Untz, bug #155061.
+
+2009-02-24 Federico Mena Quintero <[email protected]>
+
+ https://bugzilla.novell.com/show_bug.cgi?id=381135
+ http://bugzilla.mate.org/show_bug.cgi?id=166485
+
+ * screenshot-utils.c (screenshot_get_pixbuf): If we are in a
+ multi-monitor setup that is not rectangular (for example, if it is
+ L-shaped due to monitors of different resolutions), we need to
+ blank out the "invisible" areas from the rectangular root window.
+ Otherwise, the screenshot will contain content that the user
+ cannot actually see. Call a helper function to blank out these areas.
+ (mask_monitors): New helper function.
+
+ * screenshot-utils.c (screenshot_get_pixbuf): Don't try to shape
+ the root window. Patch by Hal Ashburner <[email protected]>
+
+2009-02-24 Cosimo Cecchi <[email protected]>
+
+ * Makefile.am: add gthread CFLAGS and LIBS.
+ * mate-screenshot.c (main): initialize threads at startup,
+ so that we can be sure to use them and not the idle fallback when
+ doing async transfers.
+
+2009-01-10 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c (set_recent_entry), (save_callback):
+ Add newly saved files to GtkRecentManager (#567205).
+
+2008-11-24 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: (save_options): Save the include-pointer
+ setting to MateConf.
+
+2008-11-18 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (try_check_file):
+ Plug some other memory leaks.
+
+2008-11-18 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (save_folder_to_mateconf):
+ Plug a memory leak.
+
+2008-11-10 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (create_interactive_dialog): set a meaningful
+ title for the interactive dialog (#434581).
+
+2008-11-10 Cosimo Cecchi <[email protected]>
+
+ * screenshot-utils.c: (screenshot_get_pixbuf): fix the mouse
+ cursor overlay position when not in fullscreen mode (#559594).
+
+2008-11-09 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (main): always use the delay command line
+ argument if present (follow-up to #554114).
+
+2008-11-09 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.ui: move the "Copy to Clipboard" button on
+ the left of the "Cancel" one, as for HIG (#554805).
+
+2008-11-09 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (check_file_done), (try_check_file),
+ (prepare_screenshot), (get_desktop_dir):
+ Always use URIs to build the GFile when checking for existance.
+ Don't show an error message when the file-chooser can't find the
+ parent directory of the last saved position. (#321944).
+
+2008-11-08 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (main):
+ Only delay the screenshot when in interactive mode (#554114).
+
+2008-11-08 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (save_callback):
+ Fixed a typo in a string (#551440).
+
+2008-11-08 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (try_check_file):
+ Don't free NULL errors, as it makes a warning.
+
+2008-11-08 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (finish_prepare_screenshot):
+ Simplify the code a bit. Thanks to Paolo Borelli.
+
+2008-11-08 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (finish_prepare_screenshot):
+ Don't try to include the window border when doing fullscreen
+ captures (#559532).
+
+2008-11-04 Cosimo Cecchi <[email protected]>
+
+ * Makefile.am: update to the new configure pkg-config checks
+ (part of #557262).
+
+2008-10-21 Cosimo Cecchi <[email protected]>
+
+ * Makefile.am:
+ * mate-screenshot.c: (display_help), (create_screenshot_frame),
+ (load_options), (main):
+ Drop libmateui dependency (#557169).
+
+2008-10-20 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c:
+ (load_options), (save_options): Remove the code that loads/saves
+ the take_window_shot MateConf key.
+
+ (main): Mark the -B switch description as translatable.
+
+ * mate-screenshot.schemas.in: Deprecate the take_window_shot key.
+
+2008-10-20 Emmanuele Bassi <[email protected]>
+
+ Bug 553085 – After doing mate-screenshot -w (grab a window), "grab the entire
+ screen" works to grab a window.
+
+ * mate-screenshot.c:
+ (load_options), (save_options): Ignore the take_window_shot MateConf key
+ when loading and saving the options.
+
+ (main): Include a -B|--remove-border command line switch to disable the
+ window border.
+
+2008-09-14 Emmanuele Bassi <[email protected]>
+
+ Bug 552175 – invalid schemas file
+
+ * mate-screenshot.schemas.in: Provide default values for
+ the take_window_show and delay keys. (Christian Persch)
+
+2008-09-08 Emmanuele Bassi <[email protected]>
+
+ Bug 99897 – Screen Shot to clip board
+
+ * mate-screenshot.c:
+ (screenshot_dialog_response_cb): Allow copying the screenshot
+ image to the clipboard. Based on a patch by Vincent Untz.
+
+ * mate-screenshot.ui: Add the 'Copy to Clipboard' button.
+
+ * screenshot-dialog.[ch]: Allow retrieving the GdkPixbuf
+ of the screenshot from the dialog.
+
+2008-08-28 Cosimo Cecchi <[email protected]>
+
+ * mate-screenshot.c: (finish_prepare_screenshot),
+ (check_file_done), (try_check_file), (find_current_window),
+ (prepare_screenshot):
+ * screenshot-save.c: (screenshot_sanitize_filename):
+ Make guessing screenshot filename from window title work again
+ (#549736).
+
+2008-08-28 Cosimo Cecchi <[email protected]>
+
+ * Makefile.am:
+ * mate-screenshot.c: (main):
+ * mate-screenshot.glade:
+ * mate-screenshot.ui:
+ * screenshot-dialog.c: (screenshot_dialog_new),
+ (screenshot_dialog_enable_dnd), (screenshot_dialog_get_toplevel):
+ * screenshot-dialog.h:
+ Drop libglade dependency. Use GtkBuilder instead (#549700).
+
+2008-08-28 Emmanuele Bassi <[email protected]>
+
+ Bug 549667 – crash in shape code
+
+ * screenshot-utils.c (screenshot_get_pixbuf): Check the returned
+ value for XShapeGetRectangles(). (Matthias Clasen)
+
+2008-08-25 Cosimo Cecchi <[email protected]>
+
+ * Makefile.am: Leftover from the Gdk port: remove $(XFIXES_LIBS)
+ from LDADD.
+
+2008-08-25 Cosimo Cecchi <[email protected]>
+
+ * screenshot-save.c: (clean_up_temporary_dir),
+ (read_pipe_from_child): Fix some warnings due to missing string
+ format placeholders.
+
+2008-08-10 Emmanuele Bassi <[email protected]>
+
+ Bug 434580 – mate-screenshot does not remember user settings
+
+ * mate-screenshot.c:
+ * mate-screenshot.schemas.in: Save the user settings after
+ the screenshot has been taken. (Elliott Hughes)
+
+2008-08-10 Emmanuele Bassi <[email protected]>
+
+ Bug 454689 – Messages in the mate-screenshot --help output
+ are not all translated
+
+ * mate-screenshot.c (main): Remove the useless option group
+ and allow the translation of the application options. (Gabor
+ Kelemen)
+
+2008-08-10 Emmanuele Bassi <[email protected]>
+
+ Bug 521799 – Use PNG standard keyword
+
+ * screenshot-save.c (screenshot_save_start): Use the standard
+ "Software" keyword and update the name of the program. (Felix
+ Riemann)
+
+2008-08-10 Emmanuele Bassi <[email protected]>
+
+ Bug 515179 – F1 does not display help in interactive dialogue
+
+ * mate-screenshot.c:
+ (key_press_cb), (create_interactive_dialog): Show help in the
+ interactive dialog when pressing F1. (Alexander Gnodtke)
+
+2008-07-27 Vincent Untz <[email protected]>
+
+ * mate-screenshot.c: (target_toggled_cb), (create_effects_frame):
+ Use HIG frame spacing and toggle label sensitivity too.
+ Patch by Dennis Cranston <[email protected]>
+ Fix bug #403462.
+
+2008-07-25 Cosimo Cecchi <[email protected]>
+
+ * mate-egg-xfer-dialog-icons.h:
+ * mate-egg-xfer-dialog.c:
+ * mate-egg-xfer-dialog.h:
+ Remove these files, which aren't used anymore after the port to GIO.
+
+2008-07-22 Cosimo Cecchi <[email protected]>
+
+ * Makefile.am:
+ * mate-screenshot.c: (error_dialog_response_cb), (save_callback),
+ (try_to_save), (screenshot_dialog_response_cb), (run_dialog),
+ (finish_prepare_screenshot), (check_file_done), (build_uri),
+ (try_check_file), (prepare_screenshot),
+ (prepare_screenshot_timeout), (expand_initial_tilde),
+ (load_options), (screenshooter_init_stock_icons), (main):
+ * screenshot-dialog.c: (screenshot_dialog_new),
+ (screenshot_dialog_focus_entry), (screenshot_dialog_get_uri):
+ * screenshot-dialog.h:
+ * screenshot-save.c: (screenshot_sanitize_filename):
+ * screenshot-shadow.c:
+ * screenshot-shadow.h:
+ * screenshot-xfer.c: (do_run_overwrite_confirm_dialog),
+ (transfer_dialog_response_cb), (transfer_progress_dialog_new),
+ (transfer_progress_dialog_start), (run_overwrite_confirm_dialog),
+ (transfer_progress_dialog_update), (transfer_job_done),
+ (transfer_progress_cb), (get_file_size), (transfer_file),
+ (screenshot_xfer_uri):
+ * screenshot-xfer.h:
+ Port mate-screenshot to GIO, clean up some code and add licence
+ headers (#526834).
+
+2008-06-04 Christian Persch <[email protected]>
+
+ * mate-screenshot.c: (create_interactive_dialog): Don't skip the
+ screenshot window in the taskbar. Bug #400203.
+
+2008-04-08 Sebastien Bacher <[email protected]>
+
+ * mate-screenshot.c: (create_interactive_dialog):
+ set the default response to the ok action (#487134)
+
+2008-04-07 Matthias Clasen <[email protected]>
+
+ Bug 171151 – showing the cursor in the screenshot
+
+ * screenshot-utils.h:
+ * screenshot-utils.c (screenshot_get_pixbuf): Optionally
+ include the cursor in the image.
+ (screenshot_grab_lock): Don't grab the pointer while taking
+ the screenshot.
+
+ * mate-screenshot.schemas.in:
+ * mate-screenshot.c: Add an 'include pointer' option.
+
+2007-10-23 Stéphane Loeuillet <[email protected]>
+
+ * mate-screenshot.desktop.in: Trivial fix to .desktop file
+ to conform to latest fd.o .desktop specs.
+ Spoted by desktop-file-validate. Fixes #481721
+
+2007-08-23 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Use g_get_user_special_dir() to find
+ out the Desktop directory. Use the temp directory as a final
+ fallback, instead of / (which won't be writable in most cases
+ and will utterly fail). (#455204, Luca Ferretti, Frederic Crozat)
+
+2007-08-04 Emmanuele Bassi <[email protected]>
+
+ * screenshot-save.c: Make the temporary directory work on
+ multi-user setups by using a unique name. (#427870)
+
+2007-03-10 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c: Warn (in console) if g_filename_from_utf8()
+ fails to convert the screenshot file name in case we are not in
+ a utf8 locale, and fall back to the default screenshot file name,
+ to avoid eating the user's folders. (#411050, thanks to
+ Pascal Terjan for his detective work)
+
+2007-03-04 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Add call to gettext() in order to
+ translate the user visible name of the effect. (#413019,
+ reported by Funda Wang, based on a patch by Gabor Kelemen).
+
+2007-02-11 Emmanuele Bassi <[email protected]>
+
+ * screenshot-shadow.c: Clean up some of the indentation;
+ use BILINEAR instead of NEAREST as the interpolation
+ algorithm, for better results.
+
+2007-01-25 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Another set of HIG fixes for the
+ interactive dialog: make the dialog not resizable; add more
+ spacing; add/update the mnemonics. (#400203, Christian Persch)
+
+2007-01-25 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Use an array of structures to hold the
+ border effects informations (id, nick, label) and iterate on
+ the array to fill up the model fed to the effects combo.
+
+2007-01-25 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Unref the MateProgram instance if you
+ cancel the screenshot in interactive mode; hide the dialog
+ inside the response callback, while the main loop is still
+ running, so that the screenshot functions can grab the desired
+ window instead of the (non-existing) interactive dialog.
+
+ * screenshot-save.[ch]:
+ * screenshot-utils.[ch]: Add license and copyright notice on
+ these files.
+
+2007-01-24 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Do not leak MateProgram instance
+ and don't free the GOptionContext we pass to
+ mate_program_init(). (#400200, Christian Persch)
+
+2007-01-23 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Add a value for the "no effect" value
+ of the border_effect MateConf key; add it to the effects combo
+ box. (#399859)
+
+ * mate-screenshot.schemas.in: Fix the description of the
+ border-effect key.
+
+2007-01-22 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.c: Use symbolic names for the MateConf keys;
+ move the frame creation of the interactive dialog into its
+ own function; add an "effects" frame containing a check button
+ for the "include window border" option and a combo box for
+ the effects on the screenshot.
+
+2007-01-09 Dennis Cranston <[email protected]>
+
+ * mate-screenshot.c: Register "applets-screenshooter" in
+ the icon factory so gtk_image_new_from_stock() works. HIG
+ fixes for interactive dialog. (#394941)
+
+2007-01-07 Emmanuele Bassi <[email protected]>
+
+ A mate-screenshot.c
+ D mate-panel-screenshot.c
+ D mate-panel-screenshot.glade
+
+ * Makefile.am:
+ * mate-screenshot.c: Remove mention of mate-panel-screenshot
+ from the build and code: this is not a panel applet anymore;
+ implement the interactive dialog for controlling the settings
+ of the screenshot without using the command line switching.
+
+ * screenshot-save.c: Honour TMPDIR using g_get_tmp_dir() and
+ use a nicer layout for creating a unique directory for the
+ temporary screenshot file: now use a <pid>-<try-n> directory
+ under the ${TMPDIR}/mate-screenshot root.
+
+ * screenshot-utils.[ch]: Add utility functions for showing a
+ dialog in case of error.
+
+ * mate-screenshot.desktop.in: Use the interactive mode when
+ launching mate-screenshot from the menu.
+
+2007-01-04 Emmanuele Bassi <[email protected]>
+
+ * mate-panel-screenshot.c: Add a call to initialise the
+ auth manager. (#362903)
+
+2006-12-22 Emmanuele Bassi <[email protected]>
+
+ * screenshot-save.c: Apply long-forgotten patch: if we can
+ successfully read something from the child pipe, then it's
+ surely an error; seems to fix mate-screenshot on *BSD
+ boxes. (hopefully closes #161525)
+
+2006-10-18 Emmanuele Bassi <[email protected]>
+
+ * mate-panel-screenshot.c: Remove the unneeded headers
+ Xmu/WinUtil.h and cursorfont.h. (closes #167089)
+
+2006-10-18 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c: Remove the useless target
+ "x-special/mate-icon-list", as we already support
+ text/uri-list and we don't pass any of the icon
+ data with DnD.
+
+ (drag_data_get): Fix the URI passed via DnD using the
+ gtk_selection_data_set_uris() API function instead of
+ manually building the URI list. (closes #361016, reported
+ by Stephan Delcroix)
+
+2006-09-26 Emmanuele Bassi <[email protected]>
+
+ * mate-screenshot.desktop.in: Add the OtherBinaries key
+ for Bugzilla to the desktop file.
+
+2006-09-26 Ross Burton <[email protected]>
+
+ * screenshot-dialog.c:
+ Implement the advertised dragging of image data (#354685).
+
+2006-08-25 Emmanuele Bassi <[email protected]>
+
+ * screenshot-shadow.c (create_outline_filter): outline
+ filter is now a filled square instead of filled
+ circle. (#310860, Emmanuel Pacaud)
+
+2006-08-25 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c (screenshot_dialog_new): Use the
+ UTF-8 functions to select just the file name and not the
+ extension. (#348896, Kouhei Sutou)
+
+2006-08-08 Emmanuele Bassi <[email protected]>
+
+ * screenshot-utils.c (get_window_property): Fix the
+ call to XGetWindowProperty that got mangled by the
+ compiler fixes patch of bug #318095. (#350447)
+
+2006-07-01 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c (screenshot_dialog_new): Use
+ g_utf8_strrchr() to find the last mark before the
+ extension.
+
+2006-06-14 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c: Fix includes.
+
+2006-06-11 Emmanuele Bassi <[email protected]>
+
+ * mate-egg-xfer-dialog.c:
+ * screenshot-dialog.c:
+ * screenshot-save:
+ * screenshot-utils.c:
+ * screenshot-xfer.c: Fix includes, and drop deprecated I18N
+ header mate-i18n.h (Kristof Vansant, #167098).
+
+ * screenshot-utils.c (get_utf8_property), (find_toplevel_window),
+ (look_for_hint_helper): Fix signedness warnings.
+
+ * mate-screenshot.desktop.in: Fix Comment key using the
+ description provided in http://live.mate.org/UsabilityTeam/Menu.
+
+2006-04-23 Emmanuele Bassi <[email protected]>
+
+ * screenshot-utils.c (screenshot_get_pixbuf): Compose with the
+ root window in order to avoid showing garbage for half-visible
+ windows and for menus (Björn Lindqvist, fixes bug #121492
+ and #165949)
+
+2006-04-23 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c:
+ * screenshot-save.c: Use 'Folder' in place of 'Directory'
+ for consistency with the naming policy (closes bug #100052)
+
+2006-04-23 Emmanuele Bassi <[email protected]>
+
+ * screenshot-utils.c (screenshot_get_pixbuf): Fix transparency
+ for shaped windows (closes bug #332017)
+
+2006-04-23 Emmanuele Bassi <[email protected]>
+
+ * mate-panel-screenshot.c (main): Show the application's
+ options using the --help command switch (closes bug #338330)
+
+2006-02-14 Emmanuele Bassi <[email protected]>
+
+ * screenshot-dialog.c (screenshot_dialog_new): select just the name
+ of the file but not its extension (closes bug #312346).
+
+2006-02-07 Emmanuele Bassi <[email protected]>
+
+ * mate-panel-screenshot.c (main): switch to GOption for parsing the
+ command line switches. Display a warning if the option is not
+ recognized (closes bug #329296).
+
+2006-02-05 Emmanuele Bassi <[email protected]>
+
+ * screenshot-save.c (screenshot_save_start): mark the name of the
+ temporary file as translatable, in order to have the localized file
+ name when saving by drag and drop (closes bug #170171).
+
+2006-01-15 Emmanuele Bassi <[email protected]>
+
+ A mate-screenshot.desktop.in
+
+ * mate-screenshot.desktop.in: provide a .desktop file launcher for
+ MATE Screenshot.
+
+ * Makefile.am: include mate-screenshot.desktop.in to the build.
+
+2005-12-23 Dennis Cranston <[email protected]>
+
+ * screenshot-dialog.c: (screenshot_dialog_get_uri):
+ * screenshot-save.c: Clean up compiler warnings.
+
+2005-12-23 Dennis Cranston <[email protected]>
+
+ * screenshot-dialog.c: (screenshot_dialog_get_uri):
+ * screenshot-save.c: (screenshot_sanitize_filename): Escape the
+ path strings. Fixes crash reported in bug #319188.
+
+2005-12-21 Emmanuele Bassi <[email protected]>
+
+ * screenshot-xfer.c (handle_transfer_ok): fix compiler warnings (patch
+ by Kjartan Maraas, closes bug #324552).
+
+2005-10-19 Sebastien Bacher <[email protected]>
+
+ * screenshot-dialog.c: (screenshot_dialog_new):
+ mention mate-utils instead of mate-panel (Closes: #167158).
+
+2005-10-19 Sebastien Bacher <[email protected]>
+
+ * mate-panel-screenshot.c: (generate_filename_for_uri):
+ clean an useless g_print statement (Closes: #319191).
+
+==================== 2.12.0 ====================
+
+Sun Aug 28 01:09:42 2005 Jonathan Blandford <[email protected]>
+
+ * mate-panel-screenshot.c: (generate_filename_for_uri),
+ (prepare_screenshot), (get_desktop_dir), (load_options): Fix
+ crasher when we can't access the last saved dir for some reason,
+ #309731
+
+2005-08-21 Gustavo Noronha Silva <[email protected]>
+
+ * mate-panel-screenshot.c:
+ - fix typo in variable name "filename" -> "file_name"
+
+2005-08-21 Kang Jeong-Hee <[email protected]>
+
+ * mate-panel-screenshot.c, screenshot-dialog.c: Fix wrong order
+ of filename charset conversion.
+
+Sun Jul 17 15:04:13 2005 Jonathan Blandford <[email protected]>
+
+ * mate-panel-screenshot.c: (prepare_screenshot), (load_options):
+ * screenshot-shadow.c: (create_outline_filter), (create_effect),
+ (screenshot_add_shadow), (screenshot_add_border): *
+ screenshot-shadow.h: Patch from Emmanuel Pacaud
+ <[email protected]> to clean up the shadow drawing
+ code, #169248.
+
+2005-05-16 Olav Vitters <[email protected]>
+
+ * mate-panel-screenshot.c: Fix typo in mateconf key (mate_screenshot
+ instead of mate-screenshot). Fixes bug #303891. Patch by Trent
+ "Lathiat" Lloyd.
+
+2005-01-22 Carlos Garnacho Parro <[email protected]>
+
+ * screenshot-dialog.c: mark a string for translation, remove an
+ unnecessary function call
+
+2004-12-22 Vincent Noel <[email protected]>
+
+ * mate-panel-screenshot.c (main, prepare_screenshot_timeout):
+ When called with --delay, call gtk_main() otherwise the g_timeout
+ is never called. Fixes bug #161461.
+
+2004-12-21 Dennis Cranston <[email protected]>
+
+ * mate-screenshot.glade: Use proper HIG spacing in table1,
+ replace file_chooser_frame with file_chooser_box to remove
+ extra padding from around the file-chooser-button.
+
+ * screenshot-dialog.c: (screenshot_dialog_new): Replace
+ file_chooser_frame with file_chooser_box.
+
+2004-12-16 Vincent Noel <[email protected]>
+
+ * screenshot-dialog.c (screenshot_dialog_new): Use new
+ GtkFileChooserButton API.
+
+2004-11-10 Dennis Cranston <[email protected]>
+
+ * mate-screenshot.schemas.in, mate-panel-screenshot.c:
+ Follow mateconf naming standard.
+
+Wed Nov 10 01:15:02 2004 Jonathan Blandford <[email protected]>
+
+ * mate-panel-screenshot.c: Save the last directory visited to
+ mateconf. Remove dead code. M-x clean-line-ends
+
+ * mate-screenshot.glade: resizable->False; has_default->True;
+ redo layout.
+
+ * screenshot-dialog.c: Fixes to DnD. Render insensitive when
+ saving. Redo layout to use a GtkFileChooserButton.
+
+ * screenshot-xfer.c: Minor changes in prep for upcoming big merge.
+
+2004-11-06 Fernando Herrera <[email protected]>
+
+ * screenshot-dialog.c: (screenshot_dialog_new): use the new
+ glade filename.
+
+Wed Nov 3 11:53:02 2004 Jonathan Blandford <[email protected]>
+
+ * screenshot-xfer.[ch]: remove bogus old dialog stuff.
+
+Wed Nov 3 11:29:28 2004 Jonathan Blandford <[email protected]>
+
+ * New ChangeLog to go along with module move.
+
diff --git a/mate-screenshot/Makefile.am b/mate-screenshot/Makefile.am
new file mode 100644
index 00000000..d83f5869
--- /dev/null
+++ b/mate-screenshot/Makefile.am
@@ -0,0 +1,90 @@
+NULL =
+
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+ -DUIDIR=\""$(uidir)"\" \
+ $(MATE_UTILS_CFLAGS) \
+ $(LIBCANBERRA_GTK_CFLAGS) \
+ $(GTHREAD_CFLAGS) \
+ $(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_LDFLAGS = -export-dynamic
+
+mate_screenshot_LDADD = \
+ $(XSHAPE_LIBS) \
+ $(MATE_UTILS_LIBS) \
+ $(LIBCANBERRA_GTK_LIBS) \
+ $(GTHREAD_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)
+
+EXTRA_DIST = \
+ $(schemas_in_files) \
+ $(mate_screenshot_in_files) \
+ $(man_MANS) \
+ $(ui_DATA) \
+ $(NULL)
+
+schemasdir = $(MATECONF_SCHEMA_FILE_DIR)
+schemas_in_files = \
+ mate-screenshot.schemas.in \
+ $(NULL)
+schemas_DATA = $(schemas_in_files:.schemas.in=.schemas)
+
+@INTLTOOL_SCHEMAS_RULE@
+
+if MATECONF_SCHEMAS_INSTALL
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schemas_DATA) ; do \
+ MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) $(MATECONFTOOL) --makefile-install-rule $(top_builddir)/mate-screenshot/$$p ; \
+ done ; \
+ fi
+else
+install-data-local:
+endif
+
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ $(schemas_in_files:.schemas.in=.schemas) \
+ $(mate_screenshot_DATA) \
+ $(sys_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
diff --git a/mate-screenshot/mate-screenshot.1 b/mate-screenshot/mate-screenshot.1
new file mode 100644
index 00000000..aef5ea35
--- /dev/null
+++ b/mate-screenshot/mate-screenshot.1
@@ -0,0 +1,59 @@
+.\\" auto-generated by docbook2man-spec $Revision: 1.1 $
+.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 <[email protected]> for
+the Debian GNU/Linux system (but may be used by others).
+.PP
+Updated by Theppitak Karoonboonyanan
+<[email protected]>, Tom Feiner <[email protected]> and Cosimo Cecchi <[email protected]>
diff --git a/mate-screenshot/mate-screenshot.c b/mate-screenshot/mate-screenshot.c
new file mode 100644
index 00000000..b5ee7f23
--- /dev/null
+++ b/mate-screenshot/mate-screenshot.c
@@ -0,0 +1,1390 @@
+/* mate-screenshot.c - Take a screenshot of the desktop
+ *
+ * Copyright (C) 2001 Jonathan Blandford <[email protected]>
+ * Copyright (C) 2006 Emmanuele Bassi <[email protected]>
+ * Copyright (C) 2008 Cosimo Cecchi <[email protected]>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/* THERE ARE NO FEATURE REQUESTS ALLOWED */
+/* IF YOU WANT YOUR OWN FEATURE -- WRITE THE DAMN THING YOURSELF (-: */
+/* MAYBE I LIED... -jrb */
+
+#include <config.h>
+#include <mateconf/mateconf-client.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <locale.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <pwd.h>
+#include <X11/Xutil.h>
+#include <canberra-gtk.h>
+
+#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_MATECONF "/apps/mate-screenshot"
+#define INCLUDE_BORDER_KEY MATE_SCREENSHOT_MATECONF "/include_border"
+#define INCLUDE_POINTER_KEY MATE_SCREENSHOT_MATECONF "/include_pointer"
+#define LAST_SAVE_DIRECTORY_KEY MATE_SCREENSHOT_MATECONF "/last_save_directory"
+#define BORDER_EFFECT_KEY MATE_SCREENSHOT_MATECONF "/border_effect"
+#define DELAY_KEY MATE_SCREENSHOT_MATECONF "/delay"
+
+
+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;
+
+/* 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),
+ "ghelp: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_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 ("<b>", frame_title, "</b>", 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 ("<b>", frame_title, "</b>", 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 <spin button> 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 <spin button> 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_set_has_separator (GTK_DIALOG (retval), FALSE);
+ 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_mateconf (ScreenshotDialog *dialog)
+{
+ MateConfClient *mateconf_client;
+ char *folder;
+
+ mateconf_client = mateconf_client_get_default ();
+
+ folder = screenshot_dialog_get_folder (dialog);
+ /* Error is NULL, as there's nothing we can do */
+ mateconf_client_set_string (mateconf_client,
+ LAST_SAVE_DIRECTORY_KEY, folder,
+ NULL);
+
+ g_free (folder);
+ g_object_unref (mateconf_client);
+}
+
+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_mateconf (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;
+
+ /* always disable window border for full-desktop or selected-area screenshots */
+ if (!take_window_shot)
+ screenshot = screenshot_get_pixbuf (window, rectangle, include_pointer, FALSE);
+ else
+ {
+ screenshot = screenshot_get_pixbuf (window, rectangle, include_pointer, include_border);
+
+ 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]);
+ g_free (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;
+
+ if (window_title)
+ {
+ /* translators: this is the name of the file that gets made up
+ * with the screenshot if a specific window is taken */
+ if (job->iteration == 0)
+ {
+ file_name = g_strdup_printf (_("Screenshot-%s.png"), window_title);
+ }
+ else
+ {
+ /* translators: this is the name of the file that gets
+ * made up with the screenshot if a specific window is
+ * taken */
+ file_name = g_strdup_printf (_("Screenshot-%s-%d.png"),
+ window_title, job->iteration);
+ }
+ }
+ else
+ {
+ 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 (_("Screenshot.png"));
+ }
+ 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-%d.png"), job->iteration);
+ }
+ }
+
+ retval = g_build_filename (job->base_uris[job->type], file_name, NULL);
+ g_free (file_name);
+
+ 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 GdkRectangle *
+find_rectangle (void)
+{
+ GdkRectangle *rectangle;
+
+ if (!take_area_shot)
+ return NULL;
+
+ rectangle = g_new0 (GdkRectangle, 1);
+ if (screenshot_select_area (&rectangle->x, &rectangle->y,
+ &rectangle->width, &rectangle->height))
+ {
+ g_assert (rectangle->width >= 0);
+ g_assert (rectangle->height >= 0);
+
+ return rectangle;
+ }
+ else
+ {
+ g_free (rectangle);
+ return NULL;
+ }
+}
+
+static void
+prepare_screenshot (void)
+{
+ 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);
+ job->rectangle = find_rectangle ();
+
+ /* 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 gboolean
+prepare_screenshot_timeout (gpointer data)
+{
+ prepare_screenshot ();
+ save_options ();
+
+ return FALSE;
+}
+
+
+static gchar *
+get_desktop_dir (void)
+{
+ MateConfClient *mateconf_client;
+ gboolean desktop_is_home_dir = FALSE;
+ gchar *desktop_dir;
+
+ mateconf_client = mateconf_client_get_default ();
+ desktop_is_home_dir = mateconf_client_get_bool (mateconf_client,
+ "/apps/caja/preferences/desktop_is_home_dir",
+ NULL);
+ 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);
+
+ g_object_unref (mateconf_client);
+
+ 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)
+{
+ MateConfClient *mateconf_client;
+
+ mateconf_client = mateconf_client_get_default ();
+
+ /* Find various dirs */
+ last_save_dir = mateconf_client_get_string (mateconf_client,
+ LAST_SAVE_DIRECTORY_KEY,
+ NULL);
+ 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 = mateconf_client_get_bool (mateconf_client,
+ INCLUDE_BORDER_KEY,
+ NULL);
+
+ include_pointer = mateconf_client_get_bool (mateconf_client,
+ INCLUDE_POINTER_KEY,
+ NULL);
+
+ border_effect = mateconf_client_get_string (mateconf_client,
+ BORDER_EFFECT_KEY,
+ NULL);
+ if (!border_effect)
+ border_effect = g_strdup ("none");
+
+ delay = mateconf_client_get_int (mateconf_client, DELAY_KEY, NULL);
+
+ g_object_unref (mateconf_client);
+}
+
+static void
+save_options (void)
+{
+ MateConfClient *mateconf_client;
+
+ mateconf_client = mateconf_client_get_default ();
+
+ /* Error is NULL, as there's nothing we can do */
+
+ mateconf_client_set_bool (mateconf_client,
+ INCLUDE_BORDER_KEY, include_border,
+ NULL);
+ mateconf_client_set_bool (mateconf_client,
+ INCLUDE_POINTER_KEY, include_pointer,
+ NULL);
+ mateconf_client_set_int (mateconf_client, DELAY_KEY, delay, NULL);
+ mateconf_client_set_string (mateconf_client,
+ BORDER_EFFECT_KEY, border_effect,
+ NULL);
+
+ g_object_unref (mateconf_client);
+}
+
+
+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);
+
+ g_thread_init (NULL);
+
+ 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 ();
+
+ 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
+ {
+ /* start this in an idle anyway and fire up the mainloop */
+ g_idle_add (prepare_screenshot_timeout, NULL);
+ }
+
+ gtk_main ();
+
+ return EXIT_SUCCESS;
+}
diff --git a/mate-screenshot/mate-screenshot.desktop.in b/mate-screenshot/mate-screenshot.desktop.in
new file mode 100644
index 00000000..cd5b1eae
--- /dev/null
+++ b/mate-screenshot/mate-screenshot.desktop.in
@@ -0,0 +1,14 @@
+[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;MATE;Utility;
+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.schemas.in b/mate-screenshot/mate-screenshot.schemas.in
new file mode 100644
index 00000000..a8c2dca4
--- /dev/null
+++ b/mate-screenshot/mate-screenshot.schemas.in
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+
+<mateconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/mate-screenshot/take_window_shot</key>
+ <applyto>/apps/mate-screenshot/take_window_shot</applyto>
+ <owner>mate-screenshot</owner>
+ <type>bool</type>
+ <default>false</default>
+ <locale name="C">
+ <short>Window-specific screenshot (deprecated)</short>
+ <long>Grab just the current window, rather than the whole desktop.
+ This key has been deprecated and it is no longer in use.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/mate-screenshot/delay</key>
+ <applyto>/apps/mate-screenshot/delay</applyto>
+ <owner>mate-screenshot</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Screenshot delay</short>
+ <long>The number of seconds to wait before taking the screenshot.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/mate-screenshot/last_save_directory</key>
+ <applyto>/apps/mate-screenshot/last_save_directory</applyto>
+ <owner>mate-screenshot</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>Screenshot directory</short>
+ <long>The directory the last screenshot was saved in.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/mate-screenshot/include_border</key>
+ <applyto>/apps/mate-screenshot/include_border</applyto>
+ <owner>mate-screenshot</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Include Border</short>
+ <long>Include the window manager border along with the screenshot</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/mate-screenshot/include_pointer</key>
+ <applyto>/apps/mate-screenshot/include_pointer</applyto>
+ <owner>mate-screenshot</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Include Pointer</short>
+ <long>Include the pointer in the screenshot</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/mate-screenshot/border_effect</key>
+ <applyto>/apps/mate-screenshot/border_effect</applyto>
+ <owner>mate-screenshot</owner>
+ <type>string</type>
+ <default>none</default>
+ <locale name="C">
+ <short>Border Effect</short>
+ <long>Effect to add to the outside of a border. Possible values are "shadow", "none", and "border".
+ </long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</mateconfschemafile>
diff --git a/mate-screenshot/mate-screenshot.ui b/mate-screenshot/mate-screenshot.ui
new file mode 100644
index 00000000..fbad450a
--- /dev/null
+++ b/mate-screenshot/mate-screenshot.ui
@@ -0,0 +1,254 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="toplevel">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Save Screenshot</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">False</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+ <signal handler="on_toplevel_key_press_event" last_modification_time="Thu, 21 Oct 2004 13:29:25 GMT" name="key_press_event"/>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="help_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="copy_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="use_underline">True</property>
+ <property name="label" translatable="yes">C_opy to Clipboard</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+ <child>
+ <object class="GtkAspectFrame" id="aspect_frame">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="ratio">1</property>
+ <property name="obey_child">True</property>
+ <child>
+ <object class="GtkDrawingArea" id="preview_darea">
+ <property name="visible">True</property>
+ <signal handler="on_preview_expose_event" name="expose_event"/>
+ <signal handler="on_preview_configure_event" name="configure_event"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Name:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">filename_entry</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Save in _folder:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="filename_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">True</property>
+ <property name="width_chars">24</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="file_chooser_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help_button</action-widget>
+ <action-widget response="-6">cancel_button</action-widget>
+ <action-widget response="-5">ok_button</action-widget>
+ <action-widget response="1">copy_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/mate-screenshot/screenshot-dialog.c b/mate-screenshot/screenshot-dialog.c
new file mode 100644
index 00000000..f7527bfc
--- /dev/null
+++ b/mate-screenshot/screenshot-dialog.c
@@ -0,0 +1,402 @@
+/* screenshot-dialog.c - main MATE Screenshot dialog
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "screenshot-dialog.h"
+#include "screenshot-save.h"
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+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_F1)
+ {
+ gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_HELP);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+on_preview_expose_event (GtkWidget *drawing_area,
+ GdkEventExpose *event,
+ gpointer data)
+{
+ ScreenshotDialog *dialog = data;
+ GdkPixbuf *pixbuf = NULL;
+ gboolean free_pixbuf = FALSE;
+ cairo_t *cr;
+
+ /* 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 (drawing_area->style,
+ 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);
+ }
+
+ cr = gdk_cairo_create (drawing_area->window);
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ 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);
+ }
+ 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;
+ 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", NULL);
+ dialog->screenshot = screenshot;
+
+ if (res == 0)
+ {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("UI definition file for the screenshot program is missing.\n"
+ "Please check your installation of mate-utils"));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ 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);
+ g_signal_connect (preview_darea, "expose_event", G_CALLBACK (on_preview_expose_event), dialog);
+ 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_current_folder_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_current_folder_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 (toplevel->window, cursor);
+ gdk_cursor_unref (cursor);
+ }
+ else
+ {
+ gdk_window_set_cursor (toplevel->window, NULL);
+ }
+
+ gtk_widget_set_sensitive (toplevel, ! busy);
+
+ gdk_flush ();
+}
diff --git a/mate-screenshot/screenshot-dialog.h b/mate-screenshot/screenshot-dialog.h
new file mode 100644
index 00000000..87a5b89f
--- /dev/null
+++ b/mate-screenshot/screenshot-dialog.h
@@ -0,0 +1,42 @@
+/* screenshot-dialog.h - main MATE Screenshot dialog
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#ifndef __SCREENSHOT_DIALOG_H__
+#define __SCREENSHOT_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+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
new file mode 100644
index 00000000..75e1e457
--- /dev/null
+++ b/mate-screenshot/screenshot-save.c
@@ -0,0 +1,285 @@
+/* screenshot-save.c - image saving functions for MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <glib/gi18n.h>
+
+#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
new file mode 100644
index 00000000..1644eb8a
--- /dev/null
+++ b/mate-screenshot/screenshot-save.h
@@ -0,0 +1,34 @@
+/* screenshot-save.h - image saving functions for MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#ifndef __SCREENSHOT_SAVE_H__
+#define __SCREENSHOT_SAVE_H__
+
+#include <gtk/gtk.h>
+
+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
new file mode 100644
index 00000000..913429db
--- /dev/null
+++ b/mate-screenshot/screenshot-shadow.c
@@ -0,0 +1,231 @@
+/* screenshot-shadow.c - part of MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+/* Shadow code from anders */
+
+#include "screenshot-shadow.h"
+#include <math.h>
+
+#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
new file mode 100644
index 00000000..b62a03bb
--- /dev/null
+++ b/mate-screenshot/screenshot-shadow.h
@@ -0,0 +1,28 @@
+/* screenshot-shadow.h - part of MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#ifndef __SCREENSHOT_SHADOW_H__
+#define __SCREENSHOT_SHADOW_H__
+
+#include <gtk/gtk.h>
+
+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
new file mode 100644
index 00000000..896ad504
--- /dev/null
+++ b/mate-screenshot/screenshot-utils.c
@@ -0,0 +1,936 @@
+/* screenshot-utils.c - common functions for MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ * Copyright (C) 2008 Cosimo Cecchi <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#include "config.h"
+#include "screenshot-utils.h"
+
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
+#include <X11/extensions/shape.h>
+#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;
+}
+
+static void
+select_area_button_press (XKeyEvent *event,
+ GdkRectangle *rect,
+ GdkRectangle *draw_rect)
+{
+ rect->x = event->x_root;
+ rect->y = event->y_root;
+
+ draw_rect->x = rect->x;
+ draw_rect->y = rect->y;
+ draw_rect->width = 0;
+ draw_rect->height = 0;
+}
+
+static void
+select_area_update_rect (GtkWidget *window,
+ GdkRectangle *rect)
+{
+ if (rect->width <= 0 || rect->height <= 0)
+ {
+ gtk_widget_hide (window);
+ return;
+ }
+
+ gtk_window_move (GTK_WINDOW (window), rect->x, rect->y);
+ gtk_window_resize (GTK_WINDOW (window), rect->width, rect->height);
+ gtk_widget_show (window);
+
+ /* 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 (rect->width > 2 && rect->height > 2)
+ {
+ GdkRegion *region, *region2;
+ GdkRectangle region_rect = { 0, 0,
+ rect->width - 2, rect->height - 2 };
+
+ region = gdk_region_rectangle (&region_rect);
+ region_rect.x++;
+ region_rect.y++;
+ region_rect.width -= 2;
+ region_rect.height -= 2;
+ region2 = gdk_region_rectangle (&region_rect);
+ gdk_region_subtract (region, region2);
+
+ 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);
+ }
+}
+
+static void
+select_area_button_release (XKeyEvent *event,
+ GdkRectangle *rect,
+ GdkRectangle *draw_rect,
+ GtkWidget *window)
+{
+ gtk_widget_hide (window);
+
+ rect->width = ABS (rect->x - event->x_root);
+ rect->height = ABS (rect->y - event->y_root);
+
+ rect->x = MIN (rect->x, event->x_root);
+ rect->y = MIN (rect->y, event->y_root);
+}
+
+static void
+select_area_motion_notify (XKeyEvent *event,
+ GdkRectangle *rect,
+ GdkRectangle *draw_rect,
+ GtkWidget *window)
+{
+ draw_rect->width = ABS (rect->x - event->x_root);
+ draw_rect->height = ABS (rect->y - event->y_root);
+
+ draw_rect->x = MIN (rect->x, event->x_root);
+ draw_rect->y = MIN (rect->y, event->y_root);
+
+ select_area_update_rect (window, draw_rect);
+}
+
+typedef struct {
+ GdkRectangle rect;
+ GdkRectangle draw_rect;
+ gboolean button_pressed;
+ GtkWidget *window;
+} select_area_filter_data;
+
+static gboolean
+expose (GtkWidget *window, GdkEventExpose *event, gpointer unused)
+{
+ GtkAllocation allocation;
+ GtkStyle *style;
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (event->window);
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ 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);
+ }
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static GtkWidget *
+create_select_window (void)
+{
+ GtkWidget *window;
+ GdkScreen *screen;
+
+ screen = gdk_screen_get_default ();
+
+ window = gtk_window_new (GTK_WINDOW_POPUP);
+ if (gdk_screen_is_composited (screen) &&
+ gdk_screen_get_rgba_colormap (screen))
+ {
+ gtk_widget_set_colormap (window, gdk_screen_get_rgba_colormap (screen));
+ gtk_widget_set_app_paintable (window, TRUE);
+ }
+ g_signal_connect (window, "expose-event", G_CALLBACK (expose), NULL);
+
+ return window;
+}
+
+static GdkFilterReturn
+select_area_filter (GdkXEvent *gdk_xevent,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ select_area_filter_data *data = user_data;
+ XEvent *xevent = (XEvent *) gdk_xevent;
+
+ switch (xevent->type)
+ {
+ case ButtonPress:
+ if (!data->button_pressed)
+ {
+ select_area_button_press (&xevent->xkey,
+ &data->rect, &data->draw_rect);
+ data->button_pressed = TRUE;
+ }
+ return GDK_FILTER_REMOVE;
+ case ButtonRelease:
+ if (data->button_pressed)
+ {
+ select_area_button_release (&xevent->xkey,
+ &data->rect, &data->draw_rect,
+ data->window);
+ gtk_main_quit ();
+ }
+ return GDK_FILTER_REMOVE;
+ case MotionNotify:
+ if (data->button_pressed)
+ select_area_motion_notify (&xevent->xkey,
+ &data->rect, &data->draw_rect,
+ data->window);
+ return GDK_FILTER_REMOVE;
+ case KeyPress:
+ if (xevent->xkey.keycode == XKeysymToKeycode (gdk_display, XK_Escape))
+ {
+ data->rect.x = 0;
+ data->rect.y = 0;
+ data->rect.width = 0;
+ data->rect.height = 0;
+ gtk_main_quit ();
+ return GDK_FILTER_REMOVE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+gboolean
+screenshot_select_area (int *px,
+ int *py,
+ int *pwidth,
+ int *pheight)
+{
+ GdkWindow *root;
+ GdkCursor *cursor;
+ select_area_filter_data data;
+
+ root = gdk_get_default_root_window ();
+ cursor = gdk_cursor_new (GDK_CROSSHAIR);
+
+ if (gdk_pointer_grab (root, 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);
+ return FALSE;
+ }
+
+ if (gdk_keyboard_grab (root, FALSE, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
+ {
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ gdk_cursor_unref (cursor);
+ return FALSE;
+ }
+
+ gdk_window_add_filter (root, (GdkFilterFunc) select_area_filter, &data);
+
+ gdk_flush ();
+
+ 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();
+
+ gtk_main ();
+
+ gdk_window_remove_filter (root, (GdkFilterFunc) select_area_filter, &data);
+
+ gtk_widget_destroy (data.window);
+
+ gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ gdk_cursor_unref (cursor);
+
+ *px = data.rect.x;
+ *py = data.rect.y;
+ *pwidth = data.rect.width;
+ *pheight = data.rect.height;
+
+ return TRUE;
+}
+
+static Window
+find_wm_window (Window xid)
+{
+ Window root, parent, *children;
+ unsigned int nchildren;
+
+ do
+ {
+ if (XQueryTree (GDK_DISPLAY (), 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);
+}
+
+static GdkRegion *
+make_region_with_monitors (GdkScreen *screen)
+{
+ GdkRegion *region;
+ int num_monitors;
+ int i;
+
+ num_monitors = gdk_screen_get_n_monitors (screen);
+
+ region = gdk_region_new ();
+
+ for (i = 0; i < num_monitors; i++)
+ {
+ GdkRectangle rect;
+
+ gdk_screen_get_monitor_geometry (screen, i, &rect);
+ gdk_region_union_with_rect (region, &rect);
+ }
+
+ 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
+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);
+
+ 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++)
+ {
+ GdkRectangle dest;
+
+ if (gdk_rectangle_intersect (rects + i, &pixbuf_rect, &dest))
+ blank_rectangle_in_pixbuf (pixbuf, &dest);
+ }
+
+ g_free (rects);
+}
+
+/* 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;
+ GdkRegion *region_with_monitors;
+ GdkRegion *invisible_region;
+ GdkRectangle rect;
+
+ screen = gdk_drawable_get_screen (GDK_DRAWABLE (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);
+
+ 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);
+}
+
+GdkPixbuf *
+screenshot_get_pixbuf (GdkWindow *window,
+ GdkRectangle *rectangle,
+ gboolean include_pointer,
+ gboolean include_border)
+{
+ 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_XWINDOW (window);
+ wm = find_wm_window (xid);
+
+ if (wm != None)
+ window = gdk_window_foreign_new (wm);
+
+ /* fallback to no border if we can't find the WM window. */
+ }
+
+ root = gdk_get_default_root_window ();
+
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ real_width = gdk_window_get_width(window);
+ real_height = gdk_window_get_height(window);
+ #else
+ gdk_drawable_get_size(window, &real_width, &real_height);
+ #endif
+
+ 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;
+ }
+
+ screenshot = gdk_pixbuf_get_from_drawable (NULL, root, NULL,
+ x_orig, y_orig, 0, 0,
+ width, height);
+
+ 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 (),
+ GDK_WINDOW_XWINDOW (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 && parent->group)
+ gtk_window_group_add_window (parent->group, 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
new file mode 100644
index 00000000..994e8d1a
--- /dev/null
+++ b/mate-screenshot/screenshot-utils.h
@@ -0,0 +1,50 @@
+/* screenshot-utils.h - common functions for MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#ifndef __SCREENSHOT_UTILS_H__
+#define __SCREENSHOT_UTILS_H__
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+G_BEGIN_DECLS
+
+gboolean screenshot_grab_lock (void);
+void screenshot_release_lock (void);
+gchar *screenshot_get_window_title (GdkWindow *win);
+GdkWindow *screenshot_find_current_window (void);
+gboolean screenshot_select_area (int *px,
+ int *py,
+ int *pwidth,
+ int *pheight);
+GdkPixbuf *screenshot_get_pixbuf (GdkWindow *win,
+ GdkRectangle *rectangle,
+ gboolean include_pointer,
+ gboolean include_border);
+
+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
new file mode 100644
index 00000000..5d0ba213
--- /dev/null
+++ b/mate-screenshot/screenshot-xfer.c
@@ -0,0 +1,393 @@
+/* screenshot-xfer.c - file transfer functions for MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ * Copyright (C) 2008 Cosimo Cecchi <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#include "config.h"
+
+#include "screenshot-xfer.h"
+
+#include <time.h>
+#include <glib/gi18n.h>
+
+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 (gdialog)->label->parent),
+ 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
new file mode 100644
index 00000000..4f8353bd
--- /dev/null
+++ b/mate-screenshot/screenshot-xfer.h
@@ -0,0 +1,45 @@
+/* screenshot-xfer.h - file transfer functions for MATE Screenshot
+ *
+ * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]>
+ * Copyright (C) 2008 Cosimo Cecchi <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ */
+
+#ifndef __SCREENSHOT_XFER_H__
+#define __SCREENSHOT_XFER_H__
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+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__ */