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 // Redistribution and use in source and binary forms, with or without
00011 // modification, are permitted provided that the following conditions are
00012 // met:
00013 //
00014 // 1. Redistributions of source code must retain the above copyright
00015 // notice, this list of conditions and the following disclaimer.
00016 //
00017 // 2. Redistributions in binary form must reproduce the above copyright
00018 // notice, this list of conditions and the following disclaimer in the
00019 // documentation and/or other materials provided with the distribution.
00020 //
00021 // 3. Neither the name of the Corporation nor the names of the
00022 // contributors may be used to endorse or promote products derived from
00023 // this software without specific prior written permission.
00024 //
00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036 //
00037 // Questions? Contact Kurtis Nusbaum (klnusbaum@gmail.com) 
00038 // 
00039 // ***********************************************************************
00040 // @HEADER
00041 #include <QXmlStreamReader>
00042 #include "Optika_treemodel.hpp"
00043 #include "Teuchos_XMLParameterListWriter.hpp"
00044 #include "Teuchos_ParameterEntryXMLConverter.hpp"
00045 #include <QTextStream>
00046 #include <QDomElement>
00047 
00048 namespace Optika{
00049 
00050 
00051 TreeModel::TreeModel(RCP<ParameterList> validParameters, RCP<DependencySheet> dependencySheet,
00052      QString saveFileName, QObject *parent):
00053    QAbstractItemModel(parent),
00054    dependencies(nonnull(dependencySheet)),
00055    validParameters(validParameters),
00056    dependencySheet(dependencySheet)
00057 {
00058   basicSetup(saveFileName);
00059   if(dependencies){
00060     connect(this, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), 
00061       this, SLOT(dataChangedListener(const QModelIndex&, const QModelIndex&)));
00062   }
00063 }
00064 
00065 TreeModel::~TreeModel() {
00066   delete rootItem;
00067 }
00068 
00069 QVariant TreeModel::data(const QModelIndex &index, int role) const {
00070   if(!index.isValid()){
00071     return QVariant();
00072   }
00073   if(role != Qt::DisplayRole && role != Qt::ToolTipRole
00074     && role != getRawDataRole())
00075   {
00076     return QVariant();
00077   }
00078   TreeItem *item = (TreeItem*)(index.internalPointer());
00079   return item->data(index.column(), role);
00080 }
00081 
00082 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
00083   if(!index.isValid()){
00084     return Qt::ItemIsEnabled;
00085   }
00086   else if(index.column() == 1){
00087     return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
00088   }
00089   else{
00090     return QAbstractItemModel::flags(index);
00091   }
00092 }
00093 
00094 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const{
00095   if(orientation == Qt::Horizontal && role == Qt::DisplayRole){
00096     return rootItem->data(section);
00097   }
00098   return QVariant();
00099 }
00100 
00101 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const{
00102   if(!hasIndex(row, column, parent)){
00103     return QModelIndex();
00104   }
00105   TreeItem *parentItem;
00106 
00107   if(!parent.isValid()){
00108     parentItem = rootItem;
00109   }
00110   else{
00111     parentItem = (TreeItem*)(parent.internalPointer());
00112   }
00113   TreeItem *childItem = parentItem->child(row);
00114 
00115   if(childItem){
00116     return createIndex(row, column, childItem);
00117   }
00118   return QModelIndex();
00119 }
00120 
00121 QModelIndex TreeModel::parent(const QModelIndex &index) const{
00122   if(!index.isValid()){
00123     return QModelIndex();
00124   }
00125 
00126   TreeItem *childItem = (TreeItem*)(index.internalPointer());
00127   TreeItem *parentItem = childItem->parent();
00128 
00129   if(parentItem == rootItem){
00130     return QModelIndex();
00131   }
00132 
00133   return createIndex(parentItem->row(), 0, parentItem);
00134 }
00135 
00136 bool TreeModel::setData(const QModelIndex & index, const QVariant &value, int role){
00137   if(index.isValid() && index.column() == 1 && role == Qt::EditRole){
00138     TreeItem *item = (TreeItem*)(index.internalPointer());
00139     if(item->changeValue(value)){
00140       //Need to do this check because QDoubleValidators are really lax.
00141       //Plus it's probably good to do anyway.
00142       if(item->hasValidValue()){
00143         emit dataChanged(index, index);
00144       }
00145       else{
00146         emit badValue(index, item->getCurrentInvalidValueMessage());
00147       }
00148     }
00149     return true;
00150   }
00151   return false;
00152 }
00153 
00154 int TreeModel::rowCount(const QModelIndex &parent) const{
00155   TreeItem *parentItem;
00156   if(parent.column() > 0){
00157     return 0;
00158   }
00159 
00160   if (!parent.isValid()){
00161     parentItem = rootItem;
00162   }
00163   else{
00164     parentItem = (TreeItem*)(parent.internalPointer());
00165   }
00166 
00167   return parentItem->childCount();
00168 }
00169 
00170 int TreeModel::columnCount(const QModelIndex &parent) const {
00171   if(parent.isValid()){
00172     return ((TreeItem*)(parent.internalPointer()))->columnCount();
00173   }
00174   else{
00175     return rootItem->columnCount();
00176   }
00177 }
00178 
00179 void TreeModel::issueInitilizationSignals(){
00180   for(
00181     DependencySheet::DepSet::const_iterator it = 
00182       dependencySheet->depBegin(); 
00183     it != dependencySheet->depEnd(); 
00184     ++it)
00185   {
00186     for(
00187       Dependency::ConstParameterEntryList::const_iterator it2=
00188         (*it)->getDependees().begin();
00189       it2 != (*it)->getDependees().end();
00190       ++it2)
00191     {
00192       QModelIndex dependeeIndex = findParameterEntryIndex(*it2);
00193       TEUCHOS_TEST_FOR_EXCEPTION(!dependeeIndex.isValid(), std::logic_error,
00194         "Could not find the index of the dependee. This is an internal error. "
00195         "Please contact the Optika team.");
00196       dataChangedListener(dependeeIndex, dependeeIndex);
00197         
00198     }
00199   }
00200 }
00201 
00202 void TreeModel::printOut() const{
00203   rootItem->printOut();
00204 }
00205 
00206 bool TreeModel::writeOutput(QString fileName){
00207   QFile *file = new QFile(fileName);
00208   if(!file->open(QIODevice::WriteOnly)){
00209     return false;
00210   }
00211   std::ofstream outputFile;
00212   XMLParameterListWriter plWriter;
00213   XMLObject xmlOutput = 
00214     plWriter.toXML(*validParameters, dependencySheet);
00215   QTextStream outStream(file);
00216   outStream << QString::fromStdString(xmlOutput.toString());
00217   file->close();
00218   delete file;
00219   saved = true;
00220   saveFileName = fileName;
00221   return true;
00222 }
00223 
00224 bool TreeModel::isRootIndex(const QModelIndex& index) const{
00225   TreeItem* item = (TreeItem*)index.internalPointer();
00226   return item == NULL;
00227 }
00228   
00229 
00230 bool TreeModel::isRealMatch(
00231   const QDomElement& element, 
00232   const QModelIndex& potentialMatch) const
00233 {
00234   static QString nameAttr = QString::fromStdString(
00235     Teuchos::XMLParameterListWriter::getNameAttributeName());
00236   if(isRootIndex(potentialMatch) && element.parentNode().isDocument()){
00237     return true;
00238   }
00239   std::string potmatch = data(potentialMatch.sibling(potentialMatch.row(),0)).toString().toStdString();
00240   std::string elemcont = element.attribute(nameAttr).toStdString();
00241   if(data(potentialMatch.sibling(potentialMatch.row(),0)).toString() == 
00242     element.attribute(nameAttr))
00243   {
00244     return isRealMatch(element.parentNode().toElement(), potentialMatch.parent());
00245   }
00246   return false;
00247 
00248 }
00249 
00250 void TreeModel::processInputElement(const QDomElement& element){
00251   static QString nameAttrib = QString::fromStdString(
00252     Teuchos::XMLParameterListWriter::getNameAttributeName());
00253   static QString valueAttrib = QString::fromStdString(
00254     Teuchos::ParameterEntryXMLConverter::getValueAttributeName());
00255   QDomNode n = element.firstChild();
00256   while(!n.isNull()){
00257     QDomElement e = n.toElement();
00258     if(!e.isNull() && e.tagName().toStdString() == ParameterEntry::getTagName()){
00259       QString name = e.attribute(nameAttrib, "");
00260       TEUCHOS_TEST_FOR_EXCEPTION(name=="",std::runtime_error,
00261         "Error: Found parameter with no name attribute. Check XML");
00262       QList<QModelIndex> matches = match(index(0,0), Qt::DisplayRole, name,
00263                  -1, Qt::MatchExactly | Qt::MatchRecursive);
00264       if(matches.size() !=0){
00265         for(int i =0; i<matches.size(); ++i){
00266           if(isRealMatch(e, matches[i])){
00267             QModelIndex valueToEdit = matches.at(i).sibling(matches.at(i).row(), 1);
00268             QString newValue = e.attribute(valueAttrib);
00269             setData(valueToEdit,newValue, Qt::EditRole);
00270             break;
00271           }
00272         }
00273       }
00274     }
00275     else if(
00276       !e.isNull() 
00277       &&
00278       e.tagName().toStdString() == XMLParameterListWriter::getParameterListTagName()
00279     )
00280     {
00281       processInputElement(e);
00282     }
00283     n = n.nextSibling();
00284   }
00285 }
00286 
00287 void TreeModel::readInput(QString fileName){
00288   QFile file(fileName);
00289   TEUCHOS_TEST_FOR_EXCEPTION(!file.open(QIODevice::ReadOnly), std::runtime_error, 
00290     "Could not open file to read parameters.");
00291   QDomDocument xmlDoc;
00292   if(!xmlDoc.setContent(&file)){
00293     file.close();
00294     TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, 
00295       "Error reading xml document. Bad XML syntax.");
00296   }
00297   file.close();
00298   QDomElement docElem = xmlDoc.documentElement();
00299   processInputElement(docElem);
00300 }
00301 
00302 QString TreeModel::getSaveFileName(){
00303   return saveFileName;
00304 }
00305 
00306 bool TreeModel::isSaved(){
00307   return saved;
00308 }
00309 
00310 void TreeModel::reset(){
00311   delete rootItem;
00312   QList<QVariant> headers;
00313   rootItem = new TreeItem("", null, 0, true);
00314   validParameters->setParameters(*canonicalList);
00315   readInParameterList(validParameters, rootItem);
00316   this->saveFileName = saveFileName;
00317   if(saveFileName != ""){
00318     saved = true;
00319     readInput(saveFileName);
00320   }
00321   else{
00322     saved = false;
00323   }
00324   if(dependencies){
00325     issueInitilizationSignals();
00326   }
00327   currentFileNowModified();
00328 }
00329 
00330 QString TreeModel::itemType(const QModelIndex &index) const{
00331   int row = index.row(); 
00332   QModelIndex itemTypeIndex = index.sibling(row, 2);
00333   return index.model()->data(itemTypeIndex, Qt::DisplayRole).toString();
00334 }
00335 
00336 bool TreeModel::hasDependencies(){
00337   return dependencies;
00338 }
00339 
00340 bool TreeModel::hasValidValue(QModelIndex valueToCheck) const{
00341   TreeItem *item = static_cast<TreeItem*>(valueToCheck.internalPointer());
00342   return item->hasValidValue();
00343 }
00344 
00345 RCP<const ParameterEntryValidator> TreeModel::getValidator(const QModelIndex &index) const{
00346   return itemEntry(index)->validator();
00347 }
00348 
00349 RCP<const ParameterList> TreeModel::getCurrentParameters(){
00350   return validParameters;
00351 }
00352 
00353 QModelIndex TreeModel::parameterEntryMatch(const QModelIndex &start,
00354   const RCP<const ParameterEntry> &parameterEntry) const
00355 {
00356   QModelIndex p = parent(start);
00357   int from = start.row();
00358   int to = rowCount(p);
00359 
00360   for (int r = from; r < to; ++r) {
00361     QModelIndex idx = index(r, start.column(), p);
00362     if(!idx.isValid())
00363       continue;
00364     RCP<const ParameterEntry> entry = itemEntry(idx);
00365     if(entry != null && entry.get() == parameterEntry.get()){
00366       return idx;
00367     }  
00368             
00369     if(hasChildren(idx)) { // search the hierarchy
00370       QModelIndex childResult = parameterEntryMatch(index(0, idx.column(), idx), parameterEntry);
00371       if(childResult.isValid()){
00372         return childResult;
00373       }
00374     }
00375   }
00376   return QModelIndex();
00377 }
00378 
00379 
00380 QModelIndex TreeModel::findParameterEntryIndex(
00381   RCP<const ParameterEntry> parameterEntry)
00382 {
00383   return parameterEntryMatch(index(0,0), parameterEntry);
00384 }
00385 
00386 
00387 RCP<const ParameterEntry> 
00388 TreeModel::itemEntry(const QModelIndex &index) const{
00389   if(!index.isValid()){
00390     return null;
00391   }
00392   TreeItem* item = (TreeItem*)index.internalPointer();
00393   if(item->hasEntry()){
00394     return item->getEntry();
00395   }
00396   else{
00397     return null;
00398   }
00399 }
00400 
00401 void TreeModel::readInParameterList(RCP<ParameterList> parameterList, TreeItem *parentItem){
00402   for(ParameterList::ConstIterator itr = parameterList->begin(); itr != parameterList->end(); ++itr){
00403     std::string name = parameterList->name(itr);
00404     if(parameterList->isSublist(name)){
00405       insertParameterList(sublist(parameterList, name), parameterList->getEntryRCP(name), name, parentItem);
00406     }
00407     else if(parameterList->isParameter(name)){
00408       insertParameter(parameterList->getEntryRCP(name), name, parentItem);
00409     }
00410   }
00411 }
00412 
00413 void TreeModel::insertParameterList(RCP<ParameterList> parameterList, RCP<ParameterEntry> listEntry, 
00414             std::string plname, TreeItem *parent)
00415 {
00416   QString truncatedName = QString::fromStdString(plname).section("->",-1);
00417   
00418 
00419   TreeItem *newList = new TreeItem(truncatedName, listEntry, parent);
00420   parent->appendChild(newList);
00421   for(ParameterList::ConstIterator itr = parameterList->begin(); itr != parameterList->end(); ++itr){
00422     std::string name = parameterList->name(itr);
00423     if(parameterList->isSublist(name)){
00424       insertParameterList(sublist(parameterList, name), parameterList->getEntryRCP(name), name,  newList);
00425     }
00426     else if(parameterList->isParameter(name)){
00427       insertParameter(parameterList->getEntryRCP(name), name, newList);
00428     }
00429   }
00430 }
00431 
00432 void TreeModel::insertParameter(RCP<ParameterEntry> parameter, std::string name, TreeItem *parent){
00433   parent->appendChild(new TreeItem(QString::fromStdString(name), parameter, parent));
00434 }
00435 
00436 void TreeModel::basicSetup(QString saveFileName){
00437   QList<QVariant> headers;
00438   rootItem = new TreeItem("", null, 0, true); 
00439   canonicalList = RCP<const ParameterList>(new ParameterList(*validParameters));
00440   readInParameterList(validParameters, rootItem);
00441   this->saveFileName = saveFileName;
00442   if(saveFileName != ""){
00443     saved = true;
00444     readInput(saveFileName);
00445   }
00446   else{
00447     saved = false;
00448   }
00449 }
00450 
00451 void TreeModel::checkDependentState(const QModelIndex dependee, RCP<Dependency> dependency){
00452   QModelIndex dependent;
00453   Dependency::ParameterEntryList dependents= dependency->getDependents();
00454   for(
00455     Dependency::ParameterEntryList::iterator it = dependents.begin(); 
00456     it != dependents.end(); 
00457     ++it )
00458   {
00459     dependent = findParameterEntryIndex(*it);
00460     if(!is_null(rcp_dynamic_cast<VisualDependency>(dependency))){
00461       RCP<VisualDependency> visDep = 
00462         rcp_static_cast<VisualDependency>(dependency);
00463       visDep->isDependentVisible() ? 
00464         emit showData(dependent.row(), dependent.parent()) :
00465         emit hideData(dependent.row(), dependent.parent());
00466     }
00467 
00468     if(!hasValidValue(dependent)){
00469       QString message = 
00470         "Because you recently modified the " + 
00471         data(dependee, Qt::DisplayRole).toString() +
00472         " parameter, the valid values for the " + 
00473         data(dependent, Qt::DisplayRole).toString() +
00474         " parameter have changed.\n\nPlease modify the " +  
00475         data(dependent,Qt::DisplayRole).toString() + " value.\n";
00476       emit badValue(dependent.sibling(dependent.row(), 1), message);
00477     }
00478   }
00479 }
00480 
00481 void TreeModel::currentFileNowModified(){
00482   saved = false;
00483 }
00484 
00485 void TreeModel::dataChangedListener(const QModelIndex& index1, const QModelIndex& /*index2*/){
00486   RCP<const ParameterEntry> changedIndexEntry = 
00487     itemEntry(index1);  
00488   QModelIndex dependee = index1.sibling(index1.row(), 0);
00489   if(dependencySheet->hasDependents(changedIndexEntry)){
00490     RCP<const DependencySheet::DepSet> deps =  
00491       dependencySheet->getDependenciesForParameter(changedIndexEntry);
00492     for(
00493       DependencySheet::DepSet::const_iterator it = deps->begin();
00494       it != deps->end(); 
00495       ++it)
00496     {
00497       (*it)->evaluate();
00498       checkDependentState(dependee,*it);
00499     }
00500   }
00501 }
00502 
00503 
00504 
00505 }
00506 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Defines