Porting RiverWare's Q3ListViews to Qt4 QTreeViews
Phil Weinstein -- Edit 6-23-2010
Status: Draft, ready for test application to a particular Q3ListView port.

This document provides boiler-plate "templates" for replacing Q3ListView-implemented lists in RiverWare with a Qt4 QTreeView and a QAbstractItemModel implementation. This was devised from the port of the Slot/Slot-Column Q3ListView in the Configure Slots Dialog.

Sections:

  1. Overview
  2. Client Provisions
  3. Custom List Model Provisions
  4. Custom Item Data Provisions

(1) Overview

Typical Qt3 modules used to support a list:

  1. A Q3ListView instance
  2. Multiple instances of a custom Q3ListViewItem subclass (one for each row item)

Typical Qt4 modules used to support a list:

  1. A QTreeView instance
  2. One RwQSortFilterProxyModel instance, to support sorting.
  3. One instance of a custom QAbstractItemModel subclass
  4. Multiple instances of a custom "Item Data" class (one for each row item)

Click for larger image

Qt4 Source File Organization:

  1. Client "Dialog" (or "Panel") HEADER (.hpp) File -- Template-ClientDlg.hpp.txt
    1. Client Class (QDialog, QMainWindow, QFrame, QWidget, etc)
      1. Inner Class: EntityItemModel (: public QAbstractItemModel)
      2. Inner Class: EntityItemData
  2. Client "Dialog" CPP File -- Template-ClientDlg.cpp.txt
  3. EntityItemModel (and EntityItemData) CPP File -- Template-ClientDlg_EntityItemModel.cpp.txt

Substitutions for the three template files, above.
(to use, change the latter string pattern, e.g. to Slot, Account, SimObj, etc.) ...

:1,$s/Entity/Entity/g
:1,$s/entity/entity/g
:1,$s/ntities/ntities/g
:1,$s/ClientDlg/ClientDlg/g
...

(2) Client Provisions

Bolded sections' definitions or implementations are shown below. Note: Replace "entity" (and "Entity") with the entity name for items in the list, e.g. "slot", "simobj", "account", "supply", etc.

Header File
#include <QAbstractItemModel>
#include <QDialog>
#include <QList>

class QTreeView;
class RwQSortFilterProxyModel;

class ClientDlg : public QDialog
{
   Q_OBJECT

 private:
   class EntityItemModel;   // declared below. (:: QAbstractItemModel)
   class EntityItemData;    // declared below.

 private:
   QTreeView*               _entityTreeView;
   EntityItemModel*         _entityItemModel;
   RwQSortFilterProxyModel* _entitySortProxyModel;

 private:
   ClientDlg();
   virtual ~ClientDlg();

   void buildEntityTreeView();
   QList<EntityItemData> allEntityItems() const;
   QList<EntityItemData> selectedEntityItems() const;

 private slots:
   void entityTreeView_doubleClicked (const QModelIndex&);
   void entityTreeView_customContextMenuReqAtPoint (const QPoint&);
   void entityTreeView_selectionChanged (const QItemSelection& selected, 
                                         const QItemSelection& deselected);
};
 
