texteditoractionhandler.cpp 30.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31
#include "texteditoractionhandler.h"
32

33
#include "texteditor.h"
34
#include "displaysettings.h"
con's avatar
con committed
35
#include "linenumberfilter.h"
36
37
#include "texteditorconstants.h"
#include "texteditorplugin.h"
con's avatar
con committed
38

39
#include <coreplugin/locator/locatormanager.h>
40
#include <coreplugin/icore.h>
41
#include <coreplugin/editormanager/editormanager.h>
con's avatar
con committed
42
#include <coreplugin/coreconstants.h>
43
#include <coreplugin/actionmanager/actionmanager.h>
44
#include <coreplugin/actionmanager/actioncontainer.h>
45
#include <coreplugin/actionmanager/command.h>
46
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
47
#include <utils/qtcassert.h>
con's avatar
con committed
48

hjk's avatar
hjk committed
49
#include <QDebug>
50
#include <QAction>
con's avatar
con committed
51

52
53
#include <functional>

54
55
namespace TextEditor {
namespace Internal {
con's avatar
con committed
56

57
58
class TextEditorActionHandlerPrivate : public QObject
{
59
    Q_DECLARE_TR_FUNCTIONS(TextEditor::Internal::TextEditorActionHandler)
60
61
62
63
64
public:
    TextEditorActionHandlerPrivate(TextEditorActionHandler *parent,
                                   Core::Id contextId,
                                   uint optionalActions);

65
    QAction *registerActionHelper(Core::Id id, bool scriptable, const QString &title,
66
                            const QKeySequence &keySequence, Core::Id menueGroup,
67
68
69
70
71
72
73
74
                            Core::ActionContainer *container,
                            std::function<void(bool)> slot)
    {
        QAction *result = new QAction(title, this);
        Core::Command *command = Core::ActionManager::registerAction(result, id, Core::Context(m_contextId), scriptable);
        if (!keySequence.isEmpty())
            command->setDefaultKeySequence(keySequence);

75
        if (container && menueGroup.isValid())
76
77
78
79
80
81
            container->addAction(command, menueGroup);

        connect(result, &QAction::triggered, slot);
        return result;
    }

82
    QAction *registerAction(Core::Id id,
83
                            std::function<void(TextEditorWidget *)> slot,
84
85
86
                            bool scriptable = false,
                            const QString &title = QString(),
                            const QKeySequence &keySequence = QKeySequence(),
87
                            Core::Id menueGroup = Core::Id(),
88
89
90
91
92
93
94
                            Core::ActionContainer *container = 0)
    {
        return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container,
            [this, slot](bool) { if (m_currentEditorWidget) slot(m_currentEditorWidget); });
    }

    QAction *registerBoolAction(Core::Id id,
95
                            std::function<void(TextEditorWidget *, bool)> slot,
96
97
98
                            bool scriptable = false,
                            const QString &title = QString(),
                            const QKeySequence &keySequence = QKeySequence(),
99
                            Core::Id menueGroup = Core::Id(),
100
101
102
103
104
105
106
                            Core::ActionContainer *container = 0)
    {
        return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container,
            [this, slot](bool on) { if (m_currentEditorWidget) slot(m_currentEditorWidget, on); });
    }

    QAction *registerIntAction(Core::Id id,
107
                            std::function<void(TextEditorWidget *, int)> slot,
108
109
110
                            bool scriptable = false,
                            const QString &title = QString(),
                            const QKeySequence &keySequence = QKeySequence(),
111
                            Core::Id menueGroup = Core::Id(),
112
113
114
115
116
                            Core::ActionContainer *container = 0)
    {
        return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container,
            [this, slot](bool on) { if (m_currentEditorWidget) slot(m_currentEditorWidget, on); });
    }
117
118
119
120

    void createActions();

    void updateActions();
121
122
123
124
    void updateRedoAction(bool on);
    void updateUndoAction(bool on);
    void updateCopyAction(bool on);

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
    void updateCurrentEditor(Core::IEditor *editor);

