1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nicolas Arnaud-Cormos.
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "macromanager.h"
36 #include "macrosconstants.h"
37 #include "macroevent.h"
39 #include "imacrohandler.h"
40 #include "savedialog.h"
41 #include "actionmacrohandler.h"
42 #include "texteditormacrohandler.h"
43 #include "findmacrohandler.h"
45 #include <texteditor/texteditorconstants.h>
47 #include <coreplugin/coreconstants.h>
48 #include <coreplugin/actionmanager/actionmanager.h>
49 #include <coreplugin/actionmanager/actioncontainer.h>
50 #include <coreplugin/actionmanager/command.h>
51 #include <coreplugin/icore.h>
52 #include <coreplugin/uniqueidmanager.h>
53 #include <coreplugin/icontext.h>
54 #include <coreplugin/editormanager/editormanager.h>
55 #include <coreplugin/editormanager/ieditor.h>
57 #include <QtCore/QDir>
58 #include <QtCore/QFile>
59 #include <QtCore/QFileInfo>
60 #include <QtCore/QSettings>
61 #include <QtCore/QSignalMapper>
62 #include <QtCore/QList>
64 #include <QtGui/QShortcut>
65 #include <QtGui/QMainWindow>
66 #include <QtGui/QAction>
67 #include <QtGui/QFileDialog>
68 #include <QtGui/QMessageBox>
70 using namespace Macros;
71 using namespace Macros::Internal;
75 \brief The Macros namespace contains support for macros in Qt Creator.
80 \class Macro::MacroManager
81 \brief Manager for macros.
83 The MacroManager manage all macros, it loads them on startup, keep track of the
84 current macro and create new macros.
86 There are two important methods in this class that can be used outside the Macros plugin:
88 \o registerEventHandler: add a new event handler
89 \o registerAction: add a macro event when this action is triggered
92 This class is a singleton and can be accessed using the instance method.
96 \fn void registerAction(QAction *action, const QString &id)
98 Append this action to the list of actions registered in a macro. The id is
99 the action id passed to the ActionManager.
102 class Macros::MacroManager::MacroManagerPrivate
105 MacroManagerPrivate(MacroManager *qq);
108 QMap<QString, Macro *> macros;
112 QList<IMacroHandler*> handlers;
114 QSignalMapper *mapper;
116 ActionMacroHandler *actionHandler;
117 TextEditorMacroHandler *textEditorHandler;
118 FindMacroHandler *findHandler;
121 void addMacro(Macro *macro);
122 void removeMacro(const QString &name);
123 void changeMacroDescription(Macro *macro, const QString &description);
125 bool executeMacro(Macro *macro);
126 void showSaveDialog();
129 MacroManager::MacroManagerPrivate::MacroManagerPrivate(MacroManager *qq):
133 mapper(new QSignalMapper(qq))
135 connect(mapper, SIGNAL(mapped(QString)), q, SLOT(executeMacro(QString)));
137 // Load existing macros
140 actionHandler = new ActionMacroHandler;
141 textEditorHandler = new TextEditorMacroHandler;
142 findHandler = new FindMacroHandler;
145 void MacroManager::MacroManagerPrivate::initialize()
148 QDir dir(q->macrosDirectory());
150 filter << QString("*.")+Constants::M_EXTENSION;
151 QStringList files = dir.entryList(filter, QDir::Files);
153 foreach (const QString &name, files) {
154 QString fileName = dir.absolutePath() + '/' + name;
155 Macro *macro = new Macro;
156 macro->loadHeader(fileName);
161 void MacroManager::MacroManagerPrivate::addMacro(Macro *macro)
164 Core::Context context(TextEditor::Constants::C_TEXTEDITOR);
165 Core::ICore *core = Core::ICore::instance();
166 Core::ActionManager *am = core->actionManager();
167 QShortcut *shortcut = new QShortcut(core->mainWindow());
168 shortcut->setWhatsThis(macro->description());
169 const QString macroId = QLatin1String(Constants::PREFIX_MACRO) + macro->displayName();
170 am->registerShortcut(shortcut, macroId, context);
171 connect(shortcut, SIGNAL(activated()), mapper, SLOT(map()));
172 mapper->setMapping(shortcut, macro->displayName());
174 // Add macro to the map
175 macros[macro->displayName()] = macro;
178 void MacroManager::MacroManagerPrivate::removeMacro(const QString &name)
180 if (!macros.contains(name))
183 Core::ICore *core = Core::ICore::instance();
184 Core::ActionManager *am = core->actionManager();
185 am->unregisterShortcut(Core::Id(Constants::PREFIX_MACRO+name));
187 // Remove macro from the map
188 Macro *macro = macros.take(name);
192 void MacroManager::MacroManagerPrivate::changeMacroDescription(Macro *macro, const QString &description)
195 macro->setDescription(description);
196 macro->save(macro->fileName());
198 // Change shortcut what's this
199 Core::ICore *core = Core::ICore::instance();
200 Core::ActionManager *am = core->actionManager();
202 Core::Command *command = am->command(Core::Id(Constants::PREFIX_MACRO+macro->displayName()));
203 if (command && command->shortcut())
204 command->shortcut()->setWhatsThis(description);
207 bool MacroManager::MacroManagerPrivate::executeMacro(Macro *macro)
211 foreach (const MacroEvent ¯oEvent, macro->events()) {
212 foreach (IMacroHandler *handler, handlers) {
213 if (handler->canExecuteEvent(macroEvent)) {
214 if (!handler->executeEvent(macroEvent))
224 QMessageBox::warning(Core::ICore::instance()->mainWindow(),
226 tr("An error occured while replaying the macro, execution stopped."));
229 // Set the focus back to the editor
230 // TODO: is it really needed??
231 const Core::EditorManager *editorManager = Core::EditorManager::instance();
232 if (editorManager->currentEditor())
233 editorManager->currentEditor()->widget()->setFocus(Qt::OtherFocusReason);
238 void MacroManager::MacroManagerPrivate::showSaveDialog()
240 QMainWindow *mainWindow = Core::ICore::instance()->mainWindow();
241 SaveDialog dialog(mainWindow);
243 if (dialog.name().isEmpty())
246 // Save in the resource path
247 QString fileName = q->macrosDirectory() + '/' + dialog.name()
248 + '.' + Constants::M_EXTENSION;
249 currentMacro->setDescription(dialog.description());
250 currentMacro->save(fileName);
251 addMacro(currentMacro);
256 // ---------- MacroManager ------------
257 MacroManager *MacroManager::m_instance = 0;
259 MacroManager::MacroManager(QObject *parent) :
261 d(new MacroManagerPrivate(this))
263 registerMacroHandler(d->actionHandler);
264 registerMacroHandler(d->findHandler);
265 registerMacroHandler(d->textEditorHandler);
269 MacroManager::~MacroManager()
272 QStringList macroList = d->macros.keys();
273 foreach (const QString &name, macroList)
274 d->removeMacro(name);
277 qDeleteAll(d->handlers);
282 void MacroManager::startMacro()
284 d->isRecording = true;
285 // Delete anonymous macro
286 if (d->currentMacro && d->currentMacro->displayName().isEmpty())
287 delete d->currentMacro;
288 d->currentMacro = new Macro;
290 Core::ActionManager *am = Core::ICore::instance()->actionManager();
291 am->command(Constants::START_MACRO)->action()->setEnabled(false);
292 am->command(Constants::END_MACRO)->action()->setEnabled(true);
293 am->command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(false);
294 am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(false);
295 foreach (IMacroHandler *handler, d->handlers)
296 handler->startRecording(d->currentMacro);
298 QString endShortcut = am->command(Constants::END_MACRO)->defaultKeySequence().toString();
299 QString executeShortcut = am->command(Constants::EXECUTE_LAST_MACRO)->defaultKeySequence().toString();
300 QString help = tr("Macro mode. Type \"%1\" to stop recording and \"%2\" to play it")
301 .arg(endShortcut).arg(executeShortcut);
302 Core::EditorManager::instance()->showEditorStatusBar(
303 QLatin1String(Constants::M_STATUS_BUFFER),
305 tr("Stop Recording Macro"), this, SLOT(endMacro()));
308 void MacroManager::endMacro()
310 Core::EditorManager::instance()->hideEditorStatusBar(QLatin1String(Constants::M_STATUS_BUFFER));
312 Core::ActionManager *am = Core::ICore::instance()->actionManager();
313 am->command(Constants::START_MACRO)->action()->setEnabled(true);
314 am->command(Constants::END_MACRO)->action()->setEnabled(false);
315 am->command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(true);
316 am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true);
317 foreach (IMacroHandler *handler, d->handlers)
318 handler->endRecordingMacro(d->currentMacro);
320 d->isRecording = false;
323 void MacroManager::executeLastMacro()
326 d->executeMacro(d->currentMacro);
329 bool MacroManager::executeMacro(const QString &name)
331 // Don't execute macro while recording
332 if (d->isRecording || !d->macros.contains(name))
335 Macro *macro = d->macros.value(name);
336 if (!d->executeMacro(macro))
339 // Delete anonymous macro
340 if (d->currentMacro && d->currentMacro->displayName().isEmpty())
341 delete d->currentMacro;
342 d->currentMacro = macro;
344 Core::ActionManager *am = Core::ICore::instance()->actionManager();
345 am->command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true);
350 void MacroManager::deleteMacro(const QString &name)
352 Macro *macro = d->macros.value(name);
354 QString fileName = macro->fileName();
355 d->removeMacro(name);
356 QFile::remove(fileName);
360 const QMap<QString,Macro*> &MacroManager::macros() const
365 void MacroManager::registerMacroHandler(IMacroHandler *handler)
367 d->handlers.prepend(handler);
370 MacroManager *MacroManager::instance()
375 void MacroManager::changeMacro(const QString &name, const QString &description)
377 if (!d->macros.contains(name))
379 Macro *macro = d->macros.value(name);
381 // Change description
382 if (macro->description() != description)
383 d->changeMacroDescription(macro, description);
386 void Macros::MacroManager::saveLastMacro()
388 if (d->currentMacro->events().count())
392 QString Macros::MacroManager::macrosDirectory() const
394 const QString &path =
395 Core::ICore::instance()->userResourcePath() + QLatin1String("/macros");
396 if (QFile::exists(path) || QDir().mkpath(path))