1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * Screenshot plug-in
5  * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
6  * Copyright 2003      Henrik Brix Andersen <brix@gimp.org>
7  * Copyright 2012      Simone Karin Lehmann - OS X patches
8  * Copyright 2016      Michael Natterer <mitch@gimp.org>
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <libgimp/gimp.h>
27 #include <libgimp/gimpui.h>
28 
29 #include "screenshot.h"
30 #include "screenshot-freedesktop.h"
31 #include "screenshot-icon.h"
32 #include "screenshot-kwin.h"
33 #include "screenshot-osx.h"
34 #include "screenshot-x11.h"
35 #include "screenshot-win32.h"
36 
37 #include "libgimp/stdplugins-intl.h"
38 
39 
40 /* Defines */
41 
42 #define PLUG_IN_PROC   "plug-in-screenshot"
43 #define PLUG_IN_BINARY "screenshot"
44 #define PLUG_IN_ROLE   "gimp-screenshot"
45 
46 #ifdef __GNUC__
47 #ifdef GDK_NATIVE_WINDOW_POINTER
48 #if GLIB_SIZEOF_VOID_P != 4
49 #warning window_id does not fit in PDB_INT32
50 #endif
51 #endif
52 #endif
53 
54 
55 static void                query               (void);
56 static void                run                 (const gchar      *name,
57                                                 gint              nparams,
58                                                 const GimpParam  *param,
59                                                 gint             *nreturn_vals,
60                                                 GimpParam       **return_vals);
61 
62 static GimpPDBStatusType   shoot               (GdkScreen        *screen,
63                                                 gint32           *image_ID,
64                                                 GError          **error);
65 
66 static gboolean            shoot_dialog        (GdkScreen       **screen);
67 static gboolean            shoot_quit_timeout  (gpointer          data);
68 static gboolean            shoot_delay_timeout (gpointer          data);
69 
70 
71 /* Global Variables */
72 
73 static ScreenshotBackend       backend            = SCREENSHOT_BACKEND_NONE;
74 static ScreenshotCapabilities  capabilities       = 0;
75 static GtkWidget              *select_delay_table = NULL;
76 static GtkWidget              *shot_delay_table   = NULL;
77 
78 static ScreenshotValues shootvals =
79 {
80   SHOOT_WINDOW, /* root window            */
81   TRUE,         /* include WM decorations */
82   0,            /* window ID              */
83   0,            /* monitor                */
84   0,            /* select delay           */
85   0,            /* screenshot delay       */
86   0,            /* coords of region dragged out by pointer */
87   0,
88   0,
89   0,
90   FALSE,        /* show cursor */
91   SCREENSHOT_PROFILE_POLICY_MONITOR
92 };
93 
94 const GimpPlugInInfo PLUG_IN_INFO =
95 {
96   NULL,  /* init_proc  */
97   NULL,  /* quit_proc  */
98   query, /* query_proc */
99   run    /* run_proc   */
100 };
101 
102 
103 /* Functions */
104 
MAIN()105 MAIN ()
106 
107 static void
108 query (void)
109 {
110   static const GimpParamDef args[] =
111   {
112     { GIMP_PDB_INT32, "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"     },
113     { GIMP_PDB_INT32, "shoot-type", "The Shoot type { SHOOT-WINDOW (0), SHOOT-ROOT (1), SHOOT-REGION (2) }" },
114     { GIMP_PDB_INT32, "window-id",  "Window id for SHOOT-WINDOW"       },
115     { GIMP_PDB_INT32, "x1",         "Region left x coord for SHOOT-REGION"   },
116     { GIMP_PDB_INT32, "y1",         "Region top y coord for SHOOT-REGION"    },
117     { GIMP_PDB_INT32, "x2",         "Region right x coord for SHOOT-REGION"  },
118     { GIMP_PDB_INT32, "y2",         "Region bottom y coord for SHOOT-REGION" }
119   };
120 
121   static const GimpParamDef return_vals[] =
122   {
123     { GIMP_PDB_IMAGE, "image", "Output image" }
124   };
125 
126   gimp_install_procedure (PLUG_IN_PROC,
127                           N_("Create an image from an area of the screen"),
128                           "The plug-in takes screenshots of an "
129                           "interactively selected window or of the desktop, "
130                           "either the whole desktop or an interactively "
131                           "selected region. When called non-interactively, it "
132                           "may grab the root window or use the window-id "
133                           "passed as a parameter.  The last four parameters "
134                           "are optional and can be used to specify the corners "
135                           "of the region to be grabbed."
136                           "On Mac OS X or on gnome-shell, "
137                           "when called non-interactively, the plug-in"
138                           "only can take screenshots of the entire root window."
139                           "Grabbing a window or a region is not supported"
140                           "non-interactively. To grab a region or a particular"
141                           "window, you need to use the interactive mode."
142                           ,
143                           "Sven Neumann <sven@gimp.org>, "
144                           "Henrik Brix Andersen <brix@gimp.org>,"
145                           "Simone Karin Lehmann",
146                           "1998 - 2008",
147                           "v1.1 (2008/04)",
148                           N_("_Screenshot..."),
149                           NULL,
150                           GIMP_PLUGIN,
151                           G_N_ELEMENTS (args),
152                           G_N_ELEMENTS (return_vals),
153                           args, return_vals);
154 
155   gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/File/Create/Acquire");
156   gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_INLINE_PIXBUF,
157                              screenshot_icon);
158 }
159 
160 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)161 run (const gchar      *name,
162      gint             nparams,
163      const GimpParam  *param,
164      gint             *nreturn_vals,
165      GimpParam       **return_vals)
166 {
167   static GimpParam   values[2];
168   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
169   GimpRunMode        run_mode;
170   GdkScreen         *screen = NULL;
171   gint32             image_ID;
172   GError            *error  = NULL;
173 
174   INIT_I18N ();
175   gegl_init (NULL, NULL);
176 
177   run_mode = param[0].data.d_int32;
178 
179   *nreturn_vals = 1;
180   *return_vals  = values;
181 
182   values[0].type          = GIMP_PDB_STATUS;
183   values[0].data.d_status = status;
184 
185 #ifdef PLATFORM_OSX
186   if (! backend && screenshot_osx_available ())
187     {
188       backend      = SCREENSHOT_BACKEND_OSX;
189       capabilities = screenshot_osx_get_capabilities ();
190 
191       /* on OS X, this just means shoot the shadow, default to nope */
192       shootvals.decorate = FALSE;
193     }
194 #endif
195 
196 #ifdef G_OS_WIN32
197   if (! backend && screenshot_win32_available ())
198     {
199       backend      = SCREENSHOT_BACKEND_WIN32;
200       capabilities = screenshot_win32_get_capabilities ();
201     }
202 #endif
203 
204 #ifdef GDK_WINDOWING_X11
205   if (! backend && screenshot_x11_available ())
206     {
207       backend      = SCREENSHOT_BACKEND_X11;
208       capabilities = screenshot_x11_get_capabilities ();
209     }
210 #endif
211   if (! backend && screenshot_freedesktop_available ())
212     {
213       backend      = SCREENSHOT_BACKEND_FREEDESKTOP;
214       capabilities = screenshot_freedesktop_get_capabilities ();
215     }
216 
217   if (! backend && screenshot_kwin_available ())
218     {
219       backend      = SCREENSHOT_BACKEND_KWIN;
220       capabilities = screenshot_kwin_get_capabilities ();
221     }
222 
223   /* how are we running today? */
224   switch (run_mode)
225     {
226     case GIMP_RUN_INTERACTIVE:
227       /* Possibly retrieve data from a previous run */
228       gimp_get_data (PLUG_IN_PROC, &shootvals);
229       shootvals.window_id = 0;
230 
231       if ((shootvals.shoot_type == SHOOT_WINDOW &&
232            ! (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)) ||
233           (shootvals.shoot_type == SHOOT_REGION &&
234            ! (capabilities & SCREENSHOT_CAN_SHOOT_REGION)))
235         {
236           /* Shoot root is the only type of shoot which is definitely
237            * shared by all screenshot backends (basically just snap the
238            * whole display setup).
239            */
240           shootvals.shoot_type = SHOOT_ROOT;
241         }
242 
243       /* Get information from the dialog */
244       if (! shoot_dialog (&screen))
245         status = GIMP_PDB_CANCEL;
246       break;
247 
248     case GIMP_RUN_NONINTERACTIVE:
249       if (nparams == 3 || nparams == 7)
250         {
251           shootvals.shoot_type   = param[1].data.d_int32;
252           shootvals.window_id    = param[2].data.d_int32;
253           shootvals.select_delay = 0;
254 
255           if (shootvals.shoot_type < SHOOT_WINDOW ||
256               shootvals.shoot_type > SHOOT_REGION)
257             {
258               status = GIMP_PDB_CALLING_ERROR;
259             }
260           else if (shootvals.shoot_type == SHOOT_REGION)
261             {
262               if (nparams == 7)
263                 {
264                   shootvals.x1 = param[3].data.d_int32;
265                   shootvals.y1 = param[4].data.d_int32;
266                   shootvals.x2 = param[5].data.d_int32;
267                   shootvals.y2 = param[6].data.d_int32;
268                 }
269               else
270                 {
271                   status = GIMP_PDB_CALLING_ERROR;
272                 }
273             }
274         }
275       else
276         {
277           status = GIMP_PDB_CALLING_ERROR;
278         }
279 
280       if (status == GIMP_PDB_SUCCESS)
281         {
282           if (! gdk_init_check (NULL, NULL))
283             status = GIMP_PDB_CALLING_ERROR;
284 
285           if (! (capabilities & SCREENSHOT_CAN_PICK_NONINTERACTIVELY))
286             {
287               if (shootvals.shoot_type == SHOOT_WINDOW ||
288                   shootvals.shoot_type == SHOOT_REGION)
289                 {
290                   status = GIMP_PDB_CALLING_ERROR;
291                 }
292             }
293         }
294         break;
295 
296     case GIMP_RUN_WITH_LAST_VALS:
297       /* Possibly retrieve data from a previous run */
298       gimp_get_data (PLUG_IN_PROC, &shootvals);
299       break;
300 
301     default:
302       break;
303     }
304 
305   if (status == GIMP_PDB_SUCCESS)
306     {
307       status = shoot (screen, &image_ID, &error);
308     }
309 
310   if (status == GIMP_PDB_SUCCESS)
311     {
312       gchar *comment = gimp_get_default_comment ();
313 
314       gimp_image_undo_disable (image_ID);
315 
316       if (shootvals.profile_policy == SCREENSHOT_PROFILE_POLICY_SRGB)
317         {
318           GimpColorProfile *srgb_profile = gimp_color_profile_new_rgb_srgb ();
319 
320           gimp_image_convert_color_profile (image_ID,
321                                             srgb_profile,
322                                             GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
323                                             TRUE);
324           g_object_unref (srgb_profile);
325         }
326 
327       if (comment)
328         {
329           GimpParasite *parasite;
330 
331           parasite = gimp_parasite_new ("gimp-comment",
332                                         GIMP_PARASITE_PERSISTENT,
333                                         strlen (comment) + 1, comment);
334 
335           gimp_image_attach_parasite (image_ID, parasite);
336           gimp_parasite_free (parasite);
337 
338           g_free (comment);
339         }
340 
341       gimp_image_undo_enable (image_ID);
342 
343       if (run_mode == GIMP_RUN_INTERACTIVE)
344         {
345           /* Store variable states for next run */
346           gimp_set_data (PLUG_IN_PROC, &shootvals, sizeof (ScreenshotValues));
347 
348           gimp_display_new (image_ID);
349 
350           /* Give some sort of feedback that the shot is done */
351           if (shootvals.select_delay > 0)
352             {
353               gdk_display_beep (gdk_screen_get_display (screen));
354               gdk_flush (); /* flush so the beep makes it to the server */
355             }
356         }
357 
358       *nreturn_vals = 2;
359 
360       values[1].type         = GIMP_PDB_IMAGE;
361       values[1].data.d_image = image_ID;
362     }
363 
364   if (status != GIMP_PDB_SUCCESS && error)
365     {
366       *nreturn_vals = 2;
367       values[1].type          = GIMP_PDB_STRING;
368       values[1].data.d_string = error->message;
369     }
370 
371   values[0].data.d_status = status;
372 }
373 
374 
375 /* The main Screenshot function */
376 
377 static GimpPDBStatusType
shoot(GdkScreen * screen,gint32 * image_ID,GError ** error)378 shoot (GdkScreen  *screen,
379        gint32     *image_ID,
380        GError    **error)
381 {
382 #ifdef PLATFORM_OSX
383   if (backend == SCREENSHOT_BACKEND_OSX)
384     return screenshot_osx_shoot (&shootvals, screen, image_ID, error);
385 #endif
386 
387 #ifdef G_OS_WIN32
388   if (backend == SCREENSHOT_BACKEND_WIN32)
389     return screenshot_win32_shoot (&shootvals, screen, image_ID, error);
390 #endif
391 
392   if (backend == SCREENSHOT_BACKEND_FREEDESKTOP)
393     return screenshot_freedesktop_shoot (&shootvals, screen, image_ID, error);
394   else if (backend == SCREENSHOT_BACKEND_KWIN)
395     return screenshot_kwin_shoot (&shootvals, screen, image_ID, error);
396 
397 #ifdef GDK_WINDOWING_X11
398   if (backend == SCREENSHOT_BACKEND_X11)
399     return screenshot_x11_shoot (&shootvals, screen, image_ID, error);
400 #endif
401 
402   return GIMP_PDB_CALLING_ERROR; /* silence compiler */
403 }
404 
405 
406 /*  Screenshot dialog  */
407 
408 static void
shoot_dialog_add_hint(GtkNotebook * notebook,ShootType type,const gchar * hint)409 shoot_dialog_add_hint (GtkNotebook *notebook,
410                        ShootType    type,
411                        const gchar *hint)
412 {
413   GtkWidget *label;
414 
415   label = g_object_new (GTK_TYPE_LABEL,
416                         "label",   hint,
417                         "wrap",    TRUE,
418                         "justify", GTK_JUSTIFY_LEFT,
419                         "xalign",  0.0,
420                         "yalign",  0.0,
421                         NULL);
422   gimp_label_set_attributes (GTK_LABEL (label),
423                              PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
424                              -1);
425 
426   gtk_notebook_insert_page (notebook, label, NULL, type);
427   gtk_widget_show (label);
428 }
429 
430 static void
shoot_radio_button_toggled(GtkWidget * widget,GtkWidget * notebook)431 shoot_radio_button_toggled (GtkWidget *widget,
432                             GtkWidget *notebook)
433 {
434   gimp_radio_button_update (widget, &shootvals.shoot_type);
435 
436   if (select_delay_table)
437     {
438       if (shootvals.shoot_type == SHOOT_ROOT ||
439           (shootvals.shoot_type == SHOOT_WINDOW &&
440            ! (capabilities & SCREENSHOT_CAN_PICK_WINDOW)))
441         {
442           gtk_widget_hide (select_delay_table);
443         }
444       else
445         {
446           gtk_widget_show (select_delay_table);
447         }
448     }
449   if (shot_delay_table)
450     {
451       if (shootvals.shoot_type == SHOOT_WINDOW        &&
452           (capabilities & SCREENSHOT_CAN_PICK_WINDOW) &&
453           ! (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
454         {
455           gtk_widget_hide (shot_delay_table);
456         }
457       else
458         {
459           gtk_widget_show (shot_delay_table);
460         }
461     }
462   gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), shootvals.shoot_type);
463 }
464 
465 static gboolean
shoot_dialog(GdkScreen ** screen)466 shoot_dialog (GdkScreen **screen)
467 {
468   GtkWidget     *dialog;
469   GtkWidget     *main_vbox;
470   GtkWidget     *notebook1;
471   GtkWidget     *notebook2;
472   GtkWidget     *frame;
473   GtkWidget     *vbox;
474   GtkWidget     *hbox;
475   GtkWidget     *label;
476   GtkWidget     *button;
477   GtkWidget     *toggle;
478   GtkWidget     *spinner;
479   GtkWidget     *table;
480   GSList        *radio_group = NULL;
481   GtkAdjustment *adj;
482   gboolean       run;
483   GtkWidget     *cursor_toggle = NULL;
484 
485   gimp_ui_init (PLUG_IN_BINARY, FALSE);
486 
487   dialog = gimp_dialog_new (_("Screenshot"), PLUG_IN_ROLE,
488                             NULL, 0,
489                             gimp_standard_help_func, PLUG_IN_PROC,
490 
491                             _("_Cancel"), GTK_RESPONSE_CANCEL,
492                             _("S_nap"),   GTK_RESPONSE_OK,
493 
494                             NULL);
495 
496   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
497                                            GTK_RESPONSE_OK,
498                                            GTK_RESPONSE_CANCEL,
499                                            -1);
500 
501   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
502   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
503   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
504                       main_vbox, FALSE, FALSE, 0);
505   gtk_widget_show (main_vbox);
506 
507 
508   /*  Create delay hints notebooks early  */
509   notebook1 = g_object_new (GTK_TYPE_NOTEBOOK,
510                             "show-border", FALSE,
511                             "show-tabs",   FALSE,
512                             NULL);
513   notebook2 = g_object_new (GTK_TYPE_NOTEBOOK,
514                             "show-border", FALSE,
515                             "show-tabs",   FALSE,
516                             NULL);
517 
518   /*  Area  */
519   frame = gimp_frame_new (_("Area"));
520   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
521   gtk_widget_show (frame);
522 
523   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
524   gtk_container_add (GTK_CONTAINER (frame), vbox);
525   gtk_widget_show (vbox);
526 
527   /*  Single window  */
528   if (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)
529     {
530       button = gtk_radio_button_new_with_mnemonic (radio_group,
531                                                    _("Take a screenshot of "
532                                                      "a single _window"));
533       radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
534       gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
535       gtk_widget_show (button);
536 
537       g_object_set_data (G_OBJECT (button), "gimp-item-data",
538                          GINT_TO_POINTER (SHOOT_WINDOW));
539 
540       g_signal_connect (button, "toggled",
541                         G_CALLBACK (shoot_radio_button_toggled),
542                         notebook1);
543       g_signal_connect (button, "toggled",
544                         G_CALLBACK (shoot_radio_button_toggled),
545                         notebook2);
546 
547       /*  Window decorations  */
548       if (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS)
549         {
550           hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
551           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
552           gtk_widget_show (hbox);
553 
554           toggle = gtk_check_button_new_with_mnemonic (_("Include window _decoration"));
555           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
556                                         shootvals.decorate);
557           gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
558           gtk_widget_show (toggle);
559 
560           g_signal_connect (toggle, "toggled",
561                             G_CALLBACK (gimp_toggle_button_update),
562                             &shootvals.decorate);
563 
564           g_object_bind_property (button, "active",
565                                   toggle, "sensitive",
566                                   G_BINDING_SYNC_CREATE);
567         }
568       /*  Mouse pointer  */
569       if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
570         {
571           hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
572           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
573           gtk_widget_show (hbox);
574 
575           cursor_toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
576           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cursor_toggle),
577                                         shootvals.show_cursor);
578           gtk_box_pack_start (GTK_BOX (hbox), cursor_toggle, TRUE, TRUE, 24);
579           gtk_widget_show (cursor_toggle);
580 
581           g_signal_connect (cursor_toggle, "toggled",
582                             G_CALLBACK (gimp_toggle_button_update),
583                             &shootvals.show_cursor);
584 
585           g_object_bind_property (button, "active",
586                                   cursor_toggle, "sensitive",
587                                   G_BINDING_SYNC_CREATE);
588         }
589 
590 
591       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
592                                     shootvals.shoot_type == SHOOT_WINDOW);
593     }
594 
595   /*  Whole screen  */
596   button = gtk_radio_button_new_with_mnemonic (radio_group,
597                                                _("Take a screenshot of "
598                                                  "the entire _screen"));
599   radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
600   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
601   gtk_widget_show (button);
602 
603   g_object_set_data (G_OBJECT (button), "gimp-item-data",
604                      GINT_TO_POINTER (SHOOT_ROOT));
605 
606   g_signal_connect (button, "toggled",
607                     G_CALLBACK (shoot_radio_button_toggled),
608                     notebook1);
609   g_signal_connect (button, "toggled",
610                     G_CALLBACK (shoot_radio_button_toggled),
611                     notebook2);
612 
613   /*  Mouse pointer  */
614   if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
615     {
616       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
617       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
618       gtk_widget_show (hbox);
619 
620       toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
621       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
622                                     shootvals.show_cursor);
623       gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
624       gtk_widget_show (toggle);
625 
626       g_signal_connect (toggle, "toggled",
627                         G_CALLBACK (gimp_toggle_button_update),
628                         &shootvals.show_cursor);
629 
630       if (cursor_toggle)
631         {
632           g_object_bind_property (cursor_toggle, "active",
633                                   toggle, "active",
634                                   G_BINDING_BIDIRECTIONAL);
635         }
636       g_object_bind_property (button, "active",
637                               toggle, "sensitive",
638                               G_BINDING_SYNC_CREATE);
639     }
640 
641   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
642                                 shootvals.shoot_type == SHOOT_ROOT);
643 
644   /*  Dragged region  */
645   if (capabilities & SCREENSHOT_CAN_SHOOT_REGION)
646     {
647       button = gtk_radio_button_new_with_mnemonic (radio_group,
648                                                    _("Select a _region to grab"));
649       radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
650       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
651                                     shootvals.shoot_type == SHOOT_REGION);
652       gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
653       gtk_widget_show (button);
654 
655       g_object_set_data (G_OBJECT (button), "gimp-item-data",
656                          GINT_TO_POINTER (SHOOT_REGION));
657 
658       g_signal_connect (button, "toggled",
659                         G_CALLBACK (shoot_radio_button_toggled),
660                         notebook1);
661       g_signal_connect (button, "toggled",
662                         G_CALLBACK (shoot_radio_button_toggled),
663                         notebook2);
664     }
665 
666   frame = gimp_frame_new (_("Delay"));
667   gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
668   gtk_widget_show (frame);
669 
670   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
671   gtk_container_add (GTK_CONTAINER (frame), vbox);
672   gtk_widget_show (vbox);
673 
674   /* Selection delay  */
675   table = gtk_table_new (2, 3, FALSE);
676   select_delay_table = table;
677   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
678   /* Check if this delay must be hidden from start. */
679   if (shootvals.shoot_type == SHOOT_REGION ||
680       (shootvals.shoot_type == SHOOT_WINDOW &&
681        capabilities & SCREENSHOT_CAN_PICK_WINDOW))
682     {
683       gtk_widget_show (select_delay_table);
684     }
685 
686   label = gtk_label_new (_("Selection delay: "));
687   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
688                     GTK_SHRINK, GTK_SHRINK, 0, 0);
689   gtk_widget_show (label);
690 
691   adj = (GtkAdjustment *)
692     gtk_adjustment_new (shootvals.select_delay,
693                         0.0, 100.0, 1.0, 5.0, 0.0);
694   spinner = gimp_spin_button_new (adj, 0, 0);
695   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
696   gtk_table_attach (GTK_TABLE (table), spinner, 1, 2, 0, 1,
697                     GTK_SHRINK, GTK_SHRINK, 0, 0);
698   gtk_widget_show (spinner);
699 
700   g_signal_connect (adj, "value-changed",
701                     G_CALLBACK (gimp_int_adjustment_update),
702                     &shootvals.select_delay);
703 
704   /*  translators: this is the unit label of a spinbutton  */
705   label = gtk_label_new (_("seconds"));
706   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
707                     GTK_EXPAND | GTK_FILL, GTK_SHRINK, 1.0, 0);
708   gtk_misc_set_alignment (GTK_MISC (label), 0.1, 0.5);
709   gtk_widget_show (label);
710 
711   /*  Selection delay hints  */
712   gtk_table_attach (GTK_TABLE (table), notebook1, 0, 3, 1, 2,
713                     GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
714   gtk_widget_show (notebook1);
715 
716   /* No selection delay for full-screen. */
717   shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_ROOT, "");
718   shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_REGION,
719                          _("After the delay, drag your mouse to select "
720                            "the region for the screenshot."));
721 #ifdef G_OS_WIN32
722   shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
723                          _("Click in a window to snap it after delay."));
724 #else
725   if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
726     {
727       shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
728                              _("At the end of the delay, click in a window "
729                                "to snap it."));
730     }
731   else
732     {
733       shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW, "");
734     }
735 #endif
736   gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook1), shootvals.shoot_type);
737 
738   /* Screenshot delay  */
739   table = gtk_table_new (2, 3, FALSE);
740   shot_delay_table = table;
741   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
742   if (shootvals.shoot_type != SHOOT_WINDOW          ||
743       ! (capabilities & SCREENSHOT_CAN_PICK_WINDOW) ||
744       (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
745     {
746       gtk_widget_show (table);
747     }
748 
749   label = gtk_label_new_with_mnemonic (_("Screenshot dela_y: "));
750   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
751                     GTK_SHRINK, GTK_SHRINK, 0, 0);
752   gtk_widget_show (label);
753 
754   adj = (GtkAdjustment *)
755     gtk_adjustment_new (shootvals.screenshot_delay,
756                         0.0, 100.0, 1.0, 5.0, 0.0);
757   spinner = gimp_spin_button_new (adj, 0, 0);
758   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
759   gtk_table_attach (GTK_TABLE (table), spinner, 1, 2, 0, 1,
760                     GTK_SHRINK, GTK_SHRINK, 0, 0);
761   gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (spinner));
762   gtk_widget_show (spinner);
763 
764   g_signal_connect (adj, "value-changed",
765                     G_CALLBACK (gimp_int_adjustment_update),
766                     &shootvals.screenshot_delay);
767 
768   /*  translators: this is the unit label of a spinbutton  */
769   label = gtk_label_new (_("seconds"));
770   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
771                     GTK_EXPAND | GTK_FILL, GTK_SHRINK, 1.0, 0);
772   gtk_misc_set_alignment (GTK_MISC (label), 0.1, 0.5);
773   gtk_widget_show (label);
774 
775   /*  Screenshot delay hints  */
776   gtk_table_attach (GTK_TABLE (table), notebook2, 0, 3, 1, 2,
777                     GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
778   gtk_widget_show (notebook2);
779 
780   shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_ROOT,
781                          _("After the delay, the screenshot is taken."));
782   shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_REGION,
783                          _("Once the region is selected, it will be "
784                            "captured after this delay."));
785   if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
786     {
787       shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
788                              _("Once the window is selected, it will be "
789                                "captured after this delay."));
790     }
791   else
792     {
793       shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
794                              _("After the delay, the active window "
795                                "will be captured."));
796     }
797   gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook2), shootvals.shoot_type);
798 
799   /*  Color profile  */
800   frame = gimp_int_radio_group_new (TRUE,
801                                     _("Color Profile"),
802                                     G_CALLBACK (gimp_radio_button_update),
803                                     &shootvals.profile_policy,
804                                     shootvals.profile_policy,
805 
806                                     _("Tag image with _monitor profile"),
807                                     SCREENSHOT_PROFILE_POLICY_MONITOR,
808                                     NULL,
809 
810                                     _("Convert image to sR_GB"),
811                                     SCREENSHOT_PROFILE_POLICY_SRGB,
812                                     NULL,
813 
814                                     NULL);
815   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
816   gtk_widget_show (frame);
817 
818 
819   gtk_widget_show (dialog);
820 
821   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
822 
823   if (run)
824     {
825       /* get the screen on which we are running */
826       *screen = gtk_widget_get_screen (dialog);
827     }
828 
829   gtk_widget_destroy (dialog);
830 
831   if (run)
832     {
833       /*  A short timeout to give the server a chance to
834        *  redraw the area that was obscured by our dialog.
835        */
836       g_timeout_add (100, shoot_quit_timeout, NULL);
837       gtk_main ();
838     }
839 
840   return run;
841 }
842 
843 static gboolean
shoot_quit_timeout(gpointer data)844 shoot_quit_timeout (gpointer data)
845 {
846   gtk_main_quit ();
847 
848   return FALSE;
849 }
850 
851 
852 static gboolean
shoot_delay_timeout(gpointer data)853 shoot_delay_timeout (gpointer data)
854 {
855   gint *seconds_left = data;
856 
857   (*seconds_left)--;
858 
859   if (!*seconds_left)
860     gtk_main_quit ();
861 
862   return *seconds_left;
863 }
864 
865 
866 /*  public functions  */
867 
868 void
screenshot_delay(gint seconds)869 screenshot_delay (gint seconds)
870 {
871   g_timeout_add (1000, shoot_delay_timeout, &seconds);
872   gtk_main ();
873 }
874