public:
    TextEditorActionHandler *q;
    QAction *m_undoAction;
    QAction *m_redoAction;
    QAction *m_copyAction;
    QAction *m_cutAction;
    QAction *m_pasteAction;
    QAction *m_circularPasteAction;
    QAction *m_switchUtf8bomAction;
    QAction *m_selectAllAction;
    QAction *m_gotoAction;
    QAction *m_printAction;
    QAction *m_formatAction;
    QAction *m_rewrapParagraphAction;
    QAction *m_visualizeWhitespaceAction;
    QAction *m_cleanWhitespaceAction;
    QAction *m_textWrappingAction;
    QAction *m_unCommentSelectionAction;
    QAction *m_unfoldAllAction;
    QAction *m_foldAction;
    QAction *m_unfoldAction;
    QAction *m_cutLineAction;
    QAction *m_copyLineAction;
    QAction *m_deleteLineAction;
    QAction *m_deleteEndOfWordAction;
    QAction *m_deleteEndOfWordCamelCaseAction;
    QAction *m_deleteStartOfWordAction;
    QAction *m_deleteStartOfWordCamelCaseAction;
    QAction *m_selectEncodingAction;
    QAction *m_increaseFontSizeAction;
    QAction *m_decreaseFontSizeAction;
    QAction *m_resetFontSizeAction;
    QAction *m_gotoBlockStartAction;
    QAction *m_gotoBlockEndAction;
    QAction *m_gotoBlockStartWithSelectionAction;
    QAction *m_gotoBlockEndWithSelectionAction;
    QAction *m_selectBlockUpAction;
    QAction *m_selectBlockDownAction;
    QAction *m_viewPageUpAction;
    QAction *m_viewPageDownAction;
    QAction *m_viewLineUpAction;
    QAction *m_viewLineDownAction;
    QAction *m_moveLineUpAction;
    QAction *m_moveLineDownAction;
    QAction *m_copyLineUpAction;
    QAction *m_copyLineDownAction;
    QAction *m_joinLinesAction;
    QAction *m_insertLineAboveAction;
    QAction *m_insertLineBelowAction;
    QAction *m_upperCaseSelectionAction;
    QAction *m_lowerCaseSelectionAction;
    QAction *m_indentAction;
    QAction *m_unindentAction;
    QAction *m_followSymbolAction;
    QAction *m_followSymbolInNextSplitAction;
    QAction *m_jumpToFileAction;
    QAction *m_jumpToFileInNextSplitAction;
    QList<QAction *> m_modifyingActions;

    uint m_optionalActions;
187
    QPointer<TextEditorWidget> m_currentEditorWidget;
188
189
190
191
192
193
    Core::Id m_contextId;
};

TextEditorActionHandlerPrivate::TextEditorActionHandlerPrivate
    (TextEditorActionHandler *parent, Core::Id contextId, uint optionalActions)
  : q(parent),
194
195
196
197
198
    m_undoAction(0),
    m_redoAction(0),
    m_copyAction(0),
    m_cutAction(0),
    m_pasteAction(0),
199
    m_circularPasteAction(0),
200
    m_switchUtf8bomAction(0),
201
202
203
204
205
206
207
208
    m_selectAllAction(0),
    m_gotoAction(0),
    m_printAction(0),
    m_formatAction(0),
    m_visualizeWhitespaceAction(0),
    m_cleanWhitespaceAction(0),
    m_textWrappingAction(0),
    m_unCommentSelectionAction(0),
mae's avatar
mae committed
209
210
211
    m_unfoldAllAction(0),
    m_foldAction(0),
    m_unfoldAction(0),
212
    m_cutLineAction(0),
213
    m_copyLineAction(0),
214
    m_deleteLineAction(0),
215
216
217
218
    m_deleteEndOfWordAction(0),
    m_deleteEndOfWordCamelCaseAction(0),
    m_deleteStartOfWordAction(0),
    m_deleteStartOfWordCamelCaseAction(0),
219
220
221
    m_selectEncodingAction(0),
    m_increaseFontSizeAction(0),
    m_decreaseFontSizeAction(0),
mae's avatar
mae committed
222
    m_resetFontSizeAction(0),
223
224
225
226
227
228
    m_gotoBlockStartAction(0),
    m_gotoBlockEndAction(0),
    m_gotoBlockStartWithSelectionAction(0),
    m_gotoBlockEndWithSelectionAction(0),
    m_selectBlockUpAction(0),
    m_selectBlockDownAction(0),
229
230
231
232
    m_viewPageUpAction(0),
    m_viewPageDownAction(0),
    m_viewLineUpAction(0),
    m_viewLineDownAction(0),
233
234
235
236
    m_moveLineUpAction(0),
    m_moveLineDownAction(0),
    m_copyLineUpAction(0),
    m_copyLineDownAction(0),
237
    m_joinLinesAction(0),
238
239
240
241
    m_insertLineAboveAction(0),
    m_insertLineBelowAction(0),
    m_upperCaseSelectionAction(0),
    m_lowerCaseSelectionAction(0),
242
243
    m_indentAction(0),
    m_unindentAction(0),
244
    m_followSymbolAction(0),
245
    m_followSymbolInNextSplitAction(0),
246
    m_jumpToFileAction(0),
