[QGIS Commit] r10337 - in trunk/qgis/src/app: . attributetable legend

svn_qgis at osgeo.org svn_qgis at osgeo.org
Fri Mar 20 14:24:19 EDT 2009


Author: wonder
Date: 2009-03-20 14:24:19 -0400 (Fri, 20 Mar 2009)
New Revision: 10337

Added:
   trunk/qgis/src/app/attributetable/
   trunk/qgis/src/app/attributetable/BeataDialog.cpp
   trunk/qgis/src/app/attributetable/BeataDialog.h
   trunk/qgis/src/app/attributetable/BeataModel.cpp
   trunk/qgis/src/app/attributetable/BeataModel.h
   trunk/qgis/src/app/attributetable/BeataView.cpp
   trunk/qgis/src/app/attributetable/BeataView.h
Modified:
   trunk/qgis/src/app/CMakeLists.txt
   trunk/qgis/src/app/legend/qgslegend.cpp
   trunk/qgis/src/app/legend/qgslegendlayerfile.cpp
   trunk/qgis/src/app/qgisapp.cpp
Log:
Greetings from hackfest from one-handed guy :-)

Imported new attribute table. enjoy!



Modified: trunk/qgis/src/app/CMakeLists.txt
===================================================================
--- trunk/qgis/src/app/CMakeLists.txt	2009-03-20 17:35:32 UTC (rev 10336)
+++ trunk/qgis/src/app/CMakeLists.txt	2009-03-20 18:24:19 UTC (rev 10337)
@@ -82,6 +82,10 @@
   ogr/qgsogrhelperfunctions.cpp
   ogr/qgsopenvectorlayerdialog.cpp
   ogr/qgsnewogrconnection.cpp
+  
+  attributetable/BeataDialog.cpp
+  attributetable/BeataModel.cpp
+  attributetable/BeataView.cpp
   )
 
 
@@ -149,6 +153,9 @@
   
   ogr/qgsopenvectorlayerdialog.h
   ogr/qgsnewogrconnection.h
+  
+  attributetable/BeataDialog.h
+  attributetable/BeataModel.h
   )
 
 IF (POSTGRES_FOUND)
