1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nicolas Arnaud-Cormos.
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "macromanager.h"
35 #include "macrosconstants.h"
36 #include "macroevent.h"
38 #include "imacrohandler.h"
39 #include "savedialog.h"
40 #include "actionmacrohandler.h"
41 #include "texteditormacrohandler.h"
42 #include "findmacrohandler.h"
44 #include <texteditor/texteditorconstants.h>
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>
56 #include <QtCore/QDir>
57 #include <QtCore/QFile>
58 #include <QtCore/QFileInfo>
59 #include <QtCore/QSettings>
60 #include <QtCore/QSignalMapper>
61 #include <QtCore/QList>
63 #include <QtGui/QShortcut>
64 #include <QtGui/QMainWindow>
65 #include <QtGui/QAction>
66 #include <QtGui/QFileDialog>
67 #include <QtGui/QMessageBox>
69 using namespace Macros;
70 using namespace Macros::Internal;
74 \brief The Macros namespace contains support for macros in Qt Creator.
79 \class Macro::MacroManager
80 \brief Manager for macros.
82 The MacroManager manage all macros, it loads them on startup, keep track of the
83 current macro and create new macros.
85 There are two important methods in this class that can be used outside the Macros plugin:
87 \o registerEventHandler: add a new event handler
88 \o registerAction: add a macro event when this action is triggered
91 This class is a singleton and can be accessed using the instance method.
95 \fn void registerAction(QAction *action, const QString &id)
97 Append this action to the list of actions registered in a macro. The id is
98 the action id passed to the ActionManager.
101 class Macros::MacroManager::MacroManagerPrivate
104 MacroManagerPrivate(MacroManager *qq);
107 QMap<QString, Macro *> macros;
111 QList<IMacroHandler*> handlers;
113 QSignalMapper *mapper;
115 ActionMacroHandler *actionHandler;
116 TextEditorMacroHandler *textEditorHandler;
117 FindMacroHandler *findHandler;
120 void addMacro(Macro *macro);
121 void removeMacro(const QString &name);
122 void changeMacroDescription(Macro *macro, const QString &description);
124 bool executeMacro(Macro *macro);
125 void showSaveDialog();
128 MacroManager::MacroManagerPrivate::MacroManagerPrivate(MacroManager *qq):
132 mapper(new QSignalMapper(qq))
134 connect(mapper, SIGNAL(mapped(QString)), q, SLOT(executeMacro(QString)));
136 // Load existing macros
139 actionHandler = new ActionMacroHandler;
140 textEditorHandler = new TextEditorMacroHandler;
141 findHandler = new FindMacroHandler;
144 void MacroManager::MacroManagerPrivate::initialize()
147 QDir dir(q->macrosDirectory());
149 filter << QString("*.")+Constants::M_EXTENSION;
150 QStringList files = dir.entryList(filter, QDir::Files);
152 foreach (const QString &name, files) {
153 QString fileName = dir.absolutePath() + '/' + name;
154 Macro *macro = new Macro;
155 if (macro->loadHeader(fileName))
162 void MacroManager::MacroManagerPrivate::addMacro(Macro *macro)
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());
175 // Add macro to the map
176 macros[macro->displayName()] = macro;
179 void MacroManager::MacroManagerPrivate::removeMacro(const QString &name)
181 if (!macros.contains(name))
184 Core::ICore *core = Core::ICore::instance();
185 Core::ActionManager *am = core->actionManager();
186 am->unregisterShortcut(Core::Id(Constants::PREFIX_MACRO+name));
188 // Remove macro from the map
189 Macro *macro = macros.take(name);
193 void MacroManager::MacroManagerPrivate::changeMacroDescription(Macro *macro, const QString &description)
197 macro->setDescription(description);
198 macro->save(macro->fileName(), Core::ICore::instance()->mainWindow());
200 // Change shortcut what's this
201 Core::ICore *core = Core::ICore::instance();
202 Core::ActionManager *am = core->actionManager();
204 Core::Command *command = am->command(Core::Id(Constants::PREFIX_MACRO+macro->displayName()));
205 if (command && command->shortcut())
206 command->shortcut()->setWhatsThis(description);
209 bool MacroManager::MacroManagerPrivate::executeMacro(Macro *macro)
211 bool error = !macro->load();
212 foreach (const MacroEvent ¯oEvent, macro->events()) {
215 foreach (IMacroHandler *handler, handlers) {
216 if (handler->canExecuteEvent(macroEvent)) {
217 if (!handler->executeEvent(macroEvent))
225 QMessageBox::warning(Core::ICore::instance()->mainWindow(),
227 tr("An error occurred while replaying the macro, execution stopped."));
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);
239 void MacroManager::MacroManagerPrivate::showSaveDialog()
241 QMainWindow *mainWindow = Core::ICore::instance()->mainWindow();
242 SaveDialog dialog(mainWindow);
244 if (dialog.name().isEmpty())
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);
257 // ---------- MacroManager ------------
258 MacroManager *MacroManager::m_instance = 0;
260 MacroManager::MacroManager(QObject *parent) :
262 d(new MacroManagerPrivate(this))
264 registerMacroHandler(d->actionHandler);
265 registerMacroHandler(d->findHandler);
266 registerMacroHandler(d->textEditorHandler);
270 MacroManager::~MacroManager()
273 QStringList macroList = d->macros.keys();
274 foreach (const QString &name, macroList)
275 d->removeMacro(name);
278 qDeleteAll(d->handlers);
283 void MacroManager::startMacro()
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;
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);
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),
306 tr("Stop Recording Macro"), this, SLOT(endMacro()));
309 void MacroManager::endMacro()
311 Core::EditorManager::instance()->hideEditorStatusBar(QLatin1String(Constants::M_STATUS_BUFFER));
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);
321 d->isRecording = false;
324 void MacroManager::executeLastMacro()
326 if (!d->currentMacro)
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);
336 d->executeMacro(d->currentMacro);
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);
344 bool MacroManager::executeMacro(const QString &name)
346 // Don't execute macro while recording
347 if (d->isRecording || !d->macros.contains(name))
350 Macro *macro = d->macros.value(name);
351 if (!d->executeMacro(macro))
354 // Delete anonymous macro
355 if (d->currentMacro && d->currentMacro->displayName().isEmpty())
356 delete d->currentMacro;
357 d->currentMacro = macro;
359 Core::ActionManager *am = Core::ICore::instance()->actionManager();
360 am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true);
365 void MacroManager::deleteMacro(const QString &name)
367 Macro *macro = d->macros.value(name);
369 QString fileName = macro->fileName();
370 d->removeMacro(name);
371 QFile::remove(fileName);
375 const QMap<QString,Macro*> &MacroManager::macros() const
380 void MacroManager::registerMacroHandler(IMacroHandler *handler)
382 d->handlers.prepend(handler);
385 MacroManager *MacroManager::instance()
390 void MacroManager::changeMacro(const QString &name, const QString &description)
392 if (!d->macros.contains(name))
394 Macro *macro = d->macros.value(name);
396 // Change description
397 if (macro->description() != description)
398 d->changeMacroDescription(macro, description);
401 void Macros::MacroManager::saveLastMacro()
403 if (d->currentMacro->events().count())
407 QString Macros::MacroManager::macrosDirectory() const
409 const QString &path =
410 Core::ICore::instance()->userResourcePath() + QLatin1String("/macros");
411 if (QFile::exists(path) || QDir().mkpath(path))