Optika GUI Toolik Version of the Day
Optika_treemodel.cpp
Go to the documentation of this file.
00001 // @HEADER
00002 // ***********************************************************************
00003 // 
00004 //         Optika: A Tool For Developing Parameter Obtaining GUIs
00005 //                Copyright (2009) Sandia Corporation
00006 // 
00007 // Under terms of Contract DE-AC04-94AL85000, with Sandia Corporation, the 
00008 // U.S. Government retains certain rights in this software.
00009 // 
00010 // This library is free software; you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as
00012 // published by the Free Software Foundation; either version 2.1 of the
00013 // License, or (at your option) any later version.
00014 //  
00015 // This library is distributed in the hope that it will be useful, but
00016 // WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //  
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00023 // USA
00024 // Questions? Contact Kurtis Nusbaum (klnusbaum@gmail.com) 
00025 // 
00026 // ***********************************************************************
00027 // @HEADER
00028 #include <QXmlStreamReader>
00029 #include "Optika_treemodel.hpp"
00030 #include "Teuchos_XMLParameterListWriter.hpp"
00031 #include "Teuchos_ParameterEntryXMLConverter.hpp"
00032 #include <QTextStream>
00033 #include <QDomElement>
00034 
00035 namespace Optika{
00036 
00037 
00038 TreeModel::TreeModel(RCP<ParameterList> validParameters, RCP<DependencySheet> dependencySheet,
00039      QString saveFileName, QObject *parent):
00040    QAbstractItemModel(parent),
00041    dependencies(nonnull(dependencySheet)),
00042    validParameters(validParameters),
00043    dependencySheet(dependencySheet)
00044 {
00045   basicSetup(saveFileName);
00046   if(dependencies){
00047     connect(this, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), 
00048       this, SLOT(dataChangedListener(const QModelIndex&, const QModelIndex&)));
00049   }
00050 }
00051 
00052 TreeModel::~TreeModel() {
00053   delete rootItem;
00054 }
00055 
00056 QVariant TreeModel::data(const QModelIndex &index, int role) const {
00057   if(!index.isValid()){
00058     return QVariant();
00059   }
00060   if(role != Qt::DisplayRole && role != Qt::ToolTipRole
00061     && role != getRawDataRole())
00062   {
00063     return QVariant();
00064   }
00065   TreeItem *item = (TreeItem*)(index.internalPointer());
00066   return item->data(index.column(), role);
00067 }
00068 
00069 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
00070   if(!index.isValid()){
00071     return Qt::ItemIsEnabled;
00072   }
00073   else if(index.column() == 1){
00074     return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
00075   }
00076   else{
00077     return QAbstractItemModel::flags(index);
00078   }
00079 }
00080 
00081 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const{
00082   if(orientation == Qt::Horizontal && role == Qt::DisplayRole){
00083     return rootItem->data(section);
00084   }
00085   return QVariant();
00086 }
00087 
00088 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const{
00089   if(!hasIndex(row, column, parent)){
00090     return QModelIndex();
00091   }
00092   TreeItem *parentItem;
00093 
00094   if(!parent.isValid()){
00095     parentItem = rootItem;
00096   }
00097   else{
00098     parentItem = (TreeItem*)(parent.internalPointer());
00099   }
00100   TreeItem *childItem = parentItem->child(row);
00101 
00102   if(childItem){
00103     return createIndex(row, column, childItem);
00104   }
00105   return QModelIndex();
00106 }
00107 
00108 QModelIndex TreeModel::parent(const QModelIndex &index) const{
00109   if(!index.isValid()){
00110     return QModelIndex();
00111   }
00112 
00113   TreeItem *childItem = (TreeItem*)(index.internalPointer());
00114   TreeItem *parentItem = childItem->parent();
00115 
00116   if(parentItem == rootItem){
00117     return QModelIndex();
00118   }
00119 
00120   return createIndex(parentItem->row(), 0, parentItem);
00121 }
00122 
00123 bool TreeModel::setData(const QModelIndex & index, const QVariant &value, int role){
00124   if(index.isValid() && index.column() == 1 && role == Qt::EditRole){
00125     TreeItem *item = (TreeItem*)(index.internalPointer());
00126     if(item->changeValue(value)){
00127       //Need to do this check because QDoubleValidators are really lax.
00128       //Plus it's probably good to do anyway.
00129       if(item->hasValidValue()){
00130         emit dataChanged(index, index);
00131       }
00132       else{
00133         emit badValue(index, item->getCurrentInvalidValueMessage());
00134       }
00135     }
00136     return true;
00137   }
00138   return false;
00139 }
00140 
00141 int TreeModel::rowCount(const QModelIndex &parent) const{
00142   TreeItem *parentItem;
00143   if(parent.column() > 0){
00144     return 0;
00145   }
00146 
00147   if (!parent.isValid()){
00148     parentItem = rootItem;
00149   }
00150   else{
00151     parentItem = (TreeItem*)(parent.internalPointer());
00152   }
00153 
00154   return parentItem->childCount();
00155 }
00156 
00157 int TreeModel::columnCount(const QModelIndex &parent) const {
00158   if(parent.isValid()){
00159     return ((TreeItem*)(parent.internalPointer()))->columnCount();
00160   }
00161   else{
00162     return rootItem->columnCount();
00163   }
00164 }
00165 
00166 void TreeModel::issueInitilizationSignals(){
00167   for(
00168     DependencySheet::DepSet::const_iterator it = 
00169       dependencySheet->depBegin(); 
00170     it != dependencySheet->depEnd(); 
00171     ++it)
00172   {
00173     for(
00174       Dependency::ConstParameterEntryList::const_iterator it2=
00175         (*it)->getDependees().begin();
00176       it2 != (*it)->getDependees().end();
00177       ++it2)
00178     {
00179       QModelIndex dependeeIndex = findParameterEntryIndex(*it2);
00180       TEST_FOR_EXCEPTION(!dependeeIndex.isValid(), std::logic_error,
00181         "Could not find the index of the dependee. This is an internal error. "
00182         "Please contact the Optika team.");
00183       dataChangedListener(dependeeIndex, dependeeIndex);
00184         
00185     }
00186   }
00187 }
00188 
00189 void TreeModel::printOut() const{
00190   rootItem->printOut();
00191 }
00192 
00193 bool TreeModel::writeOutput(QString fileName){
00194   QFile *file = new QFile(fileName);
00195   if(!file->open(QIODevice::WriteOnly)){
00196     return false;
00197   }
00198   std::ofstream outputFile;
00199   XMLParameterListWriter plWriter;
00200   XMLObject xmlOutput = 
00201     plWriter.toXML(*validParameters, dependencySheet);
00202   QTextStream outStream(file);
00203   outStream << QString::fromStdString(xmlOutput.toString());
00204   file->close();
00205   delete file;
00206   saved = true;
00207   saveFileName = fileName;
00208   return true;
00209 }
00210 
00211 bool TreeModel::isRootIndex(const QModelIndex& index) const{
00212   TreeItem* item = (TreeItem*)index.internalPointer();
00213   return item == NULL;
00214 }
00215   
00216 
00217 bool TreeModel::isRealMatch(
00218   const QDomElement& element, 
00219   const QModelIndex& potentialMatch) const
00220 {
00221   static QString nameAttr = QString::fromStdString(
00222     Teuchos::XMLParameterListWriter::getNameAttributeName());
00223   if(isRootIndex(potentialMatch) && element.parentNode().isDocument()){
00224     return true;
00225   }
00226   std::string potmatch = data(potentialMatch.sibling(potentialMatch.row(),0)).toString().toStdString();
00227   std::string elemcont = element.attribute(nameAttr).toStdString();
00228   if(data(potentialMatch.sibling(potentialMatch.row(),0)).toString() == 
00229     element.attribute(nameAttr))
00230   {
00231     return isRealMatch(element.parentNode().toElement(), potentialMatch.parent());
00232   }
00233   return false;
00234 
00235 }
00236 
00237 void TreeModel::processInputElement(const QDomElement& element){
00238   static QString nameAttrib = QString::fromStdString(
00239     Teuchos::XMLParameterListWriter::getNameAttributeName());
00240   static QString valueAttrib = QString::fromStdString(
00241     Teuchos::ParameterEntryXMLConverter::getValueAttributeName());
00242   QDomNode n = element.firstChild();
00243   while(!n.isNull()){
00244     QDomElement e = n.toElement();
00245     if(!e.isNull() && e.tagName().toStdString() == ParameterEntry::getTagName()){
00246       QString name = e.attribute(nameAttrib, "");
00247       TEST_FOR_EXCEPTION(name=="",std::runtime_error,
00248         "Error: Found parameter with no name attribute. Check XML");
00249       QList<QModelIndex> matches = match(index(0,0), Qt::DisplayRole, name,
00250                  -1, Qt::MatchExactly | Qt::MatchRecursive);
00251       if(matches.size() !=0){
00252         for(int i =0; i<matches.size(); ++i){
00253           if(isRealMatch(e, matches[i])){
00254             QModelIndex valueToEdit = matches.at(i).sibling(matches.at(i).row(), 1);
00255             QString newValue = e.attribute(valueAttrib);
00256             setData(valueToEdit,newValue, Qt::EditRole);
00257             break;
00258           }
00259         }
00260       }
00261     }
00262     else if(
00263       !e.isNull() 
00264       &&
00265       e.tagName().toStdString() == XMLParameterListWriter::getParameterListTagName()
00266     )
00267     {
00268       processInputElement(e);
00269     }
00270     n = n.nextSibling();
00271   }
00272 }
00273 
00274 void TreeModel::readInput(QString fileName){
00275   QFile file(fileName);
00276   TEST_FOR_EXCEPTION(!file.open(QIODevice::ReadOnly), std::runtime_error, 
00277     "Could not open file to read parameters.");
00278   QDomDocument xmlDoc;
00279   if(!xmlDoc.setContent(&file)){
00280     file.close();
00281     TEST_FOR_EXCEPTION(true, std::runtime_error, 
00282       "Error reading xml document. Bad XML syntax.");
00283   }
00284   file.close();
00285   QDomElement docElem = xmlDoc.documentElement();
00286   processInputElement(docElem);
00287 }
00288 
00289 QString TreeModel::getSaveFileName(){
00290   return saveFileName;
00291 }
00292 
00293 bool TreeModel::isSaved(){
00294   return saved;
00295 }
00296 
00297 void TreeModel::reset(){
00298   delete rootItem;
00299   QList<QVariant> headers;
00300   rootItem = new TreeItem("", null, 0, true);
00301   validParameters->setParameters(*canonicalList);
00302   readInParameterList(validParameters, rootItem);
00303   this->saveFileName = saveFileName;
00304   if(saveFileName != ""){
00305     saved = true;
00306     readInput(saveFileName);
00307   }
00308   else{
00309     saved = false;
00310   }
00311   if(dependencies){
00312     issueInitilizationSignals();
00313   }
00314   currentFileNowModified();
00315 }
00316 
00317 QString TreeModel::itemType(const QModelIndex &index) const{
00318   int row = index.row(); 
00319   QModelIndex itemTypeIndex = index.sibling(row, 2);
00320   return index.model()->data(itemTypeIndex, Qt::DisplayRole).toString();
00321 }
00322 
00323 bool TreeModel::hasDependencies(){
00324   return dependencies;
00325 }
00326 
00327 bool TreeModel::hasValidValue(QModelIndex valueToCheck) const{
00328   TreeItem *item = static_cast<TreeItem*>(valueToCheck.internalPointer());
00329   return item->hasValidValue();
00330 }
00331 
00332 RCP<const ParameterEntryValidator> TreeModel::getValidator(const QModelIndex &index) const{
00333   return itemEntry(index)->validator();
00334 }
00335 
00336 RCP<const ParameterList> TreeModel::getCurrentParameters(){
00337   return validParameters;
00338 }
00339 
00340 QModelIndex TreeModel::parameterEntryMatch(const QModelIndex &start,
00341   const RCP<const ParameterEntry> &parameterEntry) const
00342 {
00343   QModelIndex p = parent(start);
00344   int from = start.row();
00345   int to = rowCount(p);
00346 
00347   for (int r = from; r < to; ++r) {
00348     QModelIndex idx = index(r, start.column(), p);
00349     if(!idx.isValid())
00350       continue;
00351     RCP<const ParameterEntry> entry = itemEntry(idx);
00352     if(entry != null && entry.get() == parameterEntry.get()){
00353       return idx;
00354     }  
00355             
00356     if(hasChildren(idx)) { // search the hierarchy
00357       QModelIndex childResult = parameterEntryMatch(index(0, idx.column(), idx), parameterEntry);
00358       if(childResult.isValid()){
00359         return childResult;
00360       }
00361     }
00362   }
00363   return QModelIndex();
00364 }
00365 
00366 
00367 QModelIndex TreeModel::findParameterEntryIndex(
00368   RCP<const ParameterEntry> parameterEntry)
00369 {
00370   return parameterEntryMatch(index(0,0), parameterEntry);
00371 }
00372 
00373 
00374 RCP<const ParameterEntry> 
00375 TreeModel::itemEntry(const QModelIndex &index) const{
00376   if(!index.isValid()){
00377     return null;
00378   }
00379   TreeItem* item = (TreeItem*)index.internalPointer();
00380   if(item->hasEntry()){
00381     return item->getEntry();
00382   }
00383   else{
00384     return null;
00385   }
00386 }
00387 
00388 void TreeModel::readInParameterList(RCP<ParameterList> parameterList, TreeItem *parentItem){
00389   for(ParameterList::ConstIterator itr = parameterList->begin(); itr != parameterList->end(); ++itr){
00390     std::string name = parameterList->name(itr);
00391     if(parameterList->isSublist(name)){
00392       insertParameterList(sublist(parameterList, name), parameterList->getEntryRCP(name), name, parentItem);
00393     }
00394     else if(parameterList->isParameter(name)){
00395       insertParameter(parameterList->getEntryRCP(name), name, parentItem);
00396     }
00397   }
00398 }
00399 
00400 void TreeModel::insertParameterList(RCP<ParameterList> parameterList, RCP<ParameterEntry> listEntry, 
00401             std::string plname, TreeItem *parent)
00402 {
00403   QString truncatedName = QString::fromStdString(plname).section("->",-1);
00404   
00405 
00406   TreeItem *newList = new TreeItem(truncatedName, listEntry, parent);
00407   parent->appendChild(newList);
00408   for(ParameterList::ConstIterator itr = parameterList->begin(); itr != parameterList->end(); ++itr){
00409     std::string name = parameterList->name(itr);
00410     if(parameterList->isSublist(name)){
00411       insertParameterList(sublist(parameterList, name), parameterList->getEntryRCP(name), name,  newList);
00412     }
00413     else if(parameterList->isParameter(name)){
00414       insertParameter(parameterList->getEntryRCP(name), name, newList);
00415     }
00416   }
00417 }
00418 
00419 void TreeModel::insertParameter(RCP<ParameterEntry> parameter, std::string name, TreeItem *parent){
00420   parent->appendChild(new TreeItem(QString::fromStdString(name), parameter, parent));
00421 }
00422 
00423 void TreeModel::basicSetup(QString saveFileName){
00424   QList<QVariant> headers;
00425   rootItem = new TreeItem("", null, 0, true); 
00426   canonicalList = RCP<const ParameterList>(new ParameterList(*validParameters));
00427   readInParameterList(validParameters, rootItem);
00428   this->saveFileName = saveFileName;
00429   if(saveFileName != ""){
00430     saved = true;
00431     readInput(saveFileName);
00432   }
00433   else{
00434     saved = false;
00435   }
00436 }
00437 
00438 void TreeModel::checkDependentState(const QModelIndex dependee, RCP<Dependency> dependency){
00439   QModelIndex dependent;
00440   Dependency::ParameterEntryList dependents= dependency->getDependents();
00441   for(
00442     Dependency::ParameterEntryList::iterator it = dependents.begin(); 
00443     it != dependents.end(); 
00444     ++it )
00445   {
00446     dependent = findParameterEntryIndex(*it);
00447     if(!is_null(rcp_dynamic_cast<VisualDependency>(dependency))){
00448       RCP<VisualDependency> visDep = 
00449         rcp_static_cast<VisualDependency>(dependency);
00450       visDep->isDependentVisible() ? 
00451         emit showData(dependent.row(), dependent.parent()) :
00452         emit hideData(dependent.row(), dependent.parent());
00453     }
00454 
00455     if(!hasValidValue(dependent)){
00456       QString message = 
00457         "Because you recently modified the " + 
00458         data(dependee, Qt::DisplayRole).toString() +
00459         " parameter, the valid values for the " + 
00460         data(dependent, Qt::DisplayRole).toString() +
00461         " parameter have changed.\n\nPlease modify the " +  
00462         data(dependent,Qt::DisplayRole).toString() + " value.\n";
00463       emit badValue(dependent.sibling(dependent.row(), 1), message);
00464     }
00465   }
00466 }
00467 
00468 void TreeModel::currentFileNowModified(){
00469   saved = false;
00470 }
00471 
00472 void TreeModel::dataChangedListener(const QModelIndex& index1, const QModelIndex& /*index2*/){
00473   RCP<const ParameterEntry> changedIndexEntry = 
00474     itemEntry(index1);  
00475   QModelIndex dependee = index1.sibling(index1.row(), 0);
00476   if(dependencySheet->hasDependents(changedIndexEntry)){
00477     RCP<const DependencySheet::DepSet> deps =  
00478       dependencySheet->getDependenciesForParameter(changedIndexEntry);
00479     for(
00480       DependencySheet::DepSet::const_iterator it = deps->begin();
00481       it != deps->end(); 
00482       ++it)
00483     {
00484       (*it)->evaluate();
00485       checkDependentState(dependee,*it);
00486     }
00487   }
00488 }
00489 
00490 
00491 
00492 }
00493 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Defines