Skip to content

Commit cab8420

Browse files
author
wonder
committed
Applied patch from ticket #1927.
It adds some improvements to selection handling in attribute table (see the ticket for details). Contributed by German Carillo - thanks! git-svn-id: http://svn.osgeo.org/qgis/trunk@12104 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent a54a712 commit cab8420

File tree

4 files changed

+155
-124
lines changed

4 files changed

+155
-124
lines changed

src/app/attributetable/qgsattributetabledialog.cpp

+148-77
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
115115
connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) );
116116
connect( mLayer, SIGNAL( layerDeleted() ), this, SLOT( close() ) );
117117
connect( mView->verticalHeader(), SIGNAL( sectionClicked( int ) ), this, SLOT( updateRowSelection( int ) ) );
118+
connect( mView->verticalHeader(), SIGNAL( sectionPressed( int ) ), this, SLOT( updateRowPressed( int ) ) );
118119
connect( mModel, SIGNAL( modelChanged() ), this, SLOT( updateSelection() ) );
119120

120121
mLastClickedHeaderIndex = 0;
@@ -262,8 +263,6 @@ int QgsAttributeTableDialog::columnBoxColumnId()
262263
void QgsAttributeTableDialog::updateSelection()
263264
{
264265
QModelIndex index;
265-
mView->setSelectionMode( QAbstractItemView::MultiSelection );
266-
267266
QItemSelection selection;
268267

269268
QgsFeatureIds::Iterator it = mSelectedFeatures.begin();
@@ -277,7 +276,6 @@ void QgsAttributeTableDialog::updateSelection()
277276

278277
mSelectionModel->select( selection, QItemSelectionModel::ClearAndSelect );// | QItemSelectionModel::Columns);
279278
mView->setSelectionModel( mSelectionModel );
280-
mView->setSelectionMode( QAbstractItemView::NoSelection );
281279

282280
/*for (int i = 0; i < mModel->rowCount(); ++i)
283281
{
@@ -288,127 +286,200 @@ void QgsAttributeTableDialog::updateSelection()
288286
*/
289287
}
290288

