PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/src/plugins/fakevim/fakevimplugin.cpp

https://bitbucket.org/kyanha/qt-creator
C++ | 2073 lines | 1631 code | 275 blank | 167 comment | 281 complexity | 7c0ffc4d2bcc85dca9482b5003e0c395 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of Qt Creator.
  7. **
  8. ** Commercial License Usage
  9. ** Licensees holding valid commercial Qt licenses may use this file in
  10. ** accordance with the commercial license agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and Digia. For licensing terms and
  13. ** conditions see http://qt.digia.com/licensing. For further information
  14. ** use the contact form at http://qt.digia.com/contact-us.
  15. **
  16. ** GNU Lesser General Public License Usage
  17. ** Alternatively, this file may be used under the terms of the GNU Lesser
  18. ** General Public License version 2.1 as published by the Free Software
  19. ** Foundation and appearing in the file LICENSE.LGPL included in the
  20. ** packaging of this file. Please review the following information to
  21. ** ensure the GNU Lesser General Public License version 2.1 requirements
  22. ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  23. **
  24. ** In addition, as a special exception, Digia gives you certain additional
  25. ** rights. These rights are described in the Digia Qt LGPL Exception
  26. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  27. **
  28. ****************************************************************************/
  29. #include "fakevimplugin.h"
  30. #include "fakevimhandler.h"
  31. #include "ui_fakevimoptions.h"
  32. #include <coreplugin/actionmanager/actioncontainer.h>
  33. #include <coreplugin/actionmanager/actionmanager.h>
  34. #include <coreplugin/actionmanager/command.h>
  35. #include <coreplugin/actionmanager/command.h>
  36. #include <coreplugin/actionmanager/commandmappings.h>
  37. #include <coreplugin/coreconstants.h>
  38. #include <coreplugin/dialogs/ioptionspage.h>
  39. #include <coreplugin/editormanager/editormanager.h>
  40. #include <coreplugin/editormanager/openeditorsmodel.h>
  41. #include <coreplugin/documentmanager.h>
  42. #include <coreplugin/icore.h>
  43. #include <coreplugin/idocument.h>
  44. #include <coreplugin/messagemanager.h>
  45. #include <coreplugin/id.h>
  46. #include <coreplugin/statusbarwidget.h>
  47. #include <coreplugin/statusbarmanager.h>
  48. #include <projectexplorer/projectexplorerconstants.h>
  49. #include <texteditor/basetextdocumentlayout.h>
  50. #include <texteditor/basetexteditor.h>
  51. #include <texteditor/basetextmark.h>
  52. #include <texteditor/texteditorconstants.h>
  53. #include <texteditor/typingsettings.h>
  54. #include <texteditor/tabsettings.h>
  55. #include <texteditor/icodestylepreferences.h>
  56. #include <texteditor/texteditorsettings.h>
  57. #include <texteditor/indenter.h>
  58. #include <texteditor/codeassist/basicproposalitem.h>
  59. #include <texteditor/codeassist/basicproposalitemlistmodel.h>
  60. #include <texteditor/codeassist/completionassistprovider.h>
  61. #include <texteditor/codeassist/iassistprocessor.h>
  62. #include <texteditor/codeassist/iassistinterface.h>
  63. #include <texteditor/codeassist/genericproposal.h>
  64. #include <find/findplugin.h>
  65. #include <find/textfindconstants.h>
  66. #include <find/ifindsupport.h>
  67. #include <utils/qtcassert.h>
  68. #include <utils/savedaction.h>
  69. #include <utils/treewidgetcolumnstretcher.h>
  70. #include <utils/stylehelper.h>
  71. #include <cpptools/cpptoolsconstants.h>
  72. #include <extensionsystem/pluginmanager.h>
  73. #include <QAbstractTableModel>
  74. #include <QDebug>
  75. #include <QFile>
  76. #include <QtPlugin>
  77. #include <QObject>
  78. #include <QSettings>
  79. #include <QStackedWidget>
  80. #include <QTextStream>
  81. #include <QDesktopServices>
  82. #include <QItemDelegate>
  83. #include <QMessageBox>
  84. #include <QPlainTextEdit>
  85. #include <QShortcut>
  86. #include <QTextBlock>
  87. #include <QTextCursor>
  88. #include <QTextEdit>
  89. #include <QTreeWidgetItem>
  90. using namespace TextEditor;
  91. using namespace Core;
  92. namespace FakeVim {
  93. namespace Internal {
  94. const char INSTALL_HANDLER[] = "TextEditor.FakeVimHandler";
  95. const char SETTINGS_CATEGORY[] = "D.FakeVim";
  96. const char SETTINGS_CATEGORY_FAKEVIM_ICON[] = ":/core/images/category_fakevim.png";
  97. const char SETTINGS_ID[] = "A.General";
  98. const char SETTINGS_EX_CMDS_ID[] = "B.ExCommands";
  99. const char SETTINGS_USER_CMDS_ID[] = "C.UserCommands";
  100. typedef QLatin1String _;
  101. class MiniBuffer : public QStackedWidget
  102. {
  103. Q_OBJECT
  104. public:
  105. MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
  106. {
  107. m_edit->installEventFilter(this);
  108. connect(m_edit, SIGNAL(textEdited(QString)), SLOT(changed()));
  109. connect(m_edit, SIGNAL(cursorPositionChanged(int,int)), SLOT(changed()));
  110. connect(m_edit, SIGNAL(selectionChanged()), SLOT(changed()));
  111. m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
  112. addWidget(m_label);
  113. addWidget(m_edit);
  114. }
  115. void setContents(const QString &contents, int cursorPos, int anchorPos,
  116. int messageLevel, QObject *eventFilter)
  117. {
  118. if (cursorPos != -1) {
  119. m_edit->blockSignals(true);
  120. m_label->clear();
  121. m_edit->setText(contents);
  122. if (anchorPos != -1 && anchorPos != cursorPos)
  123. m_edit->setSelection(anchorPos, cursorPos - anchorPos);
  124. else
  125. m_edit->setCursorPosition(cursorPos);
  126. m_edit->blockSignals(false);
  127. setCurrentWidget(m_edit);
  128. m_edit->setFocus();
  129. } else if (contents.isEmpty() && messageLevel != MessageShowCmd) {
  130. hide();
  131. } else {
  132. show();
  133. m_label->setText(messageLevel == MessageMode ? _("-- ") + contents + _(" --") : contents);
  134. QString css;
  135. if (messageLevel == MessageError) {
  136. css = _("border:1px solid rgba(255,255,255,150);"
  137. "background-color:rgba(255,0,0,100);");
  138. } else if (messageLevel == MessageWarning) {
  139. css = _("border:1px solid rgba(255,255,255,120);"
  140. "background-color:rgba(255,255,0,20);");
  141. } else if (messageLevel == MessageShowCmd) {
  142. css = _("border:1px solid rgba(255,255,255,120);"
  143. "background-color:rgba(100,255,100,30);");
  144. }
  145. m_label->setStyleSheet(QString::fromLatin1(
  146. "*{border-radius:2px;padding-left:4px;padding-right:4px;%1}").arg(css));
  147. if (m_edit->hasFocus())
  148. emit edited(QString(), -1, -1);
  149. setCurrentWidget(m_label);
  150. }
  151. if (m_eventFilter != eventFilter) {
  152. if (m_eventFilter != 0) {
  153. m_edit->removeEventFilter(m_eventFilter);
  154. disconnect(SIGNAL(edited(QString,int,int)));
  155. }
  156. if (eventFilter != 0) {
  157. m_edit->installEventFilter(eventFilter);
  158. connect(this, SIGNAL(edited(QString,int,int)),
  159. eventFilter, SLOT(miniBufferTextEdited(QString,int,int)));
  160. }
  161. m_eventFilter = eventFilter;
  162. }
  163. }
  164. QSize sizeHint() const
  165. {
  166. QSize size = QWidget::sizeHint();
  167. // reserve maximal width for line edit widget
  168. return currentWidget() == m_edit ? QSize(maximumWidth(), size.height()) : size;
  169. }
  170. signals:
  171. void edited(const QString &text, int cursorPos, int anchorPos);
  172. private slots:
  173. void changed()
  174. {
  175. const int cursorPos = m_edit->cursorPosition();
  176. int anchorPos = m_edit->selectionStart();
  177. if (anchorPos == cursorPos)
  178. anchorPos = cursorPos + m_edit->selectedText().length();
  179. emit edited(m_edit->text(), cursorPos, anchorPos);
  180. }
  181. bool eventFilter(QObject *ob, QEvent *ev)
  182. {
  183. // cancel editing on escape
  184. if (m_eventFilter != 0 && ob == m_edit && ev->type() == QEvent::ShortcutOverride
  185. && static_cast<QKeyEvent*>(ev)->key() == Qt::Key_Escape) {
  186. emit edited(QString(), -1, -1);
  187. ev->accept();
  188. return true;
  189. }
  190. return false;
  191. }
  192. private:
  193. QLabel *m_label;
  194. QLineEdit *m_edit;
  195. QObject *m_eventFilter;
  196. };
  197. ///////////////////////////////////////////////////////////////////////
  198. //
  199. // FakeVimOptionPage
  200. //
  201. ///////////////////////////////////////////////////////////////////////
  202. typedef QMap<QString, QRegExp> ExCommandMap;
  203. typedef QMap<int, QString> UserCommandMap;
  204. class FakeVimOptionPage : public IOptionsPage
  205. {
  206. Q_OBJECT
  207. public:
  208. FakeVimOptionPage()
  209. {
  210. setId(_(SETTINGS_ID));
  211. setDisplayName(tr("General"));
  212. setCategory(SETTINGS_CATEGORY);
  213. setDisplayCategory(tr("FakeVim"));
  214. setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
  215. }
  216. QWidget *createPage(QWidget *parent);
  217. void apply() { m_group.apply(ICore::settings()); }
  218. void finish() { m_group.finish(); }
  219. virtual bool matches(const QString &) const;
  220. private slots:
  221. void copyTextEditorSettings();
  222. void setQtStyle();
  223. void setPlainStyle();
  224. private:
  225. friend class DebuggerPlugin;
  226. Ui::FakeVimOptionPage m_ui;
  227. QString m_searchKeywords;
  228. Utils::SavedActionSet m_group;
  229. };
  230. QWidget *FakeVimOptionPage::createPage(QWidget *parent)
  231. {
  232. QWidget *w = new QWidget(parent);
  233. m_ui.setupUi(w);
  234. m_group.clear();
  235. m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
  236. m_ui.checkBoxUseFakeVim);
  237. m_group.insert(theFakeVimSetting(ConfigReadVimRc),
  238. m_ui.checkBoxReadVimRc);
  239. m_group.insert(theFakeVimSetting(ConfigExpandTab),
  240. m_ui.checkBoxExpandTab);
  241. m_group.insert(theFakeVimSetting(ConfigHlSearch),
  242. m_ui.checkBoxHlSearch);
  243. m_group.insert(theFakeVimSetting(ConfigShiftWidth),
  244. m_ui.spinBoxShiftWidth);
  245. m_group.insert(theFakeVimSetting(ConfigShowMarks),
  246. m_ui.checkBoxShowMarks);
  247. m_group.insert(theFakeVimSetting(ConfigSmartTab),
  248. m_ui.checkBoxSmartTab);
  249. m_group.insert(theFakeVimSetting(ConfigStartOfLine),
  250. m_ui.checkBoxStartOfLine);
  251. m_group.insert(theFakeVimSetting(ConfigTabStop),
  252. m_ui.spinBoxTabStop);
  253. m_group.insert(theFakeVimSetting(ConfigBackspace),
  254. m_ui.lineEditBackspace);
  255. m_group.insert(theFakeVimSetting(ConfigIsKeyword),
  256. m_ui.lineEditIsKeyword);
  257. m_group.insert(theFakeVimSetting(ConfigPassControlKey),
  258. m_ui.checkBoxPassControlKey);
  259. m_group.insert(theFakeVimSetting(ConfigAutoIndent),
  260. m_ui.checkBoxAutoIndent);
  261. m_group.insert(theFakeVimSetting(ConfigSmartIndent),
  262. m_ui.checkBoxSmartIndent);
  263. m_group.insert(theFakeVimSetting(ConfigIncSearch),
  264. m_ui.checkBoxIncSearch);
  265. m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
  266. m_ui.checkBoxUseCoreSearch);
  267. m_group.insert(theFakeVimSetting(ConfigSmartCase),
  268. m_ui.checkBoxSmartCase);
  269. m_group.insert(theFakeVimSetting(ConfigWrapScan),
  270. m_ui.checkBoxWrapScan);
  271. m_group.insert(theFakeVimSetting(ConfigShowCmd),
  272. m_ui.checkBoxShowCmd);
  273. connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
  274. SLOT(copyTextEditorSettings()));
  275. connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
  276. SLOT(setQtStyle()));
  277. connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
  278. SLOT(setPlainStyle()));
  279. if (m_searchKeywords.isEmpty()) {
  280. QLatin1Char sep(' ');
  281. QTextStream(&m_searchKeywords)
  282. << sep << m_ui.checkBoxUseFakeVim->text()
  283. << sep << m_ui.checkBoxReadVimRc->text()
  284. << sep << m_ui.checkBoxAutoIndent->text()
  285. << sep << m_ui.checkBoxSmartIndent->text()
  286. << sep << m_ui.checkBoxExpandTab->text()
  287. << sep << m_ui.checkBoxSmartTab->text()
  288. << sep << m_ui.checkBoxHlSearch->text()
  289. << sep << m_ui.checkBoxIncSearch->text()
  290. << sep << m_ui.checkBoxStartOfLine->text()
  291. << sep << m_ui.checkBoxUseCoreSearch->text()
  292. << sep << m_ui.checkBoxSmartCase->text()
  293. << sep << m_ui.checkBoxShowMarks->text()
  294. << sep << m_ui.checkBoxPassControlKey->text()
  295. << sep << m_ui.labelShiftWidth->text()
  296. << sep << m_ui.labelTabulator->text()
  297. << sep << m_ui.labelBackspace->text()
  298. << sep << m_ui.labelIsKeyword->text();
  299. m_searchKeywords.remove(QLatin1Char('&'));
  300. }
  301. return w;
  302. }
  303. void FakeVimOptionPage::copyTextEditorSettings()
  304. {
  305. TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
  306. TypingSettings tps = TextEditorSettings::instance()->typingSettings();
  307. m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
  308. m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
  309. m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
  310. m_ui.checkBoxSmartTab->setChecked(
  311. tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
  312. m_ui.checkBoxAutoIndent->setChecked(true);
  313. m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
  314. m_ui.checkBoxIncSearch->setChecked(true);
  315. }
  316. void FakeVimOptionPage::setQtStyle()
  317. {
  318. m_ui.checkBoxExpandTab->setChecked(true);
  319. m_ui.spinBoxTabStop->setValue(4);
  320. m_ui.spinBoxShiftWidth->setValue(4);
  321. m_ui.checkBoxSmartTab->setChecked(true);
  322. m_ui.checkBoxAutoIndent->setChecked(true);
  323. m_ui.checkBoxSmartIndent->setChecked(true);
  324. m_ui.checkBoxIncSearch->setChecked(true);
  325. m_ui.lineEditBackspace->setText(_("indent,eol,start"));
  326. }
  327. void FakeVimOptionPage::setPlainStyle()
  328. {
  329. m_ui.checkBoxExpandTab->setChecked(false);
  330. m_ui.spinBoxTabStop->setValue(8);
  331. m_ui.spinBoxShiftWidth->setValue(8);
  332. m_ui.checkBoxSmartTab->setChecked(false);
  333. m_ui.checkBoxAutoIndent->setChecked(false);
  334. m_ui.checkBoxSmartIndent->setChecked(false);
  335. m_ui.checkBoxIncSearch->setChecked(false);
  336. m_ui.lineEditBackspace->setText(QString());
  337. }
  338. bool FakeVimOptionPage::matches(const QString &s) const
  339. {
  340. return m_searchKeywords.contains(s, Qt::CaseInsensitive);
  341. }
  342. //const char *FAKEVIM_CONTEXT = "FakeVim";
  343. ///////////////////////////////////////////////////////////////////////
  344. //
  345. // FakeVimExCommandsPage
  346. //
  347. ///////////////////////////////////////////////////////////////////////
  348. enum { CommandRole = Qt::UserRole };
  349. class FakeVimExCommandsPage : public CommandMappings
  350. {
  351. Q_OBJECT
  352. public:
  353. FakeVimExCommandsPage(FakeVimPluginPrivate *q)
  354. : m_q(q)
  355. {
  356. setId(_(SETTINGS_EX_CMDS_ID));
  357. setDisplayName(tr("Ex Command Mapping"));
  358. setCategory(SETTINGS_CATEGORY);
  359. setDisplayCategory(tr("FakeVim"));
  360. setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
  361. }
  362. QWidget *createPage(QWidget *parent);
  363. void initialize();
  364. ExCommandMap &exCommandMap();
  365. ExCommandMap &defaultExCommandMap();
  366. public slots:
  367. void commandChanged(QTreeWidgetItem *current);
  368. void targetIdentifierChanged();
  369. void resetTargetIdentifier();
  370. void removeTargetIdentifier();
  371. void defaultAction();
  372. private:
  373. FakeVimPluginPrivate *m_q;
  374. };
  375. QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
  376. {
  377. QWidget *w = CommandMappings::createPage(parent);
  378. setPageTitle(tr("Ex Command Mapping"));
  379. setTargetHeader(tr("Ex Trigger Expression"));
  380. setTargetLabelText(tr("Regular expression:"));
  381. setTargetEditTitle(tr("Ex Command"));
  382. setImportExportEnabled(false);
  383. return w;
  384. }
  385. void FakeVimExCommandsPage::initialize()
  386. {
  387. QMap<QString, QTreeWidgetItem *> sections;
  388. foreach (Command *c, ActionManager::commands()) {
  389. if (c->action() && c->action()->isSeparator())
  390. continue;
  391. QTreeWidgetItem *item = new QTreeWidgetItem;
  392. const QString name = c->id().toString();
  393. const int pos = name.indexOf(QLatin1Char('.'));
  394. const QString section = name.left(pos);
  395. const QString subId = name.mid(pos + 1);
  396. item->setData(0, CommandRole, name);
  397. if (!sections.contains(section)) {
  398. QTreeWidgetItem *categoryItem =
  399. new QTreeWidgetItem(commandList(), QStringList() << section);
  400. QFont f = categoryItem->font(0);
  401. f.setBold(true);
  402. categoryItem->setFont(0, f);
  403. sections.insert(section, categoryItem);
  404. commandList()->expandItem(categoryItem);
  405. }
  406. sections[section]->addChild(item);
  407. item->setText(0, subId);
  408. item->setText(1, c->description());
  409. QString regex;
  410. if (exCommandMap().contains(name))
  411. regex = exCommandMap()[name].pattern();
  412. item->setText(2, regex);
  413. if (regex != defaultExCommandMap()[name].pattern())
  414. setModified(item, true);
  415. }
  416. commandChanged(0);
  417. }
  418. void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
  419. {
  420. CommandMappings::commandChanged(current);
  421. if (current)
  422. targetEdit()->setText(current->text(2));
  423. }
  424. void FakeVimExCommandsPage::targetIdentifierChanged()
  425. {
  426. QTreeWidgetItem *current = commandList()->currentItem();
  427. if (!current)
  428. return;
  429. const QString name = current->data(0, CommandRole).toString();
  430. const QString regex = targetEdit()->text();
  431. if (current->data(0, Qt::UserRole).isValid()) {
  432. current->setText(2, regex);
  433. exCommandMap()[name] = QRegExp(regex);
  434. }
  435. setModified(current, regex != defaultExCommandMap()[name].pattern());
  436. }
  437. void FakeVimExCommandsPage::resetTargetIdentifier()
  438. {
  439. QTreeWidgetItem *current = commandList()->currentItem();
  440. if (!current)
  441. return;
  442. const QString name = current->data(0, CommandRole).toString();
  443. QString regex;
  444. if (defaultExCommandMap().contains(name))
  445. regex = defaultExCommandMap()[name].pattern();
  446. targetEdit()->setText(regex);
  447. }
  448. void FakeVimExCommandsPage::removeTargetIdentifier()
  449. {
  450. targetEdit()->clear();
  451. }
  452. void FakeVimExCommandsPage::defaultAction()
  453. {
  454. int n = commandList()->topLevelItemCount();
  455. for (int i = 0; i != n; ++i) {
  456. QTreeWidgetItem *section = commandList()->topLevelItem(i);
  457. int m = section->childCount();
  458. for (int j = 0; j != m; ++j) {
  459. QTreeWidgetItem *item = section->child(j);
  460. const QString name = item->data(0, CommandRole).toString();
  461. QString regex;
  462. if (defaultExCommandMap().contains(name))
  463. regex = defaultExCommandMap()[name].pattern();
  464. setModified(item, false);
  465. item->setText(2, regex);
  466. if (item == commandList()->currentItem())
  467. commandChanged(item);
  468. }
  469. }
  470. }
  471. ///////////////////////////////////////////////////////////////////////
  472. //
  473. // FakeVimUserCommandsPage
  474. //
  475. ///////////////////////////////////////////////////////////////////////
  476. class FakeVimUserCommandsModel : public QAbstractTableModel
  477. {
  478. Q_OBJECT
  479. public:
  480. FakeVimUserCommandsModel(FakeVimPluginPrivate *q) : m_q(q) {}
  481. ~FakeVimUserCommandsModel() {}
  482. int rowCount(const QModelIndex &parent) const;
  483. int columnCount(const QModelIndex &parent) const;
  484. QVariant data(const QModelIndex &index, int role) const;
  485. bool setData(const QModelIndex &index, const QVariant &data, int role);
  486. QVariant headerData(int section, Qt::Orientation orientation, int role) const;
  487. Qt::ItemFlags flags(const QModelIndex &index) const;
  488. private:
  489. FakeVimPluginPrivate *m_q;
  490. };
  491. int FakeVimUserCommandsModel::rowCount(const QModelIndex &parent) const
  492. {
  493. return parent.isValid() ? 0 : 9;
  494. }
  495. int FakeVimUserCommandsModel::columnCount(const QModelIndex &parent) const
  496. {
  497. return parent.isValid() ? 0 : 2;
  498. }
  499. QVariant FakeVimUserCommandsModel::headerData(int section,
  500. Qt::Orientation orient, int role) const
  501. {
  502. if (orient == Qt::Horizontal && role == Qt::DisplayRole) {
  503. switch (section) {
  504. case 0: return tr("Action");
  505. case 1: return tr("Command");
  506. };
  507. }
  508. return QVariant();
  509. }
  510. Qt::ItemFlags FakeVimUserCommandsModel::flags(const QModelIndex &index) const
  511. {
  512. if (index.column() == 1)
  513. return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
  514. return QAbstractTableModel::flags(index);
  515. }
  516. class FakeVimUserCommandsDelegate : public QItemDelegate
  517. {
  518. public:
  519. explicit FakeVimUserCommandsDelegate(QObject *parent)
  520. : QItemDelegate(parent)
  521. {}
  522. QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
  523. const QModelIndex &) const
  524. {
  525. QLineEdit *lineEdit = new QLineEdit(parent);
  526. lineEdit->setFrame(false);
  527. return lineEdit;
  528. }
  529. void setModelData(QWidget *editor, QAbstractItemModel *model,
  530. const QModelIndex &index) const
  531. {
  532. QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
  533. QTC_ASSERT(lineEdit, return);
  534. model->setData(index, lineEdit->text(), Qt::EditRole);
  535. }
  536. };
  537. class FakeVimUserCommandsPage : public IOptionsPage
  538. {
  539. Q_OBJECT
  540. public:
  541. FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
  542. : m_q(q)
  543. {
  544. setId(_(SETTINGS_USER_CMDS_ID));
  545. setDisplayName(tr("User Command Mapping"));
  546. setCategory(SETTINGS_CATEGORY);
  547. setDisplayCategory(tr("FakeVim"));
  548. setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
  549. }
  550. void apply();
  551. void finish() {}
  552. QWidget *createPage(QWidget *parent);
  553. void initialize() {}
  554. UserCommandMap &userCommandMap();
  555. UserCommandMap &defaultUserCommandMap();
  556. private:
  557. FakeVimPluginPrivate *m_q;
  558. };
  559. QWidget *FakeVimUserCommandsPage::createPage(QWidget *parent)
  560. {
  561. QGroupBox *box = new QGroupBox(parent);
  562. FakeVimUserCommandsModel *model = new FakeVimUserCommandsModel(m_q);
  563. QTreeView *widget = new QTreeView;
  564. widget->setModel(model);
  565. widget->resizeColumnToContents(0);
  566. FakeVimUserCommandsDelegate *delegate = new FakeVimUserCommandsDelegate(widget);
  567. widget->setItemDelegateForColumn(1, delegate);
  568. QGridLayout *layout = new QGridLayout(box);
  569. layout->addWidget(widget, 0, 0);
  570. box->setLayout(layout);
  571. return box;
  572. }
  573. void FakeVimUserCommandsPage::apply()
  574. {
  575. //m_q->writeSettings();
  576. }
  577. ///////////////////////////////////////////////////////////////////////
  578. //
  579. // WordCompletion
  580. //
  581. ///////////////////////////////////////////////////////////////////////
  582. class FakeVimCompletionAssistProvider : public CompletionAssistProvider
  583. {
  584. public:
  585. bool supportsEditor(const Id &) const
  586. {
  587. return false;
  588. }
  589. IAssistProcessor *createProcessor() const;
  590. void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
  591. {
  592. Q_UNUSED(forward);
  593. m_handler = handler;
  594. if (!m_handler)
  595. return;
  596. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
  597. if (!editor)
  598. return;
  599. //qDebug() << "ACTIVATE: " << needle << forward;
  600. m_needle = needle;
  601. editor->invokeAssist(Completion, this);
  602. }
  603. void setInactive()
  604. {
  605. m_needle.clear();
  606. m_handler = 0;
  607. }
  608. const QString &needle() const
  609. {
  610. return m_needle;
  611. }
  612. void appendNeedle(const QChar &c)
  613. {
  614. m_needle.append(c);
  615. }
  616. FakeVimHandler *handler() const
  617. {
  618. return m_handler;
  619. }
  620. private:
  621. FakeVimHandler *m_handler;
  622. QString m_needle;
  623. };
  624. class FakeVimAssistProposalItem : public BasicProposalItem
  625. {
  626. public:
  627. FakeVimAssistProposalItem(const FakeVimCompletionAssistProvider *provider)
  628. : m_provider(const_cast<FakeVimCompletionAssistProvider *>(provider))
  629. {}
  630. virtual bool implicitlyApplies() const
  631. {
  632. return false;
  633. }
  634. virtual bool prematurelyApplies(const QChar &c) const
  635. {
  636. m_provider->appendNeedle(c);
  637. return text() == m_provider->needle();
  638. }
  639. virtual void applyContextualContent(BaseTextEditor *, int) const
  640. {
  641. QTC_ASSERT(m_provider->handler(), return);
  642. m_provider->handler()->handleReplay(text().mid(m_provider->needle().size()));
  643. const_cast<FakeVimCompletionAssistProvider *>(m_provider)->setInactive();
  644. }
  645. private:
  646. FakeVimCompletionAssistProvider *m_provider;
  647. };
  648. class FakeVimAssistProposalModel : public BasicProposalItemListModel
  649. {
  650. public:
  651. FakeVimAssistProposalModel(const QList<BasicProposalItem *> &items)
  652. : BasicProposalItemListModel(items)
  653. {}
  654. virtual bool supportsPrefixExpansion() const
  655. {
  656. return false;
  657. }
  658. };
  659. class FakeVimCompletionAssistProcessor : public IAssistProcessor
  660. {
  661. public:
  662. FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
  663. : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
  664. {}
  665. IAssistProposal *perform(const IAssistInterface *interface)
  666. {
  667. const QString &needle = m_provider->needle();
  668. const int basePosition = interface->position() - needle.size();
  669. QTextCursor tc(interface->textDocument());
  670. tc.setPosition(interface->position());
  671. tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
  672. QList<BasicProposalItem *> items;
  673. QSet<QString> seen;
  674. QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
  675. while (1) {
  676. tc = tc.document()->find(needle, tc.position(), flags);
  677. if (tc.isNull())
  678. break;
  679. QTextCursor sel = tc;
  680. sel.select(QTextCursor::WordUnderCursor);
  681. QString found = sel.selectedText();
  682. // Only add "real" completions.
  683. if (found.startsWith(needle)
  684. && !seen.contains(found)
  685. && sel.anchor() != basePosition) {
  686. seen.insert(found);
  687. BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
  688. item->setText(found);
  689. items.append(item);
  690. }
  691. tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
  692. }
  693. //qDebug() << "COMPLETIONS" << completions->size();
  694. delete interface;
  695. return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
  696. }
  697. private:
  698. const FakeVimCompletionAssistProvider *m_provider;
  699. };
  700. IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
  701. {
  702. return new FakeVimCompletionAssistProcessor(this);
  703. }
  704. ///////////////////////////////////////////////////////////////////////
  705. //
  706. // FakeVimPluginPrivate
  707. //
  708. ///////////////////////////////////////////////////////////////////////
  709. class FakeVimPluginPrivate : public QObject
  710. {
  711. Q_OBJECT
  712. public:
  713. FakeVimPluginPrivate(FakeVimPlugin *);
  714. ~FakeVimPluginPrivate();
  715. friend class FakeVimPlugin;
  716. friend class FakeVimExCommandsPage;
  717. friend class FakeVimUserCommandsPage;
  718. friend class FakeVimUserCommandsModel;
  719. bool initialize();
  720. void aboutToShutdown();
  721. private slots:
  722. void onCoreAboutToClose();
  723. void editorOpened(Core::IEditor *);
  724. void editorAboutToClose(Core::IEditor *);
  725. void setUseFakeVim(const QVariant &value);
  726. void setUseFakeVimInternal(bool on);
  727. void quitFakeVim();
  728. void triggerCompletions();
  729. void triggerSimpleCompletions(const QString &needle, bool forward);
  730. void windowCommand(int key);
  731. void find(bool reverse);
  732. void findNext(bool reverse);
  733. void foldToggle(int depth);
  734. void foldAll(bool fold);
  735. void fold(int depth, bool fold);
  736. void foldGoTo(int count, bool current);
  737. void jumpToGlobalMark(QChar mark, bool backTickMode, const QString &fileName);
  738. void showSettingsDialog();
  739. void maybeReadVimRc();
  740. void setBlockSelection(bool);
  741. void hasBlockSelection(bool*);
  742. void resetCommandBuffer();
  743. void showCommandBuffer(const QString &contents, int cursorPos, int anchorPos,
  744. int messageLevel, QObject *eventFilter);
  745. void showExtraInformation(const QString &msg);
  746. void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
  747. void highlightMatches(const QString &needle);
  748. void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
  749. void checkForElectricCharacter(bool *result, QChar c);
  750. void indentRegion(int beginBlock, int endBlock, QChar typedChar);
  751. void handleExCommand(bool *handled, const ExCommand &cmd);
  752. void writeSettings();
  753. void readSettings();
  754. void handleDelayedQuitAll(bool forced);
  755. void handleDelayedQuit(bool forced, Core::IEditor *editor);
  756. void userActionTriggered();
  757. void switchToFile(int n);
  758. int currentFile() const;
  759. signals:
  760. void delayedQuitRequested(bool forced, Core::IEditor *editor);
  761. void delayedQuitAllRequested(bool forced);
  762. private:
  763. FakeVimPlugin *q;
  764. FakeVimOptionPage *m_fakeVimOptionsPage;
  765. FakeVimExCommandsPage *m_fakeVimExCommandsPage;
  766. FakeVimUserCommandsPage *m_fakeVimUserCommandsPage;
  767. QHash<IEditor *, FakeVimHandler *> m_editorToHandler;
  768. void triggerAction(const Id &id);
  769. void setActionChecked(const Id &id, bool check);
  770. typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
  771. void moveSomewhere(DistFunction f);
  772. ExCommandMap &exCommandMap() { return m_exCommandMap; }
  773. ExCommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
  774. ExCommandMap m_exCommandMap;
  775. ExCommandMap m_defaultExCommandMap;
  776. UserCommandMap &userCommandMap() { return m_userCommandMap; }
  777. UserCommandMap &defaultUserCommandMap() { return m_defaultUserCommandMap; }
  778. UserCommandMap m_userCommandMap;
  779. UserCommandMap m_defaultUserCommandMap;
  780. StatusBarWidget *m_statusBar;
  781. // @TODO: Delete
  782. //WordCompletion *m_wordCompletion;
  783. FakeVimCompletionAssistProvider *m_wordProvider;
  784. };
  785. QVariant FakeVimUserCommandsModel::data(const QModelIndex &index, int role) const
  786. {
  787. if (!index.isValid())
  788. return QVariant();
  789. if (role == Qt::DisplayRole || role == Qt::EditRole) {
  790. switch (index.column()) {
  791. case 0: // Action
  792. return tr("User command #%1").arg(index.row() + 1);
  793. case 1: // Command
  794. return m_q->userCommandMap().value(index.row() + 1);
  795. }
  796. }
  797. return QVariant();
  798. }
  799. bool FakeVimUserCommandsModel::setData(const QModelIndex &index,
  800. const QVariant &data, int role)
  801. {
  802. if (role == Qt::DisplayRole || role == Qt::EditRole)
  803. if (index.column() == 1)
  804. m_q->userCommandMap()[index.row() + 1] = data.toString();
  805. return true;
  806. }
  807. FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
  808. {
  809. q = plugin;
  810. m_fakeVimOptionsPage = 0;
  811. m_fakeVimExCommandsPage = 0;
  812. m_fakeVimUserCommandsPage = 0;
  813. defaultExCommandMap()[_(CppTools::Constants::SWITCH_HEADER_SOURCE)] =
  814. QRegExp(_("^A$"));
  815. defaultExCommandMap()[_("Coreplugin.OutputPane.previtem")] =
  816. QRegExp(_("^(cN(ext)?|cp(revious)?)!?( (.*))?$"));
  817. defaultExCommandMap()[_("Coreplugin.OutputPane.nextitem")] =
  818. QRegExp(_("^cn(ext)?!?( (.*))?$"));
  819. defaultExCommandMap()[_(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR)] =
  820. QRegExp(_("^tag?$"));
  821. defaultExCommandMap()[_(Core::Constants::GO_BACK)] =
  822. QRegExp(_("^pop?$"));
  823. defaultExCommandMap()[_("QtCreator.Locate")] =
  824. QRegExp(_("^e$"));
  825. for (int i = 1; i < 10; ++i) {
  826. QString cmd = QString::fromLatin1(":echo User command %1 executed.<CR>");
  827. defaultUserCommandMap().insert(i, cmd.arg(i));
  828. }
  829. m_statusBar = 0;
  830. }
  831. FakeVimPluginPrivate::~FakeVimPluginPrivate()
  832. {
  833. q->removeObject(m_fakeVimOptionsPage);
  834. delete m_fakeVimOptionsPage;
  835. m_fakeVimOptionsPage = 0;
  836. delete theFakeVimSettings();
  837. q->removeObject(m_fakeVimExCommandsPage);
  838. delete m_fakeVimExCommandsPage;
  839. m_fakeVimExCommandsPage = 0;
  840. q->removeObject(m_fakeVimUserCommandsPage);
  841. delete m_fakeVimUserCommandsPage;
  842. m_fakeVimUserCommandsPage = 0;
  843. }
  844. void FakeVimPluginPrivate::onCoreAboutToClose()
  845. {
  846. // Don't attach to editors anymore.
  847. disconnect(ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
  848. this, SLOT(editorOpened(Core::IEditor*)));
  849. }
  850. void FakeVimPluginPrivate::aboutToShutdown()
  851. {
  852. }
  853. bool FakeVimPluginPrivate::initialize()
  854. {
  855. EditorManager *editorManager = ICore::editorManager();
  856. //m_wordCompletion = new WordCompletion;
  857. //q->addAutoReleasedObject(m_wordCompletion);
  858. m_wordProvider = new FakeVimCompletionAssistProvider;
  859. /*
  860. // Set completion settings and keep them up to date.
  861. TextEditorSettings *textEditorSettings = TextEditorSettings::instance();
  862. completion->setCompletionSettings(textEditorSettings->completionSettings());
  863. connect(textEditorSettings,
  864. SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
  865. completion,
  866. SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
  867. */
  868. Context globalcontext(Core::Constants::C_GLOBAL);
  869. m_fakeVimOptionsPage = new FakeVimOptionPage;
  870. q->addObject(m_fakeVimOptionsPage);
  871. m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
  872. q->addObject(m_fakeVimExCommandsPage);
  873. m_fakeVimUserCommandsPage = new FakeVimUserCommandsPage(this);
  874. q->addObject(m_fakeVimUserCommandsPage);
  875. readSettings();
  876. Command *cmd = 0;
  877. cmd = ActionManager::registerAction(theFakeVimSetting(ConfigUseFakeVim),
  878. INSTALL_HANDLER, globalcontext, true);
  879. cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+V,Meta+V") : tr("Alt+V,Alt+V")));
  880. ActionContainer *advancedMenu =
  881. ActionManager::actionContainer(Core::Constants::M_EDIT_ADVANCED);
  882. advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
  883. for (int i = 1; i < 10; ++i) {
  884. QAction *act = new QAction(this);
  885. act->setText(tr("Execute User Action #%1").arg(i));
  886. act->setData(i);
  887. QString id = QString::fromLatin1("FakeVim.UserAction%1").arg(i);
  888. cmd = ActionManager::registerAction(act, Id(id), globalcontext);
  889. cmd->setDefaultKeySequence(QKeySequence((UseMacShortcuts ? tr("Meta+V,%1") : tr("Alt+V,%1")).arg(i)));
  890. connect(act, SIGNAL(triggered()), SLOT(userActionTriggered()));
  891. }
  892. connect(ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));
  893. // EditorManager
  894. connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
  895. this, SLOT(editorAboutToClose(Core::IEditor*)));
  896. connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
  897. this, SLOT(editorOpened(Core::IEditor*)));
  898. connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
  899. this, SLOT(setUseFakeVim(QVariant)));
  900. connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
  901. this, SLOT(maybeReadVimRc()));
  902. // Delayed operations.
  903. connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
  904. this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
  905. connect(this, SIGNAL(delayedQuitAllRequested(bool)),
  906. this, SLOT(handleDelayedQuitAll(bool)), Qt::QueuedConnection);
  907. // Vimrc can break test so don't source it if running tests.
  908. if (!ExtensionSystem::PluginManager::runningTests())
  909. maybeReadVimRc();
  910. // << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
  911. return true;
  912. }
  913. void FakeVimPluginPrivate::userActionTriggered()
  914. {
  915. QAction *act = qobject_cast<QAction *>(sender());
  916. if (!act)
  917. return;
  918. const int key = act->data().toInt();
  919. if (!key)
  920. return;
  921. QString cmd = userCommandMap().value(key);
  922. IEditor *editor = EditorManager::currentEditor();
  923. FakeVimHandler *handler = m_editorToHandler[editor];
  924. if (handler)
  925. handler->handleInput(cmd);
  926. }
  927. const char exCommandMapGroup[] = "FakeVimExCommand";
  928. const char userCommandMapGroup[] = "FakeVimUserCommand";
  929. const char reKey[] = "RegEx";
  930. const char cmdKey[] = "Cmd";
  931. const char idKey[] = "Command";
  932. void FakeVimPluginPrivate::writeSettings()
  933. {
  934. QSettings *settings = ICore::settings();
  935. theFakeVimSettings()->writeSettings(settings);
  936. { // block
  937. settings->beginWriteArray(_(exCommandMapGroup));
  938. int count = 0;
  939. typedef ExCommandMap::const_iterator Iterator;
  940. const Iterator end = exCommandMap().constEnd();
  941. for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
  942. const QString id = it.key();
  943. const QRegExp re = it.value();
  944. if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
  945. || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
  946. settings->setArrayIndex(count);
  947. settings->setValue(_(idKey), id);
  948. settings->setValue(_(reKey), re.pattern());
  949. ++count;
  950. }
  951. }
  952. settings->endArray();
  953. } // block
  954. { // block
  955. settings->beginWriteArray(_(userCommandMapGroup));
  956. int count = 0;
  957. typedef UserCommandMap::const_iterator Iterator;
  958. const Iterator end = userCommandMap().constEnd();
  959. for (Iterator it = userCommandMap().constBegin(); it != end; ++it) {
  960. const int key = it.key();
  961. const QString cmd = it.value();
  962. if ((defaultUserCommandMap().contains(key)
  963. && defaultUserCommandMap()[key] != cmd)
  964. || (!defaultUserCommandMap().contains(key) && !cmd.isEmpty())) {
  965. settings->setArrayIndex(count);
  966. settings->setValue(_(idKey), key);
  967. settings->setValue(_(cmdKey), cmd);
  968. ++count;
  969. }
  970. }
  971. settings->endArray();
  972. } // block
  973. }
  974. void FakeVimPluginPrivate::readSettings()
  975. {
  976. QSettings *settings = ICore::settings();
  977. theFakeVimSettings()->readSettings(settings);
  978. exCommandMap() = defaultExCommandMap();
  979. int size = settings->beginReadArray(_(exCommandMapGroup));
  980. for (int i = 0; i < size; ++i) {
  981. settings->setArrayIndex(i);
  982. const QString id = settings->value(_(idKey)).toString();
  983. const QString re = settings->value(_(reKey)).toString();
  984. exCommandMap()[id] = QRegExp(re);
  985. }
  986. settings->endArray();
  987. userCommandMap() = defaultUserCommandMap();
  988. size = settings->beginReadArray(_(userCommandMapGroup));
  989. for (int i = 0; i < size; ++i) {
  990. settings->setArrayIndex(i);
  991. const int id = settings->value(_(idKey)).toInt();
  992. const QString cmd = settings->value(_(cmdKey)).toString();
  993. userCommandMap()[id] = cmd;
  994. }
  995. settings->endArray();
  996. }
  997. void FakeVimPluginPrivate::maybeReadVimRc()
  998. {
  999. //qDebug() << theFakeVimSetting(ConfigReadVimRc)
  1000. // << theFakeVimSetting(ConfigReadVimRc)->value();
  1001. //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
  1002. if (!theFakeVimSetting(ConfigReadVimRc)->value().toBool())
  1003. return;
  1004. QString fileName =
  1005. QDesktopServices::storageLocation(QDesktopServices::HomeLocation)
  1006. + _("/.vimrc");
  1007. //qDebug() << "READING VIMRC: " << fileName;
  1008. // Read it into a temporary handler for effects modifying global state.
  1009. QPlainTextEdit editor;
  1010. FakeVimHandler handler(&editor);
  1011. handler.handleCommand(_("source ") + fileName);
  1012. //writeSettings();
  1013. //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
  1014. }
  1015. void FakeVimPluginPrivate::showSettingsDialog()
  1016. {
  1017. ICore::showOptionsDialog(SETTINGS_CATEGORY, SETTINGS_ID);
  1018. }
  1019. void FakeVimPluginPrivate::triggerAction(const Id &id)
  1020. {
  1021. Core::Command *cmd = ActionManager::command(id);
  1022. QTC_ASSERT(cmd, qDebug() << "UNKNOWN CODE: " << id.name(); return);
  1023. QAction *action = cmd->action();
  1024. QTC_ASSERT(action, return);
  1025. action->trigger();
  1026. }
  1027. void FakeVimPluginPrivate::setActionChecked(const Id &id, bool check)
  1028. {
  1029. Core::Command *cmd = ActionManager::command(id);
  1030. QTC_ASSERT(cmd, return);
  1031. QAction *action = cmd->action();
  1032. QTC_ASSERT(action, return);
  1033. QTC_ASSERT(action->isCheckable(), return);
  1034. action->setChecked(!check); // trigger negates the action's state
  1035. action->trigger();
  1036. }
  1037. static int moveRightWeight(const QRect &cursor, const QRect &other)
  1038. {
  1039. int dx = other.left() - cursor.right();
  1040. if (dx < 0)
  1041. return -1;
  1042. int w = 10000 * dx;
  1043. int dy1 = cursor.top() - other.bottom();
  1044. int dy2 = cursor.bottom() - other.top();
  1045. w += dy1 * (dy1 > 0);
  1046. w += dy2 * (dy2 > 0);
  1047. qDebug() << " DX: " << dx << dy1 << dy2 << w;
  1048. return w;
  1049. }
  1050. static int moveLeftWeight(const QRect &cursor, const QRect &other)
  1051. {
  1052. int dx = other.right() - cursor.left();
  1053. if (dx < 0)
  1054. return -1;
  1055. int w = 10000 * dx;
  1056. int dy1 = cursor.top() - other.bottom();
  1057. int dy2 = cursor.bottom() - other.top();
  1058. w += dy1 * (dy1 > 0);
  1059. w += dy2 * (dy2 > 0);
  1060. return w;
  1061. }
  1062. static int moveUpWeight(const QRect &cursor, const QRect &other)
  1063. {
  1064. int dy = other.bottom() - cursor.top();
  1065. if (dy < 0)
  1066. return -1;
  1067. int w = 10000 * dy;
  1068. int dx1 = cursor.left() - other.right();
  1069. int dx2 = cursor.right() - other.left();
  1070. w += dx1 * (dx1 > 0);
  1071. w += dx2 * (dx2 > 0);
  1072. return w;
  1073. }
  1074. static int moveDownWeight(const QRect &cursor, const QRect &other)
  1075. {
  1076. int dy = other.top() - cursor.bottom();
  1077. if (dy < 0)
  1078. return -1;
  1079. int w = 10000 * dy;
  1080. int dx1 = cursor.left() - other.right();
  1081. int dx2 = cursor.right() - other.left();
  1082. w += dx1 * (dx1 > 0);
  1083. w += dx2 * (dx2 > 0);
  1084. return w;
  1085. }
  1086. void FakeVimPluginPrivate::windowCommand(int key)
  1087. {
  1088. # define control(n) (256 + n)
  1089. switch (key) {
  1090. case 'c': case 'C': case control('c'):
  1091. triggerAction(Core::Constants::CLOSE);
  1092. break;
  1093. case 'n': case 'N': case control('n'):
  1094. triggerAction(Core::Constants::GOTONEXT);
  1095. break;
  1096. case 'o': case 'O': case control('o'):
  1097. //triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
  1098. triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
  1099. break;
  1100. case 'p': case 'P': case control('p'):
  1101. triggerAction(Core::Constants::GOTOPREV);
  1102. break;
  1103. case 's': case 'S': case control('s'):
  1104. triggerAction(Core::Constants::SPLIT);
  1105. break;
  1106. case 'w': case 'W': case control('w'):
  1107. triggerAction(Core::Constants::GOTO_OTHER_SPLIT);
  1108. break;
  1109. case Qt::Key_Right:
  1110. moveSomewhere(&moveRightWeight);
  1111. break;
  1112. case Qt::Key_Left:
  1113. moveSomewhere(&moveLeftWeight);
  1114. break;
  1115. case Qt::Key_Up:
  1116. moveSomewhere(&moveUpWeight);
  1117. break;
  1118. case Qt::Key_Down:
  1119. moveSomewhere(&moveDownWeight);
  1120. break;
  1121. default:
  1122. qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
  1123. break;
  1124. }
  1125. # undef control
  1126. }
  1127. void FakeVimPluginPrivate::moveSomewhere(DistFunction f)
  1128. {
  1129. IEditor *editor = EditorManager::currentEditor();
  1130. QWidget *w = editor->widget();
  1131. QPlainTextEdit *pe = qobject_cast<QPlainTextEdit *>(w);
  1132. QTC_ASSERT(pe, return);
  1133. QRect rc = pe->cursorRect();
  1134. QRect cursorRect(w->mapToGlobal(rc.topLeft()),
  1135. w->mapToGlobal(rc.bottomRight()));
  1136. //qDebug() << "\nCURSOR: " << cursorRect;
  1137. IEditor *bestEditor = 0;
  1138. int bestValue = 1 << 30;
  1139. foreach (IEditor *editor, EditorManager::instance()->visibleEditors()) {
  1140. QWidget *w = editor->widget();
  1141. QRect editorRect(w->mapToGlobal(w->geometry().topLeft()),
  1142. w->mapToGlobal(w->geometry().bottomRight()));
  1143. //qDebug() << " EDITOR: " << editorRect << editor;
  1144. int value = f(cursorRect, editorRect);
  1145. if (value != -1 && value < bestValue) {
  1146. bestValue = value;
  1147. bestEditor = editor;
  1148. //qDebug() << " BEST SO FAR: " << bestValue << bestEditor;
  1149. }
  1150. }
  1151. //qDebug() << " BEST: " << bestValue << bestEditor;
  1152. // FIME: This is know to fail as the EditorManager will fall back to
  1153. // the current editor's view. Needs additional public API there.
  1154. if (bestEditor)
  1155. EditorManager::activateEditor(bestEditor);
  1156. }
  1157. void FakeVimPluginPrivate::find(bool reverse)
  1158. {
  1159. if (Find::FindPlugin *plugin = Find::FindPlugin::instance()) {
  1160. plugin->setUseFakeVim(true);
  1161. plugin->openFindToolBar(reverse
  1162. ? Find::FindPlugin::FindBackward
  1163. : Find::FindPlugin::FindForward);
  1164. }
  1165. }
  1166. void FakeVimPluginPrivate::findNext(bool reverse)
  1167. {
  1168. if (reverse)
  1169. triggerAction(Find::Constants::FIND_PREVIOUS);
  1170. else
  1171. triggerAction(Find::Constants::FIND_NEXT);
  1172. }
  1173. void FakeVimPluginPrivate::foldToggle(int depth)
  1174. {
  1175. IEditor *ieditor = EditorManager::currentEditor();
  1176. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1177. QTC_ASSERT(editor != 0, return);
  1178. QTextBlock block = editor->textCursor().block();
  1179. fold(depth, !BaseTextDocumentLayout::isFolded(block));
  1180. }
  1181. void FakeVimPluginPrivate::foldAll(bool fold)
  1182. {
  1183. IEditor *ieditor = EditorManager::currentEditor();
  1184. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1185. QTC_ASSERT(editor != 0, return);
  1186. QTextDocument *doc = editor->document();
  1187. BaseTextDocumentLayout *documentLayout =
  1188. qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
  1189. QTC_ASSERT(documentLayout != 0, return);
  1190. QTextBlock block = editor->document()->firstBlock();
  1191. while (block.isValid()) {
  1192. BaseTextDocumentLayout::doFoldOrUnfold(block, !fold);
  1193. block = block.next();
  1194. }
  1195. documentLayout->requestUpdate();
  1196. documentLayout->emitDocumentSizeChanged();
  1197. }
  1198. void FakeVimPluginPrivate::fold(int depth, bool fold)
  1199. {
  1200. IEditor *ieditor = EditorManager::currentEditor();
  1201. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1202. QTC_ASSERT(editor != 0, return);
  1203. QTextDocument *doc = editor->document();
  1204. BaseTextDocumentLayout *documentLayout =
  1205. qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
  1206. QTC_ASSERT(documentLayout != 0, return);
  1207. QTextBlock block = editor->textCursor().block();
  1208. int indent = BaseTextDocumentLayout::foldingIndent(block);
  1209. if (fold) {
  1210. if (BaseTextDocumentLayout::isFolded(block)) {
  1211. while (block.isValid() && (BaseTextDocumentLayout::foldingIndent(block) >= indent
  1212. || !block.isVisible())) {
  1213. block = block.previous();
  1214. }
  1215. }
  1216. if (BaseTextDocumentLayout::canFold(block))
  1217. ++indent;
  1218. while (depth != 0 && block.isValid()) {
  1219. const int indent2 = BaseTextDocumentLayout::foldingIndent(block);
  1220. if (BaseTextDocumentLayout::canFold(block) && indent2 < indent) {
  1221. BaseTextDocumentLayout::doFoldOrUnfold(block, false);
  1222. if (depth > 0)
  1223. --depth;
  1224. indent = indent2;
  1225. }
  1226. block = block.previous();
  1227. }
  1228. } else {
  1229. if (BaseTextDocumentLayout::isFolded(block)) {
  1230. if (depth < 0) {
  1231. // recursively open fold
  1232. while (depth < 0 && block.isValid()
  1233. && BaseTextDocumentLayout::foldingIndent(block) >= indent) {
  1234. if (BaseTextDocumentLayout::canFold(block)) {
  1235. BaseTextDocumentLayout::doFoldOrUnfold(block, true);
  1236. if (depth > 0)
  1237. --depth;
  1238. }
  1239. block = block.next();
  1240. }
  1241. } else {
  1242. if (BaseTextDocumentLayout::canFold(block)) {
  1243. BaseTextDocumentLayout::doFoldOrUnfold(block, true);
  1244. if (depth > 0)
  1245. --depth;
  1246. }
  1247. }
  1248. }
  1249. }
  1250. documentLayout->requestUpdate();
  1251. documentLayout->emitDocumentSizeChanged();
  1252. }
  1253. void FakeVimPluginPrivate::foldGoTo(int count, bool current)
  1254. {
  1255. IEditor *ieditor = EditorManager::currentEditor();
  1256. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1257. QTC_ASSERT(editor != 0, return);
  1258. QTextCursor tc = editor->textCursor();
  1259. QTextBlock block = tc.block();
  1260. int pos = -1;
  1261. if (count > 0) {
  1262. int repeat = count;
  1263. block = block.next();
  1264. QTextBlock prevBlock = block;
  1265. int indent = BaseTextDocumentLayout::foldingIndent(block);
  1266. block = block.next();
  1267. while (block.isValid()) {
  1268. int newIndent = BaseTextDocumentLayout::foldingIndent(block);
  1269. if (current ? indent > newIndent : indent < newIndent) {
  1270. if (prevBlock.isVisible()) {
  1271. pos = prevBlock.position();
  1272. if (--repeat <= 0)
  1273. break;
  1274. } else if (current) {
  1275. indent = newIndent;
  1276. }
  1277. }
  1278. if (!current)
  1279. indent = newIndent;
  1280. prevBlock = block;
  1281. block = block.next();
  1282. }
  1283. } else if (count < 0) {
  1284. int repeat = -count;
  1285. int indent = BaseTextDocumentLayout::foldingIndent(block);
  1286. block = block.previous();
  1287. while (block.isValid()) {
  1288. int newIndent = BaseTextDocumentLayout::foldingIndent(block);
  1289. if (current ? indent > newIndent : indent < newIndent) {
  1290. while (block.isValid() && !block.isVisible())
  1291. block = block.previous();
  1292. pos = block.position();
  1293. if (--repeat <= 0)
  1294. break;
  1295. }
  1296. if (!current)
  1297. indent = newIndent;
  1298. block = block.previous();
  1299. }
  1300. }
  1301. if (pos != -1) {
  1302. tc.setPosition(pos, QTextCursor::KeepAnchor);
  1303. editor->setTextCursor(tc);
  1304. }
  1305. }
  1306. void FakeVimPluginPrivate::jumpToGlobalMark(QChar mark, bool backTickMode,
  1307. const QString &fileName)
  1308. {
  1309. Core::IEditor *iedit = Core::EditorManager::openEditor(fileName);
  1310. if

Large files files are truncated, but you can click here to view the full file