OSDN Git Service

Fix crash when replying macro including alt+r
[qt-creator-jp/qt-creator-jp.git] / src / plugins / macros / macromanager.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nicolas Arnaud-Cormos.
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "macromanager.h"
34
35 #include "macrosconstants.h"
36 #include "macroevent.h"
37 #include "macro.h"
38 #include "imacrohandler.h"
39 #include "savedialog.h"
40 #include "actionmacrohandler.h"
41 #include "texteditormacrohandler.h"
42 #include "findmacrohandler.h"
43
44 #include <texteditor/texteditorconstants.h>
45
46 #include <coreplugin/coreconstants.h>
47 #include <coreplugin/actionmanager/actionmanager.h>
48 #include <coreplugin/actionmanager/actioncontainer.h>
49 #include <coreplugin/actionmanager/command.h>
50 #include <coreplugin/icore.h>
51 #include <coreplugin/uniqueidmanager.h>
52 #include <coreplugin/icontext.h>
53 #include <coreplugin/editormanager/editormanager.h>
54 #include <coreplugin/editormanager/ieditor.h>
55
56 #include <QtCore/QDir>
57 #include <QtCore/QFile>
58 #include <QtCore/QFileInfo>
59 #include <QtCore/QSettings>
60 #include <QtCore/QSignalMapper>
61 #include <QtCore/QList>
62
63 #include <QtGui/QShortcut>
64 #include <QtGui/QMainWindow>
65 #include <QtGui/QAction>
66 #include <QtGui/QFileDialog>
67 #include <QtGui/QMessageBox>
68
69 using namespace Macros;
70 using namespace Macros::Internal;
71
72 /*!
73     \namespace Macros
74     \brief The Macros namespace contains support for macros in Qt Creator.
75 */
76
77 /*!
78
79     \class Macro::MacroManager
80     \brief Manager for macros.
81
82     The MacroManager manage all macros, it loads them on startup, keep track of the
83     current macro and create new macros.
84
85     There are two important methods in this class that can be used outside the Macros plugin:
86     \list
87     \o registerEventHandler: add a new event handler
88     \o registerAction: add a macro event when this action is triggered
89     \endlist
90
91     This class is a singleton and can be accessed using the instance method.
92 */
93
94 /*!
95     \fn void registerAction(QAction *action, const QString &id)
96
97     Append this action to the list of actions registered in a macro. The id is
98     the action id passed to the ActionManager.
99 */
100
101 class Macros::MacroManager::MacroManagerPrivate
102 {
103 public:
104     MacroManagerPrivate(MacroManager *qq);
105
106     MacroManager *q;
107     QMap<QString, Macro *> macros;
108     Macro *currentMacro;
109     bool isRecording;
110
111     QList<IMacroHandler*> handlers;
112
113     QSignalMapper *mapper;
114
115     ActionMacroHandler *actionHandler;
116     TextEditorMacroHandler *textEditorHandler;
117     FindMacroHandler *findHandler;
118
119     void initialize();
120     void addMacro(Macro *macro);
121     void removeMacro(const QString &name);
122     void changeMacroDescription(Macro *macro, const QString &description);
123
124     bool executeMacro(Macro *macro);
125     void showSaveDialog();
126 };
127
128 MacroManager::MacroManagerPrivate::MacroManagerPrivate(MacroManager *qq):
129     q(qq),
130     currentMacro(0),
131     isRecording(false),
132     mapper(new QSignalMapper(qq))
133 {
134     connect(mapper, SIGNAL(mapped(QString)), q, SLOT(executeMacro(QString)));
135
136     // Load existing macros
137     initialize();
138
139     actionHandler = new ActionMacroHandler;
140     textEditorHandler = new TextEditorMacroHandler;
141     findHandler = new FindMacroHandler;
142 }
143
144 void MacroManager::MacroManagerPrivate::initialize()
145 {
146     macros.clear();
147     QDir dir(q->macrosDirectory());
148     QStringList filter;
149     filter << QString("*.")+Constants::M_EXTENSION;
150     QStringList files = dir.entryList(filter, QDir::Files);
151
152     foreach (const QString &name, files) {
153         QString fileName = dir.absolutePath() + '/' + name;
154         Macro *macro = new Macro;
155         if (macro->loadHeader(fileName))
156             addMacro(macro);
157         else
158             delete macro;
159     }
160 }
161
162 void MacroManager::MacroManagerPrivate::addMacro(Macro *macro)
163 {
164     // Add sortcut
165     Core::Context context(TextEditor::Constants::C_TEXTEDITOR);
166     Core::ICore *core = Core::ICore::instance();
167     Core::ActionManager *am = core->actionManager();
168     QShortcut *shortcut = new QShortcut(core->mainWindow());
169     shortcut->setWhatsThis(macro->description());
170     const QString macroId = QLatin1String(Constants::PREFIX_MACRO) + macro->displayName();
171     am->registerShortcut(shortcut, macroId, context);
172     connect(shortcut, SIGNAL(activated()), mapper, SLOT(map()));
173     mapper->setMapping(shortcut, macro->displayName());
174
175     // Add macro to the map
176     macros[macro->displayName()] = macro;
177 }
178
179 void MacroManager::MacroManagerPrivate::removeMacro(const QString &name)
180 {
181     if (!macros.contains(name))
182         return;
183     // Remove shortcut
184     Core::ICore *core = Core::ICore::instance();
185     Core::ActionManager *am = core->actionManager();
186     am->unregisterShortcut(Core::Id(Constants::PREFIX_MACRO+name));
187
188     // Remove macro from the map
189     Macro *macro = macros.take(name);
190     delete macro;
191 }
192
193 void MacroManager::MacroManagerPrivate::changeMacroDescription(Macro *macro, const QString &description)
194 {
195     if (!macro->load())
196         return;
197     macro->setDescription(description);
198     macro->save(macro->fileName(), Core::ICore::instance()->mainWindow());
199
200     // Change shortcut what's this
201     Core::ICore *core = Core::ICore::instance();
202     Core::ActionManager *am = core->actionManager();
203
204     Core::Command *command = am->command(Core::Id(Constants::PREFIX_MACRO+macro->displayName()));
205     if (command && command->shortcut())
206         command->shortcut()->setWhatsThis(description);
207 }
208
209 bool MacroManager::MacroManagerPrivate::executeMacro(Macro *macro)
210 {
211     bool error = !macro->load();
212     foreach (const MacroEvent &macroEvent, macro->events()) {
213         if (error)
214             break;
215         foreach (IMacroHandler *handler, handlers) {
216             if (handler->canExecuteEvent(macroEvent)) {
217                 if (!handler->executeEvent(macroEvent))
218                     error = true;
219                 break;
220             }
221         }
222     }
223
224     if (error) {
225         QMessageBox::warning(Core::ICore::instance()->mainWindow(),
226                              tr("Playing Macro"),
227                              tr("An error occurred while replaying the macro, execution stopped."));
228     }
229
230     // Set the focus back to the editor
231     // TODO: is it really needed??
232     const Core::EditorManager *editorManager = Core::EditorManager::instance();
233     if (editorManager->currentEditor())
234         editorManager->currentEditor()->widget()->setFocus(Qt::OtherFocusReason);
235
236     return !error;
237 }
238
239 void MacroManager::MacroManagerPrivate::showSaveDialog()
240 {
241     QMainWindow *mainWindow = Core::ICore::instance()->mainWindow();
242     SaveDialog dialog(mainWindow);
243     if (dialog.exec()) {
244         if (dialog.name().isEmpty())
245             return;
246
247         // Save in the resource path
248         QString fileName = q->macrosDirectory() + '/' + dialog.name()
249                            + '.' + Constants::M_EXTENSION;
250         currentMacro->setDescription(dialog.description());
251         currentMacro->save(fileName, mainWindow);
252         addMacro(currentMacro);
253     }
254 }
255
256
257 // ---------- MacroManager ------------
258 MacroManager *MacroManager::m_instance = 0;
259
260 MacroManager::MacroManager(QObject *parent) :
261     QObject(parent),
262     d(new MacroManagerPrivate(this))
263 {
264     registerMacroHandler(d->actionHandler);
265     registerMacroHandler(d->findHandler);
266     registerMacroHandler(d->textEditorHandler);
267     m_instance = this;
268 }
269
270 MacroManager::~MacroManager()
271 {
272     // Cleanup macro
273     QStringList macroList = d->macros.keys();
274     foreach (const QString &name, macroList)
275         d->removeMacro(name);
276
277     // Cleanup handlers
278     qDeleteAll(d->handlers);
279
280     delete d;
281 }
282
283 void MacroManager::startMacro()
284 {
285     d->isRecording = true;
286     // Delete anonymous macro
287     if (d->currentMacro && d->currentMacro->displayName().isEmpty())
288         delete d->currentMacro;
289     d->currentMacro = new Macro;
290
291     Core::ActionManager *am = Core::ICore::instance()->actionManager();
292     am->command(Constants::START_MACRO)->action()->setEnabled(false);
293     am->command(Constants::END_MACRO)->action()->setEnabled(true);
294     am->command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(false);
295     am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(false);
296     foreach (IMacroHandler *handler, d->handlers)
297         handler->startRecording(d->currentMacro);
298
299     QString endShortcut = am->command(Constants::END_MACRO)->defaultKeySequence().toString();
300     QString executeShortcut = am->command(Constants::EXECUTE_LAST_MACRO)->defaultKeySequence().toString();
301     QString help = tr("Macro mode. Type \"%1\" to stop recording and \"%2\" to play it")
302         .arg(endShortcut).arg(executeShortcut);
303     Core::EditorManager::instance()->showEditorStatusBar(
304                 QLatin1String(Constants::M_STATUS_BUFFER),
305                 help,
306                 tr("Stop Recording Macro"), this, SLOT(endMacro()));
307 }
308
309 void MacroManager::endMacro()
310 {
311     Core::EditorManager::instance()->hideEditorStatusBar(QLatin1String(Constants::M_STATUS_BUFFER));
312
313     Core::ActionManager *am = Core::ICore::instance()->actionManager();
314     am->command(Constants::START_MACRO)->action()->setEnabled(true);
315     am->command(Constants::END_MACRO)->action()->setEnabled(false);
316     am->command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(true);
317     am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true);
318     foreach (IMacroHandler *handler, d->handlers)
319         handler->endRecordingMacro(d->currentMacro);
320
321     d->isRecording = false;
322 }
323
324 void MacroManager::executeLastMacro()
325 {
326     if (!d->currentMacro)
327         return;
328
329     // make sure the macro doesn't accidentally invoke a macro action
330     Core::ActionManager *am = Core::ICore::instance()->actionManager();
331     am->command(Constants::START_MACRO)->action()->setEnabled(false);
332     am->command(Constants::END_MACRO)->action()->setEnabled(false);
333     am->command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(false);
334     am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(false);
335
336     d->executeMacro(d->currentMacro);
337
338     am->command(Constants::START_MACRO)->action()->setEnabled(true);
339     am->command(Constants::END_MACRO)->action()->setEnabled(false);
340     am->command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(true);
341     am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true);
342 }
343
344 bool MacroManager::executeMacro(const QString &name)
345 {
346     // Don't execute macro while recording
347     if (d->isRecording || !d->macros.contains(name))
348         return false;
349
350     Macro *macro = d->macros.value(name);
351     if (!d->executeMacro(macro))
352         return false;
353
354     // Delete anonymous macro
355     if (d->currentMacro && d->currentMacro->displayName().isEmpty())
356         delete d->currentMacro;
357     d->currentMacro = macro;
358
359     Core::ActionManager *am = Core::ICore::instance()->actionManager();
360     am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true);
361
362     return true;
363 }
364
365 void MacroManager::deleteMacro(const QString &name)
366 {
367     Macro *macro = d->macros.value(name);
368     if (macro) {
369         QString fileName = macro->fileName();
370         d->removeMacro(name);
371         QFile::remove(fileName);
372     }
373 }
374
375 const QMap<QString,Macro*> &MacroManager::macros() const
376 {
377     return d->macros;
378 }
379
380 void MacroManager::registerMacroHandler(IMacroHandler *handler)
381 {
382     d->handlers.prepend(handler);
383 }
384
385 MacroManager *MacroManager::instance()
386 {
387     return m_instance;
388 }
389
390 void MacroManager::changeMacro(const QString &name, const QString &description)
391 {
392     if (!d->macros.contains(name))
393         return;
394     Macro *macro = d->macros.value(name);
395
396     // Change description
397     if (macro->description() != description)
398         d->changeMacroDescription(macro, description);
399 }
400
401 void Macros::MacroManager::saveLastMacro()
402 {
403     if (d->currentMacro->events().count())
404         d->showSaveDialog();
405 }
406
407 QString Macros::MacroManager::macrosDirectory() const
408 {
409     const QString &path =
410         Core::ICore::instance()->userResourcePath() + QLatin1String("/macros");
411     if (QFile::exists(path) || QDir().mkpath(path))
412         return path;
413     return QString();
414 }