289+
void QgsAttributeTableDialog::updateRowPressed( int index )
290+
{
291+
mView->setSelectionMode( QAbstractItemView::ExtendedSelection );
292+
mIndexPressed = index;
293+
}
294+
291295
void QgsAttributeTableDialog::updateRowSelection( int index )
292296
{
293-
// map index to filter model
294-
//index = mFilterModel->mapFromSource(mModel->index(index, 0)).row();
297+
bool bDrag;
295298

296-
if ( mView->shiftPressed() )
299+
if ( mIndexPressed == index )
300+
bDrag = false;
301+
else
302+
bDrag = true;
303+
304+
QString key = "";
305+
if ( QApplication::keyboardModifiers() == Qt::ControlModifier )
306+
key = "Control";
307+
else if ( QApplication::keyboardModifiers() == Qt::ShiftModifier )
308+
key = "Shift";
309+
310+
int first, last;
311+
312+
if ( bDrag )
297313
{
298-
QgsDebugMsg( "shift" );
299-
// get the first and last index of the rows to be selected/deselected
300-
int first, last;
301-
if ( index > mLastClickedHeaderIndex )
314+
if ( mIndexPressed < index )
302315
{
303-
first = mLastClickedHeaderIndex + 1;
304-
last = index;
305-
}
306-
else if ( index == mLastClickedHeaderIndex )
307-
{
308-
// row was selected and now it is shift-clicked
309-
// ignore the shift and deselect the row
310-
first = last = index;
316+
first = mIndexPressed;
317+
mLastClickedHeaderIndex = last = index;
311318
}
312319
else
313320
{
314-
first = index;
315-
last = mLastClickedHeaderIndex - 1;
321+
last = mIndexPressed;
322+
mLastClickedHeaderIndex = first = index;
316323
}
317324

318-
// for all the rows update the selection, without starting a new selection
319-
if ( first <= last )
320-
updateRowSelection( first, last, false );
321-
322-
mLastClickedHeaderIndex = last;
325+
updateRowSelection( first, last, 3 );
326+
mView->setSelectionMode( QAbstractItemView::NoSelection );
327+
return;
323328
}
324-
else if ( mView->ctrlPressed() )
329+
else // No drag
325330
{
326-
QgsDebugMsg( "ctrl" );
327-
// update the single row selection, without starting a new selection
328-
updateRowSelection( index, index, false );
331+
if ( key == "Shift" )
332+
{
333+
QgsDebugMsg( "shift" );
334+
// get the first and last index of the rows to be selected/deselected
335+
first = last = 0;
329336

330-
// the next shift would start from here
331-
mLastClickedHeaderIndex = index;
332-
}
333-
else
334-
{
335-
QgsDebugMsg( "ordinary click" );
336-
// update the single row selection, start a new selection if the row was not selected
337-
updateRowSelection( index, index, true );
337+
if ( index > mLastClickedHeaderIndex )
338+
{
339+
first = mLastClickedHeaderIndex;
340+
last = index;
341+
}
342+
else if ( index == mLastClickedHeaderIndex )
343+
{
344+
// row was selected and now it is shift-clicked
345+
first = last = index;
346+
}
347+
else
348+
{
349+
first = index;
350+
last = mLastClickedHeaderIndex;
351+
}
338352

339-
// the next shift would start from here
340-
mLastClickedHeaderIndex = index;
353+
// for all the rows update the selection, without starting a new selection
354+
if ( first <= last )
355+
updateRowSelection( first, last, 1 );
356+
}
357+
else if ( key == "Control" )
358+
{
359+
QgsDebugMsg( "ctrl" );
360+
// update the single row selection, without starting a new selection
361+
updateRowSelection( index, index, 2 );
362+
363+
// the next shift would start from here
364+
mLastClickedHeaderIndex = index;
365+
}
366+
else // Single click
367+
{
368+
// Start a new selection if the row was not selected
369+
updateRowSelection( index, index, 0 );
370+
371+
// the next shift would start from here
372+
mLastClickedHeaderIndex = index;
373+
}
341374
}
375+
mView->setSelectionMode( QAbstractItemView::NoSelection );
342376
}
343377

344-
// fast row deselection needed
345-
void QgsAttributeTableDialog::updateRowSelection( int first, int last, bool startNewSelection )
378+
void QgsAttributeTableDialog::updateRowSelection( int first, int last, int clickType )
346379
{
380+
// clickType= 0:Single click, 1:Shift, 2:Ctrl, 3: Dragged click
347381
disconnect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) );
348382

349-
//index = mFilterModel->mapFromSource(mModel->index(index, 0)).row();
350383
// Id must be mapped to table/view row
351384
QModelIndex index = mFilterModel->mapToSource( mFilterModel->index( first, 0 ) );
352385
int fid = mModel->rowToId( index.row() );
353386
bool wasSelected = mSelectedFeatures.contains( fid );
354387

355388
// new selection should be created
356-
if ( startNewSelection )
389+
if ( clickType == 0 ) // Single click
357390
{
358-
mView->clearSelection();
359-
mSelectedFeatures.clear();
360-
361-
if ( wasSelected )
362-
{
363-
mLayer->removeSelection();
364-
connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) );
365-
return;
366-
}
391+
if ( mSelectedFeatures.size() == 1 and wasSelected ) // One item selected
392+
return // Click over a selected item doesn't do anything
367393

368-
// set clicked row to current
369394
mView->setCurrentIndex( mFilterModel->index( first, 0 ) );
370-
mView->setSelectionMode( QAbstractItemView::SingleSelection );
371-
372-
//QModelIndex index = mFilterModel->mapFromSource(mModel->index(first, 0));
373-
374395
mView->selectRow( first );
375-
mView->setSelectionMode( QAbstractItemView::NoSelection );
376396

