Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

SceneTreeView.cpp

/*
 * SceneTreeView.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include "stdafx.h"

#include "SceneTreeView.h"

#include "swt.h"

#include "Scene.h"
#include "DuneApp.h"
#include "Node.h"
#include "Proto.h"
#include "Field.h"
#include "MFNode.h"
#include "SFNode.h"
#include "Path.h"
#include "resource.h"

class TreeNode {
public:
            TreeNode (int f, Node *n)
            { field = f; node = n; }
    int           field;          // which field of parent is this node in
    Node       *node;       // the node, or NULL if a field
};

static void
treeCallback(void *data, int type, STREEITEM item)
{
    switch (type) {
      case SW_TREE_SELECT:
      ((SceneTreeView *) data)->OnSelectionChanged(item);
      break;
      case SW_TREE_BEGIN_DRAG:
      ((SceneTreeView *) data)->OnBeginDrag(item);
      break;
    }
}

SceneTreeView::SceneTreeView(Scene *scene, SWND parent)
  : SceneView(scene, parent)
{
    int           width, height;

    swGetSize(parent, &width, &height);
    _tree = swCreateTree(0, 0, width, height, parent);
    swTreeSetClientData(_tree, this);
    swTreeSetCallback(_tree, treeCallback);
    _bitmap = swLoadBitmap(parent, IDB_NODE_ICONS);
    _bitmapItems = scene->getNumberBuildinProtos() + 3;
    _mask = swCreateMask(_bitmap, 16 * _bitmapItems, 15, 0x808000);
    swTreeSetImageList(_tree, _bitmap, _mask, 16, 15, _bitmapItems);
    swTreeSetOverlayImage(_tree, _bitmapItems - 2);
    _currentDragSource = NULL;
    _currentDragParent = NULL;
    _currentDragField = -1;
    RegisterDropTarget();
}

SceneTreeView::~SceneTreeView()
{
    UnregisterDropTarget();
    swTreeSetCurrentItem(_tree, NULL);
    DeleteItemRec(swTreeGetRootItem(_tree));
    swDestroyTree(_tree);
    swDestroyBitmap(_bitmap);
    swDestroyBitmap(_mask);
}

void SceneTreeView::renameNode(STREEITEM item, Node *node)
{
    STREEITEM child;
    for (child = swTreeGetFirstChild(_tree,item); child != NULL;
         child = swTreeGetNextItem(_tree, child)) {
        TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
        if (cinfo->node == node)
            swTreeSetItemName(_tree, child, (char*)(const char*)
                              node->getName());        
        renameNode(child, node);
    }        
}

void SceneTreeView::renameNode(STREEITEM item, RouteUpdate *routeUpdate)
{
    STREEITEM child;
    for (child = swTreeGetFirstChild(_tree,item); child != NULL;
         child = swTreeGetNextItem(_tree, child)) {
        TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
        if (cinfo->node == routeUpdate->src)
            swTreeSetItemName(_tree, child, (char*)(const char*)
                              routeUpdate->src->getName());        
        if (cinfo->node == routeUpdate->dest)
            swTreeSetItemName(_tree, child, (char*)(const char*)
                              routeUpdate->dest->getName());
        renameNode(child,routeUpdate);
    }        
}


void SceneTreeView::OnUpdate(SceneView* sender, int type, Hint *hint) 
{
    Node       *root = _scene->getRoot();
    Node           *node;
    NodeUpdate       *nodeUpdate;
    RouteUpdate      *routeUpdate;
    STREEITEM item;

    switch (type) {
      case UPDATE_ADD_ROUTE:
        routeUpdate = (RouteUpdate *) hint;
        for (item = swTreeGetRootItem(_tree); item != NULL;
             item = swTreeGetNextItem(_tree, item)) 
            renameNode(item,routeUpdate);
        break;
      case UPDATE_ALL:
      swTreeSetCurrentItem(_tree, NULL);
      DeleteItemRec(swTreeGetRootItem(_tree));
      _scene->getNodes()->clearFlag(NODE_FLAG_TOUCHED);
      _scene->getNodes()->zeroNumberUse();
      if (root != NULL) InsertNodeRec(root, -1, SW_INSERT_ROOT, NULL);
      UpdateOverlay();
        // open always the first level of scenegraph on UPDATE_ALL
        item = swTreeGetFirstChild(_tree, swTreeGetRootItem(_tree));
        if (item) {
            swTreeSetCurrentItem(_tree, item);
            swTreeSetCurrentItem(_tree, swTreeGetRootItem(_tree));
        }
      UpdateSelection();
      break;
      case UPDATE_SELECTION_NAME:
        node = _scene->getSelection()->getNode();
        for (item = swTreeGetRootItem(_tree); item != NULL;
             item = swTreeGetNextItem(_tree, item)) 
            renameNode(item, node);
        break;
      case UPDATE_SELECTION:
      UpdateSelection();
      break;
      case UPDATE_FIELD:
      break;
      case UPDATE_ADD_NODE:
      nodeUpdate = (NodeUpdate *) hint;
      UpdateAddNode(swTreeGetRootItem(_tree), nodeUpdate->node, 
                  nodeUpdate->parent, nodeUpdate->field);
      UpdateOverlay();
      break;
      case UPDATE_REMOVE_NODE:
      nodeUpdate = (NodeUpdate *) hint;
      UpdateRemoveNode(swTreeGetRootItem(_tree), nodeUpdate->node,
                   nodeUpdate->parent, nodeUpdate->field, NULL);
      UpdateOverlay();
      break;
      case UPDATE_MODE:
      case UPDATE_TIME:
      break;
    }
}

void SceneTreeView::OnSize(int width, int height)
{
    swSetSize(swTreeGetWindow(_tree), width, height);
}

bool SceneTreeView::UpdateAddNode(STREEITEM item, Node *node,
                          Node *parent, int field)
{
    STREEITEM     child, next, sib;
    bool    found = false;

    TreeNode      *info = (TreeNode *) swTreeGetItemData(_tree, item);
    if (info->node == parent) {
      int pos = parent->findChild(node, field);
      if (TheApp->GetShowAllFields() || parent->showFields()) {
          for (child = swTreeGetFirstChild(_tree, item); child != NULL;
             child = swTreeGetNextItem(_tree, child)) {
            TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
            if (!cinfo->node && cinfo->field == field) {
                item = child;
                break;
            }
          }
      } else {
          for (child = swTreeGetFirstChild(_tree, item); child != NULL;
             child = swTreeGetNextItem(_tree, child)) {
            TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
            if (cinfo->field >= field) break;
            pos++;
          }
      }
      int vpos;
      if (pos == 0) {
          vpos = SW_INSERT_FIRST_CHILD;
          sib = item;
      } else {
          vpos = SW_INSERT_AFTER;
          sib = swTreeGetFirstChild(_tree, item);
          while (--pos) {
            sib = swTreeGetNextItem(_tree, sib);
          }
      }
      item = InsertNodeRec(node, field, vpos, sib);
      swTreeDeselectAll(_tree);
      swTreeSetCurrentItem(_tree, item);
      swTreeSelectItem(_tree, item);
      found = true;
    } else {
      for (child = swTreeGetFirstChild(_tree, item); child != NULL;
           child = next) {
          next = swTreeGetNextItem(_tree, child);
          found = UpdateAddNode(child, node, parent, field);
      }
    }
    return found;
}

bool SceneTreeView::UpdateRemoveNode(STREEITEM item,
                             Node *node, Node *parent, int field,
                             Node *curParent)
{
    STREEITEM     child, next;

    TreeNode *info = (TreeNode *) swTreeGetItemData(_tree, item);
    if (info->node == node && info->field == field && curParent == parent) {
        // remove a node only once (only one USE'd Node)
        DeleteItemRec(item);
        return true;
    } else {
      if (info->node != NULL)
            curParent = info->node;
      for (child = swTreeGetFirstChild(_tree, item); child != NULL;
           child = next) {
          next = swTreeGetNextItem(_tree, child);
          if (UpdateRemoveNode(child, node, parent, field, curParent))
                return true;
      }
    }
    return false;
}

void SceneTreeView::DeleteItemRec(STREEITEM item)
{
    if (!item) return;

    STREEITEM         child, next;

    for (child = swTreeGetFirstChild(_tree, item); child != NULL;
       child = next) {
        TreeNode *info = (TreeNode *) swTreeGetItemData(_tree, child);
      if (info->node != NULL)
            if (info->node != _scene->getRoot()) 
                if (info->node->getNumberUse() > 0)
                    info->node->setNumberUse(info->node->getNumberUse() - 1);
      next = swTreeGetNextItem(_tree, child);
      DeleteItemRec(child);
    }

    delete (TreeNode *) swTreeGetItemData(_tree, item);
    swTreeDeleteItem(_tree, item);
}

void SceneTreeView::UpdateSelection()
{
    const Path       *sel = _scene->getSelection();
    Node       *node = _scene->getRoot();
    STREEITEM         item = swTreeGetRootItem(_tree);
    bool        showAllFields = TheApp->GetShowAllFields() || 
                                    node->showFields();
    if (sel == NULL) return;

    int               len = sel->getPathLen();
    const int        *path = sel->getPath();

    for (int i = 0; i < len;) {
      int       field = path[i++];
      FieldValue  *value = node->getField(field);
        if (showAllFields) {
          item = swTreeGetFirstChild(_tree, item);
          for (int j = 0; j < field; j++) {
            FieldValue *value2 = node->getField(j);
            if (value2->getType() == SFNODE || value2->getType() == MFNODE) {
                item = swTreeGetNextItem(_tree, item);
            }
          }
      }
      if (i == len) break;
        int       pos = path[i++];
      if (value->getType() == SFNODE) {
          node = ((SFNode *) value)->getValue();
          item = swTreeGetFirstChild(_tree, item);
      } else if (value->getType() == MFNODE) {
          node = ((MFNode *) value)->getValue(pos);
          item = swTreeGetFirstChild(_tree, item);
          while (pos--) item = swTreeGetNextItem(_tree, item);
      }
      if (!showAllFields) {
          for (;item != NULL; item = swTreeGetNextItem(_tree, item)) {
            TreeNode *t = (TreeNode *) swTreeGetItemData(_tree, item);
            if (t->field == field) break;
          }
      }
    }
    if (item != NULL && item != swTreeGetCurrentItem(_tree)) {
      swTreeDeselectAll(_tree);
      swTreeSetCurrentItem(_tree, item);
      swTreeSelectItem(_tree, item);
    }
}

void SceneTreeView::InsertNodeListRec(NodeList *list, int field, STREEITEM parent)
{
    if (list == NULL) return;
    for (int i = 0; i < list->size(); i++) {
      InsertNodeRec(list->get(i), field, SW_INSERT_LAST_CHILD, parent);
    }
}

void SceneTreeView::InsertChildren(STREEITEM item, Node *node)
{
    assert(node != NULL);

    STREEITEM fieldItem;
    Proto *def = node->getProto();
    bool showAllFields = TheApp->GetShowAllFields() || node->showFields();

    for (int i = 0; i < def->getNumFields(); i++) {
      Field *field = def->getField(i);
      if (field->getType() == MFNODE) {
          MFNode *value = (MFNode *) node->getField(i);
          const char *name = (const char *) field->getName();
          if (showAllFields) {
            fieldItem = swTreeInsertItem(_tree, SW_INSERT_LAST_CHILD,
                                   item, name);
            swTreeSetItemData(_tree, fieldItem, new TreeNode(i, NULL));
            swTreeSetItemImage(_tree, fieldItem, _bitmapItems - 2, _bitmapItems - 2);
            InsertNodeListRec(value->getValues(), i, fieldItem);
          } else {
            InsertNodeListRec(value->getValues(), i, item);
          }
      } else if (field->getType() == SFNODE) {
          SFNode *value = (SFNode *) node->getField(i);
          const char *name = (const char *) field->getName();
          if (showAllFields) {
            fieldItem = swTreeInsertItem(_tree, SW_INSERT_LAST_CHILD,
                                   item, name);
            swTreeSetItemData(_tree, fieldItem, new TreeNode(i, NULL));
            swTreeSetItemImage(_tree, fieldItem, _bitmapItems - 2, _bitmapItems - 2);
            InsertNodeRec(value->getValue(), i,
                        SW_INSERT_LAST_CHILD, fieldItem);
          } else {
            InsertNodeRec(value->getValue(), i, SW_INSERT_LAST_CHILD,
                        item);
          }
      }
    }
}

STREEITEM SceneTreeView::InsertNodeRec(Node *node, int field, int position,
                               STREEITEM relative)
{
    STREEITEM           item;
    const char           *name;

    if (node == NULL) return NULL;
    if (node == _scene->getRoot()) {
      name = "Scene";
    } else if (node->getName()[0]!=0) {
        name = node->getName();
    } else {
        name = node->getProto()->getName();
    }
    item = swTreeInsertItem(_tree, position, relative, name);

    int img = node->getType();
    if (node == _scene->getRoot())
        img = _bitmapItems - 1;
    swTreeSetItemImage(_tree, item, img, img);

    swTreeSetItemData(_tree, item, new TreeNode(field, node));
    swTreeSetItemCollapsed(_tree, item, node->isCollapsed());

    // fixme: find a better method to avoid a endless loop on
    //        a recursive scenegraph
    if (node->getNumberUse() > (node->getRefs() + 1))
        return NULL;
    if (node != _scene->getRoot()) 
        node->setNumberUse(node->getNumberUse() + 1);

    InsertChildren(item, node);
    node->setFlag(NODE_FLAG_TOUCHED);
    return item;
}

void SceneTreeView::OnSelectionChanged(STREEITEM item) 
{
    if (item) {
      Path *path = MakePath(item);
      _scene->setSelection(path);
      _scene->UpdateViews(this, UPDATE_SELECTION);
    }
}

void SceneTreeView::OnBeginDrag(STREEITEM item) 
{
    TreeNode         *treeNode = (TreeNode *) swTreeGetItemData(_tree, item);
    Node       *node = treeNode->node;

    if (treeNode->node) {
      STREEITEM       parentItem = swTreeGetParentItem(_tree, item);

      if (parentItem) {
          Node             *parent = ((TreeNode *) swTreeGetItemData(_tree, parentItem))->node;

          if (!parent) {
            parentItem = swTreeGetParentItem(_tree, parentItem);
            parent = ((TreeNode *) swTreeGetItemData(_tree, parentItem))->node;
          }
          _currentDragSource = node;
          _currentDragParent = parent;
          _currentDragField = treeNode->field;
          swDragDrop(_wnd, SW_DRAG_MOVE | SW_DRAG_LINK | SW_DRAG_COPY,
                   _bitmap, _mask, node->getType() * 16, 0, 16, 15);
      }
    }
}

int SceneTreeView::OnDragEnter(int x, int y, int modifiers)
{
    return OnDragOver(x, y, modifiers);
}

int SceneTreeView::OnDragOver(int x, int y, int modifiers) 
{
    int     rc = 0;

    STREEITEM target = swTreeHitTest(_tree, x, y);
    if (target) {
      TreeNode   *treeNode = (TreeNode *) swTreeGetItemData(_tree, target);
      Node     *node = treeNode->node;
      int       field = -1;
      if (!node) {
          // dragging onto a field, so we know what field
          field = treeNode->field;
          treeNode = (TreeNode *) swTreeGetItemData(_tree, 
                            swTreeGetParentItem(_tree, target));
          node = treeNode->node;
      }
      if (_currentDragSource) {
          rc = _scene->OnDragOver(_currentDragSource, _currentDragParent,
                            _currentDragField, node, field, modifiers);
          if (rc != 0) {
            swTreeSelectDropTarget(_tree, target);
          } else {
            swTreeSelectDropTarget(_tree, NULL);
          }
      } else {
          // the data came from another app
          // eventually, get the actual data object through drag & drop
      }
    }
    return rc;
}

void SceneTreeView::OnDragLeave()
{
    swTreeSelectDropTarget(_tree, NULL);
}

int SceneTreeView::OnDrop(int x, int y, int effect) 
{
    int           rc = 0;

    swTreeSelectDropTarget(_tree, NULL);
    STREEITEM target = swTreeHitTest(_tree, x, y);
    if (target) {
      TreeNode   *treeNode = (TreeNode *) swTreeGetItemData(_tree, target);
      Node     *node = treeNode->node;
      int       field = -1;
      if (!node) {
          // dragging onto a field, so we know what field
          field = treeNode->field;
          treeNode = (TreeNode *) swTreeGetItemData(_tree, swTreeGetParentItem(_tree, target));
          node = treeNode->node;
      }
      rc = _scene->OnDrop(_currentDragSource, _currentDragParent, _currentDragField,
                      node, field, effect);
      _currentDragSource = NULL;
      _currentDragParent = NULL;
      _currentDragField = -1;
    }
    return rc;
}

Path *SceneTreeView::MakePath(STREEITEM item)
{
    int           len;
    STREEITEM     p;
    TreeNode   *t, *t1;
    Node       *root = _scene->getRoot();

    t1 = (TreeNode *) swTreeGetItemData(_tree, item);
    len = t1->node ? 0 : 1;

    for (p = item; p != NULL; p = swTreeGetParentItem(_tree, p)) {
      t = (TreeNode *) swTreeGetItemData(_tree, p);
      if (t->node && t->node != root) {
          len += 2;
      }
    }
    int        *list = new int[len];

    int i = len-1;
    if (!t1->node) {
      list[i--] = t1->field;
    }
    
    for (p = item; p != NULL; p = swTreeGetParentItem(_tree, p)) {
      t = (TreeNode *) swTreeGetItemData(_tree, p);
      if (t->node && t->node != root) {
          list[i--] = GetIndex(p);
          list[i--] = t->field;
      }
    }

    Path    *path = new Path(list, len, _scene);
    delete [] list;
    return path;
}

int
SceneTreeView::GetIndex(STREEITEM item)
{
    assert (item != NULL);
    TreeNode *i = (TreeNode *) swTreeGetItemData(_tree, item);
    STREEITEM parent = swTreeGetParentItem(_tree, item);

    if (parent) {
        TreeNode *p = (TreeNode *) swTreeGetItemData(_tree, parent);

      if (!p->node) {
          // parent is a field, look up
          parent = swTreeGetParentItem(_tree, parent);
            p = (TreeNode *) swTreeGetItemData(_tree, parent);
      }
      assert(p->node != NULL);
      FieldValue *value = p->node->getField(i->field);
      if (value->getType() == MFNODE) {
          int pos = ((MFNode *) value)->getValues()->find(i->node);
          assert (pos >= 0);
          return pos;
      } else if (value->getType() == SFNODE)  {
          return 0;
      } else {
          assert(false);
          return -1;
      }
    } else {
      return 0;
    }
}

#if 0
void SceneTreeView::OnRButtonDown(UINT nFlags, CPoint point) 
{
    STREEITEM item = swTreeHitTest(point.x, point.y);

    if (item) {
      swTreeSetCurrentItem(_tree, item);
      CRect     r;
      GetWindowRect(&r);

      FancyMenu       menu, insertMenu;
      Path *path = MakePath(item);
        GetDocument()->ContextMenu(path, &menu, &insertMenu);
      menu.TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON, 
                       point.x + r.left, point.y + r.top, this);
      delete path;
    }
}
#endif

void SceneTreeView::UpdateOverlay()
{
    _scene->getNodes()->clearFlag(NODE_FLAG_TOUCHED);
    UpdateOverlayRec(swTreeGetRootItem(_tree));
}

void SceneTreeView::UpdateOverlayRec(STREEITEM item)
{
    Node    *node = ((TreeNode *) swTreeGetItemData(_tree, item))->node;

    if (node) {
      if (node->getFlag(NODE_FLAG_TOUCHED)) {
          swTreeSetFlags(_tree, item, SW_TREE_ITEM_OVERLAY,
                              SW_TREE_ITEM_OVERLAY);
      } else {
          swTreeSetFlags(_tree, item, SW_TREE_ITEM_OVERLAY, 0);
          node->setFlag(NODE_FLAG_TOUCHED);
      }
    }

    for (STREEITEM child = swTreeGetFirstChild(_tree, item); child != NULL;
       child = swTreeGetNextItem(_tree, child)) {
      UpdateOverlayRec(child);
    }
}

#if 0
void SceneTreeView::OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_TREEVIEW      *pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    STREEITEM         item = pNMTreeView->itemNew.hItem;
    TreeNode         *treeNode = (TreeNode *) GetTreeCtrl().GetItemData(item);

    if (treeNode->node) {
      if (pNMTreeView->action == TVE_COLLAPSE) {
          treeNode->node->setFlag(NODE_FLAG_COLLAPSED);
      } else if (pNMTreeView->action == TVE_EXPAND) {
          treeNode->node->clearFlag(NODE_FLAG_COLLAPSED);
      }
    }
    *pResult = 0;
}
#endif

Generated by  Doxygen 1.6.0   Back to index