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