1 /* additional_toolbar.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include <config.h>
11 
12 #include <glib.h>
13 
14 #include <ui/qt/widgets/additional_toolbar.h>
15 #include <ui/qt/widgets/apply_line_edit.h>
16 #include <ui/qt/utils/qt_ui_utils.h>
17 #include <ui/qt/utils/variant_pointer.h>
18 #include <ui/qt/wireshark_application.h>
19 
20 #include <QLabel>
21 #include <QLineEdit>
22 #include <QHBoxLayout>
23 #include <QComboBox>
24 #include <QWidget>
25 #include <QCheckBox>
26 #include <QPushButton>
27 #include <QStandardItem>
28 #include <QStandardItemModel>
29 #include <QLayoutItem>
30 
31 const char * AdditionalToolbarWidgetAction::propertyName = "additional_toolbar_item";
32 
AdditionalToolBar(ext_toolbar_t * exttoolbar,QWidget * parent)33 AdditionalToolBar::AdditionalToolBar(ext_toolbar_t * exttoolbar, QWidget * parent)
34 : QToolBar(parent),
35   toolbar(exttoolbar)
36 { }
37 
~AdditionalToolBar()38 AdditionalToolBar::~AdditionalToolBar()
39 { }
40 
create(QWidget * parent,ext_toolbar_t * toolbar)41 AdditionalToolBar * AdditionalToolBar::create(QWidget * parent, ext_toolbar_t * toolbar)
42 {
43     if (g_list_length(toolbar->children) == 0)
44         return NULL;
45 
46     AdditionalToolBar * result = new AdditionalToolBar(toolbar, parent);
47     result->setMovable(false);
48     result->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
49     result->layout()->setContentsMargins(0, 0, 0, 0);
50     result->layout()->setSpacing(4);
51 
52     GList * walker = toolbar->children;
53     bool spacerNeeded = true;
54 
55     while (walker && walker->data)
56     {
57         ext_toolbar_t * item = gxx_list_data(ext_toolbar_t *, walker);
58         if (item->type == EXT_TOOLBAR_ITEM)
59         {
60             if (item->item_type == EXT_TOOLBAR_STRING)
61                 spacerNeeded = false;
62 
63             QAction * newAction = new AdditionalToolbarWidgetAction(item, result);
64             if (newAction)
65             {
66                 result->addAction(newAction);
67                 /* Necessary, because enable state is reset upon adding the action */
68                 result->actions()[result->actions().count() - 1]->setEnabled(!item->capture_only);
69             }
70         }
71 
72         walker = gxx_list_next (walker);
73     }
74 
75     if (result->children().count() == 0)
76         return Q_NULLPTR;
77 
78     if (spacerNeeded)
79     {
80         QWidget * empty = new QWidget();
81         empty->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
82         result->addWidget(empty);
83 
84     }
85 
86     return result;
87 }
88 
menuName()89 QString AdditionalToolBar::menuName()
90 {
91     return (toolbar && toolbar->name) ? QString(toolbar->name) : QString();
92 }
93 
AdditionalToolbarWidgetAction(QObject * parent)94 AdditionalToolbarWidgetAction::AdditionalToolbarWidgetAction(QObject * parent)
95 : QWidgetAction(parent),
96   toolbar_item(0)
97 { }
98 
AdditionalToolbarWidgetAction(ext_toolbar_t * item,QObject * parent)99 AdditionalToolbarWidgetAction::AdditionalToolbarWidgetAction(ext_toolbar_t * item, QObject * parent)
100 : QWidgetAction(parent),
101   toolbar_item(item)
102 {
103     connect(wsApp, &WiresharkApplication::captureActive, this, &AdditionalToolbarWidgetAction::captureActive);
104 }
105 
AdditionalToolbarWidgetAction(const AdditionalToolbarWidgetAction & copy_object)106 AdditionalToolbarWidgetAction::AdditionalToolbarWidgetAction(const AdditionalToolbarWidgetAction & copy_object)
107 :  QWidgetAction(copy_object.parent()),
108    toolbar_item(copy_object.toolbar_item)
109 {
110     connect(wsApp, &WiresharkApplication::captureActive, this, &AdditionalToolbarWidgetAction::captureActive);
111 }
112 
113 
captureActive(int activeCaptures)114 void AdditionalToolbarWidgetAction::captureActive(int activeCaptures)
115 {
116     if (toolbar_item && toolbar_item->capture_only)
117     {
118         setEnabled(activeCaptures != 0);
119     }
120 }
121 
122 /* Exists, so a default deconstructor does not call delete on toolbar_item */
~AdditionalToolbarWidgetAction()123 AdditionalToolbarWidgetAction::~AdditionalToolbarWidgetAction() { }
124 
createWidget(QWidget * parent)125 QWidget * AdditionalToolbarWidgetAction::createWidget(QWidget * parent)
126 {
127     QWidget * barItem = 0;
128 
129     if (toolbar_item->type != EXT_TOOLBAR_ITEM)
130         return barItem;
131 
132     switch (toolbar_item->item_type)
133     {
134     case EXT_TOOLBAR_BUTTON:
135         barItem = createButton(toolbar_item, parent);
136         break;
137     case EXT_TOOLBAR_BOOLEAN:
138         barItem = createBoolean(toolbar_item, parent);
139         break;
140     case EXT_TOOLBAR_STRING:
141         barItem = createTextEditor(toolbar_item, parent);
142         break;
143     case EXT_TOOLBAR_SELECTOR:
144         barItem = createSelector(toolbar_item, parent);
145         break;
146     }
147 
148     if (! barItem)
149         return 0;
150 
151     barItem->setToolTip(toolbar_item->tooltip);
152     barItem->setProperty(propertyName, VariantPointer<ext_toolbar_t>::asQVariant(toolbar_item));
153 
154 #ifdef Q_OS_MAC
155     barItem->setAttribute(Qt::WA_MacSmallSize, true);
156 #endif
157 
158     return barItem;
159 }
160 
161 static void
toolbar_button_cb(gpointer item,gpointer item_data,gpointer user_data)162 toolbar_button_cb(gpointer item, gpointer item_data, gpointer user_data)
163 {
164     if (! item || ! item_data || ! user_data)
165         return;
166 
167     QPushButton * widget = (QPushButton *)(item_data);
168     ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data;
169 
170     if (widget)
171     {
172         if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE)
173             widget->setText((gchar *)update_entry->user_data);
174         else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE)
175         {
176             bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1;
177             widget->setEnabled(enableState);
178         }
179 
180     }
181 }
182 
createButton(ext_toolbar_t * item,QWidget * parent)183 QWidget * AdditionalToolbarWidgetAction::createButton(ext_toolbar_t * item, QWidget * parent)
184 {
185     if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_BUTTON)
186         return 0;
187 
188     QPushButton * button = new QPushButton(item->name, parent);
189     button->setText(item->name);
190     connect(button, &QPushButton::clicked, this, &AdditionalToolbarWidgetAction::onButtonClicked);
191 
192     ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_button_cb, (void *)button);
193 
194     return button;
195 }
196 
197 static void
toolbar_boolean_cb(gpointer item,gpointer item_data,gpointer user_data)198 toolbar_boolean_cb(gpointer item, gpointer item_data, gpointer user_data)
199 {
200     if (! item || ! item_data || ! user_data)
201         return;
202 
203     QCheckBox * widget = (QCheckBox *)(item_data);
204 
205     ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data;
206 
207     if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE)
208     {
209         bool oldState = false;
210         if (update_entry->silent)
211             oldState = widget->blockSignals(true);
212 
213         widget->setCheckState(GPOINTER_TO_INT(update_entry->user_data) == 1 ? Qt::Checked : Qt::Unchecked);
214 
215         if (update_entry->silent)
216             widget->blockSignals(oldState);
217     }
218     else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE)
219     {
220         bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1;
221         widget->setEnabled(enableState);
222     }
223 }
224 
createBoolean(ext_toolbar_t * item,QWidget * parent)225 QWidget * AdditionalToolbarWidgetAction::createBoolean(ext_toolbar_t * item, QWidget * parent)
226 {
227     if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_BOOLEAN)
228         return 0;
229 
230     QString defValue = toolbar_item->defvalue;
231 
232     QCheckBox * checkbox = new QCheckBox(item->name, parent);
233     checkbox->setText(item->name);
234     setCheckable(true);
235     checkbox->setCheckState(defValue.compare("true", Qt::CaseInsensitive) == 0 ? Qt::Checked : Qt::Unchecked);
236     connect(checkbox, &QCheckBox::stateChanged, this, &AdditionalToolbarWidgetAction::onCheckBoxChecked);
237 
238     ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_boolean_cb, (void *)checkbox);
239 
240     return checkbox;
241 }
242 
createLabelFrame(ext_toolbar_t * item,QWidget * parent)243 QWidget * AdditionalToolbarWidgetAction::createLabelFrame(ext_toolbar_t * item, QWidget * parent)
244 {
245     if (! item)
246         return new QWidget();
247 
248     QWidget * frame = new QWidget(parent);
249 
250     QHBoxLayout * frameLayout = new QHBoxLayout(frame);
251     frameLayout->setContentsMargins(0, 0, 0, 0);
252     frameLayout->setSpacing(0);
253 
254     QLabel * strLabel = new QLabel(item->name, frame);
255     strLabel->setToolTip(item->tooltip);
256 
257 #ifdef Q_OS_MAC
258     frame->setAttribute(Qt::WA_MacSmallSize, true);
259     strLabel->setAttribute(Qt::WA_MacSmallSize, true);
260 #endif
261 
262     frameLayout->addWidget(strLabel);
263 
264     frame->setLayout(frameLayout);
265 
266     return frame;
267 }
268 
269 static void
toolbar_string_cb(gpointer item,gpointer item_data,gpointer user_data)270 toolbar_string_cb(gpointer item, gpointer item_data, gpointer user_data)
271 {
272     if (! item || ! item_data || ! user_data)
273         return;
274 
275     ApplyLineEdit * edit = (ApplyLineEdit *)(item_data);
276 
277     ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data;
278 
279     if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE)
280     {
281         bool oldState = false;
282         if (update_entry->silent)
283             oldState = edit->blockSignals(true);
284 
285         edit->setText((gchar *)update_entry->user_data);
286 
287         if (update_entry->silent)
288             edit->blockSignals(oldState);
289     }
290     else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE)
291     {
292         bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1;
293         edit->setEnabled(enableState);
294     }
295 }
296 
createTextEditor(ext_toolbar_t * item,QWidget * parent)297 QWidget * AdditionalToolbarWidgetAction::createTextEditor(ext_toolbar_t * item, QWidget * parent)
298 {
299     if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_STRING)
300         return 0;
301 
302     QWidget * frame = createLabelFrame(toolbar_item, parent);
303 
304     ApplyLineEdit * strEdit = new ApplyLineEdit(toolbar_item->defvalue, frame);
305     strEdit->setToolTip(toolbar_item->tooltip);
306     strEdit->setRegEx(toolbar_item->regex);
307     strEdit->setEmptyAllowed(toolbar_item->is_required);
308     strEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
309 
310 #ifdef Q_OS_MAC
311     strEdit->setAttribute(Qt::WA_MacSmallSize, true);
312 #endif
313 
314     frame->layout()->addWidget(strEdit);
315 
316     connect(strEdit, &ApplyLineEdit::textApplied, this, &AdditionalToolbarWidgetAction::sendTextToCallback);
317 
318     ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_string_cb, (void *)strEdit);
319 
320     return frame;
321 }
322 
323 static void
toolbar_selector_cb(gpointer item,gpointer item_data,gpointer user_data)324 toolbar_selector_cb(gpointer item, gpointer item_data, gpointer user_data)
325 {
326     if (! item || ! item_data || ! user_data)
327         return;
328 
329     QComboBox * comboBox = (QComboBox *)(item_data);
330     ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data;
331 
332     bool oldState = false;
333 
334     if (update_entry->silent)
335         oldState = comboBox->blockSignals(true);
336 
337     QStandardItemModel * sourceModel = (QStandardItemModel *)comboBox->model();
338 
339     if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE)
340     {
341         bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1;
342         comboBox->setEnabled(enableState);
343     }
344     else if (update_entry->type != EXT_TOOLBAR_UPDATE_DATA_REMOVE && ! update_entry->user_data)
345         return;
346 
347     if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE)
348     {
349         QString data = QString((gchar *)update_entry->user_data);
350 
351         for (int i = 0; i < sourceModel->rowCount(); i++)
352         {
353             QStandardItem * dataValue = ((QStandardItemModel *)sourceModel)->item(i, 0);
354             ext_toolbar_value_t * tbValue = VariantPointer<ext_toolbar_value_t>::asPtr(dataValue->data(Qt::UserRole));
355             if (tbValue && data.compare(QString(tbValue->value)) == 0)
356             {
357                 comboBox->setCurrentIndex(i);
358                 break;
359             }
360         }
361     }
362     else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATA)
363     {
364         GList * walker = (GList *)update_entry->user_data;
365         if (g_list_length(walker) == 0)
366             return;
367 
368         sourceModel->clear();
369 
370         while (walker && walker->data)
371         {
372             ext_toolbar_value_t * listvalue = gxx_list_data(ext_toolbar_value_t *, walker);
373 
374             QStandardItem * si = new QStandardItem(listvalue->display);
375             si->setData(VariantPointer<ext_toolbar_value_t>::asQVariant(listvalue), Qt::UserRole);
376             sourceModel->appendRow(si);
377 
378             walker = gxx_list_next(walker);
379         }
380     }
381     else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATABYINDEX ||
382             update_entry->type == EXT_TOOLBAR_UPDATE_DATA_ADD ||
383             update_entry->type == EXT_TOOLBAR_UPDATE_DATA_REMOVE)
384     {
385         if (! update_entry->data_index)
386             return;
387 
388         gchar * idx = (gchar *)update_entry->data_index;
389         gchar * display = (gchar *)update_entry->user_data;
390 
391         if (update_entry->type == EXT_TOOLBAR_UPDATE_DATABYINDEX)
392         {
393             for (int i = 0; i < sourceModel->rowCount(); i++)
394             {
395                 QStandardItem * dataValue = sourceModel->item(i, 0);
396                 ext_toolbar_value_t * entry = VariantPointer<ext_toolbar_value_t>::asPtr(dataValue->data(Qt::UserRole));
397                 if (entry && g_strcmp0(entry->value, idx) == 0)
398                 {
399                     g_free(entry->display);
400                     entry->display = g_strdup(display);
401                     dataValue->setData(VariantPointer<ext_toolbar_value_t>::asQVariant(entry), Qt::UserRole);
402                     dataValue->setText(display);
403                     break;
404                 }
405             }
406         }
407         else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATA_ADD)
408         {
409             ext_toolbar_value_t * listvalue = g_new0(ext_toolbar_value_t, 1);
410             listvalue->display = g_strdup(display);
411             listvalue->value = g_strdup(idx);
412 
413             QStandardItem * si = new QStandardItem(listvalue->display);
414             si->setData(VariantPointer<ext_toolbar_value_t>::asQVariant(listvalue), Qt::UserRole);
415             sourceModel->appendRow(si);
416         }
417         else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATA_REMOVE)
418         {
419             QList<QStandardItem *> entryList = sourceModel->findItems(display);
420             /* Search for index if display did not find anything */
421             if (entryList.size() == 0)
422                 entryList = sourceModel->findItems(idx);
423 
424             foreach(QStandardItem *entry, entryList)
425             {
426                 QModelIndex index = sourceModel->indexFromItem(entry);
427                 if (index.isValid())
428                     sourceModel->removeRow(index.row());
429             }
430         }
431     }
432 
433     if (update_entry->silent)
434         comboBox->blockSignals(oldState);
435 
436 }
437 
createSelector(ext_toolbar_t * item,QWidget * parent)438 QWidget * AdditionalToolbarWidgetAction::createSelector(ext_toolbar_t * item, QWidget * parent)
439 {
440     if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_SELECTOR)
441         return 0;
442 
443     if (g_list_length(item->values) == 0)
444         return 0;
445 
446     QWidget * frame = createLabelFrame(item, parent);
447 
448     QComboBox * myBox = new QComboBox(parent);
449     myBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
450 
451     QStandardItemModel * sourceModel = new QStandardItemModel();
452 
453     GList * walker = item->values;
454     int selIndex = 0;
455     while (walker && walker->data)
456     {
457         ext_toolbar_value_t * listvalue = gxx_list_data(ext_toolbar_value_t *, walker);
458 
459         QStandardItem * si = new QStandardItem(listvalue->display);
460         si->setData(VariantPointer<ext_toolbar_value_t>::asQVariant(listvalue), Qt::UserRole);
461         sourceModel->appendRow(si);
462 
463         if (listvalue->is_default)
464             selIndex = sourceModel->rowCount();
465 
466         walker = gxx_list_next(walker);
467     }
468 
469     myBox->setModel(sourceModel);
470     myBox->setCurrentIndex(selIndex);
471 
472 #ifdef Q_OS_MAC
473     myBox->setAttribute(Qt::WA_MacSmallSize, true);
474 #endif
475 
476     frame->layout()->addWidget(myBox);
477 
478     connect(myBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
479             this, &AdditionalToolbarWidgetAction::onSelectionInWidgetChanged);
480 
481     ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_selector_cb, (void *)myBox);
482 
483     return frame;
484 }
485 
extractToolbarItemFromObject(QObject * object)486 ext_toolbar_t * AdditionalToolbarWidgetAction::extractToolbarItemFromObject(QObject * object)
487 {
488     QWidget * widget = dynamic_cast<QWidget *>(object);
489     if (! widget)
490         return 0;
491 
492     QVariant propValue = widget->property(propertyName);
493 
494     /* If property is invalid, look if our parent has this property */
495     if (! propValue.isValid())
496     {
497         QWidget * frame = dynamic_cast<QWidget *>(widget->parent());
498         if (! frame)
499             return 0;
500 
501         propValue = frame->property(propertyName);
502     }
503 
504     if (! propValue.isValid())
505         return 0;
506 
507     return VariantPointer<ext_toolbar_t>::asPtr(propValue);
508 }
509 
onButtonClicked()510 void AdditionalToolbarWidgetAction::onButtonClicked()
511 {
512     ext_toolbar_t * item = extractToolbarItemFromObject(sender());
513     if (! item)
514         return;
515 
516     item->callback(item, 0, item->user_data);
517 }
518 
onCheckBoxChecked(int checkState)519 void AdditionalToolbarWidgetAction::onCheckBoxChecked(int checkState)
520 {
521     ext_toolbar_t * item = extractToolbarItemFromObject(sender());
522     if (! item)
523         return;
524 
525     gboolean value = checkState == Qt::Checked ? true : false;
526 
527     item->callback(item, &value, item->user_data);
528 }
529 
sendTextToCallback()530 void AdditionalToolbarWidgetAction::sendTextToCallback()
531 {
532     ext_toolbar_t * item = extractToolbarItemFromObject(sender());
533     if (! item)
534         return;
535 
536     if (item->item_type != EXT_TOOLBAR_STRING)
537         return;
538 
539     ApplyLineEdit * editor = dynamic_cast<ApplyLineEdit *>(sender());
540     if (! editor)
541     {
542         /* Called from button, searching for acompanying line edit */
543         QWidget * parent = dynamic_cast<QWidget *>(sender()->parent());
544         if (parent)
545         {
546             QList<ApplyLineEdit *> children = parent->findChildren<ApplyLineEdit *>();
547             if (children.count() >= 0)
548                 editor = children.at(0);
549         }
550     }
551 
552     if (editor)
553         item->callback(item, qstring_strdup(editor->text()), item->user_data);
554 }
555 
onSelectionInWidgetChanged(int idx)556 void AdditionalToolbarWidgetAction::onSelectionInWidgetChanged(int idx)
557 {
558     QComboBox * editor = dynamic_cast<QComboBox *>(sender());
559     ext_toolbar_t * item = extractToolbarItemFromObject(editor);
560     if (! item || item->item_type != EXT_TOOLBAR_SELECTOR)
561         return;
562 
563     QStandardItemModel * sourceModel = (QStandardItemModel *) editor->model();
564     if (sourceModel->rowCount() <= idx)
565         return;
566 
567     QModelIndex mdIdx = sourceModel->index(idx, 0);
568     QVariant dataSet = sourceModel->data(mdIdx, Qt::UserRole);
569     if (dataSet.isValid())
570     {
571         ext_toolbar_value_t * value_entry = VariantPointer<ext_toolbar_value_t>::asPtr(dataSet);
572         item->callback(item, value_entry, item->user_data);
573     }
574 }
575