1 /* main_window_layout.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 <ui/qt/main_window.h>
13 #include <ui/qt/widgets/additional_toolbar.h>
14 
15 #include "ui/recent.h"
16 
17 #include "epan/prefs.h"
18 
19 #include <QSplitter>
20 #include <QVector>
21 #include <QList>
22 #include <QWidget>
23 #include <QRect>
24 #include <QAction>
25 #include <QToolBar>
26 
27 #include <ui/qt/byte_view_tab.h>
28 #include <ui/qt/packet_list.h>
29 #include <ui/qt/packet_diagram.h>
30 #include <ui/qt/proto_tree.h>
31 
32 #include <wsutil/ws_assert.h>
33 
34 /*
35  * The generated Ui_MainWindow::setupUi() can grow larger than our configured limit,
36  * so turn off -Wframe-larger-than= for ui_main_window.h.
37  */
38 DIAG_OFF(frame-larger-than=)
39 #include <ui_main_window.h>
40 DIAG_ON(frame-larger-than=)
41 
showWelcome()42 void MainWindow::showWelcome()
43 {
44     main_ui_->mainStack->setCurrentWidget(welcome_page_);
45 }
46 
showCapture()47 void MainWindow::showCapture()
48 {
49     main_ui_->mainStack->setCurrentWidget(&master_split_);
50 }
51 
getLayoutWidget(layout_pane_content_e type)52 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
53     switch (type) {
54         case layout_pane_content_none:
55             return &empty_pane_;
56         case layout_pane_content_plist:
57             return packet_list_;
58         case layout_pane_content_pdetails:
59             return proto_tree_;
60         case layout_pane_content_pbytes:
61             return byte_view_tab_;
62         case layout_pane_content_pdiagram:
63             return packet_diagram_;
64         default:
65             ws_assert_not_reached();
66             return NULL;
67     }
68 }
69 
70 
71 // A new layout should be applied when it differs from the old layout AND
72 // at the following times:
73 // - At startup
74 // - When the preferences change
75 // - When the profile changes
layoutPanes()76 void MainWindow::layoutPanes()
77 {
78     QVector<unsigned> new_layout = QVector<unsigned>() << prefs.gui_layout_type
79                                                        << prefs.gui_layout_content_1
80                                                        << prefs.gui_layout_content_2
81                                                        << prefs.gui_layout_content_3
82                                                        << recent.packet_list_show
83                                                        << recent.tree_view_show
84                                                        << recent.byte_view_show
85                                                        << recent.packet_diagram_show;
86 
87     if (cur_layout_ == new_layout) return;
88 
89     QSplitter *parents[3];
90 
91     // Reparent all widgets and add them back in the proper order below.
92     // This hides each widget as well.
93     packet_list_->freeze(); // Clears tree, byte view tabs, and diagram.
94     packet_list_->setParent(main_ui_->mainStack);
95     proto_tree_->setParent(main_ui_->mainStack);
96     byte_view_tab_->setParent(main_ui_->mainStack);
97     packet_diagram_->setParent(main_ui_->mainStack);
98     empty_pane_.setParent(main_ui_->mainStack);
99     extra_split_.setParent(main_ui_->mainStack);
100 
101     // XXX We should try to preserve geometries if we can, e.g. by
102     // checking to see if the layout type is the same.
103     switch(prefs.gui_layout_type) {
104     case(layout_type_2):
105     case(layout_type_1):
106         extra_split_.setOrientation(Qt::Horizontal);
107         /* Fall Through */
108     case(layout_type_5):
109         master_split_.setOrientation(Qt::Vertical);
110         break;
111 
112     case(layout_type_4):
113     case(layout_type_3):
114         extra_split_.setOrientation(Qt::Vertical);
115         /* Fall Through */
116     case(layout_type_6):
117         master_split_.setOrientation(Qt::Horizontal);
118         break;
119 
120     default:
121         ws_assert_not_reached();
122     }
123 
124     switch(prefs.gui_layout_type) {
125     case(layout_type_5):
126     case(layout_type_6):
127         parents[0] = &master_split_;
128         parents[1] = &master_split_;
129         parents[2] = &master_split_;
130         break;
131     case(layout_type_2):
132     case(layout_type_4):
133         parents[0] = &master_split_;
134         parents[1] = &extra_split_;
135         parents[2] = &extra_split_;
136         break;
137     case(layout_type_1):
138     case(layout_type_3):
139         parents[0] = &extra_split_;
140         parents[1] = &extra_split_;
141         parents[2] = &master_split_;
142         break;
143     default:
144         ws_assert_not_reached();
145     }
146 
147     if (parents[0] == &extra_split_) {
148         master_split_.addWidget(&extra_split_);
149     }
150 
151     parents[0]->addWidget(getLayoutWidget(prefs.gui_layout_content_1));
152 
153     if (parents[2] == &extra_split_) {
154         master_split_.addWidget(&extra_split_);
155     }
156 
157     parents[1]->addWidget(getLayoutWidget(prefs.gui_layout_content_2));
158     parents[2]->addWidget(getLayoutWidget(prefs.gui_layout_content_3));
159 
160     const QList<QWidget *> ms_children = master_split_.findChildren<QWidget *>();
161 
162     extra_split_.setVisible(ms_children.contains(&extra_split_));
163     packet_list_->setVisible(ms_children.contains(packet_list_) && recent.packet_list_show);
164     proto_tree_->setVisible(ms_children.contains(proto_tree_) && recent.tree_view_show);
165     byte_view_tab_->setVisible(ms_children.contains(byte_view_tab_) && recent.byte_view_show);
166     packet_diagram_->setVisible(ms_children.contains(packet_diagram_) && recent.packet_diagram_show);
167 
168     // Show the packet list here to prevent pending resize events changing columns
169     // when the packet list is set as current widget for the first time.
170     packet_list_->show();
171 
172     packet_list_->thaw(true);
173     cur_layout_ = new_layout;
174 }
175 
176 // The recent layout geometry should be applied after the layout has been
177 // applied AND at the following times:
178 // - At startup
179 // - When the profile changes
applyRecentPaneGeometry()180 void MainWindow::applyRecentPaneGeometry()
181 {
182     // XXX This shrinks slightly each time the application is run. For some
183     // reason the master_split_ geometry is two pixels shorter when
184     // saveWindowGeometry is invoked.
185 
186     // This is also an awful lot of trouble to go through to reuse the GTK+
187     // pane settings. We might want to add gui.geometry_main_master_sizes
188     // and gui.geometry_main_extra_sizes and save QSplitter::saveState in
189     // each.
190 
191     // Force a geometry recalculation
192     QWidget *cur_w = main_ui_->mainStack->currentWidget();
193     showCapture();
194     QRect geom = main_ui_->mainStack->geometry();
195     QList<int> master_sizes = master_split_.sizes();
196     QList<int> extra_sizes = extra_split_.sizes();
197     main_ui_->mainStack->setCurrentWidget(cur_w);
198 
199     int master_last_size = master_split_.orientation() == Qt::Vertical ? geom.height() : geom.width();
200     master_last_size -= master_split_.handleWidth() * (master_sizes.length() - 1);
201 
202     int extra_last_size = extra_split_.orientation() == Qt::Vertical ? geom.height() : geom.width();
203     extra_last_size -= extra_split_.handleWidth();
204 
205     if (recent.gui_geometry_main_upper_pane > 0) {
206         master_sizes[0] = recent.gui_geometry_main_upper_pane;
207         master_last_size -= recent.gui_geometry_main_upper_pane;
208     } else {
209         master_sizes[0] = master_last_size / master_sizes.length();
210         master_last_size -= master_last_size / master_sizes.length();
211     }
212 
213     if (recent.gui_geometry_main_lower_pane > 0) {
214         if (master_sizes.length() > 2) {
215             master_sizes[1] = recent.gui_geometry_main_lower_pane;
216             master_last_size -= recent.gui_geometry_main_lower_pane;
217         } else if (extra_sizes.length() > 0) {
218             extra_sizes[0] = recent.gui_geometry_main_lower_pane;
219             extra_last_size -= recent.gui_geometry_main_lower_pane;
220             extra_sizes.last() = extra_last_size;
221         }
222     } else {
223         if (master_sizes.length() > 2) {
224             master_sizes[1] = master_last_size / 2;
225             master_last_size -= master_last_size / 2;
226         } else if (extra_sizes.length() > 0) {
227             extra_sizes[0] = extra_last_size / 2;
228             extra_last_size -= extra_last_size / 2;
229             extra_sizes.last() = extra_last_size;
230         }
231     }
232 
233     master_sizes.last() = master_last_size;
234 
235     master_split_.setSizes(master_sizes);
236     extra_split_.setSizes(extra_sizes);
237 }
238