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