247
    m_jumpToFileInNextSplitAction(0),
con's avatar
con committed
248
    m_optionalActions(optionalActions),
249
250
    m_currentEditorWidget(0),
    m_contextId(contextId)
con's avatar
con committed
251
{
252
    createActions();
253
254
    connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
        this, &TextEditorActionHandlerPrivate::updateCurrentEditor);
con's avatar
con committed
255
256
}

257
void TextEditorActionHandlerPrivate::createActions()
con's avatar
con committed
258
{
259
260
261
262
    using namespace Core::Constants;
    using namespace TextEditor::Constants;

    m_undoAction = registerAction(UNDO,
263
            [this] (TextEditorWidget *w) { w->undo(); }, true, tr("&Undo"));
264
    m_redoAction = registerAction(REDO,
265
            [this] (TextEditorWidget *w) { w->redo(); }, true, tr("&Redo"));
266
    m_copyAction = registerAction(COPY,
267
            [this] (TextEditorWidget *w) { w->copy(); }, true);
268
    m_cutAction = registerAction(CUT,
269
            [this] (TextEditorWidget *w) { w->cut(); }, true);
270
    m_pasteAction = registerAction(PASTE,
271
            [this] (TextEditorWidget *w) { w->paste(); }, true);
272
    m_selectAllAction = registerAction(SELECTALL,
273
274
            [this] (TextEditorWidget *w) { w->selectAll(); }, true);
    m_gotoAction = registerAction(GOTO, [this] (TextEditorWidget *) {
275
276
277
278
279
280
            QString locatorString = TextEditorPlugin::lineNumberFilter()->shortcutString();
            locatorString += QLatin1Char(' ');
            const int selectionStart = locatorString.size();
            locatorString += TextEditorActionHandler::tr("<line>:<column>");
            Core::LocatorManager::show(locatorString, selectionStart, locatorString.size() - selectionStart);
        });
hjk's avatar
hjk committed
281
    m_printAction = registerAction(PRINT,
282
            [this] (TextEditorWidget *widget) { widget->print(Core::ICore::printer()); });
283
    m_deleteLineAction = registerAction(DELETE_LINE,
284
            [this] (TextEditorWidget *w) { w->deleteLine(); }, true, tr("Delete &Line"));
285
    m_deleteEndOfWordAction = registerAction(DELETE_END_OF_WORD,
286
            [this] (TextEditorWidget *w) { w->deleteEndOfWord(); }, true, tr("Delete Word from Cursor On"));
287
    m_deleteEndOfWordCamelCaseAction = registerAction(DELETE_END_OF_WORD_CAMEL_CASE,
288
            [this] (TextEditorWidget *w) { w->deleteEndOfWordCamelCase(); }, true, tr("Delete Word Camel Case from Cursor On"));
289
    m_deleteStartOfWordAction = registerAction(DELETE_START_OF_WORD,
290
            [this] (TextEditorWidget *w) { w->deleteStartOfWord(); }, true, tr("Delete Word up to Cursor"));
291
    m_deleteStartOfWordCamelCaseAction = registerAction(DELETE_START_OF_WORD_CAMEL_CASE,
292
            [this] (TextEditorWidget *w) { w->deleteStartOfWordCamelCase(); }, true, tr("Delete Word Camel Case up to Cursor"));
293
    m_gotoBlockStartWithSelectionAction = registerAction(GOTO_BLOCK_START_WITH_SELECTION,
294
            [this] (TextEditorWidget *w) { w->gotoBlockStartWithSelection(); }, true, tr("Go to Block Start with Selection"),
295
296
            QKeySequence(tr("Ctrl+{")));
    m_gotoBlockEndWithSelectionAction = registerAction(GOTO_BLOCK_END_WITH_SELECTION,
297
            [this] (TextEditorWidget *w) { w->gotoBlockEndWithSelection(); }, true, tr("Go to Block End with Selection"),
298
299
            QKeySequence(tr("Ctrl+}")));
    m_moveLineUpAction = registerAction(MOVE_LINE_UP,
300
            [this] (TextEditorWidget *w) { w->moveLineUp(); }, true, tr("Move Line Up"),
301
302
            QKeySequence(tr("Ctrl+Shift+Up")));
    m_moveLineDownAction = registerAction(MOVE_LINE_DOWN,
303
            [this] (TextEditorWidget *w) { w->moveLineDown(); }, true, tr("Move Line Down"),
304
305
            QKeySequence(tr("Ctrl+Shift+Down")));
    m_copyLineUpAction = registerAction(COPY_LINE_UP,
306
            [this] (TextEditorWidget *w) { w->copyLineUp(); }, true, tr("Copy Line Up"),
307
308
            QKeySequence(tr("Ctrl+Alt+Up")));
    m_copyLineDownAction = registerAction(COPY_LINE_DOWN,
309
            [this] (TextEditorWidget *w) { w->copyLineDown(); }, true, tr("Copy Line Down"),
310
311
            QKeySequence(tr("Ctrl+Alt+Down")));
    m_joinLinesAction = registerAction(JOIN_LINES,
312
            [this] (TextEditorWidget *w) { w->joinLines(); }, true, tr("Join Lines"),
313
314
            QKeySequence(tr("Ctrl+J")));
    m_insertLineAboveAction = registerAction(INSERT_LINE_ABOVE,
315
            [this] (TextEditorWidget *w) { w->insertLineAbove(); }, true, tr("Insert Line Above Current Line"),
316
317
            QKeySequence(tr("Ctrl+Shift+Return")));
    m_insertLineBelowAction = registerAction(INSERT_LINE_BELOW,
318
            [this] (TextEditorWidget *w) { w->insertLineBelow(); }, true, tr("Insert Line Below Current Line"),
319
320
            QKeySequence(tr("Ctrl+Return")));
    m_switchUtf8bomAction = registerAction(SWITCH_UTF8BOM,
321
            [this] (TextEditorWidget *w) { w->switchUtf8bom(); }, true);
322
    m_indentAction = registerAction(INDENT,
323
            [this] (TextEditorWidget *w) { w->indent(); }, true, tr("Indent"));
324
    m_unindentAction = registerAction(UNINDENT,
325
            [this] (TextEditorWidget *w) { w->unindent(); }, true, tr("Unindent"));
326
    m_followSymbolAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR,
327
            [this] (TextEditorWidget *w) { w->openLinkUnderCursor(); }, true, tr("Follow Symbol Under Cursor"),
328
329
            QKeySequence(Qt::Key_F2));
    m_followSymbolInNextSplitAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT,
