Rename "ws_version_info.h", also .c
[metze/wireshark/wip.git] / ui / qt / main_welcome.cpp
1 /* main_welcome.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "config.h"
23
24 #include <glib.h>
25
26 #include <epan/prefs.h>
27
28 #include "ui/capture_globals.h"
29 #include "ui/help_url.h"
30
31 #include "version_info.h"
32
33 #include "main_welcome.h"
34 #include <ui_main_welcome.h>
35 #include <ui/qt/utils/tango_colors.h>
36 #include <ui/qt/utils/color_utils.h>
37 #include <ui/qt/utils/qt_ui_utils.h>
38 #include "wireshark_application.h"
39
40 #include <QClipboard>
41 #include <QDesktopServices>
42 #include <QDir>
43 #include <QListWidget>
44 #include <QMenu>
45 #include <QResizeEvent>
46 #include <QTreeWidgetItem>
47 #include <QUrl>
48 #include <QWidget>
49
50 #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
51 #include <QGraphicsBlurEffect>
52 #endif
53
54 #ifndef VERSION_FLAVOR
55 #define VERSION_FLAVOR ""
56 #endif
57
58 #ifdef HAVE_EXTCAP
59 #include <extcap.h>
60 #endif
61
62 MainWelcome::MainWelcome(QWidget *parent) :
63     QFrame(parent),
64     welcome_ui_(new Ui::MainWelcome),
65     flavor_(tr(VERSION_FLAVOR)),
66     #ifdef Q_OS_MAC
67     show_in_str_(tr("Show in Finder")),
68     #else
69     show_in_str_(tr("Show in Folder")),
70     #endif
71     splash_overlay_(NULL)
72
73 {
74     welcome_ui_->setupUi(this);
75
76     recent_files_ = welcome_ui_->recentList;
77
78     welcome_ui_->captureFilterComboBox->setEnabled(false);
79
80     QColor hover_color = ColorUtils::alphaBlend(palette().window(), palette().highlight(), 0.5);
81
82     QString welcome_ss = QString(
83                 "MainWelcome {"
84                 "  padding: 1em;"
85                 " }"
86                 "MainWelcome, QAbstractItemView {"
87                 "  background-color: palette(base);"
88                 "  color: palette(text);"
89                 " }"
90                 "QAbstractItemView {"
91                 "  border: 0;"
92                 "}"
93                 );
94 #if !defined(Q_OS_WIN)
95     welcome_ss += QString(
96                 "QAbstractItemView:item:hover {"
97                 "  background-color: %1;"
98                 "  color: palette(text);"
99                 "}"
100                 )
101             .arg(hover_color.name());
102 #endif
103     setStyleSheet(welcome_ss);
104
105     QString banner_ss = QString(
106                 "QLabel {"
107                 "  border-radius: 0.33em;"
108                 "  color: %1;"
109                 "  background-color: %2;"
110                 "  padding: 0.33em;"
111                 "}"
112                 )
113             .arg(QColor(tango_aluminium_6).name())   // Text color
114             .arg(QColor(tango_sky_blue_2).name());   // Background color
115     welcome_ui_->mainWelcomeBanner->setStyleSheet(banner_ss);
116
117     QString title_button_ss = QString(
118             "QLabel {"
119             "  color: %1;"
120             "}"
121             "QLabel::hover {"
122             "  color: %2;"
123             "}"
124             )
125             .arg(QColor(tango_aluminium_4).name())   // Text color
126             .arg(QColor(tango_sky_blue_4).name());   // Hover color
127
128     // XXX Is there a better term than "flavor"? Provider? Admonition (a la DocBook)?
129     // Release_source?
130     // Typical use cases are automated builds from wireshark.org and private,
131     // not-for-redistribution packages.
132     if (flavor_.isEmpty()) {
133         welcome_ui_->flavorBanner->hide();
134     } else {
135         // If needed there are a couple of ways we can make this customizable.
136         // - Add one or more classes, e.g. "note" or "warning" similar to
137         //   SyntaxLineEdit, which we can then expose vi #defines.
138         // - Just expose direct color values via #defines.
139         QString flavor_ss = QString(
140                     "QLabel {"
141                     "  border-radius: 0.25em;"
142                     "  color: %1;"
143                     "  background-color: %2;"
144                     "  padding: 0.25em;"
145                     "}"
146                     )
147                 .arg("white") //   Text color
148                 .arg("#2c4bc4"); // Background color. Matches capture start button.
149         //            .arg(QColor(tango_butter_5).name());      // "Warning" background
150
151         welcome_ui_->flavorBanner->setText(flavor_);
152         welcome_ui_->flavorBanner->setStyleSheet(flavor_ss);
153     }
154     welcome_ui_->captureLabel->setStyleSheet(title_button_ss);
155     welcome_ui_->recentLabel->setStyleSheet(title_button_ss);
156     welcome_ui_->helpLabel->setStyleSheet(title_button_ss);
157
158 #ifdef Q_OS_MAC
159     recent_files_->setAttribute(Qt::WA_MacShowFocusRect, false);
160 #endif
161
162     welcome_ui_->openFrame->hide();
163     recent_files_->setStyleSheet(
164             "QListWidget::item {"
165             "  padding-top: 0.2em;"
166             "  padding-bottom: 0.2em;"
167             "}"
168             "QListWidget::item::first {"
169             "  padding-top: 0;"
170             "}"
171             "QListWidget::item::last {"
172             "  padding-bottom: 0;"
173             "}"
174             );
175     recent_files_->setTextElideMode(Qt::ElideLeft);
176
177     recent_ctx_menu_ = new QMenu(this);
178     welcome_ui_->recentList->setContextMenuPolicy(Qt::CustomContextMenu);
179     connect(recent_files_, SIGNAL(customContextMenuRequested(QPoint)),
180             this, SLOT(showRecentContextMenu(QPoint)));
181
182     connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
183     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(appInitialized()));
184     connect(wsApp, SIGNAL(localInterfaceListChanged()), this, SLOT(interfaceListChanged()));
185     connect(welcome_ui_->interfaceFrame, SIGNAL(itemSelectionChanged()),
186             welcome_ui_->captureFilterComboBox, SIGNAL(interfacesChanged()));
187     connect(welcome_ui_->interfaceFrame, SIGNAL(typeSelectionChanged()),
188                     this, SLOT(interfaceListChanged()));
189     connect(welcome_ui_->interfaceFrame, SIGNAL(itemSelectionChanged()), this, SLOT(interfaceSelected()));
190     connect(welcome_ui_->captureFilterComboBox->lineEdit(), SIGNAL(textEdited(QString)),
191             this, SLOT(captureFilterTextEdited(QString)));
192     connect(welcome_ui_->captureFilterComboBox, SIGNAL(pushFilterSyntaxStatus(const QString&)),
193             this, SIGNAL(pushFilterSyntaxStatus(const QString&)));
194     connect(welcome_ui_->captureFilterComboBox, SIGNAL(popFilterSyntaxStatus()),
195             this, SIGNAL(popFilterSyntaxStatus()));
196     connect(welcome_ui_->captureFilterComboBox, SIGNAL(captureFilterSyntaxChanged(bool)),
197             this, SIGNAL(captureFilterSyntaxChanged(bool)));
198     connect(welcome_ui_->captureFilterComboBox, SIGNAL(startCapture()),
199             this, SIGNAL(startCapture()));
200     connect(recent_files_, SIGNAL(itemActivated(QListWidgetItem *)), this, SLOT(openRecentItem(QListWidgetItem *)));
201     updateRecentCaptures();
202
203 #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
204     QGraphicsBlurEffect *blur = new QGraphicsBlurEffect(welcome_ui_->childContainer);
205     blur->setBlurRadius(2);
206     welcome_ui_->childContainer->setGraphicsEffect(blur);
207 #endif
208
209     splash_overlay_ = new SplashOverlay(this);
210 }
211
212 MainWelcome::~MainWelcome()
213 {
214     delete welcome_ui_;
215 }
216
217 InterfaceFrame *MainWelcome::getInterfaceFrame()
218 {
219     return welcome_ui_->interfaceFrame;
220 }
221
222 const QString MainWelcome::captureFilter()
223 {
224     return welcome_ui_->captureFilterComboBox->currentText();
225 }
226
227 void MainWelcome::setCaptureFilter(const QString capture_filter)
228 {
229     // capture_filter comes from the current filter in
230     // CaptureInterfacesDialog. We need to find a good way to handle
231     // multiple filters.
232     welcome_ui_->captureFilterComboBox->lineEdit()->setText(capture_filter);
233 }
234
235 void MainWelcome::interfaceListChanged()
236 {
237     QString btnText = tr("All interfaces shown");
238     if (welcome_ui_->interfaceFrame->interfacesHidden() > 0) {
239         btnText = tr("%n interface(s) shown, %1 hidden", "",
240                      welcome_ui_->interfaceFrame->interfacesPresent())
241                 .arg(welcome_ui_->interfaceFrame->interfacesHidden());
242     }
243     welcome_ui_->btnInterfaceType->setText(btnText);
244     welcome_ui_->btnInterfaceType->setMenu(welcome_ui_->interfaceFrame->getSelectionMenu());
245 }
246
247 void MainWelcome::appInitialized()
248 {
249     // XXX Add a "check for updates" link?
250     QString full_release = tr("You are running Wireshark ");
251     full_release += get_ws_vcs_version_info();
252     full_release += tr(".");
253 #ifdef HAVE_SOFTWARE_UPDATE
254     if (prefs.gui_update_enabled) {
255         full_release += tr(" You receive automatic updates.");
256     } else {
257         full_release += tr(" You have disabled automatic updates.");
258     }
259 #else
260     // XXX Is there a way to tell if the user installed Wireshark via an
261     // external package manager? If so we could say so here. We could
262     // also add a link to the download page.
263 #endif
264     welcome_ui_->fullReleaseLabel->setText(full_release);
265
266 #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
267     welcome_ui_->childContainer->setGraphicsEffect(NULL);
268 #endif
269
270 #ifdef HAVE_LIBPCAP
271     welcome_ui_->captureFilterComboBox->lineEdit()->setText(global_capture_opts.default_options.cfilter);
272 #endif // HAVE_LIBPCAP
273
274     welcome_ui_->captureFilterComboBox->setEnabled(true);
275
276     interfaceListChanged();
277
278     welcome_ui_->interfaceFrame->ensureSelectedInterface();
279
280     delete splash_overlay_;
281     splash_overlay_ = NULL;
282 }
283
284 #ifdef HAVE_LIBPCAP
285 // Update each selected device cfilter when the user changes the contents
286 // of the capture filter lineedit. We do so here so that we don't clobber
287 // filters set in the Capture Options / Interfaces dialog or ones set via
288 // the command line.
289 void MainWelcome::captureFilterTextEdited(const QString capture_filter)
290 {
291     if (global_capture_opts.num_selected > 0) {
292         interface_t *device;
293
294         for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) {
295             device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
296             if (!device->selected) {
297                 continue;
298             }
299             //                if (device->active_dlt == -1) {
300             //                    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The link type of interface %s was not specified.", device->name);
301             //                    continue;  /* Programming error: somehow managed to select an "unsupported" entry */
302             //                }
303             g_free(device->cfilter);
304             if (capture_filter.isEmpty()) {
305                 device->cfilter = NULL;
306             } else {
307                 device->cfilter = qstring_strdup(capture_filter);
308             }
309             //                update_filter_string(device->name, filter_text);
310         }
311     }
312 }
313 #else
314 // No-op if we don't have capturing.
315 void MainWelcome::captureFilterTextEdited(const QString)
316 {
317 }
318 #endif
319
320 // The interface list selection has changed. At this point the user might
321 // have entered a filter or we might have pre-filled one from a number of
322 // sources such as our remote connection, the command line, or a previous
323 // selection.
324 // Must not change any interface data.
325 void MainWelcome::interfaceSelected()
326 {
327     QPair <const QString, bool> sf_pair = CaptureFilterEdit::getSelectedFilter();
328     const QString user_filter = sf_pair.first;
329     bool conflict = sf_pair.second;
330
331     if (conflict) {
332         welcome_ui_->captureFilterComboBox->lineEdit()->clear();
333         welcome_ui_->captureFilterComboBox->setConflict(true);
334     } else {
335         welcome_ui_->captureFilterComboBox->lineEdit()->setText(user_filter);
336     }
337
338     // Notify others (capture interfaces dialog) that the selection has changed.
339     emit interfacesChanged();
340 }
341
342 #ifdef HAVE_EXTCAP
343 void MainWelcome::on_interfaceFrame_showExtcapOptions(QString device_name)
344 {
345     emit showExtcapOptions(device_name);
346 }
347 #endif
348
349 void MainWelcome::on_interfaceFrame_startCapture()
350 {
351     emit startCapture();
352 }
353
354 void MainWelcome::updateRecentCaptures() {
355     QString itemLabel;
356     QListWidgetItem *rfItem;
357     QFont rfFont;
358     QString selectedFilename;
359
360     if (!recent_files_->selectedItems().isEmpty()) {
361         rfItem = recent_files_->selectedItems().first();
362         selectedFilename = rfItem->data(Qt::UserRole).toString();
363     }
364
365     if (wsApp->recentItems().count() == 0) {
366        // Recent menu has been cleared, remove all recent files.
367        while (recent_files_->count()) {
368           delete recent_files_->item(0);
369        }
370     }
371
372     int rfRow = 0;
373     foreach (recent_item_status *ri, wsApp->recentItems()) {
374         itemLabel = ri->filename;
375
376         if (rfRow >= recent_files_->count()) {
377             recent_files_->addItem(itemLabel);
378         }
379
380         itemLabel.append(" (");
381         if (ri->accessible) {
382             if (ri->size/1024/1024/1024 > 10) {
383                 itemLabel.append(QString("%1 GB").arg(ri->size/1024/1024/1024));
384             } else if (ri->size/1024/1024 > 10) {
385                 itemLabel.append(QString("%1 MB").arg(ri->size/1024/1024));
386             } else if (ri->size/1024 > 10) {
387                 itemLabel.append(QString("%1 KB").arg(ri->size/1024));
388             } else {
389                 itemLabel.append(QString("%1 Bytes").arg(ri->size));
390             }
391         } else {
392             itemLabel.append(tr("not found"));
393         }
394         itemLabel.append(")");
395         rfFont.setItalic(!ri->accessible);
396         rfItem = recent_files_->item(rfRow);
397         rfItem->setText(itemLabel);
398         rfItem->setData(Qt::AccessibleTextRole, itemLabel);
399         rfItem->setData(Qt::UserRole, ri->filename);
400         rfItem->setFlags(ri->accessible ? Qt::ItemIsSelectable | Qt::ItemIsEnabled : Qt::NoItemFlags);
401         rfItem->setFont(rfFont);
402         if (ri->filename == selectedFilename) {
403             recent_files_->setItemSelected(rfItem, true);
404         }
405         rfRow++;
406     }
407
408     int row = recent_files_->count();
409     while (row > 0 && (row > (int) prefs.gui_recent_files_count_max || row > rfRow)) {
410         row--;
411         delete recent_files_->item(row);
412     }
413     if (recent_files_->count() > 0) {
414         welcome_ui_->openFrame->animatedShow();
415     } else {
416         welcome_ui_->openFrame->animatedHide();
417     }
418 }
419
420 void MainWelcome::openRecentItem(QListWidgetItem *item) {
421     QString cfPath = item->data(Qt::UserRole).toString();
422     emit recentFileActivated(cfPath);
423 }
424
425 void MainWelcome::resizeEvent(QResizeEvent *event)
426 {
427     if (splash_overlay_)
428         splash_overlay_->resize(event->size());
429 //    event->accept();
430
431     QFrame::resizeEvent(event);
432 }
433
434 void MainWelcome::setCaptureFilterText(const QString capture_filter)
435 {
436     welcome_ui_->captureFilterComboBox->lineEdit()->setText(capture_filter);
437     captureFilterTextEdited(capture_filter);
438 }
439
440 void MainWelcome::changeEvent(QEvent* event)
441 {
442     if (0 != event)
443     {
444         switch (event->type())
445         {
446         case QEvent::LanguageChange:
447             welcome_ui_->retranslateUi(this);
448             welcome_ui_->flavorBanner->setText(flavor_);
449             interfaceListChanged();
450             break;
451         default:
452             break;
453         }
454     }
455     QFrame::changeEvent(event);
456 }
457
458 void MainWelcome::showRecentContextMenu(QPoint pos)
459 {
460     QListWidgetItem *li = recent_files_->itemAt(pos);
461     if (!li) return;
462
463     recent_ctx_menu_->clear();
464
465     QString cf_path = li->data(Qt::UserRole).toString();
466
467     QAction *show_action = recent_ctx_menu_->addAction(show_in_str_);
468     show_action->setData(cf_path);
469     connect(show_action, SIGNAL(triggered(bool)), this, SLOT(showRecentFolder()));
470
471     QAction *copy_action = recent_ctx_menu_->addAction(tr("Copy file path"));
472     copy_action->setData(cf_path);
473     connect(copy_action, SIGNAL(triggered(bool)), this, SLOT(copyRecentPath()));
474
475     recent_ctx_menu_->addSeparator();
476
477     QAction *remove_action = recent_ctx_menu_->addAction(tr("Remove"));
478     remove_action->setData(cf_path);
479     connect(remove_action, SIGNAL(triggered(bool)), this, SLOT(removeRecentPath()));
480
481     recent_ctx_menu_->exec(recent_files_->mapToGlobal(pos));
482 }
483
484 void MainWelcome::showRecentFolder()
485 {
486     QAction *ria = qobject_cast<QAction*>(sender());
487     if (!ria) return;
488
489     QString cf_path = ria->data().toString();
490     desktop_show_in_folder(cf_path);
491 }
492
493 void MainWelcome::copyRecentPath()
494 {
495     QAction *ria = qobject_cast<QAction*>(sender());
496     if (!ria) return;
497
498     QString cf_path = ria->data().toString();
499     if (cf_path.isEmpty()) return;
500
501     wsApp->clipboard()->setText(cf_path);
502 }
503
504 void MainWelcome::removeRecentPath()
505 {
506     QAction *ria = qobject_cast<QAction*>(sender());
507     if (!ria) return;
508
509     QString cf_path = ria->data().toString();
510     if (cf_path.isEmpty()) return;
511
512     wsApp->removeRecentItem(cf_path);
513 }
514
515 void MainWelcome::on_captureLabel_clicked()
516 {
517     wsApp->doTriggerMenuItem(WiresharkApplication::CaptureOptionsDialog);
518 }
519
520 void MainWelcome::on_helpLabel_clicked()
521 {
522     QDesktopServices::openUrl(QUrl(topic_online_url(ONLINEPAGE_DOCS)));
523 }
524
525 void MainWelcome::on_recentLabel_clicked()
526 {
527     wsApp->doTriggerMenuItem(WiresharkApplication::FileOpenDialog);
528 }
529
530 /*
531  * Editor modelines
532  *
533  * Local Variables:
534  * c-basic-offset: 4
535  * tab-width: 8
536  * indent-tabs-mode: nil
537  * End:
538  *
539  * ex: set shiftwidth=4 tabstop=8 expandtab:
540  * :indentSize=4:tabSize=8:noTabs=true:
541  */