Qt Editor: Add effects to object list view + fixes

Also remove cache of C4Values and use callbacks on object and effect deletion instead.
qteditor
Sven Eberhardt 2016-02-21 15:56:07 -05:00
parent a4d28c383b
commit 771a333e21
14 changed files with 225 additions and 196 deletions

View File

@ -36,6 +36,7 @@
// * mattex pictures in dropdown
// * mat drawing cursor size preview in active viewports
// * proper viewport focus when clicked
// * crash when viewport closes on player elimination
// -----------------------------------------------
void C4ConsoleGUI::Execute() { state->Execute(); }
@ -184,7 +185,7 @@ bool C4ConsoleGUI::FileSelect(StdStrBuf *sFilename, const char * szFilter, DWORD
if (!filenames.length()) return false;
for (auto fn : filenames)
{
sFilename->Append(fn.toStdString().c_str());
sFilename->Append(fn.toUtf8());
sFilename->AppendChar('\0');
}
return true;
@ -192,7 +193,7 @@ bool C4ConsoleGUI::FileSelect(StdStrBuf *sFilename, const char * szFilter, DWORD
// Cancelled?
if (filename.length() <= 0) return false;
// File selected!
sFilename->Copy(filename.toStdString().c_str());
sFilename->Copy(filename.toUtf8());
sFilename->AppendChar('\0');
return true;
}

View File

@ -24,6 +24,7 @@
// Avoid some name conflicts
#undef LineFeed
#undef new
#undef delete
// disable OPENGL_ES
// (not necessery if Qt is compiled with -opengl desktop)
//#define QT_OPENGL_ES_2
@ -31,6 +32,8 @@
//#define QT_OPENGL_ES
//#define QT_NO_OPENGL_ES
#include <QtWidgets>
#include <qabstractitemmodel.h>
#include <QAbstractTableModel>
#include <memory>
// TODO: If we remove the other editors, state and consolegui can be merged and the relevant header go into this file

View File

@ -29,71 +29,12 @@
/* Object list information cache */
// Cached copy of displayed tree
// (Leaf nodes only validated on demand)
class C4ConsoleQtObjectListModel::Node
{
Node *parent;
C4Value data; // root data
std::vector<class Node> children; // child vector - is kept around and usually only increased on query because most updates will just re-set the same object
int32_t num_children; // children valid until this point
bool valid; // if false, re-fill node (and all children) on next data query
public:
Node(Node *parent) : parent(parent), valid(false), num_children(0) {}
void Validate();
void Invalidate() { valid = false; }
bool IsValid() const { return valid; }
void SetData(class C4PropList *to_data) { data.SetPropList(to_data); Invalidate(); }
C4PropList *GetData() const { return data.getPropList(); }
Node *_GetChild(int32_t idx) { return &children[idx]; } // unchecked child access
Node *GetParent() const { return parent; }
int32_t GetNumChildren() const { return num_children; }
void EnsureValid() { if (!valid) Validate(); }
void AddChild(C4PropList *p)
{
if (num_children >= children.size()) children.resize(num_children + 1, Node(this));
children[num_children++].SetData(p);
}
int32_t GetChildIndex(class C4PropList *data)
{
for (int32_t idx = 0; idx < num_children; ++idx)
if (_GetChild(idx)->GetData() == data)
return idx;
return -1; // not found
}
};
void C4ConsoleQtObjectListModel::Node::Validate()
{
num_children = 0;
// Fill from object list (either main list or contents)
C4Object *obj = data.getObj();
C4ObjectList *list = NULL;
if (!parent)
list = &::Objects;
else if (obj)
list = &obj->Contents;
if (list)
for (auto cobj : *list)
if (cobj && cobj->Contained == obj)
AddChild(cobj);
// Add object effects
if (obj)
{
// TODO
}
valid = true;
}
C4ConsoleQtObjectListModel::C4ConsoleQtObjectListModel(class QTreeView *view)
: selection_model(NULL), view(view), is_updating(0)
: selection_model(NULL), view(view), is_updating(0), last_row_count(0)
{
root.reset(new Node(NULL));
// default font colors
clr_deleted.setColor(QApplication::palette(view).color(QPalette::Mid));
clr_effect.setColor(QApplication::palette(view).color(QPalette::Dark));
// install the item model
view->setModel(this);
// get the selection model to control it
@ -110,12 +51,9 @@ C4ConsoleQtObjectListModel::~C4ConsoleQtObjectListModel()
void C4ConsoleQtObjectListModel::Invalidate()
{
// Invalidate everything
int32_t num_invalid = root->GetNumChildren();
root->Invalidate();
// Force redraw
QModelIndex topLeft = index(0, 0, QModelIndex());
QModelIndex bottomRight = index(num_invalid, columnCount() - 1, QModelIndex());
QModelIndex bottomRight = index(last_row_count, columnCount() - 1, QModelIndex());
emit dataChanged(topLeft, bottomRight);
emit layoutChanged();
}
@ -141,10 +79,32 @@ void C4ConsoleQtObjectListModel::SetSelection(class C4EditCursorSelection &rSele
int C4ConsoleQtObjectListModel::rowCount(const QModelIndex & parent) const
{
Node *parent_node = this->GetNodeByIndex(parent);
if (!parent_node) return 0;
parent_node->Validate();
return parent_node->GetNumChildren();
int result = 0;
if (parent.isValid())
{
// Child row count of object
C4PropList *parent_item = static_cast<C4PropList *>(parent.internalPointer());
if (!parent_item) return result;
C4Object *obj = parent_item->GetObject();
if (!obj) return result;
// Contained objects plus effects
for (C4Object *contents : obj->Contents)
if (contents && contents->Status)
++result;
for (C4Effect *fx = obj->pEffects; fx; fx = fx->pNext) if (fx->IsActive())
++result;
}
else
{
// Main object + effect count
for (C4Object *obj : ::Objects)
if (obj && obj->Status && !obj->Contained)
++result;
for (C4Effect *fx = ::Game.pGlobalEffects; fx; fx = fx->pNext) if (fx->IsActive())
++result;
last_row_count = result;
}
return result;
}
int C4ConsoleQtObjectListModel::columnCount(const QModelIndex & parent) const
@ -155,12 +115,10 @@ int C4ConsoleQtObjectListModel::columnCount(const QModelIndex & parent) const
QVariant C4ConsoleQtObjectListModel::data(const QModelIndex & index, int role) const
{
// Object list lookup is done in index(). Here we just use the pointer.
Node *node = static_cast<Node *>(index.internalPointer());
if (!node) return QVariant();
C4PropList *data = static_cast<C4PropList *>(index.internalPointer());
if (role == Qt::DisplayRole)
{
// Deleted proplist?
C4PropList *data = node->GetData();
if (!data) return QString("<deleted>");
// Prefer own name
const char *name = data->GetName();
@ -171,51 +129,76 @@ QVariant C4ConsoleQtObjectListModel::data(const QModelIndex & index, int role) c
// Fallback to effect names for effects
return QString("Fx???");
}
else if (role == Qt::ForegroundRole)
{
// Deleted proplist?
if (!data) return clr_deleted;
// Object?
C4Object *obj = data->GetObject();
if (obj) return QVariant(); // default
// Effect
return clr_effect;
}
// Nothing to show
return QVariant();
}
C4ConsoleQtObjectListModel::Node *C4ConsoleQtObjectListModel::GetNodeByIndex(const QModelIndex &index) const
{
// Find node recursively
if (!index.isValid()) return root.get();
Node *parent = GetNodeByIndex(index.parent());
// Out of range
if (index.row() < 0 || index.column() != 0) return NULL;
// Get indexed child node
parent->EnsureValid();
if (index.row() >= parent->GetNumChildren()) return NULL;
return parent->_GetChild(index.row());
}
QModelIndex C4ConsoleQtObjectListModel::index(int row, int column, const QModelIndex &parent) const
{
Node *parent_node = GetNodeByIndex(parent);
// Parent out of range?
if (!parent_node) return QModelIndex();
// Make sure it's updated
parent_node->EnsureValid();
// Current index out of range?
if (row < 0 || column != 0 || row >= parent_node->GetNumChildren()) return QModelIndex();
// This item is OK. Create an index!
return createIndex(row, column, parent_node->_GetChild(row));
if (row < 0 || column != 0) return QModelIndex();
int index = row;
// Child element or main list?
if (parent.isValid())
{
// Child of valid object?
C4PropList *parent_item = static_cast<C4PropList *>(parent.internalPointer());
if (!parent_item) return QModelIndex();
C4Object *obj = parent_item->GetObject();
if (!obj) return QModelIndex();
// Contained objects plus effects
for (C4Object *contents : obj->Contents)
if (contents && contents->Status)
if (!index--)
return createIndex(row, column, contents);
for (C4Effect *fx = obj->pEffects; fx; fx = fx->pNext) if (fx->IsActive())
if (!index--)
return createIndex(row, column, fx);
}
else
{
// Main object list
for (C4Object *obj : ::Objects)
if (obj && obj->Status && !obj->Contained)
if (!index--)
return createIndex(row, column, obj);
}
return QModelIndex(); // out of range
}
QModelIndex C4ConsoleQtObjectListModel::parent(const QModelIndex &index) const
{
// Look up parent through node structure
// Find parent of object or effect
if (!index.isValid()) return QModelIndex();
Node *node = static_cast<Node *>(index.internalPointer());
if (!node) return QModelIndex();
Node *parent = node->GetParent();
if (!parent) return QModelIndex();
// Parent must not be the root
Node *grandparent = parent->GetParent();
if (!grandparent) return QModelIndex();
// Find index of parent
for (int idx = 0; idx < grandparent->GetNumChildren(); ++idx)
if (grandparent->_GetChild(idx) == parent)
return createIndex(idx, 0, parent);
C4PropList *data = static_cast<C4PropList *>(index.internalPointer());
if (!data) return QModelIndex();
C4Object *obj = data->GetObject();
if (obj) return GetModelIndexByItem(obj->Contained);
// Parent object of effect
// TODO: Effects currently don't keep track of their owners.
// If this ever changes, lookup can be much more efficient...
C4Effect *fx = data->GetEffect();
if (fx)
{
for (C4Object *cobj : ::Objects) if (cobj && cobj->Status)
{
for (C4Effect *cfx = cobj->pEffects; cfx; cfx = cfx->pNext)
if (cfx == fx) { obj = cobj; break; }
if (obj) break;
}
return GetModelIndexByItem(obj); // returns root index for obj==NULL, i.e. global effects
}
// Can't happen
return QModelIndex();
}
@ -223,72 +206,58 @@ void C4ConsoleQtObjectListModel::OnSelectionChanged(const QItemSelection & selec
{
if (is_updating) return;
// Forward to EditCursor
Node *node; C4PropList *p;
for (const QItemSelectionRange &item_range : deselected)
if (item_range.isValid())
for (const QModelIndex &item : item_range.indexes())
if ((node = static_cast<Node *>(item.internalPointer())))
if ((p = node->GetData()))
::Console.EditCursor.RemoveFromSelection(p);
for (const QItemSelectionRange &item_range : selected)
if (item_range.isValid())
for (const QModelIndex &item : item_range.indexes())
if ((node = static_cast<Node *>(item.internalPointer())))
if ((p = node->GetData()))
::Console.EditCursor.AddToSelection(p);
C4PropList *p;
for (const QModelIndex &item : deselected.indexes())
if ((p = static_cast<C4PropList *>(item.internalPointer())))
::Console.EditCursor.RemoveFromSelection(p);
for (const QModelIndex &item : selected.indexes())
if ((p = static_cast<C4PropList *>(item.internalPointer())))
::Console.EditCursor.AddToSelection(p);
::Console.EditCursor.OnSelectionChanged(true);
}
bool C4ConsoleQtObjectListModel::GetNodeByItem(class C4PropList *item, C4ConsoleQtObjectListModel::Node **out_parent_node, int32_t *out_index) const
{
// Deduce node and parent index from item pointer
if (!item) return false;
Node *parent_node;
int32_t idx;
C4Object *obj = item->GetObject();
if (obj)
{
if (!obj->Contained)
{
// Uncontained object
parent_node = root.get();
}
else
{
// Contained object
if (!GetNodeByItem(obj->Contained, &parent_node, &idx)) return false;
parent_node = parent_node->_GetChild(idx);
}
}
else
{
// Effect
C4Effect *fx = item->GetEffect();
if (!fx) return false;
// Parent object of effect
// TODO: Effects currently don't keep track of their owners.
// If this ever changes, lookup can be much faster
for (C4Object *cobj : ::Objects)
{
for (C4Effect *cfx = obj->pEffects; cfx; cfx = cfx->pNext)
if (cfx == fx) { obj = cobj; break; }
if (obj) break;
}
if (!GetNodeByItem(obj, &parent_node, &idx)) return false;
parent_node = parent_node->_GetChild(idx);
}
parent_node->EnsureValid();
idx = parent_node->GetChildIndex(obj);
if (idx < 0) return false;
*out_index = idx;
*out_parent_node = parent_node;
return true;
}
QModelIndex C4ConsoleQtObjectListModel::GetModelIndexByItem(C4PropList *item) const
{
// Deduce position in model list from item pointer
Node *parent_node=NULL; int32_t row=0;
if (!GetNodeByItem(item, &parent_node, &row)) return QModelIndex();
return createIndex(row, 0, parent_node->_GetChild(row));
if (!item) return QModelIndex();
C4Object *obj; C4Effect *fx;
int row=0;
if ((obj = item->GetObject()))
{
const C4ObjectList *list = &::Objects;
if (obj->Contained) list = &(obj->Contained->Contents);
for (C4Object *cobj : *list)
{
if (cobj == obj) break;
if (cobj && cobj->Status) ++row;
}
}
else if ((fx = item->GetEffect()))
{
// TODO: Effects currently don't keep track of their owners.
// If this ever changes, lookup can be much more efficient...
bool found = false;
for (C4Object *cobj : ::Objects) if (cobj && cobj->Status)
{
row = 0;
for (C4Effect *cfx = cobj->pEffects; cfx; cfx = cfx->pNext)
if (cfx == fx) { obj = cobj; found = true; break; } else ++row;
if (obj) break;
}
// Also search global effect list
if (!found)
{
row = 0;
for (C4Effect *cfx = ::Game.pGlobalEffects; cfx; cfx = cfx->pNext) if (cfx->IsActive())
if (cfx == fx) { found = true; break; } else ++row;
if (!found) return QModelIndex();
}
// Add other objects on top of this index
const C4ObjectList *list = &::Objects;
if (obj) list = &obj->Contents;
for (C4Object *cobj : *list)
if (cobj && cobj->Status)
++row;
}
return createIndex(row, 0, item);
}

View File

@ -24,18 +24,17 @@
#include <C4ConsoleGUI.h> // for glew.h
#include <C4Value.h>
#include <C4ConsoleQt.h>
#include <qabstractitemmodel.h>
// Prop list view implemented as a model view
class C4ConsoleQtObjectListModel : public QAbstractItemModel
{
Q_OBJECT
class Node;
std::unique_ptr<Node> root;
class QItemSelectionModel *selection_model;
QTreeView *view;
int32_t is_updating;
mutable int32_t last_row_count;
QBrush clr_deleted, clr_effect;
public:
C4ConsoleQtObjectListModel(class QTreeView *view);
@ -48,8 +47,6 @@ public:
void SetSelection(class C4EditCursorSelection &rSelection);
private:
Node *GetNodeByIndex(const QModelIndex &index) const;
bool GetNodeByItem(class C4PropList *item, Node **out_parent_node, int32_t *out_index) const;
QModelIndex GetModelIndexByItem(class C4PropList *item) const;
protected:

View File

@ -23,7 +23,6 @@
#include <C4Include.h> // needed for automoc
#include <C4ConsoleGUI.h> // for glew.h
#include <C4ConsoleQt.h>
#include <QAbstractTableModel>
// Prop list view implemented as a model view
class C4ConsoleQtPropListModel : public QAbstractTableModel

View File

@ -114,9 +114,9 @@ void C4ConsoleViewportWidget::OnActiveChanged(bool active)
QColor bgclr = QApplication::palette(this).color(QPalette::Highlight);
QColor fontclr = QApplication::palette(this).color(QPalette::HighlightedText);
if (active)
setStyleSheet(FormatString("QDockWidget::title { background: %s; padding: 5px; } QDockWidget { color: %s; font-weight: bold; }",
bgclr.name().toStdString().c_str(),
fontclr.name().toStdString().c_str()).getData());
setStyleSheet(QString(
"QDockWidget::title { background: %1; padding: 5px; } QDockWidget { color: %2; font-weight: bold; }")
.arg(bgclr.name(), fontclr.name()));
else
setStyleSheet("");
}
@ -306,17 +306,14 @@ void C4ConsoleQtMainWindow::HelpAbout() { ::Console.HelpAbout(); }
void C4ConsoleQtMainWindow::MainConsoleEditEnter()
{
QLineEdit *main_console_edit = state->ui.consoleInputBox->lineEdit();
std::string script = main_console_edit->text().toStdString();
::Console.RegisterRecentInput(script.c_str(), C4Console::MRU_Scenario);
::Console.In(script.c_str());
::Console.RegisterRecentInput(main_console_edit->text().toUtf8(), C4Console::MRU_Scenario);
::Console.In(main_console_edit->text().toUtf8());
}
void C4ConsoleQtMainWindow::PropertyConsoleEditEnter()
{
QLineEdit *property_console_edit = state->ui.propertyInputBox->lineEdit();
std::string script = property_console_edit->text().toStdString();
//::Console.RegisterRecentInput(script.c_str(), C4Console::MRU_Object); - done by EditCursor
::Console.EditCursor.In(script.c_str());
::Console.EditCursor.In(property_console_edit->text().toUtf8());
}
@ -570,13 +567,10 @@ void C4ConsoleGUIState::PropertyDlgUpdate(C4EditCursorSelection &rSelection, boo
{
// Single object selection: Show property view + Object info in label
property_model->SetPropList(rSelection.front().getPropList());
ui.selectionInfoLabel->setText(rSelection.front().GetDataString().getData());
ui.selectionInfoLabel->setText(rSelection.front().GetDataString(0).getData());
ui.propertyTable->setVisible(true);
}
// Function update in script combo box
if (force_function_update)
SetComboItems(ui.propertyInputBox, ::Console.GetScriptSuggestions(::Console.PropertyDlgObject, C4Console::MRU_Object));
// Update object list on timer update
if (!force_function_update)
object_list_model->Invalidate();
}

View File

@ -44,8 +44,7 @@ StdStrBuf C4EditCursorSelection::GetDataString() const
{
StdStrBuf Output;
// Compose info text by selected object(s)
// Only care for objects, not other proplists, because the function is relevant for the non-qt-editor only anyway
int32_t obj_count = ObjectCount();
int32_t obj_count = size();
switch (obj_count)
{
// No selection
@ -54,8 +53,14 @@ StdStrBuf C4EditCursorSelection::GetDataString() const
break;
// One selected object
case 1:
Output.Take(GetObject()->GetDataString());
{
C4Object *obj = GetObject();
if (obj)
Output.Take(obj->GetDataString());
else
Output.Take(front().GetDataString());
break;
}
// Multiple selected objects
default:
Output.Format(LoadResStr("IDS_CNS_MULTIPLEOBJECTS"), obj_count);
@ -105,7 +110,7 @@ bool C4EditCursorSelection::ClearPointers(C4Object *obj)
bool C4EditCursorSelection::IsContained(C4PropList *obj) const
{
for (const C4Value &v : (*this)) if (obj == v.getObj()) return true;
for (const C4Value &v : (*this)) if (obj == v.getPropList()) return true;
return false;
}

View File

@ -822,6 +822,19 @@ void C4ObjectListDlg::Open()
}
}
void C4ObjectListDlg::OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container)
{
}
void C4ObjectListDlg::OnEffectAdded(C4Effect *fx)
{
}
void C4ObjectListDlg::OnEffectRemoved(C4Effect *fx)
{
}
#else
C4ObjectListDlg::C4ObjectListDlg()
@ -849,6 +862,7 @@ void C4ObjectListDlg::Update(C4EditCursorSelection &rSelection)
if (view_model) view_model->SetSelection(rSelection);
}
// Could do some crazy fine-grained updates. But updating is cheap enough...
void C4ObjectListDlg::OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk)
{
if (view_model) view_model->Invalidate();
@ -864,6 +878,21 @@ void C4ObjectListDlg::OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk)
if (view_model) view_model->Invalidate();
}
void C4ObjectListDlg::OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container)
{
if (view_model) view_model->Invalidate();
}
void C4ObjectListDlg::OnEffectAdded(C4Effect *fx)
{
if (view_model) view_model->Invalidate();
}
void C4ObjectListDlg::OnEffectRemoved(C4Effect *fx)
{
if (view_model) view_model->Invalidate();
}
#else
@ -883,6 +912,19 @@ void C4ObjectListDlg::OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk)
{
}
void C4ObjectListDlg::OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container)
{
}
void C4ObjectListDlg::OnEffectAdded(C4Effect *fx)
{
}
void C4ObjectListDlg::OnEffectRemoved(C4Effect *fx)
{
}
#endif WITH_QT_EDITOR
#endif