@@ -202,7 +209,7 @@
 
 
 INCLUDE_DIRECTORIES(
-  ${CMAKE_CURRENT_SOURCE_DIR} composer legend
+  ${CMAKE_CURRENT_SOURCE_DIR} composer legend attributetable
   ${CMAKE_CURRENT_BINARY_DIR}
   ${CMAKE_CURRENT_BINARY_DIR}/../ui
   ../core

Added: trunk/qgis/src/app/attributetable/BeataDialog.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/BeataDialog.cpp	                        (rev 0)
+++ trunk/qgis/src/app/attributetable/BeataDialog.cpp	2009-03-20 18:24:19 UTC (rev 10337)
@@ -0,0 +1,508 @@
+/***************************************************************************
+  BeataDialog.cpp 
+  BEtter Attribute TAble
+  -------------------
+         date                 : Feb 2009
+         copyright            : Vita Cizek
+         email                : weetya (at) gmail.com
+
+ ***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <QtGui>
+
+#include "BeataDialog.h"
+#include "BeataModel.h"
+#include "BeataView.h"
+
+#include <qgsapplication.h>
+#include <qgsvectordataprovider.h>
+#include <qgsvectorlayer.h>
+#include <qgssearchstring.h>
+#include <qgssearchtreenode.h>
+
+#include "qgisapp.h"
+#include "qgssearchquerybuilder.h"
+
+BeataDialog::BeataDialog(QgsVectorLayer *theLayer, QWidget *parent, Qt::WindowFlags flags)
+  : QDialog(parent, flags)
+{
+  mLayer = theLayer;
+
+  setupUi(this);
+  
+  setAttribute(Qt::WA_DeleteOnClose);
+
+  QSettings settings;
+  restoreGeometry( settings.value( "/Windows/BetterAttributeTable/geometry" ).toByteArray() );
+  
+  mView->setLayer(mLayer);
+  mFilterModel = (BeataFilterModel *) mView->model();
+  mModel = (BeataModel *)((BeataFilterModel *)mView->model())->sourceModel();
+
+  mQuery = query;
+  mColumnBox = columnBox;
+  columnBoxInit();
+
+  mShowBox = showBox;
+  mShowBox->addItem("Show unselected rows");
+  mShowBox->addItem("Hide unselected rows");
+  
+  mMenuActions = new QMenu();
+  mMenuActions->addAction(tr("Advanced search"), this, SLOT(advancedSearch()));
+  mMenuActions->addSeparator();
+  mMenuActions->addAction(getThemeIcon( "/mActionCopySelected.png" ), tr("Copy selected rows"), this, SLOT(copySelectedRowsToClipboard()));
+  mMenuActions->addAction(getThemeIcon( "/mActionZoomToSelected.png" ), tr("Zoom to selected"), this, SLOT(zoomMapToSelectedRows()));
+  mMenuActions->addAction(getThemeIcon( "/mActionSelectedToTop.png" ), tr("Move selected to top"), this, SLOT(selectedToTop()));
+  mMenuActions->addAction(getThemeIcon( "/mActionUnselectAttributes.png" ), tr("Clear selection"), this, SLOT(removeSelection()));
+  mMenuActions->addAction(getThemeIcon( "/mActionInvertSelection.png" ), tr("Invert selection"), this, SLOT(invertSelection()));
+  mMenuActions->addSeparator();
+  
+  // toggle editing
+  mActionToggleEditing = mMenuActions->addAction(getThemeIcon( "/mActionToggleEditing.png" ), tr("Toggle editing"), this, SLOT(toggleEditing()));
+  mActionToggleEditing->setCheckable( true );
+  mActionToggleEditing->setEnabled( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues );
+  // info from table to application
+  connect( this, SIGNAL( editingToggled( QgsMapLayer * ) ), parentWidget(), SLOT( toggleEditing( QgsMapLayer * ) ) );
+  // info from layer to table
+  connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
+  connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );
+  
+  connect(mShowBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(toggleShowDeselected(const QString &)));
+  connect(searchButton, SIGNAL(clicked()), this, SLOT(search()));
+  connect(actionsButton, SIGNAL(clicked()), this, SLOT(showAdvanced()));
+
+  connect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+  connect(mLayer, SIGNAL(layerDeleted()), this, SLOT( close()));
+  connect(mView->verticalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(updateRowSelection(int)));
+  connect(mModel, SIGNAL(modelChanged()), this, SLOT(updateSelection()));
+
+  mLastClickedHeaderIndex = 0;
+  mSelectionModel = new QItemSelectionModel(mFilterModel);
+  updateSelectionFromLayer();
+}
+
+BeataDialog::~BeataDialog()
+{
+}
+
+void BeataDialog::closeEvent( QCloseEvent* event )
+{
+  QDialog::closeEvent( event );
+  
+  QSettings settings;
+  settings.setValue( "/Windows/BetterAttributeTable/geometry", saveGeometry() );
+}
+
+
+QIcon BeataDialog::getThemeIcon( const QString theName )
+{
+  // copied from QgisApp::getThemeIcon. To be removed when merged to SVN
+  
+  QString myPreferredPath = QgsApplication::activeThemePath() + QDir::separator() + theName;
+  QString myDefaultPath = QgsApplication::defaultThemePath() + QDir::separator() + theName;
+  if ( QFile::exists( myPreferredPath ) )
+  {
+    return QIcon( myPreferredPath );
+  }
+  else if ( QFile::exists( myDefaultPath ) )
+  {
+    //could still return an empty icon if it
+    //doesnt exist in the default theme either!
+    return QIcon( myDefaultPath );
+  }
+  else
+  {
+    return QIcon();
+  }
+}
+
+void BeataDialog::showAdvanced()
+{
+  mMenuActions->exec(QCursor::pos());
+}
+
+void BeataDialog::selectedToTop()
+{
+  int freeIndex = 0;
+
+  //QgsFeatureIds fids = mSelectedFeatures;
+  //QgsFeatureIds::Iterator it = fids.begin();
+
+  mModel->incomingChangeLayout();
+
+  QgsFeatureIds::Iterator it = mSelectedFeatures.begin();
+  for (; it != mSelectedFeatures.end(); ++it, ++freeIndex)
+  {
+    QModelIndex sourceIndex = mFilterModel->mapToSource(mFilterModel->index(freeIndex, 0));
+    mModel->swapRows(mModel->rowToId(sourceIndex.row()), *it);
+  }
+
+/*
+  while (it != fids.end())
+  { //map!!!!
+    //mModel->swapRows(mModel->rowToId(freeIndex), *it);
+    //QModelIndex index = mFilterModel->mapFromSource(mModel->index(mModel->idToRow(*it), 0));
+    QModelIndex sourceIndex = mFilterModel->mapToSource(mFilterModel->index(freeIndex, 0));
+    mModel->swapRows(mModel->rowToId(sourceIndex.row()), *it);
+    //mModel->swapRows(freeIndex, *it);
+
+    if (fids.empty())
+      break;
+    else
+      ++it;
+
+    ++freeIndex; 
+  }
+*/
+  // just select proper rows
+  //mModel->reload(mModel->index(0,0), mModel->index(mModel->rowCount(), mModel->columnCount()));
+  //mModel->changeLayout();
+  mModel->resetModel();
+  updateSelection();
+}
+
+void BeataDialog::copySelectedRowsToClipboard()
+{
+  QgisApp* pApp = dynamic_cast<QgisApp*>(parentWidget());
+  if (pApp)
+    pApp->editCopy(mLayer);
+}
+
+void BeataDialog::zoomMapToSelectedRows()
+{
+  QgisApp* pApp = dynamic_cast<QgisApp*>(parentWidget());
+  if (pApp)
+    pApp->zoomToSelected();
+}
+
+void BeataDialog::invertSelection()
+{
+  mLayer->invertSelection();
+}
+
+void BeataDialog::removeSelection()
+{
+  mLayer->removeSelection();
+}
+
+void BeataDialog::toggleShowDeselected(const QString &text)
+{
+  if (text == "Show unselected rows")
+  {
+    mFilterModel->mHideUnselected = false;
+    //TODO: divne
+    //mModel->changeLayout();
+    mFilterModel->invalidate();
+    return;
+  }
+
+  // show only selected
+  mFilterModel->mHideUnselected = true;
+  mFilterModel->invalidate();
+  //mModel->changeLayout();
+}
+
+void BeataDialog::columnBoxInit()
+{
+  QgsFieldMap fieldMap = mLayer->dataProvider()->fields();
+  QgsFieldMap::Iterator it = fieldMap.begin();
+
+  for (; it != fieldMap.end(); ++it)
+    mColumnBox->addItem(it.value().name());
+}
+
+int BeataDialog::columnBoxColumnId()
+{
+  QgsFieldMap fieldMap = mLayer->dataProvider()->fields();
+  QgsFieldMap::Iterator it = fieldMap.begin();
+
+  for (; it != fieldMap.end(); ++it)
+    if (it.value().name() == mColumnBox->currentText())
+      return it.key();
+
+  return 0;
+}
+
+void BeataDialog::updateSelection()
+{
+  QModelIndex index;
+  mView->setSelectionMode(QAbstractItemView::MultiSelection);
+
+  QItemSelection selection;
+  
+  QgsFeatureIds::Iterator it = mSelectedFeatures.begin();
+  for (; it != mSelectedFeatures.end(); ++it)
+  {
+    QModelIndex leftUpIndex = mFilterModel->mapFromSource(mModel->index(mModel->idToRow(*it), 0));
+    QModelIndex rightBottomIndex = mFilterModel->mapFromSource(mModel->index(mModel->idToRow(*it), mModel->columnCount() - 1));
+    selection.append(QItemSelectionRange(leftUpIndex, rightBottomIndex));
+    //selection.append(QItemSelectionRange(leftUpIndex, leftUpIndex));
+  }
+
+  mSelectionModel->select(selection, QItemSelectionModel::ClearAndSelect);// | QItemSelectionModel::Columns);
+  mView->setSelectionModel(mSelectionModel);
+  mView->setSelectionMode(QAbstractItemView::NoSelection);
+
+  /*for (int i = 0; i < mModel->rowCount(); ++i)
+  {
+	 int id = mModel->rowToId(i);
+    std::cout << id << "\n";
+  }
+  std::cout << "--------------\n";
+*/
+}
+
+void BeataDialog::updateRowSelection(int index)
+{
+  // map index to filter model
+  //index = mFilterModel->mapFromSource(mModel->index(index, 0)).row();
+
+  if (mView->shiftPressed) {
+	std::cout << "shift\n";
+    // get the first and last index of the rows to be selected/deselected
+    int first, last;
+    if (index > mLastClickedHeaderIndex) {
+      first = mLastClickedHeaderIndex + 1;
+      last = index;
+    }
+    else if (index == mLastClickedHeaderIndex) {
+      // row was selected and now it is shift-clicked
+      // ignore the shift and deselect the row
+      first = last = index;
+    }
+    else {
+      first = index;
+      last = mLastClickedHeaderIndex - 1;
+    }
+    
+    // for all the rows update the selection, without starting a new selection
+    if (first <= last)
+      updateRowSelection(first, last, false);
+      
+    mLastClickedHeaderIndex = last;
+  }
+  else if (mView->ctrlPressed) {
+	std::cout << "ctrl\n";
+    // update the single row selection, without starting a new selection
+    updateRowSelection(index, index, false);
+    
+    // the next shift would start from here
+    mLastClickedHeaderIndex = index;
+  } 
+  else {
+	std::cout << "ordinary click\n";
+    // update the single row selection, start a new selection if the row was not selected
+    updateRowSelection(index, index, true);
+    
+    // the next shift would start from here
+    mLastClickedHeaderIndex = index;
+  }
+}
+
+// fast row deselection needed
+void BeataDialog::updateRowSelection(int first, int last, bool startNewSelection)
+{  
+  disconnect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+
+  //index = mFilterModel->mapFromSource(mModel->index(index, 0)).row();
+  // Id must be mapped to table/view row
+  QModelIndex index = mFilterModel->mapToSource(mFilterModel->index(first, 0));
+  int fid = mModel->rowToId(index.row());
+  bool wasSelected = mSelectedFeatures.contains(fid);
+
+  // new selection should be created
+  if (startNewSelection)
+  {
+    mView->clearSelection();
+    mSelectedFeatures.clear();
+
+    if (wasSelected)
+    {
+      mLayer->removeSelection();
+      connect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+      return;
+    }
+
+    // set clicked row to current
+    mView->setCurrentIndex(mFilterModel->index(first, 0));
+    mView->setSelectionMode(QAbstractItemView::SingleSelection);
+
+    //QModelIndex index = mFilterModel->mapFromSource(mModel->index(first, 0));
+
+    mView->selectRow(first);
+    mView->setSelectionMode(QAbstractItemView::NoSelection);
+
+    mSelectedFeatures.insert(fid);
+    //mLayer->setSelectedFeatures(mSelectedFeatures);
+    mLayer->removeSelection();
+    mLayer->select(fid);
+    //mFilterModel->invalidate();
+    connect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+    return;
+  }
+
+  // existing selection should be updated
+  for (int i = first; i <= last; ++i) {
+    if (i > first) {
+      // Id must be mapped to table/view row
+      index = mFilterModel->mapToSource(mFilterModel->index(i, 0));
+      fid = mModel->rowToId(index.row());
+      wasSelected = mSelectedFeatures.contains(fid);
+    }
+    
+    if (wasSelected) 
+      mSelectedFeatures.remove(fid);
+    else
+      mSelectedFeatures.insert(fid);
+  }
+  //mFilterModel->invalidate();
+    
+  /*
+  QItemSelection selection;
+  QModelIndex leftUpIndex = mFilterModel->index(first, 0);
+  QModelIndex rightBottomIndex = mFilterModel->index(last, mModel->columnCount() - 1);
+  selection.append(QItemSelectionRange(leftUpIndex, rightBottomIndex));
+  mSelectionModel->select(selection, QItemSelectionModel::Select);
+  mView->setSelectionModel(mSelectionModel);
+  */
+  updateSelection();
+  mLayer->setSelectedFeatures(mSelectedFeatures);
+  connect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+}
+
+void BeataDialog::updateSelectionFromLayer()
+{
+  std::cout << "updateFromLayer\n";
+  mSelectedFeatures = mLayer->selectedFeaturesIds();
+  updateSelection();
+}
+
+void BeataDialog::doSearch(QString searchString)
+{
+  // parse search string and build parsed tree
+  QgsSearchString search;
+  if (!search.setString(searchString))
+  {
+    QMessageBox::critical(this, tr("Search string parsing error"), search.parserErrorMsg());
+    return;
+  }
+
+  QgsSearchTreeNode* searchTree = search.tree();
+  if (searchTree == NULL)
+  {
+    QMessageBox::information(this, tr("Search results"), tr("You've supplied an empty search string."));
+    return;
+  }
+
+  QApplication::setOverrideCursor(Qt::WaitCursor);
+
+  mSelectedFeatures.clear();
+  mLayer->select(mLayer->pendingAllAttributesList(), QgsRectangle(), false);
+
+  QgsFeature f;
+  while (mLayer->nextFeature(f))
+  {
+    if (searchTree->checkAgainst(mLayer->pendingFields(), f.attributeMap()))
+      mSelectedFeatures << f.id();
+
+    // check if there were errors during evaluating
+    if (searchTree->hasError())
+      break;
+  }
+
+  QApplication::restoreOverrideCursor();
+
+  if (searchTree->hasError())
+  {
+    QMessageBox::critical(this, tr("Error during search"), searchTree->errorMsg());
+    return;
+  }
+
+  // update view
+  updateSelection();
+
+  disconnect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+  mLayer->setSelectedFeatures(mSelectedFeatures);
+  connect(mLayer, SIGNAL(selectionChanged()), this, SLOT(updateSelectionFromLayer()));
+
+  QString str;
+  if (mSelectedFeatures.size())
+    str.sprintf(tr("Found %d matching features.", "", mSelectedFeatures.size()).toUtf8(), mSelectedFeatures.size());
+  else
+    str = tr("No matching features found.");
+
+  QMessageBox::information(this, tr("Search results"), str);
+}
+
+void BeataDialog::search()
+{
+
+  QString str = mColumnBox->currentText();
+
+  const QgsFieldMap& flds = mLayer->dataProvider()->fields();
+  int fldIndex = mLayer->dataProvider()->fieldNameIndex(str);
+  QVariant::Type fldType = flds[fldIndex].type();
+  bool numeric = (fldType == QVariant::Int || fldType == QVariant::Double);
+  
+  if (numeric)
+    str += " = '";
+  else
+    str += " ~ '";
+  
+  str += mQuery->displayText();
+  str += "'";
+
+  doSearch(str);
+}
+
+void BeataDialog::advancedSearch()
+{
+  QgsSearchQueryBuilder dlg(mLayer, this);
+  dlg.setSearchString(mQuery->displayText());
+
+  if (dlg.exec())
+    doSearch(dlg.searchString());
+}
+
+void BeataDialog::toggleEditing()
+{
+  emit editingToggled( mLayer );
+}
+
+void BeataDialog::editingToggled()
+{
+  mActionToggleEditing->setChecked( mLayer->isEditable() );
+  
+  // (probably reload data if user stopped editing - possible revert)
+  mModel->reload(mModel->index(0,0), mModel->index(mModel->rowCount(), mModel->columnCount()));
+  
+  // not necessary to set table read only if layer is not editable
+  // because model always reflects actual state when returning item flags
+}
+
+// not used now
+void BeataDialog::startEditing()
+{
+  mLayer->startEditing();
+}
+
+// not used now
+void BeataDialog::submit()
+{
+  mLayer->commitChanges();
+}
+
+// not used now
+void BeataDialog::revert()
+{
+  mLayer->rollBack();
+  mModel->revert();
+  mModel->reload(mModel->index(0,0), mModel->index(mModel->rowCount(), mModel->columnCount()));
+}

