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 "congestionSetupDialog.h"
34 
35 #include <QColor>
36 #include <QColorDialog>
37 #include <QLinearGradient>
38 #include <QListWidgetItem>
39 #include <QPalette>
40 #include <map>
41 #include <string>
42 
43 #include "db.h"
44 #include "gui/gui.h"
45 
46 namespace gui {
47 
48 CongestionSetupDialog::ColorRangeMap CongestionSetupDialog::_color_ranges
49     = {{2, {CongestionBandInfo("blue", "0"), CongestionBandInfo("red", "1")}},
50        {3,
51         {CongestionBandInfo("blue", "0"),
52          CongestionBandInfo("yellow", "0.5"),
53          CongestionBandInfo("red", "1")}},
54        {4,
55         {CongestionBandInfo("blue", "0"),
56          CongestionBandInfo("green", "0.33"),
57          CongestionBandInfo("yellow", "0.66"),
58          CongestionBandInfo("red", "1")}},
59        {5,
60         {CongestionBandInfo("blue", "0"),
61          CongestionBandInfo("cyan", "0.25"),
62          CongestionBandInfo("green", "0.5"),
63          CongestionBandInfo("yellow", "0.75"),
64          CongestionBandInfo("red", "1")}}};
65 
CongestionSetupDialog(QWidget * parent)66 CongestionSetupDialog::CongestionSetupDialog(QWidget* parent) : QDialog(parent)
67 {
68   setupUi(this);
69   std::vector<QString> labels = getColorNames(2);
70   QListWidgetItem* lower_bound_item = new QListWidgetItem(labels[0]);
71   lower_bound_item->setBackground(QBrush(Qt::blue));
72   QListWidgetItem* upper_bound_item = new QListWidgetItem(labels[1]);
73   upper_bound_item->setBackground(QBrush(Qt::red));
74   colorRangeListWidget->clear();
75   colorRangeListWidget->addItem(lower_bound_item);
76   colorRangeListWidget->addItem(upper_bound_item);
77 
78   congestionButton->setStyleSheet(
79       "QPushButton { background-color: qlineargradient(x1:0 y1:0, x2:1 y2:0, "
80       "stop:0 blue, stop:1 red); }");
81 
82   colorRangeListWidget->setStyleSheet(
83       "QListView::item:selected {background : none; border: solid 2px "
84       "red;}");
85 
86   colorRangeListWidget->setSelectionMode(QAbstractItemView::NoSelection);
87 
88   connect(colorRangeListWidget,
89           SIGNAL(itemDoubleClicked(QListWidgetItem*)),
90           this,
91           SLOT(colorIntervalItemDblClicked(QListWidgetItem*)));
92 
93   connect(spinBox,
94           SIGNAL(valueChanged(int)),
95           this,
96           SLOT(colorIntervalChanged(int)));
97   connect(startCongestionSpinBox,
98           SIGNAL(valueChanged(int)),
99           this,
100           SLOT(congestionBandChanged(int)));
101   connect(endCongestionSpinBox,
102           SIGNAL(valueChanged(int)),
103           this,
104           SLOT(congestionBandChanged(int)));
105   connect(applyButton, SIGNAL(pressed()), this, SLOT(saveState()));
106   connect(
107       applyButton, SIGNAL(clicked()), this, SIGNAL(applyCongestionRequested()));
108   min_congestion_ = startCongestionSpinBox->value();
109   max_congestion_ = endCongestionSpinBox->value();
110 
111   cong_dir_index_ = BOTH_DIR;
112 
113   colors_.push_back(_color_ranges[2][0].band_color_);
114   colors_.push_back(_color_ranges[2][1].band_color_);
115 }
116 
saveState()117 void CongestionSetupDialog::saveState()
118 {
119   // Update the dialog state
120   min_congestion_ = startCongestionSpinBox->value();
121   max_congestion_ = endCongestionSpinBox->value();
122   colors_.clear();
123   int row = 0;
124   while (row < colorRangeListWidget->count()) {
125     colors_.push_back(colorRangeListWidget->item(row++)->background().color());
126   }
127   cong_dir_index_ = BOTH_DIR;
128   if (horCongDir->isChecked())
129     cong_dir_index_ = HORIZONTAL_DIR;
130   else if (verCongDir->isChecked())
131     cong_dir_index_ = VERTICAL_DIR;
132 }
133 
accept()134 void CongestionSetupDialog::accept()
135 {
136   saveState();
137   QDialog::accept();
138   emit congestionSetupChanged();
139 }
140 
reject()141 void CongestionSetupDialog::reject()
142 {
143   // reset the dialog state
144   startCongestionSpinBox->setValue(min_congestion_);
145   endCongestionSpinBox->setValue(max_congestion_);
146   int row = 0;
147   spinBox->setValue(colors_.size());
148   std::string color_sheet_value;
149   colorRangeListWidget->clear();
150   std::vector<QString> color_labels = getColorNames(spinBox->value());
151 
152   for (auto& color_data : _color_ranges[spinBox->value()]) {
153     QListWidgetItem* item = new QListWidgetItem(color_labels[row]);
154     item->setBackground(QBrush(colors_[row]));
155     colorRangeListWidget->addItem(item);
156     color_sheet_value = color_sheet_value
157                         + "stop:" + color_data.color_style_sheet_ + " "
158                         + colors_[row++].name().toStdString() + ", ";
159   }
160 
161   color_sheet_value = color_sheet_value.substr(0, color_sheet_value.size() - 2);
162   updateCongestionButtonStyleSheet(color_sheet_value);
163 
164   if (cong_dir_index_ == BOTH_DIR) {
165     bothCongDir->setChecked(true);
166     horCongDir->setChecked(false);
167     verCongDir->setChecked(false);
168   } else if (cong_dir_index_ == HORIZONTAL_DIR) {
169     bothCongDir->setChecked(false);
170     horCongDir->setChecked(true);
171     verCongDir->setChecked(false);
172   } else {
173     bothCongDir->setChecked(false);
174     horCongDir->setChecked(false);
175     verCongDir->setChecked(true);
176   }
177   QDialog::reject();
178 }
179 
updateCongestionButtonStyleSheet(const std::string & color_values)180 void CongestionSetupDialog::updateCongestionButtonStyleSheet(
181     const std::string& color_values)
182 {
183   std::string new_style_sheet(
184       "QPushButton { background-color: qlineargradient(x1:0 y1:0, x2:1 y2:0, ");
185   new_style_sheet += color_values;
186   new_style_sheet += "); }";
187   congestionButton->setStyleSheet(new_style_sheet.c_str());
188 }
189 
colorIntervalChanged(int value)190 void CongestionSetupDialog::colorIntervalChanged(int value)
191 {
192   std::string color_sheet_value;
193   colorRangeListWidget->clear();
194   std::vector<QString> color_labels = getColorNames(value);
195   int row = 0;
196   for (auto& color_data : CongestionSetupDialog::_color_ranges[value]) {
197     QListWidgetItem* item = new QListWidgetItem(color_labels[row++]);
198     item->setBackground(QBrush(color_data.band_color_));
199     colorRangeListWidget->addItem(item);
200     color_sheet_value = color_sheet_value
201                         + "stop:" + color_data.color_style_sheet_ + " "
202                         + color_data.band_color_.name().toStdString() + ", ";
203   }
204 
205   color_sheet_value = color_sheet_value.substr(0, color_sheet_value.size() - 2);
206   updateCongestionButtonStyleSheet(color_sheet_value);
207 }
208 
congestionBandChanged(int value)209 void CongestionSetupDialog::congestionBandChanged(int value)
210 {
211   std::vector<QString> color_labels = getColorNames(spinBox->value());
212   int row = 0;
213   for (auto& color_label : color_labels) {
214     QListWidgetItem* item = colorRangeListWidget->item(row++);
215     item->setText(color_label);
216   }
217 }
218 
colorIntervalItemDblClicked(QListWidgetItem * item)219 void CongestionSetupDialog::colorIntervalItemDblClicked(QListWidgetItem* item)
220 {
221   auto prev_color = item->background().color();
222   QColor new_color = QColorDialog::getColor(prev_color, this);
223   if (new_color.isValid()) {
224     item->setBackground(QBrush(new_color));
225     std::string color_sheet_value;
226     int row = 0;
227     for (auto& color_data :
228          CongestionSetupDialog::_color_ranges[spinBox->value()]) {
229       QListWidgetItem* item = colorRangeListWidget->item(row++);
230       color_sheet_value
231           = color_sheet_value + "stop:" + color_data.color_style_sheet_ + " "
232             + item->background().color().name().toStdString() + ", ";
233     }
234 
235     color_sheet_value
236         = color_sheet_value.substr(0, color_sheet_value.size() - 2);
237     updateCongestionButtonStyleSheet(color_sheet_value);
238   }
239   return;
240 }
241 
getCongestionButtonStyleSheetColors()242 std::string CongestionSetupDialog::getCongestionButtonStyleSheetColors()
243 {
244   std::string color_sheet_value;
245   int value = spinBox->value();
246   int row = 0;
247   for (auto& color_data : CongestionSetupDialog::_color_ranges[value]) {
248     QListWidgetItem* item = colorRangeListWidget->item(row++);
249     color_sheet_value = color_sheet_value
250                         + "stop:" + color_data.band_color_.name().toStdString()
251                         + " " + item->background().color().name().toStdString()
252                         + ", ";
253   }
254 
255   color_sheet_value = color_sheet_value.substr(0, color_sheet_value.size() - 2);
256   return color_sheet_value;
257 }
258 
getColorNames(int band_index)259 std::vector<QString> CongestionSetupDialog::getColorNames(int band_index)
260 {
261   std::vector<QString> labels;
262   QString label;
263   auto color_data = _color_ranges[band_index];
264   unsigned int index = 0;
265   int min_congestion = getMinCongestionValue();
266   int max_congestion = getMaxCongestionValue();
267   int percent_step = (max_congestion - min_congestion) / spinBox->value();
268   while (index < color_data.size() - 1) {
269     label = QString::number(min_congestion + percent_step * index)
270             + "% <= Usage < "
271             + QString::number(min_congestion + percent_step * (index + 1))
272             + "%";
273     labels.push_back(label);
274     ++index;
275   }
276   label = QString("Usage >= ")
277           + QString::number(min_congestion + percent_step * (index + 1)) + "%";
278   labels.push_back(label);
279   return labels;
280 }
281 
getCongestionColorForPercentage(float congestion_percent,int alpha) const282 QColor CongestionSetupDialog::getCongestionColorForPercentage(
283     float congestion_percent,
284     int alpha) const
285 {
286   auto& color_data_vec = _color_ranges[colors_.size()];
287   int min_congestion = getMinCongestionValue();
288   int max_congestion = getMaxCongestionValue();
289   if (congestion_percent >= max_congestion)
290     return color_data_vec.rbegin()->band_color_;
291 
292   int index = 0;
293   int step = (max_congestion - min_congestion) / spinBox->value();
294   while (index < colors_.size()) {
295     if (congestion_percent <= (min_congestion + step * index))
296       break;
297     ++index;
298   }
299 
300   QColor min_color = colors_[index - 1];
301   QColor max_color = colors_[index];
302 
303   int band_llimit = min_congestion + (step * (index - 1));
304   int band_ulimit = min_congestion + (step * index);
305 
306   float band_percent
307       = (congestion_percent - band_llimit) / (band_ulimit - band_llimit);
308 
309   int red_val = std::min(
310       255,
311       min_color.red()
312           + std::abs(int(band_percent * (max_color.red() - min_color.red()))));
313   int green_val = std::min(
314       255,
315       min_color.green()
316           + std::abs(
317               int(band_percent * (max_color.green() - min_color.green()))));
318   int blue_val
319       = std::min(255,
320                  min_color.blue()
321                      + std::abs(int(band_percent
322                                     * (max_color.blue() - min_color.blue()))));
323   QColor congestion_color;
324 
325   congestion_color.setRed(red_val);
326   congestion_color.setGreen(green_val);
327   congestion_color.setBlue(blue_val);
328 
329   congestion_color.setAlpha(alpha);
330 
331   return congestion_color;
332 }
333 
334 }  // namespace gui
335