330
            [this] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, tr("Follow Symbol Under Cursor in Next Split"),
331
332
            QKeySequence(Utils::HostOsInfo::isMacHost() ? tr("Meta+E, F2") : tr("Ctrl+E, F2")));
    m_jumpToFileAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR,
333
            [this] (TextEditorWidget *w) { w->openLinkUnderCursor(); }, true, tr("Jump to File Under Cursor"),
334
335
            QKeySequence(Qt::Key_F2));
    m_jumpToFileInNextSplitAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT,
336
            [this] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, tr("Jump to File Under Cursor in Next Split"),
337
            QKeySequence(Utils::HostOsInfo::isMacHost() ? tr("Meta+E, F2") : tr("Ctrl+E, F2")).toString());
338

339
    m_viewPageUpAction = registerAction(VIEW_PAGE_UP,
340
            [this] (TextEditorWidget *w) { w->viewPageUp(); }, true, tr("Move the View a Page Up and Keep the Cursor Position"),
341
342
            QKeySequence(tr("Ctrl+PgUp")));
    m_viewPageDownAction = registerAction(VIEW_PAGE_DOWN,
343
            [this] (TextEditorWidget *w) { w->viewPageDown(); }, true, tr("Move the View a Page Down and Keep the Cursor Position"),
344
345
            QKeySequence(tr("Ctrl+PgDown")));
    m_viewLineUpAction = registerAction(VIEW_LINE_UP,
346
            [this] (TextEditorWidget *w) { w->viewLineUp(); }, true, tr("Move the View a Line Up and Keep the Cursor Position"),
347
348
            QKeySequence(tr("Ctrl+Up")));
    m_viewLineDownAction = registerAction(VIEW_LINE_DOWN,
349
            [this] (TextEditorWidget *w) { w->viewLineDown(); }, true, tr("Move the View a Line Down and Keep the Cursor Position"),
350
351
            QKeySequence(tr("Ctrl+Down")));

