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,v 1.27.2.2 2005/08/16 19:35:55 mhugent Exp $ */
19
#include <qapplication.h>
21
#include <qpopupmenu.h>
24
#include "qgsattributetable.h"
25
#include "qgsfeature.h"
27
#include "qgsvectordataprovider.h"
28
#include "qgsvectorlayer.h"
32
QgsAttributeTable::QgsAttributeTable(QWidget * parent, const char *name):QTable(parent, name), lockKeyPressed(false),
33
sort_ascending(true), mActionPopup(0), mEditable(false), mEdited(false)
36
f.setFamily("Helvetica");
39
setSelectionMode(QTable::MultiRow);
40
QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
41
connect(this, SIGNAL(contextMenuRequested(int, int, const QPoint&)), this, SLOT(popupMenu(int, int, const QPoint&)));
42
connect(this, SIGNAL(valueChanged(int, int)), this, SLOT(storeChangedValue(int,int)));
47
QgsAttributeTable::~QgsAttributeTable()
51
void QgsAttributeTable::columnClicked(int col)
53
QApplication::setOverrideCursor(Qt::waitCursor);
55
//store the ids of the selected rows in a list
56
QValueList < int >idsOfSelected;
57
for (int i = 0; i < numSelections(); i++)
59
for (int j = selection(i).topRow(); j <= selection(i).bottomRow(); j++)
61
idsOfSelected.append(text(j, 0).toInt());
65
sortColumn(col, sort_ascending, true);
67
//clear and rebuild rowIdMap. Overwrite sortColumn later and sort rowIdMap there
70
for (int i = 0; i < numRows(); i++)
72
id = text(i, 0).toInt();
73
rowIdMap.insert(id, i);
76
QObject::disconnect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
79
//select the rows again after sorting
81
QValueList < int >::iterator it;
82
for (it = idsOfSelected.begin(); it != idsOfSelected.end(); ++it)
84
selectRowWithId((*it));
86
QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(handleChangedSelections()));
88
//change the sorting order after each sort
89
(sort_ascending == true) ? sort_ascending = false : sort_ascending = true;
91
QApplication::restoreOverrideCursor();
94
void QgsAttributeTable::keyPressEvent(QKeyEvent * ev)
96
if (ev->key() == Qt::Key_Control || ev->key() == Qt::Key_Shift)
98
lockKeyPressed = true;
102
void QgsAttributeTable::keyReleaseEvent(QKeyEvent * ev)
104
if (ev->key() == Qt::Key_Control || ev->key() == Qt::Key_Shift)
106
lockKeyPressed = false;
110
void QgsAttributeTable::handleChangedSelections()
112
QTableSelection cselection;
113
if (lockKeyPressed == false)
115
//clear the list and evaluate the last selection
116
emit selectionRemoved();
118
//if there is no current selection, there is nothing to do
119
if (currentSelection() == -1)
121
emit repaintRequested();
125
cselection = selection(currentSelection());
127
for (int index = cselection.topRow(); index <= cselection.bottomRow(); index++)
129
emit selected(text(index, 0).toInt());
132
//emit repaintRequested();
136
void QgsAttributeTable::insertFeatureId(int id, int row)
138
rowIdMap.insert(id, row);
141
void QgsAttributeTable::selectRowWithId(int id)
143
QMap < int, int >::iterator it = rowIdMap.find(id);
144
selectRow(it.data());
147
void QgsAttributeTable::sortColumn(int col, bool ascending, bool wholeRows)
149
//if the first entry contains a letter, sort alphanumerically, otherwise numerically
150
QString firstentry = text(0, col);
151
bool containsletter = false;
152
for (uint i = 0; i < firstentry.length(); i++)
154
if (firstentry.ref(i).isLetter())
156
containsletter = true;
162
qsort(0, numRows() - 1, col, ascending, true);
165
qsort(0, numRows() - 1, col, ascending, false);
173
XXX Doesn't QString have something ilke this already?
175
int QgsAttributeTable::compareItems(QString s1, QString s2, bool ascending, bool alphanumeric)
203
double d1 = s1.toDouble();
204
double d2 = s2.toDouble();
229
return 0; // XXX has to return something; is this reasonable?
233
void QgsAttributeTable::qsort(int lower, int upper, int col, bool ascending, bool alphanumeric)
239
//chose a random element (this avoids n^2 worst case)
240
int element = int ( (double)rand() / (double)RAND_MAX * (upper - lower) + lower);
241
swapRows(element, upper);
242
v = text(upper, col);
247
while (compareItems(text(++i, col), v, ascending, alphanumeric) == -1);
248
while (compareItems(text(--j, col), v, ascending, alphanumeric) == 1 && j > 0); //make sure that j does not get negative
256
qsort(lower, i - 1, col, ascending, alphanumeric);
257
qsort(i + 1, upper, col, ascending, alphanumeric);
261
void QgsAttributeTable::contentsMouseReleaseEvent(QMouseEvent * e)
263
contentsMouseMoveEvent(e); //send out a move event to keep the selections updated
264
emit repaintRequested();
267
void QgsAttributeTable::popupMenu(int row, int col, const QPoint& pos)
269
std::cerr << "context menu requested" << std::endl;
271
// Duplication of code in qgsidentufyresults.cpp. Consider placing
272
// in a seperate class
273
if (mActionPopup == 0)
275
mActionPopup = new QPopupMenu();
277
QLabel* popupLabel = new QLabel( mActionPopup );
278
popupLabel->setText( tr("<center>Run action</center>") );
279
mActionPopup->insertItem(popupLabel);
280
mActionPopup->insertSeparator();
282
QgsAttributeAction::aIter iter = mActions.begin();
283
for (int j = 0; iter != mActions.end(); ++iter, ++j)
285
int id = mActionPopup->insertItem(iter->name(), this,
286
SLOT(popupItemSelected(int)));
287
mActionPopup->setItemParameter(id, j);
291
// Get and store the attribute values and their column names are
292
// these are needed for substituting into the actions if the user
294
mActionValues.clear();
295
QHeader* header = horizontalHeader();
298
if (header->count() != numCols())
299
std::cerr << "Something wrong with the table (file "
300
<< __FILE__<< ", line " << __LINE__
301
<< ")." << std::endl;
304
for (int i = 0; i < numCols(); ++i)
305
mActionValues.push_back(std::make_pair(header->label(i), text(row, i)));
307
// The item that was clicked on, stored as an index into the
308
// mActionValues vector.
309
mClickedOnValue = col;
311
mActionPopup->popup(pos);
314
void QgsAttributeTable::popupItemSelected(int id)
316
mActions.doAction(id, mActionValues, mClickedOnValue);
319
bool QgsAttributeTable::addAttribute(const QString& name, const QString& type)
321
//first test if an attribute with the same name is already in the table
322
for(int i=0;i<horizontalHeader()->count();++i)
324
if(horizontalHeader()->label(i)==name)
330
mAddedAttributes.insert(std::make_pair(name,type));
332
qWarning(("inserting attribute "+name+" of type "+type).local8Bit());
333
//add a new column at the end of the table
334
qWarning(("numCols: "+QString::number(numCols())).local8Bit());
336
insertColumns(numCols());
337
horizontalHeader()->setLabel(numCols()-1,name);
342
void QgsAttributeTable::deleteAttribute(const QString& name)
344
//check, if there is already an attribute with this name in mAddedAttributes
345
std::map<QString,QString>::iterator iter=mAddedAttributes.find(name);
346
if(iter!=mAddedAttributes.end())
348
mAddedAttributes.erase(iter);
349
removeAttrColumn(name);
354
qWarning(("QgsAttributeTable: deleteAttribute "+name).local8Bit());
356
mDeletedAttributes.insert(name);
357
removeAttrColumn(name);
362
bool QgsAttributeTable::commitChanges(QgsVectorLayer* layer)
364
bool returnvalue=true;
367
if(!layer->commitAttributeChanges(mDeletedAttributes, mAddedAttributes, mChangedValues))
377
clearEditingStructures();
381
bool QgsAttributeTable::rollBack(QgsVectorLayer* layer)
385
layer->fillTable(this);
388
clearEditingStructures();
392
//todo: replace some of the lines in QgsVectorLayer::table() with this function
393
void QgsAttributeTable::fillTable(QgsVectorLayer* layer)
395
QgsVectorDataProvider* provider=layer->getDataProvider();
399
// set up the column headers
400
QHeader *colHeader = horizontalHeader();
401
colHeader->setLabel(0, "id"); //label for the id-column
402
std::vector < QgsField > fields = provider->fields();
403
int fieldcount=provider->fieldCount();
404
setNumCols(fieldcount+1);
406
for (int h = 1; h <= fieldcount; h++)
408
colHeader->setLabel(h, fields[h - 1].name());
410
qWarning(("Setting column label "+fields[h - 1].name()).local8Bit());
415
while ((fet = provider->getNextFeature(true)))
418
setText(row, 0, QString::number(fet->featureId()));
419
insertFeatureId(fet->featureId(), row); //insert the id into the search tree of qgsattributetable
420
std::vector < QgsFeatureAttribute > attr = fet->attributeMap();
421
for (int i = 0; i < attr.size(); i++)
423
// get the field values
424
setText(row, i + 1, attr[i].fieldValue());
426
//qWarning("Setting value for "+QString::number(i+1)+"//"+QString::number(row)+"//"+attr[i].fieldValue());
435
void QgsAttributeTable::storeChangedValue(int row, int column)
437
//id column is not editable
441
int id=text(row,0).toInt();
442
QString attribute=horizontalHeader()->label(column);
444
qWarning(("feature id: "+QString::number(id)).local8Bit());
445
qWarning(("attribute: "+attribute).local8Bit());
447
std::map<int,std::map<QString,QString> >::iterator iter=mChangedValues.find(id);
448
if(iter==mChangedValues.end())
450
std::map<QString,QString> themap;
451
mChangedValues.insert(std::make_pair(id,themap));
452
iter=mChangedValues.find(id);
454
iter->second.erase(attribute);
455
iter->second.insert(std::make_pair(attribute,text(row,column)));
457
qWarning(("value: "+text(row,column)).local8Bit());
463
void QgsAttributeTable::clearEditingStructures()
465
mDeletedAttributes.clear();
466
mAddedAttributes.clear();
467
for(std::map<int,std::map<QString,QString> >::iterator iter=mChangedValues.begin();iter!=mChangedValues.end();++iter)
469
iter->second.clear();
471
mChangedValues.clear();
474
void QgsAttributeTable::removeAttrColumn(const QString& name)
476
QHeader* header=horizontalHeader();
477
for(int i=0;i<header->count();++i)
479
if(header->label(i)==name)
487
void QgsAttributeTable::bringSelectedToTop()
491
std::list<QTableSelection> selections;
492
bool removeselection;
494
for(int i=0;i<numSelections();++i)
496
selections.push_back(selection(i));
501
for(std::list<QTableSelection>::iterator iter=selections.begin();iter!=selections.end();++iter)
503
removeselection=true;
504
while(isRowSelected(swaptorow, true))//selections are not necessary stored in ascending order
509
for(int j=iter->topRow();j<=iter->bottomRow();++j)
511
if(j>swaptorow)//selections are not necessary stored in ascending order
513
swapRows(j,swaptorow);
514
selectRow(swaptorow);
520
removeselection=false;//keep selection
525
removeSelection(*iter);
529
//clear and rebuild rowIdMap.
532
for (int i = 0; i < numRows(); i++)
534
id = text(i, 0).toInt();
535
rowIdMap.insert(id, i);