/* * 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