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