1 /*
2 * Copyright 2016 CodiLime
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17 #include "visualization/panel.h"
18
19 #include <QComboBox>
20 #include <QHBoxLayout>
21 #include <QLabel>
22 #include <QLayoutItem>
23 #include <QVBoxLayout>
24
25 #include "util/icons.h"
26 #include "util/sampling/fake_sampler.h"
27 #include "util/sampling/uniform_sampler.h"
28 #include "util/settings/shortcuts.h"
29 #include "visualization/digram.h"
30 #include "visualization/trigram.h"
31
32 namespace veles {
33 namespace visualization {
34
35 using util::settings::shortcuts::ShortcutsModel;
36
37 const std::map<QString, VisualizationPanel::ESampler>
38 VisualizationPanel::k_sampler_map = {
39 {"No sampling", VisualizationPanel::ESampler::NO_SAMPLER},
40 {"Uniform random sampling",
41 VisualizationPanel::ESampler::UNIFORM_SAMPLER}};
42
43 /*****************************************************************************/
44 /* Public methods */
45 /*****************************************************************************/
46
VisualizationPanel(ui::MainWindowWithDetachableDockWidgets * main_window,const QSharedPointer<ui::FileBlobModel> & data_model,QWidget *)47 VisualizationPanel::VisualizationPanel(
48 ui::MainWindowWithDetachableDockWidgets* main_window,
49 const QSharedPointer<ui::FileBlobModel>& data_model, QWidget* /*parent*/)
50 : veles::ui::IconAwareView("Visualization", ":/images/trigram_icon.png"),
51 sampler_type_(k_default_sampler),
52 visualization_type_(k_default_visualization),
53 sample_size_(1024 * 1024),
54 data_model_(data_model),
55 main_window_(main_window),
56 visible_(true) {
57 sampler_ = getSampler(sampler_type_, data_, sample_size_);
58 sampler_->allowAsynchronousResampling(true);
59 minimap_sampler_ =
60 getSampler(ESampler::UNIFORM_SAMPLER, data_, k_minimap_sample_size);
61 minimap_ = new MinimapPanel(this);
62 minimap_->setSampler(minimap_sampler_);
63 connect(minimap_, &MinimapPanel::selectionChanged, this,
64 &VisualizationPanel::minimapSelectionChanged);
65
66 visualization_ = getVisualization(visualization_type_, this);
67 visualization_root_ = new QMainWindow;
68 visualization_root_->setCentralWidget(visualization_);
69 setFocusProxy(visualization_);
70
71 sampling_method_dialog_ = new SamplingMethodDialog(this);
72 sampling_method_dialog_->setSampleSize(sample_size_);
73 sampling_method_dialog_->setMaximumSampleSize(k_max_sample_size);
74
75 connect(sampling_method_dialog_, &SamplingMethodDialog::samplingMethodChanged,
76 this, &VisualizationPanel::setSamplingMethod);
77 connect(sampling_method_dialog_, &SamplingMethodDialog::sampleSizeChanged,
78 this, &VisualizationPanel::setSampleSize);
79
80 initLayout();
81 }
82
~VisualizationPanel()83 VisualizationPanel::~VisualizationPanel() {
84 delete visualization_;
85 delete minimap_;
86 delete sampler_;
87 delete minimap_sampler_;
88 }
89
setData(const QByteArray & data)90 void VisualizationPanel::setData(const QByteArray& data) {
91 delete sampler_;
92 delete minimap_sampler_;
93 data_ = data;
94 sampler_ = getSampler(sampler_type_, data_, sample_size_);
95 sampler_->allowAsynchronousResampling(true);
96 minimap_sampler_ =
97 getSampler(ESampler::UNIFORM_SAMPLER, data_, k_minimap_sample_size);
98 minimap_->setSampler(minimap_sampler_);
99 visualization_->setSampler(sampler_);
100 selection_label_->setText(prepareAddressString(
101 0, sampler_->getFileOffset(sampler_->getSampleSize())));
102 }
103
setRange(size_t start,size_t end)104 void VisualizationPanel::setRange(size_t start, size_t end) {
105 sampler_->setRange(start, end);
106 }
107
eventFilter(QObject *,QEvent * event)108 bool VisualizationPanel::eventFilter(QObject* /*watched*/, QEvent* event) {
109 // filter out timer events for not visible visualisation so that we don't
110 // waste resources on rotating something that isn't visible.
111 return !visible_ && event->type() == QEvent::Timer;
112 }
113
visibilityChanged(bool visibility)114 void VisualizationPanel::visibilityChanged(bool visibility) {
115 visible_ = visibility;
116 }
117
118 /*****************************************************************************/
119 /* Static factory methods */
120 /*****************************************************************************/
121
getSampler(ESampler type,const QByteArray & data,qint64 sample_size)122 util::ISampler* VisualizationPanel::getSampler(ESampler type,
123 const QByteArray& data,
124 qint64 sample_size) {
125 switch (type) {
126 case ESampler::NO_SAMPLER:
127 return new util::FakeSampler(data);
128 case ESampler::UNIFORM_SAMPLER:
129 auto* sampler = new util::UniformSampler(data);
130 sampler->setSampleSize(sample_size);
131 return sampler;
132 }
133 return nullptr;
134 }
135
getVisualization(EVisualization type,QWidget * parent)136 VisualizationWidget* VisualizationPanel::getVisualization(EVisualization type,
137 QWidget* parent) {
138 TrigramWidget* trigram = nullptr;
139 switch (type) {
140 case EVisualization::DIGRAM:
141 return new DigramWidget(parent);
142 case EVisualization::TRIGRAM:
143 trigram = new TrigramWidget(parent);
144 trigram->setMode(TrigramWidget::EVisualizationMode::TRIGRAM);
145 break;
146 case EVisualization::LAYERED_DIGRAM:
147 trigram = new TrigramWidget(parent);
148 trigram->setMode(TrigramWidget::EVisualizationMode::LAYERED_DIGRAM,
149 false);
150 break;
151 }
152 if (trigram != nullptr) {
153 trigram->installEventFilter(this);
154 return trigram;
155 }
156 return nullptr;
157 }
158
prepareAddressString(size_t start,size_t end)159 QString VisualizationPanel::prepareAddressString(size_t start, size_t end) {
160 auto label = QString("0x%1 : ").arg(start, 8, 16, QChar('0'));
161 label.append(QString("0x%1 ").arg(end, 8, 16, QChar('0')));
162 label.append(QString("(%1 bytes)").arg(end - start));
163 return label;
164 }
165
166 /*****************************************************************************/
167 /* Private slots */
168 /*****************************************************************************/
169
setSamplingMethod(const QString & name)170 void VisualizationPanel::setSamplingMethod(const QString& name) {
171 auto new_sampler_type = k_sampler_map.at(name);
172 if (new_sampler_type == sampler_type_) {
173 return;
174 }
175
176 auto old_sampler = sampler_;
177 sampler_ = getSampler(new_sampler_type, data_, sample_size_);
178 sampler_->allowAsynchronousResampling(true);
179 auto selection = minimap_->getSelection();
180 sampler_->setRange(selection.first, selection.second);
181 visualization_->setSampler(sampler_);
182 delete old_sampler;
183 sampler_type_ = new_sampler_type;
184 }
185
setSampleSize(size_t size)186 void VisualizationPanel::setSampleSize(size_t size) {
187 sample_size_ = size;
188 if (sampler_type_ == ESampler::UNIFORM_SAMPLER) {
189 sampler_->setSampleSize(size);
190 }
191 }
192
showDigramVisualization()193 void VisualizationPanel::showDigramVisualization() {
194 setVisualization(EVisualization::DIGRAM);
195 }
196
showTrigramVisualization()197 void VisualizationPanel::showTrigramVisualization() {
198 if (visualization_type_ == EVisualization::LAYERED_DIGRAM) {
199 visualization_type_ = EVisualization::TRIGRAM;
200 auto trigram = static_cast<TrigramWidget*>(visualization_);
201 trigram->setMode(TrigramWidget::EVisualizationMode::TRIGRAM);
202 } else {
203 setVisualization(EVisualization::TRIGRAM);
204 }
205 }
206
showLayeredDigramVisualization()207 void VisualizationPanel::showLayeredDigramVisualization() {
208 if (visualization_type_ == EVisualization::TRIGRAM) {
209 visualization_type_ = EVisualization::LAYERED_DIGRAM;
210 auto trigram = static_cast<TrigramWidget*>(visualization_);
211 trigram->setMode(TrigramWidget::EVisualizationMode::LAYERED_DIGRAM);
212 } else {
213 setVisualization(EVisualization::LAYERED_DIGRAM);
214 }
215 }
216
minimapSelectionChanged(size_t start,size_t end)217 void VisualizationPanel::minimapSelectionChanged(size_t start, size_t end) {
218 selection_label_->setText(prepareAddressString(start, end));
219 sampler_->setRange(start, end);
220 }
221
showMoreOptions()222 void VisualizationPanel::showMoreOptions() { sampling_method_dialog_->show(); }
223
224 /*****************************************************************************/
225 /* Private methods */
226 /*****************************************************************************/
227
setVisualization(EVisualization type)228 void VisualizationPanel::setVisualization(EVisualization type) {
229 if (type != visualization_type_) {
230 auto toolbars = visualization_root_->findChildren<QToolBar*>();
231 for (auto toolbar : toolbars) {
232 if (toolbar->actions().empty() || !toolbar->property("common").toBool()) {
233 toolbar->deleteLater();
234 }
235 }
236 VisualizationWidget* old = visualization_;
237 visualization_type_ = type;
238 visualization_ = getVisualization(visualization_type_, this);
239 visualization_->setSampler(sampler_);
240 visualization_root_->setCentralWidget(visualization_);
241 prepareVisualizationOptions();
242 visualization_root_->setFocus();
243 delete old;
244 }
245 }
246
refreshVisualization()247 void VisualizationPanel::refreshVisualization() {
248 if (visualization_ != nullptr) {
249 visualization_->refreshVisualization();
250 }
251 }
252
initLayout()253 void VisualizationPanel::initLayout() {
254 initOptionsPanel();
255
256 setCentralWidget(visualization_root_);
257 setDockNestingEnabled(true);
258
259 node_tree_dock_ = new QDockWidget;
260 node_tree_dock_->setWindowTitle("Node tree");
261 QSharedPointer<QItemSelectionModel> new_selection_model(
262 new QItemSelectionModel(data_model_.data()));
263 node_tree_widget_ =
264 new ui::NodeTreeWidget(main_window_, data_model_, new_selection_model);
265 node_tree_dock_->setWidget(node_tree_widget_);
266 node_tree_dock_->setContextMenuPolicy(Qt::PreventContextMenu);
267 node_tree_dock_->setAllowedAreas(Qt::LeftDockWidgetArea |
268 Qt::RightDockWidgetArea);
269 node_tree_dock_->hide();
270 addDockWidget(Qt::LeftDockWidgetArea, node_tree_dock_);
271
272 minimap_dock_ = new QDockWidget;
273 minimap_dock_->setWindowTitle("Minimap");
274 minimap_dock_->setWidget(minimap_);
275 minimap_dock_->setContextMenuPolicy(Qt::PreventContextMenu);
276 minimap_dock_->setAllowedAreas(Qt::LeftDockWidgetArea |
277 Qt::RightDockWidgetArea);
278 tabifyDockWidget(node_tree_dock_, minimap_dock_);
279 ui::MainWindowWithDetachableDockWidgets::splitDockWidget2(
280 this, node_tree_dock_, minimap_dock_, Qt::Horizontal);
281
282 // connect(show_node_tree_act_, &QAction::toggled,
283 // node_tree_dock_, &QDockWidget::setVisible);
284 connect(show_minimap_act_, &QAction::toggled, minimap_dock_,
285 &QDockWidget::setVisible);
286 }
287
prepareVisualizationOptions()288 void VisualizationPanel::prepareVisualizationOptions() {
289 visualization_->prepareOptions(visualization_root_);
290 }
291
initOptionsPanel()292 void VisualizationPanel::initOptionsPanel() {
293 /////////////////////////////////////
294 // Node tree / minimap
295 // show_node_tree_act_ = new QAction(QIcon(":/images/show_node_tree.png"),
296 // tr("&Node tree"), this);
297 // show_node_tree_act_->setToolTip(tr("Node tree"));
298 // show_node_tree_act_->setEnabled(true);
299 // show_node_tree_act_->setCheckable(true);
300 // show_node_tree_act_->setChecked(false);
301 show_minimap_act_ = ShortcutsModel::getShortcutsModel()->createQAction(
302 util::settings::shortcuts::SHOW_MINIMAP, this,
303 QIcon(":/images/show_minimap.png"), Qt::WidgetWithChildrenShortcut);
304 show_minimap_act_->setToolTip(tr("Minimap"));
305 show_minimap_act_->setEnabled(true);
306 show_minimap_act_->setCheckable(true);
307 show_minimap_act_->setChecked(true);
308
309 tools_tool_bar_ = new QToolBar(tr("Tools"));
310 tools_tool_bar_->setMovable(false);
311 addAction(show_minimap_act_);
312 // addAction(show_node_tree_act_);
313 // tools_tool_bar_->addAction(show_node_tree_act_);
314 tools_tool_bar_->addAction(show_minimap_act_);
315 tools_tool_bar_->setContextMenuPolicy(Qt::PreventContextMenu);
316 tools_tool_bar_->setProperty("common", true);
317 tools_tool_bar_->addSeparator();
318 visualization_root_->addToolBar(tools_tool_bar_);
319
320 /////////////////////////////////////
321 // Modes: digram / trigrams
322 visualization_modes_group_ = new QActionGroup(this);
323 QColor icon_color = palette().color(QPalette::WindowText);
324
325 digram_action_ = ShortcutsModel::getShortcutsModel()->createQAction(
326 util::settings::shortcuts::VISUALIZATION_DIGRAM,
327 visualization_modes_group_,
328 util::getColoredIcon(":/images/digram_icon.png", icon_color),
329 Qt::WidgetWithChildrenShortcut);
330 digram_action_->setToolTip("Digram Visualization");
331 digram_action_->setCheckable(true);
332 connect(digram_action_, &QAction::triggered, this,
333 &VisualizationPanel::showDigramVisualization);
334
335 trigram_action_ = ShortcutsModel::getShortcutsModel()->createQAction(
336 util::settings::shortcuts::VISUALIZATION_TRIGRAM,
337 visualization_modes_group_,
338 util::getColoredIcon(":/images/trigram_icon.png", icon_color),
339 Qt::WidgetWithChildrenShortcut);
340 trigram_action_->setToolTip("Trigram Visualization");
341 trigram_action_->setCheckable(true);
342 connect(trigram_action_, &QAction::triggered, this,
343 &VisualizationPanel::showTrigramVisualization);
344
345 layered_digram_action_ = ShortcutsModel::getShortcutsModel()->createQAction(
346 util::settings::shortcuts::VISUALIZATION_LAYERED_DIGRAM,
347 visualization_modes_group_,
348 util::getColoredIcon(":/images/layered_digram_icon.png", icon_color,
349 false),
350 Qt::WidgetWithChildrenShortcut);
351 layered_digram_action_->setToolTip("Layered Digram Visualization");
352 layered_digram_action_->setCheckable(true);
353 connect(layered_digram_action_, &QAction::triggered, this,
354 &VisualizationPanel::showLayeredDigramVisualization);
355
356 switch (visualization_type_) {
357 case EVisualization::DIGRAM:
358 digram_action_->setChecked(true);
359 break;
360 case EVisualization::TRIGRAM:
361 trigram_action_->setChecked(true);
362 break;
363 case EVisualization::LAYERED_DIGRAM:
364 layered_digram_action_->setChecked(true);
365 break;
366 }
367
368 modes_tool_bar_ = new QToolBar(tr("Modes"));
369 modes_tool_bar_->setMovable(false);
370 addAction(digram_action_);
371 addAction(trigram_action_);
372 addAction(layered_digram_action_);
373 modes_tool_bar_->addAction(digram_action_);
374 modes_tool_bar_->addAction(trigram_action_);
375 modes_tool_bar_->addAction(layered_digram_action_);
376 modes_tool_bar_->setContextMenuPolicy(Qt::PreventContextMenu);
377 modes_tool_bar_->addSeparator();
378 modes_tool_bar_->setProperty("common", true);
379 visualization_root_->addToolBar(modes_tool_bar_);
380
381 /////////////////////////////////////
382 // Selection
383 auto* selection_widget_action = new QWidgetAction(this);
384 selection_label_ = new QLabel(prepareAddressString(0, 0));
385 selection_widget_action->setDefaultWidget(selection_label_);
386 selection_label_->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
387 selection_label_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
388 selection_label_->setTextInteractionFlags(Qt::TextSelectableByMouse);
389 auto* selection_toolbar = new QToolBar;
390 selection_toolbar->setMovable(false);
391 selection_toolbar->addAction(selection_widget_action);
392 selection_toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
393 selection_toolbar->setProperty("common", true);
394 visualization_root_->addToolBar(selection_toolbar);
395
396 /////////////////////////////////////
397 // Sampling
398
399 QAction* show_more_options_action =
400 ShortcutsModel::getShortcutsModel()->createQAction(
401 util::settings::shortcuts::VISUALIZATION_OPTIONS, this,
402 QIcon(":/images/more.png"), Qt::WidgetWithChildrenShortcut);
403 connect(show_more_options_action, &QAction::triggered, this,
404 &VisualizationPanel::showMoreOptions);
405 addAction(show_more_options_action);
406 selection_toolbar->addAction(show_more_options_action);
407
408 /////////////////////////////////////
409 // Additional toolbars specific for current visualization
410 prepareVisualizationOptions();
411
412 // TODO(mkow): considering that there is already some code duplication between
413 // here and HexEditWidget we should consider refactoring some parts of those
414 // classes into common base.
415 QAction* open_hex = ShortcutsModel::getShortcutsModel()->createQAction(
416 util::settings::shortcuts::OPEN_HEX, this,
417 Qt::WidgetWithChildrenShortcut);
418
419 connect(open_hex, &QAction::triggered,
420 [this]() { main_window_->createHexEditTab(data_model_); });
421 addAction(open_hex);
422
423 QAction* open_visualization =
424 ShortcutsModel::getShortcutsModel()->createQAction(
425 util::settings::shortcuts::OPEN_VISUALIZATION, this,
426 Qt::WidgetWithChildrenShortcut);
427
428 connect(open_visualization, &QAction::triggered,
429 [this]() { main_window_->createVisualization(data_model_); });
430 addAction(open_visualization);
431 }
432
433 } // namespace visualization
434 } // namespace veles
435