1 //////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (c) 2019, The Regents of the University of California
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice, this
11 // list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright notice,
14 // this list of conditions and the following disclaimer in the documentation
15 // and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32
33 #include <QComboBox>
34 #include <QDebug>
35 #include <QHBoxLayout>
36 #include <QPushButton>
37 #include <QSortFilterProxyModel>
38 #include <QString>
39 #include <QToolButton>
40 #include <QVBoxLayout>
41 #include <iomanip>
42 #include <sstream>
43 #include <string>
44
45 #include "gui/gui.h"
46 #include "highlightGroupDialog.h"
47 #include "selectHighlightWindow.h"
48
49 namespace gui {
50
getBBoxString(odb::Rect & bbox,odb::dbDatabase * db)51 static QString getBBoxString(odb::Rect& bbox, odb::dbDatabase* db)
52 {
53 double to_microns = db->getChip()->getBlock()->getDbUnitsPerMicron();
54 std::stringstream ss;
55 ss << std::fixed << std::setprecision(5) << "(";
56 ss << bbox.xMin() / to_microns << ",";
57 ss << bbox.yMin() / to_microns << "), (";
58 ss << bbox.xMax() / to_microns << ",";
59 ss << bbox.yMax() / to_microns << ")";
60 return QString::fromStdString(ss.str());
61 }
62
SelectionModel(const SelectionSet & objs)63 SelectionModel::SelectionModel(const SelectionSet& objs)
64 : db_(nullptr), objs_(objs)
65 {
66 }
67
populateModel()68 void SelectionModel::populateModel()
69 {
70 beginResetModel();
71 table_data_.clear();
72 table_data_.reserve(objs_.size());
73 for (auto& obj : objs_) {
74 table_data_.push_back(&obj);
75 }
76 endResetModel();
77 }
78
rowCount(const QModelIndex & parent) const79 int SelectionModel::rowCount(const QModelIndex& parent) const
80 {
81 Q_UNUSED(parent);
82 return objs_.size();
83 }
84
columnCount(const QModelIndex & parent) const85 int SelectionModel::columnCount(const QModelIndex& parent) const
86 {
87 Q_UNUSED(parent);
88 return 3;
89 }
90
data(const QModelIndex & index,int role) const91 QVariant SelectionModel::data(const QModelIndex& index, int role) const
92 {
93 if (!index.isValid() || role != Qt::DisplayRole) {
94 return QVariant();
95 }
96 unsigned int row_index = index.row();
97 if (row_index > table_data_.size())
98 return QVariant();
99 const std::string obj_name = table_data_[row_index]->getName();
100 const std::string obj_type = table_data_[row_index]->getTypeName();
101 if (index.column() == 0) {
102 return QString::fromStdString(obj_name);
103 } else if (index.column() == 1) {
104 return QString::fromStdString(obj_type);
105 } else if (index.column() == 2) {
106 odb::Rect bbox;
107 bool valid = table_data_[row_index]->getBBox(bbox);
108 return valid ? getBBoxString(bbox, db_) : "<none>";
109 }
110 return QVariant();
111 }
112
headerData(int section,Qt::Orientation orientation,int role) const113 QVariant SelectionModel::headerData(int section,
114 Qt::Orientation orientation,
115 int role) const
116 {
117 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
118 if (section == 0) {
119 return QString("Object");
120 } else if (section == 1) {
121 return QString("Type");
122 } else if (section == 2) {
123 return QString("Bounds");
124 }
125 }
126 return QVariant();
127 }
128
HighlightModel(const HighlightSet & objs)129 HighlightModel::HighlightModel(const HighlightSet& objs)
130 : db_(nullptr), objs_(objs)
131 {
132 }
133
populateModel()134 void HighlightModel::populateModel()
135 {
136 beginResetModel();
137 table_data_.clear();
138 table_data_.reserve(objs_.size());
139 int highlight_group = 0;
140 for (auto& highlight_objs : objs_) {
141 for (auto& obj : highlight_objs) {
142 table_data_.push_back(std::make_pair(highlight_group, &obj));
143 }
144 ++highlight_group;
145 }
146 endResetModel();
147 }
148
rowCount(const QModelIndex & parent) const149 int HighlightModel::rowCount(const QModelIndex& parent) const
150 {
151 Q_UNUSED(parent);
152 return table_data_.size();
153 }
154
columnCount(const QModelIndex & parent) const155 int HighlightModel::columnCount(const QModelIndex& parent) const
156 {
157 Q_UNUSED(parent);
158 return 4;
159 }
160
data(const QModelIndex & index,int role) const161 QVariant HighlightModel::data(const QModelIndex& index, int role) const
162 {
163 if (!index.isValid()
164 || (role != Qt::DisplayRole && role != Qt::EditRole
165 && role != Qt::BackgroundRole)) {
166 return QVariant();
167 }
168 if (role == Qt::BackgroundRole && index.column() != 3)
169 return QVariant();
170 else if (role == Qt::BackgroundRole && index.column() == 3) {
171 auto highlight_color
172 = Painter::highlightColors[table_data_[index.row()].first];
173 return QColor(highlight_color.r, highlight_color.g, highlight_color.b, 100);
174 }
175 unsigned int row_index = index.row();
176 if (row_index > table_data_.size())
177 return QVariant();
178 std::string obj_name = table_data_[row_index].second->getName();
179 std::string obj_type("");
180 if (obj_name.rfind("Net: ", 0) == 0) {
181 obj_name = obj_name.substr(5);
182 obj_type = "Net";
183 } else if (obj_name.rfind("Inst: ", 0) == 0) {
184 obj_name = obj_name.substr(6);
185 obj_type = "Instance";
186 }
187 if (index.column() == 0) {
188 return QString::fromStdString(obj_name);
189 } else if (index.column() == 1) {
190 return QString::fromStdString(obj_type);
191 } else if (index.column() == 2) {
192 odb::Rect bbox;
193 bool valid = table_data_[row_index].second->getBBox(bbox);
194 return valid ? getBBoxString(bbox, db_) : "<none>";
195 } else if (index.column() == 3) {
196 return QString("Group ")
197 + QString::number(table_data_[row_index].first + 1);
198 }
199 return QVariant();
200 }
201
headerData(int section,Qt::Orientation orientation,int role) const202 QVariant HighlightModel::headerData(int section,
203 Qt::Orientation orientation,
204 int role) const
205 {
206 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
207 if (section == 0) {
208 return QString("Object");
209 } else if (section == 1) {
210 return QString("Type");
211 } else if (section == 2) {
212 return QString("Bounds");
213 } else if (section == 3) {
214 return QString("Highlight Group");
215 }
216 }
217 return QVariant();
218 }
219
highlightGroup(const QModelIndex & index) const220 int HighlightModel::highlightGroup(const QModelIndex& index) const
221 {
222 int row_index = index.row();
223 return table_data_[row_index].first;
224 }
225
setData(const QModelIndex & index,const QVariant & value,int role)226 bool HighlightModel::setData(const QModelIndex& index,
227 const QVariant& value,
228 int role)
229 {
230 return false;
231 }
232
HighlightGroupDelegate(QObject * parent)233 HighlightGroupDelegate::HighlightGroupDelegate(QObject* parent)
234 : QStyledItemDelegate(parent)
235 {
236 items_.push_back("Group 1");
237 items_.push_back("Group 2");
238 items_.push_back("Group 3");
239 items_.push_back("Group 4");
240 items_.push_back("Group 5");
241 items_.push_back("Group 6");
242 items_.push_back("Group 7");
243 items_.push_back("Group 8");
244 }
245
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex & index) const246 QWidget* HighlightGroupDelegate::createEditor(
247 QWidget* parent,
248 const QStyleOptionViewItem& /* option */,
249 const QModelIndex& index) const
250 {
251 QComboBox* editor = new QComboBox(parent);
252 for (unsigned int i = 0; i < items_.size(); ++i) {
253 editor->addItem(items_[i].c_str());
254 }
255 editor->setFrame(true);
256 editor->setEditable(true);
257 return editor;
258 }
259
setEditorData(QWidget * editor,const QModelIndex & index) const260 void HighlightGroupDelegate::setEditorData(QWidget* editor,
261 const QModelIndex& index) const
262 {
263 QString value = index.model()->data(index, Qt::EditRole).toString();
264 QComboBox* combo_box = static_cast<QComboBox*>(editor);
265 combo_box->setCurrentText(value);
266 }
267
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const268 void HighlightGroupDelegate::setModelData(QWidget* editor,
269 QAbstractItemModel* model,
270 const QModelIndex& index) const
271 {
272 QComboBox* combo_box = static_cast<QComboBox*>(editor);
273 model->setData(index, combo_box->currentIndex(), Qt::EditRole);
274 }
275
updateEditorGeometry(QWidget * editor,const QStyleOptionViewItem & option,const QModelIndex &) const276 void HighlightGroupDelegate::updateEditorGeometry(
277 QWidget* editor,
278 const QStyleOptionViewItem& option,
279 const QModelIndex& /* index */) const
280 {
281 editor->setGeometry(option.rect);
282 }
283
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const284 void HighlightGroupDelegate::paint(QPainter* painter,
285 const QStyleOptionViewItem& option,
286 const QModelIndex& index) const
287 {
288 QStyleOptionViewItem my_option = option;
289
290 QString text = index.model()->data(index, Qt::EditRole).toString();
291 my_option.text = text;
292 QApplication::style()->drawControl(
293 QStyle::CE_ItemViewItem, &my_option, painter);
294 }
295
SelectHighlightWindow(const SelectionSet & sel_set,const HighlightSet & hlt_set,QWidget * parent)296 SelectHighlightWindow::SelectHighlightWindow(const SelectionSet& sel_set,
297 const HighlightSet& hlt_set,
298 QWidget* parent)
299 : QDockWidget(parent),
300 ui(new Ui::SelectHighlightWidget),
301 selection_model_(sel_set),
302 highlight_model_(hlt_set),
303 select_context_menu_(new QMenu(this)),
304 highlight_context_menu_(new QMenu(this))
305 {
306 ui->setupUi(this);
307
308 QSortFilterProxyModel* sel_filter_proxy = new QSortFilterProxyModel(this);
309 sel_filter_proxy->setSourceModel(&selection_model_);
310
311 QSortFilterProxyModel* hlt_filter_proxy = new QSortFilterProxyModel(this);
312 hlt_filter_proxy->setSourceModel(&highlight_model_);
313
314 ui->selTableView->setModel(sel_filter_proxy);
315 ui->hltTableView->setModel(hlt_filter_proxy);
316
317 connect(ui->findEditInSel, &QLineEdit::returnPressed, this, [this]() {
318 this->ui->selTableView->keyboardSearch(ui->findEditInSel->text());
319 });
320 connect(ui->findEditInHlt, &QLineEdit::returnPressed, this, [this]() {
321 this->ui->hltTableView->keyboardSearch(ui->findEditInSel->text());
322 });
323
324 connect(ui->selTableView,
325 SIGNAL(customContextMenuRequested(QPoint)),
326 this,
327 SLOT(showSelectCustomMenu(QPoint)));
328 connect(ui->hltTableView,
329 SIGNAL(customContextMenuRequested(QPoint)),
330 this,
331 SLOT(showHighlightCustomMenu(QPoint)));
332 ui->selTableView->horizontalHeader()->setSectionResizeMode(
333 QHeaderView::Stretch);
334 ui->hltTableView->horizontalHeader()->setSectionResizeMode(
335 QHeaderView::Stretch);
336
337 QAction* remove_sel_item_act = select_context_menu_->addAction("De-Select");
338 QAction* remove_all_sel_items = select_context_menu_->addAction("Clear All");
339 QAction* highlight_sel_item_act
340 = select_context_menu_->addAction("Highlight");
341 select_context_menu_->addSeparator();
342 QAction* show_sel_item_act
343 = select_context_menu_->addAction("Zoom In Layout");
344
345 connect(
346 remove_sel_item_act, SIGNAL(triggered()), this, SLOT(deselectItems()));
347 connect(remove_all_sel_items, &QAction::triggered, this, [this]() {
348 emit clearAllSelections();
349 });
350 connect(highlight_sel_item_act,
351 SIGNAL(triggered()),
352 this,
353 SLOT(highlightSelectedItems()));
354 connect(show_sel_item_act,
355 SIGNAL(triggered()),
356 this,
357 SLOT(zoomInSelectedItems()));
358
359 QAction* remove_hlt_item_act
360 = highlight_context_menu_->addAction("De-Highlight");
361 QAction* remove_all_hlt_items
362 = highlight_context_menu_->addAction("Clear All");
363 highlight_context_menu_->addSeparator();
364 QAction* show_hlt_item_act
365 = highlight_context_menu_->addAction("Zoom In Layout");
366
367 connect(
368 remove_hlt_item_act, SIGNAL(triggered()), this, SLOT(dehighlightItems()));
369 connect(remove_all_hlt_items, &QAction::triggered, this, [this]() {
370 emit clearAllHighlights();
371 });
372 connect(show_hlt_item_act,
373 SIGNAL(triggered()),
374 this,
375 SLOT(zoomInHighlightedItems()));
376
377 ui->selTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
378 ui->hltTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
379
380 ui->tabWidget->setCurrentIndex(0);
381 }
382
~SelectHighlightWindow()383 SelectHighlightWindow::~SelectHighlightWindow()
384 {
385 }
386
updateSelectionModel()387 void SelectHighlightWindow::updateSelectionModel()
388 {
389 selection_model_.populateModel();
390 ui->tabWidget->setCurrentWidget(ui->selTab);
391 }
392
updateHighlightModel()393 void SelectHighlightWindow::updateHighlightModel()
394 {
395 highlight_model_.populateModel();
396 ui->tabWidget->setCurrentWidget(ui->hltTab);
397 }
398
showSelectCustomMenu(QPoint pos)399 void SelectHighlightWindow::showSelectCustomMenu(QPoint pos)
400 {
401 select_context_menu_->popup(ui->selTableView->viewport()->mapToGlobal(pos));
402 }
403
showHighlightCustomMenu(QPoint pos)404 void SelectHighlightWindow::showHighlightCustomMenu(QPoint pos)
405 {
406 highlight_context_menu_->popup(
407 ui->hltTableView->viewport()->mapToGlobal(pos));
408 }
409
deselectItems()410 void SelectHighlightWindow::deselectItems()
411 {
412 auto sel_indices = ui->selTableView->selectionModel()->selectedRows();
413 QList<const Selected*> desel_items;
414 for (auto& sel_item : sel_indices) {
415 desel_items << selection_model_.getItemAt(sel_item.row());
416 }
417 emit clearSelectedItems(desel_items);
418 }
highlightSelectedItems()419 void SelectHighlightWindow::highlightSelectedItems()
420 {
421 HighlightGroupDialog dlg;
422 dlg.exec();
423 auto sel_indices = ui->selTableView->selectionModel()->selectedRows();
424 QList<const Selected*> sel_items;
425 for (auto& sel_item : sel_indices) {
426 sel_items << selection_model_.getItemAt(sel_item.row());
427 }
428 emit highlightSelectedItemsSig(sel_items, dlg.getSelectedHighlightGroup());
429 }
430
zoomInSelectedItems()431 void SelectHighlightWindow::zoomInSelectedItems()
432 {
433 auto sel_indices = ui->selTableView->selectionModel()->selectedRows();
434 QList<const Selected*> desel_items;
435 for (auto& sel_item : sel_indices) {
436 desel_items << selection_model_.getItemAt(sel_item.row());
437 }
438 emit zoomInToItems(desel_items);
439 }
440
dehighlightItems()441 void SelectHighlightWindow::dehighlightItems()
442 {
443 auto sel_indices = ui->hltTableView->selectionModel()->selectedRows();
444 QList<const Selected*> dehlt_items;
445 for (auto& sel_item : sel_indices) {
446 dehlt_items << highlight_model_.getItemAt(sel_item.row());
447 }
448 emit clearHighlightedItems(dehlt_items);
449 }
450
zoomInHighlightedItems()451 void SelectHighlightWindow::zoomInHighlightedItems()
452 {
453 auto sel_indices = ui->hltTableView->selectionModel()->selectedRows();
454 QList<const Selected*> dehlt_items;
455 for (auto& sel_item : sel_indices) {
456 dehlt_items << highlight_model_.getItemAt(sel_item.row());
457 }
458 emit zoomInToItems(dehlt_items);
459 }
460
setDb(odb::dbDatabase * db)461 void SelectHighlightWindow::setDb(odb::dbDatabase* db)
462 {
463 selection_model_.setDb(db);
464 highlight_model_.setDb(db);
465 }
466
467 } // namespace gui
468