kexi

keximacrodesignview.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012    You should have received a copy of the GNU Library General Public License
00013    along with this library; see the file COPYING.LIB.  If not, write to
00014    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00015    Boston, MA 02110-1301, USA.
00016 */
00017 
00018 #include "keximacrodesignview.h"
00019 #include "keximacroproperty.h"
00020 
00021 #include <qtimer.h>
00022 #include <qdom.h>
00023 #include <kdebug.h>
00024 
00025 #include <kexidialogbase.h>
00026 #include <kexidb/connection.h>
00027 #include <kexidb/error.h>
00028 
00029 #include <core/kexi.h>
00030 #include <core/kexiproject.h>
00031 #include <core/kexipartmanager.h>
00032 #include <core/kexipartinfo.h>
00033 
00034 #include <widget/kexidatatable.h>
00035 #include <widget/tableview/kexitableview.h>
00036 #include <widget/tableview/kexitableviewdata.h>
00037 #include <widget/tableview/kexitableitem.h>
00038 #include <widget/tableview/kexidataawarepropertyset.h>
00039 
00040 #include <koproperty/set.h>
00041 #include <koproperty/property.h>
00042 
00043 #include "../lib/macro.h"
00044 #include "../lib/macroitem.h"
00045 #include "../lib/xmlhandler.h"
00046 
00048 #define COLUMN_ID_ACTION 0
00049 #define COLUMN_ID_COMMENT 1
00050 
00055 class KexiMacroDesignView::Private
00056 {
00057     public:
00058 
00063         KexiDataTable* datatable;
00064 
00068         KexiTableView* tableview;
00069 
00074         KexiTableViewData* tabledata;
00075 
00080         KexiDataAwarePropertySet* propertyset;
00081 
00083         bool reloadsProperties;
00085         bool updatesProperties;
00086 
00093         Private()
00094             : propertyset(0)
00095             , reloadsProperties(false)
00096             , updatesProperties(false)
00097         {
00098         }
00099 
00103         ~Private()
00104         {
00105             delete propertyset;
00106         }
00107 
00108 };
00109 
00110 KexiMacroDesignView::KexiMacroDesignView(KexiMainWindow *mainwin, QWidget *parent, ::KoMacro::Macro* const macro)
00111     : KexiMacroView(mainwin, parent, macro, "KexiMacroDesignView")
00112     , d( new Private() )
00113 {
00114     // The table's data-model.
00115     d->tabledata = new KexiTableViewData();
00116     d->tabledata->setSorting(-1); // disable sorting
00117 
00118     // Add the "Action" column.
00119     KexiTableViewColumn* actioncol = new KexiTableViewColumn(
00120         "action", // name/identifier
00121         KexiDB::Field::Enum, // fieldtype
00122         KexiDB::Field::NoConstraints, // constraints
00123         KexiDB::Field::NoOptions, // options
00124         0, // length
00125         0, // precision
00126         QVariant(), // default value
00127         i18n("Action"), // caption
00128         QString::null, // description
00129         0 // width
00130     );
00131     d->tabledata->addColumn(actioncol);
00132 
00133     QValueVector<QString> items;
00134     items.append(""); // empty means no action
00135 
00136     // Append the list of actions provided by Kexi.
00137     QStringList actionnames = KoMacro::Manager::self()->actionNames();
00138     QStringList::ConstIterator it, end( actionnames.constEnd() );
00139     for( it = actionnames.constBegin(); it != end; ++it) {
00140         KSharedPtr<KoMacro::Action> action = KoMacro::Manager::self()->action(*it);
00141         items.append( action->text() );
00142     }
00143 
00144     actioncol->field()->setEnumHints(items);
00145 
00146     // Add the "Comment" column.
00147     d->tabledata->addColumn( new KexiTableViewColumn(
00148         "comment", // name/identifier
00149         KexiDB::Field::Text, // fieldtype
00150         KexiDB::Field::NoConstraints, // constraints
00151         KexiDB::Field::NoOptions, // options
00152         0, // length
00153         0, // precision
00154         QVariant(), // default value
00155         i18n("Comment"), // caption
00156         QString::null, // description
00157         0 // width
00158     ) );
00159 
00160     // Create the tableview.
00161     QHBoxLayout* layout = new QHBoxLayout(this);
00162     d->datatable = new KexiDataTable(mainWin(), this, "Macro KexiDataTable", false /*not db aware*/);
00163     layout->addWidget(d->datatable);
00164     d->tableview = d->datatable->tableView();
00165     d->tableview->setSpreadSheetMode();
00166     d->tableview->setColumnStretchEnabled( true, COLUMN_ID_COMMENT ); //last column occupies the rest of the area
00167 
00168     // We need to register our KexiMacroPropertyFactory to use our own
00169     // KoProperty::Property implementation.
00170     KexiMacroPropertyFactory::initFactory();
00171 
00172     // Create the propertyset.
00173     d->propertyset = new KexiDataAwarePropertySet(this, d->tableview);
00174 
00175     // Connect signals the KexiDataTable provides to local slots.
00176     connect(d->tabledata, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
00177             this, SLOT(beforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
00178     connect(d->tabledata, SIGNAL(rowUpdated(KexiTableItem*)),
00179             this, SLOT(rowUpdated(KexiTableItem*)));
00180     connect(d->tabledata, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
00181             this, SLOT(rowInserted(KexiTableItem*,uint,bool)));
00182     connect(d->tabledata, SIGNAL(rowDeleted()),
00183             this, SLOT(rowDeleted()));
00184 
00185     // Everything is ready. So, update the data now.
00186     updateData();
00187     setDirty(false);
00188 }
00189 
00190 KexiMacroDesignView::~KexiMacroDesignView()
00191 {
00192     delete d;
00193 }
00194 
00195 void KexiMacroDesignView::updateData()
00196 {
00197     kdDebug() << "KexiMacroDesignView::updateData()" << endl;
00198 
00199     // Remove previous content of tabledata.
00200     d->tabledata->deleteAllRows();
00201     // Remove old property sets.
00202     d->propertyset->clear();
00203 
00204     // Add some empty rows
00205     for (int i=0; i<50; i++) {
00206         d->tabledata->append( d->tabledata->createItem() );
00207     }
00208 
00209     // Set the MacroItem's
00210     QStringList actionnames = KoMacro::Manager::self()->actionNames();
00211     KoMacro::MacroItem::List macroitems = macro()->items();
00212     KoMacro::MacroItem::List::ConstIterator it(macroitems.constBegin()), end(macroitems.constEnd());
00213     for(uint idx = 0; it != end; ++it, idx++) {
00214         KexiTableItem* tableitem = d->tabledata->at(idx);
00215         if(! tableitem) {
00216             // If there exists no such item, add it.
00217             tableitem = d->tabledata->createItem();
00218             d->tabledata->append(tableitem);
00219         }
00220         // Set the action-column.
00221         KSharedPtr<KoMacro::Action> action = (*it)->action();
00222         if(action.data()) {
00223             int i = actionnames.findIndex( action->name() );
00224             if(i >= 0) {
00225                 tableitem->at(COLUMN_ID_ACTION) = i + 1;
00226                 //setAction(tableitem, action->name());
00227             }
00228         }
00229         // Set the comment-column.
00230         tableitem->at(COLUMN_ID_COMMENT) = (*it)->comment();
00231     }
00232 
00233     // set data for our spreadsheet: this will clear our sets
00234     d->tableview->setData(d->tabledata);
00235 
00236     // Add the property sets.
00237     it = macroitems.constBegin();
00238     for(uint idx = 0; it != end; ++it, idx++) {
00239         updateProperties(idx, 0, *it);
00240     }
00241 
00242     // work around a bug in the KexiTableView where we lose the stretch-setting...
00243     d->tableview->setColumnStretchEnabled( true, COLUMN_ID_COMMENT ); //last column occupies the rest of the area
00244 
00245     propertySetReloaded(true);
00246 }
00247 
00248 bool KexiMacroDesignView::loadData()
00249 {
00250     if(! KexiMacroView::loadData()) {
00251         return false;
00252     }
00253     updateData(); // update the tableview's data.
00254     return true;
00255 }
00256 
00257 KoProperty::Set* KexiMacroDesignView::propertySet()
00258 {
00259     return d->propertyset->currentPropertySet();
00260 }
00261 
00262 void KexiMacroDesignView::beforeCellChanged(KexiTableItem* item, int colnum, QVariant& newvalue, KexiDB::ResultInfo* result)
00263 {
00264     Q_UNUSED(result);
00265     kdDebug() << "KexiMacroDesignView::beforeCellChanged() colnum=" << colnum << " newvalue=" << newvalue.toString() << endl;
00266 
00267     int rowindex = d->tabledata->findRef(item);
00268     if(rowindex < 0) {
00269         kdWarning() << "KexiMacroDesignView::beforeCellChanged() No such item" << endl;
00270         return;
00271     }
00272 
00273     // If the rowindex doesn't exists yet, we need to append new
00274     // items till we are able to access the item we like to use.
00275     for(int i = macro()->items().count(); i <= rowindex; i++) {
00276         macro()->addItem( KSharedPtr<KoMacro::MacroItem>( new KoMacro::MacroItem() ) );
00277     }
00278 
00279     // Get the matching MacroItem.
00280     KSharedPtr<KoMacro::MacroItem> macroitem = macro()->items()[rowindex];
00281     if(! macroitem.data()) {
00282         kdWarning() << "KexiMacroDesignView::beforeCellChanged() Invalid item for rowindex=" << rowindex << endl;
00283         return;
00284     }
00285 
00286     // Handle the column that should be changed
00287     switch(colnum) {
00288         case COLUMN_ID_ACTION: { // The "Action" column
00289             QString actionname;
00290             bool ok;
00291             int selectedindex = newvalue.toInt(&ok);
00292             if(ok && selectedindex > 0) {
00293                 QStringList actionnames = KoMacro::Manager::self()->actionNames();
00294                 actionname = actionnames[ selectedindex - 1 ]; // first item is empty
00295             }
00296             KSharedPtr<KoMacro::Action> action = KoMacro::Manager::self()->action(actionname);
00297             macroitem->setAction(action);
00298             updateProperties(d->propertyset->currentRow(), d->propertyset->currentPropertySet(), macroitem);
00299             propertySetReloaded(true);
00300         } break;
00301         case COLUMN_ID_COMMENT: { // The "Comment" column
00302             macroitem->setComment( newvalue.toString() );
00303         } break;
00304         default:
00305             kdWarning() << "KexiMacroDesignView::beforeCellChanged() No such column number " << colnum << endl;
00306             return;
00307     }
00308 
00309     setDirty();
00310 }
00311 
00312 void KexiMacroDesignView::rowUpdated(KexiTableItem* item)
00313 {
00314     int rowindex = d->tabledata->findRef(item);
00315     kdDebug() << "KexiMacroDesignView::rowUpdated() rowindex=" << rowindex << endl;
00316     //propertySetSwitched();
00317     //propertySetReloaded(true);
00318     //setDirty();
00319 }
00320 
00321 void KexiMacroDesignView::rowInserted(KexiTableItem*, uint row, bool)
00322 {
00323     kdDebug() << "KexiMacroDesignView::rowInserted() rowindex=" << row << endl;
00324     KoMacro::MacroItem::List& macroitems = macro()->items();
00325 
00326     if(row < macroitems.count()) {
00327         // If a new item was inserted, we need to insert a new item to our
00328         // list of MacroItems too. If the new item was appended, we don't
00329         // need to do anything yet cause the new item will be handled on
00330         // beforeCellChanged() anyway.
00331         kdDebug() << "KexiMacroDesignView::rowInserted() Inserting new MacroItem" << endl;
00332         KSharedPtr<KoMacro::MacroItem> macroitem = KSharedPtr<KoMacro::MacroItem>( new KoMacro::MacroItem() );
00333         KoMacro::MacroItem::List::Iterator it = macroitems.at(row);
00334         macroitems.insert(it, macroitem);
00335     }
00336 }
00337 
00338 void KexiMacroDesignView::rowDeleted()
00339 {
00340     int rowindex = d->propertyset->currentRow();
00341     if(rowindex < 0) {
00342         kdWarning() << "KexiMacroDesignView::rowDeleted() No such item" << endl;
00343         return;
00344     }
00345     kdDebug() << "KexiMacroDesignView::rowDeleted() rowindex=" << rowindex << endl;
00346     KoMacro::MacroItem::List& macroitems = macro()->items();
00347     macroitems.remove( macroitems.at(rowindex) );
00348 }
00349 
00350 bool KexiMacroDesignView::updateSet(KoProperty::Set* set, KSharedPtr<KoMacro::MacroItem> macroitem, const QString& variablename)
00351 {
00352     kdDebug() << "KexiMacroDesignView::updateSet() variablename=" << variablename << endl;
00353     KoProperty::Property* property = KexiMacroProperty::createProperty(macroitem, variablename);
00354     if(! property)
00355         return false;
00356     set->addProperty(property);
00357     return true;
00358 }
00359 
00360 void KexiMacroDesignView::updateProperties(int row, KoProperty::Set* set, KSharedPtr<KoMacro::MacroItem> macroitem)
00361 {
00362     kdDebug() << "KexiMacroDesignView::updateProperties() row=" << row << endl;
00363 
00364     if(row < 0 || d->updatesProperties) {
00365         return; // ignore invalid rows and avoid infinite recursion.
00366     }
00367 
00368     KSharedPtr<KoMacro::Action> action = macroitem->action();
00369     if(! action.data()) {
00370         // don't display a propertyset if there is no action defined.
00371         d->propertyset->remove(row);
00372         return; // job done.
00373     }
00374 
00375     d->updatesProperties = true;
00376 
00377     if(set) {
00378         // we need to clear old data before adding the new content.
00379         set->clear();
00380     }
00381     else {
00382         // if there exists no such propertyset yet, create one.
00383         set = new KoProperty::Set(d->propertyset, action->name());
00384         d->propertyset->insert(row, set, true);
00385         connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
00386                 this, SLOT(propertyChanged(KoProperty::Set&, KoProperty::Property&)));
00387     }
00388 
00389     // The caption.
00390     KoProperty::Property* prop = new KoProperty::Property("this:classString", action->text());
00391     prop->setVisible(false);
00392     set->addProperty(prop);
00393 
00394     // Display the list of variables.
00395     QStringList varnames = action->variableNames();
00396     for(QStringList::Iterator it = varnames.begin(); it != varnames.end(); ++it) {
00397         if(updateSet(set, macroitem, *it)) {
00398             KSharedPtr<KoMacro::Variable> variable = macroitem->variable(*it, true);
00399             kdDebug()<<"KexiMacroDesignView::updateProperties() name=" << *it << " variable=" << variable->variant().toString() << endl;
00400 #if 0
00401             macroitem->setVariable(*it, variable);
00402 #endif
00403         }
00404     }
00405 
00406     d->updatesProperties = false;
00407 }
00408 
00409 void KexiMacroDesignView::propertyChanged(KoProperty::Set& set, KoProperty::Property& property)
00410 {
00411     Q_UNUSED(set);
00412     kdDebug() << "!!!!! KexiMacroDesignView::propertyChanged() propertyname=" << property.name() << endl;
00413     setDirty();
00414 
00415     /*
00416     if(d->reloadsProperties) // be sure to don't update properties if we are still on reloading.
00417         return;
00418     d->reloadsProperties = true;
00419 
00420     const int row = d->propertyset->currentRow();
00421     const QCString name = property.name();
00422     kdDebug() << "KexiMacroDesignView::propertyChanged() name=" << name << " row=" << row << endl;
00423 
00424     //TODO reload is only needed if something changed!
00425     bool dirty = true; bool reload = true;//dirtyvarnames.count()>0;
00426 
00427     if(dirty || reload) { // Only reload properties if it's really needed.
00428         setDirty();
00429         if(reload) {
00430             // The MacroItem which should be changed.
00431             KSharedPtr<KoMacro::MacroItem> macroitem = macro()->items()[row];
00432             // Update the properties.
00433             updateProperties(row, &set, macroitem);
00434         }
00435         // It's needed to call the reload delayed cause in KoProperty::Editor
00436         // QTimer::singleShot(10, this, SLOT(selectItemLater())); may lead
00437         // to crashes if we are to fast.
00438         QTimer::singleShot(50, this, SLOT(reloadPropertyLater()));
00439     }
00440 
00441     d->reloadsProperties = false;
00442     */
00443 
00444     /*
00445     QStringList dirtyvarnames = macroitem->setVariable(name, KSharedPtr<KoMacro::Variable>(pv));
00446     bool dirty = false;
00447     bool reload = false;
00448     for(QStringList::Iterator it = dirtyvarnames.begin(); it != dirtyvarnames.end(); ++it) {
00449         KSharedPtr<KoMacro::Variable> variable = macroitem->variable(*it);
00450         if(! variable.data()) {
00451             kdDebug() << "KexiMacroDesignView::propertyChanged() name=" << name << " it=" << *it << " skipped cause such a variable is not known." << endl;
00452             continue;
00453         }
00454 
00455         if(! set.contains( (*it).latin1() )) {
00456             // If there exist no such property yet, we need to add it.
00457             if(updateSet(&set, macroitem, *it))
00458                 reload = true; // we like to reload the whole set
00459             continue;
00460         }
00461 
00462         kdDebug() << "KexiMacroDesignView::propertyChanged() set existing property=" << *it << endl;
00463         KoProperty::Property& p = set.property((*it).latin1());
00464         KoMacro::Variable::List children = variable->children();
00465         if(children.count() > 0) {
00466             QStringList keys, names;
00467             KoMacro::Variable::List::Iterator childit(children.begin()), childend(children.end());
00468             for(; childit != childend; ++childit) {
00469                 const QString s = (*childit)->variant().toString();
00470                 keys << s;
00471                 names << s;
00472             }
00473             p.setListData( new KoProperty::Property::ListData(keys, names) );
00474         }
00475         p.setValue(variable->variant());
00476         dirty = true;
00477     }
00478 
00479     // If there are expired aka not any longer needed properties around, we
00480     // need to reload the whole set.
00481     for(KoProperty::Set::Iterator setit = set; setit.current(); ++setit) {
00482         if(setit.currentKey() == name) continue; // don't remove ourself
00483         if(setit.currentKey().left(5) == QCString("this:")) continue; // don't remove internal properties
00484         if(setit.currentKey() == QCString("newrow")) continue; // also an internal used property
00485         if(action.data() && action->hasVariable(setit.currentKey())) continue; // the property is still valid
00486         reload = true; // we like to reload the whole set
00487     }
00488     */
00489 }
00490 
00491 void KexiMacroDesignView::reloadPropertyLater()
00492 {
00493     propertySetReloaded(true);
00494 }
00495 
00496 #include "keximacrodesignview.moc"
00497 
KDE Home | KDE Accessibility Home | Description of Access Keys