352
353
354
    // register "Edit" Menu Actions
    Core::ActionContainer *editMenu = Core::ActionManager::actionContainer(M_EDIT);
    m_selectEncodingAction = registerAction(SELECT_ENCODING,
355
            [this] (TextEditorWidget *w) { w->selectEncoding(); }, false, tr("Select Encoding..."),
356
357
            QKeySequence(), G_EDIT_OTHER, editMenu);
    m_circularPasteAction = registerAction(CIRCULAR_PASTE,
358
            [this] (TextEditorWidget *w) { w->circularPaste(); }, false, tr("Paste from Clipboard History"),
359
360
361
362
363
            QKeySequence(tr("Ctrl+Shift+V")), G_EDIT_COPYPASTE, editMenu);

    // register "Edit -> Advanced" Menu Actions
    Core::ActionContainer *advancedEditMenu = Core::ActionManager::actionContainer(M_EDIT_ADVANCED);
    m_formatAction = registerAction(AUTO_INDENT_SELECTION,
364
            [this] (TextEditorWidget *w) { w->format(); }, true, tr("Auto-&indent Selection"),
365
366
367
            QKeySequence(tr("Ctrl+I")),
            G_EDIT_FORMAT, advancedEditMenu);
    m_rewrapParagraphAction = registerAction(REWRAP_PARAGRAPH,
368
            [this] (TextEditorWidget *w) { w->rewrapParagraph(); }, true, tr("&Rewrap Paragraph"),
369
370
            QKeySequence(Core::UseMacShortcuts ? tr("Meta+E, R") : tr("Ctrl+E, R")),
            G_EDIT_FORMAT, advancedEditMenu);
371
    m_visualizeWhitespaceAction = registerBoolAction(VISUALIZE_WHITESPACE,
372
            [this] (TextEditorWidget *widget, bool checked) {
373
374
375
376
377
378
379
                if (widget) {
                     DisplaySettings ds = widget->displaySettings();
                     ds.m_visualizeWhitespace = checked;
                     widget->setDisplaySettings(ds);
                }
            },
            false, tr("&Visualize Whitespace"),
380
381
            QKeySequence(Core::UseMacShortcuts ? tr("Meta+E, Meta+V") : tr("Ctrl+E, Ctrl+V")),
            G_EDIT_FORMAT, advancedEditMenu);
382
    m_visualizeWhitespaceAction->setCheckable(true);
383
    m_cleanWhitespaceAction = registerAction(CLEAN_WHITESPACE,
384
            [this] (TextEditorWidget *w) { w->cleanWhitespace(); }, true, tr("Clean Whitespace"),
385
            QKeySequence(),
Nikolai Kosjar's avatar
Nikolai Kosjar committed
386
            G_EDIT_FORMAT, advancedEditMenu);
387
    m_textWrappingAction = registerBoolAction(TEXT_WRAPPING,
388
            [this] (TextEditorWidget *widget, bool checked) {
389
390
391
392
393
394
395
                if (widget) {
                    DisplaySettings ds = widget->displaySettings();
                    ds.m_textWrapping = checked;
                    widget->setDisplaySettings(ds);
                }
            },
            false, tr("Enable Text &Wrapping"),
396
397
398
399
            QKeySequence(Core::UseMacShortcuts ? tr("Meta+E, Meta+W") : tr("Ctrl+E, Ctrl+W")),
            G_EDIT_FORMAT, advancedEditMenu);
    m_textWrappingAction->setCheckable(true);
    m_unCommentSelectionAction = registerAction(UN_COMMENT_SELECTION,
400
            [this] (TextEditorWidget *w) { w->unCommentSelection(); }, true, tr("Toggle Comment &Selection"),
401
402
            QKeySequence(tr("Ctrl+/")),
            G_EDIT_FORMAT, advancedEditMenu);
403
    m_cutLineAction = registerAction(CUT_LINE,
404
            [this] (TextEditorWidget *w) { w->cutLine(); }, true, tr("Cut &Line"),
405
406
407
            QKeySequence(tr("Shift+Del")),
            G_EDIT_TEXT, advancedEditMenu);
    m_copyLineAction = registerAction(COPY_LINE,
408
            [this] (TextEditorWidget *w) { w->copyLine(); }, false, tr("Copy &Line"),
409
410
            QKeySequence(tr("Ctrl+Ins")),
            G_EDIT_TEXT, advancedEditMenu);
411
    m_upperCaseSelectionAction = registerAction(UPPERCASE_SELECTION,
412
            [this] (TextEditorWidget *w) { w->uppercaseSelection(); }, true, tr("Uppercase Selection"),
413
414
415
            QKeySequence(Core::UseMacShortcuts ? tr("Meta+Shift+U") : tr("Alt+Shift+U")),
            G_EDIT_TEXT, advancedEditMenu);
    m_lowerCaseSelectionAction = registerAction(LOWERCASE_SELECTION,
416
            [this] (TextEditorWidget *w) { w->lowercaseSelection(); }, true, tr("Lowercase Selection"),
417
418
            QKeySequence(Core::UseMacShortcuts ? tr("Meta+U") : tr("Alt+U")),
            G_EDIT_TEXT, advancedEditMenu);