void ClientDlg::buildEntityTreeView() 
{
   QVBoxLayout* lout = new QVBoxLayout (_ui._entityTreeViewFrame); // XXX
   lout->setObjectName ("_ui._entityTreeViewFrame lout");
   lout->setSpacing (0);
   lout->setMargin (0);

   // *********************************
   // ***  Create Entity Tree View  ***
   // *********************************

   _entityTreeView = new QTreeView (this);
   _entityTreeView->setObjectName ("_entityTreeView");
   _entityTreeView->setRootIsDecorated (false);
   _entityTreeView->setAllColumnsShowFocus (true);
   _entityTreeView->setSelectionMode (QTreeView::ExtendedSelection);
   _entityTreeView->setSortingEnabled (true);
   _entityTreeView->setIconSize (RwQPixmap16::seriesSlot().size()); // XXX
   _entityTreeView->header()->setResizeMode (QHeaderView::ResizeToContents);

   lout->addWidget (_entityTreeView);

   // *******************************************************
   // ***  Create Entity Item Model and Sort Proxy Model  ***
   // *******************************************************

   _entityItemModel = new EntityItemModel (this);
   _entityItemModel->setObjectName ("_entityItemModel");

   _entitySortProxyModel = 
      new RwQSortFilterProxyModel (this, _entityItemModel, _entityTreeView,
                                   EntityItemModel::COLUMN_COUNT);

   _entitySortProxyModel->setObjectName ("_entitySortProxyModel");
   _entitySortProxyModel->setSourceModel (_entityItemModel);
   _entitySortProxyModel->setDynamicSortFilter (true);
   _entitySortProxyModel->setSortRole (EntityItemModel::UserSortRole);

   _entityTreeView->setModel (_entitySortProxyModel);
   _entityTreeView->update();

   // *************************
   // ***  Connect Signals  ***
   // *************************

   connect (_entityTreeView, 
      SIGNAL (doubleClicked (const QModelIndex&)),
      SLOT (slotTreeView_doubleClicked (const QModelIndex&)));

   _entityTreeView->setContextMenuPolicy (Qt::CustomContextMenu);
   connect (_entityTreeView, 
      SIGNAL (customContextMenuRequested (const QPoint&)),
      SLOT (slotTreeView_customContextMenuReqAtPoint (const QPoint&)));

   QItemSelectionModel* selModel = _entityTreeView->selectionModel();

   connect (selModel,
      SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
      SLOT (slotTreeView_selectionChanged (const QItemSelection&, 
                                           const QItemSelection&) ));
}
 
QList<EntityItemData> ClientDlg::allEntityItems() const  
{
QList<EntityItemData> entityItemList;
const int rowCnt = _entityItemModel->rowCount();
for (int row = 0; row < rowCnt; ++row)
{
EntityItemData* itemDat = _entityItemModel->getItemData (row);
if (itemDat)
{
entityItemList.append (*itemDat);
}
}
return entityItemList;
}
 
QList<EntityItemData> ClientDlg::selectedEntityItems() const  
{
QList<EntityItemData> entityItemList;
const QItemSelectionModel* selModel = _entityTreeView->selectionModel();

const int rowCnt = _entitySortProxyModel->rowCount();
for (int row = 0; row < rowCnt; ++row)
{
// Process the next selected row ...
if (selModel->isRowSelected (row, QModelIndex()))
{
// Map the Sort Proxy Model's row index to the Source Model row index.
QModelIndex proxyRowIndex = _entitySortProxyModel->index (row, 0);
QModelIndex modelRowIndex =
_entitySortProxyModel->mapToSource (proxyRowIndex);
int modelRow = modelRowIndex.row();
EntityItemData* itemDat = _entityItemModel->getItemData (modelRow);
if (itemDat)
{
entityItemList.append (*itemDat);
}
}
}
return entityItemList;
}
 
void ClientDlg::entityTreeView_doubleClicked (const QModelIndex& minx)
{
QModelIndex modelRowIndex = _entitySortProxyModel->mapToSource (minx);
const int modelRow = modelRowIndex.row(); EXAMPLE ...

SlotColRef entityColRef = _entityItemModel->getSlotColRef (modelRow);
Slot* theSlot (entityColRef.entity());

// Open the dialogs for the entity
if (theSlot)
{
QGui::openSlot (theSlot);
}
}
 
