Isis 3 Programmer Reference
FilterWidget.cpp
1 
7 /* SPDX-License-Identifier: CC0-1.0 */
8 
9 #include "IsisDebug.h"
10 
11 #include "FilterWidget.h"
12 
13 #include <iostream>
14 
15 #include <QButtonGroup>
16 #include <QHBoxLayout>
17 #include <QIcon>
18 #include <QLabel>
19 #include <QPair>
20 #include <QPushButton>
21 #include <QRadioButton>
22 #include <QString>
23 #include <QVBoxLayout>
24 
25 #include "IException.h"
26 #include "IString.h"
27 #include "FileName.h"
28 
29 #include "FilterGroup.h"
30 
31 
32 using std::swap;
33 using std::nothrow;
34 
35 
36 namespace Isis {
37  FilterWidget::FilterWidget(QString type) {
38  nullify();
39 
40  m_filterType = new QString(type);
41 
42  init();
43  addGroup();
44  updateDescription();
45  }
46 
47 
48  FilterWidget::FilterWidget(const FilterWidget &other) {
49  nullify();
50 
51  m_filterType = new QString(*other.m_filterType);
52 
53  init();
54 
55  foreach (FilterGroup * group, *other.m_filterGroups) {
56  addGroup(new FilterGroup(*group));
57  }
58 
59  m_buttonGroup->button(other.m_buttonGroup->checkedId())->click();
60 
61  updateDescription();
62  }
63 
64 
65  FilterWidget::~FilterWidget() {
66  if (m_buttonGroup) {
67  delete m_buttonGroup;
68  m_buttonGroup = NULL;
69  }
70 
71  if (m_filterGroups) {
72  delete m_filterGroups;
73  m_filterGroups = NULL;
74  }
75 
76  if (m_filterType) {
77  delete m_filterType;
78  m_filterType = NULL;
79  }
80  }
81 
82 
83  bool FilterWidget::evaluate(const QPair<QString, ControlNet *> *imageAndNet) const {
84  return evaluate(imageAndNet, &AbstractFilter::canFilterImages);
85  }
86 
87 
88  bool FilterWidget::evaluate(const ControlPoint *point) const {
89  return evaluate(point, &AbstractFilter::canFilterPoints);
90  }
91 
92 
93  bool FilterWidget::evaluate(const ControlMeasure *measure) const {
94  return evaluate(measure, &AbstractFilter::canFilterMeasures);
95  }
96 
97 
98  bool FilterWidget::hasFilter(bool (AbstractFilter::*meth)() const) const {
99  bool found = false;
100 
101  for (int i = 0; !found && i < m_filterGroups->size(); i++)
102  found = m_filterGroups->at(i)->hasFilter(meth);
103 
104  return found;
105  }
106 
107 
108  FilterWidget &FilterWidget::operator=(FilterWidget other) {
109  ASSERT(m_filterType);
110  ASSERT(m_filterGroups);
111  ASSERT(m_buttonGroup);
112 
113  // create temporary list of new groups
114  QList< FilterGroup * > newGroups;
115  foreach (FilterGroup * group, *other.m_filterGroups) {
116  FilterGroup *newGroup = new(nothrow) FilterGroup(*other.m_filterType);
117  if (newGroup) {
118  *newGroup = *group;
119  newGroups.append(newGroup);
120  }
121  }
122 
123  // if all is ok, and it is safe to assign
124  if (newGroups.size() == other.m_filterGroups->size()) {
125  foreach (FilterGroup * group, *m_filterGroups) {
126  deleteGroup(group);
127  }
128 
129  foreach (FilterGroup * newGroup, newGroups) {
130  addGroup(newGroup);
131  }
132 
133  swap(*m_filterType, *other.m_filterType);
134  m_buttonGroup->button(other.m_buttonGroup->checkedId())->click();
135  }
136  else {
137  // clean up any temp groups
138  foreach (FilterGroup * newGroup, newGroups) {
139  if (newGroup) {
140  delete newGroup;
141  newGroup = NULL;
142  }
143  }
144 
145  IString msg = "Assignment of FilterWidget failed";
146  throw IException(IException::Programmer, msg, _FILEINFO_);
147  }
148 
149  return *this;
150  }
151 
152 
153  void FilterWidget::nullify() {
154  m_addGroupButton = NULL;
155  m_buttonGroup = NULL;
156  m_imageDescription = NULL;
157  m_pointDescription = NULL;
158  m_measureDescription = NULL;
159  m_mainLayout = NULL;
160  m_filterGroups = NULL;
161  m_filterType = NULL;
162  }
163 
164 
165  void FilterWidget::init() {
166  m_filterGroups = new QList< FilterGroup * >;
167 
168  QString whatsThis = "<html>Filters are organized into groups (bounded by a box)."
169  " Filters within a group will be combined using either AND or OR "
170  "logic. Furthermore, multiple groups are supported, and the logic "
171  "used to combine the various groups is also configurable.<br/><br/>"
172  "For example, let A, B, and C be filters. By creating two "
173  "groups, one with A and B and the other with C, it is possible to "
174  "build the expression \"(A and B) or C\".<br/><br/>"
175  "Each group has a green plus (+) button, which adds a new filter to "
176  "the group. There is also a green plus (+) button outside any group "
177  "for adding a new group.</html>";
178 
179  ASSERT(m_filterType);
180 
181  QString title = "Filter " + *m_filterType;
182  QLabel *titleLabel = new QLabel(title);
183  titleLabel->setFont(QFont("SansSerif", 15, QFont::DemiBold));
184 
185  QHBoxLayout *titleLayout = new QHBoxLayout;
186  titleLayout->addStretch();
187  titleLayout->addWidget(titleLabel);
188  titleLayout->addStretch();
189 
190  QLabel *logicTypeLabel = new QLabel("Combine groups using logic type: ");
191  QFont logicTypeLabelFont("SansSerif", 12);
192  logicTypeLabel->setFont(logicTypeLabelFont);
193 
194  QFont logicTypeFont("SansSerif", 12, QFont::Bold);
195  QRadioButton *andButton = new QRadioButton("AND");
196  andButton->setFont(logicTypeFont);
197  QRadioButton *orButton = new QRadioButton("OR");
198  orButton->setFont(logicTypeFont);
199  m_buttonGroup = new QButtonGroup;
200  m_buttonGroup->addButton(andButton, 0);
201  m_buttonGroup->addButton(orButton, 1);
202  connect(m_buttonGroup, SIGNAL(buttonClicked(int)), this,
203  SLOT(changeGroupCombinationLogic(int)));
204 
205  // FIXME: this should be controlled by QSettings
206  orButton->click();
207 
208  QHBoxLayout *buttonLayout = new QHBoxLayout;
209  buttonLayout->addStretch();
210  buttonLayout->addWidget(logicTypeLabel);
211  buttonLayout->addWidget(andButton);
212  buttonLayout->addWidget(orButton);
213  buttonLayout->addStretch();
214  m_logicWidget = new QWidget;
215  m_logicWidget->setLayout(buttonLayout);
216 
217  m_addGroupButton = new QPushButton;
218  m_addGroupButton->setIcon(QIcon(FileName("$ISISROOT/appdata/images/icons/add.png").expanded()));
219  QString addGroupTooltip = "Add new filter group";
220  m_addGroupButton->setToolTip(addGroupTooltip);
221  m_addGroupButton->setStatusTip(addGroupTooltip);
222  m_addGroupButton->setWhatsThis(whatsThis);
223  connect(m_addGroupButton, SIGNAL(clicked()), this, SLOT(addGroup()));
224  QHBoxLayout *addGroupLayout = new QHBoxLayout;
225  addGroupLayout->addWidget(m_addGroupButton);
226  addGroupLayout->addStretch();
227 
228  QLabel *titleDummy = new QLabel;
229  titleDummy->setFont(QFont("SansSerif", 6)); // FIXME
230 
231  m_imageDescription = new QLabel;
232  m_imageDescription->setWordWrap(true);
233  m_imageDescription->setFont(QFont("SansSerif", 10)); // FIXME
234 
235  m_pointDescription = new QLabel;
236  m_pointDescription->setWordWrap(true);
237  m_pointDescription->setFont(QFont("SansSerif", 10)); // FIXME
238 
239  m_measureDescription = new QLabel;
240  m_measureDescription->setWordWrap(true);
241  m_measureDescription->setFont(QFont("SansSerif", 10)); // FIXME
242 
243  QVBoxLayout *descriptionLayout = new QVBoxLayout;
244  descriptionLayout->addWidget(titleDummy);
245  descriptionLayout->addWidget(m_imageDescription);
246  descriptionLayout->addWidget(m_pointDescription);
247  descriptionLayout->addWidget(m_measureDescription);
248 
249  connect(this, SIGNAL(filterChanged()),
250  this, SLOT(updateDescription()));
251 
252  m_mainLayout = new QVBoxLayout;
253  m_mainLayout->addLayout(titleLayout);
254  m_mainLayout->addLayout(descriptionLayout);
255  m_mainLayout->addWidget(m_logicWidget);
256  m_mainLayout->addLayout(addGroupLayout);
257  m_mainLayout->addStretch();
258 
259  setLayout(m_mainLayout);
260  setWhatsThis(whatsThis);
261  }
262 
263 
264  void FilterWidget::updateDescription() {
265  updateDescription(m_imageDescription, &AbstractFilter::canFilterImages,
266  &AbstractFilter::getImageDescription, "images");
267  updateDescription(m_pointDescription, &AbstractFilter::canFilterPoints,
268  &AbstractFilter::getPointDescription, "points");
269  updateDescription(m_measureDescription, &AbstractFilter::canFilterMeasures,
270  &AbstractFilter::getMeasureDescription, "measures");
271  }
272 
273 
274  void FilterWidget::updateDescription(QLabel *label,
275  bool (AbstractFilter::*hasFilterMeth)() const,
276  QString(AbstractFilter::*descriptionMeth)() const,
277  QString title) {
278  ASSERT(label);
279 
280  if (label) {
281  label->clear();
282 
283  QList< FilterGroup * > groups;
284  for (int i = 0; i < m_filterGroups->size(); i++)
285  if (m_filterGroups->at(i)->hasFilter(hasFilterMeth))
286  groups.append(m_filterGroups->at(i));
287 
288  const int GROUP_SIZE = groups.size();
289 
290  if (GROUP_SIZE) {
291  QString black = "<font color=black>";
292  QString blue = "<font color=darkBlue>";
293  QString red = "<font color=darkRed>";
294  QString end = "</font>";
295 
296  QString text = "Showing " + red + title + end + black + " which " + end;
297 
298  QString groupLogic;
299  if (m_andGroupsTogether)
300  groupLogic += " AND ";
301  else
302  groupLogic += " OR ";
303 
304  QString leftParen = black + "<b>(</b>" + end;
305  QString rightParen = black + "<b>)</b>" + end;
306 
307  for (int i = 0; i < GROUP_SIZE - 1; i++) {
308  if (GROUP_SIZE > 1)
309  text += leftParen;
310  text += blue + groups[i]->getDescription(
311  hasFilterMeth, descriptionMeth) + end;
312  if (GROUP_SIZE > 1)
313  text += rightParen + black + "<b>" + groupLogic + "</b>" + end;
314  }
315 
316  if (GROUP_SIZE > 1)
317  text += leftParen;
318  text += blue + groups[GROUP_SIZE - 1]->getDescription(
319  hasFilterMeth, descriptionMeth) + end;
320  if (GROUP_SIZE > 1)
321  text += rightParen;
322 
323  text += black + "." + end;
324 
325  label->setText(text);
326  }
327  }
328  }
329 
330 
331  void FilterWidget::maybeScroll(FilterGroup *group) {
332  ASSERT(m_filterGroups);
333  ASSERT(m_filterGroups->size());
334 
335  if (m_filterGroups && m_filterGroups->size() &&
336  m_filterGroups->at(m_filterGroups->size() - 1) == group)
337  emit scrollToBottom();
338  }
339 
340 
341  void FilterWidget::addGroup() {
342  FilterGroup *newGroup = new FilterGroup(*m_filterType);
343  addGroup(newGroup);
344  }
345 
346 
347  void FilterWidget::addGroup(FilterGroup *newGroup) {
348  connect(newGroup, SIGNAL(close(FilterGroup *)),
349  this, SLOT(deleteGroup(FilterGroup *)));
350  connect(newGroup, SIGNAL(filterChanged()), this, SIGNAL(filterChanged()));
351  connect(newGroup, SIGNAL(sizeChanged(FilterGroup *)),
352  this, SLOT(maybeScroll(FilterGroup *)));
353  m_mainLayout->insertWidget(m_mainLayout->count() - 2, newGroup);
354  m_filterGroups->append(newGroup);
355  m_filterGroups->size() > 1 ? m_logicWidget->show() : m_logicWidget->hide();
356 
357  emit scrollToBottom();
358  emit filterChanged();
359  }
360 
361 
362  void FilterWidget::deleteGroup(FilterGroup *filterGroup) {
363  m_mainLayout->removeWidget(filterGroup);
364  delete filterGroup;
365  m_filterGroups->removeOne(filterGroup);
366  m_filterGroups->size() > 1 ? m_logicWidget->show() : m_logicWidget->hide();
367  emit filterChanged();
368  }
369 
370 
371  void FilterWidget::changeGroupCombinationLogic(int button) {
372  m_andGroupsTogether = button == 0;
373  emit filterChanged();
374  }
375 }
QWidget
QList
This is free and unencumbered software released into the public domain.
Definition: BoxcarCachingAlgorithm.h:13
Isis::IException::Programmer
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:146
QPair
This is free and unencumbered software released into the public domain.
Definition: CubeIoHandler.h:23
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16