1 /*
2  * Copyright (C) 2018-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Zrythm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 /**
21  * \file
22  *
23  * GTK utils.
24  */
25 
26 #ifndef __UTILS_GTK_H__
27 #define __UTILS_GTK_H__
28 
29 #include <stdbool.h>
30 
31 #include <gtk/gtk.h>
32 
33 #pragma GCC diagnostic push
34 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
35 #include <gtksourceview/gtksource.h>
36 #pragma GCC diagnostic pop
37 
38 /**
39  * @addtogroup utils
40  *
41  * @{
42  */
43 
44 #define DESTROY_LATER(x) \
45   g_idle_add ( \
46     (GSourceFunc) z_gtk_widget_destroy_idle, \
47       GTK_WIDGET (x));
48 
49 #define DEFAULT_CLIPBOARD \
50   gtk_clipboard_get_default ( \
51     gdk_display_get_default ())
52 
53 #define CREATE_MIDI_LEARN_MENU_ITEM \
54   z_gtk_create_menu_item ( \
55     _("MIDI learn"), "midi-logo", false, NULL)
56 
57 #define CREATE_CUT_MENU_ITEM(action) \
58   z_gtk_create_menu_item ( \
59     _("Cu_t"), "edit-cut", false, action)
60 
61 #define CREATE_COPY_MENU_ITEM(action) \
62   z_gtk_create_menu_item ( \
63     _("_Copy"), "edit-copy", false, action)
64 
65 #define CREATE_PASTE_MENU_ITEM(action) \
66   z_gtk_create_menu_item ( \
67     _("_Paste"), "edit-paste", false, action)
68 
69 #define CREATE_DELETE_MENU_ITEM(action) \
70   z_gtk_create_menu_item ( \
71     _("_Delete"), "edit-delete", false, action)
72 
73 #define CREATE_CLEAR_SELECTION_MENU_ITEM(action) \
74   z_gtk_create_menu_item ( \
75     /* TRANSLATORS: deselects everything */ \
76     _("Cle_ar Selection"), "edit-clear", \
77     false, action)
78 
79 #define CREATE_SELECT_ALL_MENU_ITEM(action) \
80   z_gtk_create_menu_item ( \
81     _("Select A_ll"), "edit-select-all", \
82     false, action)
83 
84 #define CREATE_DUPLICATE_MENU_ITEM(action) \
85   z_gtk_create_menu_item ( \
86     _("Duplicate"), "edit-duplicate", false, action)
87 
88 #define CREATE_MUTE_MENU_ITEM(action) \
89   z_gtk_create_menu_item ( \
90     _("Mute"), "mute", false, action)
91 
92 #define CREATE_UNMUTE_MENU_ITEM(action) \
93   z_gtk_create_menu_item ( \
94     _("Unmute"), NULL, false, action)
95 
96 #define z_gtk_assistant_set_current_page_complete( \
97   assistant, complete) \
98   gtk_assistant_set_page_complete ( \
99     GTK_ASSISTANT (assistant), \
100     gtk_assistant_get_nth_page ( \
101       GTK_ASSISTANT (assistant), \
102       gtk_assistant_get_current_page ( \
103         GTK_ASSISTANT (assistant))), \
104     complete);
105 
106 typedef enum IconType IconType;
107 
108 enum ZGtkResize
109 {
110   Z_GTK_NO_RESIZE,
111   Z_GTK_RESIZE
112 };
113 
114 enum ZGtkShrink
115 {
116   Z_GTK_NO_SHRINK,
117   Z_GTK_SHRINK
118 };
119 
120 static inline GtkWidget *
z_gtk_notebook_get_current_page_widget(GtkNotebook * notebook)121 z_gtk_notebook_get_current_page_widget (
122   GtkNotebook * notebook)
123 {
124   return
125     gtk_notebook_get_nth_page (
126       notebook,
127       gtk_notebook_get_current_page (notebook));
128 }
129 
130 static inline GtkWidget *
z_gtk_notebook_get_current_tab_label_widget(GtkNotebook * notebook)131 z_gtk_notebook_get_current_tab_label_widget (
132   GtkNotebook * notebook)
133 {
134   return
135     gtk_notebook_get_tab_label (
136       notebook,
137       z_gtk_notebook_get_current_page_widget (
138         notebook));
139 }
140 
141 int
142 z_gtk_get_primary_monitor_scale_factor (void);
143 
144 int
145 z_gtk_get_primary_monitor_refresh_rate (void);
146 
147 bool
148 z_gtk_is_wayland (void);
149 
150 void
151 z_gtk_tree_view_remove_all_columns (
152   GtkTreeView * treeview);
153 
154 int
155 z_gtk_widget_destroy_idle (
156   GtkWidget * widget);
157 
158 /**
159  * @note Bumps reference, must be decremented after
160  * calling.
161  */
162 void
163 z_gtk_container_remove_all_children (
164   GtkContainer * container);
165 
166 void
167 z_gtk_container_destroy_all_children (
168   GtkContainer * container);
169 
170 void
171 z_gtk_container_remove_children_of_type (
172   GtkContainer * container,
173   GType          type);
174 
175 void
176 z_gtk_overlay_add_if_not_exists (
177   GtkOverlay * overlay,
178   GtkWidget *  widget);
179 
180 /**
181  * Returns the primary or secondary label of the
182  * given GtkMessageDialog.
183  *
184  * @param secondary 0 for primary, 1 for secondary.
185  */
186 GtkLabel *
187 z_gtk_message_dialog_get_label (
188   GtkMessageDialog * self,
189   const int          secondary);
190 
191 /**
192  * Configures a simple value-text combo box using
193  * the given model.
194  */
195 void
196 z_gtk_configure_simple_combo_box (
197   GtkComboBox * cb,
198   GtkTreeModel * model);
199 
200 /**
201  * Sets the icon name and optionally text.
202  */
203 void
204 z_gtk_button_set_icon_name (
205   GtkButton *  btn,
206   const char * name);
207 
208 /**
209  * Sets the icon name and optionally text.
210  */
211 void
212 z_gtk_button_set_icon_name_and_text (
213   GtkButton *  btn,
214   const char * name,
215   const char * text,
216   bool         icon_first,
217   GtkOrientation orientation,
218   int          spacing);
219 
220 /**
221  * Creates a button with the given icon name.
222  */
223 GtkButton *
224 z_gtk_button_new_with_icon (
225   const char * name);
226 
227 /**
228  * Creates a toggle button with the given icon name.
229  */
230 GtkToggleButton *
231 z_gtk_toggle_button_new_with_icon (
232   const char * name);
233 
234 /**
235  * Creates a toggle button with the given icon name.
236  */
237 GtkToggleButton *
238 z_gtk_toggle_button_new_with_icon_and_text (
239   const char * name,
240   const char * text,
241   bool         icon_first,
242   GtkOrientation orientation,
243   int          spacing);
244 
245 /**
246  * Creates a button with the given icon name and
247  * text.
248  */
249 GtkButton *
250 z_gtk_button_new_with_icon_and_text (
251   const char * name,
252   const char * text,
253   bool         icon_first,
254   GtkOrientation orientation,
255   int          spacing);
256 
257 /**
258  * Creates a button with the given resource name as icon.
259  */
260 GtkButton *
261 z_gtk_button_new_with_resource (
262   IconType  icon_type,
263   const char * name);
264 
265 /**
266  * Creates a toggle button with the given resource name as
267  * icon.
268  */
269 GtkToggleButton *
270 z_gtk_toggle_button_new_with_resource (
271   IconType  icon_type,
272   const char * name);
273 
274 #define z_gtk_create_menu_item( \
275   lbl_name,icn_name,is_toggle,action_name) \
276   z_gtk_create_menu_item_full ( \
277     lbl_name, icn_name, 0, NULL, is_toggle, \
278     action_name)
279 
280 /**
281  * Creates a menu item.
282  */
283 GtkMenuItem *
284 z_gtk_create_menu_item_full (
285   const gchar *   label_name,
286   const gchar *   icon_name,
287   IconType        resource_icon_type,
288   const gchar *   resource,
289   bool            is_toggle,
290   const char *    action_name);
291 
292 /**
293  * Returns a pointer stored at the given selection.
294  */
295 void *
296 z_gtk_get_single_selection_pointer (
297   GtkTreeView * tv,
298   int           column);
299 
300 /**
301  * Returns the label from a given GtkMenuItem.
302  *
303  * The menu item must have a box with an optional
304  * icon and a label inside.
305  */
306 GtkLabel *
307 z_gtk_get_label_from_menu_item (
308   GtkMenuItem * mi);
309 
310 /**
311  * Gets the tooltip for the given action on the
312  * given widget.
313  *
314  * If the action is valid, an orange text showing
315  * the accelerator will be added to the tooltip.
316  *
317  * @return A new string that must be free'd with
318  *   g_free().
319  */
320 char *
321 z_gtk_get_tooltip_for_action (
322   const char * detailed_action,
323   const char * tooltip);
324 
325 /**
326  * Sets the tooltip for the given action on the
327  * given widget.
328  *
329  * If the action is valid, an orange text showing
330  * the accelerator will be added to the tooltip.
331  */
332 void
333 z_gtk_widget_set_tooltip_for_action (
334   GtkWidget *  widget,
335   const char * detailed_action,
336   const char * tooltip);
337 
338 /**
339  * Sets the tooltip and finds the accel keys and
340  * appends them to the tooltip in small text.
341  */
342 void
343 z_gtk_set_tooltip_for_actionable (
344   GtkActionable * actionable,
345   const char *    tooltip);
346 
347 /**
348  * Changes the size of the icon inside tool buttons.
349  */
350 void
351 z_gtk_tool_button_set_icon_size (
352   GtkToolButton * toolbutton,
353   GtkIconSize     icon_size);
354 
355 /**
356  * Adds the given style class to the widget.
357  */
358 void
359 z_gtk_widget_add_style_class (
360   GtkWidget   *widget,
361   const gchar *class_name);
362 
363 /**
364  * Removes the given style class from the widget.
365  */
366 void
367 z_gtk_widget_remove_style_class (
368   GtkWidget   *widget,
369   const gchar *class_name);
370 
371 /**
372  * Gets the GdkDevice for a GtkWidget.
373  */
374 static inline GdkDevice *
z_gtk_widget_get_device(GtkWidget * widget)375 z_gtk_widget_get_device (
376   GtkWidget * widget)
377 {
378   return (gdk_seat_get_pointer (
379     gdk_display_get_default_seat (
380       gtk_widget_get_display (widget))));
381 }
382 
383 static inline GdkScreen *
z_gtk_widget_get_screen(GtkWidget * widget)384 z_gtk_widget_get_screen (
385   GtkWidget * widget)
386 {
387   GdkScreen * screen =
388     gdk_visual_get_screen (
389       gdk_window_get_visual (
390         gtk_widget_get_window (widget)));
391   return screen;
392 }
393 
394 static inline GdkWindow *
z_gtk_widget_get_root_gdk_window(GtkWidget * widget)395 z_gtk_widget_get_root_gdk_window (
396   GtkWidget * widget)
397 {
398   GdkScreen * screen =
399     z_gtk_widget_get_screen (widget);
400   return
401     gdk_screen_get_root_window (screen);
402 }
403 
404 static inline void
z_gtk_widget_get_global_coordinates(GtkWidget * widget,int * x,int * y)405 z_gtk_widget_get_global_coordinates (
406   GtkWidget * widget,
407   int *       x,
408   int *       y)
409 {
410   GdkDevice * dev =
411     z_gtk_widget_get_device (widget);
412   GdkWindow * win =
413     z_gtk_widget_get_root_gdk_window (widget);
414   gdk_window_get_device_position (
415     win, dev, x, y, NULL);
416 }
417 
418 static inline void
z_gtk_widget_get_global_coordinates_double(GtkWidget * widget,double * x,double * y)419 z_gtk_widget_get_global_coordinates_double (
420   GtkWidget * widget,
421   double *    x,
422   double *    y)
423 {
424   GdkDevice * dev =
425     z_gtk_widget_get_device (widget);
426   GdkWindow * win =
427     z_gtk_widget_get_root_gdk_window (widget);
428   gdk_window_get_device_position_double (
429     win, dev, x, y, NULL);
430 }
431 
432 /**
433  * Wraps the cursor to the given global coordinates.
434  */
435 static inline void
z_gtk_warp_cursor_to(GtkWidget * widget,int x,int y)436 z_gtk_warp_cursor_to (
437   GtkWidget * widget, int x, int y)
438 {
439   GdkDevice * dev =
440     z_gtk_widget_get_device (widget);
441   GdkScreen * screen =
442     z_gtk_widget_get_screen (widget);
443   gdk_device_warp (dev, screen, x, y);
444 }
445 
446 /**
447  * Sets the GdkModifierType given for the widget.
448  *
449  * Used in eg. drag_motion events to check if
450  * Ctrl is held.
451  */
452 static inline void
z_gtk_widget_get_mask(GtkWidget * widget,GdkModifierType * mask)453 z_gtk_widget_get_mask (
454   GtkWidget * widget,
455   GdkModifierType * mask)
456 {
457   gdk_window_get_device_position (
458     gtk_widget_get_window (widget),
459     z_gtk_widget_get_device (widget),
460     NULL, NULL, mask);
461 }
462 
463 /**
464  * Returns if the keyval is an Alt key.
465  */
466 static inline int
z_gtk_keyval_is_alt(const guint keyval)467 z_gtk_keyval_is_alt (
468   const guint keyval)
469 {
470   return
471     keyval == GDK_KEY_Alt_L ||
472     keyval == GDK_KEY_Alt_R ||
473     keyval == GDK_KEY_Meta_L ||
474     keyval == GDK_KEY_Meta_R;
475 }
476 
477 /**
478  * Returns if the keyval is a Control key.
479  */
480 static inline int
z_gtk_keyval_is_ctrl(const guint keyval)481 z_gtk_keyval_is_ctrl (
482   const guint keyval)
483 {
484   return
485     keyval == GDK_KEY_Control_L ||
486     keyval == GDK_KEY_Control_R;
487 }
488 
489 /**
490  * Returns if the keyval is an arrow key.
491  */
492 static inline int
z_gtk_keyval_is_arrow(const guint keyval)493 z_gtk_keyval_is_arrow (
494   const guint keyval)
495 {
496   return
497     keyval == GDK_KEY_Left ||
498     keyval == GDK_KEY_Right ||
499     keyval == GDK_KEY_Down ||
500     keyval == GDK_KEY_Up;
501 }
502 
503 /**
504  * Returns if the keyval is a Shift key.
505  */
506 static inline int
z_gtk_keyval_is_shift(const guint keyval)507 z_gtk_keyval_is_shift (
508   const guint keyval)
509 {
510   return
511     keyval == GDK_KEY_Shift_L ||
512     keyval == GDK_KEY_Shift_R;
513 }
514 
515 /**
516  * Returns the single child of a container.
517  */
518 static inline GtkWidget *
z_gtk_container_get_single_child(GtkContainer * container)519 z_gtk_container_get_single_child (
520   GtkContainer * container)
521 {
522   GList *children, *iter;
523   GtkWidget * widget;
524   children =
525     gtk_container_get_children (container);
526   for (iter = children;
527        iter != NULL;
528        iter = g_list_next (iter))
529     {
530       widget =
531         GTK_WIDGET (iter->data);
532       g_list_free (children);
533       return widget;
534     }
535   g_return_val_if_reached (NULL);
536 }
537 
538 /**
539  * Returns the nth child of a container.
540  */
541 GtkWidget *
542 z_gtk_container_get_nth_child (
543   GtkContainer * container,
544   int            index);
545 
546 /**
547  * Sets the ellipsize mode of each text cell
548  * renderer in the combo box.
549  */
550 void
551 z_gtk_combo_box_set_ellipsize_mode (
552   GtkComboBox * self,
553   PangoEllipsizeMode ellipsize);
554 
555 /**
556  * Sets the given emblem to the button, or unsets
557  * the emblem if \ref emblem_icon is NULL.
558  */
559 void
560 z_gtk_button_set_emblem (
561   GtkButton *  btn,
562   const char * emblem_icon);
563 
564 /**
565  * Makes the given notebook foldable.
566  *
567  * The pages of the notebook must all be wrapped
568  * in GtkBox's.
569  */
570 void
571 z_gtk_setup_foldable_notebook (
572   GtkNotebook * notebook);
573 
574 /**
575  * Sets the margin on all 4 sides on the widget.
576  */
577 void
578 z_gtk_widget_set_margin (
579   GtkWidget * widget,
580   int         margin);
581 
582 GtkFlowBoxChild *
583 z_gtk_flow_box_get_selected_child (
584   GtkFlowBox * self);
585 
586 /**
587  * Callback to use for simple directory links.
588  */
589 bool
590 z_gtk_activate_dir_link_func (
591   GtkLabel * label,
592   char *     uri,
593   void *     data);
594 
595 GtkSourceLanguageManager *
596 z_gtk_source_language_manager_get (void);
597 
598 /**
599  * Makes the given GtkNotebook detachable to
600  * a new window.
601  */
602 void
603 z_gtk_notebook_make_detachable (
604   GtkNotebook * notebook,
605   GtkWindow *   parent_window);
606 
607 /**
608  * Wraps the message area in a scrolled window.
609  */
610 void
611 z_gtk_message_dialog_wrap_message_area_in_scroll (
612   GtkMessageDialog * dialog,
613   int                min_width,
614   int                min_height);
615 
616 /**
617  * Returns the full text contained in the text
618  * buffer.
619  *
620  * Must be free'd using g_free().
621  */
622 char *
623 z_gtk_text_buffer_get_full_text (
624   GtkTextBuffer * buffer);
625 
626 /**
627  * Generates a screenshot image for the given
628  * widget.
629  *
630  * See gdk_pixbuf_savev() for the parameters.
631  *
632  * @param accept_fallback Whether to accept a
633  *   fallback "no image" pixbuf.
634  * @param[out] ret_dir Placeholder for directory to
635  *   be deleted after using the screenshot.
636  * @param[out] ret_path Placeholder for absolute
637  *   path to the screenshot.
638  */
639 void
640 z_gtk_generate_screenshot_image (
641   GtkWidget *  widget,
642   const char * type,
643   char **      option_keys,
644   char **      option_values,
645   char **      ret_dir,
646   char **      ret_path,
647   bool         accept_fallback);
648 
649 /**
650  * Sets the action target of the given GtkActionable
651  * to be binded to the given setting.
652  *
653  * Mainly used for binding GSettings keys to toggle
654  * buttons.
655  */
656 void
657 z_gtk_actionable_set_action_from_setting (
658   GtkActionable * actionable,
659   GSettings *     settings,
660   const char *    key);
661 
662 /**
663  * Returns column number or -1 if not found or on
664  * error.
665  */
666 int
667 z_gtk_tree_view_column_get_column_id (
668   GtkTreeViewColumn * col);
669 
670 /**
671  * @}
672  */
673 #endif
674