Index: ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditorListCtrl.cpp =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditorListCtrl.cpp (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditorListCtrl.cpp (revision 2026) @@ -1,57 +1,91 @@ #include "stdafx.h" #include "ActorEditorListCtrl.h" #include "AtlasObject/AtlasObject.h" #include "FieldEditCtrl.h" ActorEditorListCtrl::ActorEditorListCtrl(wxWindow* parent) : DraggableListCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_HRULES | wxLC_VRULES | wxLC_SINGLE_SEL) { + #define COLOUR(name, c0, c1) \ + m_ListItemAttr_##name[0].SetBackgroundColour(wxColour c0); \ + m_ListItemAttr_##name[1].SetBackgroundColour(wxColour c1) + + const int f=0xFF, e=0xEE, c=0xCC; + COLOUR(Model, (f,f,e), (f,f,c)); + COLOUR(Texture, (f,e,e), (f,c,c)); + COLOUR(Anim, (e,f,e), (c,f,c)); + COLOUR(Prop, (e,e,f), (c,c,f)); + + #undef COLOUR + AddColumnType(_("Variant"), 100, "name", new FieldEditCtrl_Text()); AddColumnType(_("Freq"), 50, "frequency", new FieldEditCtrl_Text()); AddColumnType(_("Model"), 160, "mesh", new FieldEditCtrl_Text()); AddColumnType(_("Texture"), 160, "texture", new FieldEditCtrl_Text()); AddColumnType(_("Animations"), 250, "animations", new FieldEditCtrl_Dialog(_T("AnimListEditor"))); AddColumnType(_("Props"), 250, "props", new FieldEditCtrl_Dialog(_T("PropListEditor"))); } void ActorEditorListCtrl::Import(AtObj& in) { DeleteData(); for (AtIter group = in["group"]; group.defined(); ++group) { for (AtIter variant = group["variant"]; variant.defined(); ++variant) AddRow(variant); AtObj blank; AddRow(blank); } UpdateDisplay(); } void ActorEditorListCtrl::Export(AtObj& out) { AtObj group; for (size_t i = 0; i < m_ListData.size(); ++i) { if (IsRowBlank((int)i)) { if (! group.isNull()) out.add("group", group); group = AtObj(); } else { AtObj variant = m_ListData[i]; group.add("variant", variant); } } if (! group.isNull()) out.add("group", group); } + +wxListItemAttr* ActorEditorListCtrl::OnGetItemAttr(long item) const +{ + if (item >= 0 && item < (int)m_ListData.size()) + { + AtObj row (m_ListData[item]); + + // Colour-code the row, depending on the first non-empty column, + // and also alternate between light/dark. + + if (row["mesh"].hasContent()) + return const_cast(&m_ListItemAttr_Model[item%2]); + else if (row["texture"].hasContent()) + return const_cast(&m_ListItemAttr_Texture[item%2]); + else if (row["animations"].hasContent()) + return const_cast(&m_ListItemAttr_Anim[item%2]); + else if (row["props"].hasContent()) + return const_cast(&m_ListItemAttr_Prop[item%2]); + } + + return const_cast(&m_ListItemAttr[item%2]); +} Index: ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditorListCtrl.h =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditorListCtrl.h (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditorListCtrl.h (revision 2026) @@ -1,19 +1,26 @@ #include "DraggableListCtrl.h" #include "IAtlasExporter.h" class ActorEditor; class ActorEditorListCtrl : public DraggableListCtrl, public IAtlasExporter { friend class ActorEditor; public: ActorEditorListCtrl(wxWindow* parent); void OnUpdate(wxCommandEvent& event); + wxListItemAttr* OnGetItemAttr(long item) const; + private: void Import(AtObj& in); void Export(AtObj& out); -}; \ No newline at end of file + + wxListItemAttr m_ListItemAttr_Model[2]; + wxListItemAttr m_ListItemAttr_Texture[2]; + wxListItemAttr m_ListItemAttr_Anim[2]; + wxListItemAttr m_ListItemAttr_Prop[2]; +}; Index: ps/trunk/source/tools/atlas/AtlasUI/AtlasUI.vcproj =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/AtlasUI.vcproj (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasUI/AtlasUI.vcproj (revision 2026) @@ -1,270 +1,270 @@ Index: ps/trunk/source/tools/atlas/AtlasUI/CustomControls/DraggableListCtrl/DraggableListCtrl.cpp =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/CustomControls/DraggableListCtrl/DraggableListCtrl.cpp (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasUI/CustomControls/DraggableListCtrl/DraggableListCtrl.cpp (revision 2026) @@ -1,143 +1,141 @@ #include "stdafx.h" #include "DraggableListCtrl.h" #include "AtlasWindowCommandProc.h" #include "DraggableListCtrlCommands.h" -//DEFINE_EVENT_TYPE(wxEVT_LISTCTRL_UPDATED); - const int ScrollSpeed = 8; // when dragging off the top or bottom of the control DraggableListCtrl::DraggableListCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) : EditableListCtrl(parent, id, pos, size, style, validator, name) , m_DragSource(0) { } // TODO: // * Dragging of multiple selections? void DraggableListCtrl::OnBeginDrag(wxListEvent& WXUNUSED(event)) { CaptureMouse(); SetFocus(); } void DraggableListCtrl::OnEndDrag() { AtlasWindowCommandProc* commandProc = AtlasWindowCommandProc::GetFromParentFrame(this); commandProc->FinaliseLastCommand(); SetSelection(m_DragSource); //commandProc->Store(new DragCommand(this, m_InitialDragSource, m_DragSource)); } void DraggableListCtrl::OnItemSelected(wxListEvent& event) { // Don't respond while in drag-mode - only the initial selection // (when starting the drag operation) should be handled if (! HasCapture()) { // Remember which item is being dragged m_DragSource = event.GetIndex(); // Make sure this listctrl is in focus SetFocus(); } } void DraggableListCtrl::OnMouseCaptureChanged(wxMouseCaptureChangedEvent& WXUNUSED(event)) { OnEndDrag(); } void DraggableListCtrl::OnMouseEvent(wxMouseEvent& event) { // Only care when in drag-mode if (! HasCapture()) { event.Skip(); return; } if (event.LeftUp()) { // Finished dragging; stop responding to mouse motion ReleaseMouse(); } else if (event.Dragging()) { // Find which item the mouse is now over int flags; long dragTarget = HitTest(event.GetPosition(), flags); if (dragTarget == wxNOT_FOUND) { // Not over an item. Scroll the view up/down if the mouse is // outside the listctrl. if (flags & wxLIST_HITTEST_ABOVE) ScrollList(0, -ScrollSpeed); else if (flags & wxLIST_HITTEST_BELOW) ScrollList(0, ScrollSpeed); } else if (flags & wxLIST_HITTEST_ONITEM && dragTarget != m_DragSource) { // Move the source item to the location under the mouse AtlasWindowCommandProc* commandProc = AtlasWindowCommandProc::GetFromParentFrame(this); commandProc->Submit(new DragCommand(this, m_DragSource, dragTarget)); // and remember that the source item is now in a different place m_DragSource = dragTarget; } } else // Some other kind of event which we're not interested in - ignore it event.Skip(); } void DraggableListCtrl::OnChar(wxKeyEvent& event) { // Don't respond to the keyboard if the user is dragging things (else // the undo system might get slightly confused) if (HasCapture()) return; if (event.GetKeyCode() == WXK_DELETE) { long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); if (item != -1) { AtlasWindowCommandProc::GetFromParentFrame(this)->Submit( new DeleteCommand(this, item) ); UpdateDisplay(); } } else { event.Skip(); } } BEGIN_EVENT_TABLE(DraggableListCtrl, EditableListCtrl) EVT_LIST_BEGIN_DRAG(wxID_ANY, DraggableListCtrl::OnBeginDrag) EVT_LIST_ITEM_SELECTED(wxID_ANY, DraggableListCtrl::OnItemSelected) EVT_MOTION(DraggableListCtrl::OnMouseEvent) EVT_LEFT_UP(DraggableListCtrl::OnMouseEvent) EVT_CHAR(DraggableListCtrl::OnChar) EVT_MOUSE_CAPTURE_CHANGED(DraggableListCtrl::OnMouseCaptureChanged) END_EVENT_TABLE() Index: ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.h =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.h (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.h (revision 2026) @@ -1,82 +1,84 @@ #ifndef EDITABLELISTCTRL_H__ #define EDITABLELISTCTRL_H__ #include "wx/listctrl.h" #include "wx/arrstr.h" #include class FieldEditCtrl; class AtObj; class AtIter; class EditableListCtrl : public wxListCtrl { friend class DeleteCommand; friend class DragCommand; public: EditableListCtrl(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxLC_ICON, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxListCtrlNameStr); ~EditableListCtrl(); void OnMouseEvent(wxMouseEvent& event); void MakeSizeAtLeast(int n); long GetSelection(); void SetSelection(long item); void UpdateDisplay(); wxString GetCellString(long item, long column) const; AtObj GetCellObject(long item, long column) const; void SetCellString(long item, long column, wxString& str); void SetCellObject(long item, long column, AtObj& obj); struct ColumnData { ColumnData(const char* k, const FieldEditCtrl* c) : key(k), ctrl(c) {} const char* key; const FieldEditCtrl* ctrl; }; std::vector m_ColumnTypes; void CloneListData(std::vector& out); void SetListData(std::vector& in); void DeleteData(); private: int GetColumnAtPosition(wxPoint& pos); void GetCellRect(long row, int col, wxRect& rect); void TrimBlankEnds(); wxString OnGetItemText(long item, long column) const; wxListItemAttr* OnGetItemAttr(long item) const; protected: std::vector m_ListData; // objectkey must remain in existence for as long as this list control // exists (so you really don't want to be dynamically allocating it; // just use static constant strings) void AddColumnType(const wxString& title, int width, const char* objectkey, FieldEditCtrl* ctrl); void AddRow(AtObj& obj); void AddRow(AtIter& iter); bool IsRowBlank(int n); + wxListItemAttr m_ListItemAttr[2]; // standard+alternate colours + DECLARE_EVENT_TABLE(); }; #endif // EDITABLELISTCTRL_H__ Index: ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.cpp =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.cpp (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.cpp (revision 2026) @@ -1,238 +1,232 @@ #include "stdafx.h" #include "EditableListCtrl.h" #include "FieldEditCtrl.h" #include "AtlasObject/AtlasObject.h" #include "AtlasObject/AtlasObjectText.h" const int BlanksAtEnd = 2; EditableListCtrl::EditableListCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) : wxListCtrl(parent, id, pos, size, style | wxLC_VIRTUAL, validator, name) { + m_ListItemAttr[0].SetBackgroundColour(wxColour(0xff, 0xff, 0xff)); + m_ListItemAttr[1].SetBackgroundColour(wxColour(0xee, 0xee, 0xee)); + wxASSERT_MSG(style & wxLC_REPORT, _T("EditableListCtrl must be LC_REPORT")); UpdateDisplay(); } EditableListCtrl::~EditableListCtrl() { size_t count = m_ColumnTypes.size(); for (size_t n = 0; n < count; ++n) delete (FieldEditCtrl*)m_ColumnTypes[n].ctrl; m_ColumnTypes.clear(); } void EditableListCtrl::AddColumnType(const wxString& title, int width, const char* objectkey, FieldEditCtrl* ctrl) { int n = GetColumnCount(); wxASSERT(m_ColumnTypes.size() == (size_t) n); // check internal consistency InsertColumn(n, title, wxLIST_FORMAT_LEFT, width); m_ColumnTypes.push_back(ColumnData(objectkey, ctrl)); } void EditableListCtrl::OnMouseEvent(wxMouseEvent& event) { // Double-clicking on a cell lets the user edit it. The editing method // depends on what column the cell is in. if (event.LeftDClick()) { // Work out what cell was clicked on: wxPoint pt = event.GetPosition(); int col = GetColumnAtPosition(pt); wxCHECK2(col >= 0 && col < (int)m_ColumnTypes.size(), return); int flags; long row = HitTest(pt, flags); if (row != wxNOT_FOUND && (flags & wxLIST_HITTEST_ONITEM)) { // Calculate the exact positioning of the clicked cell wxRect rect; GetCellRect(row, col, rect); // Execute the appropriate FieldEditCtrl FieldEditCtrl* editor = (FieldEditCtrl*)m_ColumnTypes[col].ctrl; editor->StartEdit(this, rect, row, col); } } } int EditableListCtrl::GetColumnAtPosition(wxPoint& pos) { // Find the column which pos is in. // Get the origin of the table, in case it's scrolled horizontally wxRect rect; GetItemRect(0, rect); int x = rect.GetX(); // Loop through each column int numCols = GetColumnCount(); for (int i = 0; i < numCols; ++i) { // Calculate the position of this column's right-hand edge x += GetColumnWidth(i); // Test if pos was within this column (and assume it wasn't in an earlier one) if (pos.x <= x) return i; } // Point is outside the table's right edge return -1; } void EditableListCtrl::GetCellRect(long row, int col, wxRect& rect) { wxASSERT(col >= 0 && col < GetColumnCount()); wxASSERT(row >= 0 && row < GetItemCount()); GetItemRect(row, rect); for (int i = 0; i < col; ++i) rect.x += GetColumnWidth(i); rect.width = GetColumnWidth(col); } bool EditableListCtrl::IsRowBlank(int n) { - return m_ListData[n].isContentless(); + return ! m_ListData[n].hasContent(); } void EditableListCtrl::TrimBlankEnds() { while (m_ListData.size() && m_ListData.back().isNull()) m_ListData.pop_back(); } void EditableListCtrl::SetSelection(long item) { SetItemState(item, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED); } long EditableListCtrl::GetSelection() { for (long item = 0; item < GetItemCount(); ++item) if (GetItemState(item, wxLIST_STATE_SELECTED)) return item; return 0; } void EditableListCtrl::MakeSizeAtLeast(int n) { if ((int)m_ListData.size() < n) m_ListData.resize(n); } void EditableListCtrl::AddRow(AtObj& obj) { m_ListData.push_back(obj); } void EditableListCtrl::AddRow(AtIter& iter) { AtObj obj = iter; AddRow(obj); } void EditableListCtrl::UpdateDisplay() { TrimBlankEnds(); SetItemCount((int)m_ListData.size() + BlanksAtEnd); Refresh(); } void EditableListCtrl::CloneListData(std::vector& out) { out = m_ListData; } void EditableListCtrl::SetListData(std::vector& in) { m_ListData = in; } void EditableListCtrl::DeleteData() { m_ListData.clear(); } wxString EditableListCtrl::GetCellString(long item, long column) const { wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), _T("")); if (item >= (int)m_ListData.size()) return _T(""); AtObj cell = m_ListData[item][m_ColumnTypes[column].key]; return AtlasObject::ConvertToString(cell).c_str(); } AtObj EditableListCtrl::GetCellObject(long item, long column) const { wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), AtObj()); if (item >= (int)m_ListData.size()) return AtObj(); return m_ListData[item][m_ColumnTypes[column].key]; } void EditableListCtrl::SetCellString(long item, long column, wxString& str) { wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), ); MakeSizeAtLeast(item+1); m_ListData[item].set(m_ColumnTypes[column].key, str.c_str()); } void EditableListCtrl::SetCellObject(long item, long column, AtObj& obj) { wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), ); MakeSizeAtLeast(item+1); m_ListData[item].set(m_ColumnTypes[column].key, obj); } wxString EditableListCtrl::OnGetItemText(long item, long column) const { return GetCellString(item, column); } -wxListItemAttr* EditableListCtrl::OnGetItemAttr(long WXUNUSED(item)) const +wxListItemAttr* EditableListCtrl::OnGetItemAttr(long item) const { -// if (item > (int)m_ListData.size()-BlanksAtEnd) -// { -// static wxListItemAttr attr; -// static int attr_init = 0; -// if (attr_init++ == 0) -// attr.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); -// -// return &attr; -// } -// else - return NULL; + // Make the background colours of rows alternate + return const_cast(&m_ListItemAttr[item%2]); } BEGIN_EVENT_TABLE(EditableListCtrl, wxListCtrl) EVT_LEFT_DCLICK(EditableListCtrl::OnMouseEvent) END_EVENT_TABLE() Index: ps/trunk/source/tools/atlas/AtlasObject/AtlasObject.h =================================================================== --- ps/trunk/source/tools/atlas/AtlasObject/AtlasObject.h (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasObject/AtlasObject.h (revision 2026) @@ -1,137 +1,139 @@ // Public interface to almost all of AtlasObject. // (See AtlasObjectText for the rest of it). // // Tries to include as few headers as possible, to minimise its impact // on compile times. #ifndef ATLASOBJECT_H__ #define ATLASOBJECT_H__ #include // for wchar_t ////////////////////////////////////////////////////////////////////////// // Mostly-private bits: // Simple reference-counted pointer. class T must contain a reference count, // initialised to 0. An external implementation (in AtlasObjectImpl.cpp) // provides the inc_ref and dec_ref methods, so that this header file doesn't // need to know their implementations. template class AtSmartPtr { public: // Constructors AtSmartPtr() : ptr(NULL) {} explicit AtSmartPtr(T* p) : ptr(p) { inc_ref(); } // Copy constructor AtSmartPtr(const AtSmartPtr& r) : ptr(r.ptr) { inc_ref(); } // Assignment operators AtSmartPtr& operator=(T* p) { dec_ref(); ptr = p; inc_ref(); return *this; } AtSmartPtr& operator=(const AtSmartPtr& r) { dec_ref(); ptr = r.ptr; inc_ref(); return *this; } // Destructor ~AtSmartPtr() { dec_ref(); } // Allow conversion from non-const T* to const T* operator AtSmartPtr () { return AtSmartPtr(ptr); } // Override -> T* operator->() const { return ptr; } // Test whether the pointer is pointing to anything operator bool() const { return ptr!=NULL; } bool operator!() const { return ptr==NULL; } private: void inc_ref(); void dec_ref(); T* ptr; }; // A few required declarations class AtObj; class AtNode; class AtIterImpl; ////////////////////////////////////////////////////////////////////////// // Public bits: // AtIter is an iterator over AtObjs - use it like: // // for (AtIter thing = whatever["thing"]; thing.defined(); ++thing) // DoStuff(thing); // // to handle XML data like: // // // Stuff 1 // Stuff 2 // class AtIter { public: // Increment the iterator; or make it undefined, if there weren't any // AtObjs left to iterate over AtIter& operator ++ (); // Return whether this iterator has an AtObj to point to bool defined() const; + // Return whether this iterator is pointing to a non-contentless AtObj + bool hasContent() const; // Return an iterator to the children matching 'key'. (That is, children // of the AtObj currently pointed to by this iterator) const AtIter operator [] (const char* key) const; // Return the AtObj currently pointed to by this iterator operator const AtObj () const; // Return the string value of the AtObj currently pointed to by this iterator operator const wchar_t* () const; #ifdef __WXWINDOWS__ // Wrapper function around 'operator wchar_t*', for convenience in wx programs operator const wxString () const { return (const wchar_t*)*this; } #endif // Private implementation. (But not 'private:', because it's a waste of time // adding loads of friend functions) AtSmartPtr p; }; class AtObj { public: AtObj() {} AtObj(const AtObj& r) : p(r.p) {} // Return an iterator to the children matching 'key' const AtIter operator [] (const char* key) const; // Return the string value of this object operator const wchar_t* () const; // Check whether the object contains anything (even if those things are empty) bool isNull() const { return !p; } // Check recursively whether there's actually any non-empty data in the object - bool isContentless() const; + bool hasContent() const; // Add or set a child. The wchar_t* versions create a new AtObj with // the appropriate string value, then use that as the child. // // These alter the AtObj's internal pointer, and the pointed-to data is // never actually altered. Copies of this AtObj (including copies stored // inside other AtObjs) will not be affected. void add(const char* key, const wchar_t* value); void add(const char* key, AtObj& data); void set(const char* key, const wchar_t* value); void set(const char* key, AtObj& data); AtSmartPtr p; }; // Miscellaneous utility functions: namespace AtlasObject { void LoadFromXML(AtObj& obj, const wchar_t* filename); void SaveToXML(AtObj& obj, const wchar_t* filename); }; #endif // ATLASOBJECT_H__ Index: ps/trunk/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp =================================================================== --- ps/trunk/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp (revision 2025) +++ ps/trunk/source/tools/atlas/AtlasObject/AtlasObjectImpl.cpp (revision 2026) @@ -1,190 +1,198 @@ #include "AtlasObject.h" #include "AtlasObjectImpl.h" #include #include #include #define ATSMARTPTR_IMPL(T) \ template<> void AtSmartPtr::inc_ref() \ { \ if (ptr) ++ptr->refcount; \ } \ \ template<> void AtSmartPtr::dec_ref() \ { \ if (ptr && --ptr->refcount == 0) \ delete ptr; \ } // (Don't set ptr=NULL, since it should never be possible for an // unreferenced pointer to exist; I would rather see it die in debug // mode if that ever happens, instead of just silently ignoring the error.) ATSMARTPTR_IMPL(AtNode); ATSMARTPTR_IMPL(const AtNode); ATSMARTPTR_IMPL(AtIterImpl); ////////////////////////////////////////////////////////////////////////// const AtIter AtIter::operator [] (const char* key) const { if (p) return p->iter->second->getChild(key); else return AtIter(); } AtIter::operator const wchar_t* () const { if (p) return p->iter->second->value.c_str(); else return L""; } AtIter::operator const AtObj () const { if (p) { AtObj ret; ret.p = p->iter->second; return ret; } else return AtObj(); } AtIter& AtIter::operator ++ () { assert(p); // Increment the internal iterator, and stop if we've run out children // to iterate over. if (p && ++p->iter == p->iter_upperbound) p = NULL; return *this; } bool AtIter::defined() const { return (p != NULL); } +bool AtIter::hasContent() const +{ + if (p == NULL) + return false; + + return p->iter->second->hasContent(); +} + ////////////////////////////////////////////////////////////////////////// const AtIter AtObj::operator [] (const char* key) const { if (p) return p->getChild(key); else // This object doesn't exist, so return another object that doesn't exist return AtIter(); } AtObj::operator const wchar_t* () const { if (p) return p->value.c_str(); else return L""; } void AtObj::add(const char* key, AtObj& data) { if (!p) p = new AtNode(); p = p->addChild(key, data.p); } void AtObj::add(const char* key, const wchar_t* value) { const AtNode* o = new AtNode(value); if (!p) p = new AtNode(); p = p->addChild(key, AtNode::Ptr(o)); } void AtObj::set(const char* key, AtObj& data) { if (!p) p = new AtNode(); p = p->setChild(key, data.p); } void AtObj::set(const char* key, const wchar_t* value) { const AtNode* o = new AtNode(value); if (!p) p = new AtNode(); p = p->setChild(key, AtNode::Ptr(o)); } -bool AtObj::isContentless() const +bool AtObj::hasContent() const { if (! p) return true; - return ! p->hasContent(); + return p->hasContent(); } ////////////////////////////////////////////////////////////////////////// const AtIter AtNode::getChild(const char* key) const { // Find the range of matching children AtNode::child_maptype::const_iterator it = children.lower_bound(key); AtNode::child_maptype::const_iterator it_upper = children.upper_bound(key); if (it == it_upper) // No match found return AtIter(); AtIter obj; obj.p = new AtIterImpl(it, it_upper); return obj; } bool AtNode::hasContent() const { if (value.length()) return true; for (child_maptype::const_iterator it = children.begin(); it != children.end(); ++it) if (it->second->hasContent()) return true; return false; } const AtNode::Ptr AtNode::setValue(const wchar_t* value) const { AtNode* newNode = new AtNode(); newNode->children = children; newNode->value = value; return AtNode::Ptr(newNode); } const AtNode::Ptr AtNode::setChild(const char* key, const AtNode::Ptr &data) const { AtNode* newNode = new AtNode(this); newNode->children.erase(key); newNode->children.insert(AtNode::child_pairtype(key, data)); return AtNode::Ptr(newNode); } const AtNode::Ptr AtNode::addChild(const char* key, const AtNode::Ptr &data) const { AtNode* newNode = new AtNode(this); newNode->children.insert(AtNode::child_pairtype(key, data)); return AtNode::Ptr(newNode); -} \ No newline at end of file +}