397+
mSelectedFeatures.clear();
377398
mSelectedFeatures.insert( fid );
378-
//mLayer->setSelectedFeatures(mSelectedFeatures);
379399
mLayer->removeSelection();
380400
mLayer->select( fid );
381-
//mFilterModel->invalidate();
382401
connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) );
383402
return;
384403
}
385-
386-
// existing selection should be updated
387-
for ( int i = first; i <= last; ++i )
404+
else if ( clickType == 1 ) // Shift
388405
{
389-
if ( i > first )
406+
QgsFeatureIds newSelection;
407+
408+
for ( int i = first; i <= last; ++i )
390409
{
391-
// Id must be mapped to table/view row
392-
index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) );
393-
fid = mModel->rowToId( index.row() );
394-
wasSelected = mSelectedFeatures.contains( fid );
410+
if ( i >= first )
411+
{
412+
// Id must be mapped to table/view row
413+
index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) );
414+
fid = mModel->rowToId( index.row() );
415+
}
416+
newSelection.insert( fid );
395417
}
396418

419+
// Remove items in mSelectedFeatures if they aren't in mNewSelection
420+
QgsFeatureIds::Iterator it = mSelectedFeatures.begin();
421+
while ( it != mSelectedFeatures.end() ) {
422+
if ( !newSelection.contains( *it ) ) {
423+
it = mSelectedFeatures.erase( it );
424+
} else {
425+
++it;
426+
}
427+
}
428+
429+
// Append the other fids in range first-last to mSelectedFeatures
430+
QgsFeatureIds::Iterator itNew = newSelection.begin();
431+
for ( ; itNew != newSelection.end(); ++itNew )
432+
{
433+
if ( !mSelectedFeatures.contains( *itNew ) )
434+
{
435+
mSelectedFeatures.insert( *itNew );
436+
}
437+
}
438+
}
439+
else if ( clickType == 2 ) // Ctrl
440+
{
441+
// existing selection should be updated
397442
if ( wasSelected )
398443
mSelectedFeatures.remove( fid );
399444
else
400445
mSelectedFeatures.insert( fid );
401446
}
402-
//mFilterModel->invalidate();
447+
else if ( clickType == 3 ) // Dragged click
448+
{
449+
QgsFeatureIds newSelection;
450+
451+
for ( int i = first; i <= last; ++i )
452+
{
453+
if ( i >= first )
454+
{
455+
// Id must be mapped to table/view row
456+
index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) );
457+
fid = mModel->rowToId( index.row() );
458+
}
459+
newSelection.insert( fid );
460+
}
461+
462+
// Remove items in mSelectedFeatures if they aren't in mNewSelection
463+
QgsFeatureIds::Iterator it = mSelectedFeatures.begin();
464+
while ( it != mSelectedFeatures.end() ) {
465+
if ( !newSelection.contains( *it ) ) {
466+
it = mSelectedFeatures.erase( it );
467+
} else {
468+
++it;
469+
}
470+
}
471+
472+
// Append the other fids in range first-last to mSelectedFeatures
473+
QgsFeatureIds::Iterator itNew = newSelection.begin();
474+
for ( ; itNew != newSelection.end(); ++itNew )
475+
{
476+
if ( !mSelectedFeatures.contains( *itNew ) )
477+
{
478+
mSelectedFeatures.insert( *itNew );
479+
}
480+
}
481+
}
403482

404-
/*
405-
QItemSelection selection;
406-
QModelIndex leftUpIndex = mFilterModel->index(first, 0);
407-
QModelIndex rightBottomIndex = mFilterModel->index(last, mModel->columnCount() - 1);
408-
selection.append(QItemSelectionRange(leftUpIndex, rightBottomIndex));
409-
mSelectionModel->select(selection, QItemSelectionModel::Select);
410-
mView->setSelectionModel(mSelectionModel);
411-
*/
412483
updateSelection();
413484
mLayer->setSelectedFeatures( mSelectedFeatures );
414485
connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) );

