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