diff options
70 files changed, 1868 insertions, 336 deletions
diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp index 95effec40c..1fee419252 100644 --- a/src/libs/cplusplus/pp-engine.cpp +++ b/src/libs/cplusplus/pp-engine.cpp @@ -1,5 +1,4 @@ -/************************************************************************** -** +/** ** This file is part of Qt Creator ** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). @@ -51,6 +50,7 @@ #include <Lexer.h> #include <Token.h> #include <Literals.h> +#include <cctype> #include <QtDebug> #include <algorithm> diff --git a/src/libs/utils/basevalidatinglineedit.h b/src/libs/utils/basevalidatinglineedit.h index 9dd8f8ec56..a57d04b49a 100644 --- a/src/libs/utils/basevalidatinglineedit.h +++ b/src/libs/utils/basevalidatinglineedit.h @@ -39,7 +39,8 @@ namespace Utils { struct BaseValidatingLineEditPrivate; -/* Base class for validating line edits that performs validation in a virtual +/** + * Base class for validating line edits that performs validation in a virtual * validate() function to be implemented in derived classes. * When invalid, the text color will turn red and a tooltip will * contain the error message. This approach is less intrusive than a @@ -47,9 +48,10 @@ struct BaseValidatingLineEditPrivate; * * The widget has a concept of an "initialText" which can be something like * "<Enter name here>". This results in state 'DisplayingInitialText', which - * is not valid, but is not marked red. */ - -class QWORKBENCH_UTILS_EXPORT BaseValidatingLineEdit : public QLineEdit { + * is not valid, but is not marked red. + */ +class QWORKBENCH_UTILS_EXPORT BaseValidatingLineEdit : public QLineEdit +{ Q_OBJECT Q_DISABLE_COPY(BaseValidatingLineEdit) Q_PROPERTY(QString initialText READ initialText WRITE setInitialText DESIGNABLE true) @@ -97,4 +99,5 @@ private: } // namespace Utils } // namespace Core + #endif // BASEVALIDATINGLINEEDIT_H diff --git a/src/libs/utils/classnamevalidatinglineedit.cpp b/src/libs/utils/classnamevalidatinglineedit.cpp index 86f86a124d..64d2031a57 100644 --- a/src/libs/utils/classnamevalidatinglineedit.cpp +++ b/src/libs/utils/classnamevalidatinglineedit.cpp @@ -84,8 +84,11 @@ bool ClassNameValidatingLineEdit::validate(const QString &value, QString *errorM if (errorMessage) *errorMessage = tr("The class name must not contain namespace delimiters."); return false; - } - if (!m_d->m_nameRegexp.exactMatch(value)) { + } else if (value.isEmpty()) { + if (errorMessage) + *errorMessage = tr("Please enter a class name."); + return false; + } else if (!m_d->m_nameRegexp.exactMatch(value)) { if (errorMessage) *errorMessage = tr("The class name contains invalid characters."); return false; diff --git a/src/libs/utils/filenamevalidatinglineedit.h b/src/libs/utils/filenamevalidatinglineedit.h index cf37757175..60145bdf4c 100644 --- a/src/libs/utils/filenamevalidatinglineedit.h +++ b/src/libs/utils/filenamevalidatinglineedit.h @@ -35,6 +35,10 @@ namespace Core { namespace Utils { +/** + * A control that let's the user choose a file name, based on a QLineEdit. Has + * some validation logic for embedding into QWizardPage. + */ class QWORKBENCH_UTILS_EXPORT FileNameValidatingLineEdit : public BaseValidatingLineEdit { Q_OBJECT @@ -47,6 +51,10 @@ public: bool allowDirectories = false, QString *errorMessage = 0); + /** + * Sets whether entering directories is allowed. This will enable the user + * to enter slashes in the filename. Default is off. + */ bool allowDirectories() const; void setAllowDirectories(bool v); diff --git a/src/libs/utils/filewizardpage.cpp b/src/libs/utils/filewizardpage.cpp index 59f1bbc56b..3fb0b79b8c 100644 --- a/src/libs/utils/filewizardpage.cpp +++ b/src/libs/utils/filewizardpage.cpp @@ -30,10 +30,6 @@ #include "filewizardpage.h" #include "ui_filewizardpage.h" -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtGui/QMessageBox> - namespace Core { namespace Utils { @@ -103,6 +99,16 @@ bool FileWizardPage::isComplete() const return m_d->m_complete; } +void FileWizardPage::setNameLabel(const QString &label) +{ + m_d->m_ui.nameLabel->setText(label); +} + +void FileWizardPage::setPathLabel(const QString &label) +{ + m_d->m_ui.pathLabel->setText(label); +} + void FileWizardPage::slotValidChanged() { const bool newComplete = m_d->m_ui.pathChooser->isValid() && m_d->m_ui.nameLineEdit->isValid(); diff --git a/src/libs/utils/filewizardpage.h b/src/libs/utils/filewizardpage.h index 657ec4f4ef..b41839e0bf 100644 --- a/src/libs/utils/filewizardpage.h +++ b/src/libs/utils/filewizardpage.h @@ -39,10 +39,15 @@ namespace Utils { struct FileWizardPagePrivate; -/* Standard wizard page for a single file letting the user choose name - * and path. Sets the "FileNames" QWizard field. */ - -class QWORKBENCH_UTILS_EXPORT FileWizardPage : public QWizardPage { +/** + * Standard wizard page for a single file letting the user choose name + * and path. Sets the "FileNames" QWizard field. + * + * The name and path labels can be changed. By default they are simply "Name:" + * and "Path:". + */ +class QWORKBENCH_UTILS_EXPORT FileWizardPage : public QWizardPage +{ Q_OBJECT Q_DISABLE_COPY(FileWizardPage) Q_PROPERTY(QString path READ path WRITE setPath DESIGNABLE true) @@ -56,7 +61,10 @@ public: virtual bool isComplete() const; - // Validate a base name entry field (potentially containing extension) + void setNameLabel(const QString &label); + void setPathLabel(const QString &label); + + // Validate a base name entry field (potentially containing extension) static bool validateBaseName(const QString &name, QString *errorMessage = 0); signals: diff --git a/src/libs/utils/filewizardpage.ui b/src/libs/utils/filewizardpage.ui index 2e614c6f55..98d0873128 100644 --- a/src/libs/utils/filewizardpage.ui +++ b/src/libs/utils/filewizardpage.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>196</width> + <height>68</height> </rect> </property> <property name="windowTitle"> @@ -16,52 +16,26 @@ <property name="title"> <string>Choose the location</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QWidget" name="widget" native="true"> - <layout class="QFormLayout" name="formLayout"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="nameLabel"> - <property name="text"> - <string>Name:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="Core::Utils::FileNameValidatingLineEdit" name="nameLineEdit"/> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="pathLabel"> - <property name="text"> - <string>Path:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="Core::Utils::PathChooser" name="pathChooser" native="true"/> - </item> - </layout> - </widget> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Name:</string> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>201</height> - </size> + </widget> + </item> + <item row="0" column="1"> + <widget class="Core::Utils::FileNameValidatingLineEdit" name="nameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Path:</string> </property> - </spacer> + </widget> + </item> + <item row="1" column="1"> + <widget class="Core::Utils::PathChooser" name="pathChooser" native="true"/> </item> </layout> </widget> diff --git a/src/libs/utils/newclasswidget.h b/src/libs/utils/newclasswidget.h index 15b21fcb0e..674adf6c84 100644 --- a/src/libs/utils/newclasswidget.h +++ b/src/libs/utils/newclasswidget.h @@ -43,11 +43,12 @@ namespace Utils { struct NewClassWidgetPrivate; -/* NewClassWidget: Utility widget for 'New Class' wizards. Prompts the user +/** + * NewClassWidget: Utility widget for 'New Class' wizards. Prompts the user * to enter a class name (optionally derived from some base class) and file * names for header, source and form files. Has some smart logic to derive - * the file names from the class name. */ - + * the file names from the class name. + */ class QWORKBENCH_UTILS_EXPORT NewClassWidget : public QWidget { Q_DISABLE_COPY(NewClassWidget) @@ -115,8 +116,10 @@ public slots: void setFormInputCheckable(bool v); void setFormInputChecked(bool v); - /* The name passed into the new class widget will be reformatted to be a - * valid class name. */ + /** + * The name passed into the new class widget will be reformatted to be a + * valid class name. + */ void setClassName(const QString &suggestedName); void setBaseClassName(const QString &); void setPath(const QString &path); @@ -127,13 +130,15 @@ public slots: void setAllowDirectories(bool v); void setLowerCaseFiles(bool v); - /* Suggest a class name from the base class by stripping the leading 'Q' + /** + * Suggest a class name from the base class by stripping the leading 'Q' * character. This will happen automagically if the base class combo - * changes until the class line edited is manually edited. */ + * changes until the class line edited is manually edited. + */ void suggestClassNameFromBase(); public slots: - // Trigger an update (after changing settings) + /** Trigger an update (after changing settings) */ void triggerUpdateFileNames(); private slots: diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 99f2330371..04607ef700 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -39,10 +39,10 @@ namespace Utils { struct PathChooserPrivate; -/* A Control that let's the user choose a path, consisting of a QLineEdit and - * a "Browse" button. Has some validation logic for embedding into - * QWizardPage. */ - +/** + * A control that let's the user choose a path, consisting of a QLineEdit and + * a "Browse" button. Has some validation logic for embedding into QWizardPage. + */ class QWORKBENCH_UTILS_EXPORT PathChooser : public QWidget { Q_DISABLE_COPY(PathChooser) @@ -74,12 +74,12 @@ public: QString path() const; - // Returns the suggested label title when used in a form layout + /** Returns the suggested label title when used in a form layout. */ static QString label(); virtual bool validatePath(const QString &path, QString *errorMessage = 0); - // Return the home directory, which needs some fixing under Windows. + /** Return the home directory, which needs some fixing under Windows. */ static QString homePath(); private: diff --git a/src/libs/utils/process_stub_win.c b/src/libs/utils/process_stub_win.c index 3d99a68539..0ca484eb75 100644 --- a/src/libs/utils/process_stub_win.c +++ b/src/libs/utils/process_stub_win.c @@ -34,6 +34,7 @@ #define _WIN32_WINNT 0x0501 /* WinXP, needed for DebugActiveProcessStop() */ #include <windows.h> +#include <shellapi.h> #include <string.h> #include <stdio.h> #include <errno.h> diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp index a047682b81..b9c8259020 100644 --- a/src/libs/utils/submiteditorwidget.cpp +++ b/src/libs/utils/submiteditorwidget.cpp @@ -33,8 +33,15 @@ #include <QtCore/QDebug> #include <QtCore/QPointer> #include <QtCore/QTimer> +#include <QtCore/QSignalMapper> #include <QtGui/QPushButton> +#include <QtGui/QMenu> +#include <QtGui/QLineEdit> +#include <QtGui/QFormLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QToolButton> +#include <QtGui/QSpacerItem> enum { debug = 0 }; @@ -104,8 +111,12 @@ QList<int> selectedRows(const QAbstractItemView *view) } // ----------- SubmitEditorWidgetPrivate + struct SubmitEditorWidgetPrivate { + // A pair of position/action to extend context menus + typedef QPair<int, QPointer<QAction> > AdditionalContextMenuAction; + SubmitEditorWidgetPrivate(); Ui::SubmitEditorWidget m_ui; @@ -113,13 +124,22 @@ struct SubmitEditorWidgetPrivate bool m_filesChecked; int m_fileNameColumn; int m_activatedRow; + + QList<AdditionalContextMenuAction> descriptionEditContextMenuActions; + QFormLayout *m_fieldLayout; + // Field entries (label, line edits) + typedef QPair<QString, QLineEdit*> FieldEntry; + QList<FieldEntry> m_fieldEntries; + QSignalMapper *m_fieldSignalMapper; }; SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() : m_filesSelected(false), m_filesChecked(false), m_fileNameColumn(1), - m_activatedRow(-1) + m_activatedRow(-1), + m_fieldLayout(0), + m_fieldSignalMapper(0) { } @@ -128,6 +148,10 @@ SubmitEditorWidget::SubmitEditorWidget(QWidget *parent) : m_d(new SubmitEditorWidgetPrivate) { m_d->m_ui.setupUi(this); + m_d->m_ui.description->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_d->m_ui.description, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(editorCustomContextMenuRequested(QPoint))); + // File List m_d->m_ui.fileView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_d->m_ui.fileView->setRootIsDecorated(false); @@ -212,7 +236,18 @@ QString SubmitEditorWidget::trimmedDescriptionText() const QString SubmitEditorWidget::descriptionText() const { - return m_d->m_ui.description->toPlainText(); + QString rc = m_d->m_ui.description->toPlainText(); + // append field entries + foreach(const SubmitEditorWidgetPrivate::FieldEntry &fe, m_d->m_fieldEntries) { + const QString fieldText = fe.second->text().trimmed(); + if (!fieldText.isEmpty()) { + rc += fe.first; + rc += QLatin1Char(' '); + rc += fieldText; + rc += QLatin1Char('\n'); + } + } + return rc; } void SubmitEditorWidget::setDescriptionText(const QString &text) @@ -381,6 +416,74 @@ void SubmitEditorWidget::insertTopWidget(QWidget *w) m_d->m_ui.vboxLayout->insertWidget(0, w); } +void SubmitEditorWidget::addDescriptionEditContextMenuAction(QAction *a) +{ + m_d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(-1, a)); +} + +void SubmitEditorWidget::insertDescriptionEditContextMenuAction(int pos, QAction *a) +{ + m_d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(pos, a)); +} + +void SubmitEditorWidget::editorCustomContextMenuRequested(const QPoint &pos) +{ + QMenu *menu = m_d->m_ui.description->createStandardContextMenu(); + // Extend + foreach (const SubmitEditorWidgetPrivate::AdditionalContextMenuAction &a, m_d->descriptionEditContextMenuActions) { + if (a.second) { + if (a.first >= 0) { + menu->insertAction(menu->actions().at(a.first), a.second); + } else { + menu->addAction(a.second); + } + } + } + menu->exec(m_d->m_ui.description->mapToGlobal(pos)); + delete menu; +} + +QLineEdit *SubmitEditorWidget::addField(const QString &label, bool hasDialogButton) +{ + // Insert the form layout below the editor + if (!m_d->m_fieldLayout) { + QHBoxLayout *outerLayout = new QHBoxLayout; + m_d->m_fieldLayout = new QFormLayout; + outerLayout->addLayout(m_d->m_fieldLayout); + outerLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + QBoxLayout *descrLayout = qobject_cast<QBoxLayout*>(m_d->m_ui.descriptionBox->layout()); + Q_ASSERT(descrLayout); + descrLayout->addLayout(outerLayout); + } + if (hasDialogButton && !m_d->m_fieldSignalMapper) { + m_d->m_fieldSignalMapper = new QSignalMapper; + connect(m_d->m_fieldSignalMapper, SIGNAL(mapped(int)), this, SIGNAL(fieldDialogRequested(int))); + } + // Add a field row consisting of label and line edit + QLineEdit *lineEdit = new QLineEdit; + QHBoxLayout *fieldLayout = new QHBoxLayout; + fieldLayout->addWidget(lineEdit); + if (hasDialogButton) { + QToolButton *dialogButton = new QToolButton; + dialogButton->setText(tr("...")); + connect(dialogButton, SIGNAL(clicked()), m_d->m_fieldSignalMapper, SLOT(map())); + m_d->m_fieldSignalMapper->setMapping(dialogButton, m_d->m_fieldEntries.size()); + fieldLayout->addWidget(dialogButton); + } + QToolButton *clearButton = new QToolButton; + clearButton->setText(tr("Clear")); + connect(clearButton, SIGNAL(clicked()), lineEdit, SLOT(clear())); + fieldLayout->addWidget(clearButton); + m_d->m_fieldLayout->addRow(label, fieldLayout); + m_d->m_fieldEntries.push_back(SubmitEditorWidgetPrivate::FieldEntry(label, lineEdit)); + return lineEdit; +} + +QLineEdit *SubmitEditorWidget::fieldLineEdit(int i) const +{ + return m_d->m_fieldEntries.at(i).second; +} + } // namespace Utils } // namespace Core diff --git a/src/libs/utils/submiteditorwidget.h b/src/libs/utils/submiteditorwidget.h index 74dd56d006..e77da4379e 100644 --- a/src/libs/utils/submiteditorwidget.h +++ b/src/libs/utils/submiteditorwidget.h @@ -41,6 +41,7 @@ class QListWidgetItem; class QAction; class QAbstractItemModel; class QModelIndex; +class QLineEdit; QT_END_NAMESPACE namespace Core { @@ -104,10 +105,20 @@ public: QPlainTextEdit *descriptionEdit() const; + void addDescriptionEditContextMenuAction(QAction *a); + void insertDescriptionEditContextMenuAction(int pos, QAction *a); + + // Fields are additional fields consisting of a Label and a Line Edit. + // A field dialog is wired to a button labeled "..." that pops up a chooser + // resulting in text being set + QLineEdit *addField(const QString &label, bool hasDialogButton); + QLineEdit *fieldLineEdit(int i) const; + signals: void diffSelected(const QStringList &); void fileSelectionChanged(bool someFileSelected); void fileCheckStateChanged(bool someFileChecked); + void fieldDialogRequested(int); protected: virtual void changeEvent(QEvent *e); @@ -120,6 +131,7 @@ private slots: void updateActions(); void updateSubmitAction(); void updateDiffAction(); + void editorCustomContextMenuRequested(const QPoint &); private: bool hasSelection() const; diff --git a/src/plugins/bookmarks/bookmarkmanager.cpp b/src/plugins/bookmarks/bookmarkmanager.cpp index 4954cad38e..6e079a23e7 100644 --- a/src/plugins/bookmarks/bookmarkmanager.cpp +++ b/src/plugins/bookmarks/bookmarkmanager.cpp @@ -501,7 +501,7 @@ void BookmarkManager::documentPrevNext(bool next) } Core::EditorManager *em = Core::EditorManager::instance(); - em->addCurrentPositionToNavigationHistory(true); + em->addCurrentPositionToNavigationHistory(); if (next) { if (nextLine == -1) editor->gotoLine(firstLine); @@ -513,7 +513,6 @@ void BookmarkManager::documentPrevNext(bool next) else editor->gotoLine(prevLine); } - em->addCurrentPositionToNavigationHistory(); } void BookmarkManager::next() diff --git a/src/plugins/cmakeprojectmanager/makestep.cpp b/src/plugins/cmakeprojectmanager/makestep.cpp index d7544b62e5..8417e842af 100644 --- a/src/plugins/cmakeprojectmanager/makestep.cpp +++ b/src/plugins/cmakeprojectmanager/makestep.cpp @@ -202,7 +202,6 @@ void MakeStep::removeDirectory(const QString &dir) m_openDirectories.remove(dir); } - CMakeProject *MakeStep::project() const { return m_pro; diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp index 5e0cac944e..195325bceb 100644 --- a/src/plugins/coreplugin/dialogs/settingsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp @@ -30,18 +30,37 @@ #include "settingsdialog.h" #include <extensionsystem/pluginmanager.h> +#include "icore.h" +#include <QtCore/QSettings> #include <QtGui/QHeaderView> #include <QtGui/QPushButton> +namespace { + struct PageData { + int index; + QString category; + QString id; + }; +} + +Q_DECLARE_METATYPE(::PageData); + using namespace Core; using namespace Core::Internal; -SettingsDialog::SettingsDialog(QWidget *parent, const QString &initialCategory, - const QString &initialPage) +SettingsDialog::SettingsDialog(QWidget *parent, const QString &categoryId, + const QString &pageId) : QDialog(parent), m_applied(false) { setupUi(this); + QString initialCategory = categoryId; + QString initialPage = pageId; + if (initialCategory.isEmpty() && initialPage.isEmpty()) { + QSettings *settings = ICore::instance()->settings(); + initialCategory = settings->value("General/LastPreferenceCategory", QVariant(QString())).toString(); + initialPage = settings->value("General/LastPreferencePage", QVariant(QString())).toString(); + } buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); @@ -50,7 +69,7 @@ SettingsDialog::SettingsDialog(QWidget *parent, const QString &initialCategory, pageTree->header()->setVisible(false); connect(pageTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), - this, SLOT(pageSelected(QTreeWidgetItem *))); + this, SLOT(pageSelected())); QMap<QString, QTreeWidgetItem *> categories; @@ -59,9 +78,14 @@ SettingsDialog::SettingsDialog(QWidget *parent, const QString &initialCategory, int index = 0; foreach (IOptionsPage *page, pages) { + PageData pageData; + pageData.index = index; + pageData.category = page->category(); + pageData.id = page->id(); + QTreeWidgetItem *item = new QTreeWidgetItem; item->setText(0, page->trName()); - item->setData(0, Qt::UserRole, index); + item->setData(0, Qt::UserRole, qVariantFromValue(pageData)); QStringList categoriesId = page->category().split(QLatin1Char('|')); QStringList trCategories = page->trCategory().split(QLatin1Char('|')); @@ -71,7 +95,7 @@ SettingsDialog::SettingsDialog(QWidget *parent, const QString &initialCategory, if (!categories.contains(currentCategory)) { treeitem = new QTreeWidgetItem(pageTree); treeitem->setText(0, trCategories.at(0)); - treeitem->setData(0, Qt::UserRole, index); + treeitem->setData(0, Qt::UserRole, qVariantFromValue(pageData)); categories.insert(currentCategory, treeitem); } @@ -81,7 +105,7 @@ SettingsDialog::SettingsDialog(QWidget *parent, const QString &initialCategory, treeitem = new QTreeWidgetItem(categories.value(currentCategory)); currentCategory += QLatin1Char('|') + categoriesId.at(catCount); treeitem->setText(0, trCategories.at(catCount)); - treeitem->setData(0, Qt::UserRole, index); + treeitem->setData(0, Qt::UserRole, qVariantFromValue(pageData)); categories.insert(currentCategory, treeitem); } else { currentCategory += QLatin1Char('|') + categoriesId.at(catCount); @@ -114,10 +138,13 @@ SettingsDialog::~SettingsDialog() { } -void SettingsDialog::pageSelected(QTreeWidgetItem *) +void SettingsDialog::pageSelected() { QTreeWidgetItem *item = pageTree->currentItem(); - int index = item->data(0, Qt::UserRole).toInt(); + PageData data = item->data(0, Qt::UserRole).value<PageData>(); + int index = data.index; + m_currentCategory = data.category; + m_currentPage = data.id; stackedPages->setCurrentIndex(index); } @@ -151,3 +178,11 @@ bool SettingsDialog::execDialog() exec(); return m_applied; } + +void SettingsDialog::done(int val) +{ + QSettings *settings = ICore::instance()->settings(); + settings->setValue("General/LastPreferenceCategory", m_currentCategory); + settings->setValue("General/LastPreferencePage", m_currentPage); + QDialog::done(val); +} diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.h b/src/plugins/coreplugin/dialogs/settingsdialog.h index 36c7d8a1a6..88f0a7a5ab 100644 --- a/src/plugins/coreplugin/dialogs/settingsdialog.h +++ b/src/plugins/coreplugin/dialogs/settingsdialog.h @@ -53,8 +53,11 @@ public: // at least once bool execDialog(); +public slots: + void done(int); + private slots: - void pageSelected(QTreeWidgetItem *cat); + void pageSelected(); void accept(); void reject(); void apply(); @@ -62,6 +65,8 @@ private slots: private: QList<Core::IOptionsPage*> m_pages; bool m_applied; + QString m_currentCategory; + QString m_currentPage; }; } // namespace Internal diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 6cfec71e0f..8badefeb9a 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -190,7 +190,7 @@ EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) : m_goBackAction(new QAction(EditorManager::tr("Go back"), parent)), m_goForwardAction(new QAction(EditorManager::tr("Go forward"), parent)), m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)), - currentNavigationHistoryPosition(-1), + currentNavigationHistoryPosition(0), m_windowPopup(0), m_coreListener(0) { @@ -473,15 +473,11 @@ void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHisto setCurrentView(0); if (m_d->m_currentEditor == editor) return; - if (m_d->m_currentEditor) - updateCurrentPositionInNavigationHistory(); + if (m_d->m_currentEditor && !ignoreNavigationHistory) + addCurrentPositionToNavigationHistory(); m_d->m_currentEditor = editor; if (editor) { - bool addToHistory = (!ignoreNavigationHistory); - if (addToHistory) - addCurrentPositionToNavigationHistory(true); - if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor)) splitterOrView->view()->setCurrentEditor(editor); } @@ -723,8 +719,8 @@ bool EditorManager::closeEditors(const QList<IEditor*> editorsToClose, bool askA } } - emit editorsClosed(acceptedEditors); + foreach (IEditor *editor, acceptedEditors) { delete editor; } @@ -1362,7 +1358,7 @@ QList<IEditor*> EditorManager::editorHistory() const return m_d->m_editorHistory; } -void EditorManager::addCurrentPositionToNavigationHistory(bool compress) +void EditorManager::addCurrentPositionToNavigationHistory(const QByteArray &saveState, bool compress) { IEditor *editor = currentEditor(); if (!editor) @@ -1371,20 +1367,25 @@ void EditorManager::addCurrentPositionToNavigationHistory(bool compress) return; QString fileName = editor->file()->fileName(); - QByteArray state = editor->saveState(); + QByteArray state; + if (saveState.isNull()) { + state = editor->saveState(); + } else { + state = saveState; + } // cut existing int firstIndexToRemove; - if (compress && m_d->currentNavigationHistoryPosition >= 0) { + if (compress && m_d->currentNavigationHistoryPosition > 0) { EditorManagerPrivate::EditLocation *previousLocation = - m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition); + m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition-1); if ((previousLocation->editor && editor == previousLocation->editor) || (!fileName.isEmpty() && previousLocation->fileName == fileName)) { - firstIndexToRemove = m_d->currentNavigationHistoryPosition; + firstIndexToRemove = m_d->currentNavigationHistoryPosition-1; } else { - firstIndexToRemove = m_d->currentNavigationHistoryPosition+1; + firstIndexToRemove = m_d->currentNavigationHistoryPosition; } } else { - firstIndexToRemove = m_d->currentNavigationHistoryPosition+1; + firstIndexToRemove = m_d->currentNavigationHistoryPosition; } if (firstIndexToRemove >= 0) { for (int i = m_d->m_navigationHistory.size()-1; i >= firstIndexToRemove; --i) { @@ -1400,17 +1401,26 @@ void EditorManager::addCurrentPositionToNavigationHistory(bool compress) location->kind = editor->kind(); location->state = QVariant(state); m_d->m_navigationHistory.append(location); - m_d->currentNavigationHistoryPosition = m_d->m_navigationHistory.size()-1; + m_d->currentNavigationHistoryPosition = m_d->m_navigationHistory.size(); updateActions(); } void EditorManager::updateCurrentPositionInNavigationHistory() { - if (!m_d->m_currentEditor - || m_d->currentNavigationHistoryPosition < 0 - || m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition)->editor != m_d->m_currentEditor) + if (!m_d->m_currentEditor || !m_d->m_currentEditor->file()) return; - m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition)->state = m_d->m_currentEditor->saveState(); + + EditorManagerPrivate::EditLocation *location; + if (m_d->currentNavigationHistoryPosition < m_d->m_navigationHistory.size()) { + location = m_d->m_navigationHistory[m_d->currentNavigationHistoryPosition]; + } else { + location = new EditorManagerPrivate::EditLocation; + m_d->m_navigationHistory.append(location); + } + location->editor = m_d->m_currentEditor; + location->fileName = m_d->m_currentEditor->file()->fileName(); + location->kind = m_d->m_currentEditor->kind(); + location->state = QVariant(m_d->m_currentEditor->saveState()); } void EditorManager::goBackInNavigationHistory() diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 401e94b00d..75c7e28cc3 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -132,7 +132,7 @@ public: QList<IEditor*> editorsForFiles(QList<IFile*> files) const; //QList<EditorGroup *> editorGroups() const; QList<IEditor*> editorHistory() const; - void addCurrentPositionToNavigationHistory(bool compress = false); + void addCurrentPositionToNavigationHistory(const QByteArray &saveState = QByteArray(), bool compress = false); bool saveEditor(IEditor *editor); diff --git a/src/plugins/coreplugin/welcomemode.cpp b/src/plugins/coreplugin/welcomemode.cpp index de9b8893f6..588bfcb827 100644 --- a/src/plugins/coreplugin/welcomemode.cpp +++ b/src/plugins/coreplugin/welcomemode.cpp @@ -92,6 +92,7 @@ WelcomeModePrivate::WelcomeModePrivate() : m_projectHtmlTemplate(readFile(QLatin1String(":/core/html/recent_projects.html"))), m_baseUrl(QUrl(QLatin1String("qrc:/core/html/welcome.html"))) { + m_webview->setContextMenuPolicy(Qt::NoContextMenu); } #if defined(QT_NO_WEBKIT) diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index e356e378cf..5b363e291d 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -979,9 +979,8 @@ bool CPPEditor::openEditorAt(Symbol *s) if (baseTextDocument()->fileName() == fileName) { Core::EditorManager *editorManager = Core::EditorManager::instance(); - editorManager->addCurrentPositionToNavigationHistory(true); - gotoLine(line, column); editorManager->addCurrentPositionToNavigationHistory(); + gotoLine(line, column); setFocus(); return true; } diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index 47a72dc0a9..0a102c4ae9 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -339,6 +339,7 @@ DebuggerSettings *theDebuggerSettings() item = new DebuggerAction(instance); instance->insertItem(WatchExpressionInWindow, item); item->setTextPattern(QObject::tr("Watch expression \"%1\" in separate window")); + //item->setCheckable(true); item = new DebuggerAction(instance); instance->insertItem(AssignValue, item); @@ -421,6 +422,10 @@ DebuggerSettings *theDebuggerSettings() item->setText(QObject::tr("Use custom dumpers")); item->setCheckable(true); + item = new DebuggerAction(instance); + instance->insertItem(Terminal, item); + item->setDefaultValue("xterm"); + item->setSettingsKey("DebugMode", "Terminal"); item = new DebuggerAction(instance); instance->insertItem(ListSourceFiles, item); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index 2a64e45c2a..528bcc2c4d 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -141,6 +141,7 @@ enum DebuggerActionCode GdbScriptFile, GdbAutoRun, GdbAutoQuit, + Terminal, // Watchers & Locals WatchExpression, diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 13de5d0c8e..8f4905c9d2 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -268,6 +268,8 @@ QWidget *GdbOptionPage::createPage(QWidget *parent) m_ui.gdbLocationChooser->setPromptDialogTitle(tr("Choose Gdb Location")); m_ui.scriptFileChooser->setExpectedKind(Core::Utils::PathChooser::File); m_ui.scriptFileChooser->setPromptDialogTitle(tr("Choose Location of Startup Script File")); + m_ui.terminalChooser->setExpectedKind(Core::Utils::PathChooser::Command); + m_ui.terminalChooser->setPromptDialogTitle(tr("Choose Location of Terminal Application")); theDebuggerAction(GdbLocation) ->connectWidget(m_ui.gdbLocationChooser); @@ -275,6 +277,8 @@ QWidget *GdbOptionPage::createPage(QWidget *parent) ->connectWidget(m_ui.scriptFileChooser); theDebuggerAction(GdbEnvironment) ->connectWidget(m_ui.environmentEdit); + theDebuggerAction(Terminal) + ->connectWidget(m_ui.terminalChooser); theDebuggerAction(AllPluginBreakpoints) ->connectWidget(m_ui.radioButtonAllPluginBreakpoints); @@ -328,6 +332,7 @@ void GdbOptionPage::apply() theDebuggerAction(GdbLocation)->apply(s); theDebuggerAction(GdbScriptFile)->apply(s); theDebuggerAction(GdbEnvironment)->apply(s); + theDebuggerAction(Terminal)->apply(s); theDebuggerAction(AllPluginBreakpoints)->apply(s); theDebuggerAction(SelectedPluginBreakpoints)->apply(s); diff --git a/src/plugins/debugger/gdboptionpage.ui b/src/plugins/debugger/gdboptionpage.ui index 5305559947..3a51818c86 100644 --- a/src/plugins/debugger/gdboptionpage.ui +++ b/src/plugins/debugger/gdboptionpage.ui @@ -32,10 +32,21 @@ <item row="0" column="0"> <widget class="QLabel" name="labelGdbLocation"> <property name="toolTip"> - <string>This is either a full abolute path leading to the gdb binary you intend to use or the name of a gdb binary that wiull be searched in your PATH.</string> + <string>This is either a full abolute path leading to the gdb binary you intend to use or the name of a gdb binary that will be searched in your PATH.</string> </property> <property name="text"> - <string>Gdb Location:</string> + <string>Gdb location:</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="terminalLocation"> + <property name="toolTip"> + <string>This is either a full abolute path leading to the terminal +you indent to use or the name of a terminal that will be searched in your PATH.</string> + </property> + <property name="text"> + <string>Terminal:</string> </property> </widget> </item> @@ -55,7 +66,7 @@ <string>This is either empty or points to a file containing gdb commands that will be executed immediately after gdb starts up.</string> </property> <property name="text"> - <string>Gdb Startup Script:</string> + <string>Gdb startup script:</string> </property> </widget> </item> @@ -65,6 +76,9 @@ <item row="0" column="1"> <widget class="Core::Utils::PathChooser" name="gdbLocationChooser" native="true"/> </item> + <item row="3" column="1"> + <widget class="Core::Utils::PathChooser" name="terminalChooser" native="true"/> + </item> </layout> </widget> </item> diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 9dcec117d3..ab96217b1e 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -499,9 +499,6 @@ QVariant WatchHandler::data(const QModelIndex &idx, int role) const case INameRole: return data.iname; - case VisualRole: - return m_displayedINames.contains(data.iname); - case ExpandedRole: //qDebug() << " FETCHING: " << data.iname // << m_expandedINames.contains(data.iname) diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index 4cac4a87f9..a419203815 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -132,7 +132,7 @@ public: bool changed; }; -enum { INameRole = Qt::UserRole, ExpressionRole, VisualRole, ExpandedRole }; +enum { INameRole = Qt::UserRole, ExpressionRole, ExpandedRole }; class WatchHandler : public QAbstractItemModel diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index ceef4bc1f8..de30e8f380 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -54,7 +54,7 @@ using namespace Debugger::Internal; // ///////////////////////////////////////////////////////////////////// -enum { INameRole = Qt::UserRole, ExpressionRole, VisualRole, ExpandedRole }; +enum { INameRole = Qt::UserRole, ExpressionRole, ExpandedRole }; class WatchDelegate : public QItemDelegate { @@ -158,8 +158,6 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) QAction *act2 = new QAction("Always adjust column widths to contents", &menu); act2->setCheckable(true); act2->setChecked(m_alwaysResizeColumnsToContents); - //QAction *act3 = 0; - QAction *act4 = 0; menu.addAction(act1); menu.addAction(act2); @@ -169,20 +167,16 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) QString exp = model()->data(mi0).toString(); QModelIndex mi1 = idx.sibling(idx.row(), 0); QString value = model()->data(mi1).toString(); - bool visual = false; menu.addSeparator(); int type = (m_type == LocalsType) ? WatchExpression : RemoveWatchExpression; menu.addAction(theDebuggerAction(type)->updatedAction(exp)); - visual = model()->data(mi0, VisualRole).toBool(); - //act4 = theDebuggerAction(WatchExpressionInWindow)->action(); - //act4->setCheckable(true); - //act4->setChecked(visual); + //QAction *act4 = theDebuggerAction(WatchExpressionInWindow); //menu.addAction(act4); - //act3 = new QAction(tr("Add to watch window..."), &menu); - //menu.addAction(act3); + QAction *act3 = new QAction(tr("Insert new watch item"), &menu); + menu.addAction(act3); menu.addSeparator(); menu.addAction(theDebuggerAction(RecheckDumpers)); @@ -196,10 +190,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) resizeColumnsToContents(); else if (act == act2) setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); - else if (act == act4) - model()->setData(mi0, !visual, VisualRole); - else if (act == act4) - model()->setData(mi0, !visual, VisualRole); + else if (act == act3) + theDebuggerAction(WatchExpression)->trigger("<Edit>"); } void WatchWindow::resizeColumnsToContents() diff --git a/src/plugins/fakevim/fakevimconstants.h b/src/plugins/fakevim/fakevimconstants.h index 7c21750451..c1b7ff1b15 100644 --- a/src/plugins/fakevim/fakevimconstants.h +++ b/src/plugins/fakevim/fakevimconstants.h @@ -44,6 +44,13 @@ const char * const ConfigSmartTab = "smarttab"; const char * const ConfigStartOfLine = "startofline"; const char * const ConfigTabStop = "tabstop"; +const char * const ConfigBackspace = "backspace"; +// indent allow backspacing over autoindent +// eol allow backspacing over line breaks (join lines) +// start allow backspacing over the start of insert; CTRL-W and CTRL-U +// stop once at the start of insert. + + } // namespace Constants } // namespace FakeVim diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 62f56238f4..36a6a3264e 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -250,6 +250,8 @@ private: int cursorColumnInDocument() const; int linesInDocument() const; void scrollToLineInDocument(int line); + void scrollUp(int count); + void scrollDown(int count) { scrollUp(-count); } // helper functions for indenting bool isElectricCharacter(QChar c) const @@ -378,9 +380,14 @@ public: // marks as lines QHash<int, int> m_marks; + QString m_oldNeedle; // vi style configuration QHash<QString, QString> m_config; + bool hasConfig(const char *name) const + { return m_config[name] == ConfigOn; } + bool hasConfig(const char *name, const char *value) const + { return m_config[name].contains(value); } // FIXME // for restoring cursor position int m_savedYankPosition; @@ -418,6 +425,8 @@ FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget) m_savedYankPosition = 0; m_cursorWidth = EDITOR(cursorWidth()); +#if 1 + // Plain m_config[ConfigStartOfLine] = ConfigOn; m_config[ConfigHlSearch] = ConfigOn; m_config[ConfigTabStop] = "8"; @@ -425,6 +434,18 @@ FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget) m_config[ConfigShiftWidth] = "8"; m_config[ConfigExpandTab] = ConfigOff; m_config[ConfigAutoIndent] = ConfigOff; + m_config[ConfigBackspace] = ""; +#else + // Qt Local + m_config[ConfigStartOfLine] = ConfigOn; + m_config[ConfigHlSearch] = ConfigOn; + m_config[ConfigTabStop] = "4"; + m_config[ConfigSmartTab] = ConfigOff; + m_config[ConfigShiftWidth] = "4"; + m_config[ConfigExpandTab] = ConfigOn; + m_config[ConfigAutoIndent] = ConfigOff; + m_config[ConfigBackspace] = "indent,eol,start"; +#endif } bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev) @@ -513,7 +534,7 @@ EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev) moveLeft(); EDITOR(setTextCursor(m_tc)); - EDITOR(ensureCursorVisible()); + //EDITOR(ensureCursorVisible()); return result; } @@ -838,21 +859,21 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, if (key == Key_Return || key == 't') { // cursor line to top of window if (!m_mvcount.isEmpty()) setPosition(firstPositionInLine(count())); - scrollToLineInDocument(cursorLineInDocument()); + scrollUp(- cursorLineOnScreen()); if (key == Key_Return) moveToFirstNonBlankOnLine(); finishMovement(); } else if (key == '.' || key == 'z') { // cursor line to center of window if (!m_mvcount.isEmpty()) setPosition(firstPositionInLine(count())); - scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2); + scrollUp(linesOnScreen() / 2 - cursorLineOnScreen()); if (key == '.') moveToFirstNonBlankOnLine(); finishMovement(); } else if (key == '-' || key == 'b') { // cursor line to bottom of window if (!m_mvcount.isEmpty()) setPosition(firstPositionInLine(count())); - scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() - 1); + scrollUp(linesOnScreen() - cursorLineOnScreen()); if (key == '-') moveToFirstNonBlankOnLine(); finishMovement(); @@ -1092,6 +1113,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, m_moveType = MoveInclusive; moveToWordBoundary(true, true); finishMovement(); + } else if (key == control('e')) { + // FIXME: this should use the "scroll" option, and "count" + if (cursorLineOnScreen() == 0) + moveDown(1); + scrollDown(1); + finishMovement(); } else if (key == 'f') { m_subsubmode = FtSubSubMode; m_moveType = MoveInclusive; @@ -1104,7 +1131,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, if (m_gflag) { m_gflag = false; m_tc.setPosition(firstPositionInLine(1), KeepAnchor); - if (m_config[ConfigStartOfLine] == ConfigOn) + if (hasConfig(ConfigStartOfLine)) moveToFirstNonBlankOnLine(); finishMovement(); } else { @@ -1113,7 +1140,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, } else if (key == 'G') { int n = m_mvcount.isEmpty() ? linesInDocument() : count(); m_tc.setPosition(firstPositionInLine(n), KeepAnchor); - if (m_config[ConfigStartOfLine] == ConfigOn) + if (hasConfig(ConfigStartOfLine)) moveToFirstNonBlankOnLine(); finishMovement(); } else if (key == 'h' || key == Key_Left @@ -1229,7 +1256,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, moveToEndOfLine(); recordInsertText("\n"); moveToStartOfLine(); - if (0 && m_config[ConfigAutoIndent] == ConfigOn) + if (0 && hasConfig(ConfigAutoIndent)) recordInsertText(QString(indentDist(), ' ')); else recordInsertText(QString(numSpaces, ' ')); @@ -1447,11 +1474,13 @@ EventResult FakeVimHandler::Private::handleInsertMode(int key, int, m_submode = NoSubMode; m_tc.insertBlock(); m_lastInsertion += "\n"; - if (0 && m_config[ConfigAutoIndent] == ConfigOn) + if (0 && hasConfig(ConfigAutoIndent)) indentRegion('\n'); } else if (key == Key_Backspace || key == control('h')) { - m_tc.deletePreviousChar(); - m_lastInsertion = m_lastInsertion.left(m_lastInsertion.size() - 1); + if (!m_lastInsertion.isEmpty() || hasConfig(ConfigBackspace, "start")) { + m_tc.deletePreviousChar(); + m_lastInsertion = m_lastInsertion.left(m_lastInsertion.size() - 1); + } } else if (key == Key_Delete) { m_tc.deleteChar(); m_lastInsertion.clear(); @@ -1461,7 +1490,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(int key, int, } else if (key == Key_PageUp || key == control('b')) { moveUp(count() * (linesOnScreen() - 2)); m_lastInsertion.clear(); - } else if (key == Key_Tab && m_config[ConfigExpandTab] == ConfigOn) { + } else if (key == Key_Tab && hasConfig(ConfigExpandTab)) { QString str = QString(m_config[ConfigTabStop].toInt(), ' '); m_lastInsertion.append(str); m_tc.insertText(str); @@ -1476,8 +1505,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(int key, int, m_tc.deleteChar(); } m_tc.insertText(text); - if (0 && m_config[ConfigAutoIndent] == ConfigOn - && isElectricCharacter(text.at(0))) { + if (0 && hasConfig(ConfigAutoIndent) && isElectricCharacter(text.at(0))) { const QString leftText = m_tc.block().text() .left(m_tc.position() - 1 - m_tc.block().position()); if (leftText.simplified().isEmpty()) @@ -1500,10 +1528,11 @@ EventResult FakeVimHandler::Private::handleMiniBufferModes(int key, int unmodifi enterCommandMode(); updateMiniBuffer(); } else if (key == Key_Backspace) { - if (m_commandBuffer.isEmpty()) + if (m_commandBuffer.isEmpty()) { enterCommandMode(); - else + } else { m_commandBuffer.chop(1); + } updateMiniBuffer(); } else if (key == Key_Left) { // FIXME: @@ -1837,51 +1866,56 @@ void FakeVimHandler::Private::search(const QString &needle0, bool forward) if (oldLine != cursorLineInDocument() - cursorLineOnScreen()) scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2); highlightMatches(needle); - return; - } - - m_tc.setPosition(forward ? 0 : lastPositionInDocument() - 1); - EDITOR(setTextCursor(m_tc)); - if (EDITOR(find(needle, flags))) { - m_tc = EDITOR(textCursor()); - m_tc.setPosition(m_tc.anchor()); - if (oldLine != cursorLineInDocument() - cursorLineOnScreen()) - scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2); - if (forward) - showRedMessage("search hit BOTTOM, continuing at TOP"); - else - showRedMessage("search hit TOP, continuing at BOTTOM"); - highlightMatches(needle); - return; + } else { + m_tc.setPosition(forward ? 0 : lastPositionInDocument() - 1); + EDITOR(setTextCursor(m_tc)); + if (EDITOR(find(needle, flags))) { + m_tc = EDITOR(textCursor()); + m_tc.setPosition(m_tc.anchor()); + if (oldLine != cursorLineInDocument() - cursorLineOnScreen()) + scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2); + if (forward) + showRedMessage("search hit BOTTOM, continuing at TOP"); + else + showRedMessage("search hit TOP, continuing at BOTTOM"); + highlightMatches(needle); + } else { + m_tc = orig; + showRedMessage("E486: Pattern not found: " + needle); + highlightMatches(QString()); + } } - - m_tc = orig; } void FakeVimHandler::Private::highlightMatches(const QString &needle0) { - if (m_config[ConfigHlSearch] == ConfigOff) + if (!hasConfig(ConfigHlSearch)) + return; + if (needle0 == m_oldNeedle) return; + m_oldNeedle = needle0; + m_searchSelections.clear(); - QTextCursor tc = m_tc; - tc.movePosition(QTextCursor::Start, MoveAnchor); + if (!needle0.isEmpty()) { + QTextCursor tc = m_tc; + tc.movePosition(QTextCursor::Start, MoveAnchor); - QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively; - QString needle = needle0; - vimPatternToQtPattern(&needle, &flags); + QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively; + QString needle = needle0; + vimPatternToQtPattern(&needle, &flags); - m_searchSelections.clear(); - EDITOR(setTextCursor(tc)); - while (EDITOR(find(needle, flags))) { - tc = EDITOR(textCursor()); - QTextEdit::ExtraSelection sel; - sel.cursor = tc; - sel.format = tc.blockCharFormat(); - sel.format.setBackground(QColor(177, 177, 0)); - m_searchSelections.append(sel); - tc.movePosition(Right, MoveAnchor); EDITOR(setTextCursor(tc)); + while (EDITOR(find(needle, flags))) { + tc = EDITOR(textCursor()); + QTextEdit::ExtraSelection sel; + sel.cursor = tc; + sel.format = tc.blockCharFormat(); + sel.format.setBackground(QColor(177, 177, 0)); + m_searchSelections.append(sel); + tc.movePosition(Right, MoveAnchor); + EDITOR(setTextCursor(tc)); + } } updateSelection(); } @@ -2134,9 +2168,15 @@ void FakeVimHandler::Private::scrollToLineInDocument(int line) { // FIXME: works only for QPlainTextEdit QScrollBar *scrollBar = EDITOR(verticalScrollBar()); + //qDebug() << "SCROLL: " << scrollBar->value() << line; scrollBar->setValue(line); } +void FakeVimHandler::Private::scrollUp(int count) +{ + scrollToLineInDocument(cursorLineInDocument() - cursorLineOnScreen() - count); +} + int FakeVimHandler::Private::lastPositionInDocument() const { QTextBlock block = m_tc.block().document()->lastBlock(); diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index 9070dabd5c..c0f497c062 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -31,6 +31,7 @@ #include "genericprojectconstants.h" #include "genericmakestep.h" +#include <projectexplorer/toolchain.h> #include <projectexplorer/projectexplorerconstants.h> #include <cpptools/cppmodelmanagerinterface.h> #include <extensionsystem/pluginmanager.h> @@ -112,12 +113,12 @@ GenericProject::GenericProject(Manager *manager, const QString &fileName) m_toolChain(0) { QFileInfo fileInfo(m_fileName); - const QString projectBaseName = fileInfo.baseName(); QDir dir = fileInfo.dir(); - m_filesFileName = QFileInfo(dir, projectBaseName + QLatin1String(".files")).absoluteFilePath(); - m_includesFileName = QFileInfo(dir, projectBaseName + QLatin1String(".includes")).absoluteFilePath(); - m_configFileName = QFileInfo(dir, projectBaseName + QLatin1String(".config")).absoluteFilePath(); + m_projectName = fileInfo.baseName(); + m_filesFileName = QFileInfo(dir, m_projectName + QLatin1String(".files")).absoluteFilePath(); + m_includesFileName = QFileInfo(dir, m_projectName + QLatin1String(".includes")).absoluteFilePath(); + m_configFileName = QFileInfo(dir, m_projectName + QLatin1String(".config")).absoluteFilePath(); m_file = new GenericProjectFile(this, fileName); m_rootNode = new GenericProjectNode(this, m_file); @@ -142,7 +143,7 @@ QString GenericProject::includesFileName() const QString GenericProject::configFileName() const { return m_configFileName; } -QStringList GenericProject::readLines(const QString &absoluteFileName) const +static QStringList readLines(const QString &absoluteFileName) { QStringList lines; @@ -167,34 +168,41 @@ QStringList GenericProject::readLines(const QString &absoluteFileName) const } -void GenericProject::parseProject() +void GenericProject::parseProject(RefreshOptions options) { - const QFileInfo projectFileInfo(m_fileName); - - QSettings projectInfo(m_fileName, QSettings::IniFormat); - - m_files = convertToAbsoluteFiles(readLines(filesFileName())); - m_files.removeDuplicates(); + if (options & Files) { + m_files = convertToAbsoluteFiles(readLines(filesFileName())); + m_files.removeDuplicates(); + } - m_projectIncludePaths = readLines(includesFileName()); - m_projectIncludePaths.removeDuplicates(); + if (options & Configuration) { + m_projectIncludePaths = readLines(includesFileName()); + m_projectIncludePaths.removeDuplicates(); - m_generated = convertToAbsoluteFiles(projectInfo.value(QLatin1String("generated")).toStringList()); + QSettings projectInfo(m_fileName, QSettings::IniFormat); + m_generated = convertToAbsoluteFiles(projectInfo.value(QLatin1String("generated")).toStringList()); - m_defines.clear(); + m_defines.clear(); - QFile configFile(configFileName()); - if (configFile.open(QFile::ReadOnly)) - m_defines = configFile.readAll(); + QFile configFile(configFileName()); + if (configFile.open(QFile::ReadOnly)) + m_defines = configFile.readAll(); + } - emit fileListChanged(); + if (options & Files) + emit fileListChanged(); } -void GenericProject::refresh() +void GenericProject::refresh(RefreshOptions options) { - parseProject(); + QSet<QString> oldFileList; + if (!(options & Configuration)) + oldFileList = m_files.toSet(); - m_rootNode->refresh(); + parseProject(options); + + if (options & Files) + m_rootNode->refresh(); CppTools::CppModelManagerInterface *modelManager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>(); @@ -213,7 +221,6 @@ void GenericProject::refresh() foreach (const ProjectExplorer::HeaderPath &headerPath, m_toolChain->systemHeaderPaths()) { if (headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath) allFrameworkPaths.append(headerPath.path()); - else allIncludePaths.append(headerPath.path()); } @@ -227,8 +234,17 @@ void GenericProject::refresh() pinfo.sourceFiles = files(); pinfo.sourceFiles += generated(); - QStringList filesToUpdate = pinfo.sourceFiles; - filesToUpdate.append(QLatin1String("<configuration>")); // XXX don't hardcode configuration file name + QStringList filesToUpdate; + + if (options & Configuration) { + filesToUpdate = pinfo.sourceFiles; + filesToUpdate.append(QLatin1String("<configuration>")); // XXX don't hardcode configuration file name + } else if (options & Files) { + // Only update files that got added to the list + QSet<QString> newFileList = m_files.toSet(); + newFileList.subtract(oldFileList); + filesToUpdate.append(newFileList.toList()); + } modelManager->updateProjectInfo(pinfo); modelManager->updateSourceFiles(filesToUpdate); @@ -449,8 +465,7 @@ void GenericProject::restoreSettingsImpl(ProjectExplorer::PersistentSettingsRead setIncludePaths(allIncludePaths()); - parseProject(); - refresh(); + refresh(Everything); } void GenericProject::saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer) diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h index a5f4abb7fb..25877c40ac 100644 --- a/src/plugins/genericprojectmanager/genericproject.h +++ b/src/plugins/genericprojectmanager/genericproject.h @@ -36,7 +36,6 @@ #include <projectexplorer/project.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/buildstep.h> -#include <projectexplorer/toolchain.h> #include <coreplugin/ifile.h> QT_BEGIN_NAMESPACE @@ -95,6 +94,14 @@ public: QString buildParser(const QString &buildConfiguration) const; ProjectExplorer::ToolChain *toolChain() const; + enum RefreshOptions { + Files = 0x01, + Configuration = 0x02, + Everything = Files | Configuration + }; + + void refresh(RefreshOptions options); + QStringList includePaths() const; void setIncludePaths(const QStringList &includePaths); @@ -107,18 +114,15 @@ public: public Q_SLOTS: void setToolChainId(const QString &toolChainId); - void refresh(); protected: virtual void saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer); virtual void restoreSettingsImpl(ProjectExplorer::PersistentSettingsReader &reader); private: - void parseProject(); + void parseProject(RefreshOptions options); QStringList convertToAbsoluteFiles(const QStringList &paths) const; - QStringList readLines(const QString &absoluteFileName) const; - Manager *m_manager; QString m_fileName; QString m_filesFileName; diff --git a/src/plugins/genericprojectmanager/genericprojectmanager.cpp b/src/plugins/genericprojectmanager/genericprojectmanager.cpp index 5495173f8d..58f19648a3 100644 --- a/src/plugins/genericprojectmanager/genericprojectmanager.cpp +++ b/src/plugins/genericprojectmanager/genericprojectmanager.cpp @@ -79,10 +79,12 @@ void Manager::unregisterProject(GenericProject *project) void Manager::notifyChanged(const QString &fileName) { foreach (GenericProject *project, m_projects) { - if (fileName == project->filesFileName() || - fileName == project->includesFileName() || - fileName == project->configFileName()) - project->refresh(); + if (fileName == project->filesFileName()) { + project->refresh(GenericProject::Files); + } + else if (fileName == project->includesFileName() || + fileName == project->configFileName()) { + project->refresh(GenericProject::Configuration); + } } } - diff --git a/src/plugins/genericprojectmanager/genericprojectnodes.cpp b/src/plugins/genericprojectmanager/genericprojectnodes.cpp index b6bde51ca4..016ada7f47 100644 --- a/src/plugins/genericprojectmanager/genericprojectnodes.cpp +++ b/src/plugins/genericprojectmanager/genericprojectnodes.cpp @@ -30,12 +30,10 @@ #include "genericprojectnodes.h" #include "genericproject.h" +#include <coreplugin/ifile.h> #include <projectexplorer/projectexplorer.h> -#include <QDir> #include <QFileInfo> -#include <QSettings> -#include <QtDebug> using namespace GenericProjectManager; using namespace GenericProjectManager::Internal; @@ -44,7 +42,9 @@ GenericProjectNode::GenericProjectNode(GenericProject *project, Core::IFile *pro : ProjectExplorer::ProjectNode(QFileInfo(projectFile->fileName()).absolutePath()), m_project(project), m_projectFile(projectFile) -{} +{ + setFolderName(QFileInfo(projectFile->fileName()).baseName()); +} GenericProjectNode::~GenericProjectNode() { } diff --git a/src/plugins/genericprojectmanager/genericprojectnodes.h b/src/plugins/genericprojectmanager/genericprojectnodes.h index 4b647a860e..326e0ee3ce 100644 --- a/src/plugins/genericprojectmanager/genericprojectnodes.h +++ b/src/plugins/genericprojectmanager/genericprojectnodes.h @@ -30,11 +30,15 @@ #ifndef GENERICPROJECTNODE_H #define GENERICPROJECTNODE_H -#include <coreplugin/ifile.h> #include <projectexplorer/projectnodes.h> + #include <QStringList> #include <QHash> +namespace Core { +class IFile; +} + namespace GenericProjectManager { namespace Internal { diff --git a/src/plugins/genericprojectmanager/genericprojectwizard.cpp b/src/plugins/genericprojectmanager/genericprojectwizard.cpp index 289cbfd343..7c3185e148 100644 --- a/src/plugins/genericprojectmanager/genericprojectwizard.cpp +++ b/src/plugins/genericprojectmanager/genericprojectwizard.cpp @@ -4,16 +4,17 @@ #include <coreplugin/mimedatabase.h> #include <projectexplorer/projectexplorer.h> +#include <utils/filenamevalidatinglineedit.h> +#include <utils/filewizardpage.h> #include <utils/pathchooser.h> #include <QtCore/QDir> #include <QtCore/QtDebug> -#include <QtGui/QWizard> +#include <QtGui/QDirModel> #include <QtGui/QFormLayout> #include <QtGui/QListView> #include <QtGui/QTreeView> -#include <QtGui/QDirModel> using namespace GenericProjectManager::Internal; using namespace Core::Utils; @@ -94,14 +95,12 @@ GenericProjectWizardDialog::GenericProjectWizardDialog(QWidget *parent) setWindowTitle(tr("Import Existing Project")); // first page - QWizardPage *firstPage = new QWizardPage; - firstPage->setTitle(tr("Project")); - - QFormLayout *layout = new QFormLayout(firstPage); - m_pathChooser = new PathChooser; - layout->addRow(tr("Source Directory:"), m_pathChooser); + m_firstPage = new FileWizardPage; + m_firstPage->setTitle(tr("Import Project")); + m_firstPage->setNameLabel(tr("Project name:")); + m_firstPage->setPathLabel(tr("Location:")); - m_firstPageId = addPage(firstPage); + addPage(m_firstPage); #if 0 // second page @@ -146,7 +145,14 @@ GenericProjectWizardDialog::~GenericProjectWizardDialog() { } QString GenericProjectWizardDialog::path() const -{ return m_pathChooser->path(); } +{ + return m_firstPage->path(); +} + +QString GenericProjectWizardDialog::projectName() const +{ + return m_firstPage->name(); +} void GenericProjectWizardDialog::updateFilesView(const QModelIndex ¤t, const QModelIndex &) @@ -182,12 +188,11 @@ bool GenericProjectWizardDialog::validateCurrentPage() { using namespace Core::Utils; - if (currentId() == m_firstPageId) { - return ! m_pathChooser->path().isEmpty(); - - } else if (currentId() == m_secondPageId) { +#if 0 + if (currentId() == m_secondPageId) { return true; } +#endif return QWizard::validateCurrentPage(); } @@ -271,7 +276,7 @@ Core::GeneratedFiles GenericProjectWizard::generateFiles(const QWizard *w, const GenericProjectWizardDialog *wizard = qobject_cast<const GenericProjectWizardDialog *>(w); const QString projectPath = wizard->path(); const QDir dir(projectPath); - const QString projectName = QFileInfo(projectPath).baseName(); + const QString projectName = wizard->projectName(); const QString creatorFileName = QFileInfo(dir, projectName + QLatin1String(".creator")).absoluteFilePath(); const QString filesFileName = QFileInfo(dir, projectName + QLatin1String(".files")).absoluteFilePath(); const QString includesFileName = QFileInfo(dir, projectName + QLatin1String(".includes")).absoluteFilePath(); diff --git a/src/plugins/genericprojectmanager/genericprojectwizard.h b/src/plugins/genericprojectmanager/genericprojectwizard.h index c268ce5f2a..73cf61e82c 100644 --- a/src/plugins/genericprojectmanager/genericprojectwizard.h +++ b/src/plugins/genericprojectmanager/genericprojectwizard.h @@ -2,23 +2,31 @@ #define GENERICPROJECTWIZARD_H #include <coreplugin/basefilewizard.h> -#include <utils/pathchooser.h> -#include <QWizard> + +#include <QtGui/QWizard> QT_BEGIN_NAMESPACE -class QListView; -class QTreeView; +class QDir; class QDirModel; +class QFileInfo; +class QListView; class QModelIndex; -class QDir; class QStringList; -class QFileInfo; +class QTreeView; QT_END_NAMESPACE +namespace Core { +namespace Utils { + +class FileWizardPage; + +} // namespace Utils +} // namespace Core + namespace GenericProjectManager { namespace Internal { -class GenericProjectWizardDialog: public QWizard +class GenericProjectWizardDialog : public QWizard { Q_OBJECT @@ -27,6 +35,7 @@ public: virtual ~GenericProjectWizardDialog(); QString path() const; + QString projectName() const; private Q_SLOTS: void updateFilesView(const QModelIndex ¤t, @@ -37,10 +46,9 @@ protected: virtual bool validateCurrentPage(); private: - int m_firstPageId; int m_secondPageId; - Core::Utils::PathChooser *m_pathChooser; + Core::Utils::FileWizardPage *m_firstPage; QTreeView *m_dirView; QDirModel *m_dirModel; diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 29af542988..2c04340401 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -142,7 +142,8 @@ GitPlugin::GitPlugin() : m_coreListener(0), m_submitEditorFactory(0), m_versionControl(0), - m_changeTmpFile(0) + m_changeTmpFile(0), + m_submitActionTriggered(false) { m_instance = this; } @@ -651,6 +652,7 @@ Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const Commit void GitPlugin::submitCurrentLog() { // Close the submit editor + m_submitActionTriggered = true; QList<Core::IEditor*> editors; editors.push_back(m_core->editorManager()->currentEditor()); m_core->editorManager()->closeEditors(editors); @@ -672,13 +674,18 @@ bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor) // Paranoia! if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) return true; - // Prompt user. - const QMessageBox::StandardButton answer = QMessageBox::question(m_core->mainWindow(), tr("Closing git editor"), tr("Do you want to commit the change?"), - QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + // Prompt user. Force a prompt unless submit was actually invoked (that + // is, the editor was closed or shutdown). + const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer = + editor->promptSubmit(tr("Closing git editor"), + tr("Do you want to commit the change?"), + tr("The commit message check failed. Do you want to commit the change?"), + !m_submitActionTriggered); + m_submitActionTriggered = false; switch (answer) { - case QMessageBox::Cancel: + case VCSBase::VCSBaseSubmitEditor::SubmitCanceled: return false; // Keep editing and change file - case QMessageBox::No: + case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded: cleanChangeTmpFile(); return true; // Cancel all default: diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 6337a60d96..8f7f0bfeed 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -171,6 +171,7 @@ private: QString m_submitRepository; QStringList m_submitOrigCommitFiles; QTemporaryFile *m_changeTmpFile; + bool m_submitActionTriggered; }; } // namespace Git diff --git a/src/plugins/git/gitsubmitpanel.ui b/src/plugins/git/gitsubmitpanel.ui index aba46b7c52..7dfa9b3ba0 100644 --- a/src/plugins/git/gitsubmitpanel.ui +++ b/src/plugins/git/gitsubmitpanel.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>201</width> - <height>189</height> + <width>374</width> + <height>229</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> @@ -59,26 +59,43 @@ <property name="title"> <string>Commit Information</string> </property> - <layout class="QFormLayout" name="editFormLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="authorLabel"> - <property name="text"> - <string>Author:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="authorLineEdit"/> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="authorLabel"> + <property name="text"> + <string>Author:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="authorLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="emailLabel"> + <property name="text"> + <string>Email:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="emailLineEdit"/> + </item> + </layout> </item> - <item row="1" column="0"> - <widget class="QLabel" name="emailLabel"> - <property name="text"> - <string>Email:</string> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="emailLineEdit"/> + <property name="sizeHint" stdset="0"> + <size> + <width>161</width> + <height>20</height> + </size> + </property> + </spacer> </item> </layout> </widget> diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 65efbc977f..25ea4524a6 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -175,13 +175,18 @@ PerforcePlugin::PerforcePlugin() : m_diffProjectAction(0), m_diffAllAction(0), m_resolveAction(0), - m_submitAction(0), + m_submitAction(0), m_pendingAction(0), m_describeAction(0), m_annotateCurrentAction(0), m_annotateAction(0), m_filelogCurrentAction(0), m_filelogAction(0), + m_submitCurrentLogAction(0), + m_submitActionTriggered(false), + m_diffSelectedFiles(0), + m_undoAction(0), + m_redoAction(0), m_changeTmpFile(0), #ifdef USE_P4_API m_workbenchClientUser(0), @@ -949,6 +954,7 @@ void PerforcePlugin::describe(const QString & source, const QString &n) void PerforcePlugin::submitCurrentLog() { + m_submitActionTriggered = true; Core::EditorManager *em = Core::EditorManager::instance(); em->closeEditors(QList<Core::IEditor*>() << em->currentEditor()); } @@ -961,21 +967,28 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor) Core::IFile *fileIFace = editor->file(); if (!fileIFace) return true; + const PerforceSubmitEditor *perforceEditor = qobject_cast<PerforceSubmitEditor *>(editor); + if (!perforceEditor) + return true; QFileInfo editorFile(fileIFace->fileName()); QFileInfo changeFile(m_changeTmpFile->fileName()); if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) { - const QMessageBox::StandardButton answer = - QMessageBox::question(core->mainWindow(), - tr("Closing p4 Editor"), - tr("Do you want to submit this change list?"), - QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); - if (answer == QMessageBox::Cancel) + // Prompt the user. Force a prompt unless submit was actually invoked (that + // is, the editor was closed or shutdown). + const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer = + perforceEditor->promptSubmit(tr("Closing p4 Editor"), + tr("Do you want to submit this change list?"), + tr("The commit message check failed. Do you want to submit this change list"), + !m_submitActionTriggered); + m_submitActionTriggered = false; + + if (answer == VCSBase::VCSBaseSubmitEditor::SubmitCanceled) return false; core->fileManager()->blockFileChange(fileIFace); fileIFace->save(); core->fileManager()->unblockFileChange(fileIFace); - if (answer == QMessageBox::Yes) { + if (answer == VCSBase::VCSBaseSubmitEditor::SubmitConfirmed) { QByteArray change = m_changeTmpFile->readAll(); m_changeTmpFile->close(); if (!checkP4Command()) { @@ -1007,10 +1020,10 @@ bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor) m_changeTmpFile = 0; return false; } - QString output = QString::fromUtf8(proc.readAll()); + const QString output = QString::fromUtf8(proc.readAll()); showOutput(output); - if (output.contains("Out of date files must be resolved or reverted"), true) { - QMessageBox::warning(editor->widget(), "Pending change", "Could not submit the change, because your workspace was out of date. Created a pending submit instead."); + if (output.contains(QLatin1String("Out of date files must be resolved or reverted)"))) { + QMessageBox::warning(editor->widget(), tr("Pending change"), tr("Could not submit the change, because your workspace was out of date. Created a pending submit instead.")); } QApplication::restoreOverrideCursor(); } diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h index 919ab0c6d0..8ac7188994 100644 --- a/src/plugins/perforce/perforceplugin.h +++ b/src/plugins/perforce/perforceplugin.h @@ -190,6 +190,7 @@ private: QAction *m_filelogCurrentAction; QAction *m_filelogAction; QAction *m_submitCurrentLogAction; + bool m_submitActionTriggered; QAction *m_diffSelectedFiles; QAction *m_undoAction; @@ -232,7 +233,7 @@ private: PerforceVersionControl *m_versionControl; PerforceSettings m_settings; - friend class PerforceOutputWindow; + friend class PerforceOutputWindow; // needs openFiles() }; } // namespace Perforce diff --git a/src/plugins/projectexplorer/environment.cpp b/src/plugins/projectexplorer/environment.cpp index 0c2e74f67e..541445cf99 100644 --- a/src/plugins/projectexplorer/environment.cpp +++ b/src/plugins/projectexplorer/environment.cpp @@ -288,6 +288,16 @@ void Environment::modify(const QList<EnvironmentItem> & list) *this = resultEnvironment; } +bool Environment::operator!=(const Environment &other) +{ + return !(*this == other); +} + +bool Environment::operator==(const Environment &other) +{ + return m_values == other.m_values; +} + QStringList Environment::parseCombinedArgString(const QString &program) { QStringList args; @@ -341,3 +351,5 @@ QString Environment::joinArgumentList(const QStringList &arguments) return result; } + + diff --git a/src/plugins/projectexplorer/environment.h b/src/plugins/projectexplorer/environment.h index 91cd958d4b..367aee5a80 100644 --- a/src/plugins/projectexplorer/environment.h +++ b/src/plugins/projectexplorer/environment.h @@ -89,6 +89,8 @@ public: static QStringList parseCombinedArgString(const QString &program); static QString joinArgumentList(const QStringList &arguments); + bool operator!=(const Environment &other); + bool operator==(const Environment &other); private: QMap<QString, QString> m_values; }; diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 7c080aad8e..e827a5a3d4 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -246,13 +246,13 @@ QList<HeaderPath> MSVCToolChain::systemHeaderPaths() void MSVCToolChain::addToEnvironment(ProjectExplorer::Environment &env) { - if (!m_valuesSet) { + if (!m_valuesSet || env != m_lastEnvironment) { + m_lastEnvironment = env; QSettings registry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", QSettings::NativeFormat); if (m_name.isEmpty()) return; QString path = registry.value(m_name).toString(); - ProjectExplorer::Environment oldEnv(env); QString desc; QString varsbat = path + "Common7\\Tools\\vsvars32.bat"; if (QFileInfo(varsbat).exists()) { @@ -265,7 +265,8 @@ void MSVCToolChain::addToEnvironment(ProjectExplorer::Environment &env) tf.flush(); tf.waitForBytesWritten(30000); - QProcess run; // TODO run in the environment we want to add to... + QProcess run; + run.setEnvironment(env.toStringList()); QString cmdPath = env.searchInPath("cmd"); run.start(cmdPath, QStringList()<<"/c"<<filename); run.waitForFinished(); @@ -281,9 +282,7 @@ void MSVCToolChain::addToEnvironment(ProjectExplorer::Environment &env) if (regexp.exactMatch(line2)) { QString variable = regexp.cap(1); QString value = regexp.cap(2); - value.replace('%' + variable + '%', oldEnv.value(variable)); m_values.append(QPair<QString, QString>(variable, value)); - } } vars.close(); @@ -293,14 +292,11 @@ void MSVCToolChain::addToEnvironment(ProjectExplorer::Environment &env) m_valuesSet = true; } - //qDebug()<<"MSVC Environment:"; QList< QPair<QString, QString> >::const_iterator it, end; end = m_values.constEnd(); for (it = m_values.constBegin(); it != end; ++it) { env.set((*it).first, (*it).second); - //qDebug()<<"variable:"<<(*it).first<<"value:"<<(*it).second; } - } QString MSVCToolChain::makeCommand() const diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index a977c125bf..813092d7bc 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -119,6 +119,7 @@ protected: private: mutable QList<QPair<QString, QString> > m_values; mutable bool m_valuesSet; + mutable ProjectExplorer::Environment m_lastEnvironment; }; // TODO some stuff needs to be moved into here diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h index 7e663fc34b..e94b08e67a 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.h +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -98,7 +98,8 @@ class Qt4PriFileNode; class Qt4ProFileNode; // Implements ProjectNode for qt4 pro files -class Qt4PriFileNode : public ProjectExplorer::ProjectNode { +class Qt4PriFileNode : public ProjectExplorer::ProjectNode +{ Q_OBJECT Q_DISABLE_COPY(Qt4PriFileNode) public: @@ -164,7 +165,8 @@ private: }; // Implements ProjectNode for qt4 pro files -class Qt4ProFileNode : public Qt4PriFileNode { +class Qt4ProFileNode : public Qt4PriFileNode +{ Q_OBJECT Q_DISABLE_COPY(Qt4ProFileNode) public: @@ -208,7 +210,8 @@ private: friend class Qt4NodeHierarchy; }; -class Qt4NodesWatcher : public ProjectExplorer::NodesWatcher { +class Qt4NodesWatcher : public ProjectExplorer::NodesWatcher +{ Q_OBJECT Q_DISABLE_COPY(Qt4NodesWatcher) public: diff --git a/src/plugins/qt4projectmanager/qt4project.h b/src/plugins/qt4projectmanager/qt4project.h index df23a18ba3..3351d14813 100644 --- a/src/plugins/qt4projectmanager/qt4project.h +++ b/src/plugins/qt4projectmanager/qt4project.h @@ -78,8 +78,7 @@ class Qt4Manager; class Qt4Project; class Qt4RunStep; -class Qt4ProjectFile - : public Core::IFile +class Qt4ProjectFile : public Core::IFile { Q_OBJECT @@ -108,8 +107,7 @@ private: QString m_filePath; }; -class Qt4Project - : public ProjectExplorer::Project +class Qt4Project : public ProjectExplorer::Project { Q_OBJECT diff --git a/src/plugins/qt4projectmanager/qt4runconfiguration.cpp b/src/plugins/qt4projectmanager/qt4runconfiguration.cpp index 09e1827c7a..f77bc917c4 100644 --- a/src/plugins/qt4projectmanager/qt4runconfiguration.cpp +++ b/src/plugins/qt4projectmanager/qt4runconfiguration.cpp @@ -344,11 +344,8 @@ void Qt4RunConfiguration::updateTarget() #if defined (Q_OS_MAC) if (reader->values("CONFIG").contains("app_bundle")) { - QString qmakeBuildConfig = "release"; - if (projectBuildConfiguration & QtVersion::DebugBuild) - qmakeBuildConfig = "debug"; m_workingDir += QLatin1Char('/') - + qmakeBuildConfig + + reader->value("TARGET") + QLatin1String(".app/Contents/MacOS"); } #endif diff --git a/src/plugins/qt4projectmanager/qtversionmanager.cpp b/src/plugins/qt4projectmanager/qtversionmanager.cpp index 013e7b1ca7..0c968b8485 100644 --- a/src/plugins/qt4projectmanager/qtversionmanager.cpp +++ b/src/plugins/qt4projectmanager/qtversionmanager.cpp @@ -204,7 +204,7 @@ void QtVersionManager::writeVersionsIntoSettings() { QSettings *s = Core::ICore::instance()->settings(); s->setValue(defaultQtVersionKey, m_defaultVersion); - s->beginWriteArray("QtVersions"); + s->beginWriteArray(QtVersionsSectionName); for (int i = 0; i < m_versions.size(); ++i) { s->setArrayIndex(i); s->setValue("Name", m_versions.at(i)->name()); @@ -521,8 +521,8 @@ void QtDirWidget::showEnvironmentPage(QTreeWidgetItem *item) m_ui.msvcLabel->setText(""); } else { m_ui.msvcComboBox->setVisible(true); - m_ui.msvcComboBox->clear(); bool block = m_ui.msvcComboBox->blockSignals(true); + m_ui.msvcComboBox->clear(); foreach(const QString &msvcenv, msvcEnvironments) { m_ui.msvcComboBox->addItem(msvcenv); if (msvcenv == m_versions.at(index)->msvcVersion()) { @@ -1235,7 +1235,6 @@ void QtVersion::addToEnvironment(Environment &env) // add libdir, includedir and bindir // or add Mingw dirs // or do nothing on other - } int QtVersion::uniqueId() const diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index 67e56eacf6..064bb08693 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -188,7 +188,8 @@ SubversionPlugin::SubversionPlugin() : m_submitCurrentLogAction(0), m_submitDiffAction(0), m_submitUndoAction(0), - m_submitRedoAction(0) + m_submitRedoAction(0), + m_submitActionTriggered(false) { } @@ -463,15 +464,18 @@ bool SubversionPlugin::editorAboutToClose(Core::IEditor *iEditor) if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) return true; // Oops?! - // Prompt user. - const QMessageBox::StandardButton answer = QMessageBox::question( - Core::ICore::instance()->mainWindow(), tr("Closing Subversion Editor"), - tr("Do you want to commit the change?"), - QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + // Prompt user. Force a prompt unless submit was actually invoked (that + // is, the editor was closed or shutdown). + const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer = + editor->promptSubmit(tr("Closing Subversion Editor"), + tr("Do you want to commit the change?"), + tr("The commit message check failed. Do you want to commit the change?"), + !m_submitActionTriggered); + m_submitActionTriggered = false; switch (answer) { - case QMessageBox::Cancel: + case VCSBase::VCSBaseSubmitEditor::SubmitCanceled: return false; // Keep editing and change file - case QMessageBox::No: + case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded: cleanChangeTmpFile(); return true; // Cancel all default: @@ -922,6 +926,7 @@ void SubversionPlugin::slotDescribe() void SubversionPlugin::submitCurrentLog() { + m_submitActionTriggered = true; Core::EditorManager::instance()->closeEditors(QList<Core::IEditor*>() << Core::EditorManager::instance()->currentEditor()); } diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h index a9dbd5ca65..1611db8f2b 100644 --- a/src/plugins/subversion/subversionplugin.h +++ b/src/plugins/subversion/subversionplugin.h @@ -168,6 +168,7 @@ private: QAction *m_submitDiffAction; QAction *m_submitUndoAction; QAction *m_submitRedoAction; + bool m_submitActionTriggered; static const char * const SUBVERSION_MENU; static const char * const ADD; diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index e5fb77e259..6574d41631 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -128,12 +128,11 @@ ITextEditor *BaseTextEditor::openEditorAt(const QString &fileName, const QString &editorKind) { Core::EditorManager *editorManager = Core::EditorManager::instance(); - editorManager->addCurrentPositionToNavigationHistory(true); + editorManager->addCurrentPositionToNavigationHistory(); Core::IEditor *editor = editorManager->openEditor(fileName, editorKind, Core::EditorManager::IgnoreNavigationHistory); TextEditor::ITextEditor *texteditor = qobject_cast<TextEditor::ITextEditor *>(editor); if (texteditor) { texteditor->gotoLine(line, column); - editorManager->addCurrentPositionToNavigationHistory(); return texteditor; } return 0; @@ -749,7 +748,6 @@ void BaseTextEditor::cleanWhitespace() void BaseTextEditor::keyPressEvent(QKeyEvent *e) { - d->clearVisibleCollapsedBlock(); QKeyEvent *original_e = e; @@ -763,8 +761,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) return; } - d->m_contentsChanged = false; - bool ro = isReadOnly(); if (d->m_inBlockSelectionMode) { @@ -965,6 +961,7 @@ void BaseTextEditor::setTextCursor(const QTextCursor &cursor) void BaseTextEditor::gotoLine(int line, int column) { + d->m_lastCursorChangeWasInteresting = false; // avoid adding the previous position to history const int blockNumber = line - 1; const QTextBlock &block = document()->findBlockByNumber(blockNumber); if (block.isValid()) { @@ -981,6 +978,7 @@ void BaseTextEditor::gotoLine(int line, int column) setTextCursor(cursor); centerCursor(); } + saveCurrentCursorPositionForNavigation(); } int BaseTextEditor::position(ITextEditor::PositionOperation posOp, int at) const @@ -1033,6 +1031,7 @@ QChar BaseTextEditor::characterAt(int pos) const bool BaseTextEditor::event(QEvent *e) { + d->m_contentsChanged = false; switch (e->type()) { case QEvent::ShortcutOverride: e->ignore(); // we are a really nice citizen @@ -1116,9 +1115,11 @@ bool BaseTextEditor::restoreState(const QByteArray &state) stream >> hval; stream >> lval; stream >> cval; + d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history gotoLine(lval, cval); verticalScrollBar()->setValue(vval); horizontalScrollBar()->setValue(hval); + saveCurrentCursorPositionForNavigation(); return true; } @@ -1250,6 +1251,7 @@ int BaseTextEditor::visibleWrapColumn() const BaseTextEditorPrivate::BaseTextEditorPrivate() : m_contentsChanged(false), + m_lastCursorChangeWasInteresting(false), m_document(new BaseTextDocument()), m_parenthesesMatchingEnabled(false), m_extraArea(0), @@ -1435,6 +1437,7 @@ QRectF TextEditDocumentLayout::blockBoundingRect(const QTextBlock &block) const bool BaseTextEditor::viewportEvent(QEvent *event) { + d->m_contentsChanged = false; if (event->type() == QEvent::ContextMenu) { const QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event); if (ce->reason() == QContextMenuEvent::Mouse && !textCursor().hasSelection()) @@ -2321,8 +2324,20 @@ void BaseTextEditor::slotUpdateRequest(const QRect &r, int dy) slotUpdateExtraAreaWidth(); } +void BaseTextEditor::saveCurrentCursorPositionForNavigation() +{ + d->m_lastCursorChangeWasInteresting = true; + d->m_tempState = saveState(); +} + void BaseTextEditor::slotCursorPositionChanged() { + if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) { + Core::EditorManager::instance()->addCurrentPositionToNavigationHistory(d->m_tempState); + d->m_lastCursorChangeWasInteresting = false; + } else if (d->m_contentsChanged) { + saveCurrentCursorPositionForNavigation(); + } QList<QTextEdit::ExtraSelection> extraSelections; setExtraSelections(ParenthesesMatchingSelection, extraSelections); // clear if (d->m_parenthesesMatchingEnabled) diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 1813f4e4a9..8c10618134 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -454,6 +454,7 @@ private: void handleHomeKey(bool anchor); void handleBackspaceKey(); void moveLineUpDown(bool up); + void saveCurrentCursorPositionForNavigation(); void toggleBlockVisible(const QTextBlock &block); QRect collapseBox(const QTextBlock &block); @@ -498,7 +499,7 @@ public: int currentLine() const; int currentColumn() const; - inline void gotoLine(int line, int column = 0) { e->gotoLine(line, column); } + void gotoLine(int line, int column = 0) { e->gotoLine(line, column); } inline int position( ITextEditor::PositionOperation posOp = ITextEditor::Current diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index 9609d3c6ea..638f2faba2 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -140,6 +140,7 @@ public: BaseTextEditor *q; bool m_contentsChanged; + bool m_lastCursorChangeWasInteresting; QList<QTextEdit::ExtraSelection> m_syntaxHighlighterSelections; QTextEdit::ExtraSelection m_lineSelection; diff --git a/src/plugins/texteditor/linenumberfilter.cpp b/src/plugins/texteditor/linenumberfilter.cpp index f7cb55cbc6..711f8eb9da 100644 --- a/src/plugins/texteditor/linenumberfilter.cpp +++ b/src/plugins/texteditor/linenumberfilter.cpp @@ -62,9 +62,8 @@ void LineNumberFilter::accept(FilterEntry selection) const if (editor) { Core::EditorManager *editorManager = Core::EditorManager::instance(); editorManager->ensureEditorManagerVisible(); - editorManager->addCurrentPositionToNavigationHistory(true); - editor->gotoLine(selection.internalData.toInt()); editorManager->addCurrentPositionToNavigationHistory(); + editor->gotoLine(selection.internalData.toInt()); editor->widget()->setFocus(); } } diff --git a/src/plugins/vcsbase/nicknamedialog.cpp b/src/plugins/vcsbase/nicknamedialog.cpp new file mode 100644 index 0000000000..d6a10ad302 --- /dev/null +++ b/src/plugins/vcsbase/nicknamedialog.cpp @@ -0,0 +1,270 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#include "nicknamedialog.h" +#include "vcsbaseplugin.h" +#include "ui_nicknamedialog.h" + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtGui/QPushButton> +#include <QtGui/QStandardItemModel> +#include <QtGui/QSortFilterProxyModel> + +enum { NickNameRole = Qt::UserRole + 1 }; + +namespace VCSBase { +namespace Internal { + +// For code clarity, a struct representing the entries of a mail map file +// with parse and model functions. +struct NickNameEntry { + void clear(); + bool parse(const QString &); + QString nickName() const; + QList<QStandardItem *> toModelRow() const; + static QString nickNameOf(const QStandardItem *item); + + QString name; + QString email; + QString aliasName; + QString aliasEmail; +}; + +void NickNameEntry::clear() +{ + name.clear(); + email.clear(); + aliasName.clear(); + aliasEmail.clear(); +} + +// Parse "Hans Mustermann <HM@acme.de> [Alias [<alias@acme.de>]]" + +bool NickNameEntry::parse(const QString &l) +{ + clear(); + const QChar lessThan = QLatin1Char('<'); + const QChar greaterThan = QLatin1Char('>'); + // Get first name/mail pair + int mailPos = l.indexOf(lessThan); + if (mailPos == -1) + return false; + name = l.mid(0, mailPos).trimmed(); + mailPos++; + const int mailEndPos = l.indexOf(greaterThan, mailPos); + if (mailEndPos == -1) + return false; + email = l.mid(mailPos, mailEndPos - mailPos); + // get optional 2nd name/mail pair + const int aliasNameStart = mailEndPos + 1; + if (aliasNameStart >= l.size()) + return true; + int aliasMailPos = l.indexOf(lessThan, aliasNameStart); + if (aliasMailPos == -1) { + aliasName =l.mid(aliasNameStart, l.size() - aliasNameStart).trimmed(); + return true; + } + aliasName = l.mid(aliasNameStart, aliasMailPos - aliasNameStart).trimmed(); + aliasMailPos++; + const int aliasMailEndPos = l.indexOf(greaterThan, aliasMailPos); + if (aliasMailEndPos == -1) + return true; + aliasEmail = l.mid(aliasMailPos, aliasMailEndPos - aliasMailPos); + return true; +} + +// Format "Hans Mustermann <HM@acme.de>" +static inline QString formatNick(const QString &name, const QString &email) +{ + QString rc = name; + if (!email.isEmpty()) { + rc += QLatin1String(" <"); + rc += email; + rc += QLatin1Char('>'); + } + return rc; +} + +QString NickNameEntry::nickName() const +{ + return aliasName.isEmpty() ? formatNick(name, email) : formatNick(aliasName, aliasEmail); +} + +QList<QStandardItem *> NickNameEntry::toModelRow() const +{ + const QVariant nickNameData = nickName(); + QStandardItem *i1 = new QStandardItem(name); + i1->setData(nickNameData, NickNameRole); + QStandardItem *i2 = new QStandardItem(email); + i2->setData(nickNameData, NickNameRole); + QStandardItem *i3 = new QStandardItem(aliasName); + i3->setData(nickNameData, NickNameRole); + QStandardItem *i4 = new QStandardItem(aliasEmail); + i4->setData(nickNameData, NickNameRole); + QList<QStandardItem *> row; + row << i1 << i2 << i3 << i4; + return row; +} + +QString NickNameEntry::nickNameOf(const QStandardItem *item) +{ + return item->data(NickNameRole).toString(); +} + +QDebug operator<<(QDebug d, const NickNameEntry &e) +{ + d.nospace() << "Name='" << e.name << "' Mail='" << e.email + << " Alias='" << e.aliasName << " AliasEmail='" << e.aliasEmail << "'\n"; + return d; +} + +// Globally cached model tacked onto the plugin. +static QStandardItemModel *nickModel() +{ + static QStandardItemModel *model = 0; + if (!model) { + model = new QStandardItemModel(VCSBasePlugin::instance()); + QStringList headers; + headers << NickNameDialog::tr("Name") + << NickNameDialog::tr("E-mail") + << NickNameDialog::tr("Alias") + << NickNameDialog::tr("Alias e-mail"); + model->setHorizontalHeaderLabels(headers); + } + return model; +} + +static inline void clearModel(QStandardItemModel *model) +{ + if (const int rowCount = model->rowCount()) + model->removeRows(0, rowCount); +} + +NickNameDialog::NickNameDialog(QWidget *parent) : + QDialog(parent), + m_ui(new Ui::NickNameDialog), + m_filterModel(new QSortFilterProxyModel(this)) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_ui->setupUi(this); + okButton()->setEnabled(false); + + // Populate model and grow tree to accommodate it + m_filterModel->setSourceModel(nickModel()); + m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_ui->filterTreeView->setModel(m_filterModel); + const int columnCount = m_filterModel->columnCount(); + int treeWidth = 0; + for (int c = 0; c < columnCount; c++) { + m_ui->filterTreeView->resizeColumnToContents(c); + treeWidth += m_ui->filterTreeView->columnWidth(c); + } + m_ui->filterTreeView->setMinimumWidth(treeWidth + 20); + connect(m_ui->filterTreeView, SIGNAL(doubleClicked(QModelIndex)), this, + SLOT(slotDoubleClicked(QModelIndex))); + connect(m_ui->filterTreeView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), + this, SLOT(slotCurrentItemChanged(QModelIndex))); + connect(m_ui->filterLineEdit, SIGNAL(textChanged(QString)), + m_filterModel, SLOT(setFilterFixedString(QString))); +} + +NickNameDialog::~NickNameDialog() +{ + delete m_ui; +} + +QPushButton *NickNameDialog::okButton() const +{ + return m_ui->buttonBox->button(QDialogButtonBox::Ok); +} + +void NickNameDialog::slotCurrentItemChanged(const QModelIndex &index) +{ + okButton()->setEnabled(index.isValid()); +} + +void NickNameDialog::slotDoubleClicked(const QModelIndex &) +{ + if (okButton()->isEnabled()) + okButton()->animateClick(); +} + +QString NickNameDialog::nickName() const +{ + const QModelIndex index = m_ui->filterTreeView->selectionModel()->currentIndex(); + if (index.isValid()) { + const QModelIndex sourceIndex = m_filterModel->mapToSource(index); + if (const QStandardItem *item = nickModel()->itemFromIndex(sourceIndex)) + return NickNameEntry::nickNameOf(item); + } + return QString(); +} + +void NickNameDialog::clearNickNames() +{ + clearModel(nickModel()); +} + +bool NickNameDialog::readNickNamesFromMailCapFile(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = tr("Cannot open '%1': %2").arg(fileName, file.errorString()); + return false; + } + QStandardItemModel *model = nickModel(); + clearModel(model); + // Split into lines and read + NickNameEntry entry; + const QStringList lines = QString::fromUtf8(file.readAll()).trimmed().split(QLatin1Char('\n')); + const int count = lines.size(); + for (int i = 0; i < count; i++) { + if (entry.parse(lines.at(i))) { + model->appendRow(entry.toModelRow()); + } else { + qWarning("%s: Invalid mail cap entry at line %d: '%s'\n", qPrintable(fileName), i + 1, qPrintable(lines.at(i))); + } + } + model->sort(0); + return true; +} + +QStringList NickNameDialog::nickNameList() +{ + QStringList rc; + const QStandardItemModel *model = nickModel(); + const int rowCount = model->rowCount(); + for (int r = 0; r < rowCount; r++) + rc.push_back(NickNameEntry::nickNameOf(model->item(r, 0))); + return rc; +} + +} +} diff --git a/src/plugins/vcsbase/nicknamedialog.h b/src/plugins/vcsbase/nicknamedialog.h new file mode 100644 index 0000000000..7c64410577 --- /dev/null +++ b/src/plugins/vcsbase/nicknamedialog.h @@ -0,0 +1,83 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#ifndef NICKNAMEDIALOG_H +#define NICKNAMEDIALOG_H + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE +namespace Ui { + class NickNameDialog; +} +class QSortFilterProxyModel; +class QModelIndex; +class QPushButton; +QT_END_NAMESPACE + +namespace VCSBase { +namespace Internal { + +/* Nick name dialog: Manages a list of users read from an extended + * mail cap file, consisting of 4 columns: + * "Name Mail [AliasName [AliasMail]]". + * The names can be used for insertion into "RevBy:" fields; aliases will + * be preferred. The static functions to read/clear the mail map + * files access a global model which is shared by all instances of the + * dialog to achieve updating. */ + +class NickNameDialog : public QDialog { + Q_OBJECT +public: + explicit NickNameDialog(QWidget *parent = 0); + virtual ~NickNameDialog(); + + QString nickName() const; + + // Fill/clear the global nick name cache + static bool readNickNamesFromMailCapFile(const QString &file, QString *errorMessage); + static void clearNickNames(); + // Return a list for a completer on the field line edits + static QStringList nickNameList(); + +private slots: + void slotCurrentItemChanged(const QModelIndex &); + void slotDoubleClicked(const QModelIndex &); + +private: + QPushButton *okButton() const; + + Ui::NickNameDialog *m_ui; + QSortFilterProxyModel *m_filterModel; +}; + +} // namespace Internal +} // namespace VCSBase + +#endif // NICKNAMEDIALOG_H diff --git a/src/plugins/vcsbase/nicknamedialog.ui b/src/plugins/vcsbase/nicknamedialog.ui new file mode 100644 index 0000000000..6d7217d700 --- /dev/null +++ b/src/plugins/vcsbase/nicknamedialog.ui @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>NickNameDialog</class> + <widget class="QDialog" name="NickNameDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>618</width> + <height>414</height> + </rect> + </property> + <property name="windowTitle"> + <string>Nick Names</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="filterLabel"> + <property name="text"> + <string>Filter:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="filterLineEdit"/> + </item> + <item> + <widget class="QToolButton" name="filterClearToolButton"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="filterTreeView"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>NickNameDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>252</x> + <y>405</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>NickNameDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>293</x> + <y>405</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>filterClearToolButton</sender> + <signal>clicked()</signal> + <receiver>filterLineEdit</receiver> + <slot>clear()</slot> + <hints> + <hint type="sourcelabel"> + <x>263</x> + <y>14</y> + </hint> + <hint type="destinationlabel"> + <x>198</x> + <y>19</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro index d34fe84e7c..58bbc0dd4e 100644 --- a/src/plugins/vcsbase/vcsbase.pro +++ b/src/plugins/vcsbase/vcsbase.pro @@ -14,7 +14,11 @@ HEADERS += vcsbase_global.h \ basevcseditorfactory.h \ submiteditorfile.h \ basevcssubmiteditorfactory.h \ - submitfilemodel.h + submitfilemodel.h \ + vcsbasesettings.h \ + vcsbasesettingspage.h \ + nicknamedialog.h + SOURCES += vcsbaseplugin.cpp \ baseannotationhighlighter.cpp \ diffhighlighter.cpp \ @@ -24,5 +28,12 @@ SOURCES += vcsbaseplugin.cpp \ basevcseditorfactory.cpp \ submiteditorfile.cpp \ basevcssubmiteditorfactory.cpp \ - submitfilemodel.cpp -RESOURCES = vcsbase.qrc + submitfilemodel.cpp \ + vcsbasesettings.cpp \ + vcsbasesettingspage.cpp \ + nicknamedialog.cpp + +RESOURCES += vcsbase.qrc + +FORMS += vcsbasesettingspage.ui \ + nicknamedialog.ui diff --git a/src/plugins/vcsbase/vcsbaseconstants.h b/src/plugins/vcsbase/vcsbaseconstants.h index 6ce3141cb6..bb180213fb 100644 --- a/src/plugins/vcsbase/vcsbaseconstants.h +++ b/src/plugins/vcsbase/vcsbaseconstants.h @@ -36,6 +36,7 @@ namespace VCSBase { namespace Constants { const char * const VCS_SETTINGS_CATEGORY = QT_TRANSLATE_NOOP("VCSBase", "Version Control System"); +const char * const VCS_COMMON_SETTINGS_ID = QT_TRANSLATE_NOOP("VCSBase", "Common"); namespace Internal { enum { debug = 0 }; diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp index 2376f3b83e..d185efa9b0 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.cpp +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -265,9 +265,8 @@ void VCSBaseEditor::slotDiffBrowse(int index) return; const int lineNumber = d->m_diffSections.at(index); Core::EditorManager *editorManager = Core::EditorManager::instance(); - editorManager->addCurrentPositionToNavigationHistory(true); - gotoLine(lineNumber + 1, 0); // TextEdit uses 1..n convention editorManager->addCurrentPositionToNavigationHistory(); + gotoLine(lineNumber + 1, 0); // TextEdit uses 1..n convention } // Locate a line number in the list of diff sections. diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index 1aa9a32ff6..0de678e80e 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -29,6 +29,7 @@ #include "vcsbaseplugin.h" #include "diffhighlighter.h" +#include "vcsbasesettingspage.h" #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> @@ -42,7 +43,8 @@ namespace Internal { VCSBasePlugin *VCSBasePlugin::m_instance = 0; -VCSBasePlugin::VCSBasePlugin() +VCSBasePlugin::VCSBasePlugin() : + m_settingsPage(0) { m_instance = this; } @@ -61,6 +63,8 @@ bool VCSBasePlugin::initialize(const QStringList &arguments, QString *errorMessa if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/vcsbase/VCSBase.mimetypes.xml"), errorMessage)) return false; + m_settingsPage = new VCSBaseSettingsPage; + addAutoReleasedObject(m_settingsPage); return true; } @@ -73,6 +77,11 @@ VCSBasePlugin *VCSBasePlugin::instance() return m_instance; } +VCSBaseSettings VCSBasePlugin::settings() const +{ + return m_settingsPage->settings(); +} + } // namespace Internal } // namespace VCSBase diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h index 0adbd5b8a6..7b845a3055 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.h +++ b/src/plugins/vcsbase/vcsbaseplugin.h @@ -37,6 +37,9 @@ namespace VCSBase { namespace Internal { +struct VCSBaseSettings; +class VCSBaseSettingsPage; + class VCSBasePlugin : public ExtensionSystem::IPlugin { Q_OBJECT @@ -51,8 +54,11 @@ public: static VCSBasePlugin *instance(); + VCSBaseSettings settings() const; + private: static VCSBasePlugin *m_instance; + VCSBaseSettingsPage *m_settingsPage; }; } // namespace Internal diff --git a/src/plugins/vcsbase/vcsbasesettings.cpp b/src/plugins/vcsbase/vcsbasesettings.cpp new file mode 100644 index 0000000000..d2f6951bbd --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesettings.cpp @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#include "vcsbasesettings.h" + +#include <QtCore/QSettings> +#include <QtCore/QDebug> + +static const char *settingsGroupC = "VCS"; +static const char *nickNameMailMapKeyC = "NickNameMailMap"; +static const char *nickNameFieldListFileKeyC = "NickNameFieldListFile"; +static const char *promptForSubmitKeyC = "PromptForSubmit"; +static const char *submitMessageCheckScriptKeyC = "SubmitMessageCheckScript"; + +namespace VCSBase { +namespace Internal { + +VCSBaseSettings::VCSBaseSettings() : + promptForSubmit(true) +{ +} + +void VCSBaseSettings::toSettings(QSettings *s) const +{ + s->beginGroup(QLatin1String(settingsGroupC)); + s->setValue(QLatin1String(nickNameMailMapKeyC), nickNameMailMap); + s->setValue(QLatin1String(nickNameFieldListFileKeyC), nickNameFieldListFile); + s->setValue(QLatin1String(submitMessageCheckScriptKeyC), submitMessageCheckScript); + s->setValue(QLatin1String(promptForSubmitKeyC), promptForSubmit); + s->endGroup(); +} + +void VCSBaseSettings::fromSettings(QSettings *s) +{ + s->beginGroup(QLatin1String(settingsGroupC)); + nickNameMailMap = s->value(QLatin1String(nickNameMailMapKeyC), QString()).toString(); + nickNameFieldListFile = s->value(QLatin1String(nickNameFieldListFileKeyC), QString()).toString(); + submitMessageCheckScript = s->value(QLatin1String(submitMessageCheckScriptKeyC), QString()).toString(); + promptForSubmit = s->value(QLatin1String(promptForSubmitKeyC), QVariant(true)).toBool(); + s->endGroup(); +} + +bool VCSBaseSettings::equals(const VCSBaseSettings &rhs) const +{ + return promptForSubmit == rhs.promptForSubmit + && nickNameMailMap == rhs.nickNameMailMap + && nickNameFieldListFile == rhs.nickNameFieldListFile + && submitMessageCheckScript == rhs.submitMessageCheckScript; +} + +} +} diff --git a/src/plugins/vcsbase/vcsbasesettings.h b/src/plugins/vcsbase/vcsbasesettings.h new file mode 100644 index 0000000000..b4efd59d33 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesettings.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#ifndef VCSBASESETTINGS_H +#define VCSBASESETTINGS_H + +#include <QtCore/QString> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace VCSBase { +namespace Internal { + +// Common VCS settings, message check script and user nick names. +struct VCSBaseSettings { + VCSBaseSettings(); + + bool promptForSubmit; + + QString nickNameMailMap; + QString nickNameFieldListFile; + + QString submitMessageCheckScript; + + void toSettings(QSettings *) const; + void fromSettings(QSettings *); + + bool equals(const VCSBaseSettings &rhs) const; +}; + +inline bool operator==(const VCSBaseSettings &s1, const VCSBaseSettings &s2) { return s1.equals(s2); } +inline bool operator!=(const VCSBaseSettings &s1, const VCSBaseSettings &s2) { return !s1.equals(s2); } + +} // namespace Internal +} // namespace VCSBase + +#endif // VCSBASESETTINGS_H diff --git a/src/plugins/vcsbase/vcsbasesettingspage.cpp b/src/plugins/vcsbase/vcsbasesettingspage.cpp new file mode 100644 index 0000000000..0cc528a2e7 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesettingspage.cpp @@ -0,0 +1,145 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#include "vcsbasesettingspage.h" +#include "vcsbaseconstants.h" +#include "nicknamedialog.h" + +#include "ui_vcsbasesettingspage.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QDebug> +#include <QtCore/QCoreApplication> +#include <QtGui/QMessageBox> + +namespace VCSBase { +namespace Internal { + +// ------------------ VCSBaseSettingsWidget + +VCSBaseSettingsWidget::VCSBaseSettingsWidget(QWidget *parent) : + QWidget(parent), + m_ui(new Ui::VCSBaseSettingsPage) +{ + m_ui->setupUi(this); + m_ui->submitMessageCheckScriptChooser->setExpectedKind(Core::Utils::PathChooser::Command); + m_ui->nickNameFieldsFileChooser->setExpectedKind(Core::Utils::PathChooser::File); + m_ui->nickNameMailMapChooser->setExpectedKind(Core::Utils::PathChooser::File); +} + +VCSBaseSettingsWidget::~VCSBaseSettingsWidget() +{ + delete m_ui; +} + +VCSBaseSettings VCSBaseSettingsWidget::settings() const +{ + VCSBaseSettings rc; + rc.nickNameMailMap = m_ui->nickNameMailMapChooser->path(); + rc.nickNameFieldListFile = m_ui->nickNameFieldsFileChooser->path(); + rc.submitMessageCheckScript = m_ui->submitMessageCheckScriptChooser->path(); + rc.promptForSubmit = m_ui->promptForSubmitCheckBox->isChecked(); + return rc; +} + +void VCSBaseSettingsWidget::setSettings(const VCSBaseSettings &s) +{ + + m_ui->nickNameMailMapChooser->setPath(s.nickNameMailMap); + m_ui->nickNameFieldsFileChooser->setPath(s.nickNameFieldListFile); + m_ui->submitMessageCheckScriptChooser->setPath(s.submitMessageCheckScript); + m_ui->promptForSubmitCheckBox->setChecked(s.promptForSubmit); +} + +// --------------- VCSBaseSettingsPage +VCSBaseSettingsPage::VCSBaseSettingsPage(QObject *parent) : + Core::IOptionsPage(parent) +{ + m_settings.fromSettings(Core::ICore::instance()->settings()); + updateNickNames(); +} + +void VCSBaseSettingsPage::updateNickNames() +{ + if (m_settings.nickNameMailMap.isEmpty()) { + NickNameDialog::clearNickNames(); + } else { + QString errorMessage; + if (!NickNameDialog::readNickNamesFromMailCapFile(m_settings.nickNameMailMap, &errorMessage)) + qWarning("%s", qPrintable(errorMessage)); + } +} + +VCSBaseSettingsPage::~VCSBaseSettingsPage() +{ +} + +QString VCSBaseSettingsPage::id() const +{ + return QLatin1String(Constants::VCS_COMMON_SETTINGS_ID); +} + +QString VCSBaseSettingsPage::trName() const +{ + return QCoreApplication::translate("VCSBase", Constants::VCS_COMMON_SETTINGS_ID); +} + +QString VCSBaseSettingsPage::category() const +{ + return QLatin1String(Constants::VCS_SETTINGS_CATEGORY); +} + +QString VCSBaseSettingsPage::trCategory() const +{ + return QCoreApplication::translate("VCSBase", Constants::VCS_SETTINGS_CATEGORY); +} + +QWidget *VCSBaseSettingsPage::createPage(QWidget *parent) +{ + m_widget = new VCSBaseSettingsWidget(parent); + m_widget->setSettings(m_settings); + return m_widget; +} + +void VCSBaseSettingsPage::apply() +{ + if (m_widget) { + const VCSBaseSettings newSettings = m_widget->settings(); + if (newSettings != m_settings) { + m_settings = newSettings; + m_settings.toSettings(Core::ICore::instance()->settings()); + updateNickNames(); + } + } +} + +} +} diff --git a/src/plugins/vcsbase/vcsbasesettingspage.h b/src/plugins/vcsbase/vcsbasesettingspage.h new file mode 100644 index 0000000000..4492f66544 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesettingspage.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#ifndef VCSBASESETTINGSPAGE_H +#define VCSBASESETTINGSPAGE_H + +#include "vcsbasesettings.h" +#include <coreplugin/dialogs/ioptionspage.h> +#include <QtCore/QPointer> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { + class VCSBaseSettingsPage; +} +QT_END_NAMESPACE + +namespace VCSBase { +namespace Internal { + +class VCSBaseSettingsWidget : public QWidget { + Q_OBJECT +public: + explicit VCSBaseSettingsWidget(QWidget *parent = 0); + virtual ~VCSBaseSettingsWidget(); + + VCSBaseSettings settings() const; + void setSettings(const VCSBaseSettings &s); + +private: + Ui::VCSBaseSettingsPage *m_ui; +}; + +class VCSBaseSettingsPage : public Core::IOptionsPage +{ +public: + explicit VCSBaseSettingsPage(QObject *parent = 0); + virtual ~VCSBaseSettingsPage(); + + virtual QString id() const; + virtual QString trName() const; + virtual QString category() const; + virtual QString trCategory() const; + + virtual QWidget *createPage(QWidget *parent); + virtual void apply(); + virtual void finish() { } + + VCSBaseSettings settings() const { return m_settings; } + +private: + void updateNickNames(); + QPointer<VCSBaseSettingsWidget> m_widget; + VCSBaseSettings m_settings; +}; + +} // namespace Internal +} // namespace VCSBase + +#endif // VCSBASESETTINGSPAGE_H diff --git a/src/plugins/vcsbase/vcsbasesettingspage.ui b/src/plugins/vcsbase/vcsbasesettingspage.ui new file mode 100644 index 0000000000..01e478f817 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesettingspage.ui @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>VCSBaseSettingsPage</class> + <widget class="QWidget" name="VCSBaseSettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>536</width> + <height>407</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="promptForSubmitLabel"> + <property name="text"> + <string>Prompt for submit:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="promptForSubmitCheckBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="submitMessageCheckScriptLabel"> + <property name="toolTip"> + <string>An executable which is called with the submit message in a temporary file as first argument. It should return with an exit != 0 and a message on standard error to indicate failure.</string> + </property> + <property name="text"> + <string>Submit message check script:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="Core::Utils::PathChooser" name="submitMessageCheckScriptChooser" native="true"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="nickNameMailMapLabel"> + <property name="toolTip"> + <string>A file listing user names and email addresses in a 4-column mailmap format: +name <email> alias <email></string> + </property> + <property name="text"> + <string>User/alias configuration file:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="Core::Utils::PathChooser" name="nickNameMailMapChooser" native="true"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="nickNameFieldsFileLabel"> + <property name="toolTip"> + <string>A simple file containing lines with field names like "Reviewed-By:" which will be added below the submit editor.</string> + </property> + <property name="text"> + <string>User fields configuration file:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="Core::Utils::PathChooser" name="nickNameFieldsFileChooser" native="true"/> + </item> + </layout> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>307</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::PathChooser</class> + <extends>QWidget</extends> + <header location="global">utils/pathchooser.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 473abaf175..46ae4374cf 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -28,10 +28,14 @@ **************************************************************************/ #include "vcsbasesubmiteditor.h" +#include "vcsbasesettings.h" +#include "vcsbaseplugin.h" +#include "nicknamedialog.h" #include "submiteditorfile.h" #include <aggregation/aggregate.h> #include <coreplugin/ifile.h> +#include <coreplugin/icore.h> #include <coreplugin/uniqueidmanager.h> #include <coreplugin/actionmanager/actionmanager.h> #include <utils/submiteditorwidget.h> @@ -42,18 +46,31 @@ #include <QtCore/QDebug> #include <QtCore/QDir> +#include <QtCore/QTemporaryFile> +#include <QtCore/QProcess> #include <QtCore/QFile> #include <QtCore/QFileInfo> #include <QtCore/QPointer> #include <QtCore/QTextStream> #include <QtGui/QStyle> #include <QtGui/QToolBar> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtGui/QCompleter> +#include <QtGui/QLineEdit> enum { debug = 0 }; enum { wantToolBar = 0 }; namespace VCSBase { +static inline QString submitMessageCheckScript() +{ + return Internal::VCSBasePlugin::instance()->settings().submitMessageCheckScript; +} + struct VCSBaseSubmitEditorPrivate { VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters, @@ -69,6 +86,8 @@ struct VCSBaseSubmitEditorPrivate QPointer<QAction> m_diffAction; QPointer<QAction> m_submitAction; + + Internal::NickNameDialog *m_nickNameDialog; }; VCSBaseSubmitEditorPrivate::VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters, @@ -77,7 +96,8 @@ VCSBaseSubmitEditorPrivate::VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditor m_widget(editorWidget), m_toolWidget(0), m_parameters(parameters), - m_file(new VCSBase::Internal::SubmitEditorFile(QLatin1String(m_parameters->mimeType), q)) + m_file(new VCSBase::Internal::SubmitEditorFile(QLatin1String(m_parameters->mimeType), q)), + m_nickNameDialog(0) { m_contexts << Core::UniqueIDManager::instance()->uniqueIdentifier(m_parameters->context); } @@ -93,6 +113,29 @@ VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *pa connect(m_d->m_widget, SIGNAL(diffSelected(QStringList)), this, SLOT(slotDiffSelectedVCSFiles(QStringList))); connect(m_d->m_widget->descriptionEdit(), SIGNAL(textChanged()), this, SLOT(slotDescriptionChanged())); + const Internal::VCSBaseSettings settings = Internal::VCSBasePlugin::instance()->settings(); + // Add additional context menu settings + if (!settings.submitMessageCheckScript.isEmpty() || !settings.nickNameMailMap.isEmpty()) { + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_d->m_widget->addDescriptionEditContextMenuAction(sep); + // Run check action + if (!settings.submitMessageCheckScript.isEmpty()) { + QAction *checkAction = new QAction(tr("Check message"), this); + connect(checkAction, SIGNAL(triggered()), this, SLOT(slotCheckSubmitMessage())); + m_d->m_widget->addDescriptionEditContextMenuAction(checkAction); + } + // Insert nick + if (!settings.nickNameMailMap.isEmpty()) { + QAction *insertAction = new QAction(tr("Insert name..."), this); + connect(insertAction, SIGNAL(triggered()), this, SLOT(slotInsertNickName())); + m_d->m_widget->addDescriptionEditContextMenuAction(insertAction); + } + } + // Do we have user fields? + if (!settings.nickNameFieldListFile.isEmpty()) + createUserFields(settings.nickNameFieldListFile); + connect(m_d->m_widget, SIGNAL(fieldDialogRequested(int)), this, SLOT(slotSetFieldNickName(int))); Aggregation::Aggregate *aggregate = new Aggregation::Aggregate; aggregate->add(new Find::BaseTextFind(m_d->m_widget->descriptionEdit())); aggregate->add(this); @@ -105,6 +148,26 @@ VCSBaseSubmitEditor::~VCSBaseSubmitEditor() delete m_d; } +void VCSBaseSubmitEditor::createUserFields(const QString &fieldConfigFile) +{ + QFile fieldFile(fieldConfigFile); + if (!fieldFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + qWarning("%s: Unable to open %s: %s", Q_FUNC_INFO, qPrintable(fieldConfigFile), qPrintable(fieldFile.errorString())); + return; + } + // Parse into fields + const QStringList fields = QString::fromUtf8(fieldFile.readAll()).trimmed().split(QLatin1Char('\n')); + if (fields.empty()) + return; + // Create a completer on user names + QCompleter *completer = new QCompleter(Internal::NickNameDialog::nickNameList(), this); + foreach(const QString &field, fields) { + const QString trimmedField = field.trimmed(); + if (!trimmedField.isEmpty()) + m_d->m_widget->addField(trimmedField, true)->setCompleter(completer); + } +} + void VCSBaseSubmitEditor::registerActions(QAction *editorUndoAction, QAction *editorRedoAction, QAction *submitAction, QAction *diffAction)\ { @@ -139,7 +202,6 @@ void VCSBaseSubmitEditor::setFileListSelectionMode(QAbstractItemView::SelectionM m_d->m_widget->setFileListSelectionMode(sm); } - void VCSBaseSubmitEditor::slotDescriptionChanged() { } @@ -304,6 +366,131 @@ bool VCSBaseSubmitEditor::setFileContents(const QString &contents) return true; } +enum { checkDialogMinimumWidth = 500 }; + +VCSBaseSubmitEditor::PromptSubmitResult + VCSBaseSubmitEditor::promptSubmit(const QString &title, + const QString &question, + const QString &checkFailureQuestion, + bool forcePrompt) const +{ + QString errorMessage; + QMessageBox::StandardButton answer = QMessageBox::Yes; + + const bool prompt = forcePrompt || Internal::VCSBasePlugin::instance()->settings().promptForSubmit; + + QWidget *parent = Core::ICore::instance()->mainWindow(); + // Pop up a message depending on whether the check succeeded and the + // user wants to be prompted + if (checkSubmitMessage(&errorMessage)) { + // Check ok, do prompt? + if (prompt) { + answer = QMessageBox::question(parent, title, question, + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, + QMessageBox::Yes); + } + } else { + // Check failed. + QMessageBox msgBox(QMessageBox::Question, title, checkFailureQuestion, + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, parent); + msgBox.setDefaultButton(QMessageBox::Cancel); + msgBox.setInformativeText(errorMessage); + msgBox.setMinimumWidth(checkDialogMinimumWidth); + answer = static_cast<QMessageBox::StandardButton>(msgBox.exec()); + } + switch (answer) { + case QMessageBox::Cancel: + return SubmitCanceled; + case QMessageBox::No: + return SubmitDiscarded; + default: + break; + } + return SubmitConfirmed; +} + +QString VCSBaseSubmitEditor::promptForNickName() +{ + if (!m_d->m_nickNameDialog) + m_d->m_nickNameDialog = new Internal::NickNameDialog(m_d->m_widget); + if (m_d->m_nickNameDialog->exec() == QDialog::Accepted) + return m_d->m_nickNameDialog->nickName(); + return QString(); +} + +void VCSBaseSubmitEditor::slotInsertNickName() +{ + const QString nick = promptForNickName(); + if (!nick.isEmpty()) + m_d->m_widget->descriptionEdit()->textCursor().insertText(nick); +} + +void VCSBaseSubmitEditor::slotSetFieldNickName(int i) +{ + const QString nick = promptForNickName(); + if (!nick.isEmpty()) + m_d->m_widget->fieldLineEdit(i)->setText(nick); +} + +void VCSBaseSubmitEditor::slotCheckSubmitMessage() +{ + QString errorMessage; + if (!checkSubmitMessage(&errorMessage)) { + QMessageBox msgBox(QMessageBox::Warning, tr("Submit Message Check failed"), + errorMessage, QMessageBox::Ok, m_d->m_widget); + msgBox.setMinimumWidth(checkDialogMinimumWidth); + msgBox.exec(); + } +} + +bool VCSBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const +{ + const QString checkScript = submitMessageCheckScript(); + if (checkScript.isEmpty()) + return true; + QApplication::setOverrideCursor(Qt::WaitCursor); + const bool rc = runSubmitMessageCheckScript(checkScript, errorMessage); + QApplication::restoreOverrideCursor(); + return rc; +} + +bool VCSBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript, QString *errorMessage) const +{ + // Write out message + QString tempFilePattern = QDir::tempPath(); + if (!tempFilePattern.endsWith(QDir::separator())) + tempFilePattern += QDir::separator(); + tempFilePattern += QLatin1String("msgXXXXXX.txt"); + QTemporaryFile messageFile(tempFilePattern); + messageFile.setAutoRemove(true); + if (!messageFile.open()) { + *errorMessage = tr("Unable to open '%1': %2").arg(messageFile.fileName(), messageFile.errorString()); + return false; + } + const QString messageFileName = messageFile.fileName(); + messageFile.write(fileContents().toUtf8()); + messageFile.close(); + // Run check process + QProcess checkProcess; + checkProcess.start(checkScript, QStringList(messageFileName)); + if (!checkProcess.waitForStarted()) { + *errorMessage = tr("The check script '%1' could not be started: %2").arg(checkScript, checkProcess.errorString()); + return false; + } + if (!checkProcess.waitForFinished()) { + *errorMessage = tr("The check script '%1' could not be run: %2").arg(checkScript, checkProcess.errorString()); + return false; + } + const int exitCode = checkProcess.exitCode(); + if (exitCode != 0) { + *errorMessage = QString::fromLocal8Bit(checkProcess.readAllStandardError()); + if (errorMessage->isEmpty()) + *errorMessage = tr("The check script returned exit code %1.").arg(exitCode); + return false; + } + return true; +} + QIcon VCSBaseSubmitEditor::diffIcon() { return QIcon(QLatin1String(":/vcsbase/images/diff.png")); diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index 3dcc4e4d29..4a02d262a2 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -50,7 +50,9 @@ namespace Core { } namespace VCSBase { - +namespace Internal { + struct VCSBaseSettings; +} struct VCSBaseSubmitEditorPrivate; /* Utility struct to parametrize a VCSBaseSubmitEditor. */ @@ -104,6 +106,15 @@ public: virtual ~VCSBaseSubmitEditor(); + // A utility routine to be called when closing a submit editor. + // Runs checks on the message and prompts according to configuration. + // Force prompt should be true if it is invoked by closing an editor + // as opposed to invoking the "Submit" button. + enum PromptSubmitResult { SubmitConfirmed, SubmitCanceled, SubmitDiscarded }; + PromptSubmitResult promptSubmit(const QString &title, const QString &question, + const QString &checkFailureQuestion, + bool forcePrompt = false) const; + int fileNameColumn() const; void setFileNameColumn(int c); @@ -147,6 +158,9 @@ private slots: void slotDiffSelectedVCSFiles(const QStringList &rawList); bool save(const QString &fileName); void slotDescriptionChanged(); + void slotCheckSubmitMessage(); + void slotInsertNickName(); + void slotSetFieldNickName(int); protected: /* These hooks allow for modifying the contents that goes to @@ -156,6 +170,11 @@ protected: virtual bool setFileContents(const QString &contents); private: + void createUserFields(const QString &fieldConfigFile); + bool checkSubmitMessage(QString *errorMessage) const; + bool runSubmitMessageCheckScript(const QString &script, QString *errorMessage) const; + QString promptForNickName(); + VCSBaseSubmitEditorPrivate *m_d; }; |