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 "KeyboardShortcutGridTable.h"
21 
22 #include "View/ActionManager.h"
23 #include "View/KeyboardGridCellEditor.h"
24 #include "View/KeyboardShortcutEntry.h"
25 #include "View/Menu.h"
26 
27 namespace TrenchBroom {
28     namespace View {
KeyboardShortcutGridTable()29         KeyboardShortcutGridTable::KeyboardShortcutGridTable() :
30         m_cellEditor(new KeyboardGridCellEditor()) {
31             m_cellEditor->IncRef();
32         }
33 
~KeyboardShortcutGridTable()34         KeyboardShortcutGridTable::~KeyboardShortcutGridTable() {
35             m_cellEditor->DecRef();
36         }
37 
GetNumberRows()38         int KeyboardShortcutGridTable::GetNumberRows() {
39             return static_cast<int>(m_entries.size());
40         }
41 
GetNumberCols()42         int KeyboardShortcutGridTable::GetNumberCols() {
43             return 3;
44         }
45 
GetValue(int row,int col)46         wxString KeyboardShortcutGridTable::GetValue(int row, int col) {
47             assert(row >= 0 && row < GetNumberRows());
48             assert(col >= 0 && col < GetNumberCols());
49 
50             size_t rowIndex = static_cast<size_t>(row);
51 
52             switch (col) {
53                 case 0:
54                     return m_entries[rowIndex]->shortcutDescription();
55                 case 1:
56                     return m_entries[rowIndex]->actionContextDescription();
57                 case 2:
58                     return m_entries[rowIndex]->actionDescription();
59                 default:
60                     assert(false);
61                     break;
62             }
63 
64             return "";
65         }
66 
SetValue(int row,int col,const wxString & value)67         void KeyboardShortcutGridTable::SetValue(int row, int col, const wxString& value) {
68             assert(row >= 0 && row < GetNumberRows());
69             assert(col == 0);
70 
71             int key, modifier1, modifier2, modifier3;
72             const bool success = KeyboardShortcut::parseShortcut(value, key, modifier1, modifier2, modifier3);
73             assert(success);
74             unused(success);
75 
76             const size_t rowIndex = static_cast<size_t>(row);
77             m_entries[rowIndex]->updateShortcut(KeyboardShortcut(key, modifier1, modifier2, modifier3));
78 
79             if (markConflicts(m_entries))
80                 notifyRowsUpdated(m_entries.size());
81             else
82                 notifyRowsUpdated(rowIndex, 1);
83         }
84 
Clear()85         void KeyboardShortcutGridTable::Clear() {
86             assert(false);
87         }
88 
InsertRows(size_t pos,size_t numRows)89         bool KeyboardShortcutGridTable::InsertRows(size_t pos, size_t numRows) {
90             assert(false);
91             return false;
92         }
93 
AppendRows(size_t numRows)94         bool KeyboardShortcutGridTable::AppendRows(size_t numRows) {
95             assert(false);
96             return false;
97         }
98 
DeleteRows(size_t pos,size_t numRows)99         bool KeyboardShortcutGridTable::DeleteRows(size_t pos, size_t numRows) {
100             assert(false);
101             return false;
102         }
103 
GetColLabelValue(int col)104         wxString KeyboardShortcutGridTable::GetColLabelValue(int col) {
105             assert(col >= 0 && col < GetNumberCols());
106             switch (col) {
107                 case 0:
108                     return "Shortcut";
109                 case 1:
110                     return "Context";
111                 case 2:
112                     return "Description";
113                 default:
114                     assert(false);
115                     break;
116             }
117 
118             return "";
119         }
120 
GetAttr(int row,int col,wxGridCellAttr::wxAttrKind kind)121         wxGridCellAttr* KeyboardShortcutGridTable::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) {
122             wxGridCellAttr* attr = wxGridTableBase::GetAttr(row, col, kind);
123             if (row >= 0 && row < GetNumberRows()) {
124                 const KeyboardShortcutEntry* entry = m_entries[static_cast<size_t>(row)];
125                 if (entry->hasConflicts()) {
126                     if (attr == NULL)
127                         attr = new wxGridCellAttr();
128                     attr->SetTextColour(*wxRED);
129                 }
130                 if (col == 0) {
131                     if (attr == NULL)
132                         attr = new wxGridCellAttr();
133                     if (entry->modifiable()) {
134                         attr->SetEditor(m_cellEditor);
135                         m_cellEditor->IncRef();
136                     } else {
137                         attr->SetReadOnly(true);
138                         attr->SetTextColour(*wxLIGHT_GREY);
139                     }
140                 } else {
141                     if (attr == NULL)
142                         attr = new wxGridCellAttr();
143                     attr->SetReadOnly(true);
144                 }
145             }
146             return attr;
147         }
148 
hasDuplicates() const149         bool KeyboardShortcutGridTable::hasDuplicates() const {
150             for (size_t i = 0; i < m_entries.size(); i++)
151                 if (m_entries[i]->hasConflicts())
152                     return true;
153             return false;
154         }
155 
update()156         bool KeyboardShortcutGridTable::update() {
157             EntryList newEntries;
158 
159             ActionManager& actionManager = ActionManager::instance();
160             actionManager.getShortcutEntries(newEntries);
161 
162             const bool hasConflicts = markConflicts(newEntries);
163 
164             size_t oldSize = m_entries.size();
165             m_entries = newEntries;
166 
167             notifyRowsUpdated(0, oldSize);
168             if (oldSize < m_entries.size())
169                 notifyRowsAppended(m_entries.size() - oldSize);
170             else if (oldSize > m_entries.size())
171                 notifyRowsDeleted(oldSize, oldSize - m_entries.size());
172 
173             return hasConflicts;
174         }
175 
notifyRowsUpdated(size_t pos,size_t numRows)176         void KeyboardShortcutGridTable::notifyRowsUpdated(size_t pos, size_t numRows) {
177             if (GetView() != NULL) {
178                 wxGridTableMessage message(this, wxGRIDTABLE_REQUEST_VIEW_GET_VALUES,
179                                            static_cast<int>(pos),
180                                            static_cast<int>(numRows));
181                 GetView()->ProcessTableMessage(message);
182             }
183         }
184 
notifyRowsInserted(size_t pos,size_t numRows)185         void KeyboardShortcutGridTable::notifyRowsInserted(size_t pos, size_t numRows) {
186             if (GetView() != NULL) {
187                 wxGridTableMessage message(this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
188                                            static_cast<int>(pos),
189                                            static_cast<int>(numRows));
190                 GetView()->ProcessTableMessage(message);
191             }
192         }
193 
notifyRowsAppended(size_t numRows)194         void KeyboardShortcutGridTable::notifyRowsAppended(size_t numRows) {
195             if (GetView() != NULL) {
196                 wxGridTableMessage message(this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
197                                            static_cast<int>(numRows));
198                 GetView()->ProcessTableMessage(message);
199             }
200         }
201 
notifyRowsDeleted(size_t pos,size_t numRows)202         void KeyboardShortcutGridTable::notifyRowsDeleted(size_t pos, size_t numRows) {
203             if (GetView() != NULL) {
204                 wxGridTableMessage message(this, wxGRIDTABLE_NOTIFY_ROWS_DELETED,
205                                            static_cast<int>(pos),
206                                            static_cast<int>(numRows));
207                 GetView()->ProcessTableMessage(message);
208             }
209         }
210 
markConflicts(EntryList & entries)211         bool KeyboardShortcutGridTable::markConflicts(EntryList& entries) {
212             for (size_t i = 0; i < entries.size(); i++)
213                 entries[i]->resetConflicts();
214 
215             bool hasConflicts = false;
216             for (size_t i = 0; i < entries.size(); i++) {
217                 KeyboardShortcutEntry* first = entries[i];
218                 for (size_t j = i + 1; j < entries.size(); j++) {
219                     KeyboardShortcutEntry* second = entries[j];
220                     if (first->updateConflicts(second)) {
221                         second->updateConflicts(first);
222                         hasConflicts = true;
223                     }
224                 }
225             }
226             return hasConflicts;
227         }
228     }
229 }
230