419
    m_foldAction = registerAction(FOLD,
420
            [this] (TextEditorWidget *w) { w->fold(); }, true, tr("Fold"),
421
422
423
            QKeySequence(tr("Ctrl+<")),
            G_EDIT_COLLAPSING, advancedEditMenu);
    m_unfoldAction = registerAction(UNFOLD,
424
            [this] (TextEditorWidget *w) { w->unfold(); }, true, tr("Unfold"),
425
426
427
            QKeySequence(tr("Ctrl+>")),
            G_EDIT_COLLAPSING, advancedEditMenu);
    m_unfoldAllAction = registerAction(UNFOLD_ALL,
428
            [this] (TextEditorWidget *w) { w->unfoldAll(); }, true, tr("Toggle &Fold All"),
429
430
431
            QKeySequence(),
            G_EDIT_COLLAPSING, advancedEditMenu);
    m_increaseFontSizeAction = registerAction(INCREASE_FONT_SIZE,
432
            [this] (TextEditorWidget *w) { w->zoomIn(); }, false, tr("Increase Font Size"),
433
434
435
            QKeySequence(tr("Ctrl++")),
            G_EDIT_FONT, advancedEditMenu);
    m_decreaseFontSizeAction = registerAction(DECREASE_FONT_SIZE,
436
            [this] (TextEditorWidget *w) { w->zoomOut(); }, false, tr("Decrease Font Size"),
437
438
439
            QKeySequence(tr("Ctrl+-")),
            G_EDIT_FONT, advancedEditMenu);
    m_resetFontSizeAction = registerAction(RESET_FONT_SIZE,
440
            [this] (TextEditorWidget *w) { w->zoomReset(); }, false, tr("Reset Font Size"),
441
442
443
            QKeySequence(Core::UseMacShortcuts ? tr("Meta+0") : tr("Ctrl+0")),
            G_EDIT_FONT, advancedEditMenu);
    m_gotoBlockStartAction = registerAction(GOTO_BLOCK_START,
444
            [this] (TextEditorWidget *w) { w->gotoBlockStart(); }, true, tr("Go to Block Start"),
445
446
447
            QKeySequence(tr("Ctrl+[")),
            G_EDIT_BLOCKS, advancedEditMenu);
    m_gotoBlockEndAction = registerAction(GOTO_BLOCK_END,
448
            [this] (TextEditorWidget *w) { w->gotoBlockEnd(); }, true, tr("Go to Block End"),
449
450
451
            QKeySequence(tr("Ctrl+]")),
            G_EDIT_BLOCKS, advancedEditMenu);
    m_selectBlockUpAction = registerAction(SELECT_BLOCK_UP,
452
            [this] (TextEditorWidget *w) { w->selectBlockUp(); }, true, tr("Select Block Up"),
453
454
455
            QKeySequence(tr("Ctrl+U")),
            G_EDIT_BLOCKS, advancedEditMenu);
    m_selectBlockDownAction = registerAction(SELECT_BLOCK_DOWN,
456
            [this] (TextEditorWidget *w) { w->selectBlockDown(); }, true, tr("Select Block Down"),
457
458
459
460
461
            QKeySequence(),
            G_EDIT_BLOCKS, advancedEditMenu);

    // register GOTO Actions
    registerAction(GOTO_LINE_START,
462
            [this] (TextEditorWidget *w) { w->gotoLineStart(); }, true, tr("Go to Line Start"));
463
    registerAction(GOTO_LINE_END,
464
            [this] (TextEditorWidget *w) { w->gotoLineEnd(); }, true, tr("Go to Line End"));
465
    registerAction(GOTO_NEXT_LINE,
466
            [this] (TextEditorWidget *w) { w->gotoNextLine(); }, true, tr("Go to Next Line"));
467
    registerAction(GOTO_PREVIOUS_LINE,
468
            [this] (TextEditorWidget *w) { w->gotoPreviousLine(); }, true, tr("Go to Previous Line"));
469
    registerAction(GOTO_PREVIOUS_CHARACTER,
470
            [this] (TextEditorWidget *w) { w->gotoPreviousCharacter(); }, true, tr("Go to Previous Character"));
471
    registerAction(GOTO_NEXT_CHARACTER,
472
            [this] (TextEditorWidget *w) { w->gotoNextCharacter(); }, true, tr("Go to Next Character"));
473
    registerAction(GOTO_PREVIOUS_WORD,
474
            [this] (TextEditorWidget *w) { w->gotoPreviousWord(); }, true, tr("Go to Previous Word"));
475
    registerAction(GOTO_NEXT_WORD,
476
            [this] (TextEditorWidget *w) { w->gotoNextWord(); }, true, tr("Go to Next Word"));
