2121 */
2222
2323// ViSimpl
24- #include " SelectionManagerWidget.h"
25-
26- // Qt
27- #include < QGridLayout>
28- #include < QGroupBox>
29- #include < QLabel>
30- #include < QFileDialog>
31- #include < QMessageBox>
32- #include < QTextStream>
33- #include < QShortcut>
24+ #include < qabstractitemmodel.h>
25+ #include < qabstractitemview.h>
26+ #include < qalgorithms.h>
27+ #include < qboxlayout.h>
28+ #include < qchar.h>
29+ #include < qdir.h>
30+ #include < qfile.h>
31+ #include < qfiledialog.h>
32+ #include < qflags.h>
33+ #include < qgridlayout.h>
34+ #include < qgroupbox.h>
35+ #include < qicon.h>
36+ #include < qiodevice.h>
37+ #include < qitemselectionmodel.h>
38+ #include < qkeysequence.h>
39+ #include < qlabel.h>
40+ #include < qlineedit.h>
41+ #include < qlist.h>
42+ #include < qlistview.h>
43+ #include < qmessagebox.h>
44+ #include < qnamespace.h>
45+ #include < qobject.h>
46+ #include < qobjectdefs.h>
47+ #include < qpushbutton.h>
48+ #include < qradiobutton.h>
49+ #include < qshortcut.h>
50+ #include < qstandarditemmodel.h>
51+ #include < qstring.h>
52+ #include < qstringlist.h>
53+ #include < qtabwidget.h>
54+ #include < qtextstream.h>
55+ #include < qvariant.h>
56+ #include < sumrice/types.h>
57+ #include < visimpl/SelectionManagerWidget.h>
3458
3559namespace visimpl
3660{
@@ -109,16 +133,36 @@ namespace visimpl
109133 _listViewAvailable->setModel ( _modelAvailable );
110134 _listViewSelected->setModel ( _modelSelected );
111135
112- _buttonAddToSelection = new QPushButton ( " --> " );
136+ _buttonAddToSelection = new QPushButton (QIcon ( " :/icons/right.svg " ), " " );
113137 _buttonAddToSelection->setToolTip ( tr ( " Add to selected GIDs" ));
114138
115- _buttonRemoveFromSelection = new QPushButton ( " <-- " );
139+ _buttonRemoveFromSelection = new QPushButton (QIcon ( " :/icons/left.svg " ), " " );
116140 _buttonRemoveFromSelection->setToolTip ( tr ( " Remove from selected GIDs" ));
117141
118142 layoutSelection->addWidget ( _listViewAvailable, 1 , 0 , 5 , 1 );
119143 layoutSelection->addWidget ( _buttonAddToSelection, 2 , 1 , 1 , 1 );
120144 layoutSelection->addWidget ( _buttonRemoveFromSelection, 4 , 1 , 1 , 1 );
121145 layoutSelection->addWidget ( _listViewSelected, 1 , 2 , 5 , 1 );
146+ layoutSelection->addWidget ( new QLabel (" Selected:" ), 6 ,0 ,1 ,1 );
147+
148+ QRegExp rx (" [0-9 \\ -\\ ,]+" );
149+ auto validator = new QRegExpValidator (rx, this );
150+
151+ _rangeEdit = new QLineEdit ();
152+ _rangeEdit->setPlaceholderText (" Nothing selected" );
153+ _rangeEdit->setValidator (validator);
154+
155+ auto rangeLayout = new QHBoxLayout ();
156+ rangeLayout->addWidget (new QLabel (" Selected:" ), 0 );
157+ rangeLayout->addWidget (_rangeEdit, 1 );
158+
159+ layoutSelection->addLayout (rangeLayout, 6 , 0 , 1 , 4 );
160+
161+ connect (_rangeEdit, SIGNAL (editingFinished ()),
162+ this , SLOT (_updateRangeEdit ()));
163+
164+ connect (_listViewAvailable->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection&)),
165+ this , SLOT (_updateRangeEdit ()));
122166
123167 connect ( _buttonAddToSelection, SIGNAL ( clicked ( void )),
124168 this , SLOT ( _addToSelected ( void )));
@@ -244,8 +288,10 @@ namespace visimpl
244288 for ( auto gid : gids )
245289 {
246290 const auto text = QString::number (gid);
291+ auto idx = new QStandardItem (text);
292+ idx->setData (gid);
247293
248- available << new QStandardItem (text) ;
294+ available << idx ;
249295 selected << new QStandardItem (text);
250296
251297 _gidIndex.insert ( std::make_pair ( gid, index ));
@@ -279,11 +325,13 @@ namespace visimpl
279325 {
280326 _labelAvailable->setText ( QString ( " Available GIDs: " ) + QString::number ( _gidsAvailable.size ( )));
281327 _labelSelection->setText ( QString ( " Selected GIDs: " ) + QString::number ( _gidsSelected.size ( )));
328+
329+ _buttonRemoveFromSelection->setEnabled (!_gidsSelected.empty ());
282330 }
283331
284332 void SelectionManagerWidget::_addToSelected ( void )
285333 {
286- auto selectedIndices =
334+ const auto selectedIndices =
287335 _listViewAvailable->selectionModel ( )->selectedIndexes ( );
288336
289337 if ( selectedIndices.size ( ) <= 0 )
@@ -294,22 +342,23 @@ namespace visimpl
294342 if ( index.row ( ) < 0 )
295343 continue ;
296344
297- auto item = _modelAvailable->itemFromIndex ( index );
345+ const auto item = _modelAvailable->itemFromIndex ( index );
298346
299347 bool ok;
300348 unsigned int gid = item->data ( Qt::DisplayRole ).toUInt ( &ok );
301349
302350 _gidsAvailable.erase ( gid );
303351 _gidsSelected.insert ( gid );
304352
305- auto gidIndex = _gidIndex.find ( gid );
353+ const auto gidIndex = _gidIndex.find ( gid );
306354 assert ( gidIndex != _gidIndex.end ( ));
307355
308356 _listViewAvailable->setRowHidden ( gidIndex->second , true );
309357 _listViewSelected->setRowHidden ( gidIndex->second , false );
310358 }
311359
312360 _listViewAvailable->selectionModel ( )->clearSelection ( );
361+ _rangeEdit->clear ();
313362
314363 _updateListsLabelNumbers ( );
315364 }
@@ -342,6 +391,120 @@ namespace visimpl
342391 _updateListsLabelNumbers ( );
343392 }
344393
394+ void SelectionManagerWidget::_updateRangeEdit ()
395+ {
396+ auto lEdit = qobject_cast<QLineEdit*>(sender ());
397+ if (lEdit)
398+ {
399+ const auto parts = _rangeEdit->text ().split (' ,' );
400+ const auto model = _listViewAvailable->model ();
401+ QItemSelection selection;
402+
403+ auto processPart = [&model, &selection, this ](const QString &s)
404+ {
405+ if (s.isEmpty ()) return true ;
406+ const auto trim = s.trimmed ();
407+ bool ok = false ;
408+
409+ if (trim.contains (" -" ))
410+ {
411+ const auto values = trim.split (" -" );
412+ if (values.size () > 2 ) return false ;
413+ const auto min = values.first ().trimmed ().toULongLong (&ok);
414+ if (!ok) return false ;
415+ const auto max = values.last ().trimmed ().toULongLong (&ok);
416+ if (!ok || max < min) return false ;
417+ const auto minIt = _gidIndex.find (min);
418+ const auto maxIt = _gidIndex.find (max);
419+ if (minIt == _gidIndex.end () || maxIt == _gidIndex.end ()) return false ;
420+
421+ const auto range = QItemSelectionRange (model->index ((*minIt).second , 0 ), model->index ((*maxIt).second , 0 ));
422+ selection.append (range);
423+ return true ;
424+ }
425+
426+ // not a range
427+ const auto value = trim.toULongLong (&ok);
428+ if (!ok) return false ;
429+ const auto it = _gidIndex.find (value);
430+ if (it == _gidIndex.end ()) return false ;
431+ const auto item = model->index ((*it).second , 0 );
432+ selection.append (QItemSelectionRange (item, item));
433+ return true ;
434+ };
435+
436+ auto it = std::find_if_not (parts.cbegin (), parts.cend (), processPart);
437+ if (it != parts.cend ())
438+ {
439+ lEdit->setStyleSheet (" QLineEdit { background: rgb(255, 160, 160); selection-background-color: rgb(0, 200, 200); }" );
440+ }
441+ else
442+ {
443+ lEdit->setStyleSheet (" " );
444+ }
445+
446+ if (!selection.isEmpty ())
447+ {
448+ auto sModel = _listViewAvailable->selectionModel ();
449+ sModel ->blockSignals (true );
450+ sModel ->select (selection, QItemSelectionModel::ClearAndSelect);
451+ sModel ->blockSignals (false );
452+ _listViewAvailable->scrollTo (selection.last ().bottomRight (), QAbstractItemView::EnsureVisible);
453+ _listViewAvailable->repaint ();
454+ }
455+ }
456+ else
457+ {
458+ auto sModel = qobject_cast<QItemSelectionModel*>(sender ());
459+ if (sModel )
460+ {
461+ const auto selection = sModel ->selection ();
462+ std::vector<Range> ranges;
463+
464+ auto processRange = [&ranges](const QItemSelectionRange &r)
465+ {
466+ bool ok = false ;
467+ const auto min = r.topLeft ();
468+ if (!min.isValid ()) return false ;
469+ const auto minData = min.data (Qt::UserRole + 1 );
470+ if (!minData.isValid ()) return false ;
471+ const auto minValue = minData.toULongLong (&ok);
472+ if (!ok) return false ;
473+
474+ const auto max = r.bottomRight ();
475+ if (!max.isValid ()) return false ;
476+ if (min != max)
477+ {
478+ const auto maxData = max.data (Qt::UserRole + 1 );
479+ if (!maxData.isValid ()) return false ;
480+ const auto maxValue = maxData.toULongLong (&ok);
481+ if (!ok) return false ;
482+
483+ ranges.emplace_back (minValue, maxValue);
484+ return true ;
485+ }
486+
487+ ranges.emplace_back (minValue, minValue);
488+ return true ;
489+ };
490+ auto it = std::find_if_not (selection.cbegin (), selection.cend (), processRange);
491+ if (it != selection.cend () || ranges.empty ()) return ;
492+
493+ ranges = mergeRanges (ranges);
494+
495+ _rangeEdit->setStyleSheet (" " );
496+ _rangeEdit->blockSignals (true );
497+ _rangeEdit->setText (printRanges (ranges));
498+ _rangeEdit->blockSignals (false );
499+ _rangeEdit->repaint ();
500+ }
501+ else
502+ {
503+ std::cerr << " Unable to indentify sender! " << __FILE__ << " ," << __LINE__ << std::endl;
504+ }
505+ }
506+ }
507+
345508 void SelectionManagerWidget::_saveToFile ( const QString& filePath,
346509 const QString& separator,
347510 const QString& prefix,
@@ -353,6 +516,7 @@ namespace visimpl
353516 msgBox.setWindowTitle (" Selection save" );
354517 msgBox.setText ( " The prefix and the suffix cannot contain the separator character." );
355518 msgBox.setStandardButtons ( QMessageBox::Ok );
519+ msgBox.setWindowIcon (QIcon (" :/visimpl.png" ));
356520 msgBox.exec ( );
357521
358522 return ;
@@ -364,6 +528,7 @@ namespace visimpl
364528 QMessageBox msgBox ( this );
365529 msgBox.setWindowTitle (" Selection save" );
366530 msgBox.setText ( " The selected file already exists." );
531+ msgBox.setWindowIcon (QIcon (" :/visimpl.png" ));
367532 msgBox.setInformativeText ( " Do you want to overwrite?" );
368533 msgBox.setStandardButtons ( QMessageBox::Save | QMessageBox::Cancel );
369534 msgBox.setDefaultButton ( QMessageBox::Save );
@@ -429,4 +594,48 @@ namespace visimpl
429594 _saveToFile ( _lineEditFilePath->text ( ), separator,
430595 _lineEditPrefix->text ( ), _lineEditSuffix->text ( ));
431596 }
597+
598+ SelectionManagerWidget::Ranges SelectionManagerWidget::mergeRanges (
599+ SelectionManagerWidget::Ranges &r)
600+ {
601+ Ranges result;
602+
603+ auto sortRanges = [](const Range &lhs, const Range &rhs)
604+ {
605+ // ranges dont overlap
606+ return lhs.min < rhs.min ;
607+ };
608+ std::sort (r.begin (), r.end (), sortRanges);
609+
610+ // join sorted ranges
611+ for (auto it = r.cbegin (); it != r.cend (); ++it)
612+ {
613+ if (result.empty ())
614+ {
615+ result.emplace_back (*it);
616+ continue ;
617+ }
618+
619+ if (result.back ().canMerge (*it))
620+ {
621+ result.back () = result.back () + (*it);
622+ }
623+ else
624+ {
625+ result.emplace_back (*it);
626+ }
627+ }
628+
629+ return result;
630+ }
631+
632+ QString SelectionManagerWidget::printRanges (const SelectionManagerWidget::Ranges &r)
633+ {
634+ QStringList texts;
635+ auto printRange = [&texts](const Range &item){ texts << item.print (); };
636+ std::for_each (r.cbegin (), r.cend (), printRange);
637+
638+ return texts.join (' ,' );
639+ }
640+
432641}
0 commit comments