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