477
    registerAction(GOTO_PREVIOUS_WORD_CAMEL_CASE,
478
            [this] (TextEditorWidget *w) { w->gotoPreviousWordCamelCase(); }, false, tr("Go to Previous Word Camel Case"));
479
    registerAction(GOTO_NEXT_WORD_CAMEL_CASE,
480
            [this] (TextEditorWidget *w) { w->gotoNextWordCamelCase(); }, false, tr("Go to Next Word Camel Case"));
481
482
483

    // register GOTO actions with selection
    registerAction(GOTO_LINE_START_WITH_SELECTION,
484
            [this] (TextEditorWidget *w) { w->gotoLineStartWithSelection(); }, true, tr("Go to Line Start with Selection"));
485
    registerAction(GOTO_LINE_END_WITH_SELECTION,
486
            [this] (TextEditorWidget *w) { w->gotoLineEndWithSelection(); }, true, tr("Go to Line End with Selection"));
487
    registerAction(GOTO_NEXT_LINE_WITH_SELECTION,
488
            [this] (TextEditorWidget *w) { w->gotoNextLineWithSelection(); }, true, tr("Go to Next Line with Selection"));
489
    registerAction(GOTO_PREVIOUS_LINE_WITH_SELECTION,
490
            [this] (TextEditorWidget *w) { w->gotoPreviousLineWithSelection(); }, true, tr("Go to Previous Line with Selection"));
491
    registerAction(GOTO_PREVIOUS_CHARACTER_WITH_SELECTION,
492
            [this] (TextEditorWidget *w) { w->gotoPreviousCharacterWithSelection(); }, true, tr("Go to Previous Character with Selection"));
493
    registerAction(GOTO_NEXT_CHARACTER_WITH_SELECTION,
494
            [this] (TextEditorWidget *w) { w->gotoNextCharacterWithSelection(); }, true, tr("Go to Next Character with Selection"));
495
    registerAction(GOTO_PREVIOUS_WORD_WITH_SELECTION,
496
            [this] (TextEditorWidget *w) { w->gotoPreviousWordWithSelection(); }, true, tr("Go to Previous Word with Selection"));
497
    registerAction(GOTO_NEXT_WORD_WITH_SELECTION,
498
            [this] (TextEditorWidget *w) { w->gotoNextWordWithSelection(); }, true, tr("Go to Next Word with Selection"));
499
    registerAction(GOTO_PREVIOUS_WORD_CAMEL_CASE_WITH_SELECTION,
500
            [this] (TextEditorWidget *w) { w->gotoPreviousWordCamelCaseWithSelection(); }, false, tr("Go to Previous Word Camel Case with Selection"));
501
    registerAction(GOTO_NEXT_WORD_CAMEL_CASE_WITH_SELECTION,
502
            [this] (TextEditorWidget *w) { w->gotoNextWordCamelCaseWithSelection(); }, false, tr("Go to Next Word Camel Case with Selection"));
503
504
505

    // Collect all modifying actions so we can check for them inside a readonly file
    // and disable them
506
507
508
509
510
511
512
    m_modifyingActions << m_pasteAction;
    m_modifyingActions << m_formatAction;
    m_modifyingActions << m_rewrapParagraphAction;
    m_modifyingActions << m_cleanWhitespaceAction;
    m_modifyingActions << m_unCommentSelectionAction;
    m_modifyingActions << m_cutLineAction;
    m_modifyingActions << m_deleteLineAction;
513
514
515
516
    m_modifyingActions << m_deleteEndOfWordAction;
    m_modifyingActions << m_deleteEndOfWordCamelCaseAction;
    m_modifyingActions << m_deleteStartOfWordAction;
    m_modifyingActions << m_deleteStartOfWordCamelCaseAction;
517
518
519
520
521
522
523
524
525
    m_modifyingActions << m_moveLineUpAction;
    m_modifyingActions << m_moveLineDownAction;
    m_modifyingActions << m_copyLineUpAction;
    m_modifyingActions << m_copyLineDownAction;
    m_modifyingActions << m_joinLinesAction;
    m_modifyingActions << m_insertLineAboveAction;
    m_modifyingActions << m_insertLineBelowAction;
    m_modifyingActions << m_upperCaseSelectionAction;
    m_modifyingActions << m_lowerCaseSelectionAction;
526
    m_modifyingActions << m_circularPasteAction;
527
    m_modifyingActions << m_switchUtf8bomAction;
528
529
    m_modifyingActions << m_indentAction;
    m_modifyingActions << m_unindentAction;
530
531

    // set enabled state of optional actions
