1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 2009 Martin Nordholts <martinn@src.gnome.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include <gegl.h>
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
24 
25 #include "libgimpbase/gimpbase.h"
26 #include "libgimpmath/gimpmath.h"
27 #include "libgimpwidgets/gimpwidgets.h"
28 
29 #include "dialogs/dialogs-types.h"
30 
31 #include "display/gimpdisplay.h"
32 #include "display/gimpdisplayshell.h"
33 #include "display/gimpdisplayshell-scale.h"
34 #include "display/gimpdisplayshell-transform.h"
35 #include "display/gimpimagewindow.h"
36 
37 #include "widgets/gimpdialogfactory.h"
38 #include "widgets/gimpdock.h"
39 #include "widgets/gimpdockable.h"
40 #include "widgets/gimpdockbook.h"
41 #include "widgets/gimpdockcontainer.h"
42 #include "widgets/gimpdocked.h"
43 #include "widgets/gimpdockwindow.h"
44 #include "widgets/gimphelp-ids.h"
45 #include "widgets/gimpsessioninfo.h"
46 #include "widgets/gimpsessioninfo-aux.h"
47 #include "widgets/gimpsessionmanaged.h"
48 #include "widgets/gimptoolbox.h"
49 #include "widgets/gimptooloptionseditor.h"
50 #include "widgets/gimpuimanager.h"
51 #include "widgets/gimpwidgets-utils.h"
52 
53 #include "core/gimp.h"
54 #include "core/gimpchannel.h"
55 #include "core/gimpcontext.h"
56 #include "core/gimpimage.h"
57 #include "core/gimplayer.h"
58 #include "core/gimplayer-new.h"
59 #include "core/gimptoolinfo.h"
60 #include "core/gimptooloptions.h"
61 
62 #include "tests.h"
63 
64 #include "gimp-app-test-utils.h"
65 
66 
67 #define GIMP_UI_WINDOW_POSITION_EPSILON 30
68 #define GIMP_UI_POSITION_EPSILON        1
69 #define GIMP_UI_ZOOM_EPSILON            0.01
70 
71 #define ADD_TEST(function) \
72   g_test_add_data_func ("/gimp-ui/" #function, gimp, function);
73 
74 
75 /* Put this in the code below when you want the test to pause so you
76  * can do measurements of widgets on the screen for example
77  */
78 #define GIMP_PAUSE (g_usleep (20 * 1000 * 1000))
79 
80 
81 typedef gboolean (*GimpUiTestFunc) (GObject *object);
82 
83 
84 static void            gimp_ui_synthesize_delete_event          (GtkWidget         *widget);
85 static gboolean        gimp_ui_synthesize_click                 (GtkWidget         *widget,
86                                                                  gint               x,
87                                                                  gint               y,
88                                                                  gint               button,
89                                                                  GdkModifierType    modifiers);
90 static GtkWidget     * gimp_ui_find_window                      (GimpDialogFactory *dialog_factory,
91                                                                  GimpUiTestFunc     predicate);
92 static gboolean        gimp_ui_not_toolbox_window               (GObject           *object);
93 static gboolean        gimp_ui_multicolumn_not_toolbox_window   (GObject           *object);
94 static gboolean        gimp_ui_is_gimp_layer_list               (GObject           *object);
95 static int             gimp_ui_aux_data_eqiuvalent              (gconstpointer      _a,
96                                                                  gconstpointer      _b);
97 static void            gimp_ui_switch_window_mode               (Gimp              *gimp);
98 
99 
100 /**
101  * tool_options_editor_updates:
102  * @data:
103  *
104  * Makes sure that the tool options editor is updated when the tool
105  * changes.
106  **/
107 static void
tool_options_editor_updates(gconstpointer data)108 tool_options_editor_updates (gconstpointer data)
109 {
110   Gimp                  *gimp         = GIMP (data);
111   GimpDisplay           *display      = GIMP_DISPLAY (gimp_get_empty_display (gimp));
112   GimpDisplayShell      *shell        = gimp_display_get_shell (display);
113   GtkWidget             *toplevel     = gtk_widget_get_toplevel (GTK_WIDGET (shell));
114   GimpImageWindow       *image_window = GIMP_IMAGE_WINDOW (toplevel);
115   GimpUIManager         *ui_manager   = gimp_image_window_get_ui_manager (image_window);
116   GtkWidget             *dockable     = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
117                                                                         gtk_widget_get_screen (toplevel),
118                                                                         gimp_widget_get_monitor (toplevel),
119                                                                         NULL /*ui_manager*/,
120                                                                         "gimp-tool-options",
121                                                                         -1 /*view_size*/,
122                                                                         FALSE /*present*/);
123   GimpToolOptionsEditor *editor       = GIMP_TOOL_OPTIONS_EDITOR (gtk_bin_get_child (GTK_BIN (dockable)));
124 
125   /* First select the rect select tool */
126   gimp_ui_manager_activate_action (ui_manager,
127                                    "tools",
128                                    "tools-rect-select");
129   g_assert_cmpstr (GIMP_HELP_TOOL_RECT_SELECT,
130                    ==,
131                    gimp_tool_options_editor_get_tool_options (editor)->
132                    tool_info->help_id);
133 
134   /* Change tool and make sure the change is taken into account by the
135    * tool options editor
136    */
137   gimp_ui_manager_activate_action (ui_manager,
138                                    "tools",
139                                    "tools-ellipse-select");
140   g_assert_cmpstr (GIMP_HELP_TOOL_ELLIPSE_SELECT,
141                    ==,
142                    gimp_tool_options_editor_get_tool_options (editor)->
143                    tool_info->help_id);
144 }
145 
146 static GtkWidget *
gimp_ui_get_dialog(const gchar * identifier)147 gimp_ui_get_dialog (const gchar *identifier)
148 {
149   GtkWidget *result = NULL;
150   GList     *iter;
151 
152   for (iter = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ());
153        iter;
154        iter = g_list_next (iter))
155     {
156       GtkWidget *dialog = GTK_WIDGET (iter->data);
157       GimpDialogFactoryEntry *entry = NULL;
158 
159       gimp_dialog_factory_from_widget (dialog, &entry);
160 
161       if (strcmp (entry->identifier, identifier) == 0)
162         {
163           result = dialog;
164           break;
165         }
166     }
167 
168   return result;
169 }
170 
171 static void
automatic_tab_style(gconstpointer data)172 automatic_tab_style (gconstpointer data)
173 {
174   GtkWidget    *channel_dockable = gimp_ui_get_dialog ("gimp-channel-list");
175   GimpDockable *dockable;
176   GimpUIManager *ui_manager;
177   g_assert (channel_dockable != NULL);
178 
179   dockable = GIMP_DOCKABLE (channel_dockable);
180 
181   gimp_test_run_mainloop_until_idle ();
182 
183   /* The channel dockable is the only dockable, it has enough space
184    * for the icon-blurb
185    */
186   g_assert_cmpint (GIMP_TAB_STYLE_ICON_BLURB,
187                    ==,
188                    gimp_dockable_get_actual_tab_style (dockable));
189 
190   /* Add some dockables to the channel dockable dockbook */
191   ui_manager =
192     gimp_dockbook_get_ui_manager (gimp_dockable_get_dockbook (dockable));
193   gimp_ui_manager_activate_action (ui_manager,
194                                    "dockable",
195                                    "dialogs-sample-points");
196   gimp_ui_manager_activate_action (ui_manager,
197                                    "dockable",
198                                    "dialogs-vectors");
199   gimp_test_run_mainloop_until_idle ();
200 
201   /* Now there is not enough space to have icon-blurb for channel
202    * dockable, make sure it's just an icon now
203    */
204   g_assert_cmpint (GIMP_TAB_STYLE_ICON,
205                    ==,
206                    gimp_dockable_get_actual_tab_style (dockable));
207 
208   /* Close the two dockables we added */
209   gimp_ui_manager_activate_action (ui_manager,
210                                    "dockable",
211                                    "dockable-close-tab");
212   gimp_ui_manager_activate_action (ui_manager,
213                                    "dockable",
214                                    "dockable-close-tab");
215   gimp_test_run_mainloop_until_idle ();
216 
217   /* We should now be back on icon-blurb */
218   g_assert_cmpint (GIMP_TAB_STYLE_ICON_BLURB,
219                    ==,
220                    gimp_dockable_get_actual_tab_style (dockable));
221 }
222 
223 static void
create_new_image_via_dialog(gconstpointer data)224 create_new_image_via_dialog (gconstpointer data)
225 {
226   Gimp      *gimp = GIMP (data);
227   GimpImage *image;
228   GimpLayer *layer;
229 
230   image = gimp_test_utils_create_image_from_dialog (gimp);
231 
232   /* Add a layer to the image to make it more useful in later tests */
233   layer = gimp_layer_new (image,
234                           gimp_image_get_width (image),
235                           gimp_image_get_height (image),
236                           gimp_image_get_layer_format (image, TRUE),
237                           "Layer for testing",
238                           GIMP_OPACITY_OPAQUE,
239                           GIMP_LAYER_MODE_NORMAL);
240 
241   gimp_image_add_layer (image, layer,
242                         GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
243   gimp_test_run_mainloop_until_idle ();
244 }
245 
246 static void
keyboard_zoom_focus(gconstpointer data)247 keyboard_zoom_focus (gconstpointer data)
248 {
249   Gimp              *gimp    = GIMP (data);
250   GimpDisplay       *display = GIMP_DISPLAY (gimp_get_display_iter (gimp)->data);
251   GimpDisplayShell  *shell   = gimp_display_get_shell (display);
252   GimpImageWindow   *window  = gimp_display_shell_get_window (shell);
253   gint               image_x;
254   gint               image_y;
255   gint               shell_x_before_zoom;
256   gint               shell_y_before_zoom;
257   gdouble            factor_before_zoom;
258   gint               shell_x_after_zoom;
259   gint               shell_y_after_zoom;
260   gdouble            factor_after_zoom;
261 
262   /* We need to use a point that is within the visible (exposed) part
263    * of the canvas
264    */
265   image_x = 400;
266   image_y = 50;
267 
268   /* Setup zoom focus on the bottom right part of the image. We avoid
269    * 0,0 because that's essentially a particularly easy special case.
270    */
271   gimp_display_shell_transform_xy (shell,
272                                    image_x,
273                                    image_y,
274                                    &shell_x_before_zoom,
275                                    &shell_y_before_zoom);
276   gimp_display_shell_push_zoom_focus_pointer_pos (shell,
277                                                   shell_x_before_zoom,
278                                                   shell_y_before_zoom);
279   factor_before_zoom = gimp_zoom_model_get_factor (shell->zoom);
280 
281   /* Do the zoom */
282   gimp_test_utils_synthesize_key_event (GTK_WIDGET (window), GDK_KEY_plus);
283   gimp_test_run_mainloop_until_idle ();
284 
285   /* Make sure the zoom focus point remained fixed */
286   gimp_display_shell_transform_xy (shell,
287                                    image_x,
288                                    image_y,
289                                    &shell_x_after_zoom,
290                                    &shell_y_after_zoom);
291   factor_after_zoom = gimp_zoom_model_get_factor (shell->zoom);
292 
293   /* First of all make sure a zoom happened at all. If this assert
294    * fails, it means that the zoom didn't happen. Possible causes:
295    *
296    *  * gdk_test_simulate_key() failed to map 'GDK_KEY_plus' to the proper
297    *    'plus' X keysym, probably because it is mapped to a keycode
298    *    with modifiers like 'shift'. Run "xmodmap -pk | grep plus" to
299    *    find out. Make sure 'plus' is the first keysym for the given
300    *    keycode. If not, use "xmodmap <keycode> = plus" to correct it.
301    */
302   g_assert_cmpfloat (fabs (factor_before_zoom - factor_after_zoom),
303                      >=,
304                      GIMP_UI_ZOOM_EPSILON);
305 
306 #ifdef __GNUC__
307 #warning disabled zoom test, it fails randomly, no clue how to fix it
308 #endif
309 #if 0
310   g_assert_cmpint (ABS (shell_x_after_zoom - shell_x_before_zoom),
311                    <=,
312                    GIMP_UI_POSITION_EPSILON);
313   g_assert_cmpint (ABS (shell_y_after_zoom - shell_y_before_zoom),
314                    <=,
315                    GIMP_UI_POSITION_EPSILON);
316 #endif
317 }
318 
319 /**
320  * alt_click_is_layer_to_selection:
321  * @data:
322  *
323  * Makes sure that we can alt-click on a layer to do
324  * layer-to-selection. Also makes sure that the layer clicked on is
325  * not set as the active layer.
326  **/
327 static void
alt_click_is_layer_to_selection(gconstpointer data)328 alt_click_is_layer_to_selection (gconstpointer data)
329 {
330 #if __GNUC__
331 #warning FIXME: please fix alt_click_is_layer_to_selection test
332 #endif
333 #if 0
334   Gimp        *gimp      = GIMP (data);
335   GimpImage   *image     = GIMP_IMAGE (gimp_get_image_iter (gimp)->data);
336   GimpChannel *selection = gimp_image_get_mask (image);
337   GimpLayer   *active_layer;
338   GtkWidget   *dockable;
339   GtkWidget   *gtk_tree_view;
340   gint         assumed_layer_x;
341   gint         assumed_empty_layer_y;
342   gint         assumed_background_layer_y;
343 
344   /* Hardcode assumptions of where the layers are in the
345    * GtkTreeView. Doesn't feel worth adding proper API for this. One
346    * can just use GIMP_PAUSE and re-measure new coordinates if we
347    * start to layout layers in the GtkTreeView differently
348    */
349   assumed_layer_x            = 96;
350   assumed_empty_layer_y      = 16;
351   assumed_background_layer_y = 42;
352 
353   /* Store the active layer, it shall not change during the execution
354    * of this test
355    */
356   active_layer = gimp_image_get_active_layer (image);
357 
358   /* Find the layer tree view to click in. Note that there is a
359    * potential problem with gtk_test_find_widget and GtkNotebook: it
360    * will return e.g. a GtkTreeView from another page if that page is
361    * "on top" of the reference label.
362    */
363   dockable = gimp_ui_find_window (gimp_dialog_factory_get_singleton (),
364                                   gimp_ui_is_gimp_layer_list);
365   gtk_tree_view = gtk_test_find_widget (dockable,
366                                         "Lock:",
367                                         GTK_TYPE_TREE_VIEW);
368 
369   /* First make sure there is no selection */
370   g_assert (gimp_channel_is_empty (selection));
371 
372   /* Now simulate alt-click on the background layer */
373   g_assert (gimp_ui_synthesize_click (gtk_tree_view,
374                                       assumed_layer_x,
375                                       assumed_background_layer_y,
376                                       1 /*button*/,
377                                       GDK_MOD1_MASK));
378   gimp_test_run_mainloop_until_idle ();
379 
380   /* Make sure we got a selection and that the active layer didn't
381    * change
382    */
383   g_assert (! gimp_channel_is_empty (selection));
384   g_assert (gimp_image_get_active_layer (image) == active_layer);
385 
386   /* Now simulate alt-click on the empty layer */
387   g_assert (gimp_ui_synthesize_click (gtk_tree_view,
388                                       assumed_layer_x,
389                                       assumed_empty_layer_y,
390                                       1 /*button*/,
391                                       GDK_MOD1_MASK));
392   gimp_test_run_mainloop_until_idle ();
393 
394   /* Make sure that emptied the selection and that the active layer
395    * still didn't change
396    */
397   g_assert (gimp_channel_is_empty (selection));
398   g_assert (gimp_image_get_active_layer (image) == active_layer);
399 #endif
400 }
401 
402 static void
restore_recently_closed_multi_column_dock(gconstpointer data)403 restore_recently_closed_multi_column_dock (gconstpointer data)
404 {
405   Gimp      *gimp                          = GIMP (data);
406   GtkWidget *dock_window                   = NULL;
407   gint       n_session_infos_before_close  = -1;
408   gint       n_session_infos_after_close   = -1;
409   gint       n_session_infos_after_restore = -1;
410   GList     *session_infos                 = NULL;
411 
412   /* Find a non-toolbox dock window */
413   dock_window = gimp_ui_find_window (gimp_dialog_factory_get_singleton (),
414                                      gimp_ui_multicolumn_not_toolbox_window);
415   g_assert (dock_window != NULL);
416 
417   /* Count number of docks */
418   session_infos = gimp_dialog_factory_get_session_infos (gimp_dialog_factory_get_singleton ());
419   n_session_infos_before_close = g_list_length (session_infos);
420 
421   /* Close one of the dock windows */
422   gimp_ui_synthesize_delete_event (GTK_WIDGET (dock_window));
423   gimp_test_run_mainloop_until_idle ();
424 
425   /* Make sure the number of session infos went down */
426   session_infos = gimp_dialog_factory_get_session_infos (gimp_dialog_factory_get_singleton ());
427   n_session_infos_after_close = g_list_length (session_infos);
428   g_assert_cmpint (n_session_infos_before_close,
429                    >,
430                    n_session_infos_after_close);
431 
432 #ifdef __GNUC__
433 #warning FIXME test disabled until we depend on GTK+ >= 2.24.11
434 #endif
435 #if 0
436   /* Restore the (only available) closed dock and make sure the session
437    * infos in the global dock factory are increased again
438    */
439   gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
440                                    "windows",
441                                    /* FIXME: This is severely hardcoded */
442                                    "windows-recent-0003");
443   gimp_test_run_mainloop_until_idle ();
444   session_infos = gimp_dialog_factory_get_session_infos (gimp_dialog_factory_get_singleton ());
445   n_session_infos_after_restore = g_list_length (session_infos);
446   g_assert_cmpint (n_session_infos_after_close,
447                    <,
448                    n_session_infos_after_restore);
449 #endif
450 }
451 
452 /**
453  * tab_toggle_dont_change_dock_window_position:
454  * @data:
455  *
456  * Makes sure that when dock windows are hidden with Tab and shown
457  * again, their positions and sizes are not changed. We don't really
458  * use Tab though, we only simulate its effect.
459  **/
460 static void
tab_toggle_dont_change_dock_window_position(gconstpointer data)461 tab_toggle_dont_change_dock_window_position (gconstpointer data)
462 {
463   Gimp      *gimp          = GIMP (data);
464   GtkWidget *dock_window   = NULL;
465   gint       x_before_hide = -1;
466   gint       y_before_hide = -1;
467   gint       w_before_hide = -1;
468   gint       h_before_hide = -1;
469   gint       x_after_show  = -1;
470   gint       y_after_show  = -1;
471   gint       w_after_show  = -1;
472   gint       h_after_show  = -1;
473 
474   /* Find a non-toolbox dock window */
475   dock_window = gimp_ui_find_window (gimp_dialog_factory_get_singleton (),
476                                      gimp_ui_not_toolbox_window);
477   g_assert (dock_window != NULL);
478   g_assert (gtk_widget_get_visible (dock_window));
479 
480   /* Get the position and size */
481   gimp_test_run_mainloop_until_idle ();
482   gtk_window_get_position (GTK_WINDOW (dock_window),
483                            &x_before_hide,
484                            &y_before_hide);
485   gtk_window_get_size (GTK_WINDOW (dock_window),
486                        &w_before_hide,
487                        &h_before_hide);
488 
489   /* Hide all dock windows */
490   gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
491                                    "windows",
492                                    "windows-hide-docks");
493   gimp_test_run_mainloop_until_idle ();
494   g_assert (! gtk_widget_get_visible (dock_window));
495 
496   /* Show them again */
497   gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
498                                    "windows",
499                                    "windows-hide-docks");
500   gimp_test_run_mainloop_until_idle ();
501   g_assert (gtk_widget_get_visible (dock_window));
502 
503   /* Get the position and size again and make sure it's the same as
504    * before
505    */
506   gtk_window_get_position (GTK_WINDOW (dock_window),
507                            &x_after_show,
508                            &y_after_show);
509   gtk_window_get_size (GTK_WINDOW (dock_window),
510                        &w_after_show,
511                        &h_after_show);
512   g_assert_cmpint ((int)abs (x_before_hide - x_after_show), <=, GIMP_UI_WINDOW_POSITION_EPSILON);
513   g_assert_cmpint ((int)abs (y_before_hide - y_after_show), <=, GIMP_UI_WINDOW_POSITION_EPSILON);
514   g_assert_cmpint ((int)abs (w_before_hide - w_after_show), <=, GIMP_UI_WINDOW_POSITION_EPSILON);
515   g_assert_cmpint ((int)abs (h_before_hide - h_after_show), <=, GIMP_UI_WINDOW_POSITION_EPSILON);
516 }
517 
518 static void
switch_to_single_window_mode(gconstpointer data)519 switch_to_single_window_mode (gconstpointer data)
520 {
521   Gimp *gimp = GIMP (data);
522 
523   /* Switch to single-window mode. We consider this test as passed if
524    * we don't get any GLib warnings/errors
525    */
526   gimp_ui_switch_window_mode (gimp);
527 }
528 
529 static void
gimp_ui_toggle_docks_in_single_window_mode(Gimp * gimp)530 gimp_ui_toggle_docks_in_single_window_mode (Gimp *gimp)
531 {
532   GimpDisplay      *display       = GIMP_DISPLAY (gimp_get_display_iter (gimp)->data);
533   GimpDisplayShell *shell         = gimp_display_get_shell (display);
534   GtkWidget        *toplevel      = GTK_WIDGET (gimp_display_shell_get_window (shell));
535   gint              x_temp        = -1;
536   gint              y_temp        = -1;
537   gint              x_before_hide = -1;
538   gint              y_before_hide = -1;
539   gint              x_after_hide  = -1;
540   gint              y_after_hide  = -1;
541   g_assert (shell);
542   g_assert (toplevel);
543 
544   /* Get toplevel coordinate of image origin */
545   gimp_test_run_mainloop_until_idle ();
546   gimp_display_shell_transform_xy (shell,
547                                    0.0, 0.0,
548                                    &x_temp, &y_temp);
549   gtk_widget_translate_coordinates (GTK_WIDGET (shell),
550                                     toplevel,
551                                     x_temp, y_temp,
552                                     &x_before_hide, &y_before_hide);
553 
554   /* Hide all dock windows */
555   gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
556                                    "windows",
557                                    "windows-hide-docks");
558   gimp_test_run_mainloop_until_idle ();
559 
560   /* Get toplevel coordinate of image origin */
561   gimp_test_run_mainloop_until_idle ();
562   gimp_display_shell_transform_xy (shell,
563                                    0.0, 0.0,
564                                    &x_temp, &y_temp);
565   gtk_widget_translate_coordinates (GTK_WIDGET (shell),
566                                     toplevel,
567                                     x_temp, y_temp,
568                                     &x_after_hide, &y_after_hide);
569 
570   g_assert_cmpint ((int)abs (x_after_hide - x_before_hide), <=, GIMP_UI_POSITION_EPSILON);
571   g_assert_cmpint ((int)abs (y_after_hide - y_before_hide), <=, GIMP_UI_POSITION_EPSILON);
572 }
573 
574 static void
hide_docks_in_single_window_mode(gconstpointer data)575 hide_docks_in_single_window_mode (gconstpointer data)
576 {
577   Gimp *gimp = GIMP (data);
578   gimp_ui_toggle_docks_in_single_window_mode (gimp);
579 }
580 
581 static void
show_docks_in_single_window_mode(gconstpointer data)582 show_docks_in_single_window_mode (gconstpointer data)
583 {
584   Gimp *gimp = GIMP (data);
585   gimp_ui_toggle_docks_in_single_window_mode (gimp);
586 }
587 
588 static void
maximize_state_in_aux_data(gconstpointer data)589 maximize_state_in_aux_data (gconstpointer data)
590 {
591   Gimp               *gimp    = GIMP (data);
592   GimpDisplay        *display = GIMP_DISPLAY (gimp_get_display_iter (gimp)->data);
593   GimpDisplayShell   *shell   = gimp_display_get_shell (display);
594   GimpImageWindow    *window  = gimp_display_shell_get_window (shell);
595   gint                i;
596 
597   for (i = 0; i < 2; i++)
598     {
599       GList              *aux_info = NULL;
600       GimpSessionInfoAux *target_info;
601       gboolean            target_max_state;
602 
603       if (i == 0)
604         {
605           target_info = gimp_session_info_aux_new ("maximized" , "yes");
606           target_max_state = TRUE;
607         }
608       else
609         {
610           target_info = gimp_session_info_aux_new ("maximized", "no");
611           target_max_state = FALSE;
612         }
613 
614       /* Set the aux info to out target data */
615       aux_info = g_list_append (aux_info, target_info);
616       gimp_session_managed_set_aux_info (GIMP_SESSION_MANAGED (window), aux_info);
617       g_list_free (aux_info);
618 
619       /* Give the WM a chance to maximize/unmaximize us */
620       gimp_test_run_mainloop_until_idle ();
621       g_usleep (500 * 1000);
622       gimp_test_run_mainloop_until_idle ();
623 
624       /* Make sure the maximize/unmaximize happened */
625       g_assert (gimp_image_window_is_maximized (window) == target_max_state);
626 
627       /* Make sure we can read out the window state again */
628       aux_info = gimp_session_managed_get_aux_info (GIMP_SESSION_MANAGED (window));
629       g_assert (g_list_find_custom (aux_info, target_info, gimp_ui_aux_data_eqiuvalent));
630       g_list_free_full (aux_info,
631                         (GDestroyNotify) gimp_session_info_aux_free);
632 
633       gimp_session_info_aux_free (target_info);
634     }
635 }
636 
637 static void
switch_back_to_multi_window_mode(gconstpointer data)638 switch_back_to_multi_window_mode (gconstpointer data)
639 {
640   Gimp *gimp = GIMP (data);
641 
642   /* Switch back to multi-window mode. We consider this test as passed
643    * if we don't get any GLib warnings/errors
644    */
645   gimp_ui_switch_window_mode (gimp);
646 }
647 
648 static void
close_image(gconstpointer data)649 close_image (gconstpointer data)
650 {
651   Gimp *gimp       = GIMP (data);
652   int   undo_count = 4;
653 
654   /* Undo all changes so we don't need to find the 'Do you want to
655    * save?'-dialog and its 'No' button
656    */
657   while (undo_count--)
658     {
659       gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
660                                        "edit",
661                                        "edit-undo");
662       gimp_test_run_mainloop_until_idle ();
663     }
664 
665   /* Close the image */
666   gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
667                                    "view",
668                                    "view-close");
669   gimp_test_run_mainloop_until_idle ();
670 
671   /* Did it really disappear? */
672   g_assert_cmpint (g_list_length (gimp_get_image_iter (gimp)), ==, 0);
673 }
674 
675 /**
676  * repeatedly_switch_window_mode:
677  * @data:
678  *
679  * Makes sure that the size of the image window is properly handled
680  * when repeatedly switching between window modes.
681  **/
682 static void
repeatedly_switch_window_mode(gconstpointer data)683 repeatedly_switch_window_mode (gconstpointer data)
684 {
685 #ifdef __GNUC__
686 #warning FIXME: plesase fix repeatedly_switch_window_mode test
687 #endif
688 #if 0
689   Gimp             *gimp     = GIMP (data);
690   GimpDisplay      *display  = GIMP_DISPLAY (gimp_get_empty_display (gimp));
691   GimpDisplayShell *shell    = gimp_display_get_shell (display);
692   GtkWidget        *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
693 
694   gint expected_initial_height;
695   gint expected_initial_width;
696   gint expected_second_height;
697   gint expected_second_width;
698   gint initial_width;
699   gint initial_height;
700   gint second_width;
701   gint second_height;
702 
703   /* We need this for some reason */
704   gimp_test_run_mainloop_until_idle ();
705 
706   /* Remember the multi-window mode size */
707   gtk_window_get_size (GTK_WINDOW (toplevel),
708                        &expected_initial_width,
709                        &expected_initial_height);
710 
711   /* Switch to single-window mode */
712   gimp_ui_switch_window_mode (gimp);
713 
714   /* Remember the single-window mode size */
715   gtk_window_get_size (GTK_WINDOW (toplevel),
716                        &expected_second_width,
717                        &expected_second_height);
718 
719   /* Make sure they differ, otherwise the test is pointless */
720   g_assert_cmpint (expected_initial_width,  !=, expected_second_width);
721   g_assert_cmpint (expected_initial_height, !=, expected_second_height);
722 
723   /* Switch back to multi-window mode */
724   gimp_ui_switch_window_mode (gimp);
725 
726   /* Make sure the size is the same as before */
727   gtk_window_get_size (GTK_WINDOW (toplevel), &initial_width, &initial_height);
728   g_assert_cmpint (expected_initial_width,  ==, initial_width);
729   g_assert_cmpint (expected_initial_height, ==, initial_height);
730 
731   /* Switch to single-window mode again... */
732   gimp_ui_switch_window_mode (gimp);
733 
734   /* Make sure the size is the same as before */
735   gtk_window_get_size (GTK_WINDOW (toplevel), &second_width, &second_height);
736   g_assert_cmpint (expected_second_width,  ==, second_width);
737   g_assert_cmpint (expected_second_height, ==, second_height);
738 
739   /* Finally switch back to multi-window mode since that was the mode
740    * when we started
741    */
742   gimp_ui_switch_window_mode (gimp);
743 #endif
744 }
745 
746 /**
747  * window_roles:
748  * @data:
749  *
750  * Makes sure that different windows have the right roles specified.
751  **/
752 static void
window_roles(gconstpointer data)753 window_roles (gconstpointer data)
754 {
755   GtkWidget      *dock           = NULL;
756   GtkWidget      *toolbox        = NULL;
757   GimpDockWindow *dock_window    = NULL;
758   GimpDockWindow *toolbox_window = NULL;
759 
760   dock           = gimp_dock_with_window_new (gimp_dialog_factory_get_singleton (),
761                                               gdk_screen_get_default (),
762                                               0,
763                                               FALSE /*toolbox*/);
764   toolbox        = gimp_dock_with_window_new (gimp_dialog_factory_get_singleton (),
765                                               gdk_screen_get_default (),
766                                               0,
767                                               TRUE /*toolbox*/);
768   dock_window    = gimp_dock_window_from_dock (GIMP_DOCK (dock));
769   toolbox_window = gimp_dock_window_from_dock (GIMP_DOCK (toolbox));
770 
771   g_assert_cmpint (g_str_has_prefix (gtk_window_get_role (GTK_WINDOW (dock_window)), "gimp-dock-"), ==,
772                    TRUE);
773   g_assert_cmpint (g_str_has_prefix (gtk_window_get_role (GTK_WINDOW (toolbox_window)), "gimp-toolbox-"), ==,
774                    TRUE);
775 
776   /* When we get here we have a ref count of one, but the signals we
777    * emit cause the reference count to become less than zero for some
778    * reason. So we're lazy and simply ignore to unref these
779   g_object_unref (toolbox);
780   g_object_unref (dock);
781    */
782 }
783 
784 static void
paintbrush_is_standard_tool(gconstpointer data)785 paintbrush_is_standard_tool (gconstpointer data)
786 {
787   Gimp         *gimp         = GIMP (data);
788   GimpContext  *user_context = gimp_get_user_context (gimp);
789   GimpToolInfo *tool_info    = gimp_context_get_tool (user_context);
790 
791   g_assert_cmpstr (tool_info->help_id,
792                    ==,
793                    "gimp-tool-paintbrush");
794 }
795 
796 /**
797  * gimp_ui_synthesize_delete_event:
798  * @widget:
799  *
800  * Synthesize a delete event to @widget.
801  **/
802 static void
gimp_ui_synthesize_delete_event(GtkWidget * widget)803 gimp_ui_synthesize_delete_event (GtkWidget *widget)
804 {
805   GdkWindow *window = NULL;
806   GdkEvent *event = NULL;
807 
808   window = gtk_widget_get_window (widget);
809   g_assert (window);
810 
811   event = gdk_event_new (GDK_DELETE);
812   event->any.window     = g_object_ref (window);
813   event->any.send_event = TRUE;
814   gtk_main_do_event (event);
815   gdk_event_free (event);
816 }
817 
818 static gboolean
gimp_ui_synthesize_click(GtkWidget * widget,gint x,gint y,gint button,GdkModifierType modifiers)819 gimp_ui_synthesize_click (GtkWidget       *widget,
820                           gint             x,
821                           gint             y,
822                           gint             button, /*1..3*/
823                           GdkModifierType  modifiers)
824 {
825   return (gdk_test_simulate_button (gtk_widget_get_window (widget),
826                                     x, y,
827                                     button,
828                                     modifiers,
829                                     GDK_BUTTON_PRESS) &&
830           gdk_test_simulate_button (gtk_widget_get_window (widget),
831                                     x, y,
832                                     button,
833                                     modifiers,
834                                     GDK_BUTTON_RELEASE));
835 }
836 
837 static GtkWidget *
gimp_ui_find_window(GimpDialogFactory * dialog_factory,GimpUiTestFunc predicate)838 gimp_ui_find_window (GimpDialogFactory *dialog_factory,
839                      GimpUiTestFunc     predicate)
840 {
841   GList     *iter        = NULL;
842   GtkWidget *dock_window = NULL;
843 
844   g_return_val_if_fail (predicate != NULL, NULL);
845 
846   for (iter = gimp_dialog_factory_get_session_infos (dialog_factory);
847        iter;
848        iter = g_list_next (iter))
849     {
850       GtkWidget *widget = gimp_session_info_get_widget (iter->data);
851 
852       if (predicate (G_OBJECT (widget)))
853         {
854           dock_window = widget;
855           break;
856         }
857     }
858 
859   return dock_window;
860 }
861 
862 static gboolean
gimp_ui_not_toolbox_window(GObject * object)863 gimp_ui_not_toolbox_window (GObject *object)
864 {
865   return (GIMP_IS_DOCK_WINDOW (object) &&
866           ! gimp_dock_window_has_toolbox (GIMP_DOCK_WINDOW (object)));
867 }
868 
869 static gboolean
gimp_ui_multicolumn_not_toolbox_window(GObject * object)870 gimp_ui_multicolumn_not_toolbox_window (GObject *object)
871 {
872   gboolean           not_toolbox_window;
873   GimpDockWindow    *dock_window;
874   GimpDockContainer *dock_container;
875   GList             *docks;
876 
877   if (! GIMP_IS_DOCK_WINDOW (object))
878     return FALSE;
879 
880   dock_window    = GIMP_DOCK_WINDOW (object);
881   dock_container = GIMP_DOCK_CONTAINER (object);
882   docks          = gimp_dock_container_get_docks (dock_container);
883 
884   not_toolbox_window = (! gimp_dock_window_has_toolbox (dock_window) &&
885                         g_list_length (docks) > 1);
886 
887   g_list_free (docks);
888 
889   return not_toolbox_window;
890 }
891 
892 static gboolean
gimp_ui_is_gimp_layer_list(GObject * object)893 gimp_ui_is_gimp_layer_list (GObject *object)
894 {
895   GimpDialogFactoryEntry *entry = NULL;
896 
897   if (! GTK_IS_WIDGET (object))
898     return FALSE;
899 
900   gimp_dialog_factory_from_widget (GTK_WIDGET (object), &entry);
901 
902   return strcmp (entry->identifier, "gimp-layer-list") == 0;
903 }
904 
905 static int
gimp_ui_aux_data_eqiuvalent(gconstpointer _a,gconstpointer _b)906 gimp_ui_aux_data_eqiuvalent (gconstpointer _a, gconstpointer _b)
907 {
908   GimpSessionInfoAux *a = (GimpSessionInfoAux*) _a;
909   GimpSessionInfoAux *b = (GimpSessionInfoAux*) _b;
910   return (strcmp (a->name, b->name) || strcmp (a->value, b->value));
911 }
912 
913 static void
gimp_ui_switch_window_mode(Gimp * gimp)914 gimp_ui_switch_window_mode (Gimp *gimp)
915 {
916   gimp_ui_manager_activate_action (gimp_test_utils_get_ui_manager (gimp),
917                                    "windows",
918                                    "windows-use-single-window-mode");
919   gimp_test_run_mainloop_until_idle ();
920 
921   /* Add a small sleep to let things stabilize */
922   g_usleep (500 * 1000);
923   gimp_test_run_mainloop_until_idle ();
924 }
925 
main(int argc,char ** argv)926 int main(int argc, char **argv)
927 {
928   Gimp *gimp   = NULL;
929   gint  result = -1;
930 
931   gimp_test_bail_if_no_display ();
932   gtk_test_init (&argc, &argv, NULL);
933 
934   gimp_test_utils_set_gimp2_directory ("GIMP_TESTING_ABS_TOP_SRCDIR",
935                                        "app/tests/gimpdir");
936   gimp_test_utils_setup_menus_path ();
937 
938   /* Start up GIMP */
939   gimp = gimp_init_for_gui_testing (TRUE /*show_gui*/);
940   gimp_test_run_mainloop_until_idle ();
941 
942   /* Add tests. Note that the order matters. For example,
943    * 'paintbrush_is_standard_tool' can't be after
944    * 'tool_options_editor_updates'
945    */
946   ADD_TEST (paintbrush_is_standard_tool);
947   ADD_TEST (tool_options_editor_updates);
948   ADD_TEST (automatic_tab_style);
949   ADD_TEST (create_new_image_via_dialog);
950   ADD_TEST (keyboard_zoom_focus);
951   ADD_TEST (alt_click_is_layer_to_selection);
952   ADD_TEST (restore_recently_closed_multi_column_dock);
953   ADD_TEST (tab_toggle_dont_change_dock_window_position);
954   ADD_TEST (switch_to_single_window_mode);
955 #warning FIXME: hide/show docks doesn't work when running make check
956 #if 0
957   ADD_TEST (hide_docks_in_single_window_mode);
958   ADD_TEST (show_docks_in_single_window_mode);
959 #endif
960 #warning FIXME: maximize_state_in_aux_data doesn't work without WM
961 #if 0
962   ADD_TEST (maximize_state_in_aux_data);
963 #endif
964   ADD_TEST (switch_back_to_multi_window_mode);
965   ADD_TEST (close_image);
966   ADD_TEST (repeatedly_switch_window_mode);
967   ADD_TEST (window_roles);
968 
969   /* Run the tests and return status */
970   result = g_test_run ();
971 
972   /* Don't write files to the source dir */
973   gimp_test_utils_set_gimp2_directory ("GIMP_TESTING_ABS_TOP_BUILDDIR",
974                                        "app/tests/gimpdir-output");
975 
976   /* Exit properly so we don't break script-fu plug-in wire */
977   gimp_exit (gimp, TRUE);
978 
979   return result;
980 }
981