/* This file is part of the KDE project Copyright (C) 2006 Sebastian Sauer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "keximacrodesignview.h" #include "keximacroproperty.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../lib/macro.h" #include "../lib/macroitem.h" #include "../lib/xmlhandler.h" /// constants used to name columns instead of hardcoding indices #define COLUMN_ID_ACTION 0 #define COLUMN_ID_COMMENT 1 /** * \internal d-pointer class to be more flexible on future extension of the * functionality without to much risk to break the binary compatibility. */ class KexiMacroDesignView::Private { public: /** * The view used to display the actions * a \a Macro has. */ KexiDataTable* datatable; /** * For convenience. The table view ( datatable->tableView() ). */ KexiTableView* tableview; /** * The \a KexiTableViewData data-model for the * \a KexiTableView above. */ KexiTableViewData* tabledata; /** * The \a KexiDataAwarePropertySet is used to display * properties an action provides in the propertyview. */ KexiDataAwarePropertySet* propertyset; /// Boolean flag to avoid infinite recursion. bool reloadsProperties; /// Boolean flag to avoid infinite recursion. bool updatesProperties; /** * Constructor. * * \param m The passed \a KoMacro::Manager instance our * \a manager points to. */ Private() : propertyset(0) , reloadsProperties(false) , updatesProperties(false) { } /** * Destructor. */ ~Private() { delete propertyset; } }; KexiMacroDesignView::KexiMacroDesignView(KexiMainWindow *mainwin, TQWidget *tqparent, ::KoMacro::Macro* const macro) : KexiMacroView(mainwin, tqparent, macro, "KexiMacroDesignView") , d( new Private() ) { // The table's data-model. d->tabledata = new KexiTableViewData(); d->tabledata->setSorting(-1); // disable sorting // Add the "Action" column. KexiTableViewColumn* actioncol = new KexiTableViewColumn( "action", // name/identifier KexiDB::Field::Enum, // fieldtype KexiDB::Field::NoConstraints, // constraints KexiDB::Field::NoOptions, // options 0, // length 0, // precision TQVariant(), // default value i18n("Action"), // caption TQString(), // description 0 // width ); d->tabledata->addColumn(actioncol); TQValueVector items; items.append(""); // empty means no action // Append the list of actions provided by Kexi. TQStringList actionnames = KoMacro::Manager::self()->actionNames(); TQStringList::ConstIterator it, end( actionnames.constEnd() ); for( it = actionnames.constBegin(); it != end; ++it) { KSharedPtr action = KoMacro::Manager::self()->action(*it); items.append( action->text() ); } actioncol->field()->setEnumHints(items); // Add the "Comment" column. d->tabledata->addColumn( new KexiTableViewColumn( "comment", // name/identifier KexiDB::Field::Text, // fieldtype KexiDB::Field::NoConstraints, // constraints KexiDB::Field::NoOptions, // options 0, // length 0, // precision TQVariant(), // default value i18n("Comment"), // caption TQString(), // description 0 // width ) ); // Create the tableview. TQHBoxLayout* tqlayout = new TQHBoxLayout(this); d->datatable = new KexiDataTable(mainWin(), this, "Macro KexiDataTable", false /*not db aware*/); tqlayout->addWidget(d->datatable); d->tableview = d->datatable->tableView(); d->tableview->setSpreadSheetMode(); d->tableview->setColumnStretchEnabled( true, COLUMN_ID_COMMENT ); //last column occupies the rest of the area // We need to register our KexiMacroPropertyFactory to use our own // KoProperty::Property implementation. KexiMacroPropertyFactory::initFactory(); // Create the propertyset. d->propertyset = new KexiDataAwarePropertySet(this, d->tableview); // Connect signals the KexiDataTable provides to local slots. connect(d->tabledata, TQT_SIGNAL(aboutToChangeCell(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)), this, TQT_SLOT(beforeCellChanged(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*))); connect(d->tabledata, TQT_SIGNAL(rowUpdated(KexiTableItem*)), this, TQT_SLOT(rowUpdated(KexiTableItem*))); connect(d->tabledata, TQT_SIGNAL(rowInserted(KexiTableItem*,uint,bool)), this, TQT_SLOT(rowInserted(KexiTableItem*,uint,bool))); connect(d->tabledata, TQT_SIGNAL(rowDeleted()), this, TQT_SLOT(rowDeleted())); // Everything is ready. So, update the data now. updateData(); setDirty(false); } KexiMacroDesignView::~KexiMacroDesignView() { delete d; } void KexiMacroDesignView::updateData() { kdDebug() << "KexiMacroDesignView::updateData()" << endl; // Remove previous content of tabledata. d->tabledata->deleteAllRows(); // Remove old property sets. d->propertyset->clear(); // Add some empty rows for (int i=0; i<50; i++) { d->tabledata->append( d->tabledata->createItem() ); } // Set the MacroItem's TQStringList actionnames = KoMacro::Manager::self()->actionNames(); KoMacro::MacroItem::List macroitems = macro()->items(); KoMacro::MacroItem::List::ConstIterator it(macroitems.constBegin()), end(macroitems.constEnd()); for(uint idx = 0; it != end; ++it, idx++) { KexiTableItem* tableitem = d->tabledata->at(idx); if(! tableitem) { // If there exists no such item, add it. tableitem = d->tabledata->createItem(); d->tabledata->append(tableitem); } // Set the action-column. KSharedPtr action = (*it)->action(); if(action.data()) { int i = actionnames.findIndex( action->name() ); if(i >= 0) { tableitem->at(COLUMN_ID_ACTION) = i + 1; //setAction(tableitem, action->name()); } } // Set the comment-column. tableitem->at(COLUMN_ID_COMMENT) = (*it)->comment(); } // set data for our spreadsheet: this will clear our sets d->tableview->setData(d->tabledata); // Add the property sets. it = macroitems.constBegin(); for(uint idx = 0; it != end; ++it, idx++) { updateProperties(idx, 0, *it); } // work around a bug in the KexiTableView where we lose the stretch-setting... d->tableview->setColumnStretchEnabled( true, COLUMN_ID_COMMENT ); //last column occupies the rest of the area propertySetReloaded(true); } bool KexiMacroDesignView::loadData() { if(! KexiMacroView::loadData()) { return false; } updateData(); // update the tableview's data. return true; } KoProperty::Set* KexiMacroDesignView::propertySet() { return d->propertyset->currentPropertySet(); } void KexiMacroDesignView::beforeCellChanged(KexiTableItem* item, int colnum, TQVariant& newvalue, KexiDB::ResultInfo* result) { Q_UNUSED(result); kdDebug() << "KexiMacroDesignView::beforeCellChanged() colnum=" << colnum << " newvalue=" << newvalue.toString() << endl; int rowindex = d->tabledata->findRef(item); if(rowindex < 0) { kdWarning() << "KexiMacroDesignView::beforeCellChanged() No such item" << endl; return; } // If the rowindex doesn't exists yet, we need to append new // items till we are able to access the item we like to use. for(int i = macro()->items().count(); i <= rowindex; i++) { macro()->addItem( KSharedPtr( new KoMacro::MacroItem() ) ); } // Get the matching MacroItem. KSharedPtr macroitem = macro()->items()[rowindex]; if(! macroitem.data()) { kdWarning() << "KexiMacroDesignView::beforeCellChanged() Invalid item for rowindex=" << rowindex << endl; return; } // Handle the column that should be changed switch(colnum) { case COLUMN_ID_ACTION: { // The "Action" column TQString actionname; bool ok; int selectedindex = newvalue.toInt(&ok); if(ok && selectedindex > 0) { TQStringList actionnames = KoMacro::Manager::self()->actionNames(); actionname = actionnames[ selectedindex - 1 ]; // first item is empty } KSharedPtr action = KoMacro::Manager::self()->action(actionname); macroitem->setAction(action); updateProperties(d->propertyset->currentRow(), d->propertyset->currentPropertySet(), macroitem); propertySetReloaded(true); } break; case COLUMN_ID_COMMENT: { // The "Comment" column macroitem->setComment( newvalue.toString() ); } break; default: kdWarning() << "KexiMacroDesignView::beforeCellChanged() No such column number " << colnum << endl; return; } setDirty(); } void KexiMacroDesignView::rowUpdated(KexiTableItem* item) { int rowindex = d->tabledata->findRef(item); kdDebug() << "KexiMacroDesignView::rowUpdated() rowindex=" << rowindex << endl; //propertySetSwitched(); //propertySetReloaded(true); //setDirty(); } void KexiMacroDesignView::rowInserted(KexiTableItem*, uint row, bool) { kdDebug() << "KexiMacroDesignView::rowInserted() rowindex=" << row << endl; KoMacro::MacroItem::List& macroitems = macro()->items(); if(row < macroitems.count()) { // If a new item was inserted, we need to insert a new item to our // list of MacroItems too. If the new item was appended, we don't // need to do anything yet cause the new item will be handled on // beforeCellChanged() anyway. kdDebug() << "KexiMacroDesignView::rowInserted() Inserting new MacroItem" << endl; KSharedPtr macroitem = KSharedPtr( new KoMacro::MacroItem() ); KoMacro::MacroItem::List::Iterator it = macroitems.at(row); macroitems.insert(it, macroitem); } } void KexiMacroDesignView::rowDeleted() { int rowindex = d->propertyset->currentRow(); if(rowindex < 0) { kdWarning() << "KexiMacroDesignView::rowDeleted() No such item" << endl; return; } kdDebug() << "KexiMacroDesignView::rowDeleted() rowindex=" << rowindex << endl; KoMacro::MacroItem::List& macroitems = macro()->items(); macroitems.remove( macroitems.at(rowindex) ); } bool KexiMacroDesignView::updateSet(KoProperty::Set* set, KSharedPtr macroitem, const TQString& variablename) { kdDebug() << "KexiMacroDesignView::updateSet() variablename=" << variablename << endl; KoProperty::Property* property = KexiMacroProperty::createProperty(macroitem, variablename); if(! property) return false; set->addProperty(property); return true; } void KexiMacroDesignView::updateProperties(int row, KoProperty::Set* set, KSharedPtr macroitem) { kdDebug() << "KexiMacroDesignView::updateProperties() row=" << row << endl; if(row < 0 || d->updatesProperties) { return; // ignore invalid rows and avoid infinite recursion. } KSharedPtr action = macroitem->action(); if(! action.data()) { // don't display a propertyset if there is no action defined. d->propertyset->remove(row); return; // job done. } d->updatesProperties = true; if(set) { // we need to clear old data before adding the new content. set->clear(); } else { // if there exists no such propertyset yet, create one. set = new KoProperty::Set(d->propertyset, action->name()); d->propertyset->insert(row, set, true); connect(set, TQT_SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)), this, TQT_SLOT(propertyChanged(KoProperty::Set&, KoProperty::Property&))); } // The caption. KoProperty::Property* prop = new KoProperty::Property("this:classString", action->text()); prop->setVisible(false); set->addProperty(prop); // Display the list of variables. TQStringList varnames = action->variableNames(); for(TQStringList::Iterator it = varnames.begin(); it != varnames.end(); ++it) { if(updateSet(set, macroitem, *it)) { KSharedPtr variable = macroitem->variable(*it, true); kdDebug()<<"KexiMacroDesignView::updateProperties() name=" << *it << " variable=" << variable->variant().toString() << endl; #if 0 macroitem->setVariable(*it, variable); #endif } } d->updatesProperties = false; } void KexiMacroDesignView::propertyChanged(KoProperty::Set& set, KoProperty::Property& property) { Q_UNUSED(set); kdDebug() << "!!!!! KexiMacroDesignView::propertyChanged() propertyname=" << property.name() << endl; setDirty(); /* if(d->reloadsProperties) // be sure to don't update properties if we are still on reloading. return; d->reloadsProperties = true; const int row = d->propertyset->currentRow(); const TQCString name = property.name(); kdDebug() << "KexiMacroDesignView::propertyChanged() name=" << name << " row=" << row << endl; //TODO reload is only needed if something changed! bool dirty = true; bool reload = true;//dirtyvarnames.count()>0; if(dirty || reload) { // Only reload properties if it's really needed. setDirty(); if(reload) { // The MacroItem which should be changed. KSharedPtr macroitem = macro()->items()[row]; // Update the properties. updateProperties(row, &set, macroitem); } // It's needed to call the reload delayed cause in KoProperty::Editor // TQTimer::singleShot(10, this, TQT_SLOT(selectItemLater())); may lead // to crashes if we are to fast. TQTimer::singleShot(50, this, TQT_SLOT(reloadPropertyLater())); } d->reloadsProperties = false; */ /* TQStringList dirtyvarnames = macroitem->setVariable(name, KSharedPtr(pv)); bool dirty = false; bool reload = false; for(TQStringList::Iterator it = dirtyvarnames.begin(); it != dirtyvarnames.end(); ++it) { KSharedPtr variable = macroitem->variable(*it); if(! variable.data()) { kdDebug() << "KexiMacroDesignView::propertyChanged() name=" << name << " it=" << *it << " skipped cause such a variable is not known." << endl; continue; } if(! set.contains( (*it).latin1() )) { // If there exist no such property yet, we need to add it. if(updateSet(&set, macroitem, *it)) reload = true; // we like to reload the whole set continue; } kdDebug() << "KexiMacroDesignView::propertyChanged() set existing property=" << *it << endl; KoProperty::Property& p = set.property((*it).latin1()); KoMacro::Variable::List tqchildren = variable->tqchildren(); if(tqchildren.count() > 0) { TQStringList keys, names; KoMacro::Variable::List::Iterator childit(tqchildren.begin()), childend(tqchildren.end()); for(; childit != childend; ++childit) { const TQString s = (*childit)->variant().toString(); keys << s; names << s; } p.setListData( new KoProperty::Property::ListData(keys, names) ); } p.setValue(variable->variant()); dirty = true; } // If there are expired aka not any longer needed properties around, we // need to reload the whole set. for(KoProperty::Set::Iterator setit = set; setit.current(); ++setit) { if(setit.currentKey() == name) continue; // don't remove ourself if(setit.currentKey().left(5) == TQCString("this:")) continue; // don't remove internal properties if(setit.currentKey() == TQCString("newrow")) continue; // also an internal used property if(action.data() && action->hasVariable(setit.currentKey())) continue; // the property is still valid reload = true; // we like to reload the whole set } */ } void KexiMacroDesignView::reloadPropertyLater() { propertySetReloaded(true); } #include "keximacrodesignview.moc"