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