1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Inkscape Widget Utilities
4  *
5  * Authors:
6  *   Bryce W. Harrington <brycehar@bryceharrington.org>
7  *   bulia byak <buliabyak@users.sf.net>
8  *
9  * Copyright (C) 2003 Bryce W. Harrington
10  *
11  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12  */
13 
14 #include <cstring>
15 #include <string>
16 
17 #include <gtkmm/box.h>
18 #include <gtkmm/label.h>
19 #include <gtkmm/grid.h>
20 
21 #include "selection.h"
22 
23 #include "spw-utilities.h"
24 
25 /**
26  * Creates a label widget with the given text, at the given col, row
27  * position in the table.
28  */
spw_label(Gtk::Grid * table,const gchar * label_text,int col,int row,Gtk::Widget * target)29 Gtk::Label * spw_label(Gtk::Grid *table, const gchar *label_text, int col, int row, Gtk::Widget* target)
30 {
31   Gtk::Label *label_widget = new Gtk::Label();
32   g_assert(label_widget != nullptr);
33   if (target != nullptr) {
34     label_widget->set_text_with_mnemonic(label_text);
35     label_widget->set_mnemonic_widget(*target);
36   } else {
37     label_widget->set_text(label_text);
38   }
39   label_widget->show();
40 
41   label_widget->set_halign(Gtk::ALIGN_START);
42   label_widget->set_valign(Gtk::ALIGN_CENTER);
43   label_widget->set_margin_start(4);
44   label_widget->set_margin_end(4);
45 
46   table->attach(*label_widget, col, row, 1, 1);
47 
48   return label_widget;
49 }
50 
51 /**
52  * Creates a horizontal layout manager with 4-pixel spacing between children
53  * and space for 'width' columns.
54  */
spw_hbox(Gtk::Grid * table,int width,int col,int row)55 Gtk::Box * spw_hbox(Gtk::Grid * table, int width, int col, int row)
56 {
57   /* Create a new hbox with a 4-pixel spacing between children */
58   Gtk::Box *hb = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4);
59   g_assert(hb != nullptr);
60   hb->show();
61   hb->set_hexpand();
62   hb->set_halign(Gtk::ALIGN_FILL);
63   hb->set_valign(Gtk::ALIGN_CENTER);
64   table->attach(*hb, col, row, width, 1);
65 
66   return hb;
67 }
68 
69 /**
70  * Finds the descendant of w which has the data with the given key and returns the data, or NULL if there's none.
71  */
sp_search_by_data_recursive(GtkWidget * w,gpointer key)72 gpointer sp_search_by_data_recursive(GtkWidget *w, gpointer key)
73 {
74     gpointer r = nullptr;
75 
76     if (w && G_IS_OBJECT(w)) {
77         r = g_object_get_data(G_OBJECT(w), (gchar *) key);
78     }
79     if (r) return r;
80 
81     if (GTK_IS_CONTAINER(w)) {
82             std::vector<Gtk::Widget*> children = Glib::wrap(GTK_CONTAINER(w))->get_children();
83         for (auto i:children) {
84             r = sp_search_by_data_recursive(GTK_WIDGET(i->gobj()), key);
85             if (r) return r;
86         }
87     }
88 
89     return nullptr;
90 }
91 
92 /**
93  * Returns a named descendent of parent, which has the given name, or nullptr if there's none.
94  *
95  * \param[in] parent The widget to search
96  * \param[in] name   The name of the desired child widget
97  *
98  * \return The specified child widget, or nullptr if it cannot be found
99  */
100 Gtk::Widget *
sp_search_by_name_recursive(Gtk::Widget * parent,const Glib::ustring & name)101 sp_search_by_name_recursive(Gtk::Widget *parent, const Glib::ustring& name)
102 {
103     auto parent_bin = dynamic_cast<Gtk::Bin *>(parent);
104     auto parent_container = dynamic_cast<Gtk::Container *>(parent);
105 
106     if (parent && parent->get_name() == name) {
107         return parent;
108     }
109     else if (parent_bin) {
110         auto child = parent_bin->get_child();
111         return sp_search_by_name_recursive(child, name);
112     }
113     else if (parent_container) {
114         auto children = parent_container->get_children();
115 
116         for (auto child : children) {
117             auto tmp = sp_search_by_name_recursive(child, name);
118 
119             if (tmp) {
120                 return tmp;
121             }
122         }
123     }
124 
125     return nullptr;
126 }
127 
128 /**
129  * Returns the descendant of w which has the given key and value pair, or NULL if there's none.
130  */
sp_search_by_value_recursive(GtkWidget * w,gchar * key,gchar * value)131 GtkWidget *sp_search_by_value_recursive(GtkWidget *w, gchar *key, gchar *value)
132 {
133     gchar *r = nullptr;
134 
135     if (w && G_IS_OBJECT(w)) {
136         r = (gchar *) g_object_get_data(G_OBJECT(w), key);
137     }
138     if (r && !strcmp (r, value)) return w;
139 
140     if (GTK_IS_CONTAINER(w)) {
141                 std::vector<Gtk::Widget*> children = Glib::wrap(GTK_CONTAINER(w))->get_children();
142         for (auto i:children) {
143             GtkWidget *child = sp_search_by_value_recursive(GTK_WIDGET(i->gobj()), key, value);
144             if (child) return child;
145         }
146     }
147 
148     return nullptr;
149 }
150 
151 /**
152  * This function traverses a tree of widgets descending into bins and containers.
153  * It stops and returns a pointer to the first child widget for which 'eval' evaluates to true.
154  * If 'eval' never returns true then this function visits all widgets and returns nullptr.
155  *
156  * \param[in] widget The widget to start traversal from - top of the tree
157  * \param[in] eval   The callback invoked for each visited widget
158  *
159  * \return The widget for which 'eval' returned true, or nullptr overwise.
160  * Note: it could be a starting widget too.
161  */
sp_traverse_widget_tree(Gtk::Widget * widget,const std::function<bool (Gtk::Widget *)> & eval)162 Gtk::Widget* sp_traverse_widget_tree(Gtk::Widget* widget, const std::function<bool (Gtk::Widget*)>& eval) {
163     if (!widget) return nullptr;
164 
165     if (eval(widget)) return widget;
166 
167     if (auto bin = dynamic_cast<Gtk::Bin*>(widget)) {
168         return sp_traverse_widget_tree(bin->get_child(), eval);
169     }
170     else if (auto container = dynamic_cast<Gtk::Container*>(widget)) {
171         auto children = container->get_children();
172         for (auto child : children) {
173             if (auto found = sp_traverse_widget_tree(child, eval)) {
174                 return found;
175             }
176         }
177     }
178 
179     return nullptr;
180 }
181 
182 /**
183  * This function traverses a tree of widgets searching for first focusable widget.
184  *
185  * \param[in] widget The widget to start traversal from - top of the tree
186  *
187  * \return The first focusable widget or nullptr if none are focusable.
188  */
sp_find_focusable_widget(Gtk::Widget * widget)189 Gtk::Widget* sp_find_focusable_widget(Gtk::Widget* widget) {
190     return sp_traverse_widget_tree(widget, [](Gtk::Widget* w) { return w->get_can_focus(); });
191 }
192 
193 /*
194   Local Variables:
195   mode:c++
196   c-file-style:"stroustrup"
197   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
198   indent-tabs-mode:nil
199   fill-column:99
200   End:
201 */
202 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
203