void ClientDlg::entityTreeView_customContextMenuReqAtPoint (
const QPoint& widgetPos)
{
static const char* mname
("ClientDlg:: entityTreeView_customContextMenuReqAtPoint");

QTreeView* tv = _entityTreeView;

// Hack. For some reason, in Qt 4.4.3 [January 2010], the height of the
// column header is not taken into account. That's really strange.
// And strangely, the offset methods don't do the trick. They return 0.
//
//-- const QPoint adjWidPos (tv->widgetPos.x() + tv->horizontalOffset(),
//-- tv->widgetPos.y() + tv->verticalOffset());
//
QPoint adjWidPos = widgetPos;
if (!tv->isHeaderHidden())
{
QHeaderView* hdrView = tv->header();
if (hdrView)
adjWidPos += QPoint (0, hdrView->height());
}

// Map adjusted widget position to global position
const QPoint globalPos = tv->mapToGlobal (adjWidPos);
// Note: widget item and column lookup use the non-adjusted widget position.
QTreeWidgetItem* twItem = tw->itemAt (widgetPos);
const QModelIndex modelIndex = tw->indexAt (widgetPos);
const int col = modelIndex.column();

// const int twItemInx = tw->indexOfTopLevelItem (twItem);
// std::cout << mname
// << ": wid [" << widgetPos.x() << "," << widgetPos.y() << "]"
// << ", adj [" << adjWidPos.x() << "," << adjWidPos.y() << "]"
// << ", gbl [" << globalPos.x() << "," << globalPos.y() << "]"
// << ", inx " << twItemInx
// << ", col " << col
// << std::endl;

// *******************************************************
// *** QTreeWidgetItem-Based Context Menu Processing ***
// *******************************************************

QAccountTreeWidgetItem (*acctItem)
(dynamic_cast<QAccountTreeWidgetItem*>(twItem));

QMenu *contextMenu = new QMenu (this);
switch (col)
{
... ... ...

if (no context menu)
{
delete contextMenu;
contextMenu = NULL;
}

... ... ...
}

if (contextMenu)
{
contextMenu->popup (globalPos); delete contextMenu;
} }
 
void ClientDlg::entityTreeView_selectionChanged (
const QItemSelection& /*selected*/,
const QItemSelection& /*deselected*/)
{
static const char* mname ("ClientDlg::entityTreeView_selectionChanged");
static int callCnt (0);
++callCnt;

// *** Test: Query the Selection Model ***
const QItemSelectionModel* selModel = _entityTreeView->selectionModel();
const int selected = selModel->selectedRows().count();
const int total = _entityItemModel->itemCount();
selected;
total;
// std::cout << mname << " [#" << callCnt << "] "
// << selected << " of " << total << " selected."
// << std::endl;
updateEnabledness(); // XXX -- Example
updateConfigEnabledness(); // XXX -- Example
}

(3) Custom List Model Provisions

In general, the custom model class will be an inner class -- within the client dialog class. There will be one for each supported list (Qt4 QTreeView).

In header file ...
class ClientDlg::EntityItemModel : public QAbstractItemModel
{
 // *********************
 // ***  Definitions  ***
 // *********************

 public:
   enum {
     COL_OBJECT = 0,   // XXX -- EXAMPLE
     COL_SLOT,         // XXX -- EXAMPLE
     COL_SLOTCOL,      // XXX -- EXAMPLE

     ... ... ...

     COLUMN_COUNT  // number of columns
   };

   enum {
     UserSortRole = Qt::UserRole + 0
   };
 
 // ******************************
 // ***  EntityItemModel Data  ***
 // ******************************
 
 QList<EntityItemData*> _itemDataList;

 // ******************************************
 // ***  EntityItemModel Client Interface  ***
 // ******************************************

 public:
   EntityItemModel (QObject* parentObj);
   virtual ~EntityItemModel();

   int itemCount() const { return (_itemDataList.size()); }
   EntityItemData* getEntityItemData (int inx) const;
   int findEntityItemDataIndex (const EntityItemData*) const;

   void addItem (const SlotColRef& slotColRef);  // XXX -- EXAMPLE
   void markItemForDeletion (int inx);
   void deleteMarkedItems();
   void clear();

 // **************************************
 // ***  QAbstractItemModel Interface  ***
 // **************************************

 public:
   // virtual from QAbstractItemModel -- Basic Methods
   virtual QModelIndex index (int row, int col,
             const QModelIndex& parent = QModelIndex()) const;

   // virtual from QAbstractItemModel -- Display Methods
   int rowCount (const QModelIndex& parent = QModelIndex()) const;
   int columnCount (const QModelIndex& parent = QModelIndex()) const;
   QVariant data (const QModelIndex&, int role = Qt::DisplayRole) const;
   QVariant headerData (int section, Qt::Orientation,
                        int role = Qt::DisplayRole) const;

 private:
   // virtual from QAbstractItemModel -- Basic Private Methods
   QModelIndex parent (const QModelIndex& childInx) const;
   bool hasChildren (const QModelIndex& parentInx) const;

