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 "EntityAttributeGrid.h" 21 22 #include "Model/Object.h" 23 #include "View/EntityAttributeGridTable.h" 24 #include "View/EntityAttributeSelectedCommand.h" 25 #include "View/ViewConstants.h" 26 #include "View/MapDocument.h" 27 #include "View/wxUtils.h" 28 29 #include <wx/bmpbuttn.h> 30 #include <wx/checkbox.h> 31 #include <wx/sizer.h> 32 33 namespace TrenchBroom { 34 namespace View { EntityAttributeGrid(wxWindow * parent,MapDocumentWPtr document)35 EntityAttributeGrid::EntityAttributeGrid(wxWindow* parent, MapDocumentWPtr document) : 36 wxPanel(parent), 37 m_document(document), 38 m_lastHoveredCell(wxGridCellCoords(-1, -1)), 39 m_ignoreSelection(false), 40 m_lastSelectedCol(0) { 41 createGui(document); 42 bindObservers(); 43 } 44 ~EntityAttributeGrid()45 EntityAttributeGrid::~EntityAttributeGrid() { 46 unbindObservers(); 47 } 48 OnAttributeGridSize(wxSizeEvent & event)49 void EntityAttributeGrid::OnAttributeGridSize(wxSizeEvent& event) { 50 if (IsBeingDeleted()) return; 51 52 m_grid->SetColSize(0, 100); 53 const int colSize = std::max(1, m_grid->GetClientSize().x - m_grid->GetColSize(0)); 54 m_grid->SetColSize(1, colSize); 55 event.Skip(); 56 } 57 OnAttributeGridSelectCell(wxGridEvent & event)58 void EntityAttributeGrid::OnAttributeGridSelectCell(wxGridEvent& event) { 59 if (IsBeingDeleted()) return; 60 fireSelectionEvent(event.GetRow(), event.GetCol()); 61 } 62 OnAttributeGridTab(wxGridEvent & event)63 void EntityAttributeGrid::OnAttributeGridTab(wxGridEvent& event) { 64 if (IsBeingDeleted()) return; 65 66 if (event.ShiftDown()) { 67 if (event.GetCol() > 0) 68 moveCursorTo(event.GetRow(), event.GetCol() - 1); 69 else if (event.GetRow() > 0) 70 moveCursorTo(event.GetRow() - 1, m_grid->GetNumberCols() - 1); 71 } else { 72 if (event.GetCol() < m_grid->GetNumberCols() - 1) 73 moveCursorTo(event.GetRow(), event.GetCol() + 1); 74 else if (event.GetRow() < m_grid->GetNumberRows() - 1) 75 moveCursorTo(event.GetRow() + 1, 0); 76 } 77 } 78 moveCursorTo(const int row,const int col)79 void EntityAttributeGrid::moveCursorTo(const int row, const int col) { 80 { 81 const SetBool ignoreSelection(m_ignoreSelection); 82 m_grid->GoToCell(row, col); 83 m_grid->SelectRow(row); 84 } 85 fireSelectionEvent(row, col); 86 } 87 fireSelectionEvent(const int row,const int col)88 void EntityAttributeGrid::fireSelectionEvent(const int row, const int col) { 89 if (!m_ignoreSelection) { 90 const Model::AttributeName name = m_table->attributeName(row); 91 m_lastSelectedName = name; 92 m_lastSelectedCol = col; 93 94 EntityAttributeSelectedCommand command; 95 command.setName(name); 96 command.SetEventObject(this); 97 command.SetId(GetId()); 98 ProcessEvent(command); 99 } 100 } 101 OnAttributeGridKeyDown(wxKeyEvent & event)102 void EntityAttributeGrid::OnAttributeGridKeyDown(wxKeyEvent& event) { 103 if (IsBeingDeleted()) return; 104 105 if (isInsertRowShortcut(event)) { 106 addAttribute(); 107 } else if (isRemoveRowShortcut(event)) { 108 if (canRemoveSelectedAttributes()) 109 removeSelectedAttributes(); 110 } else { 111 event.Skip(); 112 } 113 } 114 OnAttributeGridKeyUp(wxKeyEvent & event)115 void EntityAttributeGrid::OnAttributeGridKeyUp(wxKeyEvent& event) { 116 if (IsBeingDeleted()) return; 117 118 if (!isInsertRowShortcut(event) && !isRemoveRowShortcut(event)) 119 event.Skip(); 120 } 121 isInsertRowShortcut(const wxKeyEvent & event) const122 bool EntityAttributeGrid::isInsertRowShortcut(const wxKeyEvent& event) const { 123 return event.GetKeyCode() == WXK_RETURN && event.ControlDown(); 124 } 125 isRemoveRowShortcut(const wxKeyEvent & event) const126 bool EntityAttributeGrid::isRemoveRowShortcut(const wxKeyEvent& event) const { 127 return (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_BACK) && !m_grid->IsCellEditControlShown(); 128 } 129 OnAttributeGridMouseMove(wxMouseEvent & event)130 void EntityAttributeGrid::OnAttributeGridMouseMove(wxMouseEvent& event) { 131 if (IsBeingDeleted()) return; 132 133 int logicalX, logicalY; 134 m_grid->CalcUnscrolledPosition(event.GetX(), event.GetY(), &logicalX, &logicalY); 135 136 const wxGridCellCoords currentCell = m_grid->XYToCell(logicalX, logicalY); 137 if (m_lastHoveredCell != currentCell) { 138 const String tooltip = m_table->tooltip(currentCell); 139 m_grid->SetToolTip(tooltip); 140 m_lastHoveredCell = currentCell; 141 } 142 event.Skip(); 143 } 144 OnUpdateAttributeView(wxUpdateUIEvent & event)145 void EntityAttributeGrid::OnUpdateAttributeView(wxUpdateUIEvent& event) { 146 if (IsBeingDeleted()) return; 147 148 MapDocumentSPtr document = lock(m_document); 149 event.Enable(document->hasSelectedNodes()); 150 } 151 OnAddAttributeButton(wxCommandEvent & event)152 void EntityAttributeGrid::OnAddAttributeButton(wxCommandEvent& event) { 153 if (IsBeingDeleted()) return; 154 155 addAttribute(); 156 } 157 OnRemovePropertiesButton(wxCommandEvent & event)158 void EntityAttributeGrid::OnRemovePropertiesButton(wxCommandEvent& event) { 159 if (IsBeingDeleted()) return; 160 161 removeSelectedAttributes(); 162 } 163 addAttribute()164 void EntityAttributeGrid::addAttribute() { 165 m_grid->InsertRows(m_table->GetNumberAttributeRows()); 166 m_grid->SetFocus(); 167 const int row = m_table->GetNumberAttributeRows() - 1; 168 m_grid->SelectRow(row); 169 m_grid->GoToCell(row, 0); 170 m_grid->ShowCellEditControl(); 171 } 172 removeSelectedAttributes()173 void EntityAttributeGrid::removeSelectedAttributes() { 174 assert(canRemoveSelectedAttributes()); 175 176 int firstRowIndex = m_grid->GetNumberRows(); 177 wxArrayInt selectedRows = m_grid->GetSelectedRows(); 178 wxArrayInt::reverse_iterator it, end; 179 for (it = selectedRows.rbegin(), end = selectedRows.rend(); it != end; ++it) { 180 m_grid->DeleteRows(*it, 1); 181 firstRowIndex = std::min(*it, firstRowIndex); 182 } 183 184 if (firstRowIndex < m_grid->GetNumberRows()) 185 m_grid->SelectRow(firstRowIndex); 186 } 187 OnShowDefaultPropertiesCheckBox(wxCommandEvent & event)188 void EntityAttributeGrid::OnShowDefaultPropertiesCheckBox(wxCommandEvent& event) { 189 if (IsBeingDeleted()) return; 190 191 m_table->setShowDefaultRows(event.IsChecked()); 192 } 193 OnUpdateAddAttributeButton(wxUpdateUIEvent & event)194 void EntityAttributeGrid::OnUpdateAddAttributeButton(wxUpdateUIEvent& event) { 195 if (IsBeingDeleted()) return; 196 197 MapDocumentSPtr document = lock(m_document); 198 event.Enable(document->hasSelectedNodes()); 199 } 200 OnUpdateRemovePropertiesButton(wxUpdateUIEvent & event)201 void EntityAttributeGrid::OnUpdateRemovePropertiesButton(wxUpdateUIEvent& event) { 202 if (IsBeingDeleted()) return; 203 204 event.Enable(!m_grid->GetSelectedRows().IsEmpty() && canRemoveSelectedAttributes()); 205 } 206 OnUpdateShowDefaultPropertiesCheckBox(wxUpdateUIEvent & event)207 void EntityAttributeGrid::OnUpdateShowDefaultPropertiesCheckBox(wxUpdateUIEvent& event) { 208 if (IsBeingDeleted()) return; 209 210 event.Check(m_table->showDefaultRows()); 211 } 212 canRemoveSelectedAttributes() const213 bool EntityAttributeGrid::canRemoveSelectedAttributes() const { 214 const wxArrayInt selectedRows = m_grid->GetSelectedRows(); 215 wxArrayInt::const_iterator it, end; 216 for (it = selectedRows.begin(), end = selectedRows.end(); it != end; ++it) { 217 if (!m_table->canRemove(*it)) 218 return false; 219 } 220 return true; 221 } 222 createGui(MapDocumentWPtr document)223 void EntityAttributeGrid::createGui(MapDocumentWPtr document) { 224 SetBackgroundColour(*wxWHITE); 225 226 m_table = new EntityAttributeGridTable(document); 227 228 m_grid = new wxGrid(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); 229 m_grid->SetTable(m_table, true, wxGrid::wxGridSelectRows); 230 // m_grid->SetUseNativeColLabels(); 231 // m_grid->UseNativeColHeader(); 232 m_grid->SetColLabelSize(18); 233 m_grid->SetDefaultCellBackgroundColour(*wxWHITE); 234 m_grid->HideRowLabels(); 235 236 m_grid->DisableColResize(0); 237 m_grid->DisableColResize(1); 238 m_grid->DisableDragColMove(); 239 m_grid->DisableDragCell(); 240 m_grid->DisableDragColSize(); 241 m_grid->DisableDragGridSize(); 242 m_grid->DisableDragRowSize(); 243 244 m_grid->Bind(wxEVT_SIZE, &EntityAttributeGrid::OnAttributeGridSize, this); 245 m_grid->Bind(wxEVT_GRID_SELECT_CELL, &EntityAttributeGrid::OnAttributeGridSelectCell, this); 246 m_grid->Bind(wxEVT_GRID_TABBING, &EntityAttributeGrid::OnAttributeGridTab, this); 247 m_grid->Bind(wxEVT_KEY_DOWN, &EntityAttributeGrid::OnAttributeGridKeyDown, this); 248 m_grid->Bind(wxEVT_KEY_UP, &EntityAttributeGrid::OnAttributeGridKeyUp, this); 249 m_grid->GetGridWindow()->Bind(wxEVT_MOTION, &EntityAttributeGrid::OnAttributeGridMouseMove, this); 250 m_grid->Bind(wxEVT_UPDATE_UI, &EntityAttributeGrid::OnUpdateAttributeView, this); 251 252 wxWindow* addAttributeButton = createBitmapButton(this, "Add.png", "Add a new property"); 253 wxWindow* removePropertiesButton = createBitmapButton(this, "Remove.png", "Remove the selected properties"); 254 255 addAttributeButton->Bind(wxEVT_BUTTON, &EntityAttributeGrid::OnAddAttributeButton, this); 256 addAttributeButton->Bind(wxEVT_UPDATE_UI, &EntityAttributeGrid::OnUpdateAddAttributeButton, this); 257 removePropertiesButton->Bind(wxEVT_BUTTON, &EntityAttributeGrid::OnRemovePropertiesButton, this); 258 removePropertiesButton->Bind(wxEVT_UPDATE_UI, &EntityAttributeGrid::OnUpdateRemovePropertiesButton, this); 259 260 wxCheckBox* showDefaultPropertiesCheckBox = new wxCheckBox(this, wxID_ANY, "Show default properties"); 261 showDefaultPropertiesCheckBox->Bind(wxEVT_CHECKBOX, &EntityAttributeGrid::OnShowDefaultPropertiesCheckBox, this); 262 showDefaultPropertiesCheckBox->Bind(wxEVT_UPDATE_UI, &EntityAttributeGrid::OnUpdateShowDefaultPropertiesCheckBox, this); 263 264 wxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL); 265 buttonSizer->Add(addAttributeButton, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, LayoutConstants::NarrowVMargin); 266 buttonSizer->Add(removePropertiesButton, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, LayoutConstants::NarrowVMargin); 267 buttonSizer->AddSpacer(LayoutConstants::WideHMargin); 268 buttonSizer->Add(showDefaultPropertiesCheckBox, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, LayoutConstants::NarrowVMargin); 269 buttonSizer->AddStretchSpacer(); 270 271 wxSizer* sizer = new wxBoxSizer(wxVERTICAL); 272 sizer->Add(m_grid, 1, wxEXPAND); 273 sizer->Add(buttonSizer, 0, wxEXPAND); 274 SetSizer(sizer); 275 } 276 bindObservers()277 void EntityAttributeGrid::bindObservers() { 278 MapDocumentSPtr document = lock(m_document); 279 document->documentWasNewedNotifier.addObserver(this, &EntityAttributeGrid::documentWasNewed); 280 document->documentWasLoadedNotifier.addObserver(this, &EntityAttributeGrid::documentWasLoaded); 281 document->nodesDidChangeNotifier.addObserver(this, &EntityAttributeGrid::nodesDidChange); 282 document->selectionWillChangeNotifier.addObserver(this, &EntityAttributeGrid::selectionWillChange); 283 document->selectionDidChangeNotifier.addObserver(this, &EntityAttributeGrid::selectionDidChange); 284 } 285 unbindObservers()286 void EntityAttributeGrid::unbindObservers() { 287 if (!expired(m_document)) { 288 MapDocumentSPtr document = lock(m_document); 289 document->documentWasNewedNotifier.removeObserver(this, &EntityAttributeGrid::documentWasNewed); 290 document->documentWasLoadedNotifier.removeObserver(this, &EntityAttributeGrid::documentWasLoaded); 291 document->nodesDidChangeNotifier.removeObserver(this, &EntityAttributeGrid::nodesDidChange); 292 document->selectionWillChangeNotifier.removeObserver(this, &EntityAttributeGrid::selectionWillChange); 293 document->selectionDidChangeNotifier.removeObserver(this, &EntityAttributeGrid::selectionDidChange); 294 } 295 } 296 documentWasNewed(MapDocument * document)297 void EntityAttributeGrid::documentWasNewed(MapDocument* document) { 298 updateControls(); 299 } 300 documentWasLoaded(MapDocument * document)301 void EntityAttributeGrid::documentWasLoaded(MapDocument* document) { 302 updateControls(); 303 } 304 nodesDidChange(const Model::NodeList & nodes)305 void EntityAttributeGrid::nodesDidChange(const Model::NodeList& nodes) { 306 updateControls(); 307 } 308 selectionWillChange()309 void EntityAttributeGrid::selectionWillChange() { 310 m_grid->SaveEditControlValue(); 311 m_grid->HideCellEditControl(); 312 } 313 selectionDidChange(const Selection & selection)314 void EntityAttributeGrid::selectionDidChange(const Selection& selection) { 315 updateControls(); 316 } 317 updateControls()318 void EntityAttributeGrid::updateControls() { 319 // const SetBool ignoreSelection(m_ignoreSelection); 320 wxGridUpdateLocker lockGrid(m_grid); 321 m_table->update(); 322 323 const int row = m_table->rowForName(m_lastSelectedName); 324 if (row != -1) { 325 m_grid->SelectRow(row); 326 m_grid->GoToCell(row, m_lastSelectedCol); 327 } else { 328 fireSelectionEvent(row, m_lastSelectedCol); 329 } 330 } 331 selectedRowName() const332 Model::AttributeName EntityAttributeGrid::selectedRowName() const { 333 wxArrayInt selectedRows = m_grid->GetSelectedRows(); 334 if (selectedRows.empty()) 335 return ""; 336 const int row = selectedRows.front(); 337 return m_table->attributeName(row); 338 } 339 } 340 } 341