Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / common / cmdproc.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/cmdproc.cpp
3 // Purpose: wxCommand and wxCommandProcessor classes
4 // Author: Julian Smart (extracted from docview.h by VZ)
5 // Modified by:
6 // Created: 05.11.00
7 // Copyright: (c) wxWidgets team
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/intl.h"
28 #include "wx/string.h"
29 #include "wx/menu.h"
30 #include "wx/accel.h"
31 #endif //WX_PRECOMP
32
33 #include "wx/cmdproc.h"
34
35 // ============================================================================
36 // implementation
37 // ============================================================================
38
39 IMPLEMENT_CLASS(wxCommand, wxObject)
40 IMPLEMENT_DYNAMIC_CLASS(wxCommandProcessor, wxObject)
41
42 // ----------------------------------------------------------------------------
43 // wxCommand
44 // ----------------------------------------------------------------------------
45
46 wxCommand::wxCommand(bool canUndoIt, const wxString& name)
47 {
48 m_canUndo = canUndoIt;
49 m_commandName = name;
50 }
51
52 // ----------------------------------------------------------------------------
53 // Command processor
54 // ----------------------------------------------------------------------------
55
56 wxCommandProcessor::wxCommandProcessor(int maxCommands)
57 {
58 m_maxNoCommands = maxCommands;
59 #if wxUSE_MENUS
60 m_commandEditMenu = NULL;
61 #endif // wxUSE_MENUS
62
63 #if wxUSE_ACCEL
64 m_undoAccelerator = '\t' + wxAcceleratorEntry(wxACCEL_CTRL, 'Z').ToString();
65 m_redoAccelerator = '\t' + wxAcceleratorEntry(wxACCEL_CTRL, 'Y').ToString();
66 #endif // wxUSE_ACCEL
67
68 m_lastSavedCommand =
69 m_currentCommand = wxList::compatibility_iterator();
70 }
71
72 wxCommandProcessor::~wxCommandProcessor()
73 {
74 ClearCommands();
75 }
76
77 bool wxCommandProcessor::DoCommand(wxCommand& cmd)
78 {
79 return cmd.Do();
80 }
81
82 bool wxCommandProcessor::UndoCommand(wxCommand& cmd)
83 {
84 return cmd.Undo();
85 }
86
87 // Pass a command to the processor. The processor calls Do();
88 // if successful, is appended to the command history unless
89 // storeIt is false.
90 bool wxCommandProcessor::Submit(wxCommand *command, bool storeIt)
91 {
92 wxCHECK_MSG( command, false, wxT("no command in wxCommandProcessor::Submit") );
93
94 if ( !DoCommand(*command) )
95 {
96 // the user code expects the command to be deleted anyhow
97 delete command;
98
99 return false;
100 }
101
102 if ( storeIt )
103 Store(command);
104 else
105 delete command;
106
107 return true;
108 }
109
110 void wxCommandProcessor::Store(wxCommand *command)
111 {
112 wxCHECK_RET( command, wxT("no command in wxCommandProcessor::Store") );
113
114 // Correct a bug: we must chop off the current 'branch'
115 // so that we're at the end of the command list.
116 if (!m_currentCommand)
117 ClearCommands();
118 else
119 {
120 wxList::compatibility_iterator node = m_currentCommand->GetNext();
121 while (node)
122 {
123 wxList::compatibility_iterator next = node->GetNext();
124
125 // Make sure m_lastSavedCommand won't point to freed memory
126 if ( m_lastSavedCommand && m_lastSavedCommand == node )
127 m_lastSavedCommand = wxList::compatibility_iterator();
128
129 delete (wxCommand *)node->GetData();
130 m_commands.Erase(node);
131
132 node = next;
133 }
134 }
135
136 if ( (int)m_commands.GetCount() == m_maxNoCommands )
137 {
138 wxList::compatibility_iterator firstNode = m_commands.GetFirst();
139
140 // Make sure m_lastSavedCommand won't point to freed memory
141 if ( m_lastSavedCommand && m_lastSavedCommand == firstNode )
142 m_lastSavedCommand = wxList::compatibility_iterator();
143
144 wxCommand *firstCommand = (wxCommand *)firstNode->GetData();
145 delete firstCommand;
146 m_commands.Erase(firstNode);
147 }
148
149 m_commands.Append(command);
150 m_currentCommand = m_commands.GetLast();
151 SetMenuStrings();
152 }
153
154 bool wxCommandProcessor::Undo()
155 {
156 wxCommand *command = GetCurrentCommand();
157 if ( command && command->CanUndo() )
158 {
159 if ( UndoCommand(*command) )
160 {
161 m_currentCommand = m_currentCommand->GetPrevious();
162 SetMenuStrings();
163 return true;
164 }
165 }
166
167 return false;
168 }
169
170 bool wxCommandProcessor::Redo()
171 {
172 wxCommand *redoCommand = NULL;
173 wxList::compatibility_iterator redoNode
174 #if !wxUSE_STD_CONTAINERS
175 = NULL // just to avoid warnings
176 #endif // !wxUSE_STD_CONTAINERS
177 ;
178
179 if ( m_currentCommand )
180 {
181 // is there anything to redo?
182 if ( m_currentCommand->GetNext() )
183 {
184 redoCommand = (wxCommand *)m_currentCommand->GetNext()->GetData();
185 redoNode = m_currentCommand->GetNext();
186 }
187 }
188 else // no current command, redo the first one
189 {
190 if (m_commands.GetCount() > 0)
191 {
192 redoCommand = (wxCommand *)m_commands.GetFirst()->GetData();
193 redoNode = m_commands.GetFirst();
194 }
195 }
196
197 if (redoCommand)
198 {
199 bool success = DoCommand(*redoCommand);
200 if (success)
201 {
202 m_currentCommand = redoNode;
203 SetMenuStrings();
204 return true;
205 }
206 }
207 return false;
208 }
209
210 bool wxCommandProcessor::CanUndo() const
211 {
212 wxCommand *command = GetCurrentCommand();
213
214 return command && command->CanUndo();
215 }
216
217 bool wxCommandProcessor::CanRedo() const
218 {
219 if (m_currentCommand && !m_currentCommand->GetNext())
220 return false;
221
222 if (m_currentCommand && m_currentCommand->GetNext())
223 return true;
224
225 if (!m_currentCommand && (m_commands.GetCount() > 0))
226 return true;
227
228 return false;
229 }
230
231 void wxCommandProcessor::Initialize()
232 {
233 m_currentCommand = m_commands.GetLast();
234 SetMenuStrings();
235 }
236
237 void wxCommandProcessor::SetMenuStrings()
238 {
239 #if wxUSE_MENUS
240 if (m_commandEditMenu)
241 {
242 wxString undoLabel = GetUndoMenuLabel();
243 wxString redoLabel = GetRedoMenuLabel();
244
245 m_commandEditMenu->SetLabel(wxID_UNDO, undoLabel);
246 m_commandEditMenu->Enable(wxID_UNDO, CanUndo());
247
248 m_commandEditMenu->SetLabel(wxID_REDO, redoLabel);
249 m_commandEditMenu->Enable(wxID_REDO, CanRedo());
250 }
251 #endif // wxUSE_MENUS
252 }
253
254 // Gets the current Undo menu label.
255 wxString wxCommandProcessor::GetUndoMenuLabel() const
256 {
257 wxString buf;
258 if (m_currentCommand)
259 {
260 wxCommand *command = (wxCommand *)m_currentCommand->GetData();
261 wxString commandName(command->GetName());
262 if (commandName.empty()) commandName = _("Unnamed command");
263 bool canUndo = command->CanUndo();
264 if (canUndo)
265 buf = wxString(_("&Undo ")) + commandName + m_undoAccelerator;
266 else
267 buf = wxString(_("Can't &Undo ")) + commandName + m_undoAccelerator;
268 }
269 else
270 {
271 buf = _("&Undo") + m_undoAccelerator;
272 }
273
274 return buf;
275 }
276
277 // Gets the current Undo menu label.
278 wxString wxCommandProcessor::GetRedoMenuLabel() const
279 {
280 wxString buf;
281 if (m_currentCommand)
282 {
283 // We can redo, if we're not at the end of the history.
284 if (m_currentCommand->GetNext())
285 {
286 wxCommand *redoCommand = (wxCommand *)m_currentCommand->GetNext()->GetData();
287 wxString redoCommandName(redoCommand->GetName());
288 if (redoCommandName.empty()) redoCommandName = _("Unnamed command");
289 buf = wxString(_("&Redo ")) + redoCommandName + m_redoAccelerator;
290 }
291 else
292 {
293 buf = _("&Redo") + m_redoAccelerator;
294 }
295 }
296 else
297 {
298 if (m_commands.GetCount() == 0)
299 {
300 buf = _("&Redo") + m_redoAccelerator;
301 }
302 else
303 {
304 // currentCommand is NULL but there are commands: this means that
305 // we've undone to the start of the list, but can redo the first.
306 wxCommand *redoCommand = (wxCommand *)m_commands.GetFirst()->GetData();
307 wxString redoCommandName(redoCommand->GetName());
308 if (redoCommandName.empty()) redoCommandName = _("Unnamed command");
309 buf = wxString(_("&Redo ")) + redoCommandName + m_redoAccelerator;
310 }
311 }
312 return buf;
313 }
314
315 void wxCommandProcessor::ClearCommands()
316 {
317 wxList::compatibility_iterator node = m_commands.GetFirst();
318 while (node)
319 {
320 wxCommand *command = (wxCommand *)node->GetData();
321 delete command;
322 m_commands.Erase(node);
323 node = m_commands.GetFirst();
324 }
325
326 m_currentCommand = wxList::compatibility_iterator();
327 m_lastSavedCommand = wxList::compatibility_iterator();
328 }
329
330 bool wxCommandProcessor::IsDirty() const
331 {
332 if ( m_commands.empty() )
333 {
334 // If we have never been modified, we can't be dirty.
335 return false;
336 }
337
338 if ( !m_lastSavedCommand )
339 {
340 // If we have been modified but have never been saved, we're dirty.
341 return true;
342 }
343
344 if ( !m_currentCommand )
345 {
346 // This only happens if all commands were undone after saving the
347 // document: we're dirty then.
348 return true;
349 }
350
351 // Finally if both iterators are valid, we may just compare them.
352 return m_currentCommand != m_lastSavedCommand;
353 }
354