1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "EntityAttributeGridTable.h"
21 
22 #include "Assets/AttributeDefinition.h"
23 #include "Assets/EntityDefinition.h"
24 #include "Model/AttributableNode.h"
25 #include "Model/Entity.h"
26 #include "Model/EntityAttributes.h"
27 #include "View/MapDocument.h"
28 #include "View/ViewUtils.h"
29 
30 #include <wx/msgdlg.h>
31 
32 namespace TrenchBroom {
33     namespace View {
AttributeRow()34         EntityAttributeGridTable::AttributeRow::AttributeRow() :
35         m_nameMutable(false),
36         m_valueMutable(false),
37         m_default(false),
38         m_maxCount(0),
39         m_count(0),
40         m_multi(false) {}
41 
AttributeRow(const String & name,const String & value,const bool nameMutable,const bool valueMutable,const String & tooltip,const bool i_default,const size_t maxCount)42         EntityAttributeGridTable::AttributeRow::AttributeRow(const String& name, const String& value, const bool nameMutable, const bool valueMutable, const String& tooltip, const bool i_default, const size_t maxCount) :
43         m_name(name),
44         m_value(value),
45         m_nameMutable(nameMutable),
46         m_valueMutable(valueMutable),
47         m_tooltip(tooltip),
48         m_default(i_default),
49         m_maxCount(maxCount),
50         m_count(1),
51         m_multi(false) {
52             assert(!m_default || m_valueMutable);
53         }
54 
name() const55         const String& EntityAttributeGridTable::AttributeRow::name() const {
56             return m_name;
57         }
58 
value() const59         const String& EntityAttributeGridTable::AttributeRow::value() const {
60             return m_value;
61         }
62 
nameMutable() const63         bool EntityAttributeGridTable::AttributeRow::nameMutable() const {
64             return m_nameMutable;
65         }
66 
valueMutable() const67         bool EntityAttributeGridTable::AttributeRow::valueMutable() const {
68             return m_valueMutable;
69         }
70 
tooltip() const71         const String& EntityAttributeGridTable::AttributeRow::tooltip() const {
72             return m_multi ? EmptyString : m_tooltip;
73         }
74 
isDefault() const75         bool EntityAttributeGridTable::AttributeRow::isDefault() const {
76             return m_default;
77         }
78 
merge(const String & i_value,const bool nameMutable,const bool valueMutable)79         void EntityAttributeGridTable::AttributeRow::merge(const String& i_value, const bool nameMutable, const bool valueMutable) {
80             m_multi |= (m_value != i_value);
81             m_nameMutable &= nameMutable;
82             m_valueMutable &= valueMutable;
83             m_default = false;
84             ++m_count;
85         }
86 
multi() const87         bool EntityAttributeGridTable::AttributeRow::multi() const {
88             return m_multi;
89         }
90 
subset() const91         bool EntityAttributeGridTable::AttributeRow::subset() const {
92             return m_count < m_maxCount;
93         }
94 
reset()95         void EntityAttributeGridTable::AttributeRow::reset() {
96             m_count = m_maxCount;
97             m_multi = false;
98         }
99 
totalRowCount() const100         size_t EntityAttributeGridTable::RowManager::totalRowCount() const {
101             return m_rows.size();
102         }
103 
defaultRowCount() const104         size_t EntityAttributeGridTable::RowManager::defaultRowCount() const {
105             return m_defaultRowCount;
106         }
107 
attributeRowCount() const108         size_t EntityAttributeGridTable::RowManager::attributeRowCount() const {
109             return totalRowCount() - defaultRowCount();
110         }
111 
isAttributeRow(const size_t rowIndex) const112         bool EntityAttributeGridTable::RowManager::isAttributeRow(const size_t rowIndex) const {
113             return !isDefaultRow(rowIndex);
114         }
115 
isDefaultRow(const size_t rowIndex) const116         bool EntityAttributeGridTable::RowManager::isDefaultRow(const size_t rowIndex) const {
117             assert(rowIndex < totalRowCount());
118             return m_rows[rowIndex].isDefault();
119         }
120 
indexOf(const String & name) const121         size_t EntityAttributeGridTable::RowManager::indexOf(const String& name) const {
122             AttributeRow::List::const_iterator propIt = findRow(m_rows, name);
123             if (propIt != m_rows.end())
124                 return static_cast<size_t>(std::distance(m_rows.begin(), propIt));
125             return totalRowCount();
126         }
127 
name(const size_t rowIndex) const128         const String& EntityAttributeGridTable::RowManager::name(const size_t rowIndex) const {
129             assert(rowIndex < totalRowCount());
130             return m_rows[rowIndex].name();
131         }
132 
value(const size_t rowIndex) const133         const String& EntityAttributeGridTable::RowManager::value(const size_t rowIndex) const {
134             assert(rowIndex < totalRowCount());
135             const AttributeRow& row = m_rows[rowIndex];
136             return row.multi() ? EmptyString : row.value();
137         }
138 
nameMutable(const size_t rowIndex) const139         bool EntityAttributeGridTable::RowManager::nameMutable(const size_t rowIndex) const {
140             assert(rowIndex < totalRowCount());
141             return m_rows[rowIndex].nameMutable();
142         }
143 
valueMutable(const size_t rowIndex) const144         bool EntityAttributeGridTable::RowManager::valueMutable(const size_t rowIndex) const {
145             assert(rowIndex < totalRowCount());
146             return m_rows[rowIndex].valueMutable();
147         }
148 
tooltip(const size_t rowIndex) const149         const String& EntityAttributeGridTable::RowManager::tooltip(const size_t rowIndex) const {
150             assert(rowIndex < totalRowCount());
151             return m_rows[rowIndex].tooltip();
152         }
153 
multi(const size_t rowIndex) const154         bool EntityAttributeGridTable::RowManager::multi(const size_t rowIndex) const {
155             assert(rowIndex < totalRowCount());
156             return m_rows[rowIndex].multi();
157         }
158 
subset(const size_t rowIndex) const159         bool EntityAttributeGridTable::RowManager::subset(const size_t rowIndex) const {
160             assert(rowIndex < totalRowCount());
161             return m_rows[rowIndex].subset();
162         }
163 
names(const size_t rowIndex,const size_t count) const164         const StringList EntityAttributeGridTable::RowManager::names(const size_t rowIndex, const size_t count) const {
165             assert(rowIndex + count <= totalRowCount());
166 
167             StringList result(count);
168             for (size_t i = 0; i < count; ++i)
169                 result[i] = m_rows[rowIndex + i].name();
170             return result;
171         }
172 
updateRows(const Model::AttributableNodeList & attributables,const bool showDefaultRows)173         void EntityAttributeGridTable::RowManager::updateRows(const Model::AttributableNodeList& attributables, const bool showDefaultRows) {
174             m_rows.clear();
175             m_defaultRowCount = 0;
176 
177             Model::AttributableNodeList::const_iterator attriutableIt, attributableEnd;
178             Model::EntityAttribute::List::const_iterator attributeIt, attributeEnd;
179 
180             for (attriutableIt = attributables.begin(),
181                  attributableEnd = attributables.end();
182                  attriutableIt != attributableEnd;
183                  ++attriutableIt) {
184 
185                 const Model::AttributableNode* attributable = *attriutableIt;
186                 const Model::EntityAttribute::List& attributes = attributable->attributes();
187                 for (attributeIt = attributes.begin(),
188                      attributeEnd = attributes.end();
189                      attributeIt != attributeEnd;
190                      ++attributeIt) {
191 
192                     const Model::EntityAttribute& attribute = *attributeIt;
193                     const Assets::AttributeDefinition* attributeDefinition = attribute.definition();
194 
195                     const bool nameMutable = attributable->isAttributeNameMutable(attribute.name());
196                     const bool valueMutable = attributable->isAttributeValueMutable(attribute.value());
197 
198                     AttributeRow::List::iterator rowIt = findRow(m_rows, attribute.name());
199                     if (rowIt != m_rows.end()) {
200                         rowIt->merge(attribute.value(), nameMutable, valueMutable);
201                     } else {
202                         const String tooltip = Assets::AttributeDefinition::safeFullDescription(attributeDefinition);
203                         m_rows.push_back(AttributeRow(attribute.name(), attribute.value(),
204                                                       nameMutable, valueMutable,
205                                                       tooltip, false, attributables.size()));
206                     }
207                 }
208             }
209 
210             if (showDefaultRows) {
211                 const Assets::EntityDefinition* definition = Model::AttributableNode::selectEntityDefinition(attributables);
212                 if (definition != NULL) {
213                     const Assets::AttributeDefinitionList& attributeDefs = definition->attributeDefinitions();
214                     Assets::AttributeDefinitionList::const_iterator definitionIt, definitionEnd;
215                     for (definitionIt = attributeDefs.begin(),
216                          definitionEnd = attributeDefs.end();
217                          definitionIt != definitionEnd;
218                          ++definitionIt) {
219 
220                         const Assets::AttributeDefinitionPtr propertyDef = *definitionIt;
221                         const String& name = propertyDef->name();
222 
223                         if (findRow(m_rows, name) != m_rows.end())
224                             continue;
225 
226                         const String value = Assets::AttributeDefinition::defaultValue(*propertyDef);
227                         const String tooltip = Assets::AttributeDefinition::safeFullDescription(propertyDef.get());
228                         m_rows.push_back(AttributeRow(name, value, false, true, tooltip, true, attributables.size()));
229                         ++m_defaultRowCount;
230                     }
231                 }
232             }
233         }
234 
insertRows(const size_t rowIndex,const size_t count,const Model::AttributableNodeList & attributables)235         StringList EntityAttributeGridTable::RowManager::insertRows(const size_t rowIndex, const size_t count, const Model::AttributableNodeList& attributables) {
236             assert(rowIndex <= attributeRowCount());
237 
238             const StringList attributeNames = newAttributeNames(count, attributables);
239             assert(attributeNames.size() == count);
240 
241             AttributeRow::List::iterator entryIt = m_rows.begin();
242             std::advance(entryIt, rowIndex);
243             for (size_t i = 0; i < count; i++) {
244                 entryIt = m_rows.insert(entryIt, AttributeRow(attributeNames[i], "", true, true, "", false, attributables.size()));
245                 entryIt->reset();
246                 std::advance(entryIt, 1);
247             }
248 
249             return attributeNames;
250         }
251 
deleteRows(const size_t rowIndex,const size_t count)252         void EntityAttributeGridTable::RowManager::deleteRows(const size_t rowIndex, const size_t count) {
253             assert(rowIndex + count <= attributeRowCount());
254 
255             AttributeRow::List::iterator first = m_rows.begin();
256             AttributeRow::List::iterator last = first;
257             std::advance(first, rowIndex);
258             std::advance(last, rowIndex + count);
259             m_rows.erase(first, last);
260         }
261 
findRow(AttributeRow::List & rows,const String & name)262         EntityAttributeGridTable::AttributeRow::List::iterator EntityAttributeGridTable::RowManager::findRow(AttributeRow::List& rows, const String& name) {
263             AttributeRow::List::iterator it, end;
264             for (it = rows.begin(), end = rows.end(); it != end; ++it) {
265                 const AttributeRow& row = *it;
266                 if (row.name() == name)
267                     return it;
268             }
269             return end;
270         }
271 
findRow(const AttributeRow::List & rows,const String & name)272         EntityAttributeGridTable::AttributeRow::List::const_iterator EntityAttributeGridTable::RowManager::findRow(const AttributeRow::List& rows, const String& name) {
273             AttributeRow::List::const_iterator it, end;
274             for (it = rows.begin(), end = rows.end(); it != end; ++it) {
275                 const AttributeRow& row = *it;
276                 if (row.name() == name)
277                     return it;
278             }
279             return end;
280         }
281 
newAttributeNames(const size_t count,const Model::AttributableNodeList & attributables) const282         StringList EntityAttributeGridTable::RowManager::newAttributeNames(const size_t count, const Model::AttributableNodeList& attributables) const {
283             StringList result;
284             result.reserve(count);
285 
286             size_t index = 1;
287             for (size_t i = 0; i < count; ++i) {
288                 while (true) {
289                     StringStream nameStream;
290                     nameStream << "property " << index;
291 
292                     bool indexIsFree = true;
293                     Model::AttributableNodeList::const_iterator it, end;
294                     for (it = attributables.begin(), end = attributables.end(); it != end && indexIsFree; ++it) {
295                         const Model::AttributableNode& attributable = **it;
296                         indexIsFree = !attributable.hasAttribute(nameStream.str());
297                     }
298 
299                     if (indexIsFree) {
300                         result.push_back(nameStream.str());
301                         break;
302                     }
303 
304                     ++index;
305                 }
306             }
307             return result;
308         }
309 
EntityAttributeGridTable(MapDocumentWPtr document)310         EntityAttributeGridTable::EntityAttributeGridTable(MapDocumentWPtr document) :
311         m_document(document),
312         m_rows(),
313         m_ignoreUpdates(false),
314         m_showDefaultRows(true),
315         m_readonlyCellColor(wxColor(224, 224, 224)),
316         m_specialCellColor(wxColor(128, 128, 128)) {}
317 
GetNumberRows()318         int EntityAttributeGridTable::GetNumberRows() {
319             return static_cast<int>(m_rows.totalRowCount());
320         }
321 
GetNumberAttributeRows() const322         int EntityAttributeGridTable::GetNumberAttributeRows() const {
323             return static_cast<int>(m_rows.attributeRowCount());
324         }
325 
GetNumberCols()326         int EntityAttributeGridTable::GetNumberCols() {
327             return 2;
328         }
329 
GetValue(const int row,const int col)330         wxString EntityAttributeGridTable::GetValue(const int row, const int col) {
331             // Fixes a problem when the user deselects everything while editing an entity property.
332             if (row < 0 || col < 0)
333                 return wxEmptyString;
334 
335             assert(row >= 0 && row < GetRowsCount());
336             assert(col >= 0 && col < GetColsCount());
337 
338             const size_t rowIndex = static_cast<size_t>(row);
339             if (col == 0)
340                 return m_rows.name(rowIndex);
341             return m_rows.value(rowIndex);
342         }
343 
SetValue(const int row,const int col,const wxString & value)344         void EntityAttributeGridTable::SetValue(const int row, const int col, const wxString& value) {
345             assert(row >= 0 && row < GetRowsCount());
346             assert(col >= 0 && col < GetColsCount());
347 
348             MapDocumentSPtr document = lock(m_document);
349 
350             const size_t rowIndex = static_cast<size_t>(row);
351             const Model::AttributableNodeList attributables = document->allSelectedAttributableNodes();
352             assert(!attributables.empty());
353 
354             // Ignoring the updates here fails if the user changes the entity classname because in that
355             // case, we must really refresh everything from the entity.
356             // const SetBool ignoreUpdates(m_ignoreUpdates);
357             if (col == 0)
358                 renameAttribute(rowIndex, value.ToStdString(), attributables);
359             else
360                 updateAttribute(rowIndex, value.ToStdString(), attributables);
361         }
362 
Clear()363         void EntityAttributeGridTable::Clear() {
364             DeleteRows(0, m_rows.totalRowCount());
365         }
366 
InsertRows(const size_t pos,const size_t numRows)367         bool EntityAttributeGridTable::InsertRows(const size_t pos, const size_t numRows) {
368             assert(pos <= m_rows.totalRowCount());
369 
370             MapDocumentSPtr document = lock(m_document);
371 
372             const Model::AttributableNodeList attributables = document->allSelectedAttributableNodes();
373             assert(!attributables.empty());
374 
375             const StringList newKeys = m_rows.insertRows(pos, numRows, attributables);
376 
377             const SetBool ignoreUpdates(m_ignoreUpdates);
378 
379             const Transaction transaction(document);
380             StringList::const_iterator it, end;
381             for (it = newKeys.begin(), end = newKeys.end(); it != end; ++it) {
382                 const String& name = *it;
383                 document->setAttribute(name, "");
384             }
385 
386             notifyRowsInserted(pos, numRows);
387 
388             return true;
389         }
390 
AppendRows(const size_t numRows)391         bool EntityAttributeGridTable::AppendRows(const size_t numRows) {
392             return InsertRows(m_rows.totalRowCount(), numRows);
393         }
394 
DeleteRows(const size_t pos,size_t numRows)395         bool EntityAttributeGridTable::DeleteRows(const size_t pos, size_t numRows) {
396             // TODO: when deleting a property that has a default value in the property definition, re-add it to the list
397             // of default properties...
398 
399             if (pos >= m_rows.totalRowCount())
400                 return false;
401 
402             numRows = std::min(m_rows.totalRowCount() - pos, numRows);
403             assert(pos + numRows <= m_rows.totalRowCount());
404 
405             MapDocumentSPtr document = lock(m_document);
406 
407             const Model::AttributableNodeList attributables = document->allSelectedAttributableNodes();
408             assert(!attributables.empty());
409 
410             const StringList names = m_rows.names(pos, numRows);
411             assert(names.size() == numRows);
412 
413             const SetBool ignoreUpdates(m_ignoreUpdates);
414 
415             Transaction transaction(document, StringUtils::safePlural(numRows, "Remove Attribute", "Remove Attributes"));
416 
417             bool success = true;
418             for (size_t i = 0; i < numRows && success; i++)
419                 success = document->removeAttribute(names[i]);
420 
421             if (!success) {
422                 transaction.rollback();
423                 return false;
424             }
425 
426             m_rows.deleteRows(pos, numRows);
427             notifyRowsDeleted(pos, numRows);
428             return true;
429         }
430 
GetColLabelValue(const int col)431         wxString EntityAttributeGridTable::GetColLabelValue(const int col) {
432             assert(col >= 0 && col < GetColsCount());
433             if (col == 0)
434                 return "Key";
435             return "Value";
436         }
437 
GetAttr(const int row,const int col,const wxGridCellAttr::wxAttrKind kind)438         wxGridCellAttr* EntityAttributeGridTable::GetAttr(const int row, const int col, const wxGridCellAttr::wxAttrKind kind) {
439             if (row < 0 || row >= GetRowsCount() ||
440                 col < 0 || col >= GetColsCount())
441                 return NULL;
442 
443             const size_t rowIndex = static_cast<size_t>(row);
444             wxGridCellAttr* attr = wxGridTableBase::GetAttr(row, col, kind);
445             if (attr == NULL)
446                 attr = new wxGridCellAttr();
447 
448             if (col == 0) {
449                 if (m_rows.isDefaultRow(rowIndex)) {
450                     attr->SetFont(GetView()->GetFont().MakeItalic());
451                     attr->SetReadOnly();
452                 } else {
453                     attr->SetFont(GetView()->GetFont());
454 
455                     if (!m_rows.nameMutable(rowIndex)) {
456                         attr->SetReadOnly(true);
457                         attr->SetBackgroundColour(m_readonlyCellColor);
458                     } else if (m_rows.subset(rowIndex)) {
459                         attr->SetTextColour(m_specialCellColor);
460                     }
461                 }
462             } else if (col == 1) {
463                 if (m_rows.isDefaultRow(rowIndex)) {
464                     attr->SetFont(GetView()->GetFont().MakeItalic());
465                 } else {
466                     attr->SetFont(GetView()->GetFont());
467 
468                     if (!m_rows.valueMutable(rowIndex)) {
469                         attr->SetReadOnly(true);
470                         attr->SetBackgroundColour(m_readonlyCellColor);
471                     }
472                     if (m_rows.multi(rowIndex))
473                         attr->SetTextColour(m_specialCellColor);
474                 }
475             }
476             return attr;
477         }
478 
update()479         void EntityAttributeGridTable::update() {
480             if (m_ignoreUpdates)
481                 return;
482 
483             MapDocumentSPtr document = lock(m_document);
484             const size_t oldRowCount = m_rows.totalRowCount();
485             m_rows.updateRows(document->allSelectedAttributableNodes(), m_showDefaultRows);
486             const size_t newRowCount = m_rows.totalRowCount();
487 
488             if (oldRowCount < newRowCount)
489                 notifyRowsAppended(newRowCount - oldRowCount);
490             else if (oldRowCount > newRowCount)
491                 notifyRowsDeleted(oldRowCount - 1, oldRowCount - newRowCount);
492             notifyRowsUpdated(0, newRowCount);
493         }
494 
tooltip(const wxGridCellCoords cellCoords) const495         String EntityAttributeGridTable::tooltip(const wxGridCellCoords cellCoords) const {
496             if (cellCoords.GetRow() < 0 || cellCoords.GetRow() >= GetRowsCount())
497                 return "";
498 
499             const size_t rowIndex = static_cast<size_t>(cellCoords.GetRow());
500             return m_rows.tooltip(rowIndex);
501         }
502 
attributeName(const int row) const503         Model::AttributeName EntityAttributeGridTable::attributeName(const int row) const {
504             if (row < 0 || row >= static_cast<int>(m_rows.totalRowCount()))
505                 return "";
506             return m_rows.name(static_cast<size_t>(row));
507         }
508 
rowForName(const Model::AttributeName & name) const509         int EntityAttributeGridTable::rowForName(const Model::AttributeName& name) const {
510             const size_t index = m_rows.indexOf(name);
511             if (index >= m_rows.totalRowCount())
512                 return -1;
513             return static_cast<int>(index);
514         }
515 
canRemove(const int row)516         bool EntityAttributeGridTable::canRemove(const int row) {
517             if (row < 0 || row >= GetNumberAttributeRows())
518                 return false;
519             const size_t index = static_cast<size_t>(row);
520             return m_rows.nameMutable(index) && m_rows.valueMutable(index);
521         }
522 
showDefaultRows() const523         bool EntityAttributeGridTable::showDefaultRows() const {
524             return m_showDefaultRows;
525         }
526 
setShowDefaultRows(const bool showDefaultRows)527         void EntityAttributeGridTable::setShowDefaultRows(const bool showDefaultRows) {
528             if (showDefaultRows == m_showDefaultRows)
529                 return;
530             m_showDefaultRows = showDefaultRows;
531             update();
532         }
533 
renameAttribute(const size_t rowIndex,const String & newName,const Model::AttributableNodeList & attributables)534         void EntityAttributeGridTable::renameAttribute(const size_t rowIndex, const String& newName, const Model::AttributableNodeList& attributables) {
535             assert(rowIndex < m_rows.attributeRowCount());
536 
537             const String& oldName = m_rows.name(rowIndex);
538             if (!m_rows.nameMutable(rowIndex)) {
539                 wxString msg;
540                 msg << "Cannot rename attribute '" << oldName << "' to '" << newName << "'";
541                 wxMessageBox(msg, "Error", wxOK | wxICON_ERROR | wxCENTRE, GetView());
542                 return;
543             }
544 
545             MapDocumentSPtr document = lock(m_document);
546             if (document->renameAttribute(oldName, newName)) {
547                 m_rows.updateRows(attributables, m_showDefaultRows);
548                 notifyRowsUpdated(0, m_rows.totalRowCount());
549             }
550         }
551 
updateAttribute(const size_t rowIndex,const String & newValue,const Model::AttributableNodeList & attributables)552         void EntityAttributeGridTable::updateAttribute(const size_t rowIndex, const String& newValue, const Model::AttributableNodeList& attributables) {
553             assert(rowIndex < m_rows.totalRowCount());
554 
555             const String& name = m_rows.name(rowIndex);
556             Model::AttributableNodeList::const_iterator it, end;
557             for (it = attributables.begin(), end = attributables.end(); it != end; ++it) {
558                 const Model::AttributableNode* attributable = *it;
559                 if (attributable->hasAttribute(name)) {
560                     if (!attributable->canAddOrUpdateAttribute(name, newValue)) {
561                         const Model::AttributeValue& oldValue = attributable->attribute(name);
562                         wxString msg;
563                         msg << "Cannot change property value '" << oldValue << "' to '" << newValue << "'";
564                         wxMessageBox(msg, "Error", wxOK | wxICON_ERROR | wxCENTRE, GetView());
565                         return;
566                     }
567                 }
568             }
569 
570             MapDocumentSPtr document = lock(m_document);
571             if (document->setAttribute(name, newValue)) {
572                 m_rows.updateRows(attributables, m_showDefaultRows);
573                 notifyRowsUpdated(0, m_rows.totalRowCount());
574             }
575         }
576 
notifyRowsUpdated(size_t pos,size_t numRows)577         void EntityAttributeGridTable::notifyRowsUpdated(size_t pos, size_t numRows) {
578             if (GetView() != NULL) {
579                 wxGridTableMessage message(this, wxGRIDTABLE_REQUEST_VIEW_GET_VALUES,
580                                            static_cast<int>(pos),
581                                            static_cast<int>(numRows));
582                 GetView()->ProcessTableMessage(message);
583             }
584         }
585 
notifyRowsInserted(size_t pos,size_t numRows)586         void EntityAttributeGridTable::notifyRowsInserted(size_t pos, size_t numRows) {
587             if (GetView() != NULL) {
588                 wxGridTableMessage message(this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
589                                            static_cast<int>(pos),
590                                            static_cast<int>(numRows));
591                 GetView()->ProcessTableMessage(message);
592             }
593         }
594 
notifyRowsAppended(size_t numRows)595         void EntityAttributeGridTable::notifyRowsAppended(size_t numRows) {
596             if (GetView() != NULL) {
597                 wxGridTableMessage message(this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
598                                            static_cast<int>(numRows));
599                 GetView()->ProcessTableMessage(message);
600             }
601         }
602 
notifyRowsDeleted(size_t pos,size_t numRows)603         void EntityAttributeGridTable::notifyRowsDeleted(size_t pos, size_t numRows) {
604             if (GetView() != NULL) {
605                 wxGridTableMessage message(this, wxGRIDTABLE_NOTIFY_ROWS_DELETED,
606                                            static_cast<int>(pos),
607                                            static_cast<int>(numRows));
608                 GetView()->ProcessTableMessage(message);
609             }
610         }
611     }
612 }
613