532
533
534
535
536
    m_followSymbolAction->setEnabled(m_optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor);
    m_followSymbolInNextSplitAction->setEnabled(m_optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor);
    m_jumpToFileAction->setEnabled(m_optionalActions & TextEditorActionHandler::JumpToFileUnderCursor);
    m_jumpToFileInNextSplitAction->setEnabled(m_optionalActions & TextEditorActionHandler::JumpToFileUnderCursor);
    m_unfoldAllAction->setEnabled(m_optionalActions & TextEditorActionHandler::UnCollapseAll);
con's avatar
con committed
537
538
}

539
void TextEditorActionHandlerPrivate::updateActions()
con's avatar
con committed
540
{
541
542
    QTC_ASSERT(m_currentEditorWidget, return);
    bool isWritable = !m_currentEditorWidget->isReadOnly();
543
    foreach (QAction *a, m_modifyingActions)
544
        a->setEnabled(isWritable);
545
546
    m_formatAction->setEnabled((m_optionalActions & TextEditorActionHandler::Format) && isWritable);
    m_unCommentSelectionAction->setEnabled((m_optionalActions & TextEditorActionHandler::UnCommentSelection) && isWritable);
547
548
    m_visualizeWhitespaceAction->setChecked(m_currentEditorWidget->displaySettings().m_visualizeWhitespace);
    m_textWrappingAction->setChecked(m_currentEditorWidget->displaySettings().m_textWrapping);
con's avatar
con committed
549

550
551
552
    updateRedoAction(m_currentEditorWidget->document()->isRedoAvailable());
    updateUndoAction(m_currentEditorWidget->document()->isUndoAvailable());
    updateCopyAction(m_currentEditorWidget->textCursor().hasSelection());
con's avatar
con committed
553
554
}

555
void TextEditorActionHandlerPrivate::updateRedoAction(bool on)
con's avatar
con committed
556
{
557
    m_redoAction->setEnabled(on);
con's avatar
con committed
558
559
}

560
void TextEditorActionHandlerPrivate::updateUndoAction(bool on)
con's avatar
con committed
561
{
562
    m_undoAction->setEnabled(on);
con's avatar
con committed
563
564
}

565
void TextEditorActionHandlerPrivate::updateCopyAction(bool hasCopyableText)
con's avatar
con committed
566
{
567
    QTC_ASSERT(m_currentEditorWidget, return);
con's avatar
con committed
568
    if (m_cutAction)
569
        m_cutAction->setEnabled(hasCopyableText && !m_currentEditorWidget->isReadOnly());
570
    if (m_copyAction)
con's avatar
con committed
571
572
573
        m_copyAction->setEnabled(hasCopyableText);
}

574
void TextEditorActionHandlerPrivate::updateCurrentEditor(Core::IEditor *editor)
con's avatar
con committed
575
{
576
577
578
    if (m_currentEditorWidget)
        m_currentEditorWidget->disconnect(this);
    m_currentEditorWidget = 0;
579

580
581
    // don't need to do anything if the editor's context doesn't match
    // (our actions will be disabled because our context will not be active)
582
    if (!editor || !editor->context().contains(m_contextId))
583
584
        return;

585
    TextEditorWidget *editorWidget = q->resolveTextEditorWidget(editor);
586
587
    QTC_ASSERT(editorWidget, return); // editor has our context id, so shouldn't happen
    m_currentEditorWidget = editorWidget;
588
589
590
591
592
593
    connect(editorWidget, &QPlainTextEdit::undoAvailable,
            this, &TextEditorActionHandlerPrivate::updateUndoAction);
    connect(editorWidget, &QPlainTextEdit::redoAvailable,
            this, &TextEditorActionHandlerPrivate::updateRedoAction);
    connect(editorWidget, &QPlainTextEdit::copyAvailable,
            this, &TextEditorActionHandlerPrivate::updateCopyAction);
594
    connect(editorWidget, &TextEditorWidget::readOnlyChanged,
595
            this, &TextEditorActionHandlerPrivate::updateActions);
596
    updateActions();
con's avatar
con committed
597
}
598
599
600
601
602
603
604
605
606
607
608
609
610

} // namespace Internal

TextEditorActionHandler::TextEditorActionHandler(QObject *parent, Core::Id contextId, uint optionalActions)
    : QObject(parent), d(new Internal::TextEditorActionHandlerPrivate(this, contextId, optionalActions))
{
}

TextEditorActionHandler::~TextEditorActionHandler()
{
    delete d;
}

611
TextEditorWidget *TextEditorActionHandler::resolveTextEditorWidget(Core::IEditor *editor) const
612
{
613
    return qobject_cast<TextEditorWidget *>(editor->widget());
614
615
616
}

} // namespace TextEditor