1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Authors:
4 * Tavmjong Bah <tavmjong@free.fr>
5 *
6 * Copyright (C) 2017 Tavmjong Bah
7 *
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10
11
12 /** \file
13 A combobox that can be displayed in a toolbar.
14 */
15
16 #include "combo-tool-item.h"
17 #include "preferences.h"
18 #include <iostream>
19 #include <utility>
20 #include <gtkmm/toolitem.h>
21 #include <gtkmm/menuitem.h>
22 #include <gtkmm/radiomenuitem.h>
23 #include <gtkmm/combobox.h>
24 #include <gtkmm/menu.h>
25 #include <gtkmm/box.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/image.h>
28
29 namespace Inkscape {
30 namespace UI {
31 namespace Widget {
32
33 ComboToolItem*
create(const Glib::ustring & group_label,const Glib::ustring & tooltip,const Glib::ustring & stock_id,Glib::RefPtr<Gtk::ListStore> store,bool has_entry)34 ComboToolItem::create(const Glib::ustring &group_label,
35 const Glib::ustring &tooltip,
36 const Glib::ustring &stock_id,
37 Glib::RefPtr<Gtk::ListStore> store,
38 bool has_entry)
39 {
40 return new ComboToolItem(group_label, tooltip, stock_id, store, has_entry);
41 }
42
ComboToolItem(Glib::ustring group_label,Glib::ustring tooltip,Glib::ustring stock_id,Glib::RefPtr<Gtk::ListStore> store,bool has_entry)43 ComboToolItem::ComboToolItem(Glib::ustring group_label,
44 Glib::ustring tooltip,
45 Glib::ustring stock_id,
46 Glib::RefPtr<Gtk::ListStore> store,
47 bool has_entry) :
48 _active(-1),
49 _group_label(std::move( group_label )),
50 _tooltip(std::move( tooltip )),
51 _stock_id(std::move( stock_id )),
52 _store (std::move(store)),
53 _use_label (true),
54 _use_icon (false),
55 _use_pixbuf (true),
56 _icon_size ( Gtk::ICON_SIZE_LARGE_TOOLBAR ),
57 _combobox (nullptr),
58 _group_label_widget(nullptr),
59 _container(Gtk::manage(new Gtk::Box())),
60 _menuitem (nullptr)
61 {
62 add(*_container);
63 _container->set_spacing(3);
64
65 // ": " is added to the group label later
66 if (!_group_label.empty()) {
67 // we don't expect trailing spaces
68 // g_assert(_group_label.raw()[_group_label.raw().size() - 1] != ' ');
69
70 // strip space (note: raw() indexing is much cheaper on Glib::ustring)
71 if (_group_label.raw()[_group_label.raw().size() - 1] == ' ') {
72 _group_label.resize(_group_label.size() - 1);
73 }
74 }
75 if (!_group_label.empty()) {
76 // we don't expect a trailing colon
77 // g_assert(_group_label.raw()[_group_label.raw().size() - 1] != ':');
78
79 // strip colon (note: raw() indexing is much cheaper on Glib::ustring)
80 if (_group_label.raw()[_group_label.raw().size() - 1] == ':') {
81 _group_label.resize(_group_label.size() - 1);
82 }
83 }
84
85
86 // Create combobox
87 _combobox = Gtk::manage (new Gtk::ComboBox(has_entry));
88 _combobox->set_model(_store);
89
90 populate_combobox();
91
92 _combobox->signal_changed().connect(
93 sigc::mem_fun(*this, &ComboToolItem::on_changed_combobox));
94 _container->pack_start(*_combobox);
95
96 show_all();
97 }
98
99 void
focus_on_click(bool focus_on_click)100 ComboToolItem::focus_on_click( bool focus_on_click )
101 {
102 _combobox->set_focus_on_click(focus_on_click);
103 }
104
105
106 void
use_label(bool use_label)107 ComboToolItem::use_label(bool use_label)
108 {
109 _use_label = use_label;
110 populate_combobox();
111 }
112
113 void
use_icon(bool use_icon)114 ComboToolItem::use_icon(bool use_icon)
115 {
116 _use_icon = use_icon;
117 populate_combobox();
118 }
119
120 void
use_pixbuf(bool use_pixbuf)121 ComboToolItem::use_pixbuf(bool use_pixbuf)
122 {
123 _use_pixbuf = use_pixbuf;
124 populate_combobox();
125 }
126
127 void
use_group_label(bool use_group_label)128 ComboToolItem::use_group_label(bool use_group_label)
129 {
130 if (use_group_label == (_group_label_widget != nullptr)) {
131 return;
132 }
133 if (use_group_label) {
134 _container->remove(*_combobox);
135 _group_label_widget = Gtk::manage(new Gtk::Label(_group_label + ": "));
136 _container->pack_start(*_group_label_widget);
137 _container->pack_start(*_combobox);
138 } else {
139 _container->remove(*_group_label_widget);
140 delete _group_label_widget;
141 _group_label_widget = nullptr;
142 }
143 }
144
145 void
populate_combobox()146 ComboToolItem::populate_combobox()
147 {
148 _combobox->clear();
149
150 ComboToolItemColumns columns;
151 if (_use_icon) {
152 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
153 if (prefs->getBool("/theme/symbolicIcons", false)) {
154 auto children = _store->children();
155 for (auto row : children) {
156 Glib::ustring icon = row[columns.col_icon];
157 gint pos = icon.find("-symbolic");
158 if (pos == std::string::npos) {
159 icon += "-symbolic";
160 }
161 row[columns.col_icon] = icon;
162 }
163 }
164 Gtk::CellRendererPixbuf *renderer = new Gtk::CellRendererPixbuf;
165 renderer->set_property ("stock_size", Gtk::ICON_SIZE_LARGE_TOOLBAR);
166 _combobox->pack_start (*Gtk::manage(renderer), false);
167 _combobox->add_attribute (*renderer, "icon_name", columns.col_icon );
168 } else if (_use_pixbuf) {
169 Gtk::CellRendererPixbuf *renderer = new Gtk::CellRendererPixbuf;
170 //renderer->set_property ("stock_size", Gtk::ICON_SIZE_LARGE_TOOLBAR);
171 _combobox->pack_start (*Gtk::manage(renderer), false);
172 _combobox->add_attribute (*renderer, "pixbuf", columns.col_pixbuf );
173 }
174
175 if (_use_label) {
176 _combobox->pack_start(columns.col_label);
177 }
178
179 std::vector<Gtk::CellRenderer*> cells = _combobox->get_cells();
180 for (auto & cell : cells) {
181 _combobox->add_attribute (*cell, "sensitive", columns.col_sensitive);
182 }
183
184 set_tooltip_text(_tooltip);
185 _combobox->set_tooltip_text(_tooltip);
186 _combobox->set_active (_active);
187 }
188
189 void
set_active(gint active)190 ComboToolItem::set_active (gint active) {
191 if (_active != active) {
192
193 _active = active;
194
195 if (_combobox) {
196 _combobox->set_active (active);
197 }
198
199 if (active < _radiomenuitems.size()) {
200 _radiomenuitems[ active ]->set_active();
201 }
202 }
203 }
204
205 Glib::ustring
get_active_text()206 ComboToolItem::get_active_text () {
207 Gtk::TreeModel::Row row = _store->children()[_active];
208 ComboToolItemColumns columns;
209 Glib::ustring label = row[columns.col_label];
210 return label;
211 }
212
213 bool
on_create_menu_proxy()214 ComboToolItem::on_create_menu_proxy()
215 {
216 if (_menuitem == nullptr) {
217
218 _menuitem = Gtk::manage (new Gtk::MenuItem(_group_label));
219 Gtk::Menu *menu = Gtk::manage (new Gtk::Menu);
220
221 Gtk::RadioButton::Group group;
222 int index = 0;
223 auto children = _store->children();
224 for (auto row : children) {
225 ComboToolItemColumns columns;
226 Glib::ustring label = row[columns.col_label ];
227 Glib::ustring icon = row[columns.col_icon ];
228 Glib::ustring tooltip = row[columns.col_tooltip ];
229 bool sensitive = row[columns.col_sensitive ];
230
231 Gtk::RadioMenuItem* button = Gtk::manage(new Gtk::RadioMenuItem(group));
232 button->set_label (label);
233 button->set_tooltip_text( tooltip );
234 button->set_sensitive( sensitive );
235
236 button->signal_toggled().connect( sigc::bind<0>(
237 sigc::mem_fun(*this, &ComboToolItem::on_toggled_radiomenu), index++)
238 );
239
240 menu->add (*button);
241
242 _radiomenuitems.push_back( button );
243 }
244
245 if ( _active < _radiomenuitems.size()) {
246 _radiomenuitems[ _active ]->set_active();
247 }
248
249 _menuitem->set_submenu (*menu);
250 _menuitem->show_all();
251 }
252
253 set_proxy_menu_item(_group_label, *_menuitem);
254 return true;
255 }
256
257 void
on_changed_combobox()258 ComboToolItem::on_changed_combobox() {
259
260 int row = _combobox->get_active_row_number();
261 set_active( row );
262 _changed.emit (_active);
263 _changed_after.emit (_active);
264 }
265
266 void
on_toggled_radiomenu(int n)267 ComboToolItem::on_toggled_radiomenu(int n) {
268
269 // toggled emitted twice, first for button toggled off, second for button toggled on.
270 // We want to react only to the button turned on.
271 if ( n < _radiomenuitems.size() &&_radiomenuitems[ n ]->get_active()) {
272 set_active ( n );
273 _changed.emit (_active);
274 _changed_after.emit (_active);
275 }
276 }
277
278 }
279 }
280 }
281 /*
282 Local Variables:
283 mode:c++
284 c-file-style:"stroustrup"
285 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
286 indent-tabs-mode:nil
287 fill-column:99
288 End:
289 */
290 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
291