Skip to content

Commit ecb3612

Browse files
Merge branch 'selection_improvements' into 'master'
Selection dialog improvements See merge request nsviz/visimpl!76
2 parents bb5fa6d + f4591ad commit ecb3612

7 files changed

Lines changed: 355 additions & 17 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
cmake_minimum_required( VERSION 3.1 FATAL_ERROR )
1010

1111
# visimpl project and version
12-
project( visimpl VERSION 1.5.1 )
12+
project( visimpl VERSION 1.5.2 )
1313
set( visimpl_VERSION_ABI 6 )
1414

1515
SET( VISIMPL_LICENSE "GPL")

visimpl/MainWindow.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ namespace visimpl
857857
_selectionManager->setWindowModality( Qt::WindowModal );
858858
_selectionManager->setMinimumHeight( 300 );
859859
_selectionManager->setMinimumWidth( 500 );
860+
_selectionManager->setWindowIcon(QIcon( ":/visimpl.png" ));
860861

861862
connect( _selectionManager, SIGNAL( selectionChanged( void ) ), this,
862863
SLOT( selectionManagerChanged( void ) ) );

visimpl/SelectionManagerWidget.cpp

Lines changed: 225 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,40 @@
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

3559
namespace 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

Comments
 (0)