1
/***************************************************************************
2
qgsattributetable.cpp - description
4
begin : Sat Nov 23 2002
5
copyright : (C) 2002 by Gary E.Sherman
6
email : sherman at mrcc dot com
7
Romans 3:23=>Romans 6:23=>Romans 5:8=>Romans 10:9,10=>Romans 12
8
***************************************************************************/
10
/***************************************************************************
12
* This program is free software; you can redistribute it and/or modify *
13
* it under the terms of the GNU General Public License as published by *
14
* the Free Software Foundation; either version 2 of the License, or *
15
* (at your option) any later version. *
17
***************************************************************************/
18
/* $Id: qgsattributetable.cpp 5720 2006-08-21 14:12:55Z mhugent $ */
19
#include <QApplication>
20
#include <QMouseEvent>
28
#include "qgsattributetable.h"
29
#include "qgsfeature.h"
31
#include "qgsvectordataprovider.h"
32
#include "qgsvectorlayer.h"
37
QgsAttributeTable::QgsAttributeTable(QWidget * parent, const char *name):
38
Q3Table(parent, name),
39
lockKeyPressed(false),
46
f.setFamily("Helvetica");
49
setSelectionMode(Q3Table::MultiRow);
50
QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
51
connect(this, SIGNAL(contextMenuRequested(int, int, const QPoint&)), this, SLOT(popupMenu(int, int, const QPoint&)));
52
connect(this, SIGNAL(valueChanged(int, int)), this, SLOT(storeChangedValue(int,int)));
53
connect(verticalHeader(), SIGNAL(released(int)), this, SLOT(rowClicked(int)));
58
QgsAttributeTable::~QgsAttributeTable()
62
void QgsAttributeTable::columnClicked(int col)
64
QApplication::setOverrideCursor(Qt::waitCursor);
66
//store the ids of the selected rows in a list
67
QList < int >idsOfSelected;
68
for (int i = 0; i < numSelections(); i++)
70
for (int j = selection(i).topRow(); j <= selection(i).bottomRow(); j++)
72
idsOfSelected.append(text(j, 0).toInt());
76
sortColumn(col, sort_ascending, true);
78
//clear and rebuild rowIdMap. Overwrite sortColumn later and sort rowIdMap there
81
for (int i = 0; i < numRows(); i++)
83
id = text(i, 0).toInt();
84
rowIdMap.insert(id, i);
87
QObject::disconnect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
90
//select the rows again after sorting
92
QList < int >::iterator it;
93
for (it = idsOfSelected.begin(); it != idsOfSelected.end(); ++it)
95
selectRowWithId((*it));
97
QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
99
//change the sorting order after each sort
100
(sort_ascending == true) ? sort_ascending = false : sort_ascending = true;
102
QApplication::restoreOverrideCursor();
105
void QgsAttributeTable::keyPressEvent(QKeyEvent * ev)
107
if (ev->key() == Qt::Key_Control || ev->key() == Qt::Key_Shift)
109
lockKeyPressed = true;
113
void QgsAttributeTable::keyReleaseEvent(QKeyEvent * ev)
115
if (ev->key() == Qt::Key_Control || ev->key() == Qt::Key_Shift)
117
lockKeyPressed = false;
121
void QgsAttributeTable::handleChangedSelections()
123
Q3TableSelection cselection;
124
if (lockKeyPressed == false)
126
//clear the list and evaluate the last selection
127
emit selectionRemoved();
129
//if there is no current selection, there is nothing to do
130
if (currentSelection() == -1)
135
cselection = selection(currentSelection());
137
for (int index = cselection.topRow(); index <= cselection.bottomRow(); index++)
139
emit selected(text(index, 0).toInt());
142
//don't send the signal repaintRequested() from here
143
//but in contentsMouseReleaseEvent() and rowClicked(int)
144
//todo: don't repaint in case of double clicks
147
void QgsAttributeTable::insertFeatureId(int id, int row)
149
rowIdMap.insert(id, row);
152
void QgsAttributeTable::selectRowWithId(int id)
154
QMap < int, int >::iterator it = rowIdMap.find(id);
155
selectRow(it.data());
158
void QgsAttributeTable::sortColumn(int col, bool ascending, bool wholeRows)
160
//if the first entry contains a letter, sort alphanumerically, otherwise numerically
161
QString firstentry = text(0, col);
162
bool containsletter = false;
163
for (uint i = 0; i < firstentry.length(); i++)
165
if (firstentry.ref(i).isLetter())
167
containsletter = true;
173
qsort(0, numRows() - 1, col, ascending, true);
176
qsort(0, numRows() - 1, col, ascending, false);
184
XXX Doesn't QString have something ilke this already?
186
int QgsAttributeTable::compareItems(QString s1, QString s2, bool ascending, bool alphanumeric)
214
double d1 = s1.toDouble();
215
double d2 = s2.toDouble();
240
return 0; // XXX has to return something; is this reasonable?
244
void QgsAttributeTable::qsort(int lower, int upper, int col, bool ascending, bool alphanumeric)
250
//chose a random element (this avoids n^2 worst case)
251
int element = int ( (double)rand() / (double)RAND_MAX * (upper - lower) + lower);
252
swapRows(element, upper);
253
v = text(upper, col);
258
while (compareItems(text(++i, col), v, ascending, alphanumeric) == -1);
259
while (compareItems(text(--j, col), v, ascending, alphanumeric) == 1 && j > 0); //make sure that j does not get negative
267
qsort(lower, i - 1, col, ascending, alphanumeric);
268
qsort(i + 1, upper, col, ascending, alphanumeric);
272
void QgsAttributeTable::popupMenu(int row, int col, const QPoint& pos)
274
// Duplication of code in qgsidentufyresults.cpp. Consider placing
275
// in a seperate class
276
if (mActionPopup == 0)
278
mActionPopup = new QMenu();
279
QAction *a = mActionPopup->addAction( tr("Run action") );
280
mActionPopup->addSeparator();
282
QgsAttributeAction::aIter iter = mActions.begin();
283
for (int j = 0; iter != mActions.end(); ++iter, ++j)
285
QAction* a = mActionPopup->addAction(iter->name());
286
// The menu action stores an integer that is used later on to
287
// associate an menu action with an actual qgis action.
288
a->setData(QVariant::fromValue(j));
290
connect(mActionPopup, SIGNAL(triggered(QAction*)),
291
this, SLOT(popupItemSelected(QAction*)));
294
// Get and store the attribute values and their column names are
295
// these are needed for substituting into the actions if the user
297
mActionValues.clear();
298
Q3Header* header = horizontalHeader();
301
if (header->count() != numCols())
302
std::cerr << "Something wrong with the table (file "
303
<< __FILE__<< ", line " << __LINE__
304
<< ")." << std::endl;
307
for (int i = 0; i < numCols(); ++i)
308
mActionValues.push_back(std::make_pair(header->label(i), text(row, i)));
310
// The item that was clicked on, stored as an index into the
311
// mActionValues vector.
312
mClickedOnValue = col;
314
if (mActions.size() > 0)
315
mActionPopup->popup(pos);
318
void QgsAttributeTable::popupItemSelected(QAction* menuAction)
320
int id = menuAction->data().toInt();
321
mActions.doAction(id, mActionValues, mClickedOnValue);
324
bool QgsAttributeTable::addAttribute(const QString& name, const QString& type)
326
//first test if an attribute with the same name is already in the table
327
for(int i=0;i<horizontalHeader()->count();++i)
329
if(horizontalHeader()->label(i)==name)
335
mAddedAttributes.insert(std::make_pair(name,type));
337
qWarning(("inserting attribute "+name+" of type "+type).toLocal8Bit().data());
338
//add a new column at the end of the table
339
qWarning(("numCols: "+QString::number(numCols())).toLocal8Bit().data());
341
insertColumns(numCols());
342
horizontalHeader()->setLabel(numCols()-1,name);
347
void QgsAttributeTable::deleteAttribute(const QString& name)
349
//check, if there is already an attribute with this name in mAddedAttributes
350
std::map<QString,QString>::iterator iter=mAddedAttributes.find(name);
351
if(iter!=mAddedAttributes.end())
353
mAddedAttributes.erase(iter);
354
removeAttrColumn(name);
359
qWarning(("QgsAttributeTable: deleteAttribute "+name).toLocal8Bit().data());
361
mDeletedAttributes.insert(name);
362
removeAttrColumn(name);
368
/* Deprecated: See QgisApp::editCopy() instead */
369
void QgsAttributeTable::copySelectedRows()
371
// Copy selected rows to the clipboard
374
const char fieldSep = '\t';
376
// Pick up the headers first
377
Q3Header* header = horizontalHeader();
378
for (int i = 0; i < header->count(); ++i)
379
toClipboard += header->label(i) + fieldSep;
382
// Then populate with the cell contents
383
for (int i = 0; i < numSelections(); ++i)
385
Q3TableSelection sel = selection(i);
386
for (int row = sel.topRow(); row < sel.topRow()+sel.numRows(); ++row)
388
for (int column = 0; column < numCols(); ++column)
389
toClipboard += text(row, column) + fieldSep;
394
std::cerr << "Selected data in table is:\n" << toClipboard.data();
396
// And then copy to the clipboard
397
QClipboard* clipboard = QApplication::clipboard();
399
// With qgis running under Linux, but with a Windows based X
400
// server (Xwin32), ::Selection was necessary to get the data into
401
// the Windows clipboard (which seems contrary to the Qt
402
// docs). With a Linux X server, ::Clipboard was required.
403
// The simple solution was to put the text into both clipboards.
405
// The ::Selection setText() below one may need placing inside so
406
// #ifdef so that it doesn't get compiled under Windows.
407
clipboard->setText(toClipboard, QClipboard::Selection);
408
clipboard->setText(toClipboard, QClipboard::Clipboard);
411
bool QgsAttributeTable::commitChanges(QgsVectorLayer* layer)
413
bool isSuccessful = FALSE;
417
isSuccessful = layer->commitAttributeChanges(
426
clearEditingStructures();
432
bool QgsAttributeTable::rollBack(QgsVectorLayer* layer)
439
clearEditingStructures();
444
void QgsAttributeTable::fillTable(QgsVectorLayer* layer)
446
QgsVectorDataProvider* provider=layer->getDataProvider();
452
std::vector<QgsFeature*>& addedFeatures = layer->addedFeatures();
453
std::set<int>& deletedFeatures = layer->deletedFeatureIds();
455
// set up the column headers
456
Q3Header *colHeader = horizontalHeader();
457
std::vector < QgsField > fields = provider->fields();
458
int fieldcount=provider->fieldCount();
460
for (int l = 0; l < fields.size(); l++)
461
std::cout << "field: " << fields[l].name().toLocal8Bit().data() << " | " << fields[l].isNumeric() << " | " << fields[l].type().toLocal8Bit().data() << std::endl;
464
setNumRows(provider->featureCount() + addedFeatures.size() - deletedFeatures.size());
465
setNumCols(fieldcount+1);
466
colHeader->setLabel(0, "id"); //label for the id-column
468
for (int h = 1; h <= fieldcount; h++)
470
colHeader->setLabel(h, fields[h - 1].name());
472
qWarning("Setting column label "+fields[h - 1].name());
476
//go through the features and fill the values into the table
478
while ((fet = provider->getNextFeature(true)))
480
if(deletedFeatures.find(fet->featureId()) == deletedFeatures.end())
482
putFeatureInTable(row, fet);
488
//also consider the not commited features
489
for(std::vector<QgsFeature*>::iterator it = addedFeatures.begin(); it != addedFeatures.end(); it++)
491
putFeatureInTable(row, *it);
499
void QgsAttributeTable::putFeatureInTable(int row, QgsFeature* fet)
501
if(row >= numRows())//prevent a crash if a provider doesn't update the feature count properly
507
int id = fet->featureId();
508
setText(row, 0, QString::number(id));
509
insertFeatureId(id, row); //insert the id into the search tree of qgsattributetable
510
const std::vector < QgsFeatureAttribute >& attr = fet->attributeMap();
511
for (int i = 0; i < attr.size(); i++)
513
// get the field values
514
setText(row, i + 1, attr[i].fieldValue());
518
void QgsAttributeTable::storeChangedValue(int row, int column)
520
//id column is not editable
524
int id=text(row,0).toInt();
525
QString attribute=horizontalHeader()->label(column);
527
qWarning(("feature id: "+QString::number(id)).toLocal8Bit().data());
528
qWarning(("attribute: "+attribute).toLocal8Bit().data());
530
std::map<int,std::map<QString,QString> >::iterator iter=mChangedValues.find(id);
531
if(iter==mChangedValues.end())
533
std::map<QString,QString> themap;
534
mChangedValues.insert(std::make_pair(id,themap));
535
iter=mChangedValues.find(id);
537
iter->second.erase(attribute);
538
iter->second.insert(std::make_pair(attribute,text(row,column)));
540
qWarning(("value: "+text(row,column)).toLocal8Bit().data());
546
void QgsAttributeTable::clearEditingStructures()
548
mDeletedAttributes.clear();
549
mAddedAttributes.clear();
550
for(std::map<int,std::map<QString,QString> >::iterator iter=mChangedValues.begin();iter!=mChangedValues.end();++iter)
552
iter->second.clear();
554
mChangedValues.clear();
557
void QgsAttributeTable::removeAttrColumn(const QString& name)
559
Q3Header* header=horizontalHeader();
560
for(int i=0;i<header->count();++i)
562
if(header->label(i)==name)
570
void QgsAttributeTable::bringSelectedToTop()
574
std::list<Q3TableSelection> selections;
575
bool removeselection;
577
for(int i=0;i<numSelections();++i)
579
selections.push_back(selection(i));
582
Q3TableSelection sel;
584
for(std::list<Q3TableSelection>::iterator iter=selections.begin();iter!=selections.end();++iter)
586
removeselection=true;
587
while(isRowSelected(swaptorow, true))//selections are not necessary stored in ascending order
592
for(int j=iter->topRow();j<=iter->bottomRow();++j)
594
if(j>swaptorow)//selections are not necessary stored in ascending order
596
swapRows(j,swaptorow);
597
selectRow(swaptorow);
603
removeselection=false;//keep selection
608
removeSelection(*iter);
612
//clear and rebuild rowIdMap.
615
for (int i = 0; i < numRows(); i++)
617
id = text(i, 0).toInt();
618
rowIdMap.insert(id, i);
625
void QgsAttributeTable::selectRowsWithId(const std::vector<int>& ids)
627
// to select more rows at once effectively, we stop sending signals to handleChangedSelections()
628
// otherwise it will repaint map everytime row is selected
630
QObject::disconnect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
632
clearSelection(false);
633
for (int i = 0; i < ids.size(); i++)
635
selectRowWithId(ids[i]);
636
emit selected(ids[i]);
639
QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
641
emit repaintRequested();
644
void QgsAttributeTable::showRowsWithId(const std::vector<int>& ids)
646
setUpdatesEnabled(false);
648
// hide all rows first
649
for (int i = 0; i < numRows(); i++)
652
// show only matching rows
653
for (int i = 0; i < ids.size(); i++)
654
showRow(rowIdMap[ids[i]]);
656
clearSelection(); // deselect all
657
setUpdatesEnabled(true);
661
void QgsAttributeTable::showAllRows()
663
for (int i = 0; i < numRows(); i++)
667
void QgsAttributeTable::rowClicked(int row)
669
if(checkSelectionChanges())//only repaint the canvas if the selection has changed
671
emit repaintRequested();
675
void QgsAttributeTable::contentsMouseReleaseEvent(QMouseEvent* e)
677
Q3Table::contentsMouseReleaseEvent(e);
678
if(checkSelectionChanges())//only repaint the canvas if the selection has changed
680
emit repaintRequested();
684
bool QgsAttributeTable::checkSelectionChanges()
686
std::set<int> theCurrentSelection;
687
Q3TableSelection cselection;
688
cselection = selection(currentSelection());
689
for (int index = cselection.topRow(); index <= cselection.bottomRow(); index++)
691
theCurrentSelection.insert(index);
694
if(theCurrentSelection == mLastSelectedRows)
700
mLastSelectedRows = theCurrentSelection;