 // ******************
 // ***  Internal  ***
 // ******************

 private:
   void itemDeleted (const EntityItemData*);
};
 

ClientDlg::EntityItemModel Implementation ...

ClientDlg::EntityItemModel::EntityItemModel (QObject* parentObj)
  : QAbstractItemModel (parentObj),
    _itemDataList()
{
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
ClientDlg::EntityItemModel::~EntityItemModel()
{
   clear();
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
ClientDlg::EntityItemModel::EntityItemData* 
  ClientDlg::EntityItemModel::getEntityItemData (int inx) const
{
   const int itemListSize = _itemDataList.size();
   if ((inx >= 0) && (inx < itemListSize))
   {
      EntityItemData* itemDat = _itemDataList [inx];
      return (itemDat);
   }

   return (NULL);
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
int ClientDlg::EntityItemModel::findEntityItemDataIndex (
                                    const EntityItemData* itemDat) const
{
   if (itemDat != NULL)
   {
      const int itemListSize = _itemDataList.size();
      for (int inx = 0;  inx < itemListSize;  ++inx)
      {
         const EntityItemData* listItemDat = _itemDataList [inx];
         if (listItemDat)
         {
            if (*itemDat == *listItemDat)
            {
               return (inx);
               //--------->>
            }
         }
      }
   }

   return (-1);  // EntityItemData not found in _itemDataList
} 
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// EXAMPLE ...
void ClientDlg::EntityItemModel::addItem (const SlotColRef& slotColRef)
{
   static const char* mname ("ClientDlg::EntityItemModel::addItem");

   // std::cout << mname << ": " 
   //           << qPrintable (slotColRef.getQuotedCompleteName())
   //           << std::endl;

   int newItemCnt (0);

   // Insert normal SlotColRef item if it doesn't already exist in list.
   const int existingInx1 = findSlotColItemIndex (slotColRef, false);
   if (existingInx1 < 0)
   {
      EntityItemData* newItemDat1 = 
         new EntityItemData (this, slotColRef, false); // not isColumnMap 
   
      _itemDataList.append (newItemDat1);
      ++newItemCnt;
   }

   // If slot has a column map, create a list item to represent the column 
   // map configuration.

   const TableSlot* tslot = dynamic_cast (slotColRef.slot());
   if (tslot && tslot->hasColumnMap())
   {
      // Insert "column map" item if it doesn't already exist in list.
      const int existingInx2 = findSlotColItemIndex (slotColRef, true);
      if (existingInx2 < 0)
      { 
         EntityItemData* newItemDat2 = 
            new EntityItemData (this, slotColRef, true); // isColumnMap 

         _itemDataList.append (newItemDat2);
         ++newItemCnt;
      }
   }

   if (newItemCnt > 0)
   {
      reset();  // QAbstractItemModel
   }
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemModel::markItemForDeletion (int inx)
{
   const int cnt = _itemDataList.size();
   if ((inx >= 0) && (inx < cnt)) 
   {
      EntityItemData* itemDat = _itemDataList [inx];
      if (itemDat)
      {
         itemDat->markForDeletion (true);
      }
   }
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemModel::deleteMarkedItems()
{
   // ************************************************************
   // ***  (1) Seperate out the Items to be Saved and Deleted  ***
   // ************************************************************

   QList<EntityItemData*> saveEntityItemDataList;
   QList<EntityItemData*> delEntityItemDataList;

   const int cnt = _itemDataList.size();
   for (int inx = 0;  inx < cnt;  ++inx)
   {
      EntityItemData* itemDat = _itemDataList [inx];
      if (itemDat)
      {
         if (itemDat->markedForDeletion())
            delEntityItemDataList.append (itemDat);
         else
            saveEntityItemDataList.append (itemDat);
      }
   }

   // *********************************************
   // ***  (2) Keep only the Items to be Saved  ***
   // *********************************************

   _itemDataList = saveEntityItemDataList;
   saveEntityItemDataList.clear();

   // ********************************************
   // ***  (3) Delete the Items to be Deleted  ***
   // ********************************************

   const int delCnt = delEntityItemDataList.size();
   for (int delInx = 0;  delInx < delCnt;  ++delInx)
   {
      EntityItemData* delItemDat = delEntityItemDataList [delInx];
      if (delItemDat)
      {
         // Delete the item.  This cancels callbacks and resets the model.
         // Note that the model state (represented by _itemDataList) has 
         // already been set to the ultimate state (with all items to be 
         // deleted already removed).
         //
         delete delItemDat;
      }
   }

   delEntityItemDataList.clear();
   
   // Do one final reset.
   reset();  // QAbstractItemModel
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemModel::clear()
{
   QList<EntityItemData*> saveEntityItemDataList = _itemDataList;
   _itemDataList.clear();

   const int cnt = saveEntityItemDataList.size();
   for (int inx = 0;  inx < cnt;  ++inx)
   {
      EntityItemData* itemDat = saveEntityItemDataList [inx];
      if (itemDat)
      {
         delete itemDat;
      }
   }

   saveEntityItemDataList.clear(); 
   reset();  // QAbstractItemModel
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// **************************************
// ***  QAbstractItemModel Interface  ***
// **************************************

// virtual from QAbstractItemModel -- Basic Method
QModelIndex ClientDlg::EntityItemModel::index (int row, int col,
             const QModelIndex& parentInx /*=QModelIndex()*/) const
{
   if (hasIndex (row, col, parentInx))
   {
      return createIndex (row, col, 0);
   }

   return QModelIndex();
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// virtual from QAbstractItemModel -- Display Method
int ClientDlg::EntityItemModel::rowCount (
                     const QModelIndex& parentInx /*=QModelIndex()*/) const
{
   if (parentInx.isValid())
   {
      // For table-based models parent indices are not used.  This means
      // that if a valid parent index is provided, zero should be returned.
      return (0);
      //------->>
   }

   return (_itemDataList.size());
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// virtual from QAbstractItemModel -- Display Method
int ClientDlg::EntityItemModel::columnCount (
                     const QModelIndex& parentInx /*=QModelIndex()*/) const
{
   if (parentInx.isValid())
   {
      // For table-based models parent indices are not used.  This means
      // that if a valid parent index is provided, zero should be returned.
      return (0);
      //------->>
   }

   return (COLUMN_COUNT);
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// virtual from QAbstractItemModel -- Display Method
QVariant ClientDlg::EntityItemModel::data (const QModelIndex& index, 
                                      int role /*=Qt::DisplayRole*/) const
{
   static const QVariant NullVar;
   static const QString SP1 (" ");

   const int row = index.row();
   const int col = index.column();

   EntityItemData* itemDat (NULL);
   const int itemListSize = _itemDataList.size();
   if ((row >= 0) && (row < itemListSize))
   {
      itemDat = _itemDataList [row];
   }

   if (role == Qt::TextAlignmentRole)
   {
      static const QVariant lftAlign (Qt::AlignLeft  | Qt::AlignVCenter);
      static const QVariant rgtAlign (Qt::AlignRight | Qt::AlignVCenter);

      // switch (col)
      // {
      //    return (rgtAlign);
      //    //-------------->>
      // }

      return (lftAlign);
      //-------------->>
   }

   if (itemDat == NULL) 
   {
      return NullVar;
      //----------->>
   }

   if (role == ClientDlg::EntityItemModel::UserSortRole)
   {
      return itemDat->sortKey (col);
   }

   if (role == Qt::DisplayRole)
   {
      QString dispTxt ("");
      if (itemDat)
      {
         dispTxt = itemDat->displayText (col);
      }
      else
      {
         dispTxt = QString ("%1:%2") .arg (row) .arg (col);
      }
         
      if (!dispTxt.isEmpty())
         dispTxt += QChar (' ');

      return QVariant (dispTxt);
   }

   if (role == Qt::DecorationRole)
   {
      QPixmap pmap;

      if (itemDat)
      {
         pmap = itemDat->displayPmap (col);
      }

      if (pmap.isNull())
      {
         return NullVar;
      }

      return QVariant (pmap);    
   }

   return NullVar;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// virtual from QAbstractItemModel -- Display Method
QVariant ClientDlg::EntityItemModel::headerData (int section, Qt::Orientation,
                        int role /*=Qt::DisplayRole*/) const
{
   static const QVariant NullVar;

   if (role == Qt::DisplayRole)
   {
      QString hdrStr ("");
      switch (section)
      {
         case COL_OBJECT:   hdrStr = "Object";       break;  // XXX -- EXAMPLE
         case COL_SLOT:     hdrStr = "Slot";         break;  // XXX -- EXAMPLE
         case COL_SLOTCOL:  hdrStr = "Column";       break;  // XXX -- EXAMPLE

         ... ... ...
      }

      return QVariant (hdrStr);
   }

   return NullVar;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// virtual from QAbstractItemModel -- Basic Private Method
QModelIndex ClientDlg::EntityItemModel::parent (
                               const QModelIndex& /*childInx*/) const
{
   return QModelIndex();
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// virtual from QAbstractItemModel -- Basic Private Method
bool ClientDlg::EntityItemModel::hasChildren (const QModelIndex& parentInx) const
{
   if (parentInx.model() == this || !parentInx.isValid())
   {
      return ( (rowCount (parentInx)    > 0) &&
               (columnCount (parentInx) > 0) );
   }

   return false;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// ******************
// ***  Internal  ***
// ******************
 
void ClientDlg::EntityItemModel::itemDeleted (const EntityItemData* delItemDat)
{
   EntityItemData* mutableItemData = const_cast<EntityItemData*> (delItemDat);
   const bool itemFound = _itemDataList.removeOne (mutableItemData);
  
   if (itemFound)
   {
      reset();  // QAbstractItemModel
   }
}

(4) Custom Item Data Provisions

In general, the custom model class will be an inner class -- within the client dialog class. There will be one for each type of supported list (Qt4 QTreeView). That is, if there are multiple QTreeView's in the dialog which have the same type of items, only one Custom Item Data class is necessary for those particular QTreeViews. (Reasonably enough).

In header file ...
class ClientDlg::EntityItemData
{
  public:
   EntityItemData (EntityItemModel* parentModel, ... data ...);
   virtual ~EntityItemData();
 
  public:
   // Primary Data Fields
   ... ... ...
   ... ... ...
 
  private:
   // Secondary Data Fields (implementation support)
   EntityItemModel* _parentModel;
   bool _markedForDeletion;
   Callback* _entityCallback;  // XXX -- IF APPLICABLE
 
  public:
   double numericValue (int col, bool& supported) const;
   QString displayText (int col) const;
   QPixmap displayPmap (int col) const;
   QVariant sortKey (int col) const;
 
   void updateItemDisplay() const;
   void markForDeletion (bool doMark=true);
   bool markedForDeletion() const { return _markedForDeletion; }
 
   // equality operator: consider only Primary Data Fields
   bool operator== (const ItemData& rhs) const;
 
  private:
   // Callback handling -- XXX -- IF APPLICABLE
   void addEntityCallback();
   void deleteEntityCallback();
   int entityCallbackHandler (CallbackType, CallbackData*, void*);
};
 

ClientDlg::EntityItemData Implementation ...

ClientDlg::EntityItemData::EntityItemData (
                ClientDlg* parentModel,
                ... data ...)       // XXX -- Primary Data Values
  : ... ... ...                     // XXX -- Primary Data Field
    ... ... ...                     // XXX -- Primary Data Field
    _parentModel (parentModel),
    _markedForDeletion (false),
    _entityCallback (NULL)
{
   static const char* mname ("ClientDlg::EntityItemData ctor");

   // std::cout << mname << ": " 
   //           << qPrintable (_entityColRef.getQuotedCompleteName())
   //           << std::endl;

   addEntityCallback();
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
ClientDlg::EntityItemData::~EntityItemData()
{
   deleteEntityCallback();

   if (_parentModel)
   {
      _parentModel->itemDeleted (this);
      _parentModel = NULL;
   }
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
double ClientDlg::EntityItemData::numericValue (
                                  int col, bool& supported) const
{
   switch (col)
   {
      case EntityItemModel::COL_OBJECT:     // XXX -- EXAMPLE
      case EntityItemModel::COL_SLOT:       // XXX -- EXAMPLE
      case EntityItemModel::COL_SLOTCOL:    // XXX -- EXAMPLE
      ... ... ...

        supported = false;
        return (INVALIDVALUE);
        //------------------>>
   }

   // The indicated column supports numeric values.
   supported = true; // return parameter

   double retVal (INVALIDVALUE);  // tentative

   ... ... ...   // XXX
   ... ... ...   // XXX

   return retVal;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
QString ClientDlg::EntityItemData::displayText (int col) const
{
   static const QString emptyStr ("");
   QString retText (emptyStr);

   ... ... ...   // XXX
   ... ... ...   // XXX

   return retText;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
QPixmap ClientDlg::EntityItemData::displayPmap (int col) const
{
   QPixmap retPmap;  // isNull()

   ... ... ...   // XXX
   ... ... ...   // XXX

   return retPmap;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
QVariant ClientDlg::EntityItemData::sortKey (int col) const
{
   bool numericSupported (false);  // tentative
   const double colVal = numericValue (col, numericSupported);

   if (numericSupported)
   {
      return QVariant (colVal);
      //--------------------->>
   }

   const QString dispText = displayText (col);
   return QVariant (dispText);
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemData::updateItemDisplay() const
{
   if (_parentModel)
   {
      const int itemRow = _parentModel->findEntityItemDataIndex (this);
      const int lastCol = COLUMN_COUNT-1;
      if (itemRow >= 0)
      {
         const QModelIndex index1 = _parentModel->index (itemRow, 0);
         const QModelIndex index2 = _parentModel->index (itemRow, lastCol);
         _parentModel->emit dataChanged (index1, index2);
      }
   }
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemData::markForDeletion (bool doMark /*=true*/)
{
   _markedForDeletion = doMark;
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
// equality operator: consider only Primary Data Fields
bool ClientDlg::EntityItemData::operator== (const EntityItemData& rhs) const
{
   // return (Primary Data Fields are Equal);  // XXX
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemData::addEntityCallback()
{
   static const char (*mname)
      ("ClientDlg::EntityItemData::addEntityCallback");

   // Register Entity callbacks for both the top-level Entity and,
   // if applicable the AccSeriesEntity Column's SeriesEntity.

   Entity* entityInstance = // XXX

   // ******************************************
   // *** Regsiter Callback on the Top Entity  ***
   // ******************************************

   if (rwAssert (entityInstance != NULL) && rwAssert (_entityCallback == NULL))
   {
      // std::cout << mname << " [T] "
      //           << entityInstance->getCompleteName() << std::endl;

      MethodCb* cb =
          new MethodCb(
          this,
          &ClientDlg::EntityItemData::entityCallbackHandler,
          ANY_CALLBACK_TYPE);

      entityInstance->addCCallback (ANY_CALLBACK_TYPE, cb);
      _entityCallback = cb;
   }
}
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
void ClientDlg::EntityItemData::deleteEntityCallback()
{
   Entity* entityInstance  = _entityColRef.entity();
   Entity* compEntity = _entityColRefComposite;

   if (_entityCallback)
   {
      if (entityInstance)
      {
         entityInstance->deleteCallback (_entityCallback);
      }

      _entityCallback = NULL;
   }
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

int ClientDlg::EntityItemData::entityCallbackHandler (
                                CallbackType cbType, 
                                CallbackData* cbData, 
                                void*)
{
   static const char (*mname)
      ("ClientDlg::EntityItemData::entityCallbackHandler");

   bool doDelete (false);     // tentative
   bool doUpdateAll (false);  // tentative

   switch (cbType)
   {
      ... ... ...   // XXX
      ... ... ...   // XXX
   }

   if (doDelete)
   {
      // std::cout << mname << " DELETE " 
      //           << _entityColRef.entity()->getCompleteName()
      //           << " [" << _entityColRef.col() << "]" << std::endl;

      deleteEntityCallback();

      // SELF DELETION!
      delete this;
   }

   else if (doUpdateAll)
   {
      if (_parentModel)
      {
         _parentModel->reset();
      }
   }

   return (true);
}

--- (end) ---