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