View File

@ -44,6 +44,9 @@ public:
virtual void OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk) override;
virtual void OnObjectAdded(C4ObjectList * pList, C4ObjectLink * pLnk) override;
virtual void OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk) override;
virtual void OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container) override;
virtual void OnEffectAdded(class C4Effect *fx) override;
virtual void OnEffectRemoved(class C4Effect *fx) override;
#ifdef USE_GTK
private:

View File

@ -129,6 +129,8 @@ C4Effect * C4Effect::New(C4Object * pForObj, C4String * szName, int32_t iPrio, i
// Update OnFire cache
if (!pEffect->IsDead() && pForObj && WildcardMatch(C4Fx_AnyFire, szName->GetCStr()))
pForObj->SetOnFire(true);
// Creation callback
if (!pEffect->IsDead()) ObjectListChangeListener.OnEffectAdded(pEffect);
return pEffect;
}
@ -181,6 +183,12 @@ void C4Effect::ClearPointers(C4Object *pObj)
while ((pEff=pEff->pNext));
}
void C4Effect::SetDead()
{
iPriority = 0;
ObjectListChangeListener.OnEffectRemoved(this);
}
C4Effect *C4Effect::Get(const char *szName, int32_t iIndex, int32_t iMaxPriority)
{
// safety

View File

@ -99,7 +99,7 @@ public:
void Denumerate(C4ValueNumbers *); // numbers to object pointers
void ClearPointers(C4Object *pObj); // clear all pointers to object - may kill some effects w/o callback, because the callback target is lost
void SetDead() { iPriority=0; } // mark effect to be removed in next execution cycle
void SetDead(); // mark effect to be removed in next execution cycle
bool IsDead() { return !iPriority; } // return whether effect is to be removed
void FlipActive() { iPriority*=-1; } // alters activation status
bool IsActive() { return iPriority>0; } // returns whether effect is active

View File

@ -1366,6 +1366,8 @@ bool C4Object::Exit(int32_t iX, int32_t iY, int32_t iR, C4Real iXDir, C4Real iYD
CloseMenu(true);
UpdateFace(true);
SetOCF();
// Object list callback (before script callbacks, because script callbacks may enter again)
ObjectListChangeListener.OnObjectContainerChanged(this, pContainer, NULL);
// Engine calls
if (fCalls) pContainer->Call(PSF_Ejection,&C4AulParSet(this));
if (fCalls) Call(PSF_Departure,&C4AulParSet(pContainer));
@ -1437,6 +1439,8 @@ bool C4Object::Enter(C4Object *pTarget, bool fCalls, bool fCopyMotion, bool *pfR
// Update container
Contained->UpdateMass();
Contained->SetOCF();
// Object list callback (before script callbacks, because script callbacks may exit again)
ObjectListChangeListener.OnObjectContainerChanged(this, NULL, Contained);
// Collection call
if (fCalls) pTarget->Call(PSF_Collection2,&C4AulParSet(this));
if (!Contained || !Contained->Status || !pTarget->Status) return true;

View File

@ -35,6 +35,9 @@ public:
virtual void OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk) = 0;
virtual void OnObjectAdded(C4ObjectList * pList, C4ObjectLink * pLnk) = 0;
virtual void OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk) = 0;
virtual void OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container) = 0;
virtual void OnEffectAdded(class C4Effect *fx) = 0;
virtual void OnEffectRemoved(class C4Effect *fx) = 0;
virtual ~C4ObjectListChangeListener() { }
};

View File

@ -147,8 +147,9 @@ StdStrBuf C4Value::GetDataString(int depth) const
const C4PropListStatic * Def = Data.PropList->IsStatic();
if (Def)
return Def->GetDataString();
C4Effect * fx = Data.PropList->GetEffect();
StdStrBuf DataString;
DataString = "{";
DataString = (fx ? "effect {" : "{");
Data.PropList->AppendDataString(&DataString, ", ", depth);
DataString.AppendChar('}');
return DataString;