Added: trunk/qgis/src/app/attributetable/BeataDialog.h
===================================================================
--- trunk/qgis/src/app/attributetable/BeataDialog.h	                        (rev 0)
+++ trunk/qgis/src/app/attributetable/BeataDialog.h	2009-03-20 18:24:19 UTC (rev 10337)
@@ -0,0 +1,103 @@
+/***************************************************************************
+  BeataDialog.h 
+  BEtter Attribute TAble
+  -------------------
+         date                 : Feb 2009
+         copyright            : Vita Cizek
+         email                : weetya (at) gmail.com
+
+ ***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef BEATADIALOG_H_
+#define BEATADIALOG_H_
+
+#include <QDialog>
+#include <QModelIndex>
+#include <QItemSelectionModel>
+#include <QMutex>
+
+#include "ui_BeataGui.h"
+
+class QgsMapLayer;
+class QgsVectorLayer;
+
+#include "qgsvectorlayer.h" //QgsFeatureIds
+
+class QDialogButtonBox;
+class QPushButton;
+class QLineEdit;
+class QComboBox;
+class QMenu;
+
+class BeataModel;
+class BeataFilterModel;
+class BeataView;
+
+class BeataDialog : public QDialog, private Ui::BeataDialogGui
+{
+Q_OBJECT
+
+public:
+  BeataDialog(QgsVectorLayer *theLayer, QWidget *parent = 0, Qt::WindowFlags flags = Qt::Window);
+  ~BeataDialog();
+
+public slots:
+  void editingToggled();
+  
+private slots:
+  void submit();
+  void revert();
+  void search();
+  void advancedSearch();
+  void updateSelection();
+  void updateSelectionFromLayer();
+  void updateRowSelection(int index);
+  void updateRowSelection(int first, int last, bool startNewSelection);
+  void toggleShowDeselected(const QString &text);
+
+  void startEditing();
+  void invertSelection();
+  void removeSelection();
+  void copySelectedRowsToClipboard();
+  void zoomMapToSelectedRows();
+  void selectedToTop();
+  void showAdvanced();
+  void toggleEditing();
+
+signals:
+  void editingToggled( QgsMapLayer * );
+  
+protected:
+  void closeEvent( QCloseEvent* event );
+  
+private:
+  void columnBoxInit();
+  int columnBoxColumnId();
+  void doSearch(QString searchString);
+
+  QIcon getThemeIcon( const QString theName );
+  
+  QLineEdit *mQuery;
+  QComboBox *mColumnBox; 
+  QComboBox *mShowBox; 
+  
+  QMenu* mMenuActions;
+  QAction* mActionToggleEditing;
+
+  BeataModel *mModel;
+  BeataFilterModel *mFilterModel;
+  QgsVectorLayer *mLayer;
+  QgsFeatureIds mSelectedFeatures;
+
+  QItemSelectionModel* mSelectionModel;
+  int mLastClickedHeaderIndex;
+};
+
+#endif

Added: trunk/qgis/src/app/attributetable/BeataModel.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/BeataModel.cpp	                        (rev 0)
+++ trunk/qgis/src/app/attributetable/BeataModel.cpp	2009-03-20 18:24:19 UTC (rev 10337)
@@ -0,0 +1,630 @@
+/***************************************************************************
+     BeataModel.cpp
+     --------------------------------------
+    Date                 : Feb 2009 
+    Copyright            : (C) 2009 Vita Cizek
+    Email                : weetya (at) gmail.com
+ ***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "BeataModel.h"
+#include "BeataView.h"
+
+#include "qgsvectordataprovider.h"
+#include "qgsfield.h"
+#include "qgsvectorlayer.h"
+
+#include <QtGui>
+#include <QVariant>
+#include <QtAlgorithms>
+
+//could be faster when type guessed before sorting
+bool idColumnPair::operator<(const idColumnPair &b) const
+{
+  //QVariat thinks gid is a string!
+  QVariant::Type columnType = columnItem.type();
+
+  if (columnType == QVariant::Int || columnType == QVariant::UInt || columnType == QVariant::LongLong || columnType == QVariant::ULongLong)
+    return columnItem.toLongLong() < b.columnItem.toLongLong();
+
+  if (columnType == QVariant::Double)
+	  return columnItem.toDouble() < b.columnItem.toDouble();
+
+  return columnItem.toString() < b.columnItem.toString();
+}
+
+//////////////////
+// Filter Model //
+//////////////////
+
+void BeataFilterModel::sort(int column, Qt::SortOrder order)
+{
+	((BeataModel *)sourceModel())->sort(column, order);	
+}
+
+BeataFilterModel::BeataFilterModel(QgsVectorLayer* theLayer)
+{
+  mLayer = theLayer;
+  mHideUnselected = false;
+  setDynamicSortFilter(true);
+}
+
+bool BeataFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+  if(mHideUnselected)
+  // unreadable? yes, i agree :-)
+    return mLayer->selectedFeaturesIds().contains(((BeataModel *)sourceModel())->rowToId(sourceRow));
+
+  return true;
+}
+
+/*
+QModelIndex BeataFilterModel::mapFromSource ( const QModelIndex& sourceIndex ) const
+{
+  return sourceIndex;
+}
+
+QModelIndex BeataFilterModel::mapToSource ( const QModelIndex& filterIndex ) const
+{
+  return filterIndex;
+}
+*/
+
+////////////////
+// BeataModel //
+////////////////
+
+BeataModel::BeataModel(QgsVectorLayer *theLayer, QObject *parent)
+: QAbstractTableModel(parent) {
+  mLastRowId = -1;
+  mLastRow = NULL;
+  mLayer = theLayer;
+  mFeatureCount = mLayer->pendingFeatureCount();
+  mFieldCount = mLayer->dataProvider()->fieldCount();
+  mAttributes = mLayer->dataProvider()->attributeIndexes();
+
+  connect(mLayer, SIGNAL(layerModified(bool)), this, SLOT( layerModified(bool)));
+  //connect(mLayer, SIGNAL(attributeAdded(int)), this, SLOT( attributeAdded(int)));
+  //connect(mLayer, SIGNAL(attributeDeleted(int)), this, SLOT( attributeDeleted(int)));
+  //connect(mLayer, SIGNAL(attributeValueChanged(int, int, const QVariant&)), this, SLOT( attributeValueChanged(int, int, const QVariant&)));
+  //connect(mLayer, SIGNAL(featureDeleted(int)), this, SLOT( featureDeleted(int)));
+  //connect(mLayer, SIGNAL(featureAdded(int)), this, SLOT( featureAdded(int)));
+  
+  loadLayer();
+}
+
+void BeataModel::featureDeleted(int fid)
+{
+  std::cout << "BM feature deleted\n";
+
+  int idx = mIdRowMap[fid];
+  std::cout << idx;
+  std::cout << fid;
+
+  /*--mFeatureCount;
+  mIdRowMap.remove(fid);
+  mRowIdMap.remove(idx);
+  
+  // fill the hole in the view
+  if (idx != mFeatureCount)
+  {
+	  std::cout << "jo";
+    //mRowIdMap[idx] = mRowIdMap[mFeatureCount];
+    //mIdRowMap[mRowIdMap[idx]] = idx;
+    int movedId = mRowIdMap[mFeatureCount];
+    mRowIdMap.remove(mFeatureCount);
+    mRowIdMap.insert(idx, movedId);
+    mIdRowMap[movedId] = idx;
+    //mIdRowMap.remove(mRowIdMap[idx]);
+    //mIdRowMap.insert(mRowIdMap[idx], idx);
+  }
+
+  std::cout << "map sizes:" << mRowIdMap.size() << ", " << mIdRowMap.size();
+  emit layoutChanged();
+  //reload(index(0,0), index(rowCount(), columnCount()));
+  std::cout << "\n";
+*/
+  std::cout << "id->row";
+  QMap<int, int>::iterator it;
+  for (it = mIdRowMap.begin(); it != mIdRowMap.end(); ++it)
+	  std::cout << it.key() << "->" << *it << "\n";
+
+  std::cout << "row->id";
+
+  for (it = mRowIdMap.begin(); it != mRowIdMap.end(); ++it)
+	  std::cout << it.key() << "->" << *it << "\n";
+
+}
+ 
+void BeataModel::featureAdded(int fid)
+{
+  std::cout << "BM feature added\n";
+  ++mFeatureCount;
+  mIdRowMap.insert(fid, mFeatureCount - 1);
+  mRowIdMap.insert(mFeatureCount - 1, fid);
+  std::cout << "map sizes:" << mRowIdMap.size() << ", " << mIdRowMap.size() << "\n";;
+  reload(index(0,0), index(rowCount(), columnCount()));
+}
+ 
+void BeataModel::attributeAdded (int idx)
+{
+std::cout << "BM attribute added\n";
+  loadLayer();
+  std::cout << "map sizes:" << mRowIdMap.size() << ", " << mIdRowMap.size() << "\n";;
+  reload(index(0,0), index(rowCount(), columnCount()));
+  emit modelChanged();
+}
+ 
+void BeataModel::attributeDeleted (int idx)
+{
+std::cout << "BM attribute deleted\n";
+  loadLayer();
+  std::cout << "map sizes:" << mRowIdMap.size() << ", " << mIdRowMap.size() << "\n";;
+  reload(index(0,0), index(rowCount(), columnCount()));
+  emit modelChanged();
+}
+ 
+void BeataModel::layerDeleted()
+{
+std::cout << "BM attribute deleted\n";
+  mIdRowMap.clear();
+  mRowIdMap.clear();
+  std::cout << "map sizes:" << mRowIdMap.size() << ", " << mIdRowMap.size() << "\n";;
+  reload(index(0,0), index(rowCount(), columnCount()));
+}
+ 
+//TODO: check whether caching in data()/setData() doesn't cache old value
+void BeataModel::attributeValueChanged (int fid, int idx, const QVariant &value)
+{
+  std::cout << "BM attribute changed\n";
+  reload(index(0,0), index(rowCount(), columnCount()));
+}
+
+void BeataModel::layerModified(bool onlyGeometry)
+{
+  if (onlyGeometry)
+    return;
+
+  loadLayer();
+  emit modelChanged();
+}
+
+void BeataModel::loadLayer()
+{
+  std::cout << "BM loadlayer\n";
+
+  QgsFeature f;
+  bool ins = false, rm = false;
+
+  mRowIdMap.clear();
+  mIdRowMap.clear();
+
+  if (mFeatureCount < mLayer->pendingFeatureCount()) 
+  {
+    std::cout<<"ins\n";
+    ins = true; 
+    beginInsertRows(QModelIndex(), mFeatureCount, mLayer->pendingFeatureCount() - 1);
+    //std::cout << mFeatureCount << ", " << mLayer->pendingFeatureCount() - 1 << "\n";
+  }
+  else if (mFeatureCount > mLayer->pendingFeatureCount()) 
+  {
+    std::cout<<"rm\n";
+    rm = true; 
+    beginRemoveRows(QModelIndex(), mLayer->pendingFeatureCount(), mFeatureCount - 1);
+    //std::cout << mFeatureCount << ", " << mLayer->pendingFeatureCount() -1 << "\n";
+  }
+
+  mLayer->select(QgsAttributeList(), QgsRectangle(), false);
+
+  for (int i = 0; mLayer->nextFeature(f); ++i)
+  {
+    mRowIdMap.insert(i, f.id());
+    mIdRowMap.insert(f.id(), i);
+  }
+
+  // not needed when we have featureAdded signal
+  mFeatureCount = mLayer->pendingFeatureCount();
+  mFieldCount = mLayer->dataProvider()->fieldCount();
+
+  if (ins)
+  {
+    endInsertRows();
+    std::cout << "end ins\n";
+  }
+  else if (rm)
+  {
+    endRemoveRows();
+    std::cout << "end rm\n";
+  }
+
+/*  std::cout << "id->row";
+  QMap<int, int>::iterator it;
+  for (it = mIdRowMap.begin(); it != mIdRowMap.end(); ++it)
+	  std::cout << it.key() << "->" << *it << "\n";
+
+  std::cout << "row->id";
+
+  for (it = mRowIdMap.begin(); it != mRowIdMap.end(); ++it)
+	  std::cout << it.key() << "->" << *it << "\n";*/
+
+
+}
+
+void BeataModel::swapRows(int a, int b)
+{
+  if (a == b)
+    return;
+
+  int rowA = idToRow(a);
+  int rowB = idToRow(b);
+
+  //emit layoutAboutToBeChanged();
+
+  mRowIdMap.remove(rowA);
+  mRowIdMap.remove(rowB);
+  mRowIdMap.insert(rowA, b); 
+  mRowIdMap.insert(rowB, a); 
+
+  mIdRowMap.remove(a);
+  mIdRowMap.remove(b);
+  mIdRowMap.insert(a, rowB); 
+  mIdRowMap.insert(b, rowA); 
+
+  //emit layoutChanged();
+}
+
+int BeataModel::idToRow(const int id) const
+{
+  if (!mIdRowMap.contains(id))
+  {
+    std::cout << "idToRow: id " << id << " not in map\n";
+    return -1;
+  }
+
+  return mIdRowMap[id];
+}
+
+int BeataModel::rowToId(const int id) const
+{
+  if (!mRowIdMap.contains(id))
+  {
+    std::cout << "rowToId: row " << id << " not in map\n";
+    return -1;
+  }
+
+  return mRowIdMap[id];
+}
+
+int BeataModel::rowCount(const QModelIndex &parent) const
+{
+  return mFeatureCount;
+}
+
+int BeataModel::columnCount(const QModelIndex &parent) const
+{
+  return mFieldCount;
+}
+
+QVariant BeataModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+  if (role == Qt::DisplayRole)
+  {
+    if (orientation == Qt::Vertical) //row
+    {
+      return QVariant(section);
+    }
+    else
+    {
+      QgsField field = mLayer->dataProvider()->fields()[section]; //column
+      return QVariant(field.name());
+    }
+  }
+  else return QVariant();
+}
+
+void BeataModel::sort(int column, Qt::SortOrder order)
+{
+  QgsAttributeMap row;
+  idColumnPair pair;
+  QgsAttributeList attrs;
+  QgsFeature f;
+
+  attrs.append(column);
+
+  emit layoutAboutToBeChanged();
+  //std::cout << "SORTing\n";
+
+  mSortList.clear();
+  mLayer->select(attrs, QgsRectangle(), false);
+  while (mLayer->nextFeature(f))
+  {
+    row = f.attributeMap();
+
+    pair.id = f.id();
+    pair.columnItem = row[column];
+
+    mSortList.append(pair);
+  }
+
+  if (order == Qt::AscendingOrder)
+    qStableSort(mSortList.begin(), mSortList.end());
+  else
+    qStableSort(mSortList.begin(), mSortList.end(), qGreater<idColumnPair>());
+
+  // recalculate id<->row maps
+  mRowIdMap.clear();
+  mIdRowMap.clear();
+
+  int i = 0;
+  QList<idColumnPair>::Iterator it;
+  for (it = mSortList.begin(); it != mSortList.end(); ++it, ++i)
+  {
+    mRowIdMap.insert(i, it->id);
+    mIdRowMap.insert(it->id, i);
+  }
+
+  // restore selection
+  emit layoutChanged();
+  //reset();
+  emit modelChanged();
+}
+
+QVariant BeataModel::data(const QModelIndex &index, int role) const
+{
+  if (!index.isValid() || (role != Qt::TextAlignmentRole && role != Qt::DisplayRole && role != Qt::EditRole) )
+    return QVariant();
+
+  QVariant::Type fldType = mLayer->dataProvider()->fields()[index.column()].type();
+  bool fldNumeric = (fldType == QVariant::Int || fldType == QVariant::Double);
+  
+  if (role == Qt::TextAlignmentRole)
+  {
+    if (fldNumeric)
+      return QVariant(Qt::AlignRight);
+    else
+      return QVariant(Qt::AlignLeft);
+  }
+  
+  // if we don't have the row in current cache, load it from layer first
+  if (mLastRowId != rowToId(index.row()))
+  {
+    bool res = mLayer->featureAtId(rowToId(index.row()), mFeat, false, true);
+
+    if (!res)
+      return QVariant("ERROR");
+    
+    mLastRowId = rowToId(index.row());
+    mLastRow = (QgsAttributeMap *)(&(mFeat.attributeMap()));
+  }
+    
+  QVariant& val = (*mLastRow)[index.column()];
+  
+  if (val.isNull())
+  {
+    // if the value is NULL, show that in table, but don't show "NULL" text in editor
+    if (role == Qt::EditRole)
+      return QVariant();
+    else
+      return QVariant("NULL");
+  }
+  
+  // force also numeric data for EditRole to be strings
+  // otherwise it creates spinboxes instead of line edits
+  // (probably not what we do want)
+  if (fldNumeric && role == Qt::EditRole)
+    return val.toString();
+  
+  // convert to QString from some other representation
+  // this prevents displaying greater numbers in exponential format
+  return val.toString();
+}
+
+bool BeataModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+  if (!index.isValid() || role != Qt::EditRole)
+    return false;
+
+  if (!mLayer->isEditable())
+    return false;
+
+  bool res = mLayer->featureAtId(rowToId(index.row()), mFeat, false, true);
+
+  if (res) {
+    mLastRowId = rowToId(index.row());
+    mLastRow = (QgsAttributeMap *)(&(mFeat.attributeMap()));
+
+    mLayer->changeAttributeValue(rowToId(index.row()), index.column(), value, true);
+  } 
+
+  if (!mLayer->isModified())
+    return false;
+
+  emit dataChanged(index, index);
+  return true;
+}
+
+Qt::ItemFlags BeataModel::flags(const QModelIndex &index) const
+{
+  if (!index.isValid())
+    return Qt::ItemIsEnabled;
+
+  Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+  
+  if (mLayer->isEditable())
+    flags |= Qt::ItemIsEditable;
+  
+  return flags;
+}
+
+void BeataModel::reload(const QModelIndex &index1, const QModelIndex &index2)
+{
+  emit dataChanged(index1, index2);
+}
+
+void BeataModel::resetModel()
+{
+  reset();
+}
+
+void BeataModel::changeLayout()
+{
+  emit layoutChanged();
+}
+
+void BeataModel::incomingChangeLayout()
+{
+  emit layoutAboutToBeChanged();
+}
+
+/////////////////////
+// In-Memory model //
+/////////////////////
+
+void BeataMemModel::loadLayer()
+{
+  BeataModel::loadLayer();
+  mLayer->select(mLayer->pendingAllAttributesList(), QgsRectangle(), false);
+
+  QgsFeature f;
+  while (mLayer->nextFeature(f))
+    mFeatureMap.insert(f.id(), f);
+}
+
+BeataMemModel::BeataMemModel
+(QgsVectorLayer *theLayer)
+: BeataModel(theLayer)
+{
+  loadLayer();
+}
+
+QVariant BeataMemModel::data(const QModelIndex &index, int role) const
+{
+  if (!index.isValid() || (role != Qt::TextAlignmentRole && role != Qt::DisplayRole && role != Qt::EditRole))
+    return QVariant();
+
+  QVariant::Type fldType = mLayer->dataProvider()->fields()[index.column()].type();
+  bool fldNumeric = (fldType == QVariant::Int || fldType == QVariant::Double);
+  
+  if (role == Qt::TextAlignmentRole)
+  {
+    if (fldNumeric)
+      return QVariant(Qt::AlignRight);
+    else
+      return QVariant(Qt::AlignLeft);
+  }
+  
+  // if we don't have the row in current cache, load it from layer first
+  if (mLastRowId != rowToId(index.row()))
+  {
+    //bool res = mLayer->featureAtId(rowToId(index.row()), mFeat, false, true);
+    bool res = mFeatureMap.contains(rowToId(index.row()));
+
+    if (!res)
+      return QVariant("ERROR");
+    
+    mLastRowId = rowToId(index.row());
+    mFeat = mFeatureMap[rowToId(index.row())];
+    mLastRow = (QgsAttributeMap *)(&(mFeat.attributeMap()));
+  }
+    
+  QVariant& val = (*mLastRow)[index.column()];
+
+  if (val.isNull())
+  {
+    // if the value is NULL, show that in table, but don't show "NULL" text in editor
+    if (role == Qt::EditRole)
+      return QVariant();
+    else
+      return QVariant("NULL");
+  }
+  
+  // force also numeric data for EditRole to be strings
+  // otherwise it creates spinboxes instead of line edits
+  // (probably not what we do want)
+  if (fldNumeric && role == Qt::EditRole)
+    return val.toString();
+  
+  // convert to QString from some other representation
+  // this prevents displaying greater numbers in exponential format
+  return val.toString();
+}
+
+bool BeataMemModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+  if (!index.isValid() || role != Qt::EditRole)
+    return false;
+
+  if (!mLayer->isEditable())
+    return false;
+
+  //bool res = mLayer->featureAtId(rowToId(index.row()), mFeat, false, true);
+  bool res = mFeatureMap.contains(rowToId(index.row()));
+
+  if (res) {
+    mLastRowId = rowToId(index.row());
+    mFeat = mFeatureMap[rowToId(index.row())];
+    mLastRow = (QgsAttributeMap *)(&(mFeat.attributeMap()));
+
+
+    //std::cout << mFeatureMap[rowToId(index.row())].id();
+    mFeatureMap[rowToId(index.row())].changeAttribute(index.column(), value);
+    // propagate back to the layer
+    mLayer->changeAttributeValue(rowToId(index.row()), index.column(), value, true);
+  }
+
+  if (!mLayer->isModified())
+    return false;
+
+  emit dataChanged(index, index);
+  return true;
+}
+
+void BeataMemModel::featureDeleted(int fid)
+{
+std::cout << "BMM feature deleted\n";
+  mFeatureMap.remove(fid);
+  BeataModel::featureDeleted(fid);
+}
+ 
+void BeataMemModel::featureAdded(int fid)
+{
+  std::cout << "BMM feature added\n";
+  QgsFeature f;
+  mLayer->featureAtId(fid, f, false, true);
+  mFeatureMap.insert(fid, f);
+  BeataModel::featureAdded(fid);
+}
+ 
+/*void BeataMemModel::attributeAdded (int idx)
+{
+	std::cout << "attribute added\n";
+  loadLayer();
+  reload(index(0,0), index(rowCount(), columnCount()));
+}
+ 
+void BeataMemModel::attributeDeleted (int idx)
+{
+	std::cout << "attribute deleted\n";
+  loadLayer();
+  reload(index(0,0), index(rowCount(), columnCount()));
+}
+ */
+void BeataMemModel::layerDeleted ()
+{
+  std::cout << "BMM layer del\n";
+  mFeatureMap.clear();
+  BeataModel::layerDeleted();
+}
+ 
+void BeataMemModel::attributeValueChanged (int fid, int idx, const QVariant &value)
+{
+  std::cout << "BMM attribute changed\n";
+  mFeatureMap[fid].changeAttribute(idx, value);
+  reload(index(0,0), index(rowCount(), columnCount()));
+}

