DumontEXE 0.0.1
cmcTreeModel.cpp
00001 /* ***************************************************************************
00002 **
00003 ** Copyright (C) 2007 Lorimark Solutions, LLC. All rights reserved.
00004 **
00005 ** This file is part of the DumontEXE Scripting Extension Kit
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://dumont.showoff-db.org/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://dumont.showoff-db.org/licensing.html 
00017 ** or contact the sales department at sales@lorimarksolutions.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ** author: Mark Petryk ~ Lorimark Solutions, LLC
00023 ** **************************************************************************/
00024 
00025 #include <QPointer>
00026 
00027 #include "cmcTreeModel.h"
00028 #include "cmcApplication.h"
00029 #include "cmcCursor.h"
00030 #include "cmcRowSet.h"
00031 
00032 namespace cmcDatabaseApi {
00033 
00034 QString dq( const QString & inString )
00035 {
00036   return( "\"" + inString + "\"" );
00037 }
00038 
00039 class cmcTreeModel;
00040 class cmcTreeModelNode
00041 {
00042   public:
00043 
00044     /*
00045     ** Construct on char * name
00046     **
00047     */
00048     cmcTreeModelNode
00049     ( 
00050       const char * name, 
00051       cmcTreeModelNode * parent, 
00052       cmcTreeModel * model 
00053     )
00054     {
00055       m_itemName = name;
00056       m_model    = model;
00057       m_parent   = parent;
00058       m_isValid  = false;
00059       m_isDirty  = true;
00060 
00061     }
00062 
00063     /*
00064     ** Construct on QString name
00065     **
00066     */
00067     cmcTreeModelNode
00068     ( 
00069       const QString & name, 
00070       cmcTreeModelNode * parent, 
00071       cmcTreeModel * model 
00072     )
00073     {
00074       m_itemName = name;
00075       m_model    = model;
00076       m_parent   = parent;
00077       m_isValid  = false;
00078       m_isDirty  = true;
00079     }
00080 
00081     /*
00082     ** Construct on UUID
00083     **
00084     */
00085     cmcTreeModelNode
00086     (
00087       const QUuid & uuid, 
00088       cmcTreeModelNode * parent, 
00089       cmcTreeModel * model 
00090     )
00091     {
00092       m_id       = uuid;
00093       m_model    = model;
00094       m_parent   = parent;
00095       m_isValid  = false;
00096       m_isDirty  = true;
00097     }
00098 
00099     ~cmcTreeModelNode()
00100     {
00101       qDeleteAll(m_children);
00102     }
00103 
00104     cmcTreeModel * model()
00105     {
00106       return( m_model );
00107     }
00108 
00109     cmcTreeModelNode * parent() const
00110     {
00111       return( m_parent );
00112     }
00113 
00114     QList<cmcTreeModelNode*> children()
00115     {
00116       return( m_children );
00117     }
00118 
00119     bool isValid() const
00120     {
00121       return( m_isValid );
00122     }
00123 
00124     bool isDirty() const
00125     {
00126       return( m_isDirty );
00127     }
00128 
00129     bool load( cmcCursor * cursor )
00130     {
00131       if( !cursor )
00132       {
00133         qDebug( "load no cursor exiting" );
00134         m_id = QUuid::createUuid();
00135         return( false );
00136       }
00137 
00138       qDebug( "loading %s from %s %ld", qPrintable(itemName()), qPrintable(cursor->filters()), cursor->rowCount() );
00139 
00140       if( cursor-> rowCount() != 1 )
00141       {
00142         qDebug( "load rowcount not 1 exiting" );
00143         m_id = QUuid::createUuid();
00144         return( false );
00145       }
00146 
00147 #ifdef USE_SCOPED_POINTER
00148       QScopedPointer<cmcRowSet> qrs(cursor->getQueryRowSet(1));
00149       return( load( 0, qrs.data() ) );
00150 #else
00151       std::auto_ptr<cmcRowSet> qrs(cursor->getQueryRowSet(1));
00152       return( load( 0, qrs.get() ) );
00153 #endif
00154 
00155 
00156     }
00157 
00158     bool load( int row, cmcRowSet * rs )
00159     {
00160       m_id       = rs-> getRowString( row, "id"       );
00161       m_idParent = rs-> getRowString( row, "idParent" );
00162       m_itemName = rs-> getRowString( row, "itemName" );
00163       m_itemAttr = rs-> getRowString( row, "itemAttr" );
00164       m_itemData = rs-> getRowString( row, "itemData" );
00165 
00166 //      qDebug( "load %s", qPrintable(itemName()) );
00167 
00168       m_isValid = true;
00169       m_isDirty = false;
00170 
00171       return( true );
00172     }
00173 
00174     bool load( cmcRowSet * rs )
00175     {
00176       for( int i=0; i<rs-> rowCount(); i++ )
00177       {
00178         cmcTreeModelNode * node = new cmcTreeModelNode( rs-> getRowString( i, "itemName" ), this, model() );
00179         node-> load( i, rs );
00180         m_children.append(node);
00181       }
00182 
00183 //      qDebug( "%s children count %d", qPrintable(itemName()), m_children.count() );
00184 
00185       return( true );
00186     }
00187 
00188     long updateRowSet( cmcRowSet * rowSet )
00189     {
00190       /*
00191       ** Update all the fields in the row set.
00192       **
00193       */
00194       rowSet-> modifyRow( 0, "id",       id()       );
00195       rowSet-> modifyRow( 0, "idParent", idParent() );
00196       rowSet-> modifyRow( 0, "itemData", itemData() );
00197       rowSet-> modifyRow( 0, "itemName", itemName() );
00198       rowSet-> modifyRow( 0, "itemAttr", itemAttr() );
00199 
00200       return( rowSet-> commit() );
00201 
00202     } // endlong cmcCdaItem::updateRowSet( cmcRowSet * rowSet )
00203 
00204     bool save()
00205     {
00206       qDebug( "saving" );
00207 
00208 #ifdef USE_SCOPED_POINTER
00209       QScopedPointer<cmcCursor> cursor( model()-> getCursor(this) );
00210 #else
00211       std::auto_ptr<cmcCursor> cursor( model()-> getCursor(this) );
00212 #endif
00213 
00214       /*
00215       ** We do different things depending on what we've got to work with.
00216       **
00217       */
00218       switch( cursor-> rowCount() )
00219       {
00220         /*
00221         ** No records means we have to write out this item newly.
00222         **
00223         */
00224         case 0:
00225         {
00226           qDebug( "adding new item" );
00227 #ifdef USE_SCOPED_POINTER
00228           QScopedPointer<cmcRowSet> ars( cursor-> getAddRowSet(1) );
00229           updateRowSet( ars.data() );
00230 #else
00231           std::auto_ptr<cmcRowSet> ars( cursor-> getAddRowSet(1) );
00232           updateRowSet( ars.get() );
00233 #endif
00234           break;
00235 
00236         } // endcase 0:
00237 
00238         /*
00239         ** One record means we have to update the existing item.
00240         **
00241         */
00242         case 1:
00243         {
00244           qDebug( "editing existing item" );
00245 #ifdef USE_SCOPED_POINTER
00246           QScopedPointer<cmcRowSet> ers( cursor-> getEditRowSet(1) );
00247           updateRowSet( ers.data() );
00248 #else
00249           std::auto_ptr<cmcRowSet> ers( cursor-> getEditRowSet(1) );
00250           updateRowSet( ers.get() );
00251 #endif
00252           break;
00253 
00254         } // endcase 1:
00255 
00256         /*
00257         ** This is a problem.  We should have 1 or zero rows, not more than
00258         **  1.  If there is more than one then that means there is a duplicate
00259         **  item somewhere in our database, and this doesn't work!  That's like
00260         **  having two sub-directories in the same directory with the same name,
00261         **  or a directory and a filename with the same name - it just doesn't
00262         **  work.
00263         **
00264         */
00265         default:
00266         {
00267           TRACE_MESSAGE( "cmcCdaItem::save() too many rows" );
00268           return( false );
00269 
00270         } // enddefault:
00271 
00272       } // endswitch( cursor-> rowCount() )
00273 
00274       /*
00275       ** Indicate that this item is no longer dirty.
00276       **
00277       */
00278       m_isDirty = false;
00279 
00280 //      /*
00281 //      ** Save all the children
00282 //      **
00283 //      */
00284 //      QList<cmcCdaItem*> items = findChildren<cmcCdaItem*>();
00285 //      foreach( cmcCdaItem * item, items )
00286 //      {
00287 //        if( item-> dirty() ) item-> save();
00288 //      }
00289 
00290 
00291     }
00292 
00293     QUuid id() const
00294     {
00295       return( m_id );
00296     }
00297 
00298     QUuid idParent() const
00299     {
00300       if( !parent() ) return( QUuid() );
00301       return( parent()-> id() );
00302     }
00303 
00304     QString itemName() const
00305     {
00306       return( m_itemName );
00307     }
00308 
00309     QString itemData() const
00310     {
00311       return( m_itemData );
00312     }
00313 
00314     QString itemAttr() const
00315     {
00316       return( m_itemAttr );
00317     }
00318 
00319     bool m_isValid;
00320     bool m_isDirty;
00321 
00322     QUuid   m_id;
00323     QUuid   m_idParent;
00324     QString m_itemData;
00325     QString m_itemName;
00326     QString m_itemAttr;
00327 
00328     cmcTreeModel * m_model;
00329     cmcTreeModelNode * m_parent;
00330     QList<cmcTreeModelNode*> m_children;
00331 
00332 }; // endclass cmcTreeModelNode
00333 
00334 
00335 cmcTreeModel::cmcTreeModel( const cmcTreeModel & copy ):
00336   QAbstractItemModel()
00337 {
00338   rootNode = 0;
00339 }
00340 
00341 cmcTreeModel::cmcTreeModel
00342 (
00343   cmcApplication * app,
00344   const QString & categoryName,
00345   const QString & idFieldName,
00346   const QString & idParentFieldName,
00347   const QString & itemNameFieldName,
00348   const QString & itemAttrFieldName,
00349   const QString & itemDataFieldName
00350 ):
00351   QAbstractItemModel(app)
00352 {
00353   this-> app               = app;
00354   this-> categoryName      = categoryName;
00355   this-> idFieldName       = idFieldName;
00356   this-> idParentFieldName = idParentFieldName;
00357   this-> itemNameFieldName = itemNameFieldName;
00358   this-> itemAttrFieldName = itemAttrFieldName;
00359   this-> itemDataFieldName = itemDataFieldName;
00360 
00361   rootNode = new cmcTreeModelNode( "root", NULL, this );
00362 
00363   getRootNode();
00364 
00365 //  rootNode-> save();
00366 
00367   loadChildNodes( rootNode );
00368 
00369 //  TRACE_FUNCTION
00370 //  qDebug( "root node is %p", rootNode );
00371 //  qDebug( "root node isValid %s", rootNode-> isValid()? "true":"false" );
00372 }
00373 
00374 void cmcTreeModel::loadChildNodes( cmcTreeModelNode * parentNode )
00375 {
00376 
00377 #ifdef USE_SCOPED_POINTER
00378   QScopedPointer<cmcCursor> cursor( getChildrenCursor(parentNode) );
00379   QScopedPointer<cmcRowSet> qrs( cursor-> getQueryRowSet( cursor-> rowCount() ) );
00380   parentNode-> load( qrs.data() );
00381 #else
00382   std::auto_ptr<cmcCursor> cursor( getChildrenCursor(parentNode) );
00383   std::auto_ptr<cmcRowSet> qrs( cursor-> getQueryRowSet( cursor-> rowCount() ) );
00384   parentNode-> load( qrs.get() );
00385 #endif
00386 
00387   for( int i=0; i<parentNode-> children().count(); i++ )
00388   {
00389     loadChildNodes( parentNode-> children()[i] );
00390   }
00391 }
00392 
00393 
00394 cmcTreeModel::~cmcTreeModel()
00395 {
00396   delete rootNode;
00397 }
00398 
00399 void cmcTreeModel::setRootNode( cmcTreeModelNode *node )
00400 {
00401   delete rootNode;
00402 
00403   rootNode = node;
00404 
00405   reset();
00406 
00407 }
00408 
00409 cmcTreeModelNode * cmcTreeModel::getRootNode()
00410 {
00411   if( !rootNode-> isValid() ) 
00412   {
00413 #ifdef USE_SCOPED_POINTER
00414     QScopedPointer<cmcCursor> cursor( getCursor(rootNode) );
00415     rootNode-> load(cursor.data());
00416 #else
00417     std::auto_ptr<cmcCursor> cursor( getCursor(rootNode) );
00418     rootNode-> load(cursor.get());
00419 #endif
00420   }
00421 
00422   return( rootNode );
00423 }
00424 
00425 
00426 QModelIndex cmcTreeModel::index( int row, int column, const QModelIndex &parent ) const
00427 {
00428 //  qDebug( "index %d %d %s", row, column, parent.isValid()? "true":"false" );
00429 
00430   if( !rootNode )
00431     return( QModelIndex() );
00432 
00433   cmcTreeModelNode * parentNode = nodeFromIndex(parent);
00434 
00435   return( createIndex( row, column, parentNode-> children()[row] ) );
00436 
00437 }
00438 
00439 QModelIndex cmcTreeModel::parent( const QModelIndex &child ) const
00440 {
00441   cmcTreeModelNode * node = nodeFromIndex(child);
00442 
00443   if( !node )
00444     return(QModelIndex());
00445 
00446   cmcTreeModelNode * parentNode = node-> parent();
00447 
00448   if( !parentNode )
00449     return(QModelIndex());
00450 
00451   cmcTreeModelNode * grandparentNode = parentNode-> parent();
00452 
00453   if( !grandparentNode )
00454     return(QModelIndex());
00455 
00456   int row = grandparentNode-> children().indexOf(parentNode);
00457 
00458   return( createIndex( row, child.column(), parentNode ) );
00459 
00460 }
00461 
00462 int cmcTreeModel::rowCount(const QModelIndex &parent) const
00463 {
00464   cmcTreeModelNode * parentNode = nodeFromIndex(parent);
00465 
00466   if( !parentNode ) 
00467     return(0);
00468 
00469 //  qDebug( "%s rowCount is %d", qPrintable(parentNode-> itemName()), parentNode-> children().count() );
00470 
00471   return( parentNode-> children().count() );
00472 }
00473 
00474 int cmcTreeModel::columnCount(const QModelIndex &parent) const
00475 {
00476   return( 2 );
00477 }
00478 
00479 QVariant cmcTreeModel::data(const QModelIndex &index, int role) const
00480 {
00481   if( role != Qt::DisplayRole )
00482     return( QVariant() );
00483 
00484   cmcTreeModelNode * node = nodeFromIndex(index);
00485 
00486   if( !node )
00487     return( QVariant() );
00488 
00489   if( index.column() == 0 )
00490   {
00491     return( node-> itemName() );
00492   }
00493 
00494   if( index.column() == 1 )
00495   {
00496     return( node-> itemData() );
00497   }
00498 
00499   return( QVariant() );
00500 
00501 }
00502 
00503 QVariant cmcTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
00504 {
00505   if( orientation == Qt::Horizontal &&
00506       role        == Qt::DisplayRole )
00507   {
00508     if( section == 0 )
00509     {
00510       return( "Node" );
00511     }
00512 
00513     if( section == 1 )
00514     {
00515       return( "Value" );
00516     }
00517   }
00518 
00519   return QVariant();
00520 
00521 }
00522 
00523 cmcTreeModelNode *cmcTreeModel::nodeFromIndex(const QModelIndex &index) const
00524 {
00525   if( index.isValid() )
00526   {
00527     return( static_cast<cmcTreeModelNode*>(index.internalPointer()) );
00528   }
00529 
00530   return( rootNode );
00531 }
00532 
00533 //------------------------------------------------------------------------------
00534 
00535 bool cmcTreeModel::canFetchMore ( const QModelIndex & parent ) const
00536 {
00537   return( false );
00538 
00539   /*
00540   ** Get the node for the parent item.
00541   **
00542   */
00543   cmcTreeModelNode * parentNode = nodeFromIndex(parent);
00544 
00545   qDebug( "canFetchMore %s", qPrintable(parentNode-> itemName()) );
00546 
00547   /*
00548   ** Get a cursor on the node.  If this node has a proper ID set (which it should)
00549   **  then we will get 
00550   **
00551   */
00552 #ifdef USE_SCOPED_POINTER
00553   QScopedPointer<cmcCursor> cursor( getChildrenCursor(parentNode) );
00554 #else
00555   std::auto_ptr<cmcCursor> cursor( getChildrenCursor(parentNode) );
00556 #endif
00557 
00558   if( cursor-> rowCount() == parentNode-> children().count() )
00559     return(false);
00560 
00561   return(true);
00562 
00563 }
00564 
00565 void cmcTreeModel::fetchMore ( const QModelIndex & parent )
00566 {
00567   cmcTreeModelNode * parentNode = nodeFromIndex(parent);
00568 
00569 //  qDebug( "fetchMore %s", qPrintable(parentNode-> itemName()) );
00570 
00571 #ifdef USE_SCOPED_POINTER
00572   QScopedPointer<cmcCursor> cursor( getChildrenCursor(parentNode) );
00573   QScopedPointer<cmcRowSet> qrs( cursor-> getQueryRowSet( cursor-> rowCount() ) );
00574   parentNode-> load( qrs.data() );
00575 #else
00576   std::auto_ptr<cmcCursor> cursor( getChildrenCursor(parentNode) );
00577   std::auto_ptr<cmcRowSet> qrs( cursor-> getQueryRowSet( cursor-> rowCount() ) );
00578   parentNode-> load( qrs.get() );
00579 #endif
00580 
00581 }
00582 
00583 
00584 //------------------------------------------------------------------------------
00585 
00586 
00587 cmcCursor * cmcTreeModel::getCursor( const cmcTreeModelNode  * node )
00588 {
00589   /*
00590   ** Get a cursor.  Note that the caller is responsible for destroying this cursor.
00591   **
00592   */
00593   cmcCursor * cursor = app-> db()-> getCursor( categoryName );
00594 
00595   /*
00596   ** Setup the columns on the cursor, for speed.
00597   **
00598   */
00599   cursor-> setColumn( idFieldName );
00600   cursor-> setColumn( idParentFieldName );
00601   cursor-> setColumn( itemNameFieldName );
00602   cursor-> setColumn( itemAttrFieldName );
00603   cursor-> setColumn( itemDataFieldName );
00604 
00605   /*
00606   ** If we don't actually have a node we are going to return an unfiltered cursor.
00607   **  The caller had better be prepared to deal with this!  This should never be
00608   **  called like this - trying to get a cursor on a bad node.  If that happens
00609   **  then there are other problems.
00610   **
00611   */
00612   if( node )
00613   {
00614     /*
00615     ** If our ID is set, then we can just fetch the item directly. No need
00616     **  to search by parent and item name.
00617     **
00618     */
00619     if( node-> id().isNull() )
00620     {
00621       /*
00622       ** We don't have an item ID, therefore we must search by parent ID and item
00623       **  name.  If we have a parent then we set a filter that includes reference to our
00624       **  parent ID.  If we don't have a parent, then we look for any items that
00625       **  have a parent ID set to all zeros.
00626       **
00627       */
00628       cursor-> setFilter( "[ViewFilter(1,F,," + idParentFieldName + ",Equal to," + dq(node-> idParent().toString()) + ")]" );
00629 
00630       /*
00631       ** Now that we've established a relationship to a parent item, set a filter
00632       **  that's equal to our item's name.  Items under a particular parent cannot
00633       **  have duplicate names.  This does not mean that names cannot be duplicated,
00634       **  only items with the same parent cannot be duplicated.  It's just like a
00635       **  file-system.
00636       **
00637       */
00638       cursor-> setFilter( "[ViewFilter(2,F,," + itemNameFieldName + ",Equal to," + dq(node-> itemName()) + ")]" );
00639 
00640     }
00641     else // if( node id is not null ~preferred... better to fetch by id~ )
00642     {
00643       cursor-> setFilter("[ViewFilter(1,F,," + idFieldName + ",Equal to," + dq(node-> id().toString()) + ")]");
00644 
00645     } // endif( node id is not null )
00646 
00647   } // endif( node )
00648 
00649 //  qDebug( "getCursor %ld '%s'", cursor-> rowCount(), qPrintable(cursor->filters()) );
00650 
00651   /*
00652   ** Return the cursor - filtered or not.
00653   **
00654   */
00655   return( cursor );
00656 
00657 } // endcmcCursor * cmcCdaItem::getCursor()
00658 
00659 cmcCursor * cmcTreeModel::getCursor( const QModelIndex & index )
00660 {
00661   return( getCursor(nodeFromIndex(index)) );
00662 }
00663 
00664 
00665 cmcCursor * cmcTreeModel::getChildrenCursor( const cmcTreeModelNode * parentNode )
00666 {
00667   /*
00668   ** Get a cursor.  Note that the caller is responsible for destroying this cursor.
00669   **  Note that we also call the getCursor function with a null node.  When we do this
00670   **  we get an unfiltered cursor with the columns set - which is what we want.
00671   **
00672   */
00673   cmcCursor * cursor = getCursor( NULL );
00674 
00675   /*
00676   ** We had better have a parent node.
00677   **
00678   */
00679   if( parentNode )
00680   {
00681     /*
00682     ** Filter for all items that have this item as its parent.
00683     **
00684     */
00685     cursor-> setFilter("[ViewFilter(1,F,," + idParentFieldName + ",Equal to," + dq(parentNode-> id().toString()) + ")]");
00686   }
00687   else
00688   {
00689     TRACE_MESSAGE( "warning: unable to filter due to bad 'parentNode'" );
00690   }
00691 
00692 //  qDebug( "getChildrenCursor %ld '%s'", cursor-> rowCount(), qPrintable(cursor->filters()) );
00693 
00694   /*
00695   ** Return the cursor, whatever we ended up with.
00696   **
00697   */
00698   return( cursor );
00699 
00700 }
00701 
00702 cmcCursor * cmcTreeModel::getChildrenCursor( const cmcTreeModelNode * parentNode ) const
00703 {
00704   return( const_cast<cmcTreeModel*>(this)-> getChildrenCursor(parentNode) );
00705 }
00706 
00707 
00708 cmcCursor * cmcTreeModel::getChildrenCursor( const QModelIndex & parent )
00709 {
00710   return( getChildrenCursor(nodeFromIndex(parent)) );
00711 }
00712 
00713 cmcCursor * cmcTreeModel::getChildrenCursor( const QModelIndex & parent ) const
00714 {
00715   return( const_cast<cmcTreeModel*>(this)-> getChildrenCursor(nodeFromIndex(parent)) );
00716 }
00717 
00718 
00719 
00720 
00721 
00722 
00723 
00724 
00725 
00726 
00727 
00728 } // endnamespace Commence
00729 
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties




~ ~ ~ ~ ~ ~
Source Code without Comments is like a Cranberry Garland
without the berries. Comment your Code!
 
Commence Database User Support Group Forum
http://newsgroup.showoff-db.org/
~ ~ ~ ~ ~ ~
Author: Mark Petryk
Lorimark Solutions, LLC
mark@lorimarksolutions.com