src/app/attributetable/qgsattributetabledialog.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,17 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
8686
* Updates selection of a row
8787
*/
8888
void updateRowSelection( int index );
89+
/**
90+
* Updates the index pressed
91+
*/
92+
void updateRowPressed( int index );
8993
/**
9094
* Updates selection of specifed rows
9195
* @param first first row
9296
* @param last last row
93-
* @param startNewSelection if true, then new selection is started
97+
* @param clickType 0:Single click, 1:Shift, 2:Ctrl, 3:dragged click
9498
*/
95-
void updateRowSelection( int first, int last, bool startNewSelection );
99+
void updateRowSelection( int first, int last, int clickType );
96100

97101
/**
98102
* Toggle showing of selected line only
@@ -183,6 +187,7 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
183187
QgsAttributeTableFilterModel *mFilterModel;
184188
QgsVectorLayer *mLayer;
185189
QgsFeatureIds mSelectedFeatures;
190+
int mIndexPressed;
186191

187192
QItemSelectionModel* mSelectionModel;
188193
int mLastClickedHeaderIndex;

src/app/attributetable/qgsattributetableview.cpp

-24
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ QgsAttributeTableView::QgsAttributeTableView( QWidget* parent )
4242
setSelectionMode( QAbstractItemView::NoSelection );
4343
setSortingEnabled( true );
4444

45-
mShiftPressed = false;
46-
mCtrlPressed = false;
4745
}
4846

4947
void QgsAttributeTableView::setLayer( QgsVectorLayer* layer )
@@ -73,25 +71,3 @@ void QgsAttributeTableView::closeEvent( QCloseEvent *event )
7371
QSettings settings;
7472
settings.setValue( "/BetterAttributeTable/geometry", QVariant( saveGeometry() ) );
7573
}
76-
77-
void QgsAttributeTableView::keyPressEvent( QKeyEvent *event )
78-
{
79-
// shift pressed
80-
if ( event->key() == Qt::Key_Shift )// && event->modifiers() & Qt::ShiftModifier)
81-
mShiftPressed = true;
82-
else if ( event->key() == Qt::Key_Control )
83-
mCtrlPressed = true;
84-
else
85-
QTableView::keyPressEvent( event );
86-
}
87-
88-
void QgsAttributeTableView::keyReleaseEvent( QKeyEvent *event )
89-
{
90-
// workaround for some Qt bug
91-
if ( event->key() == Qt::Key_Shift || event->key() == -1 )
92-
mShiftPressed = false;
93-
else if ( event->key() == Qt::Key_Control )
94-
mCtrlPressed = false;
95-
else
96-
QTableView::keyReleaseEvent( event );
97-
}

src/app/attributetable/qgsattributetableview.h

-21
Original file line numberDiff line numberDiff line change
@@ -41,29 +41,8 @@ class QgsAttributeTableView: public QTableView
4141
* @param event not used
4242
*/
4343
void closeEvent( QCloseEvent *event );
44-
/**
45-
* Handles Ctrl or Shift key press
46-
* @param event the key pressed
47-
*/
48-
void keyPressEvent( QKeyEvent *event );
49-
/**
50-
* Handles Ctrl or Shift key release
51-
* @param event the key released
52-
*/
53-
void keyReleaseEvent( QKeyEvent *event );
54-
/**
55-
* Returns true if shift was pressed
56-
*/
57-
bool shiftPressed() { return mShiftPressed; }
58-
/**
59-
* Returns true if ctrl was pressed
60-
*/
61-
bool ctrlPressed() { return mCtrlPressed; }
6244

6345
private:
64-
bool mShiftPressed;
65-
bool mCtrlPressed;
66-
6746
QgsAttributeTableModel* mModel;
6847
QgsAttributeTableFilterModel* mFilterModel;
6948
};

0 commit comments

Comments
 (0)