Added: trunk/qgis/src/app/attributetable/BeataModel.h
===================================================================
--- trunk/qgis/src/app/attributetable/BeataModel.h	                        (rev 0)
+++ trunk/qgis/src/app/attributetable/BeataModel.h	2009-03-20 18:24:19 UTC (rev 10337)
@@ -0,0 +1,141 @@
+/***************************************************************************
+  BeataModel.h 
+  BEtter Attribute TAble
+  -------------------
+         date                 : Feb 2009
+         copyright            : Vita Cizek
+         email                : weetya (at) gmail.com
+
+ ***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef BEATAMODEL_H
+#define BEATAMODEL_H
+
+#include <QAbstractTableModel>
+#include <QSortFilterProxyModel>
+#include <QModelIndex>
+#include <QObject>
+
+//QGIS Includes
+#include "qgis.h"
+#include "qgsfeature.h" //QgsAttributeMap
+#include "qgsvectorlayer.h" //QgsAttributeList
+
+class idColumnPair
+{
+public:
+  int id;
+  QVariant columnItem;
+
+  bool operator<(const idColumnPair &b) const;
+};
+
+class BeataFilterModel: public QSortFilterProxyModel
+{
+public:
+  BeataFilterModel(QgsVectorLayer* theLayer);
+  //QModelIndex mapToSource ( const QModelIndex & filterIndex ) const;
+  //QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const;
+  bool mHideUnselected;
+  virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+protected:
+  bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+private:
+  QgsVectorLayer* mLayer;
+};
+
+
+class BeataModel: public QAbstractTableModel
+{
+Q_OBJECT
+
+public:
+  BeataModel(QgsVectorLayer *theLayer, QObject *parent = 0);
+
+  int rowCount(const QModelIndex &parent = QModelIndex()) const;
+  int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+  virtual QVariant data(const QModelIndex &index, int role) const;
+  virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+  Qt::ItemFlags flags(const QModelIndex &index) const;
+
+  void reload(const QModelIndex &index1, const QModelIndex &index2);
+  void resetModel();
+  void changeLayout();
+  void incomingChangeLayout();
+  int idToRow(const int id) const;
+  int rowToId(const int id) const;
+  virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+  void swapRows(int a, int b);
+  
+  QgsVectorLayer* layer() const { return mLayer; }
+
+signals:
+  void modelChanged();
+  void setNumRows(int oldNum, int newNum);
+
+private slots:
+  virtual void attributeAdded (int idx);
+  virtual void attributeDeleted (int idx);
+  virtual void attributeValueChanged (int fid, int idx, const QVariant &value);
+  virtual void layerModified(bool onlyGeometry);
+
+protected slots:
+  virtual void featureDeleted(int fid);
+  virtual void featureAdded(int fid);
+  virtual void layerDeleted ();
+
+protected:
+  QgsVectorLayer *mLayer;
+  int mFeatureCount;
+  int mFieldCount;
+  mutable int mLastRowId;
+  mutable QgsFeature mFeat;
+
+  mutable QgsAttributeMap *mLastRow;
+  QgsAttributeList mAttributes;
+
+  QList<idColumnPair> mSortList;
+  QMap<int, int> mIdRowMap;
+  QMap<int, int> mRowIdMap;
+
+  void initIdMaps();
+  virtual void loadLayer();
+
+};
+
+class BeataMemModel: public BeataModel
+{
+Q_OBJECT
+
+public:
+  BeataMemModel(QgsVectorLayer *theLayer);//, QObject *parent = 0);
+
+protected slots:
+  virtual void featureDeleted(int fid);
+  virtual void featureAdded(int fid);
+  virtual void layerDeleted ();
+
+private slots:
+  //virtual void attributeAdded (int idx);
+  //virtual void attributeDeleted (int idx);
+  virtual void attributeValueChanged (int fid, int idx, const QVariant &value);
+  //virtual void layerModified(bool onlyGeometry);
+
+private:
+  virtual QVariant data(const QModelIndex &index, int role) const;
+  virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
+  virtual void loadLayer();
+
+  QMap<int, QgsFeature> mFeatureMap;
+};
+
+#endif

Added: trunk/qgis/src/app/attributetable/BeataView.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/BeataView.cpp	                        (rev 0)
+++ trunk/qgis/src/app/attributetable/BeataView.cpp	2009-03-20 18:24:19 UTC (rev 10337)
@@ -0,0 +1,144 @@
+/***************************************************************************
+     BeataView.cpp
+     --------------------------------------
+    Date                 : Feb 2009 
+    Copyright            : (C) 2009 Vita Cizek
+    Email                : weetya (at) gmail.com
+ ***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <QModelIndex>
+#include <QItemDelegate>
+#include <QHeaderView>
+#include <QSettings>
+#include <QLineEdit>
+#include <QPainter>
+#include <QKeyEvent>
+
+#include "BeataView.h"
+#include "BeataModel.h"
+
+#include "qgslogger.h"
+#include "qgsvectorlayer.h"
+#include "qgsvectordataprovider.h"
+
+
+class BeataDelegate : public QItemDelegate
+{
+public:
+  BeataDelegate(QObject* parent = NULL) : QItemDelegate(parent) {}
+
+  QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
+  {
+    QWidget *editor = QItemDelegate::createEditor( parent, option, index );
+    
+    QLineEdit *le = dynamic_cast<QLineEdit*>( editor );
+    if ( !le ) return editor;
+    
+    const BeataModel* m = dynamic_cast<const BeataModel*>(index.model());
+    if ( !m ) return editor;
+    
+    int col = index.column();
+    QVariant::Type type = m->layer()->dataProvider()->fields()[col].type();
+
+    if ( type == QVariant::Int )
+    {
+      le->setValidator( new QIntValidator( le ) );
+    }
+    else if ( type == QVariant::Double )
+    {
+      le->setValidator( new QDoubleValidator( le ) );
+    }
+
+    return editor;
+  }
+  
+  
+  void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
+  {
+    QItemDelegate::paint(painter, option, index);
+    
+    if (option.state & QStyle::State_HasFocus)
+    {  
+      QRect r = option.rect.adjusted(1,1,-1,-1);
+      QPen p(QBrush(QColor(0,255,127)), 2);
+      painter->save();
+      painter->setPen(p);
+      painter->drawRect(r);
+      painter->restore();
+    }
+  }
+
+};
+
+BeataView::BeataView(QWidget* parent)
+ : QTableView(parent)
+{
+  QSettings settings; 
+  restoreGeometry(settings.value("/BetterTable/geometry").toByteArray());
+  
+  verticalHeader()->setDefaultSectionSize(20);
+  horizontalHeader()->setHighlightSections(false);
+  
+  setItemDelegate(new BeataDelegate(this));
+	
+  setSelectionBehavior(QAbstractItemView::SelectRows);
+  setSelectionMode(QAbstractItemView::NoSelection);
+  setSortingEnabled(true);
+
+  shiftPressed = false;
+  ctrlPressed = false;
+}
+
+void BeataView::setLayer(QgsVectorLayer* layer)
+{
+  BeataModel *bModel;
+ 
+  if (layer->dataProvider()->capabilities() & QgsVectorDataProvider::RandomSelectGeometryAtId)
+    bModel = new BeataModel(layer);
+  else
+    bModel = new BeataMemModel(layer);
+
+  BeataFilterModel* bfModel = new BeataFilterModel(layer);
+  bfModel->setSourceModel(bModel);
+
+  setModel(bfModel);
+}
+
+BeataView::~BeataView()
+{
+}
+
+void BeataView::closeEvent(QCloseEvent *event)
+{
+  QSettings settings;
+  settings.setValue("/BetterAttributeTable/geometry", QVariant(saveGeometry()));
+}
+
+void BeataView::keyPressEvent(QKeyEvent *event)
+{
+  // shift pressed
+  if (event->key() == Qt::Key_Shift)// && event->modifiers() & Qt::ShiftModifier)
+    shiftPressed = true;
+  else if (event->key() == Qt::Key_Control)
+    ctrlPressed = true;
+  else
+    QTableView::keyPressEvent(event);
+}
+
+void BeataView::keyReleaseEvent(QKeyEvent *event)
+{
+  // workaround for some Qt bug
+  if (event->key() == Qt::Key_Shift || event->key() == 0xffffffff)
+      shiftPressed = false;
+  else if (event->key() == Qt::Key_Control)
+    ctrlPressed = false;
+  else
+    QTableView::keyReleaseEvent(event);
+}

Added: trunk/qgis/src/app/attributetable/BeataView.h
===================================================================
--- trunk/qgis/src/app/attributetable/BeataView.h	                        (rev 0)
+++ trunk/qgis/src/app/attributetable/BeataView.h	2009-03-20 18:24:19 UTC (rev 10337)
@@ -0,0 +1,44 @@
+/***************************************************************************
+     BeataView.h
+     --------------------------------------
+    Date                 : Feb 2009 
+    Copyright            : (C) 2009 Vita Cizek
+    Email                : weetya (at) gmail.com
+ ***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef BEATAVIEW_H_
+#define BEATAVIEW_H_
+
+#include <QTableView>
+
+class QgsVectorLayer;
+
+
+class BeataView: public QTableView
+{
+//private slots:
+  //void setRows(int rows);
+
+public:
+  BeataView(QWidget* parent = NULL);
+  virtual ~BeataView();
+
+  void setLayer(QgsVectorLayer* layer);
+
+  void closeEvent(QCloseEvent *event);
+  void keyPressEvent(QKeyEvent *event);
+  void keyReleaseEvent(QKeyEvent *event);
+
+  //make those private
+  bool shiftPressed;
+  bool ctrlPressed;
+};
+
+#endif

Modified: trunk/qgis/src/app/legend/qgslegend.cpp
===================================================================
--- trunk/qgis/src/app/legend/qgslegend.cpp	2009-03-20 17:35:32 UTC (rev 10336)
+++ trunk/qgis/src/app/legend/qgslegend.cpp	2009-03-20 18:24:19 UTC (rev 10337)
@@ -39,6 +39,8 @@
 #include "qgsvectorlayerproperties.h"
 #include "qgsattributetabledisplay.h"
 
+#include "BeataDialog.h"
+
 #include <cfloat>
 #include <iostream>
 
@@ -1859,7 +1861,9 @@
 
   if ( vlayer )
   {
-    QgsAttributeTableDisplay::attributeTable( vlayer );
+    BeataDialog *mDialog = new BeataDialog(vlayer);
+    mDialog->show();
+    // the dialog will be deleted by itself on close
   }
   else
   {

Modified: trunk/qgis/src/app/legend/qgslegendlayerfile.cpp
===================================================================
--- trunk/qgis/src/app/legend/qgslegendlayerfile.cpp	2009-03-20 17:35:32 UTC (rev 10336)
+++ trunk/qgis/src/app/legend/qgslegendlayerfile.cpp	2009-03-20 18:24:19 UTC (rev 10337)
@@ -35,6 +35,9 @@
 #include "qgsattributetable.h"
 #include "qgsattributetabledisplay.h"
 
+#include "BeataDialog.h"
+
+
 #include "qgsencodingfiledialog.h"
 
 #include <QApplication>
@@ -217,7 +220,10 @@
 
 void QgsLegendLayerFile::table()
 {
-  QgsAttributeTableDisplay::attributeTable( dynamic_cast<QgsVectorLayer*>( mLyr.layer() ) );
+  QgsVectorLayer * myLayer = dynamic_cast<QgsVectorLayer *>(mLyr.layer());
+  BeataDialog *mDialog = new BeataDialog(myLayer);
+  mDialog->show();
+  // the dialog will be deleted by itself on close
 }
 
 void QgsLegendLayerFile::saveSelectionAsShapefile()

Modified: trunk/qgis/src/app/qgisapp.cpp
===================================================================
--- trunk/qgis/src/app/qgisapp.cpp	2009-03-20 17:35:32 UTC (rev 10336)
+++ trunk/qgis/src/app/qgisapp.cpp	2009-03-20 18:24:19 UTC (rev 10337)
@@ -133,6 +133,7 @@
 #include "qgsvectordataprovider.h"
 #include "qgsvectorlayer.h"
 #include "ogr/qgsopenvectorlayerdialog.h"
+#include "BeataDialog.h"
 //
 // Gdal/Ogr includes
 //
@@ -3713,7 +3714,10 @@
     return;
   }
 
-  QgsAttributeTableDisplay::attributeTable( dynamic_cast<QgsVectorLayer *>( mMapLegend->currentLayer() ) );
+  QgsVectorLayer * myLayer = dynamic_cast<QgsVectorLayer *>(mMapLegend->currentLayer());
+  BeataDialog *mDialog = new BeataDialog(myLayer);
+  mDialog->show();
+  // the dialog will be deleted by itself on close
 }
 
 void QgisApp::saveAsShapefile()



More information about the QGIS-commit mailing list