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 <QDebug>
34 #include <QHeaderView>
35 #include <QKeyEvent>
36 #include <QLineEdit>
37 #include <QPainter>
38 #include <QSettings>
39 #include <QVBoxLayout>
40 #include <vector>
41 
42 #include "db.h"
43 #include "displayControls.h"
44 #include "ord/InitOpenRoad.hh"
45 
46 Q_DECLARE_METATYPE(odb::dbTechLayer*);
47 
48 namespace gui {
49 
50 using namespace odb;
51 
PatternButton(Qt::BrushStyle pattern,QWidget * parent)52 PatternButton::PatternButton(Qt::BrushStyle pattern, QWidget* parent)
53     : QRadioButton(parent), pattern_(pattern)
54 {
55   setFixedWidth(100);
56 }
57 
paintEvent(QPaintEvent * event)58 void PatternButton::paintEvent(QPaintEvent* event)
59 {
60   QRadioButton::paintEvent(event);
61   auto qp = QPainter(this);
62   auto brush = QBrush(QColor("black"), pattern_);
63   qp.setBrush(brush);
64   auto button_rect = rect();
65   button_rect.adjust(18, 2, -1, -1);
66   qp.drawRect(button_rect);
67   qp.end();
68 }
69 
DisplayColorDialog(QColor color,Qt::BrushStyle pattern,QWidget * parent)70 DisplayColorDialog::DisplayColorDialog(QColor color,
71                                        Qt::BrushStyle pattern,
72                                        QWidget* parent)
73     : QDialog(parent), color_(color), pattern_(pattern)
74 {
75   buildUI();
76 }
77 
buildUI()78 void DisplayColorDialog::buildUI()
79 {
80   color_dialog_ = new QColorDialog(this);
81   color_dialog_->setOptions(QColorDialog::DontUseNativeDialog);
82   color_dialog_->setOption(QColorDialog::ShowAlphaChannel);
83   color_dialog_->setWindowFlags(Qt::Widget);
84   color_dialog_->setCurrentColor(color_);
85   pattern_group_box_ = new QGroupBox("Layer Pattern");
86   grid_layout_ = new QGridLayout();
87 
88   grid_layout_->setColumnStretch(2, 4);
89 
90   int row_index = 0;
91   for (auto& pattern_group : DisplayColorDialog::brush_patterns_) {
92     int col_index = 0;
93     for (auto pattern : pattern_group) {
94       PatternButton* pattern_button = new PatternButton(pattern);
95       pattern_buttons_.push_back(pattern_button);
96       if (pattern == pattern_)
97         pattern_button->setChecked(true);
98       else
99         pattern_button->setChecked(false);
100       grid_layout_->addWidget(pattern_button, row_index, col_index);
101       ++col_index;
102     }
103     ++row_index;
104   }
105   pattern_group_box_->setLayout(grid_layout_);
106   connect(color_dialog_, SIGNAL(accepted()), this, SLOT(acceptDialog()));
107   connect(color_dialog_, SIGNAL(rejected()), this, SLOT(rejectDialog()));
108 
109   main_layout_ = new QVBoxLayout();
110   main_layout_->addWidget(pattern_group_box_);
111   main_layout_->addWidget(color_dialog_);
112 
113   setLayout(main_layout_);
114   setWindowTitle("Layer Config");
115   setFixedSize(600, width() - 20);
116 }
117 
~DisplayColorDialog()118 DisplayColorDialog::~DisplayColorDialog()
119 {
120 }
121 
getSelectedPattern() const122 Qt::BrushStyle DisplayColorDialog::getSelectedPattern() const
123 {
124   for (auto pattern_button : pattern_buttons_) {
125     if (pattern_button->isChecked())
126       return pattern_button->pattern();
127   }
128   return Qt::SolidPattern;
129 }
130 
acceptDialog()131 void DisplayColorDialog::acceptDialog()
132 {
133   color_ = color_dialog_->selectedColor();
134   accept();
135 }
136 
rejectDialog()137 void DisplayColorDialog::rejectDialog()
138 {
139   reject();
140 }
141 
DisplayControls(QWidget * parent)142 DisplayControls::DisplayControls(QWidget* parent)
143     : QDockWidget("Display Control", parent),
144       view_(new QTreeView(parent)),
145       model_(new QStandardItemModel(0, 4, parent)),
146       tech_inited_(false)
147 {
148   setObjectName("layers");  // for settings
149   model_->setHorizontalHeaderLabels({"", "C", "V", "S"});
150   view_->setModel(model_);
151 
152   QHeaderView* header = view_->header();
153   header->setSectionResizeMode(Name, QHeaderView::Stretch);
154   header->setSectionResizeMode(Swatch, QHeaderView::ResizeToContents);
155   header->setSectionResizeMode(Visible, QHeaderView::ResizeToContents);
156   header->setSectionResizeMode(Selectable, QHeaderView::ResizeToContents);
157 
158   auto layers = makeItem(
159       layers_group_,
160       "Layers",
161       model_,
162       Qt::Checked,
163       [this](bool visible) {
164         toggleAllChildren(visible, layers_group_.name, Visible);
165       },
166       [this](bool selectable) {
167         toggleAllChildren(selectable, layers_group_.name, Selectable);
168       });
169   view_->expand(layers->index());
170 
171   // Nets group
172   auto nets_parent = makeItem(
173       nets_group_, "Nets", model_, Qt::Checked, [this](bool visible) {
174         toggleAllChildren(visible, nets_group_.name, Visible);
175       });
176 
177   makeItem(nets_.signal, "Signal", nets_parent, Qt::Checked);
178 
179   makeItem(nets_.power, "Power", nets_parent, Qt::Checked);
180 
181   makeItem(nets_.ground, "Ground", nets_parent, Qt::Checked);
182 
183   makeItem(nets_.clock, "Clock", nets_parent, Qt::Checked);
184 
185   // Rows
186   makeItem(rows_, "Rows", model_, Qt::Unchecked);
187 
188   // Rows
189   makeItem(congestion_map_, "Congestion Map", model_, Qt::Unchecked);
190   makeItem(pin_markers_, "Pin Markers", model_, Qt::Checked);
191 
192   // Track patterns group
193   auto tracks = makeItem(
194       tracks_group_, "Tracks", model_, Qt::Unchecked, [this](bool visible) {
195         toggleAllChildren(visible, tracks_group_.name, Visible);
196       });
197 
198   makeItem(tracks_.pref, "Pref", tracks, Qt::Unchecked);
199   makeItem(tracks_.non_pref, "Non Pref", tracks, Qt::Unchecked);
200 
201   // Misc group
202   auto misc = makeItem(
203       misc_group_, "Misc", model_, Qt::Unchecked, [this](bool visible) {
204         toggleAllChildren(visible, misc_group_.name, Visible);
205       });
206 
207   makeItem(misc_.fills, "Fills", misc, Qt::Unchecked);
208 
209   setWidget(view_);
210   connect(model_,
211           SIGNAL(itemChanged(QStandardItem*)),
212           this,
213           SLOT(itemChanged(QStandardItem*)));
214 
215   connect(view_,
216           SIGNAL(doubleClicked(const QModelIndex&)),
217           this,
218           SLOT(displayItemDblClicked(const QModelIndex&)));
219   setMinimumWidth(375);
220   congestion_dialog_ = new CongestionSetupDialog(this);
221 
222   connect(congestion_dialog_,
223           SIGNAL(applyCongestionRequested()),
224           this,
225           SIGNAL(changed()));
226   connect(congestion_dialog_,
227           SIGNAL(congestionSetupChanged()),
228           this,
229           SIGNAL(changed()));
230 }
231 
readSettings(QSettings * settings)232 void DisplayControls::readSettings(QSettings* settings)
233 {
234   auto getChecked = [](QSettings* settings, const char* name) {
235     return settings->value(name).toBool() ? Qt::Checked : Qt::Unchecked;
236   };
237 
238   settings->beginGroup("display_controls");
239 
240   settings->beginGroup("nets");
241   nets_.signal.visible->setCheckState(getChecked(settings, "signal_visible"));
242   nets_.power.visible->setCheckState(getChecked(settings, "power_visible"));
243   nets_.ground.visible->setCheckState(getChecked(settings, "ground_visible"));
244   nets_.clock.visible->setCheckState(getChecked(settings, "clock_visible"));
245   settings->endGroup();  // nets
246 
247   rows_.visible->setCheckState(getChecked(settings, "rows_visible"));
248   congestion_map_.visible->setCheckState(
249       getChecked(settings, "congestion_map_visible"));
250   pin_markers_.visible->setCheckState(
251       getChecked(settings, "pin_markers_visible"));
252 
253   settings->beginGroup("tracks");
254   tracks_.pref.visible->setCheckState(getChecked(settings, "pref_visible"));
255   tracks_.non_pref.visible->setCheckState(
256       getChecked(settings, "non_pref_visible"));
257   settings->endGroup();  // tracks
258 
259   settings->beginGroup("misc");
260   misc_.fills.visible->setCheckState(getChecked(settings, "fills_visible"));
261   settings->endGroup();  // misc
262 
263   settings->endGroup();
264 }
265 
writeSettings(QSettings * settings)266 void DisplayControls::writeSettings(QSettings* settings)
267 {
268   auto asBool
269       = [](QStandardItem* item) { return item->checkState() == Qt::Checked; };
270 
271   settings->beginGroup("display_controls");
272 
273   settings->beginGroup("nets");
274   settings->setValue("signal_visible", asBool(nets_.signal.visible));
275   settings->setValue("power_visible", asBool(nets_.power.visible));
276   settings->setValue("ground_visible", asBool(nets_.ground.visible));
277   settings->setValue("clock_visible", asBool(nets_.clock.visible));
278   settings->endGroup();  // nets
279 
280   settings->setValue("rows_visible", asBool(rows_.visible));
281   settings->setValue("congestion_map_visible", asBool(congestion_map_.visible));
282   settings->setValue("pin_markers_visible", asBool(pin_markers_.visible));
283 
284   settings->beginGroup("tracks");
285   settings->setValue("pref_visible", asBool(tracks_.pref.visible));
286   settings->setValue("non_pref_visible", asBool(tracks_.non_pref.visible));
287   settings->endGroup();  // tracks
288 
289   settings->beginGroup("misc");
290   settings->setValue("fills_visible", asBool(misc_.fills.visible));
291   settings->endGroup();  // misc
292 
293   settings->endGroup();
294 }
295 
toggleAllChildren(bool checked,QStandardItem * parent,Column column)296 void DisplayControls::toggleAllChildren(bool checked,
297                                         QStandardItem* parent,
298                                         Column column)
299 {
300   Qt::CheckState state = checked ? Qt::Checked : Qt::Unchecked;
301   for (int row = 0; row < parent->rowCount(); ++row) {
302     auto child = parent->child(row, column);
303     child->setCheckState(state);
304   }
305   emit changed();
306 }
307 
itemChanged(QStandardItem * item)308 void DisplayControls::itemChanged(QStandardItem* item)
309 {
310   if (item->isCheckable() == false) {
311     emit changed();
312     return;
313   }
314   bool checked = item->checkState() == Qt::Checked;
315   Callback callback = item->data().value<Callback>();
316   if (callback.action) {
317     callback.action(checked);
318   }
319   emit changed();
320 }
321 
displayItemDblClicked(const QModelIndex & index)322 void DisplayControls::displayItemDblClicked(const QModelIndex& index)
323 {
324   if (index.column() == 1) {
325     auto color_item = model_->itemFromIndex(index);
326     QVariant tech_layer_data = color_item->data(Qt::UserRole);
327     if (!tech_layer_data.isValid())
328       return;
329     auto tech_layer
330         = static_cast<odb::dbTechLayer*>(tech_layer_data.value<void*>());
331     if (tech_layer == nullptr)
332       return;
333     QColor color_val = color(tech_layer);
334     Qt::BrushStyle pattern_val = pattern(tech_layer);
335     DisplayColorDialog display_dialog(color_val, pattern_val);
336     display_dialog.exec();
337     QColor chosen_color = display_dialog.getSelectedColor();
338     if (chosen_color.isValid()) {
339       QPixmap swatch(20, 20);
340       swatch.fill(chosen_color);
341       color_item->setIcon(QIcon(swatch));
342       auto cut_layer_index
343           = model_->sibling(index.row() + 1, index.column(), index);
344       if (cut_layer_index.isValid()) {
345         auto cut_color_item = model_->itemFromIndex(cut_layer_index);
346         cut_color_item->setIcon(QIcon(swatch));
347       }
348       if (chosen_color != color_val
349           || layer_pattern_[tech_layer]
350                  != display_dialog.getSelectedPattern()) {
351         layer_color_[tech_layer] = chosen_color;
352         layer_pattern_[tech_layer] = display_dialog.getSelectedPattern();
353         view_->repaint();
354         emit changed();
355       }
356     }
357   }
358 }
359 
setDb(odb::dbDatabase * db)360 void DisplayControls::setDb(odb::dbDatabase* db)
361 {
362   db_ = db;
363   if (!db) {
364     return;
365   }
366 
367   dbTech* tech = db->getTech();
368   if (!tech) {
369     return;
370   }
371 
372   techInit();
373 
374   for (dbTechLayer* layer : tech->getLayers()) {
375     dbTechLayerType type = layer->getType();
376     if (type == dbTechLayerType::ROUTING || type == dbTechLayerType::CUT) {
377       makeItem(
378           layer_controls_[layer],
379           QString::fromStdString(layer->getName()),
380           layers_group_.name,
381           Qt::Checked,
382           std::function<void(bool)>(),
383           [this](bool selectable) {},  // non-null to create checkbox
384           color(layer),
385           type == dbTechLayerType::CUT ? NULL : layer);
386     }
387   }
388 
389   for (int i = 0; i < 4; i++)
390     view_->resizeColumnToContents(i);
391   emit changed();
392 }
393 
394 template <typename T>
makeItem(ModelRow & row,const QString & text,T * parent,Qt::CheckState checked,const std::function<void (bool)> & visibility_action,const std::function<void (bool)> & select_action,const QColor & color,odb::dbTechLayer * tech_layer)395 QStandardItem* DisplayControls::makeItem(
396     ModelRow& row,
397     const QString& text,
398     T* parent,
399     Qt::CheckState checked,
400     const std::function<void(bool)>& visibility_action,
401     const std::function<void(bool)>& select_action,
402     const QColor& color,
403     odb::dbTechLayer* tech_layer)
404 {
405   row.name = new QStandardItem(text);
406   row.name->setEditable(false);
407 
408   QPixmap swatch(20, 20);
409   swatch.fill(color);
410   row.swatch = new QStandardItem(QIcon(swatch), "");
411   row.swatch->setEditable(false);
412   row.swatch->setCheckable(false);
413   if (tech_layer != nullptr) {
414     QVariant tech_layer_data(
415         QVariant::fromValue(static_cast<void*>(tech_layer)));
416     row.swatch->setData(tech_layer_data, Qt::UserRole);
417   }
418 
419   row.visible = new QStandardItem("");
420   row.visible->setCheckable(true);
421   row.visible->setEditable(false);
422   row.visible->setCheckState(checked);
423   row.visible->setData(QVariant::fromValue(Callback({visibility_action})));
424 
425   if (select_action) {
426     row.selectable = new QStandardItem("");
427     row.selectable->setCheckable(true);
428     row.selectable->setEditable(false);
429     row.selectable->setCheckState(checked);
430     row.selectable->setData(QVariant::fromValue(Callback({select_action})));
431   }
432 
433   parent->appendRow({row.name, row.swatch, row.visible, row.selectable});
434   return row.name;
435 }
436 
color(const odb::dbTechLayer * layer)437 QColor DisplayControls::color(const odb::dbTechLayer* layer)
438 {
439   return layer_color_.at(layer);
440 }
441 
pattern(const odb::dbTechLayer * layer)442 Qt::BrushStyle DisplayControls::pattern(const odb::dbTechLayer* layer)
443 {
444   return layer_pattern_.at(layer);
445 }
446 
isVisible(const odb::dbTechLayer * layer)447 bool DisplayControls::isVisible(const odb::dbTechLayer* layer)
448 {
449   auto it = layer_controls_.find(layer);
450   if (it != layer_controls_.end()) {
451     return it->second.visible->checkState() == Qt::Checked;
452   }
453   return false;
454 }
455 
isNetVisible(odb::dbNet * net)456 bool DisplayControls::isNetVisible(odb::dbNet* net)
457 {
458   switch (net->getSigType()) {
459     case dbSigType::SIGNAL:
460       return nets_.signal.visible->checkState() == Qt::Checked;
461     case dbSigType::POWER:
462       return nets_.power.visible->checkState() == Qt::Checked;
463     case dbSigType::GROUND:
464       return nets_.ground.visible->checkState() == Qt::Checked;
465     case dbSigType::CLOCK:
466       return nets_.clock.visible->checkState() == Qt::Checked;
467     default:
468       return true;
469   }
470 }
471 
isSelectable(const odb::dbTechLayer * layer)472 bool DisplayControls::isSelectable(const odb::dbTechLayer* layer)
473 {
474   auto it = layer_controls_.find(layer);
475   if (it != layer_controls_.end()) {
476     return it->second.selectable->checkState() == Qt::Checked;
477   }
478   return false;
479 }
480 
areFillsVisible()481 bool DisplayControls::areFillsVisible()
482 {
483   return misc_.fills.visible->checkState() == Qt::Checked;
484 }
485 
areRowsVisible()486 bool DisplayControls::areRowsVisible()
487 {
488   return rows_.visible->checkState() == Qt::Checked;
489 }
490 
arePrefTracksVisible()491 bool DisplayControls::arePrefTracksVisible()
492 {
493   return tracks_.pref.visible->checkState() == Qt::Checked;
494 }
495 
areNonPrefTracksVisible()496 bool DisplayControls::areNonPrefTracksVisible()
497 {
498   return tracks_.non_pref.visible->checkState() == Qt::Checked;
499 }
500 
isCongestionVisible() const501 bool DisplayControls::isCongestionVisible() const
502 {
503   return congestion_map_.visible->checkState() == Qt::Checked;
504 }
505 
arePinMarkersVisible() const506 bool DisplayControls::arePinMarkersVisible() const
507 {
508   return pin_markers_.visible->checkState() == Qt::Checked;
509 }
510 
addCustomVisibilityControl(const std::string & name,bool initially_visible)511 void DisplayControls::addCustomVisibilityControl(const std::string& name,
512                                                  bool initially_visible)
513 {
514   auto q_name = QString::fromStdString(name);
515   auto checked = initially_visible ? Qt::Checked : Qt::Unchecked;
516   makeItem(custom_controls_[name],
517            q_name,
518            model_,
519            checked,
520            [this, name](bool visible) {
521              custom_controls_[name].visible->setCheckState(
522                  visible ? Qt::Checked : Qt::Unchecked);
523            });
524 }
525 
checkCustomVisibilityControl(const std::string & name)526 bool DisplayControls::checkCustomVisibilityControl(const std::string& name)
527 {
528   return custom_controls_[name].visible->checkState() == Qt::Checked;
529 }
530 
showHorizontalCongestion() const531 bool DisplayControls::showHorizontalCongestion() const
532 {
533   return congestion_dialog_->showHorizontalCongestion()
534          || !congestion_dialog_->showVerticalCongestion();
535 }
536 
showVerticalCongestion() const537 bool DisplayControls::showVerticalCongestion() const
538 {
539   return congestion_dialog_->showVerticalCongestion()
540          || !congestion_dialog_->showHorizontalCongestion();
541 }
542 
getMinCongestionToShow() const543 float DisplayControls::getMinCongestionToShow() const
544 {
545   return congestion_dialog_->getMinCongestionValue();
546 }
547 
getMaxCongestionToShow() const548 float DisplayControls::getMaxCongestionToShow() const
549 {
550   return congestion_dialog_->getMaxCongestionValue();
551 }
552 
getCongestionColor(float congestion) const553 QColor DisplayControls::getCongestionColor(float congestion) const
554 {
555   return congestion_dialog_->getCongestionColorForPercentage(congestion);
556 }
557 
showCongestionSetup()558 void DisplayControls::showCongestionSetup()
559 {
560   return congestion_dialog_->show();
561 }
562 
techInit()563 void DisplayControls::techInit()
564 {
565   if (tech_inited_ || !db_) {
566     return;
567   }
568 
569   dbTech* tech = db_->getTech();
570   if (!tech) {
571     return;
572   }
573 
574   // Default colors
575   // From http://vrl.cs.brown.edu/color seeded with #00F, #F00, #0D0
576   const QColor colors[] = {QColor(0, 0, 254),
577                            QColor(254, 0, 0),
578                            QColor(9, 221, 0),
579                            QColor(190, 244, 81),
580                            QColor(159, 24, 69),
581                            QColor(32, 216, 253),
582                            QColor(253, 108, 160),
583                            QColor(117, 63, 194),
584                            QColor(128, 155, 49),
585                            QColor(234, 63, 252),
586                            QColor(9, 96, 19),
587                            QColor(214, 120, 239),
588                            QColor(192, 222, 164),
589                            QColor(110, 68, 107)};
590   const int num_colors = sizeof(colors) / sizeof(QColor);
591   int metal = 0;
592   int via = 0;
593 
594   // Iterate through the layers and set default colors
595   for (dbTechLayer* layer : tech->getLayers()) {
596     dbTechLayerType type = layer->getType();
597     QColor color;
598     if (type == dbTechLayerType::ROUTING) {
599       if (metal < num_colors) {
600         color = colors[metal++];
601       } else {
602         // pick a random color as we exceeded the built-in palette size
603         color = QColor(50 + rand() % 200, 50 + rand() % 200, 50 + rand() % 200);
604       }
605     } else if (type == dbTechLayerType::CUT) {
606       if (via < num_colors) {
607         color = colors[via++];
608       } else {
609         // pick a random color as we exceeded the built-in palette size
610         color = QColor(50 + rand() % 200, 50 + rand() % 200, 50 + rand() % 200);
611       }
612     } else {
613       continue;
614     }
615     color.setAlpha(180);
616     layer_color_[layer] = color;
617     layer_pattern_[layer] = Qt::SolidPattern;  // Default pattern is fill solid
618   }
619 
620   tech_inited_ = true;
621 }
622 
designLoaded(odb::dbBlock * block)623 void DisplayControls::designLoaded(odb::dbBlock* block)
624 {
625   setDb(block->getDb());
626 }
627 
628 }  // namespace gui
629