diff options
Diffstat (limited to 'src/plugins/coreplugin')
96 files changed, 1648 insertions, 581 deletions
diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt new file mode 100644 index 0000000000..7783685969 --- /dev/null +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -0,0 +1,185 @@ +add_qtc_plugin(Core + DEPENDS Qt5::PrintSupport Qt5::Qml Qt5::Sql Qt5::Gui Qt5::GuiPrivate + PUBLIC_DEPENDS Aggregation ExtensionSystem Utils app_version + SOURCES + actionmanager/actioncontainer.cpp actionmanager/actioncontainer.h actionmanager/actioncontainer_p.h + actionmanager/actionmanager.cpp actionmanager/actionmanager.h actionmanager/actionmanager_p.h + actionmanager/command.cpp actionmanager/command.h actionmanager/command_p.h + actionmanager/commandbutton.cpp actionmanager/commandbutton.h + actionmanager/commandmappings.cpp actionmanager/commandmappings.h + actionmanager/commandsfile.cpp actionmanager/commandsfile.h + basefilewizard.cpp basefilewizard.h + basefilewizardfactory.cpp basefilewizardfactory.h + core.qrc + core_global.h + coreconstants.h + coreicons.cpp coreicons.h + corejsextensions.cpp corejsextensions.h + coreplugin.cpp coreplugin.h + designmode.cpp designmode.h + dialogs/addtovcsdialog.cpp dialogs/addtovcsdialog.h dialogs/addtovcsdialog.ui + dialogs/externaltoolconfig.cpp dialogs/externaltoolconfig.h dialogs/externaltoolconfig.ui + dialogs/filepropertiesdialog.cpp dialogs/filepropertiesdialog.h dialogs/filepropertiesdialog.ui + dialogs/ioptionspage.cpp dialogs/ioptionspage.h + dialogs/newdialog.cpp dialogs/newdialog.h dialogs/newdialog.ui + dialogs/openwithdialog.cpp dialogs/openwithdialog.h dialogs/openwithdialog.ui + dialogs/promptoverwritedialog.cpp dialogs/promptoverwritedialog.h + dialogs/readonlyfilesdialog.cpp dialogs/readonlyfilesdialog.h dialogs/readonlyfilesdialog.ui + dialogs/saveitemsdialog.cpp dialogs/saveitemsdialog.h dialogs/saveitemsdialog.ui + dialogs/settingsdialog.cpp dialogs/settingsdialog.h + dialogs/shortcutsettings.cpp dialogs/shortcutsettings.h + diffservice.cpp diffservice.h + documentmanager.cpp documentmanager.h + editmode.cpp editmode.h + editormanager/documentmodel.cpp editormanager/documentmodel.h editormanager/documentmodel_p.h + editormanager/editorarea.cpp editormanager/editorarea.h + editormanager/editormanager.cpp editormanager/editormanager.h editormanager/editormanager_p.h + editormanager/editorview.cpp editormanager/editorview.h + editormanager/editorwindow.cpp editormanager/editorwindow.h + editormanager/ieditor.cpp editormanager/ieditor.h + editormanager/ieditorfactory.cpp editormanager/ieditorfactory.h editormanager/ieditorfactory_p.h + editormanager/iexternaleditor.cpp editormanager/iexternaleditor.h + editormanager/openeditorsview.cpp editormanager/openeditorsview.h + editormanager/openeditorswindow.cpp editormanager/openeditorswindow.h + editormanager/systemeditor.cpp editormanager/systemeditor.h + editortoolbar.cpp editortoolbar.h + externaltool.cpp externaltool.h + externaltoolmanager.cpp externaltoolmanager.h + fancyactionbar.cpp fancyactionbar.h + fancyactionbar.qrc + fancytabwidget.cpp fancytabwidget.h + featureprovider.cpp featureprovider.h + fileiconprovider.cpp fileiconprovider.h + fileutils.cpp fileutils.h + find/basetextfind.cpp find/basetextfind.h + find/currentdocumentfind.cpp find/currentdocumentfind.h + find/find.qrc + find/finddialog.ui + find/findplugin.cpp find/findplugin.h + find/findtoolbar.cpp find/findtoolbar.h + find/findtoolwindow.cpp find/findtoolwindow.h + find/findwidget.ui + find/highlightscrollbarcontroller.cpp find/highlightscrollbarcontroller.h + find/ifindfilter.cpp find/ifindfilter.h + find/ifindsupport.cpp find/ifindsupport.h + find/itemviewfind.cpp find/itemviewfind.h + find/optionspopup.cpp find/optionspopup.h + find/searchresultcolor.h + find/searchresultitem.h + find/searchresulttreeitemdelegate.cpp find/searchresulttreeitemdelegate.h + find/searchresulttreeitemroles.h + find/searchresulttreeitems.cpp find/searchresulttreeitems.h + find/searchresulttreemodel.cpp find/searchresulttreemodel.h + find/searchresulttreeview.cpp find/searchresulttreeview.h + find/searchresultwidget.cpp find/searchresultwidget.h + find/searchresultwindow.cpp find/searchresultwindow.h + find/textfindconstants.h + findplaceholder.cpp findplaceholder.h + generalsettings.cpp generalsettings.h generalsettings.ui + generatedfile.cpp generatedfile.h + helpitem.cpp helpitem.h + helpmanager.cpp helpmanager.h helpmanager_implementation.h + icontext.cpp icontext.h + icore.cpp icore.h + id.cpp id.h + idocument.cpp idocument.h + idocumentfactory.cpp idocumentfactory.h + ifilewizardextension.h + imode.cpp imode.h + inavigationwidgetfactory.cpp inavigationwidgetfactory.h + infobar.cpp infobar.h + ioutputpane.cpp ioutputpane.h + iversioncontrol.cpp iversioncontrol.h + iwelcomepage.cpp iwelcomepage.h + iwizardfactory.cpp iwizardfactory.h + jsexpander.cpp jsexpander.h + locator/basefilefilter.cpp locator/basefilefilter.h + locator/commandlocator.cpp locator/commandlocator.h + locator/directoryfilter.cpp locator/directoryfilter.h locator/directoryfilter.ui + locator/executefilter.cpp locator/executefilter.h + locator/externaltoolsfilter.cpp locator/externaltoolsfilter.h + locator/filesystemfilter.cpp locator/filesystemfilter.h locator/filesystemfilter.ui + locator/ilocatorfilter.cpp locator/ilocatorfilter.h + locator/locator.cpp locator/locator.h + locator/locatorconstants.h + locator/locatorfiltersfilter.cpp locator/locatorfiltersfilter.h + locator/locatormanager.cpp locator/locatormanager.h + locator/locatorsearchutils.cpp locator/locatorsearchutils.h + locator/locatorsettingspage.cpp locator/locatorsettingspage.h locator/locatorsettingspage.ui + locator/locatorwidget.cpp locator/locatorwidget.h + locator/opendocumentsfilter.cpp locator/opendocumentsfilter.h + mainwindow.cpp mainwindow.h + manhattanstyle.cpp manhattanstyle.h + menubarfilter.cpp menubarfilter.h + messagebox.cpp messagebox.h + messagemanager.cpp messagemanager.h + messageoutputwindow.cpp messageoutputwindow.h + mimetypemagicdialog.cpp mimetypemagicdialog.h mimetypemagicdialog.ui + mimetypesettings.cpp mimetypesettings.h + mimetypesettingspage.ui + minisplitter.cpp minisplitter.h + modemanager.cpp modemanager.h + navigationsubwidget.cpp navigationsubwidget.h + navigationwidget.cpp navigationwidget.h + opendocumentstreeview.cpp opendocumentstreeview.h + outputpane.cpp outputpane.h + outputpanemanager.cpp outputpanemanager.h + outputwindow.cpp outputwindow.h + patchtool.cpp patchtool.h + plugindialog.cpp plugindialog.h + progressmanager/futureprogress.cpp progressmanager/futureprogress.h + progressmanager/progressbar.cpp progressmanager/progressbar.h + progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h + progressmanager/progressview.cpp progressmanager/progressview.h + reaper.cpp reaper.h reaper_p.h + rightpane.cpp rightpane.h + settingsdatabase.cpp settingsdatabase.h + shellcommand.cpp shellcommand.h + sidebar.cpp sidebar.h + sidebarwidget.cpp sidebarwidget.h + statusbarmanager.cpp statusbarmanager.h + styleanimator.cpp styleanimator.h + systemsettings.cpp systemsettings.h systemsettings.ui + textdocument.cpp textdocument.h + themechooser.cpp themechooser.h + toolsettings.cpp toolsettings.h + variablechooser.cpp variablechooser.h + vcsmanager.cpp vcsmanager.h + versiondialog.cpp versiondialog.h + windowsupport.cpp windowsupport.h + EXPLICIT_MOC dialogs/filepropertiesdialog.h +) + +extend_qtc_plugin(Core + CONDITION WITH_TESTS + SOURCES + locator/locator_test.cpp + locator/locatorfiltertest.cpp locator/locatorfiltertest.h + testdatadir.cpp testdatadir.h +) + +extend_qtc_plugin(Core + CONDITION WIN32 + SOURCES progressmanager/progressmanager_win.cpp +) + +extend_qtc_plugin(Core + CONDITION APPLE + DEPENDS ${FWAppKit} + SOURCES + progressmanager/progressmanager_mac.mm + locator/spotlightlocatorfilter.h locator/spotlightlocatorfilter.mm +) + +extend_qtc_plugin(Core + CONDITION (NOT WIN32) AND (NOT APPLE) + SOURCES progressmanager/progressmanager_x11.cpp +) + +extend_qtc_plugin(Core + CONDITION TARGET Qt5::Script + DEPENDS Qt5::Script + DEFINES WITH_JAVASCRIPTFILTER + SOURCES + locator/javascriptfilter.cpp locator/javascriptfilter.h +) diff --git a/src/plugins/coreplugin/corejsextensions.cpp b/src/plugins/coreplugin/corejsextensions.cpp index 7398d60a94..b5cbd08973 100644 --- a/src/plugins/coreplugin/corejsextensions.cpp +++ b/src/plugins/coreplugin/corejsextensions.cpp @@ -118,7 +118,7 @@ QString UtilsJsExtension::preferredSuffix(const QString &mimetype) const QString UtilsJsExtension::fileName(const QString &path, const QString &extension) const { - return Utils::FileName::fromString(path, extension).toString(); + return Utils::FilePath::fromStringWithExtension(path, extension).toString(); } QString UtilsJsExtension::mktemp(const QString &pattern) const diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 7f36733131..95398acc29 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -276,7 +276,7 @@ void CorePlugin::addToPathChooserContextMenu(Utils::PathChooser *pathChooser, QM }); menu->insertAction(firstAction, showInGraphicalShell); - auto *showInTerminal = new QAction(Core::FileUtils::msgTerminalAction(), menu); + auto *showInTerminal = new QAction(Core::FileUtils::msgTerminalHereAction(), menu); connect(showInTerminal, &QAction::triggered, pathChooser, [pathChooser]() { Core::FileUtils::openTerminal(pathChooser->path()); }); diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index cc4da8d2fd..4a603b655c 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -314,6 +314,8 @@ Project { "ifindsupport.h", "itemviewfind.cpp", "itemviewfind.h", + "optionspopup.cpp", + "optionspopup.h", "searchresultcolor.h", "searchresulttreeitemdelegate.cpp", "searchresulttreeitemdelegate.h", diff --git a/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp b/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp index 33d2123238..fef059d7fa 100644 --- a/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp +++ b/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp @@ -37,7 +37,7 @@ #include <QFileInfo> #include <QLocale> -FilePropertiesDialog::FilePropertiesDialog(const Utils::FileName &fileName, QWidget *parent) : +FilePropertiesDialog::FilePropertiesDialog(const Utils::FilePath &fileName, QWidget *parent) : QDialog(parent), m_ui(new Ui::FilePropertiesDialog), m_fileName(fileName.toString()) @@ -78,11 +78,7 @@ void FilePropertiesDialog::refresh() m_ui->owner->setText(fileInfo.owner()); m_ui->group->setText(fileInfo.group()); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) m_ui->size->setText(locale.formattedDataSize(fileInfo.size())); -#else - m_ui->size->setText(tr("%1 Bytes").arg(locale.toString(fileInfo.size()))); -#endif m_ui->readable->setChecked(fileInfo.isReadable()); m_ui->writable->setChecked(fileInfo.isWritable()); m_ui->executable->setChecked(fileInfo.isExecutable()); diff --git a/src/plugins/coreplugin/dialogs/filepropertiesdialog.h b/src/plugins/coreplugin/dialogs/filepropertiesdialog.h index db55abeb0e..95d8bfbceb 100644 --- a/src/plugins/coreplugin/dialogs/filepropertiesdialog.h +++ b/src/plugins/coreplugin/dialogs/filepropertiesdialog.h @@ -41,7 +41,7 @@ class FilePropertiesDialog : public QDialog Q_OBJECT public: - explicit FilePropertiesDialog(const Utils::FileName &fileName, QWidget *parent = nullptr); + explicit FilePropertiesDialog(const Utils::FilePath &fileName, QWidget *parent = nullptr); ~FilePropertiesDialog() override; private: diff --git a/src/plugins/coreplugin/dialogs/newdialog.cpp b/src/plugins/coreplugin/dialogs/newdialog.cpp index e89ea9846c..e691d826ed 100644 --- a/src/plugins/coreplugin/dialogs/newdialog.cpp +++ b/src/plugins/coreplugin/dialogs/newdialog.cpp @@ -231,7 +231,7 @@ NewDialog::NewDialog(QWidget *parent) : connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &NewDialog::reject); connect(m_ui->comboBox, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged), + QOverload<const QString &>::of(&QComboBox::currentIndexChanged), this, &NewDialog::setSelectedPlatform); } diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.cpp b/src/plugins/coreplugin/dialogs/openwithdialog.cpp index 7452fdb319..526a32f7e5 100644 --- a/src/plugins/coreplugin/dialogs/openwithdialog.cpp +++ b/src/plugins/coreplugin/dialogs/openwithdialog.cpp @@ -37,7 +37,7 @@ OpenWithDialog::OpenWithDialog(const QString &fileName, QWidget *parent) { setupUi(this); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - label->setText(tr("Open file \"%1\" with:").arg(Utils::FileName::fromString(fileName).fileName())); + label->setText(tr("Open file \"%1\" with:").arg(Utils::FilePath::fromString(fileName).fileName())); buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.h b/src/plugins/coreplugin/dialogs/openwithdialog.h index a3f0db0366..1833af8383 100644 --- a/src/plugins/coreplugin/dialogs/openwithdialog.h +++ b/src/plugins/coreplugin/dialogs/openwithdialog.h @@ -29,8 +29,6 @@ #include "ui_openwithdialog.h" namespace Core { - - namespace Internal { // Present the user with a file name and a list of available diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp index af41349cff..9e9a8cc428 100644 --- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp +++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp @@ -33,6 +33,7 @@ #include <coreplugin/iversioncontrol.h> #include <coreplugin/vcsmanager.h> +#include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> #include <utils/stringutils.h> @@ -45,6 +46,8 @@ #include <QPushButton> #include <QRadioButton> +using namespace Utils; + namespace Core { namespace Internal { @@ -65,8 +68,8 @@ public: NumberOfColumns }; - void initDialog(const QStringList &fileNames); - void promptFailWarning(const QStringList &files, ReadOnlyFilesDialog::ReadOnlyResult type) const; + void initDialog(const FilePathList &filePaths); + void promptFailWarning(const FilePathList &files, ReadOnlyFilesDialog::ReadOnlyResult type) const; QRadioButton *createRadioButtonForItem(QTreeWidgetItem *item, QButtonGroup *group, ReadOnlyFilesTreeColumn type); void setAll(int index); @@ -77,14 +80,14 @@ public: // Buttongroups containing the operation for one file. struct ButtonGroupForFile { - QString fileName; + FilePath filePath; QButtonGroup *group; }; QList <ButtonGroupForFile> buttonGroups; QMap <int, int> setAllIndexForOperation; // The version control systems for every file, if the file isn't in VCS the value is 0. - QHash <QString, IVersionControl*> versionControls; + QHash<FilePath, IVersionControl*> versionControls; // Define if some specific operations should be allowed to make the files writable. const bool useSaveAs; @@ -140,19 +143,19 @@ using namespace Internal; * and Save As which is used to save the changes to a document in another file. */ -ReadOnlyFilesDialog::ReadOnlyFilesDialog(const QList<QString> &fileNames, QWidget *parent) +ReadOnlyFilesDialog::ReadOnlyFilesDialog(const Utils::FilePathList &filePaths, QWidget *parent) : QDialog(parent) , d(new ReadOnlyFilesDialogPrivate(this)) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - d->initDialog(fileNames); + d->initDialog(filePaths); } -ReadOnlyFilesDialog::ReadOnlyFilesDialog(const QString &fileName, QWidget *parent) +ReadOnlyFilesDialog::ReadOnlyFilesDialog(const Utils::FilePath &filePath, QWidget *parent) : QDialog(parent) , d(new ReadOnlyFilesDialogPrivate(this)) { - d->initDialog(QStringList(fileName)); + d->initDialog({filePath}); } ReadOnlyFilesDialog::ReadOnlyFilesDialog(IDocument *document, QWidget *parent, @@ -160,16 +163,16 @@ ReadOnlyFilesDialog::ReadOnlyFilesDialog(IDocument *document, QWidget *parent, : QDialog(parent) , d(new ReadOnlyFilesDialogPrivate(this, document, displaySaveAs)) { - d->initDialog(QStringList(document->filePath().toString())); + d->initDialog({document->filePath()}); } ReadOnlyFilesDialog::ReadOnlyFilesDialog(const QList<IDocument *> &documents, QWidget *parent) : QDialog(parent) , d(new ReadOnlyFilesDialogPrivate(this)) { - QStringList files; - foreach (IDocument *document, documents) - files << document->filePath().toString(); + FilePathList files; + for (IDocument *document : documents) + files << document->filePath(); d->initDialog(files); } @@ -202,7 +205,7 @@ void ReadOnlyFilesDialog::setShowFailWarning(bool show, const QString &warning) * Opens a message box with an error description according to the type. * \internal */ -void ReadOnlyFilesDialogPrivate::promptFailWarning(const QStringList &files, ReadOnlyFilesDialog::ReadOnlyResult type) const +void ReadOnlyFilesDialogPrivate::promptFailWarning(const FilePathList &files, ReadOnlyFilesDialog::ReadOnlyResult type) const { if (files.isEmpty()) return; @@ -210,7 +213,7 @@ void ReadOnlyFilesDialogPrivate::promptFailWarning(const QStringList &files, Rea QString message; QString details; if (files.count() == 1) { - const QString file = files.first(); + const FilePath file = files.first(); switch (type) { case ReadOnlyFilesDialog::RO_OpenVCS: { if (IVersionControl *vc = versionControls[file]) { @@ -218,31 +221,31 @@ void ReadOnlyFilesDialogPrivate::promptFailWarning(const QStringList &files, Rea title = tr("Failed to %1 File").arg(openText); message = tr("%1 file %2 from version control system %3 failed.") .arg(openText) - .arg(QDir::toNativeSeparators(file)) + .arg(file.toUserOutput()) .arg(vc->displayName()) - + QLatin1Char('\n') + + '\n' + failWarning; } else { title = tr("No Version Control System Found"); message = tr("Cannot open file %1 from version control system.\n" "No version control system found.") - .arg(QDir::toNativeSeparators(file)) - + QLatin1Char('\n') - + failWarning;; + .arg(file.toUserOutput()) + + '\n' + + failWarning; } break; } case ReadOnlyFilesDialog::RO_MakeWritable: title = tr("Cannot Set Permissions"); message = tr("Cannot set permissions for %1 to writable.") - .arg(QDir::toNativeSeparators(file)) - + QLatin1Char('\n') + .arg(file.toUserOutput()) + + '\n' + failWarning; break; case ReadOnlyFilesDialog::RO_SaveAs: title = tr("Cannot Save File"); - message = tr("Cannot save file %1").arg(QDir::toNativeSeparators(file)) - + QLatin1Char('\n') + message = tr("Cannot save file %1").arg(file.toUserOutput()) + + '\n' + failWarning; break; default: @@ -254,7 +257,7 @@ void ReadOnlyFilesDialogPrivate::promptFailWarning(const QStringList &files, Rea title = tr("Could Not Change Permissions on Some Files"); message = failWarning + QLatin1Char('\n') + tr("See details for a complete list of files."); - details = files.join(QLatin1Char('\n')); + details = Utils::transform(files, &FilePath::toString).join('\n'); } QMessageBox msgBox(QMessageBox::Warning, title, message, QMessageBox::Ok, ICore::dialogParent()); @@ -278,34 +281,34 @@ int ReadOnlyFilesDialog::exec() return RO_Cancel; ReadOnlyResult result = RO_Cancel; - QStringList failedToMakeWritable; - foreach (ReadOnlyFilesDialogPrivate::ButtonGroupForFile buttongroup, d->buttonGroups) { + FilePathList failedToMakeWritable; + for (ReadOnlyFilesDialogPrivate::ButtonGroupForFile buttongroup : qAsConst(d->buttonGroups)) { result = static_cast<ReadOnlyResult>(buttongroup.group->checkedId()); switch (result) { case RO_MakeWritable: - if (!Utils::FileUtils::makeWritable(Utils::FileName(QFileInfo(buttongroup.fileName)))) { - failedToMakeWritable << buttongroup.fileName; + if (!Utils::FileUtils::makeWritable(buttongroup.filePath)) { + failedToMakeWritable << buttongroup.filePath; continue; } break; case RO_OpenVCS: - if (!d->versionControls[buttongroup.fileName]->vcsOpen(buttongroup.fileName)) { - failedToMakeWritable << buttongroup.fileName; + if (!d->versionControls[buttongroup.filePath]->vcsOpen(buttongroup.filePath.toString())) { + failedToMakeWritable << buttongroup.filePath; continue; } break; case RO_SaveAs: if (!EditorManagerPrivate::saveDocumentAs(d->document)) { - failedToMakeWritable << buttongroup.fileName; + failedToMakeWritable << buttongroup.filePath; continue; } break; default: - failedToMakeWritable << buttongroup.fileName; + failedToMakeWritable << buttongroup.filePath; continue; } - if (!QFileInfo(buttongroup.fileName).isWritable()) - failedToMakeWritable << buttongroup.fileName; + if (!buttongroup.filePath.toFileInfo().isWritable()) + failedToMakeWritable << buttongroup.filePath; } if (!failedToMakeWritable.isEmpty()) { if (d->showWarnings) @@ -381,11 +384,11 @@ void ReadOnlyFilesDialogPrivate::updateSelectAll() /*! * Adds files to the dialog and checks for a possible operation to make the file * writable. - * \a fileNames contains the list of the files that should be added to the + * \a filePaths contains the list of the files that should be added to the * dialog. * \internal */ -void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) +void ReadOnlyFilesDialogPrivate::initDialog(const FilePathList &filePaths) { ui.setupUi(q); ui.buttonBox->addButton(tr("Change &Permission"), QDialogButtonBox::AcceptRole); @@ -394,16 +397,16 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) QString vcsOpenTextForAll; QString vcsMakeWritableTextForAll; bool useMakeWritable = false; - foreach (const QString &fileName, fileNames) { - const QFileInfo info = QFileInfo(fileName); + for (const FilePath &filePath : filePaths) { + const QFileInfo info = filePath.toFileInfo(); const QString visibleName = info.fileName(); const QString directory = info.absolutePath(); // Setup a default entry with filename folder and make writable radio button. auto item = new QTreeWidgetItem(ui.treeWidget); item->setText(FileName, visibleName); - item->setIcon(FileName, FileIconProvider::icon(fileName)); - item->setText(Folder, Utils::FileUtils::shortNativePath(Utils::FileName(QFileInfo(directory)))); + item->setIcon(FileName, FileIconProvider::icon(info)); + item->setText(Folder, Utils::FilePath::fromFileInfo(directory).shortNativePath()); auto radioButtonGroup = new QButtonGroup; // Add a button for opening the file with a version control system @@ -411,7 +414,7 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) IVersionControl *versionControlForFile = VcsManager::findVersionControlForDirectory(directory); const bool fileManagedByVCS = versionControlForFile - && versionControlForFile->openSupportMode(fileName) != IVersionControl::NoOpen; + && versionControlForFile->openSupportMode(filePath.toString()) != IVersionControl::NoOpen; if (fileManagedByVCS) { const QString vcsOpenTextForFile = Utils::stripAccelerator(versionControlForFile->vcsOpenText()); @@ -429,7 +432,7 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) vcsMakeWritableTextForAll.clear(); } // Add make writable if it is supported by the reposetory. - if (versionControlForFile->openSupportMode(fileName) == IVersionControl::OpenOptional) { + if (versionControlForFile->openSupportMode(filePath.toString()) == IVersionControl::OpenOptional) { useMakeWritable = true; createRadioButtonForItem(item, radioButtonGroup, MakeWritable); } @@ -442,15 +445,12 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) if (useSaveAs) createRadioButtonForItem(item, radioButtonGroup, SaveAs); // If the file is managed by a version control system save the vcs for this file. - versionControls[fileName] = fileManagedByVCS ? versionControlForFile : nullptr; + versionControls[filePath] = fileManagedByVCS ? versionControlForFile : nullptr; // Also save the buttongroup for every file to get the result for each entry. - ReadOnlyFilesDialogPrivate::ButtonGroupForFile groupForFile; - groupForFile.fileName = fileName; - groupForFile.group = radioButtonGroup; - buttonGroups.append(groupForFile); - QObject::connect(radioButtonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), - [this](int) { updateSelectAll(); }); + buttonGroups.append({filePath, radioButtonGroup}); + QObject::connect(radioButtonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), + [this] { updateSelectAll(); }); } // Apply the Mac file dialog style. @@ -474,7 +474,7 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) } // If there is just one file entry, there is no need to show the select all combo box - if (fileNames.count() < 2) { + if (filePaths.count() < 2) { ui.setAll->setVisible(false); ui.setAllLabel->setVisible(false); ui.verticalLayout->removeItem(ui.setAllLayout); @@ -508,7 +508,7 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames) ui.setAll->addItem(saveAsText); setAllIndexForOperation[SaveAs] = ui.setAll->count() - 1; } - QObject::connect(ui.setAll, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + QObject::connect(ui.setAll, QOverload<int>::of(&QComboBox::activated), [this](int index) { setAll(index); }); // Filter which columns should be visible and resize them to content. diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h index 6bb9afa7f5..2baf2fa883 100644 --- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h +++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h @@ -27,6 +27,8 @@ #include <coreplugin/core_global.h> +#include <utils/fileutils.h> + #include <QDialog> namespace Core { @@ -57,9 +59,9 @@ public: RO_SaveAs = SaveAs }; - explicit ReadOnlyFilesDialog(const QList<QString> &fileNames, + explicit ReadOnlyFilesDialog(const Utils::FilePathList &filePaths, QWidget *parent = nullptr); - explicit ReadOnlyFilesDialog(const QString &fileName, + explicit ReadOnlyFilesDialog(const Utils::FilePath &filePath, QWidget * parent = nullptr); explicit ReadOnlyFilesDialog(IDocument *document, QWidget * parent = nullptr, diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp index f3e476fed3..94c0bbddc1 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp @@ -81,7 +81,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, << visibleName << QDir::toNativeSeparators(directory)); if (!fileName.isEmpty()) item->setIcon(0, FileIconProvider::icon(fileName)); - item->setData(0, Qt::UserRole, qVariantFromValue(document)); + item->setData(0, Qt::UserRole, QVariant::fromValue(document)); } m_ui.treeWidget->resizeColumnToContents(0); diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.h b/src/plugins/coreplugin/dialogs/saveitemsdialog.h index 37d321fade..f62af24768 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.h +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.h @@ -36,7 +36,6 @@ class IDocument; namespace Internal { - class SaveItemsDialog : public QDialog { Q_OBJECT diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp index 596d498875..8057a9e48a 100644 --- a/src/plugins/coreplugin/dialogs/settingsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp @@ -726,7 +726,7 @@ void SettingsDialog::done(int val) QSettings *settings = ICore::settings(); settings->setValue(QLatin1String(pageKeyC), m_currentPage.toSetting()); - ICore::saveSettings(); // save all settings + ICore::saveSettings(ICore::SettingsDialogDone); // save all settings // exit event loops in reverse order of addition for (QEventLoop *eventLoop : m_eventLoops) diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp index 675996881d..d740b6ca20 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -514,7 +514,7 @@ void ShortcutSettingsWidget::initialize() if (s->m_cmd->defaultKeySequence() != s->m_key) setModified(item, true); - item->setData(0, Qt::UserRole, qVariantFromValue(s)); + item->setData(0, Qt::UserRole, QVariant::fromValue(s)); markCollisions(s); } diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index 13b5b42546..933b20bd3f 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -164,7 +164,7 @@ public: QFileSystemWatcher *m_linkWatcher = nullptr; // Delayed creation (only UNIX/if a link is seen). QString m_lastVisitedDirectory = QDir::currentPath(); QString m_defaultLocationForNewFiles; - FileName m_projectsDirectory; + FilePath m_projectsDirectory; // When we are calling into an IDocument // we don't want to receive a changed() // signal @@ -438,14 +438,14 @@ void DocumentManager::renamedFile(const QString &from, const QString &to) foreach (IDocument *document, documentsToRename) { d->m_blockedIDocument = document; removeFileInfo(document); - document->setFilePath(FileName::fromString(to)); + document->setFilePath(FilePath::fromString(to)); addFileInfo(document); d->m_blockedIDocument = nullptr; } emit m_instance->allDocumentsRenamed(from, to); } -void DocumentManager::filePathChanged(const FileName &oldName, const FileName &newName) +void DocumentManager::filePathChanged(const FilePath &oldName, const FilePath &newName) { auto doc = qobject_cast<IDocument *>(sender()); QTC_ASSERT(doc, return); @@ -961,7 +961,7 @@ bool DocumentManager::saveModifiedDocument(IDocument *document, const QString &m alwaysSaveMessage, alwaysSave, failedToClose); } -void DocumentManager::showFilePropertiesDialog(const FileName &filePath) +void DocumentManager::showFilePropertiesDialog(const FilePath &filePath) { FilePropertiesDialog properties(filePath); properties.exec(); @@ -1340,12 +1340,12 @@ void readSettings() } s->beginGroup(QLatin1String(directoryGroupC)); - const FileName settingsProjectDir = FileName::fromString(s->value(QLatin1String(projectDirectoryKeyC), + const FilePath settingsProjectDir = FilePath::fromString(s->value(QLatin1String(projectDirectoryKeyC), QString()).toString()); if (!settingsProjectDir.isEmpty() && settingsProjectDir.toFileInfo().isDir()) d->m_projectsDirectory = settingsProjectDir; else - d->m_projectsDirectory = FileName::fromString(PathChooser::homePath()); + d->m_projectsDirectory = FilePath::fromString(PathChooser::homePath()); d->m_useProjectsDirectory = s->value(QLatin1String(useProjectDirectoryKeyC), d->m_useProjectsDirectory).toBool(); @@ -1398,7 +1398,7 @@ void DocumentManager::setDefaultLocationForNewFiles(const QString &location) \sa setProjectsDirectory, setUseProjectsDirectory */ -FileName DocumentManager::projectsDirectory() +FilePath DocumentManager::projectsDirectory() { return d->m_projectsDirectory; } @@ -1410,7 +1410,7 @@ FileName DocumentManager::projectsDirectory() \sa projectsDirectory, useProjectsDirectory */ -void DocumentManager::setProjectsDirectory(const FileName &directory) +void DocumentManager::setProjectsDirectory(const FilePath &directory) { if (d->m_projectsDirectory != directory) { d->m_projectsDirectory = directory; diff --git a/src/plugins/coreplugin/documentmanager.h b/src/plugins/coreplugin/documentmanager.h index 911e46615a..92323efdc3 100644 --- a/src/plugins/coreplugin/documentmanager.h +++ b/src/plugins/coreplugin/documentmanager.h @@ -34,7 +34,7 @@ QT_BEGIN_NAMESPACE class QStringList; QT_END_NAMESPACE -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace Core { @@ -123,7 +123,7 @@ public: const QString &alwaysSaveMessage = QString(), bool *alwaysSave = nullptr, QList<IDocument *> *failedToClose = nullptr); - static void showFilePropertiesDialog(const Utils::FileName &filePath); + static void showFilePropertiesDialog(const Utils::FilePath &filePath); static QString fileDialogLastVisitedDirectory(); static void setFileDialogLastVisitedDirectory(const QString &); @@ -136,8 +136,8 @@ public: static bool useProjectsDirectory(); static void setUseProjectsDirectory(bool); - static Utils::FileName projectsDirectory(); - static void setProjectsDirectory(const Utils::FileName &directory); + static Utils::FilePath projectsDirectory(); + static void setProjectsDirectory(const Utils::FilePath &directory); /* Used to notify e.g. the code model to update the given files. Does *not* lead to any editors to reload or any other editor manager actions. */ @@ -151,7 +151,7 @@ signals: void allDocumentsRenamed(const QString &from, const QString &to); /// emitted if one document changed its name e.g. due to save as void documentRenamed(Core::IDocument *document, const QString &from, const QString &to); - void projectsDirectoryChanged(const Utils::FileName &directory); + void projectsDirectoryChanged(const Utils::FilePath &directory); private: explicit DocumentManager(QObject *parent); @@ -161,7 +161,7 @@ private: void checkForNewFileName(); void checkForReload(); void changedFile(const QString &file); - void filePathChanged(const Utils::FileName &oldName, const Utils::FileName &newName); + void filePathChanged(const Utils::FilePath &oldName, const Utils::FilePath &newName); friend class Core::Internal::MainWindow; friend class Core::Internal::DocumentManagerPrivate; diff --git a/src/plugins/coreplugin/editormanager/documentmodel.cpp b/src/plugins/coreplugin/editormanager/documentmodel.cpp index 4cdb055bfc..b3cec42210 100644 --- a/src/plugins/coreplugin/editormanager/documentmodel.cpp +++ b/src/plugins/coreplugin/editormanager/documentmodel.cpp @@ -43,7 +43,6 @@ #include <QSet> #include <QUrl> - static Core::Internal::DocumentModelPrivate *d; namespace Core { @@ -52,6 +51,10 @@ namespace Internal { namespace { bool compare(const DocumentModel::Entry *e1, const DocumentModel::Entry *e2) { + // Pinned files should go at the top. + if (e1->pinned != e2->pinned) + return e1->pinned; + const int cmp = e1->plainDisplayName().localeAwareCompare(e2->plainDisplayName()); return (cmp < 0) || (cmp == 0 && e1->fileName() < e2->fileName()); } @@ -98,7 +101,7 @@ int DocumentModelPrivate::rowCount(const QModelIndex &parent) const void DocumentModelPrivate::addEntry(DocumentModel::Entry *entry) { - const Utils::FileName fileName = entry->fileName(); + const Utils::FilePath fileName = entry->fileName(); QString fixedPath; if (!fileName.isEmpty()) fixedPath = DocumentManager::filePathKey(fileName.toString(), DocumentManager::ResolveLinks); @@ -111,7 +114,8 @@ void DocumentModelPrivate::addEntry(DocumentModel::Entry *entry) previousEntry->isSuspended = false; delete previousEntry->document; previousEntry->document = entry->document; - connect(previousEntry->document, &IDocument::changed, this, &DocumentModelPrivate::itemChanged); + connect(previousEntry->document, &IDocument::changed, + this, [this, document = previousEntry->document] { itemChanged(document); }); } delete entry; entry = nullptr; @@ -129,7 +133,9 @@ void DocumentModelPrivate::addEntry(DocumentModel::Entry *entry) disambiguateDisplayNames(entry); if (!fixedPath.isEmpty()) m_entryByFixedPath[fixedPath] = entry; - connect(entry->document, &IDocument::changed, this, &DocumentModelPrivate::itemChanged); + connect(entry->document, &IDocument::changed, this, [this, document = entry->document] { + itemChanged(document); + }); endInsertRows(); } @@ -163,7 +169,7 @@ bool DocumentModelPrivate::disambiguateDisplayNames(DocumentModel::Entry *entry) bool seenDups = false; for (int i = 0; i < dupsCount - 1; ++i) { DynamicEntry &e = dups[i]; - const Utils::FileName myFileName = e->document->filePath(); + const Utils::FilePath myFileName = e->document->filePath(); if (e->document->isTemporary() || myFileName.isEmpty() || count > 10) { // path-less entry, append number e.setNumberedName(++serial); @@ -172,7 +178,7 @@ bool DocumentModelPrivate::disambiguateDisplayNames(DocumentModel::Entry *entry) for (int j = i + 1; j < dupsCount; ++j) { DynamicEntry &e2 = dups[j]; if (e->displayName().compare(e2->displayName(), Utils::HostOsInfo::fileNameCaseSensitivity()) == 0) { - const Utils::FileName otherFileName = e2->document->filePath(); + const Utils::FilePath otherFileName = e2->document->filePath(); if (otherFileName.isEmpty()) continue; seenDups = true; @@ -196,13 +202,30 @@ bool DocumentModelPrivate::disambiguateDisplayNames(DocumentModel::Entry *entry) return true; } +void DocumentModelPrivate::setPinned(DocumentModel::Entry *entry, bool pinned) +{ + if (entry->pinned == pinned) + return; + + entry->pinned = pinned; + // Ensure that this entry is re-sorted in the list of open documents + // now that its pinned state has changed. + d->itemChanged(entry->document); +} + QIcon DocumentModelPrivate::lockedIcon() { const static QIcon icon = Utils::Icons::LOCKED.icon(); return icon; } -Utils::optional<int> DocumentModelPrivate::indexOfFilePath(const Utils::FileName &filePath) const +QIcon DocumentModelPrivate::pinnedIcon() +{ + const static QIcon icon = Utils::Icons::PINNED.icon(); + return icon; +} + +Utils::optional<int> DocumentModelPrivate::indexOfFilePath(const Utils::FilePath &filePath) const { if (filePath.isEmpty()) return Utils::nullopt; @@ -230,7 +253,7 @@ void DocumentModelPrivate::removeDocument(int idx) DocumentManager::ResolveLinks); m_entryByFixedPath.remove(fixedPath); } - disconnect(entry->document, &IDocument::changed, this, &DocumentModelPrivate::itemChanged); + disconnect(entry->document, &IDocument::changed, this, nullptr); disambiguateDisplayNames(entry); delete entry; } @@ -307,7 +330,11 @@ QVariant DocumentModelPrivate::data(const QModelIndex &index, int role) const return name; } case Qt::DecorationRole: - return entry->document->isFileReadOnly() ? lockedIcon() : QIcon(); + if (entry->document->isFileReadOnly()) + return lockedIcon(); + if (entry->pinned) + return pinnedIcon(); + return QIcon(); case Qt::ToolTipRole: return entry->fileName().isEmpty() ? entry->displayName() : entry->fileName().toUserOutput(); default: @@ -316,10 +343,8 @@ QVariant DocumentModelPrivate::data(const QModelIndex &index, int role) const return QVariant(); } -void DocumentModelPrivate::itemChanged() +void DocumentModelPrivate::itemChanged(IDocument *document) { - auto document = qobject_cast<IDocument *>(sender()); - const Utils::optional<int> idx = indexOfDocument(document); if (!idx) return; @@ -353,14 +378,19 @@ void DocumentModelPrivate::itemChanged() // Make sure the entries stay sorted: auto positions = positionEntry(m_entries, entry); if (positions.first >= 0 && positions.second >= 0) { - // Entry did move: remove and add it again. - beginRemoveRows(QModelIndex(), positions.first + 1, positions.first + 1); - m_entries.removeAt(positions.first); - endRemoveRows(); - - beginInsertRows(QModelIndex(), positions.second + 1, positions.second + 1); - m_entries.insert(positions.second, entry); - endInsertRows(); + // Entry did move: update its position. + + // Account for the <no document> entry. + static const int noDocumentEntryOffset = 1; + const int fromIndex = positions.first + noDocumentEntryOffset; + const int toIndex = positions.second + noDocumentEntryOffset; + // Account for the weird requirements of beginMoveRows(). + const int effectiveToIndex = toIndex > fromIndex ? toIndex + 1 : toIndex; + beginMoveRows(QModelIndex(), fromIndex, fromIndex, QModelIndex(), effectiveToIndex); + + m_entries.move(fromIndex - 1, toIndex - 1); + + endMoveRows(); } else { // Nothing to remove or add: The entry did not move. QTC_CHECK(positions.first == -1 && positions.second == -1); @@ -384,15 +414,18 @@ void DocumentModelPrivate::addEditor(IEditor *editor, bool *isNewDocument) } } -void DocumentModelPrivate::addSuspendedDocument(const QString &fileName, const QString &displayName, Id id) +DocumentModel::Entry *DocumentModelPrivate::addSuspendedDocument(const QString &fileName, + const QString &displayName, + Id id) { auto entry = new DocumentModel::Entry; entry->document = new IDocument; - entry->document->setFilePath(Utils::FileName::fromString(fileName)); + entry->document->setFilePath(Utils::FilePath::fromString(fileName)); entry->document->setPreferredDisplayName(displayName); entry->document->setId(id); entry->isSuspended = true; d->addEntry(entry); + return entry; } DocumentModel::Entry *DocumentModelPrivate::firstSuspendedEntry() @@ -433,15 +466,20 @@ void DocumentModelPrivate::removeEntry(DocumentModel::Entry *entry) d->removeDocument(index); } -void DocumentModelPrivate::removeAllSuspendedEntries() +void DocumentModelPrivate::removeAllSuspendedEntries(PinnedFileRemovalPolicy pinnedFileRemovalPolicy) { for (int i = d->m_entries.count()-1; i >= 0; --i) { - if (d->m_entries.at(i)->isSuspended) { - int row = i + 1/*<no document>*/; - d->beginRemoveRows(QModelIndex(), row, row); - delete d->m_entries.takeAt(i); - d->endRemoveRows(); - } + const DocumentModel::Entry *entry = d->m_entries.at(i); + if (!entry->isSuspended) + continue; + + if (pinnedFileRemovalPolicy == DoNotRemovePinnedFiles && entry->pinned) + continue; + + int row = i + 1/*<no document>*/; + d->beginRemoveRows(QModelIndex(), row, row); + delete d->m_entries.takeAt(i); + d->endRemoveRows(); } QSet<QString> displayNames; foreach (DocumentModel::Entry *entry, d->m_entries) { @@ -480,7 +518,8 @@ void DocumentModelPrivate::DynamicEntry::setNumberedName(int number) DocumentModel::Entry::Entry() : document(nullptr), - isSuspended(false) + isSuspended(false), + pinned(false) { } @@ -512,7 +551,7 @@ QAbstractItemModel *DocumentModel::model() return d; } -Utils::FileName DocumentModel::Entry::fileName() const +Utils::FilePath DocumentModel::Entry::fileName() const { return document->filePath(); } @@ -555,7 +594,7 @@ Utils::optional<int> DocumentModel::indexOfDocument(IDocument *document) return d->indexOfDocument(document); } -Utils::optional<int> DocumentModel::indexOfFilePath(const Utils::FileName &filePath) +Utils::optional<int> DocumentModel::indexOfFilePath(const Utils::FilePath &filePath) { return d->indexOfFilePath(filePath); } @@ -566,7 +605,7 @@ DocumentModel::Entry *DocumentModel::entryForDocument(IDocument *document) [&document](Entry *entry) { return entry->document == document; }); } -DocumentModel::Entry *DocumentModel::entryForFilePath(const Utils::FileName &filePath) +DocumentModel::Entry *DocumentModel::entryForFilePath(const Utils::FilePath &filePath) { const Utils::optional<int> index = d->indexOfFilePath(filePath); if (!index) @@ -581,7 +620,7 @@ QList<IDocument *> DocumentModel::openedDocuments() IDocument *DocumentModel::documentForFilePath(const QString &filePath) { - const Utils::optional<int> index = d->indexOfFilePath(Utils::FileName::fromString(filePath)); + const Utils::optional<int> index = d->indexOfFilePath(Utils::FilePath::fromString(filePath)); if (!index) return nullptr; return d->m_entries.at(*index)->document; diff --git a/src/plugins/coreplugin/editormanager/documentmodel.h b/src/plugins/coreplugin/editormanager/documentmodel.h index 09bb465272..72b4caeedb 100644 --- a/src/plugins/coreplugin/editormanager/documentmodel.h +++ b/src/plugins/coreplugin/editormanager/documentmodel.h @@ -53,14 +53,24 @@ public: struct CORE_EXPORT Entry { Entry(); ~Entry(); - Utils::FileName fileName() const; + Utils::FilePath fileName() const; QString displayName() const; QString plainDisplayName() const; QString uniqueDisplayName() const; Id id() const; IDocument *document; + // When an entry is suspended, it means that it is not in memory, + // and there is no IEditor for it and only a dummy IDocument. + // This is typically the case for files that have not been opened yet, + // but can also happen later after they have been opened. + // The related setting for this is found in: + // Options > Environment > System > Auto-suspend unmodified files bool isSuspended; + // The entry has been pinned, which means that it should stick to + // the top of any lists of open files, and that any actions that close + // files in bulk should not close this one. + bool pinned; }; static Entry *entryAtRow(int row); @@ -69,9 +79,9 @@ public: static int entryCount(); static QList<Entry *> entries(); static Utils::optional<int> indexOfDocument(IDocument *document); - static Utils::optional<int> indexOfFilePath(const Utils::FileName &filePath); + static Utils::optional<int> indexOfFilePath(const Utils::FilePath &filePath); static Entry *entryForDocument(IDocument *document); - static Entry *entryForFilePath(const Utils::FileName &filePath); + static Entry *entryForFilePath(const Utils::FilePath &filePath); static QList<IDocument *> openedDocuments(); static IDocument *documentForFilePath(const QString &filePath); diff --git a/src/plugins/coreplugin/editormanager/documentmodel_p.h b/src/plugins/coreplugin/editormanager/documentmodel_p.h index 10f8ebfb4d..e5d7fa2a2c 100644 --- a/src/plugins/coreplugin/editormanager/documentmodel_p.h +++ b/src/plugins/coreplugin/editormanager/documentmodel_p.h @@ -58,20 +58,30 @@ public: void addEntry(DocumentModel::Entry *entry); void removeDocument(int idx); - Utils::optional<int> indexOfFilePath(const Utils::FileName &filePath) const; + Utils::optional<int> indexOfFilePath(const Utils::FilePath &filePath) const; Utils::optional<int> indexOfDocument(IDocument *document) const; bool disambiguateDisplayNames(DocumentModel::Entry *entry); + static void setPinned(DocumentModel::Entry *entry, bool pinned); + static QIcon lockedIcon(); + static QIcon pinnedIcon(); static void addEditor(IEditor *editor, bool *isNewDocument); - static void addSuspendedDocument(const QString &fileName, const QString &displayName, Id id); + static DocumentModel::Entry *addSuspendedDocument(const QString &fileName, + const QString &displayName, + Id id); static DocumentModel::Entry *firstSuspendedEntry(); static DocumentModel::Entry *removeEditor(IEditor *editor); static void removeEntry(DocumentModel::Entry *entry); - static void removeAllSuspendedEntries(); + enum PinnedFileRemovalPolicy { + DoNotRemovePinnedFiles, + RemovePinnedFiles + }; + static void removeAllSuspendedEntries(PinnedFileRemovalPolicy pinnedFileRemovalPolicy + = RemovePinnedFiles); - void itemChanged(); + void itemChanged(IDocument *document); class DynamicEntry { diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 1dc2236873..f1073e6bb2 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -214,9 +214,10 @@ EditorManagerPrivate::EditorManagerPrivate(QObject *parent) : m_closeOtherDocumentsContextAction(new QAction(EditorManager::tr("Close Others"), this)), m_closeAllEditorsExceptVisibleContextAction(new QAction(EditorManager::tr("Close All Except Visible"), this)), m_openGraphicalShellAction(new QAction(FileUtils::msgGraphicalShellAction(), this)), - m_openTerminalAction(new QAction(FileUtils::msgTerminalAction(), this)), + m_openTerminalAction(new QAction(FileUtils::msgTerminalHereAction(), this)), m_findInDirectoryAction(new QAction(FileUtils::msgFindInDirectory(), this)), - m_filePropertiesAction(new QAction(tr("Properties..."), this)) + m_filePropertiesAction(new QAction(tr("Properties..."), this)), + m_pinAction(new QAction(tr("Pin"), this)) { d = this; } @@ -302,8 +303,7 @@ void EditorManagerPrivate::init() cmd = ActionManager::registerAction(m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext, true); cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W"))); mfile->addAction(cmd, Constants::G_FILE_CLOSE); - connect(m_closeAllEditorsAction, &QAction::triggered, - m_instance, []() { EditorManager::closeAllEditors(); }); + connect(m_closeAllEditorsAction, &QAction::triggered, m_instance, &EditorManager::closeAllDocuments); // Close All Others Action cmd = ActionManager::registerAction(m_closeOtherDocumentsAction, Constants::CLOSEOTHERS, editManagerContext, true); @@ -334,7 +334,7 @@ void EditorManagerPrivate::init() // Close XXX Context Actions connect(m_closeAllEditorsContextAction, &QAction::triggered, - m_instance, []() { EditorManager::closeAllEditors(); }); + m_instance, &EditorManager::closeAllDocuments); connect(m_closeCurrentEditorContextAction, &QAction::triggered, this, &EditorManagerPrivate::closeEditorFromContextMenu); connect(m_closeOtherDocumentsContextAction, &QAction::triggered, @@ -352,6 +352,7 @@ void EditorManagerPrivate::init() return; DocumentManager::showFilePropertiesDialog(d->m_contextMenuEntry->fileName()); }); + connect(m_pinAction, &QAction::triggered, this, &EditorManagerPrivate::togglePinned); // Goto Previous In History Action cmd = ActionManager::registerAction(m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext); @@ -537,7 +538,7 @@ bool EditorManagerPrivate::skipOpeningBigTextFile(const QString &filePath) messageBox.setText(text); messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No); messageBox.setDefaultButton(QDialogButtonBox::No); - messageBox.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question)); + messageBox.setIcon(QMessageBox::Question); messageBox.setCheckBoxVisible(true); messageBox.setCheckBoxText(CheckableMessageBox::msgDoNotAskAgain()); messageBox.exec(); @@ -607,7 +608,7 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const QString &fileN Utils::MimeType mimeType = Utils::mimeTypeForFile(fn); QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"), tr("Could not open \"%1\": Cannot open files of type \"%2\".") - .arg(FileName::fromString(realFn).toUserOutput(), mimeType.name()), + .arg(FilePath::fromString(realFn).toUserOutput(), mimeType.name()), QMessageBox::Ok, ICore::dialogParent()); msgbox.exec(); return nullptr; @@ -649,7 +650,7 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const QString &fileN tr("Could not open \"%1\" for reading. " "Either the file does not exist or you do not have " "the permissions to open it.") - .arg(FileName::fromString(realFn).toUserOutput()), + .arg(FilePath::fromString(realFn).toUserOutput()), QMessageBox::Ok, ICore::dialogParent()); msgbox.exec(); return nullptr; @@ -658,7 +659,7 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const QString &fileN if (errorString.isEmpty()) { errorString = tr("Could not open \"%1\": Unknown error.") - .arg(FileName::fromString(realFn).toUserOutput()); + .arg(FilePath::fromString(realFn).toUserOutput()); } QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"), errorString, QMessageBox::Open | QMessageBox::Cancel, ICore::mainWindow()); @@ -1206,12 +1207,14 @@ void EditorManagerPrivate::addEditor(IEditor *editor) bool isNewDocument = false; DocumentModelPrivate::addEditor(editor, &isNewDocument); if (isNewDocument) { - const bool isTemporary = editor->document()->isTemporary(); + IDocument *document = editor->document(); + const bool isTemporary = document->isTemporary(); const bool addWatcher = !isTemporary; - DocumentManager::addDocument(editor->document(), addWatcher); + DocumentManager::addDocument(document, addWatcher); if (!isTemporary) - DocumentManager::addToRecentFiles(editor->document()->filePath().toString(), - editor->document()->id()); + DocumentManager::addToRecentFiles(document->filePath().toString(), + document->id()); + emit m_instance->documentOpened(document); } emit m_instance->editorOpened(editor); QTimer::singleShot(0, d, &EditorManagerPrivate::autoSuspendDocuments); @@ -1222,9 +1225,11 @@ void EditorManagerPrivate::removeEditor(IEditor *editor, bool removeSuspendedEnt DocumentModel::Entry *entry = DocumentModelPrivate::removeEditor(editor); QTC_ASSERT(entry, return); if (entry->isSuspended) { - DocumentManager::removeDocument(editor->document()); + IDocument *document = editor->document(); + DocumentManager::removeDocument(document); if (removeSuspendedEntry) DocumentModelPrivate::removeEntry(entry); + emit m_instance->documentClosed(document); } ICore::removeContextObject(editor); } @@ -2213,8 +2218,13 @@ bool EditorManagerPrivate::saveDocumentAs(IDocument *document) void EditorManagerPrivate::closeAllEditorsExceptVisible() { - DocumentModelPrivate::removeAllSuspendedEntries(); + DocumentModelPrivate::removeAllSuspendedEntries(DocumentModelPrivate::DoNotRemovePinnedFiles); QList<IDocument *> documentsToClose = DocumentModel::openedDocuments(); + // Remove all pinned files from the list of files to close. + documentsToClose = Utils::filtered(documentsToClose, [](IDocument *document) { + DocumentModel::Entry *entry = DocumentModel::entryForDocument(document); + return !entry->pinned; + }); foreach (IEditor *editor, EditorManager::visibleEditors()) documentsToClose.removeAll(editor->document()); EditorManager::closeDocuments(documentsToClose, true); @@ -2296,11 +2306,20 @@ void EditorManagerPrivate::findInDirectory() { if (!d->m_contextMenuEntry || d->m_contextMenuEntry->fileName().isEmpty()) return; - const FileName path = d->m_contextMenuEntry->fileName(); + const FilePath path = d->m_contextMenuEntry->fileName(); emit m_instance->findOnFileSystemRequest( (path.toFileInfo().isDir() ? path : path.parentDir()).toString()); } +void EditorManagerPrivate::togglePinned() +{ + if (!d->m_contextMenuEntry || d->m_contextMenuEntry->fileName().isEmpty()) + return; + + const bool currentlyPinned = d->m_contextMenuEntry->pinned; + DocumentModelPrivate::setPinned(d->m_contextMenuEntry, !currentlyPinned); +} + void EditorManagerPrivate::split(Qt::Orientation orientation) { EditorView *view = currentEditorView(); @@ -2399,12 +2418,25 @@ bool EditorManager::closeAllEditors(bool askAboutModifiedEditors) void EditorManager::closeOtherDocuments(IDocument *document) { - DocumentModelPrivate::removeAllSuspendedEntries(); + DocumentModelPrivate::removeAllSuspendedEntries(DocumentModelPrivate::DoNotRemovePinnedFiles); QList<IDocument *> documentsToClose = DocumentModel::openedDocuments(); + // Remove all pinned files from the list of files to close. + documentsToClose = Utils::filtered(documentsToClose, [](IDocument *document) { + DocumentModel::Entry *entry = DocumentModel::entryForDocument(document); + return !entry->pinned; + }); documentsToClose.removeAll(document); closeDocuments(documentsToClose, true); } +bool EditorManager::closeAllDocuments() +{ + // Only close the files that aren't pinned. + const QList<DocumentModel::Entry *> entriesToClose + = Utils::filtered(DocumentModel::entries(), Utils::equal(&DocumentModel::Entry::pinned, false)); + return EditorManager::closeDocuments(entriesToClose); +} + // SLOT connected to action void EditorManager::slotCloseCurrentEditorOrDocument() { @@ -2435,7 +2467,7 @@ void EditorManager::addSaveAndCloseEditorActions(QMenu *contextMenu, DocumentMod d->m_contextMenuEntry = entry; d->m_contextMenuEditor = editor; - const FileName filePath = entry ? entry->fileName() : FileName(); + const FilePath filePath = entry ? entry->fileName() : FilePath(); const bool copyActionsEnabled = !filePath.isEmpty(); d->m_copyFilePathContextAction->setEnabled(copyActionsEnabled); d->m_copyLocationContextAction->setEnabled(copyActionsEnabled); @@ -2486,6 +2518,20 @@ void EditorManager::addSaveAndCloseEditorActions(QMenu *contextMenu, DocumentMod contextMenu->addAction(d->m_closeAllEditorsExceptVisibleContextAction); } +void EditorManager::addPinEditorActions(QMenu *contextMenu, DocumentModel::Entry *entry) +{ + const QString quotedDisplayName = entry ? Utils::quoteAmpersands(entry->displayName()) : QString(); + if (entry) { + d->m_pinAction->setText(entry->pinned + ? tr("Unpin \"%1\"").arg(quotedDisplayName) + : tr("Pin \"%1\"").arg(quotedDisplayName)); + } else { + d->m_pinAction->setText(tr("Pin Editor")); + } + d->m_pinAction->setEnabled(entry != nullptr); + contextMenu->addAction(d->m_pinAction); +} + void EditorManager::addNativeDirAndOpenWithActions(QMenu *contextMenu, DocumentModel::Entry *entry) { QTC_ASSERT(contextMenu, return); @@ -2585,6 +2631,20 @@ void EditorManager::closeDocument(DocumentModel::Entry *entry) closeDocuments({entry->document}); } +bool EditorManager::closeDocuments(const QList<DocumentModel::Entry *> &entries) +{ + QList<IDocument *> documentsToClose; + for (DocumentModel::Entry *entry : entries) { + if (!entry) + continue; + if (entry->isSuspended) + DocumentModelPrivate::removeEntry(entry); + else + documentsToClose << entry->document; + } + return closeDocuments(documentsToClose); +} + bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors) { return EditorManagerPrivate::closeEditors(editorsToClose, @@ -2904,13 +2964,13 @@ QVector<EditorWindow *> editorWindows(const QList<EditorArea *> &areas) return result; } -// Save state of all non-teporary editors. +// Save state of all non-temporary editors. QByteArray EditorManager::saveState() { QByteArray bytes; QDataStream stream(&bytes, QIODevice::WriteOnly); - stream << QByteArray("EditorManagerV4"); + stream << QByteArray("EditorManagerV5"); // TODO: In case of split views it's not possible to restore these for all correctly with this QList<IDocument *> documents = DocumentModel::openedDocuments(); @@ -2936,8 +2996,10 @@ QByteArray EditorManager::saveState() stream << entriesCount; foreach (DocumentModel::Entry *entry, entries) { - if (!entry->document->isTemporary()) - stream << entry->fileName().toString() << entry->plainDisplayName() << entry->id(); + if (!entry->document->isTemporary()) { + stream << entry->fileName().toString() << entry->plainDisplayName() << entry->id() + << entry->pinned; + } } stream << d->m_editorAreas.first()->saveState(); // TODO @@ -2962,7 +3024,8 @@ bool EditorManager::restoreState(const QByteArray &state) QByteArray version; stream >> version; - if (version != "EditorManagerV4") + const bool isVersion5 = version == "EditorManagerV5"; + if (version != "EditorManagerV4" && !isVersion5) return false; QApplication::setOverrideCursor(Qt::WaitCursor); @@ -2978,16 +3041,22 @@ bool EditorManager::restoreState(const QByteArray &state) stream >> displayName; Id id; stream >> id; + bool pinned = false; + if (isVersion5) + stream >> pinned; if (!fileName.isEmpty() && !displayName.isEmpty()) { QFileInfo fi(fileName); if (!fi.exists()) continue; QFileInfo rfi(autoSaveName(fileName)); - if (rfi.exists() && fi.lastModified() < rfi.lastModified()) - openEditor(fileName, id, DoNotMakeVisible); - else - DocumentModelPrivate::addSuspendedDocument(fileName, displayName, id); + if (rfi.exists() && fi.lastModified() < rfi.lastModified()) { + if (IEditor *editor = openEditor(fileName, id, DoNotMakeVisible)) + DocumentModelPrivate::setPinned(DocumentModel::entryForDocument(editor->document()), pinned); + } else { + if (DocumentModel::Entry *entry = DocumentModelPrivate::addSuspendedDocument(fileName, displayName, id)) + DocumentModelPrivate::setPinned(entry, pinned); + } } } diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 6cd9dd88a1..5be379ceb2 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -128,7 +128,9 @@ public: static bool closeDocument(IDocument *document, bool askAboutModifiedEditors = true); static bool closeDocuments(const QList<IDocument *> &documents, bool askAboutModifiedEditors = true); static void closeDocument(DocumentModel::Entry *entry); + static bool closeDocuments(const QList<DocumentModel::Entry *> &entries); static void closeOtherDocuments(IDocument *document); + static bool closeAllDocuments(); static void addCurrentPositionToNavigationHistory(const QByteArray &saveState = QByteArray()); static void cutForwardNavigationHistory(); @@ -161,6 +163,7 @@ public: static void addSaveAndCloseEditorActions(QMenu *contextMenu, DocumentModel::Entry *entry, IEditor *editor = nullptr); + static void addPinEditorActions(QMenu *contextMenu, DocumentModel::Entry *entry); static void addNativeDirAndOpenWithActions(QMenu *contextMenu, DocumentModel::Entry *entry); static void populateOpenWithMenu(QMenu *menu, const QString &fileName); @@ -174,10 +177,12 @@ signals: void documentStateChanged(Core::IDocument *document); void editorCreated(Core::IEditor *editor, const QString &fileName); void editorOpened(Core::IEditor *editor); + void documentOpened(Core::IDocument *document); void editorAboutToClose(Core::IEditor *editor); void editorsClosed(QList<Core::IEditor *> editors); + void documentClosed(Core::IDocument *document); void findOnFileSystemRequest(const QString &path); - void openFileProperties(const Utils::FileName &path); + void openFileProperties(const Utils::FilePath &path); void aboutToSave(IDocument *document); void saved(IDocument *document); void autoSaved(); diff --git a/src/plugins/coreplugin/editormanager/editormanager_p.h b/src/plugins/coreplugin/editormanager/editormanager_p.h index b9e021bcb8..4dd7f62e31 100644 --- a/src/plugins/coreplugin/editormanager/editormanager_p.h +++ b/src/plugins/coreplugin/editormanager/editormanager_p.h @@ -180,6 +180,8 @@ private: static void openTerminal(); static void findInDirectory(); + static void togglePinned(); + static void removeCurrentSplit(); static void setCurrentEditorFromContextChange(); @@ -251,6 +253,7 @@ private: QAction *m_openTerminalAction; QAction *m_findInDirectoryAction; QAction *m_filePropertiesAction = nullptr; + QAction *m_pinAction = nullptr; DocumentModel::Entry *m_contextMenuEntry = nullptr; IEditor *m_contextMenuEditor = nullptr; diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp index aea929a3aa..d38923be38 100644 --- a/src/plugins/coreplugin/editormanager/editorview.cpp +++ b/src/plugins/coreplugin/editormanager/editorview.cpp @@ -268,7 +268,7 @@ void EditorView::updateEditorHistory(IEditor *editor, QList<EditLocation> &histo const EditLocation &item = history.at(i); if (item.document == document || (!item.document - && !DocumentModel::indexOfFilePath(FileName::fromString(item.fileName)))) { + && !DocumentModel::indexOfFilePath(FilePath::fromString(item.fileName)))) { history.removeAt(i--); } } diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.cpp b/src/plugins/coreplugin/editormanager/openeditorsview.cpp index 234fe28e5a..a467e089be 100644 --- a/src/plugins/coreplugin/editormanager/openeditorsview.cpp +++ b/src/plugins/coreplugin/editormanager/openeditorsview.cpp @@ -115,10 +115,12 @@ void OpenEditorsWidget::contextMenuRequested(QPoint pos) { QMenu contextMenu; QModelIndex editorIndex = indexAt(pos); - DocumentModel::Entry *entry = DocumentModel::entryAtRow( - m_model->mapToSource(editorIndex).row()); + const int row = m_model->mapToSource(editorIndex).row(); + DocumentModel::Entry *entry = DocumentModel::entryAtRow(row); EditorManager::addSaveAndCloseEditorActions(&contextMenu, entry); contextMenu.addSeparator(); + EditorManager::addPinEditorActions(&contextMenu, entry); + contextMenu.addSeparator(); EditorManager::addNativeDirAndOpenWithActions(&contextMenu, entry); contextMenu.exec(mapToGlobal(pos)); } diff --git a/src/plugins/coreplugin/editormanager/openeditorswindow.cpp b/src/plugins/coreplugin/editormanager/openeditorswindow.cpp index d766531f51..ad1775f9d2 100644 --- a/src/plugins/coreplugin/editormanager/openeditorswindow.cpp +++ b/src/plugins/coreplugin/editormanager/openeditorswindow.cpp @@ -217,7 +217,7 @@ static DocumentModel::Entry *entryForEditLocation(const EditLocation &item) { if (!item.document.isNull()) return DocumentModel::entryForDocument(item.document); - return DocumentModel::entryForFilePath(Utils::FileName::fromString(item.fileName)); + return DocumentModel::entryForFilePath(Utils::FilePath::fromString(item.fileName)); } void OpenEditorsWindow::addHistoryItems(const QList<EditLocation> &history, EditorView *view, diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp index c23aaa49d2..9d19415e45 100644 --- a/src/plugins/coreplugin/editortoolbar.cpp +++ b/src/plugins/coreplugin/editortoolbar.cpp @@ -128,7 +128,6 @@ EditorToolBar::EditorToolBar(QWidget *parent) : d->m_defaultToolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); d->m_activeToolBar = d->m_defaultToolBar; - d->m_lockButton->setAutoRaise(true); d->m_lockButton->setEnabled(false); d->m_dragHandle->setProperty("noArrow", true); @@ -149,7 +148,6 @@ EditorToolBar::EditorToolBar(QWidget *parent) : d->m_editorList->setMaxVisibleItems(40); d->m_editorList->setContextMenuPolicy(Qt::CustomContextMenu); - d->m_closeEditorButton->setAutoRaise(true); d->m_closeEditorButton->setIcon(Utils::Icons::CLOSE_TOOLBAR.icon()); d->m_closeEditorButton->setEnabled(false); d->m_closeEditorButton->setProperty("showborder", true); @@ -170,7 +168,6 @@ EditorToolBar::EditorToolBar(QWidget *parent) : splitMenu->addAction(d->m_splitNewWindowAction); d->m_splitButton->setMenu(splitMenu); - d->m_closeSplitButton->setAutoRaise(true); d->m_closeSplitButton->setIcon(Utils::Icons::CLOSE_SPLIT_BOTTOM.icon()); auto toplayout = new QHBoxLayout(this); @@ -190,7 +187,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) : // this signal is disconnected for standalone toolbars and replaced with // a private slot connection - connect(d->m_editorList, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + connect(d->m_editorList, QOverload<int>::of(&QComboBox::activated), this, &EditorToolBar::listSelectionActivated); connect(d->m_editorList, &QComboBox::customContextMenuRequested, [this](QPoint p) { @@ -303,9 +300,9 @@ void EditorToolBar::setToolbarCreationFlags(ToolbarCreationFlags flags) this, &EditorToolBar::setCurrentEditor); - disconnect(d->m_editorList, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + disconnect(d->m_editorList, QOverload<int>::of(&QComboBox::activated), this, &EditorToolBar::listSelectionActivated); - connect(d->m_editorList, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + connect(d->m_editorList, QOverload<int>::of(&QComboBox::activated), this, &EditorToolBar::changeActiveEditor); d->m_splitButton->setVisible(false); d->m_closeSplitButton->setVisible(false); @@ -347,6 +344,8 @@ void EditorToolBar::fillListContextMenu(QMenu *menu) : nullptr; EditorManager::addSaveAndCloseEditorActions(menu, entry, editor); menu->addSeparator(); + EditorManager::addPinEditorActions(menu, entry); + menu->addSeparator(); EditorManager::addNativeDirAndOpenWithActions(menu, entry); } } diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index 15f7a97660..b339dddd3d 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -654,7 +654,7 @@ void ExternalToolRunner::run() } m_process = new QtcProcess(this); connect(m_process, &QProcess::started, this, &ExternalToolRunner::started); - connect(m_process, static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &ExternalToolRunner::finished); connect(m_process, &QProcess::errorOccurred, this, &ExternalToolRunner::error); connect(m_process, &QProcess::readyReadStandardOutput, @@ -663,7 +663,7 @@ void ExternalToolRunner::run() this, &ExternalToolRunner::readStandardError); if (!m_resolvedWorkingDirectory.isEmpty()) m_process->setWorkingDirectory(m_resolvedWorkingDirectory); - m_process->setCommand(m_resolvedExecutable.toString(), m_resolvedArguments); + m_process->setCommand(CommandLine(m_resolvedExecutable, m_resolvedArguments)); m_process->setEnvironment(m_resolvedEnvironment); MessageManager::write(tr("Starting external tool \"%1\" %2") .arg(m_resolvedExecutable.toUserOutput(), m_resolvedArguments), diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h index b41ae450f2..394ee091a5 100644 --- a/src/plugins/coreplugin/externaltool.h +++ b/src/plugins/coreplugin/externaltool.h @@ -145,7 +145,7 @@ private: bool resolve(); const ExternalTool *m_tool; // is a copy of the tool that was passed in - Utils::FileName m_resolvedExecutable; + Utils::FilePath m_resolvedExecutable; QString m_resolvedArguments; QString m_resolvedInput; QString m_resolvedWorkingDirectory; diff --git a/src/plugins/coreplugin/fancyactionbar.cpp b/src/plugins/coreplugin/fancyactionbar.cpp index 8d496ea3d2..d332817f62 100644 --- a/src/plugins/coreplugin/fancyactionbar.cpp +++ b/src/plugins/coreplugin/fancyactionbar.cpp @@ -102,19 +102,19 @@ static QVector<QString> splitInTwoLines(const QString &text, nextSplitPos = rx.lastIndexIn(text, nextSplitPos - text.length() - 1); if (nextSplitPos != -1) { int splitCandidate = nextSplitPos + rx.matchedLength(); - if (fontMetrics.width(text.mid(splitCandidate)) <= availableWidth) + if (fontMetrics.horizontalAdvance(text.mid(splitCandidate)) <= availableWidth) splitPos = splitCandidate; else break; } - } while (nextSplitPos > 0 && fontMetrics.width(text.left(nextSplitPos)) > availableWidth); + } while (nextSplitPos > 0 && fontMetrics.horizontalAdvance(text.left(nextSplitPos)) > availableWidth); // check if we could split at white space at all if (splitPos < 0) { splitLines[0] = fontMetrics.elidedText(text, Qt::ElideRight, int(availableWidth)); QString common = Utils::commonPrefix(QStringList({splitLines[0], text})); splitLines[1] = text.mid(common.length()); // elide the second line even if it fits, since it is cut off in mid-word - while (fontMetrics.width(QChar(0x2026) /*'...'*/ + splitLines[1]) > availableWidth + while (fontMetrics.horizontalAdvance(QChar(0x2026) /*'...'*/ + splitLines[1]) > availableWidth && splitLines[1].length() > 3 /*keep at least three original characters (should not happen)*/) { splitLines[1].remove(0, 1); @@ -222,7 +222,7 @@ void FancyToolButton::paintEvent(QPaintEvent *event) painter.setFont(boldFont); QVector<QString> splitBuildConfiguration(2); const QString buildConfiguration = defaultAction()->property("subtitle").toString(); - if (boldFm.width(buildConfiguration) <= availableWidth) + if (boldFm.horizontalAdvance(buildConfiguration) <= availableWidth) // text fits in one line splitBuildConfiguration[0] = buildConfiguration; else diff --git a/src/plugins/coreplugin/fancytabwidget.cpp b/src/plugins/coreplugin/fancytabwidget.cpp index 0653d8639c..684b47bd15 100644 --- a/src/plugins/coreplugin/fancytabwidget.cpp +++ b/src/plugins/coreplugin/fancytabwidget.cpp @@ -98,7 +98,7 @@ QSize FancyTabBar::tabSizeHint(bool minimum) const const int width = 60 + spacing + 2; int maxLabelwidth = 0; for (auto tab : qAsConst(m_tabs)) { - const int width = fm.width(tab->text); + const int width = fm.horizontalAdvance(tab->text); if (width > maxLabelwidth) maxLabelwidth = width; } diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp index 157dde7477..223cc70cf6 100644 --- a/src/plugins/coreplugin/fileutils.cpp +++ b/src/plugins/coreplugin/fileutils.cpp @@ -69,7 +69,7 @@ void FileUtils::showInGraphicalShell(QWidget *parent, const QString &pathIn) const QFileInfo fileInfo(pathIn); // Mac, Windows support folder or file. if (HostOsInfo::isWindowsHost()) { - const FileName explorer = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe")); + const FilePath explorer = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe")); if (explorer.isEmpty()) { QMessageBox::warning(parent, QApplication::translate("Core::Internal", @@ -135,13 +135,20 @@ QString FileUtils::msgGraphicalShellAction() return QApplication::translate("Core::Internal", "Show Containing Folder"); } -QString FileUtils::msgTerminalAction() +QString FileUtils::msgTerminalHereAction() { if (HostOsInfo::isWindowsHost()) return QApplication::translate("Core::Internal", "Open Command Prompt Here"); return QApplication::translate("Core::Internal", "Open Terminal Here"); } +QString FileUtils::msgTerminalWithAction() +{ + if (HostOsInfo::isWindowsHost()) + return QApplication::translate("Core::Internal", "Open Command Prompt With"); + return QApplication::translate("Core::Internal", "Open Terminal With"); +} + void FileUtils::removeFile(const QString &filePath, bool deleteFromFS) { // remove from version control diff --git a/src/plugins/coreplugin/fileutils.h b/src/plugins/coreplugin/fileutils.h index c7746251c1..0067d1d728 100644 --- a/src/plugins/coreplugin/fileutils.h +++ b/src/plugins/coreplugin/fileutils.h @@ -44,7 +44,8 @@ struct CORE_EXPORT FileUtils static QString msgFindInDirectory(); // Platform-dependent action descriptions static QString msgGraphicalShellAction(); - static QString msgTerminalAction(); + static QString msgTerminalHereAction(); + static QString msgTerminalWithAction(); // File operations aware of version control and file system case-insensitiveness static void removeFile(const QString &filePath, bool deleteFromFS); static bool renameFile(const QString &from, const QString &to); diff --git a/src/plugins/coreplugin/find/find.pri b/src/plugins/coreplugin/find/find.pri index 41005912fb..811f20a5c0 100644 --- a/src/plugins/coreplugin/find/find.pri +++ b/src/plugins/coreplugin/find/find.pri @@ -8,6 +8,7 @@ HEADERS += \ $$PWD/ifindfilter.h \ $$PWD/ifindsupport.h \ $$PWD/itemviewfind.h \ + $$PWD/optionspopup.h \ $$PWD/searchresultcolor.h \ $$PWD/searchresulttreeitemdelegate.h \ $$PWD/searchresulttreeitemroles.h \ @@ -29,6 +30,7 @@ SOURCES += \ $$PWD/ifindfilter.cpp \ $$PWD/ifindsupport.cpp \ $$PWD/itemviewfind.cpp \ + $$PWD/optionspopup.cpp \ $$PWD/searchresulttreeitemdelegate.cpp \ $$PWD/searchresulttreeitems.cpp \ $$PWD/searchresulttreemodel.cpp \ diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp index 4307ba388d..72a2d23e3c 100644 --- a/src/plugins/coreplugin/find/findtoolbar.cpp +++ b/src/plugins/coreplugin/find/findtoolbar.cpp @@ -26,6 +26,7 @@ #include "findtoolbar.h" #include "ifindfilter.h" #include "findplugin.h" +#include "optionspopup.h" #include <coreplugin/coreicons.h> #include <coreplugin/coreplugin.h> @@ -118,8 +119,7 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind) this, &FindToolBar::invokeFindEnter, Qt::QueuedConnection); connect(m_ui.replaceEdit, &Utils::FancyLineEdit::returnPressed, this, &FindToolBar::invokeReplaceEnter, Qt::QueuedConnection); - connect(m_findCompleter, - static_cast<void (QCompleter::*)(const QModelIndex &)>(&QCompleter::activated), + connect(m_findCompleter, QOverload<const QModelIndex &>::of(&QCompleter::activated), this, &FindToolBar::findCompleterActivated); auto shiftEnterAction = new QAction(m_ui.findEdit); @@ -660,7 +660,8 @@ void FindToolBar::findFlagsChanged() void FindToolBar::findEditButtonClicked() { - auto popup = new OptionsPopup(m_ui.findEdit); + auto popup = new OptionsPopup(m_ui.findEdit, {Constants::CASE_SENSITIVE, Constants::WHOLE_WORDS, + Constants::REGULAR_EXPRESSIONS, Constants::PRESERVE_CASE}); popup->show(); } @@ -1015,71 +1016,5 @@ void FindToolBar::updateReplaceEnabled() m_replacePreviousAction->setEnabled(globalsEnabled); } -OptionsPopup::OptionsPopup(QWidget *parent) - : QWidget(parent, Qt::Popup) -{ - setAttribute(Qt::WA_DeleteOnClose); - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(2, 2, 2, 2); - layout->setSpacing(2); - setLayout(layout); - QCheckBox *firstCheckBox = createCheckboxForCommand(Constants::CASE_SENSITIVE); - layout->addWidget(firstCheckBox); - layout->addWidget(createCheckboxForCommand(Constants::WHOLE_WORDS)); - layout->addWidget(createCheckboxForCommand(Constants::REGULAR_EXPRESSIONS)); - layout->addWidget(createCheckboxForCommand(Constants::PRESERVE_CASE)); - firstCheckBox->setFocus(); - move(parent->mapToGlobal(QPoint(0, -sizeHint().height()))); -} - -bool OptionsPopup::event(QEvent *ev) -{ - if (ev->type() == QEvent::ShortcutOverride) { - auto ke = static_cast<QKeyEvent *>(ev); - if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { - ev->accept(); - return true; - } - } - return QWidget::event(ev); -} - -bool OptionsPopup::eventFilter(QObject *obj, QEvent *ev) -{ - auto checkbox = qobject_cast<QCheckBox *>(obj); - if (ev->type() == QEvent::KeyPress && checkbox) { - auto ke = static_cast<QKeyEvent *>(ev); - if (!ke->modifiers() && (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return)) { - checkbox->click(); - ev->accept(); - return true; - } - } - return QWidget::eventFilter(obj, ev); -} - -void OptionsPopup::actionChanged() -{ - auto action = qobject_cast<QAction *>(sender()); - QTC_ASSERT(action, return); - QCheckBox *checkbox = m_checkboxMap.value(action); - QTC_ASSERT(checkbox, return); - checkbox->setEnabled(action->isEnabled()); -} - -QCheckBox *OptionsPopup::createCheckboxForCommand(Id id) -{ - QAction *action = ActionManager::command(id)->action(); - QCheckBox *checkbox = new QCheckBox(action->text()); - checkbox->setToolTip(action->toolTip()); - checkbox->setChecked(action->isChecked()); - checkbox->setEnabled(action->isEnabled()); - checkbox->installEventFilter(this); // enter key handling - QObject::connect(checkbox, &QCheckBox::clicked, action, &QAction::setChecked); - QObject::connect(action, &QAction::changed, this, &OptionsPopup::actionChanged); - m_checkboxMap.insert(action, checkbox); - return checkbox; -} - } // namespace Internal } // namespace Core diff --git a/src/plugins/coreplugin/find/findtoolbar.h b/src/plugins/coreplugin/find/findtoolbar.h index 8d6a9b77a4..f2fb494905 100644 --- a/src/plugins/coreplugin/find/findtoolbar.h +++ b/src/plugins/coreplugin/find/findtoolbar.h @@ -43,25 +43,6 @@ class FindToolBarPlaceHolder; namespace Internal { -class OptionsPopup : public QWidget -{ - Q_OBJECT - -public: - explicit OptionsPopup(QWidget *parent); - -protected: - bool event(QEvent *ev) override; - bool eventFilter(QObject *obj, QEvent *ev) override; - -private: - void actionChanged(); - - QCheckBox *createCheckboxForCommand(Id id); - - QMap<QAction *, QCheckBox *> m_checkboxMap; -}; - class FindToolBar : public Utils::StyledBar { Q_OBJECT diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp index 04f138a151..0490e8481f 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.cpp +++ b/src/plugins/coreplugin/find/findtoolwindow.cpp @@ -77,14 +77,13 @@ FindToolWindow::FindToolWindow(QWidget *parent) connect(m_ui.matchCase, &QAbstractButton::toggled, Find::instance(), &Find::setCaseSensitive); connect(m_ui.wholeWords, &QAbstractButton::toggled, Find::instance(), &Find::setWholeWord); connect(m_ui.regExp, &QAbstractButton::toggled, Find::instance(), &Find::setRegularExpression); - connect(m_ui.filterList, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), - this, static_cast<void (FindToolWindow::*)(int)>(&FindToolWindow::setCurrentFilter)); + connect(m_ui.filterList, QOverload<int>::of(&QComboBox::activated), + this, QOverload<int>::of(&FindToolWindow::setCurrentFilter)); m_findCompleter->setModel(Find::findCompletionModel()); m_ui.searchTerm->setSpecialCompleter(m_findCompleter); m_ui.searchTerm->installEventFilter(this); - connect(m_findCompleter, - static_cast<void (QCompleter::*)(const QModelIndex &)>(&QCompleter::activated), + connect(m_findCompleter, QOverload<const QModelIndex &>::of(&QCompleter::activated), this, &FindToolWindow::findCompleterActivated); m_ui.searchTerm->setValidationFunction(validateRegExp); diff --git a/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp b/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp index 2ea32798de..b9d07b70da 100644 --- a/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp +++ b/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp @@ -92,7 +92,7 @@ void HighlightScrollBarOverlay::scheduleUpdate() return; m_isCacheUpdateScheduled = true; - QTimer::singleShot(0, this, static_cast<void (QWidget::*)()>(&QWidget::update)); + QTimer::singleShot(0, this, QOverload<>::of(&QWidget::update)); } void HighlightScrollBarOverlay::paintEvent(QPaintEvent *paintEvent) diff --git a/src/plugins/coreplugin/find/optionspopup.cpp b/src/plugins/coreplugin/find/optionspopup.cpp new file mode 100644 index 0000000000..d7274f87e0 --- /dev/null +++ b/src/plugins/coreplugin/find/optionspopup.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "optionspopup.h" + +#include <coreplugin/actionmanager/actionmanager.h> + +#include <utils/qtcassert.h> + +#include <QAction> +#include <QCheckBox> +#include <QEvent> +#include <QKeyEvent> +#include <QVBoxLayout> + +namespace Core { + +OptionsPopup::OptionsPopup(QWidget *parent, const QVector<Id> &commands) + : QWidget(parent, Qt::Popup) +{ + setAttribute(Qt::WA_DeleteOnClose); + auto layout = new QVBoxLayout(this); + layout->setContentsMargins(2, 2, 2, 2); + layout->setSpacing(2); + setLayout(layout); + + bool first = true; + for (const Id &command : commands) { + QCheckBox *checkBox = createCheckboxForCommand(command); + if (first) { + checkBox->setFocus(); + first = false; + } + layout->addWidget(checkBox); + } + move(parent->mapToGlobal(QPoint(0, -sizeHint().height()))); +} + +bool OptionsPopup::event(QEvent *ev) +{ + if (ev->type() == QEvent::ShortcutOverride) { + auto ke = static_cast<QKeyEvent *>(ev); + if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { + ev->accept(); + return true; + } + } + return QWidget::event(ev); +} + +bool OptionsPopup::eventFilter(QObject *obj, QEvent *ev) +{ + auto checkbox = qobject_cast<QCheckBox *>(obj); + if (ev->type() == QEvent::KeyPress && checkbox) { + auto ke = static_cast<QKeyEvent *>(ev); + if (!ke->modifiers() && (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return)) { + checkbox->click(); + ev->accept(); + return true; + } + } + return QWidget::eventFilter(obj, ev); +} + +void OptionsPopup::actionChanged() +{ + auto action = qobject_cast<QAction *>(sender()); + QTC_ASSERT(action, return); + QCheckBox *checkbox = m_checkboxMap.value(action); + QTC_ASSERT(checkbox, return); + checkbox->setEnabled(action->isEnabled()); +} + +QCheckBox *OptionsPopup::createCheckboxForCommand(Id id) +{ + QAction *action = ActionManager::command(id)->action(); + QCheckBox *checkbox = new QCheckBox(action->text()); + checkbox->setToolTip(action->toolTip()); + checkbox->setChecked(action->isChecked()); + checkbox->setEnabled(action->isEnabled()); + checkbox->installEventFilter(this); // enter key handling + QObject::connect(checkbox, &QCheckBox::clicked, action, &QAction::setChecked); + QObject::connect(action, &QAction::changed, this, &OptionsPopup::actionChanged); + m_checkboxMap.insert(action, checkbox); + return checkbox; +} + +} // namespace Core diff --git a/src/plugins/coreplugin/find/optionspopup.h b/src/plugins/coreplugin/find/optionspopup.h new file mode 100644 index 0000000000..003280eb36 --- /dev/null +++ b/src/plugins/coreplugin/find/optionspopup.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <coreplugin/id.h> + +#include <QMap> +#include <QWidget> + +QT_BEGIN_NAMESPACE +class QAction; +class QCheckBox; +QT_END_NAMESPACE + +namespace Core { + +class CORE_EXPORT OptionsPopup : public QWidget +{ + Q_OBJECT + +public: + OptionsPopup(QWidget *parent, const QVector<Id> &commands); + +protected: + bool event(QEvent *ev) override; + bool eventFilter(QObject *obj, QEvent *ev) override; + +private: + void actionChanged(); + + QCheckBox *createCheckboxForCommand(Id id); + + QMap<QAction *, QCheckBox *> m_checkboxMap; +}; + +} // namespace Core diff --git a/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp index 81ec2541c6..9351387c5b 100644 --- a/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp +++ b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp @@ -40,59 +40,101 @@ SearchResultTreeItemDelegate::SearchResultTreeItemDelegate(int tabWidth, QObject setTabWidth(tabWidth); } -void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - static const int iconSize = 16; +const int lineNumberAreaHorizontalPadding = 4; +const int minimumLineNumberDigits = 6; - painter->save(); +static std::pair<int, QString> lineNumberInfo(const QStyleOptionViewItem &option, + const QModelIndex &index) +{ + const int lineNumber = index.data(ItemDataRoles::ResultBeginLineNumberRole).toInt(); + if (lineNumber < 1) + return {0, {}}; + const QString lineNumberText = QString::number(lineNumber); + const int lineNumberDigits = qMax(minimumLineNumberDigits, lineNumberText.count()); + const int fontWidth = option.fontMetrics.horizontalAdvance(QString(lineNumberDigits, QLatin1Char('0'))); + const QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + return {lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding + + style->pixelMetric(QStyle::PM_FocusFrameHMargin), + lineNumberText}; +} - QStyleOptionViewItem opt = setOptions(index, option); - painter->setFont(opt.font); +static QString itemText(const QModelIndex &index) +{ + const QString text = index.data(Qt::DisplayRole).toString(); + // show number of subresults in displayString + if (index.model()->hasChildren(index)) { + return text + QLatin1String(" (") + QString::number(index.model()->rowCount(index)) + + QLatin1Char(')'); + } + return text; +} - QItemDelegate::drawBackground(painter, opt, index); +LayoutInfo SearchResultTreeItemDelegate::getLayoutInfo(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + static const int iconSize = 16; - // ---- do the layout - QRect checkRect; - QRect pixmapRect; - QRect textRect; + LayoutInfo info; + info.option = setOptions(index, option); // check mark - bool checkable = (index.model()->flags(index) & Qt::ItemIsUserCheckable); - Qt::CheckState checkState = Qt::Unchecked; + const bool checkable = (index.model()->flags(index) & Qt::ItemIsUserCheckable); + info.checkState = Qt::Unchecked; if (checkable) { QVariant checkStateData = index.data(Qt::CheckStateRole); - checkState = static_cast<Qt::CheckState>(checkStateData.toInt()); - checkRect = doCheck(opt, opt.rect, checkStateData); + info.checkState = static_cast<Qt::CheckState>(checkStateData.toInt()); + info.checkRect = doCheck(info.option, info.option.rect, checkStateData); } // icon - QIcon icon = index.model()->data(index, ItemDataRoles::ResultIconRole).value<QIcon>(); - if (!icon.isNull()) { - const QSize size = icon.actualSize(QSize(iconSize, iconSize)); - pixmapRect = QRect(0, 0, size.width(), size.height()); + info.icon = index.data(ItemDataRoles::ResultIconRole).value<QIcon>(); + if (!info.icon.isNull()) { + const QSize size = info.icon.actualSize(QSize(iconSize, iconSize)); + info.pixmapRect = QRect(0, 0, size.width(), size.height()); } // text - textRect = opt.rect.adjusted(0, 0, checkRect.width() + pixmapRect.width(), 0); + info.textRect = info.option.rect.adjusted(0, + 0, + info.checkRect.width() + info.pixmapRect.width(), + 0); + + // do basic layout + doLayout(info.option, &info.checkRect, &info.pixmapRect, &info.textRect, false); + + // adapt for line numbers + const int lineNumberWidth = lineNumberInfo(info.option, index).first; + info.lineNumberRect = info.textRect; + info.lineNumberRect.setWidth(lineNumberWidth); + info.textRect.adjust(lineNumberWidth, 0, 0, 0); + return info; +} + +void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + painter->save(); + + const LayoutInfo info = getLayoutInfo(option, index); + + painter->setFont(info.option.font); + + QItemDelegate::drawBackground(painter, info.option, index); - // do layout - doLayout(opt, &checkRect, &pixmapRect, &textRect, false); // ---- draw the items // icon - if (!icon.isNull()) - icon.paint(painter, pixmapRect, option.decorationAlignment); + if (!info.icon.isNull()) + info.icon.paint(painter, info.pixmapRect, info.option.decorationAlignment); // line numbers - int lineNumberAreaWidth = drawLineNumber(painter, opt, textRect, index); - textRect.adjust(lineNumberAreaWidth, 0, 0, 0); + drawLineNumber(painter, info.option, info.lineNumberRect, index); // text and focus/selection - drawText(painter, opt, textRect, index); - QItemDelegate::drawFocus(painter, opt, opt.rect); + drawText(painter, info.option, info.textRect, index); + QItemDelegate::drawFocus(painter, info.option, info.option.rect); // check mark - if (checkable) - QItemDelegate::drawCheck(painter, opt, checkRect, checkState); + if (info.checkRect.isValid()) + QItemDelegate::drawCheck(painter, info.option, info.checkRect, info.checkState); painter->restore(); } @@ -102,22 +144,31 @@ void SearchResultTreeItemDelegate::setTabWidth(int width) m_tabString = QString(width, QLatin1Char(' ')); } +QSize SearchResultTreeItemDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + const LayoutInfo info = getLayoutInfo(option, index); + const int height = index.data(Qt::SizeHintRole).value<QSize>().height(); + // get text width, see QItemDelegatePrivate::displayRect + const QString text = itemText(index).replace('\t', m_tabString); + const QRect textMaxRect(0, 0, INT_MAX / 256, height); + const QRect textLayoutRect = textRectangle(nullptr, textMaxRect, info.option.font, text); + const QRect textRect(info.textRect.x(), info.textRect.y(), textLayoutRect.width(), height); + const QRect layoutRect = info.checkRect | info.pixmapRect | info.lineNumberRect | textRect; + return QSize(layoutRect.x(), layoutRect.y()) + layoutRect.size(); +} + // returns the width of the line number area int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QModelIndex &index) const { - static const int lineNumberAreaHorizontalPadding = 4; - int lineNumber = index.model()->data(index, ItemDataRoles::ResultBeginLineNumberRole).toInt(); - if (lineNumber < 1) - return 0; const bool isSelected = option.state & QStyle::State_Selected; - QString lineText = QString::number(lineNumber); - int minimumLineNumberDigits = qMax((int)m_minimumLineNumberDigits, lineText.count()); - int fontWidth = painter->fontMetrics().width(QString(minimumLineNumberDigits, QLatin1Char('0'))); - int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding; + const std::pair<int, QString> numberInfo = lineNumberInfo(option, index); + if (numberInfo.first == 0) + return 0; QRect lineNumberAreaRect(rect); - lineNumberAreaRect.setWidth(lineNumberAreaWidth); + lineNumberAreaRect.setWidth(numberInfo.first); QPalette::ColorGroup cg = QPalette::Normal; if (!(option.state & QStyle::State_Active)) @@ -137,9 +188,9 @@ int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyle const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, nullptr) + 1; const QRect rowRect = lineNumberAreaRect.adjusted(-textMargin, 0, textMargin-lineNumberAreaHorizontalPadding, 0); - QItemDelegate::drawDisplay(painter, opt, rowRect, lineText); + QItemDelegate::drawDisplay(painter, opt, rowRect, numberInfo.second); - return lineNumberAreaWidth; + return numberInfo.first; } void SearchResultTreeItemDelegate::drawText(QPainter *painter, @@ -147,18 +198,15 @@ void SearchResultTreeItemDelegate::drawText(QPainter *painter, const QRect &rect, const QModelIndex &index) const { - QString text = index.model()->data(index, Qt::DisplayRole).toString(); - // show number of subresults in displayString - if (index.model()->hasChildren(index)) { - text += QLatin1String(" (") - + QString::number(index.model()->rowCount(index)) - + QLatin1Char(')'); - } + const QString text = itemText(index); const int searchTermStart = index.model()->data(index, ItemDataRoles::ResultBeginColumnNumberRole).toInt(); int searchTermLength = index.model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt(); if (searchTermStart < 0 || searchTermStart >= text.length() || searchTermLength < 1) { - QItemDelegate::drawDisplay(painter, option, rect, text.replace(QLatin1Char('\t'), m_tabString)); + QItemDelegate::drawDisplay(painter, + option, + rect, + QString(text).replace(QLatin1Char('\t'), m_tabString)); return; } @@ -168,8 +216,8 @@ void SearchResultTreeItemDelegate::drawText(QPainter *painter, const QString textBefore = text.left(searchTermStart).replace(QLatin1Char('\t'), m_tabString); const QString textHighlight = text.mid(searchTermStart, searchTermLength).replace(QLatin1Char('\t'), m_tabString); const QString textAfter = text.mid(searchTermStart + searchTermLength).replace(QLatin1Char('\t'), m_tabString); - int searchTermStartPixels = painter->fontMetrics().width(textBefore); - int searchTermLengthPixels = painter->fontMetrics().width(textHighlight); + int searchTermStartPixels = painter->fontMetrics().horizontalAdvance(textBefore); + int searchTermLengthPixels = painter->fontMetrics().horizontalAdvance(textHighlight); // rects QRect beforeHighlightRect(rect); diff --git a/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h index 0551e4c8d7..edb13e98ed 100644 --- a/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h +++ b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h @@ -30,20 +30,31 @@ namespace Core { namespace Internal { +struct LayoutInfo +{ + QRect checkRect; + QRect pixmapRect; + QRect textRect; + QRect lineNumberRect; + QIcon icon; + Qt::CheckState checkState; + QStyleOptionViewItem option; +}; + class SearchResultTreeItemDelegate: public QItemDelegate { public: SearchResultTreeItemDelegate(int tabWidth, QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setTabWidth(int width); - + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: + LayoutInfo getLayoutInfo(const QStyleOptionViewItem &option, const QModelIndex &index) const; int drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QModelIndex &index) const; void drawText(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QModelIndex &index) const; QString m_tabString; - static const int m_minimumLineNumberDigits = 6; }; } // namespace Internal diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.cpp b/src/plugins/coreplugin/find/searchresulttreemodel.cpp index 150e9dcc09..3fba0f899d 100644 --- a/src/plugins/coreplugin/find/searchresulttreemodel.cpp +++ b/src/plugins/coreplugin/find/searchresulttreemodel.cpp @@ -258,7 +258,7 @@ QVariant SearchResultTreeModel::data(const SearchResultTreeItem *row, int role) result = row->item.text; break; case ItemDataRoles::ResultItemRole: - result = qVariantFromValue(row->item); + result = QVariant::fromValue(row->item); break; case ItemDataRoles::ResultBeginLineNumberRole: result = row->item.mainRange.begin.line; diff --git a/src/plugins/coreplugin/find/searchresulttreeview.cpp b/src/plugins/coreplugin/find/searchresulttreeview.cpp index 0ca09fcefd..48d1845053 100644 --- a/src/plugins/coreplugin/find/searchresulttreeview.cpp +++ b/src/plugins/coreplugin/find/searchresulttreeview.cpp @@ -44,6 +44,8 @@ SearchResultTreeView::SearchResultTreeView(QWidget *parent) setIndentation(14); setUniformRowHeights(true); setExpandsOnDoubleClick(true); + header()->setSectionResizeMode(QHeaderView::ResizeToContents); + header()->setStretchLastSection(false); header()->hide(); connect(this, &SearchResultTreeView::activated, @@ -93,6 +95,13 @@ void SearchResultTreeView::keyPressEvent(QKeyEvent *event) TreeView::keyPressEvent(event); } +bool SearchResultTreeView::event(QEvent *e) +{ + if (e->type() == QEvent::Resize) + header()->setMinimumSectionSize(width()); + return TreeView::event(e); +} + void SearchResultTreeView::emitJumpToSearchResult(const QModelIndex &index) { if (model()->data(index, ItemDataRoles::IsGeneratedRole).toBool()) diff --git a/src/plugins/coreplugin/find/searchresulttreeview.h b/src/plugins/coreplugin/find/searchresulttreeview.h index eb11d29a7d..4809a6887b 100644 --- a/src/plugins/coreplugin/find/searchresulttreeview.h +++ b/src/plugins/coreplugin/find/searchresulttreeview.h @@ -50,6 +50,7 @@ public: void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode); void keyPressEvent(QKeyEvent *event) override; + bool event(QEvent *e) override; signals: void jumpToSearchResult(const SearchResultItem &item); diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp index 9428a31a1d..5827885802 100644 --- a/src/plugins/coreplugin/find/searchresultwidget.cpp +++ b/src/plugins/coreplugin/find/searchresultwidget.cpp @@ -72,8 +72,8 @@ public: QSize sizeHint() const override { QSize sh = QLineEdit::minimumSizeHint(); - sh.rwidth() += qMax(25 * fontMetrics().width(QLatin1Char('x')), - fontMetrics().width(text())); + sh.rwidth() += qMax(25 * fontMetrics().horizontalAdvance(QLatin1Char('x')), + fontMetrics().horizontalAdvance(text())); return sh; } }; diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp index 7cea0166d1..ec723c8928 100644 --- a/src/plugins/coreplugin/find/searchresultwindow.cpp +++ b/src/plugins/coreplugin/find/searchresultwindow.cpp @@ -124,7 +124,7 @@ namespace Internal { m_recentSearchesBox->setProperty("drawleftborder", true); m_recentSearchesBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_recentSearchesBox->addItem(tr("New Search")); - connect(m_recentSearchesBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + connect(m_recentSearchesBox, QOverload<int>::of(&QComboBox::activated), this, &SearchResultWindowPrivate::setCurrentIndexWithFocus); m_widget->setWindowTitle(q->displayName()); @@ -135,7 +135,6 @@ namespace Internal { m_widget->addWidget(newSearchArea); m_expandCollapseButton = new QToolButton(m_widget); - m_expandCollapseButton->setAutoRaise(true); m_expandCollapseAction->setCheckable(true); m_expandCollapseAction->setIcon(Utils::Icons::EXPAND_ALL_TOOLBAR.icon()); @@ -167,11 +166,13 @@ namespace Internal { if (focus) m_widget->currentWidget()->setFocus(); m_expandCollapseAction->setEnabled(false); + m_newSearchButton->setEnabled(false); } else { if (focus) m_searchResultWidgets.at(visibleSearchIndex())->setFocusInternally(); m_searchResultWidgets.at(visibleSearchIndex())->notifyVisibilityChanged(true); m_expandCollapseAction->setEnabled(true); + m_newSearchButton->setEnabled(true); } q->navigateStateChanged(); } diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index 9cb23af656..63eba664cc 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -33,9 +33,10 @@ #include <utils/qtcassert.h> -#include <QSysInfo> #include <QApplication> +#include <QDebug> #include <QStandardPaths> +#include <QSysInfo> /*! \namespace Core @@ -329,9 +330,11 @@ ICore::ICore(MainWindow *mainwindow) m_mainwindow = mainwindow; // Save settings once after all plugins are initialized: connect(PluginManager::instance(), &PluginManager::initializationDone, - this, &ICore::saveSettings); + this, [] { ICore::saveSettings(ICore::InitializationDone); }); connect(PluginManager::instance(), &PluginManager::testsFinished, [this] (int failedTests) { emit coreAboutToClose(); + if (failedTests != 0) + qWarning("Test run was not successful: %d test(s) failed.", failedTests); QCoreApplication::exit(failedTests); }); } @@ -687,9 +690,9 @@ void ICore::setupScreenShooter(const QString &name, QWidget *w, const QRect &rc) new ScreenShooter(w, name, rc); } -void ICore::saveSettings() +void ICore::saveSettings(SaveSettingsReason reason) { - emit m_instance->saveSettingsRequested(); + emit m_instance->saveSettingsRequested(reason); m_mainwindow->saveSettings(); ICore::settings(QSettings::SystemScope)->sync(); diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 5125345cff..04d501e134 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -141,14 +141,21 @@ public: static QString systemInformation(); static void setupScreenShooter(const QString &name, QWidget *w, const QRect &rc = QRect()); + enum SaveSettingsReason { + InitializationDone, + SettingsDialogDone, + ModeChanged, + MainWindowClosing, + }; + public slots: - static void saveSettings(); + static void saveSettings(SaveSettingsReason reason); signals: void coreAboutToOpen(); void coreOpened(); void newItemDialogStateChanged(); - void saveSettingsRequested(); + void saveSettingsRequested(SaveSettingsReason reason); void coreAboutToClose(); void contextAboutToChange(const QList<Core::IContext *> &context); void contextChanged(const Core::Context &context); diff --git a/src/plugins/coreplugin/idocument.cpp b/src/plugins/coreplugin/idocument.cpp index e5a78b32d5..aab9845f1a 100644 --- a/src/plugins/coreplugin/idocument.cpp +++ b/src/plugins/coreplugin/idocument.cpp @@ -69,7 +69,7 @@ public: } QString mimeType; - Utils::FileName filePath; + Utils::FilePath filePath; QString preferredDisplayName; QString uniqueDisplayName; QString autoSaveName; @@ -167,7 +167,7 @@ bool IDocument::setContents(const QByteArray &contents) return false; } -const Utils::FileName &IDocument::filePath() const +const Utils::FilePath &IDocument::filePath() const { return d->filePath; } @@ -322,11 +322,11 @@ InfoBar *IDocument::infoBar() signals. Can be reimplemented by subclasses to do more. \sa filePath() */ -void IDocument::setFilePath(const Utils::FileName &filePath) +void IDocument::setFilePath(const Utils::FilePath &filePath) { if (d->filePath == filePath) return; - Utils::FileName oldName = d->filePath; + Utils::FilePath oldName = d->filePath; d->filePath = filePath; emit filePathChanged(oldName, d->filePath); emit changed(); diff --git a/src/plugins/coreplugin/idocument.h b/src/plugins/coreplugin/idocument.h index 4af747dc97..feae466705 100644 --- a/src/plugins/coreplugin/idocument.h +++ b/src/plugins/coreplugin/idocument.h @@ -29,7 +29,7 @@ #include <QObject> -namespace Utils { class FileName; } +namespace Utils { class FilePath; } namespace Core { class Id; @@ -93,8 +93,8 @@ public: virtual QByteArray contents() const; virtual bool setContents(const QByteArray &contents); - const Utils::FileName &filePath() const; - virtual void setFilePath(const Utils::FileName &filePath); + const Utils::FilePath &filePath() const; + virtual void setFilePath(const Utils::FilePath &filePath); QString displayName() const; void setPreferredDisplayName(const QString &name); QString preferredDisplayName() const; @@ -144,7 +144,7 @@ signals: void aboutToReload(); void reloadFinished(bool success); - void filePathChanged(const Utils::FileName &oldName, const Utils::FileName &newName); + void filePathChanged(const Utils::FilePath &oldName, const Utils::FilePath &newName); private: Internal::IDocumentPrivate *d; diff --git a/src/plugins/coreplugin/infobar.h b/src/plugins/coreplugin/infobar.h index b65a821bcf..74a0f8136f 100644 --- a/src/plugins/coreplugin/infobar.h +++ b/src/plugins/coreplugin/infobar.h @@ -55,7 +55,6 @@ public: }; InfoBarEntry(Id _id, const QString &_infoText, GlobalSuppressionMode _globalSuppression = GlobalSuppressionDisabled); - InfoBarEntry(const InfoBarEntry &other) { *this = other; } using CallBack = std::function<void()>; void setCustomButtonInfo(const QString &_buttonText, CallBack callBack); diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h index 9ca6b1ef80..b7800a65c8 100644 --- a/src/plugins/coreplugin/ioutputpane.h +++ b/src/plugins/coreplugin/ioutputpane.h @@ -26,16 +26,23 @@ #pragma once #include "core_global.h" +#include "id.h" + +#include <utils/fancylineedit.h> #include <QObject> #include <QList> #include <QString> QT_BEGIN_NAMESPACE +class QAction; +class QToolButton; class QWidget; QT_END_NAMESPACE namespace Core { +class CommandButton; +class IContext; class CORE_EXPORT IOutputPane : public QObject { @@ -46,7 +53,7 @@ public: ~IOutputPane() override; virtual QWidget *outputWidget(QWidget *parent) = 0; - virtual QList<QWidget *> toolBarWidgets() const = 0; + virtual QList<QWidget *> toolBarWidgets() const; virtual QString displayName() const = 0; virtual int priorityInStatusBar() const = 0; @@ -64,6 +71,9 @@ public: virtual void goToNext() = 0; virtual void goToPrev() = 0; + void setFont(const QFont &font); + void setWheelZoomEnabled(bool enabled); + enum Flag { NoModeSwitch = 0, ModeSwitch = 1, WithFocus = 2, EnsureSizeHint = 4}; Q_DECLARE_FLAGS(Flags, Flag) @@ -83,6 +93,39 @@ signals: void navigateStateUpdate(); void flashButton(); void setBadgeNumber(int number); + void zoomIn(int range); + void zoomOut(int range); + void resetZoom(); + void wheelZoomEnabledChanged(bool enabled); + void fontChanged(const QFont &font); + +protected: + void setupFilterUi(const QString &historyKey); + QString filterText() const; + bool filterUsesRegexp() const { return m_filterRegexp; } + Qt::CaseSensitivity filterCaseSensitivity() const { return m_filterCaseSensitivity; } + void setFilteringEnabled(bool enable); + QWidget *filterWidget() const { return m_filterOutputLineEdit; } + void setupContext(const char *context, QWidget *widget); + void setZoomButtonsEnabled(bool enabled); + +private: + virtual void updateFilter(); + + void filterOutputButtonClicked(); + void setCaseSensitive(bool caseSensitive); + void setRegularExpressions(bool regularExpressions); + Id filterRegexpActionId() const; + Id filterCaseSensitivityActionId() const; + + Core::CommandButton * const m_zoomInButton; + Core::CommandButton * const m_zoomOutButton; + QAction *m_filterActionRegexp = nullptr; + QAction *m_filterActionCaseSensitive = nullptr; + Utils::FancyLineEdit *m_filterOutputLineEdit = nullptr; + IContext *m_context = nullptr; + bool m_filterRegexp = false; + Qt::CaseSensitivity m_filterCaseSensitivity = Qt::CaseInsensitive; }; } // namespace Core diff --git a/src/plugins/coreplugin/iversioncontrol.cpp b/src/plugins/coreplugin/iversioncontrol.cpp index befa60fbab..3bc7ddd6f8 100644 --- a/src/plugins/coreplugin/iversioncontrol.cpp +++ b/src/plugins/coreplugin/iversioncontrol.cpp @@ -79,7 +79,7 @@ QStringList IVersionControl::additionalToolsPath() const } ShellCommand *IVersionControl::createInitialCheckoutCommand(const QString &url, - const Utils::FileName &baseDirectory, + const Utils::FilePath &baseDirectory, const QString &localName, const QStringList &extraArgs) { diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h index cdb780f7fc..0846793b5a 100644 --- a/src/plugins/coreplugin/iversioncontrol.h +++ b/src/plugins/coreplugin/iversioncontrol.h @@ -103,7 +103,7 @@ public: * * This method needs to be thread safe! */ - virtual bool isVcsFileOrDirectory(const Utils::FileName &fileName) const = 0; + virtual bool isVcsFileOrDirectory(const Utils::FilePath &fileName) const = 0; /*! * Returns whether files in this directory should be managed with this @@ -213,7 +213,7 @@ public: * \a extraArgs are passed on to the command being run. */ virtual ShellCommand *createInitialCheckoutCommand(const QString &url, - const Utils::FileName &baseDirectory, + const Utils::FilePath &baseDirectory, const QString &localName, const QStringList &extraArgs); @@ -226,10 +226,10 @@ private: TopicCache *m_topicCache; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IVersionControl::SettingsFlags) - } // namespace Core +Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IVersionControl::SettingsFlags) + #if defined(WITH_TESTS) #include <QSet> @@ -245,7 +245,7 @@ public: { } ~TestVersionControl() override; - bool isVcsFileOrDirectory(const Utils::FileName &fileName) const final + bool isVcsFileOrDirectory(const Utils::FilePath &fileName) const final { Q_UNUSED(fileName); return false; } void setManagedDirectories(const QHash<QString, QString> &dirs); diff --git a/src/plugins/coreplugin/jsexpander.cpp b/src/plugins/coreplugin/jsexpander.cpp index effa3464cc..4ee72752e5 100644 --- a/src/plugins/coreplugin/jsexpander.cpp +++ b/src/plugins/coreplugin/jsexpander.cpp @@ -34,8 +34,26 @@ #include <QDebug> #include <QJSEngine> -namespace Core { +#include <unordered_map> + +namespace std { +template<> struct hash<QString> +{ + using argument_type = QString; + using result_type = size_t; + result_type operator()(const argument_type &v) const + { + return hash<string>()(v.toStdString()); + } +}; +} // namespace std + +using ExtensionMap = std::unordered_map<QString, Core::JsExpander::ObjectFactory>; +Q_GLOBAL_STATIC(ExtensionMap, globalJsExtensions); +static Core::JsExpander *globalExpander = nullptr; + +namespace Core { namespace Internal { class JsExpanderPrivate { @@ -45,9 +63,14 @@ public: } // namespace Internal -static Internal::JsExpanderPrivate *d; +void JsExpander::registerGlobalObject(const QString &name, const ObjectFactory &factory) +{ + globalJsExtensions->insert({name, factory}); + if (globalExpander) + globalExpander->registerObject(name, factory()); +} -void JsExpander::registerQObjectForJs(const QString &name, QObject *obj) +void JsExpander::registerObject(const QString &name, QObject *obj) { QJSValue jsObj = d->m_engine.newQObject(obj); d->m_engine.globalObject().setProperty(name, jsObj); @@ -77,16 +100,23 @@ QString JsExpander::evaluate(const QString &expression, QString *errorMessage) return QString(); } -JsExpander::JsExpander() +QJSEngine &JsExpander::engine() { - d = new Internal::JsExpanderPrivate; - Utils::globalMacroExpander()->registerPrefix("JS", + return d->m_engine; +} + +void JsExpander::registerForExpander(Utils::MacroExpander *macroExpander) +{ + macroExpander->registerPrefix( + "JS", QCoreApplication::translate("Core::JsExpander", "Evaluate simple JavaScript statements.<br>" - "The statements may not contain '{' nor '}' characters."), - [](QString in) -> QString { + "Literal '}' characters must be escaped as \"\\}\", " + "'\\' characters must be escaped as \"\\\\\", " + "and \"%{\" must be escaped as \"%\\{\"."), + [this](QString in) -> QString { QString errorMessage; - QString result = JsExpander::evaluate(in, &errorMessage); + QString result = evaluate(in, &errorMessage); if (!errorMessage.isEmpty()) { qWarning() << errorMessage; return errorMessage; @@ -94,8 +124,21 @@ JsExpander::JsExpander() return result; } }); +} - registerQObjectForJs(QLatin1String("Util"), new Internal::UtilsJsExtension); +JsExpander *JsExpander::createGlobalJsExpander() +{ + globalExpander = new JsExpander(); + registerGlobalObject<Internal::UtilsJsExtension>("Util"); + globalExpander->registerForExpander(Utils::globalMacroExpander()); + return globalExpander; +} + +JsExpander::JsExpander() +{ + d = new Internal::JsExpanderPrivate; + for (const std::pair<const QString, ObjectFactory> &obj : *globalJsExtensions) + registerObject(obj.first, obj.second()); } JsExpander::~JsExpander() diff --git a/src/plugins/coreplugin/jsexpander.h b/src/plugins/coreplugin/jsexpander.h index 087f494ac0..df8c6bceea 100644 --- a/src/plugins/coreplugin/jsexpander.h +++ b/src/plugins/coreplugin/jsexpander.h @@ -27,26 +27,51 @@ #include "core_global.h" +#include <functional> + QT_BEGIN_NAMESPACE +class QJSEngine; class QObject; class QString; QT_END_NAMESPACE +namespace Utils { +class MacroExpander; +} + namespace Core { -namespace Internal { class MainWindow; } +namespace Internal { +class MainWindow; +class JsExpanderPrivate; +} // namespace Internal class CORE_EXPORT JsExpander { public: - static void registerQObjectForJs(const QString &name, QObject *obj); - - static QString evaluate(const QString &expression, QString *errorMessage = nullptr); + using ObjectFactory = std::function<QObject *()>; -private: JsExpander(); ~JsExpander(); + template <class T> + static void registerGlobalObject(const QString &name) + { + registerGlobalObject(name, []{ return new T; }); + } + + static void registerGlobalObject(const QString &name, const ObjectFactory &factory); + + void registerObject(const QString &name, QObject *obj); + QString evaluate(const QString &expression, QString *errorMessage = nullptr); + + QJSEngine &engine(); + void registerForExpander(Utils::MacroExpander *macroExpander); + +private: + static JsExpander *createGlobalJsExpander(); + + Internal::JsExpanderPrivate *d; friend class Core::Internal::MainWindow; }; diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp index 220f6ff782..e2b26b57ae 100644 --- a/src/plugins/coreplugin/locator/basefilefilter.cpp +++ b/src/plugins/coreplugin/locator/basefilefilter.cpp @@ -153,7 +153,7 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil QFileInfo fi(path); LocatorFilterEntry filterEntry(this, fi.fileName(), QString(path + fp.postfix)); filterEntry.fileName = path; - filterEntry.extraInfo = FileUtils::shortNativePath(FileName(fi)); + filterEntry.extraInfo = FilePath::fromFileInfo(fi).shortNativePath(); const int matchLevel = matchLevelFor(match, matchText); if (hasPathSeparator) { diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp index 645bae42d3..02e7e59fe9 100644 --- a/src/plugins/coreplugin/locator/directoryfilter.cpp +++ b/src/plugins/coreplugin/locator/directoryfilter.cpp @@ -24,6 +24,9 @@ ****************************************************************************/ #include "directoryfilter.h" +#include "ui_directoryfilter.h" + +#include "locator.h" #include <coreplugin/coreconstants.h> #include <utils/algorithm.h> @@ -32,8 +35,7 @@ #include <QFileDialog> #include <QTimer> -using namespace Core; -using namespace Core::Internal; +namespace Core { DirectoryFilter::DirectoryFilter(Id id) : m_filters({"*.h", "*.cpp", "*.ui", "*.qrc"}), @@ -64,12 +66,13 @@ void DirectoryFilter::restoreState(const QByteArray &state) QMutexLocker locker(&m_lock); QString name; + QStringList directories; QString shortcut; bool defaultFilter; QDataStream in(state); in >> name; - in >> m_directories; + in >> directories; in >> m_filters; in >> shortcut; in >> defaultFilter; @@ -79,6 +82,8 @@ void DirectoryFilter::restoreState(const QByteArray &state) else m_exclusionFilters.clear(); + if (m_isCustomFilter) + m_directories = directories; setDisplayName(name); setShortcutString(shortcut); setIncludedByDefault(defaultFilter); @@ -88,60 +93,83 @@ void DirectoryFilter::restoreState(const QByteArray &state) bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh) { + if (!m_ui) { + m_ui = new Internal::Ui::DirectoryFilterOptions; + } bool success = false; QDialog dialog(parent); m_dialog = &dialog; - m_ui.setupUi(&dialog); + m_ui->setupUi(&dialog); dialog.setWindowTitle(ILocatorFilter::msgConfigureDialogTitle()); - m_ui.prefixLabel->setText(ILocatorFilter::msgPrefixLabel()); - m_ui.prefixLabel->setToolTip(ILocatorFilter::msgPrefixToolTip()); - m_ui.defaultFlag->setText(ILocatorFilter::msgIncludeByDefault()); - m_ui.defaultFlag->setToolTip(ILocatorFilter::msgIncludeByDefaultToolTip()); - connect(m_ui.addButton, &QPushButton::clicked, - this, &DirectoryFilter::addDirectory, Qt::DirectConnection); - connect(m_ui.editButton, &QPushButton::clicked, - this, &DirectoryFilter::editDirectory, Qt::DirectConnection); - connect(m_ui.removeButton, &QPushButton::clicked, - this, &DirectoryFilter::removeDirectory, Qt::DirectConnection); - connect(m_ui.directoryList, &QListWidget::itemSelectionChanged, - this, &DirectoryFilter::updateOptionButtons, Qt::DirectConnection); - m_ui.nameEdit->setText(displayName()); - m_ui.nameEdit->selectAll(); - m_ui.directoryList->clear(); - m_ui.directoryList->addItems(m_directories); - m_ui.filePatternLabel->setText(Utils::msgFilePatternLabel()); - m_ui.filePatternLabel->setBuddy(m_ui.filePattern); - m_ui.filePattern->setToolTip(Utils::msgFilePatternToolTip()); - m_ui.filePattern->setText(Utils::transform(m_filters, &QDir::toNativeSeparators).join(',')); - m_ui.exclusionPatternLabel->setText(Utils::msgExclusionPatternLabel()); - m_ui.exclusionPatternLabel->setBuddy(m_ui.exclusionPattern); - m_ui.exclusionPattern->setToolTip(Utils::msgFilePatternToolTip()); - m_ui.exclusionPattern->setText(Utils::transform(m_exclusionFilters, &QDir::toNativeSeparators) - .join(',')); - m_ui.shortcutEdit->setText(shortcutString()); - m_ui.defaultFlag->setChecked(isIncludedByDefault()); + m_ui->prefixLabel->setText(ILocatorFilter::msgPrefixLabel()); + m_ui->prefixLabel->setToolTip(ILocatorFilter::msgPrefixToolTip()); + m_ui->defaultFlag->setText(ILocatorFilter::msgIncludeByDefault()); + m_ui->defaultFlag->setToolTip(ILocatorFilter::msgIncludeByDefaultToolTip()); + m_ui->nameEdit->setText(displayName()); + m_ui->nameEdit->selectAll(); + connect(m_ui->addButton, + &QPushButton::clicked, + this, + &DirectoryFilter::handleAddDirectory, + Qt::DirectConnection); + connect(m_ui->editButton, + &QPushButton::clicked, + this, + &DirectoryFilter::handleEditDirectory, + Qt::DirectConnection); + connect(m_ui->removeButton, + &QPushButton::clicked, + this, + &DirectoryFilter::handleRemoveDirectory, + Qt::DirectConnection); + connect(m_ui->directoryList, + &QListWidget::itemSelectionChanged, + this, + &DirectoryFilter::updateOptionButtons, + Qt::DirectConnection); + m_ui->directoryList->clear(); + m_ui->directoryList->addItems(m_directories); + m_ui->nameLabel->setVisible(m_isCustomFilter); + m_ui->nameEdit->setVisible(m_isCustomFilter); + m_ui->directoryLabel->setVisible(m_isCustomFilter); + m_ui->directoryList->setVisible(m_isCustomFilter); + m_ui->addButton->setVisible(m_isCustomFilter); + m_ui->editButton->setVisible(m_isCustomFilter); + m_ui->removeButton->setVisible(m_isCustomFilter); + m_ui->filePatternLabel->setText(Utils::msgFilePatternLabel()); + m_ui->filePatternLabel->setBuddy(m_ui->filePattern); + m_ui->filePattern->setToolTip(Utils::msgFilePatternToolTip()); + m_ui->filePattern->setText(Utils::transform(m_filters, &QDir::toNativeSeparators).join(',')); + m_ui->exclusionPatternLabel->setText(Utils::msgExclusionPatternLabel()); + m_ui->exclusionPatternLabel->setBuddy(m_ui->exclusionPattern); + m_ui->exclusionPattern->setToolTip(Utils::msgFilePatternToolTip()); + m_ui->exclusionPattern->setText( + Utils::transform(m_exclusionFilters, &QDir::toNativeSeparators).join(',')); + m_ui->shortcutEdit->setText(shortcutString()); + m_ui->defaultFlag->setChecked(isIncludedByDefault()); updateOptionButtons(); + dialog.adjustSize(); if (dialog.exec() == QDialog::Accepted) { QMutexLocker locker(&m_lock); bool directoriesChanged = false; const QStringList oldDirectories = m_directories; const QStringList oldFilters = m_filters; const QStringList oldExclusionFilters = m_exclusionFilters; - setDisplayName(m_ui.nameEdit->text().trimmed()); + setDisplayName(m_ui->nameEdit->text().trimmed()); m_directories.clear(); const int oldCount = oldDirectories.count(); - const int newCount = m_ui.directoryList->count(); + const int newCount = m_ui->directoryList->count(); if (oldCount != newCount) directoriesChanged = true; for (int i = 0; i < newCount; ++i) { - m_directories.append(m_ui.directoryList->item(i)->text()); + m_directories.append(m_ui->directoryList->item(i)->text()); if (!directoriesChanged && m_directories.at(i) != oldDirectories.at(i)) directoriesChanged = true; } - m_filters = Utils::splitFilterUiText(m_ui.filePattern->text()); - m_exclusionFilters = Utils::splitFilterUiText(m_ui.exclusionPattern->text()); - setShortcutString(m_ui.shortcutEdit->text().trimmed()); - setIncludedByDefault(m_ui.defaultFlag->isChecked()); + m_filters = Utils::splitFilterUiText(m_ui->filePattern->text()); + m_exclusionFilters = Utils::splitFilterUiText(m_ui->exclusionPattern->text()); + setShortcutString(m_ui->shortcutEdit->text().trimmed()); + setIncludedByDefault(m_ui->defaultFlag->isChecked()); needsRefresh = directoriesChanged || oldFilters != m_filters || oldExclusionFilters != m_exclusionFilters; success = true; @@ -149,37 +177,37 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh) return success; } -void DirectoryFilter::addDirectory() +void DirectoryFilter::handleAddDirectory() { QString dir = QFileDialog::getExistingDirectory(m_dialog, tr("Select Directory")); if (!dir.isEmpty()) - m_ui.directoryList->addItem(dir); + m_ui->directoryList->addItem(dir); } -void DirectoryFilter::editDirectory() +void DirectoryFilter::handleEditDirectory() { - if (m_ui.directoryList->selectedItems().count() < 1) + if (m_ui->directoryList->selectedItems().count() < 1) return; - QListWidgetItem *currentItem = m_ui.directoryList->selectedItems().at(0); + QListWidgetItem *currentItem = m_ui->directoryList->selectedItems().at(0); QString dir = QFileDialog::getExistingDirectory(m_dialog, tr("Select Directory"), currentItem->text()); if (!dir.isEmpty()) currentItem->setText(dir); } -void DirectoryFilter::removeDirectory() +void DirectoryFilter::handleRemoveDirectory() { - if (m_ui.directoryList->selectedItems().count() < 1) + if (m_ui->directoryList->selectedItems().count() < 1) return; - QListWidgetItem *currentItem = m_ui.directoryList->selectedItems().at(0); - delete m_ui.directoryList->takeItem(m_ui.directoryList->row(currentItem)); + QListWidgetItem *currentItem = m_ui->directoryList->selectedItems().at(0); + delete m_ui->directoryList->takeItem(m_ui->directoryList->row(currentItem)); } void DirectoryFilter::updateOptionButtons() { - bool haveSelectedItem = (m_ui.directoryList->selectedItems().count() > 0); - m_ui.editButton->setEnabled(haveSelectedItem); - m_ui.removeButton->setEnabled(haveSelectedItem); + bool haveSelectedItem = (m_ui->directoryList->selectedItems().count() > 0); + m_ui->editButton->setEnabled(haveSelectedItem); + m_ui->removeButton->setEnabled(haveSelectedItem); } void DirectoryFilter::updateFileIterator() @@ -225,3 +253,45 @@ void DirectoryFilter::refresh(QFutureInterface<void> &future) future.setProgressValueAndText(subDirIterator.currentProgress(), tr("%1 filter update: canceled").arg(displayName())); } } + +void DirectoryFilter::setIsCustomFilter(bool value) +{ + m_isCustomFilter = value; +} + +void DirectoryFilter::setDirectories(const QStringList &directories) +{ + if (directories == m_directories) + return; + m_directories = directories; + Internal::Locator::instance()->refresh({this}); +} + +void DirectoryFilter::addDirectory(const QString &directory) +{ + setDirectories(m_directories + QStringList(directory)); +} + +void DirectoryFilter::removeDirectory(const QString &directory) +{ + QStringList directories = m_directories; + directories.removeOne(directory); + setDirectories(directories); +} + +QStringList DirectoryFilter::directories() const +{ + return m_directories; +} + +void DirectoryFilter::setFilters(const QStringList &filters) +{ + m_filters = filters; +} + +void DirectoryFilter::setExclusionFilters(const QStringList &exclusionFilters) +{ + m_exclusionFilters = exclusionFilters; +} + +} // namespace Core diff --git a/src/plugins/coreplugin/locator/directoryfilter.h b/src/plugins/coreplugin/locator/directoryfilter.h index 06ef1f37e3..803141afdb 100644 --- a/src/plugins/coreplugin/locator/directoryfilter.h +++ b/src/plugins/coreplugin/locator/directoryfilter.h @@ -25,9 +25,10 @@ #pragma once -#include "ui_directoryfilter.h" #include "basefilefilter.h" +#include <coreplugin/core_global.h> + #include <QString> #include <QByteArray> #include <QFutureInterface> @@ -35,8 +36,12 @@ namespace Core { namespace Internal { +namespace Ui { +class DirectoryFilterOptions; +} // namespace Ui +} // namespace Internal -class DirectoryFilter : public BaseFileFilter +class CORE_EXPORT DirectoryFilter : public BaseFileFilter { Q_OBJECT @@ -47,10 +52,20 @@ public: bool openConfigDialog(QWidget *parent, bool &needsRefresh) override; void refresh(QFutureInterface<void> &future) override; + void setIsCustomFilter(bool value); + void setDirectories(const QStringList &directories); + void addDirectory(const QString &directory); + void removeDirectory(const QString &directory); + QStringList directories() const; + void setFilters(const QStringList &filters); + void setExclusionFilters(const QStringList &exclusionFilters); + + using ILocatorFilter::setDisplayName; + private: - void addDirectory(); - void editDirectory(); - void removeDirectory(); + void handleAddDirectory(); + void handleEditDirectory(); + void handleRemoveDirectory(); void updateOptionButtons(); void updateFileIterator(); @@ -60,10 +75,10 @@ private: // Our config dialog, uses in addDirectory and editDirectory // to give their dialogs the right parent QDialog *m_dialog = nullptr; - Ui::DirectoryFilterOptions m_ui; + Internal::Ui::DirectoryFilterOptions *m_ui = nullptr; mutable QMutex m_lock; QStringList m_files; + bool m_isCustomFilter = true; }; -} // namespace Internal } // namespace Core diff --git a/src/plugins/coreplugin/locator/directoryfilter.ui b/src/plugins/coreplugin/locator/directoryfilter.ui index 37d26e4ed7..5889a82750 100644 --- a/src/plugins/coreplugin/locator/directoryfilter.ui +++ b/src/plugins/coreplugin/locator/directoryfilter.ui @@ -10,11 +10,11 @@ <height>300</height> </rect> </property> - <layout class="QGridLayout"> - <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> <layout class="QGridLayout"> <item row="0" column="0"> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="nameLabel"> <property name="text"> <string>Name:</string> </property> @@ -81,7 +81,7 @@ To do this, you type this shortcut and a space in the Locator entry field, and t </widget> </item> <item row="1" column="3"> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="directoryButtonLayout"> <item> <widget class="QPushButton" name="addButton"> <property name="text"> @@ -110,8 +110,8 @@ To do this, you type this shortcut and a space in the Locator entry field, and t </property> <property name="sizeHint" stdset="0"> <size> - <width>20</width> - <height>40</height> + <width>0</width> + <height>0</height> </size> </property> </spacer> @@ -119,23 +119,23 @@ To do this, you type this shortcut and a space in the Locator entry field, and t </layout> </item> <item row="1" column="0"> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QVBoxLayout" name="directoryLabelLayout"> <item> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="directoryLabel"> <property name="text"> <string>Directories:</string> </property> </widget> </item> <item> - <spacer name="verticalSpacer"> + <spacer name="directoryLabelSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> - <width>20</width> - <height>40</height> + <width>0</width> + <height>0</height> </size> </property> </spacer> @@ -154,7 +154,20 @@ To do this, you type this shortcut and a space in the Locator entry field, and t </item> </layout> </item> - <item row="1" column="0"> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp index b85bf4ff28..bc4a4b65a7 100644 --- a/src/plugins/coreplugin/locator/executefilter.cpp +++ b/src/plugins/coreplugin/locator/executefilter.cpp @@ -44,7 +44,7 @@ ExecuteFilter::ExecuteFilter() m_process = new Utils::QtcProcess(this); m_process->setEnvironment(Utils::Environment::systemEnvironment()); - connect(m_process, static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + connect(m_process, QOverload<int ,QProcess::ExitStatus>::of(&QProcess::finished), this, &ExecuteFilter::finished); connect(m_process, &QProcess::readyReadStandardOutput, this, &ExecuteFilter::readStandardOutput); connect(m_process, &QProcess::readyReadStandardError, this, &ExecuteFilter::readStandardError); @@ -160,7 +160,7 @@ void ExecuteFilter::runHeadCommand() { if (!m_taskQueue.isEmpty()) { const ExecuteData &d = m_taskQueue.head(); - const Utils::FileName fullPath = Utils::Environment::systemEnvironment().searchInPath(d.executable); + const Utils::FilePath fullPath = Utils::Environment::systemEnvironment().searchInPath(d.executable); if (fullPath.isEmpty()) { MessageManager::write(tr("Could not find executable for \"%1\".").arg(d.executable)); m_taskQueue.dequeue(); @@ -169,7 +169,7 @@ void ExecuteFilter::runHeadCommand() } MessageManager::write(tr("Starting command \"%1\".").arg(headCommand())); m_process->setWorkingDirectory(d.workingDirectory); - m_process->setCommand(fullPath.toString(), d.arguments); + m_process->setCommand(Utils::CommandLine(fullPath, d.arguments)); m_process->start(); m_process->closeWriteChannel(); if (!m_process->waitForStarted(1000)) { diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp index 3c55e00240..0719e9b781 100644 --- a/src/plugins/coreplugin/locator/filesystemfilter.cpp +++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp @@ -142,8 +142,7 @@ QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<LocatorF const QString fullFilePath = dirInfo.filePath(fileName); if (!QFileInfo::exists(fullFilePath) && dirInfo.exists()) { LocatorFilterEntry createAndOpen(this, tr("Create and Open \"%1\"").arg(entry), fullFilePath); - createAndOpen.extraInfo = Utils::FileUtils::shortNativePath( - Utils::FileName::fromString(dirInfo.absolutePath())); + createAndOpen.extraInfo = Utils::FilePath::fromString(dirInfo.absolutePath()).shortNativePath(); betterEntries.append(createAndOpen); } diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp index 17f8fbbd86..48749cce3e 100644 --- a/src/plugins/coreplugin/locator/locator.cpp +++ b/src/plugins/coreplugin/locator/locator.cpp @@ -56,7 +56,6 @@ #include <utils/utilsicons.h> #include <QAction> -#include <QFuture> #include <QSettings> #include <QtPlugin> @@ -92,7 +91,7 @@ Locator::Locator() { m_instance = this; m_refreshTimer.setSingleShot(false); - connect(&m_refreshTimer, &QTimer::timeout, this, [this]() { refresh(); }); + connect(&m_refreshTimer, &QTimer::timeout, this, [this]() { refresh(filters()); }); } Locator::~Locator() @@ -339,12 +338,22 @@ void Locator::setRefreshInterval(int interval) void Locator::refresh(QList<ILocatorFilter *> filters) { - if (filters.isEmpty()) - filters = m_filters; - QFuture<void> task = Utils::map(filters, &ILocatorFilter::refresh, Utils::MapReduceOption::Unordered); - FutureProgress *progress = - ProgressManager::addTask(task, tr("Updating Locator Caches"), Constants::TASK_INDEX); - connect(progress, &FutureProgress::finished, this, &Locator::saveSettings); + if (m_refreshTask.isRunning()) { + m_refreshTask.cancel(); + // this is not ideal because some of the previous filters might have finished, but we + // currently cannot find out which part of a map-reduce has finished + filters = Utils::filteredUnique(m_refreshingFilters + filters); + } + m_refreshingFilters = filters; + m_refreshTask = Utils::map(filters, &ILocatorFilter::refresh, Utils::MapReduceOption::Unordered); + ProgressManager::addTask(m_refreshTask, tr("Updating Locator Caches"), Constants::TASK_INDEX); + Utils::onFinished(m_refreshTask, this, [this](const QFuture<void> &future) { + if (!future.isCanceled()) { + saveSettings(); + m_refreshingFilters.clear(); + m_refreshTask = QFuture<void>(); + } + }); } } // namespace Internal diff --git a/src/plugins/coreplugin/locator/locator.h b/src/plugins/coreplugin/locator/locator.h index 10b2dc256a..d7c62c44ba 100644 --- a/src/plugins/coreplugin/locator/locator.h +++ b/src/plugins/coreplugin/locator/locator.h @@ -30,6 +30,7 @@ #include <coreplugin/actionmanager/command.h> +#include <QFuture> #include <QObject> #include <QTimer> @@ -64,7 +65,7 @@ signals: void filtersChanged(); public slots: - void refresh(QList<ILocatorFilter *> filters = QList<ILocatorFilter *>()); + void refresh(QList<ILocatorFilter *> filters); void saveSettings() const; private: @@ -80,6 +81,8 @@ private: QList<ILocatorFilter *> m_customFilters; QMap<Id, QAction *> m_filterActionMap; QTimer m_refreshTimer; + QFuture<void> m_refreshTask; + QList<ILocatorFilter *> m_refreshingFilters; }; } // namespace Internal diff --git a/src/plugins/coreplugin/locator/locator_test.cpp b/src/plugins/coreplugin/locator/locator_test.cpp index cf99717e5b..c76a5633ea 100644 --- a/src/plugins/coreplugin/locator/locator_test.cpp +++ b/src/plugins/coreplugin/locator/locator_test.cpp @@ -89,7 +89,7 @@ void Core::Internal::CorePlugin::test_basefilefilter() void Core::Internal::CorePlugin::test_basefilefilter_data() { auto shortNativePath = [](const QString &file) { - return Utils::FileUtils::shortNativePath(Utils::FileName::fromString(file)); + return Utils::FilePath::fromString(file).shortNativePath(); }; QTest::addColumn<QStringList>("testFiles"); diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp index 994c9e5d0e..b21afd2adc 100644 --- a/src/plugins/coreplugin/locator/locatorwidget.cpp +++ b/src/plugins/coreplugin/locator/locatorwidget.cpp @@ -210,7 +210,7 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const return QColor(Qt::darkGray); break; case LocatorEntryRole: - return qVariantFromValue(mEntries.at(index.row())); + return QVariant::fromValue(mEntries.at(index.row())); case int(HighlightingItemRole::StartColumn): case int(HighlightingItemRole::Length): { LocatorFilterEntry &entry = mEntries[index.row()]; @@ -579,8 +579,9 @@ LocatorWidget::LocatorWidget(Locator *locator) : m_fileLineEdit->setButtonMenu(Utils::FancyLineEdit::Left, m_filterMenu); - connect(m_refreshAction, &QAction::triggered, - locator, [locator]() { locator->refresh(); }); + connect(m_refreshAction, &QAction::triggered, locator, [locator]() { + locator->refresh(locator->filters()); + }); connect(m_configureAction, &QAction::triggered, this, &LocatorWidget::showConfigureDialog); connect(m_fileLineEdit, &QLineEdit::textChanged, this, &LocatorWidget::showPopupDelayed); diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp index 575e992557..fe67e5d884 100644 --- a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp +++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp @@ -76,7 +76,7 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Locat const QRegularExpressionMatch match = regexp.match(displayName); if (match.hasMatch()) { LocatorFilterEntry filterEntry(this, displayName, QString(fileName + fp.postfix)); - filterEntry.extraInfo = FileUtils::shortNativePath(FileName::fromString(fileName)); + filterEntry.extraInfo = FilePath::fromString(fileName).shortNativePath(); filterEntry.fileName = fileName; filterEntry.highlightInfo = highlightInfo(match); if (match.capturedStart() == 0) diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.h b/src/plugins/coreplugin/locator/opendocumentsfilter.h index 8d140c86d7..956dea22d2 100644 --- a/src/plugins/coreplugin/locator/opendocumentsfilter.h +++ b/src/plugins/coreplugin/locator/opendocumentsfilter.h @@ -56,7 +56,7 @@ private: class Entry { public: - Utils::FileName fileName; + Utils::FilePath fileName; QString displayName; }; diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 2e8b5f2ad1..dd724d9f9c 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -100,25 +100,26 @@ namespace Internal { enum { debugMainWindow = 0 }; -MainWindow::MainWindow() : - AppMainWindow(), - m_coreImpl(new ICore(this)), - m_lowPrioAdditionalContexts(Constants::C_GLOBAL), - m_settingsDatabase(new SettingsDatabase(QFileInfo(PluginManager::settings()->fileName()).path(), - QLatin1String(Constants::IDE_CASED_ID), - this)), - m_progressManager(new ProgressManagerPrivate), - m_jsExpander(new JsExpander), - m_vcsManager(new VcsManager), - m_modeStack(new FancyTabWidget(this)), - m_generalSettings(new GeneralSettings), - m_systemSettings(new SystemSettings), - m_shortcutSettings(new ShortcutSettings), - m_toolSettings(new ToolSettings), - m_mimeTypeSettings(new MimeTypeSettings), - m_systemEditor(new SystemEditor), - m_toggleLeftSideBarButton(new QToolButton), - m_toggleRightSideBarButton(new QToolButton) +MainWindow::MainWindow() + : AppMainWindow() + , m_coreImpl(new ICore(this)) + , m_lowPrioAdditionalContexts(Constants::C_GLOBAL) + , m_settingsDatabase( + new SettingsDatabase(QFileInfo(PluginManager::settings()->fileName()).path(), + QLatin1String(Constants::IDE_CASED_ID), + this)) + , m_progressManager(new ProgressManagerPrivate) + , m_jsExpander(JsExpander::createGlobalJsExpander()) + , m_vcsManager(new VcsManager) + , m_modeStack(new FancyTabWidget(this)) + , m_generalSettings(new GeneralSettings) + , m_systemSettings(new SystemSettings) + , m_shortcutSettings(new ShortcutSettings) + , m_toolSettings(new ToolSettings) + , m_mimeTypeSettings(new MimeTypeSettings) + , m_systemEditor(new SystemEditor) + , m_toggleLeftSideBarButton(new QToolButton) + , m_toggleRightSideBarButton(new QToolButton) { (void) new DocumentManager(this); @@ -321,7 +322,7 @@ void MainWindow::closeEvent(QCloseEvent *event) return; } - ICore::saveSettings(); + ICore::saveSettings(ICore::MainWindowClosing); // Save opened files if (!DocumentManager::saveAllModifiedDocuments()) { diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 709dcf62aa..76593c18d0 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -722,11 +722,11 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt bool notElideAsterisk = widget && widget->property("notelideasterisk").toBool() && cb->currentText.endsWith(asterisk) - && option->fontMetrics.width(cb->currentText) > elideWidth; + && option->fontMetrics.horizontalAdvance(cb->currentText) > elideWidth; QString text; if (notElideAsterisk) { - elideWidth -= option->fontMetrics.width(asterisk); + elideWidth -= option->fontMetrics.horizontalAdvance(asterisk); text = asterisk; } text.prepend(option->fontMetrics.elidedText(cb->currentText, Qt::ElideRight, elideWidth)); @@ -980,7 +980,7 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti QRect arrowRect((left + right) / 2 + (reverse ? 6 : -6), rect.center().y() - 3, 9, 9); if (!alignarrow) { - int labelwidth = option->fontMetrics.width(cb->currentText); + int labelwidth = option->fontMetrics.horizontalAdvance(cb->currentText); if (reverse) arrowRect.moveLeft(qMax(rect.width() - labelwidth - menuButtonWidth - 2, 4)); else diff --git a/src/plugins/coreplugin/messagemanager.cpp b/src/plugins/coreplugin/messagemanager.cpp index c4bf4269a4..5644db547f 100644 --- a/src/plugins/coreplugin/messagemanager.cpp +++ b/src/plugins/coreplugin/messagemanager.cpp @@ -24,14 +24,18 @@ ****************************************************************************/ #include "messagemanager.h" + #include "messageoutputwindow.h" #include <extensionsystem/pluginmanager.h> +#include <utils/qtcassert.h> + +#include <QFont> using namespace Core; static MessageManager *m_instance = nullptr; -Internal::MessageOutputWindow *m_messageOutputWindow = nullptr; +static Internal::MessageOutputWindow *m_messageOutputWindow = nullptr; MessageManager *MessageManager::instance() { @@ -40,8 +44,8 @@ MessageManager *MessageManager::instance() void MessageManager::showOutputPane(Core::MessageManager::PrintToOutputPaneFlags flags) { - if (!m_messageOutputWindow) - return; + QTC_ASSERT(m_messageOutputWindow, return); + if (flags & Flash) { m_messageOutputWindow->flash(); } else if (flags & Silent) { @@ -73,11 +77,24 @@ void MessageManager::init() ExtensionSystem::PluginManager::addObject(m_messageOutputWindow); } +void MessageManager::setFont(const QFont &font) +{ + QTC_ASSERT(m_messageOutputWindow, return); + + m_messageOutputWindow->setFont(font); +} + +void MessageManager::setWheelZoomEnabled(bool enabled) +{ + QTC_ASSERT(m_messageOutputWindow, return); + + m_messageOutputWindow->setWheelZoomEnabled(enabled); +} + void MessageManager::write(const QString &text, PrintToOutputPaneFlags flags) { - if (!m_messageOutputWindow) - return; + QTC_ASSERT(m_messageOutputWindow, return); + showOutputPane(flags); - m_messageOutputWindow->append(text + QLatin1Char('\n')); + m_messageOutputWindow->append(text + '\n'); } - diff --git a/src/plugins/coreplugin/messagemanager.h b/src/plugins/coreplugin/messagemanager.h index d530db8603..6ead1ddeb8 100644 --- a/src/plugins/coreplugin/messagemanager.h +++ b/src/plugins/coreplugin/messagemanager.h @@ -27,10 +27,14 @@ #include "core_global.h" #include "ioutputpane.h" -#include <QMetaType> +#include <QMetaType> #include <QObject> +QT_BEGIN_NAMESPACE +class QFont; +QT_END_NAMESPACE + namespace Core { namespace Internal { class MainWindow; } @@ -55,6 +59,9 @@ public: static void showOutputPane(Core::MessageManager::PrintToOutputPaneFlags flags = NoModeSwitch); + static void setFont(const QFont &font); + static void setWheelZoomEnabled(bool enabled); + public slots: static void write(const QString &text, Core::MessageManager::PrintToOutputPaneFlags flags = NoModeSwitch); diff --git a/src/plugins/coreplugin/messageoutputwindow.cpp b/src/plugins/coreplugin/messageoutputwindow.cpp index 35a1bd50be..c7c752e155 100644 --- a/src/plugins/coreplugin/messageoutputwindow.cpp +++ b/src/plugins/coreplugin/messageoutputwindow.cpp @@ -30,13 +30,20 @@ #include "find/basetextfind.h" #include <aggregation/aggregate.h> +#include <coreplugin/icore.h> +#include <utils/utilsicons.h> + +#include <QFont> +#include <QToolButton> namespace Core { namespace Internal { +const char zoomSettingsKey[] = "Core/MessageOutput/Zoom"; + MessageOutputWindow::MessageOutputWindow() { - m_widget = new OutputWindow(Context(Constants::C_GENERAL_OUTPUT_PANE)); + m_widget = new OutputWindow(Context(Constants::C_GENERAL_OUTPUT_PANE), zoomSettingsKey); m_widget->setReadOnly(true); // Let selected text be colored as if the text edit was editable, // otherwise the highlight for searching is too light @@ -46,9 +53,20 @@ MessageOutputWindow::MessageOutputWindow() QColor activeHighlightedText = p.color(QPalette::Active, QPalette::HighlightedText); p.setColor(QPalette::HighlightedText, activeHighlightedText); m_widget->setPalette(p); + + connect(this, &IOutputPane::zoomIn, m_widget, &Core::OutputWindow::zoomIn); + connect(this, &IOutputPane::zoomOut, m_widget, &Core::OutputWindow::zoomOut); + connect(this, &IOutputPane::resetZoom, m_widget, &Core::OutputWindow::resetZoom); + connect(this, &IOutputPane::fontChanged, m_widget, &OutputWindow::setBaseFont); + connect(this, &IOutputPane::wheelZoomEnabledChanged, m_widget, &OutputWindow::setWheelZoomEnabled); + auto agg = new Aggregation::Aggregate; agg->add(m_widget); agg->add(new BaseTextFind(m_widget)); + + setupFilterUi("MessageOutputPane.Filter"); + setFilteringEnabled(true); + setupContext(Constants::C_GENERAL_OUTPUT_PANE, m_widget); } MessageOutputWindow::~MessageOutputWindow() @@ -126,5 +144,10 @@ bool MessageOutputWindow::canNavigate() const return false; } +void MessageOutputWindow::updateFilter() +{ + m_widget->updateFilterProperties(filterText(), filterCaseSensitivity(), filterUsesRegexp()); +} + } // namespace Internal } // namespace Core diff --git a/src/plugins/coreplugin/messageoutputwindow.h b/src/plugins/coreplugin/messageoutputwindow.h index bd8c5ca3d4..69f940dc8e 100644 --- a/src/plugins/coreplugin/messageoutputwindow.h +++ b/src/plugins/coreplugin/messageoutputwindow.h @@ -27,6 +27,11 @@ #include "ioutputpane.h" +QT_BEGIN_NAMESPACE +class QFont; +class QToolButton; +QT_END_NAMESPACE + namespace Core { class OutputWindow; @@ -41,7 +46,6 @@ public: ~MessageOutputWindow() override; QWidget *outputWidget(QWidget *parent) override; - QList<QWidget*> toolBarWidgets() const override { return {}; } QString displayName() const override; int priorityInStatusBar() const override; @@ -60,6 +64,8 @@ public: bool canNavigate() const override; private: + void updateFilter() override; + OutputWindow *m_widget; }; diff --git a/src/plugins/coreplugin/mimetypemagicdialog.cpp b/src/plugins/coreplugin/mimetypemagicdialog.cpp index 5c1566d022..36a5f099c6 100644 --- a/src/plugins/coreplugin/mimetypemagicdialog.cpp +++ b/src/plugins/coreplugin/mimetypemagicdialog.cpp @@ -55,7 +55,7 @@ MimeTypeMagicDialog::MimeTypeMagicDialog(QWidget *parent) : connect(ui.informationLabel, &QLabel::linkActivated, this, [](const QString &link) { QDesktopServices::openUrl(QUrl(link)); }); - connect(ui.typeSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), + connect(ui.typeSelector, QOverload<int>::of(&QComboBox::activated), this, [this]() { if (ui.useRecommendedGroupBox->isChecked()) setToRecommendedValues(); diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index a5750d13c7..7b4d891339 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -157,9 +157,9 @@ QVariant MimeTypeSettingsModel::data(const QModelIndex &modelIndex, int role) co return defaultHandler ? defaultHandler->displayName() : QString(); } } else if (role == Qt::EditRole) { - return qVariantFromValue(handlersForMimeType(m_mimeTypes.at(modelIndex.row()))); + return QVariant::fromValue(handlersForMimeType(m_mimeTypes.at(modelIndex.row()))); } else if (role == int(Role::DefaultHandler)) { - return qVariantFromValue(defaultHandlerForMimeType(m_mimeTypes.at(modelIndex.row()))); + return QVariant::fromValue(defaultHandlerForMimeType(m_mimeTypes.at(modelIndex.row()))); } else if (role == Qt::FontRole) { if (column == 1) { const Utils::MimeType &type = m_mimeTypes.at(modelIndex.row()); @@ -397,7 +397,7 @@ void MimeTypeSettingsPrivate::editMagicHeaderRowData(const int row, const MagicD item->setText(1, QString::fromLatin1(Utils::Internal::MimeMagicRule::typeName(data.m_rule.type()))); item->setText(2, QString::fromLatin1("%1:%2").arg(data.m_rule.startPos()).arg(data.m_rule.endPos())); item->setText(3, QString::number(data.m_priority)); - item->setData(0, Qt::UserRole, qVariantFromValue(data)); + item->setData(0, Qt::UserRole, QVariant::fromValue(data)); m_ui.magicHeadersTreeWidget->takeTopLevelItem(row); m_ui.magicHeadersTreeWidget->insertTopLevelItem(row, item); m_ui.magicHeadersTreeWidget->setCurrentItem(item); @@ -501,7 +501,7 @@ void MimeTypeSettingsPrivate::ensurePendingMimeType(const Utils::MimeType &mimeT void MimeTypeSettingsPrivate::writeUserModifiedMimeTypes() { - static Utils::FileName modifiedMimeTypesFile = Utils::FileName::fromString( + static Utils::FilePath modifiedMimeTypesFile = Utils::FilePath::fromString( ICore::userResourcePath() + QLatin1String(kModifiedMimeTypesFile)); if (QFile::exists(modifiedMimeTypesFile.toString()) @@ -690,7 +690,7 @@ void MimeEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index auto box = static_cast<QComboBox *>(editor); const auto factories = index.model()->data(index, Qt::EditRole).value<QList<IEditorFactory *>>(); for (IEditorFactory *factory : factories) - box->addItem(factory->displayName(), qVariantFromValue(factory)); + box->addItem(factory->displayName(), QVariant::fromValue(factory)); int currentIndex = factories.indexOf( index.model() ->data(index, int(MimeTypeSettingsModel::Role::DefaultHandler)) diff --git a/src/plugins/coreplugin/navigationsubwidget.cpp b/src/plugins/coreplugin/navigationsubwidget.cpp index c8aed179c5..ab4858e388 100644 --- a/src/plugins/coreplugin/navigationsubwidget.cpp +++ b/src/plugins/coreplugin/navigationsubwidget.cpp @@ -97,7 +97,7 @@ NavigationSubWidget::NavigationSubWidget(NavigationWidget *parentWidget, int pos setFactoryIndex(factoryIndex); connect(m_navigationComboBox, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NavigationSubWidget::comboBoxIndexChanged); comboBoxIndexChanged(factoryIndex); diff --git a/src/plugins/coreplugin/navigationwidget.cpp b/src/plugins/coreplugin/navigationwidget.cpp index f223cfa777..aae3a618f9 100644 --- a/src/plugins/coreplugin/navigationwidget.cpp +++ b/src/plugins/coreplugin/navigationwidget.cpp @@ -260,7 +260,7 @@ void NavigationWidget::setFactories(const QList<INavigationWidgetFactory *> &fac } QStandardItem *newRow = new QStandardItem(factory->displayName()); - newRow->setData(qVariantFromValue(factory), FactoryObjectRole); + newRow->setData(QVariant::fromValue(factory), FactoryObjectRole); newRow->setData(QVariant::fromValue(factory->id()), FactoryIdRole); newRow->setData(factory->priority(), FactoryPriorityRole); d->m_factoryModel->appendRow(newRow); diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index 7825126565..7cf2ec2c90 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -35,8 +35,10 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/actionmanager/commandbutton.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/find/optionspopup.h> #include <utils/algorithm.h> #include <utils/hostosinfo.h> @@ -88,18 +90,152 @@ static bool g_managerConstructed = false; // For debugging reasons. // OutputPane IOutputPane::IOutputPane(QObject *parent) - : QObject(parent) + : QObject(parent), + m_zoomInButton(new Core::CommandButton), + m_zoomOutButton(new Core::CommandButton) { // We need all pages first. Ignore latecomers and shout. QTC_ASSERT(!g_managerConstructed, return); g_outputPanes.append(OutputPaneData(this)); + + m_zoomInButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); + m_zoomInButton->setCommandId(Constants::ZOOM_IN); + connect(m_zoomInButton, &QToolButton::clicked, this, [this] { emit zoomIn(1); }); + + m_zoomOutButton->setIcon(Utils::Icons::MINUS.icon()); + m_zoomOutButton->setCommandId(Constants::ZOOM_OUT); + connect(m_zoomOutButton, &QToolButton::clicked, this, [this] { emit zoomOut(1); }); } IOutputPane::~IOutputPane() { + if (m_context) + ICore::removeContextObject(m_context); + const int i = Utils::indexOf(g_outputPanes, Utils::equal(&OutputPaneData::pane, this)); QTC_ASSERT(i >= 0, return); delete g_outputPanes.at(i).button; + + delete m_zoomInButton; + delete m_zoomOutButton; +} + +QList<QWidget *> IOutputPane::toolBarWidgets() const +{ + QList<QWidget *> widgets; + if (m_filterOutputLineEdit) + widgets << m_filterOutputLineEdit; + return widgets << m_zoomInButton << m_zoomOutButton; +} + +void IOutputPane::setFont(const QFont &font) +{ + emit fontChanged(font); +} + +void IOutputPane::setWheelZoomEnabled(bool enabled) +{ + emit wheelZoomEnabledChanged(enabled); +} + +void IOutputPane::setupFilterUi(const QString &historyKey) +{ + m_filterOutputLineEdit = new FancyLineEdit; + m_filterActionRegexp = new QAction(this); + m_filterActionRegexp->setCheckable(true); + m_filterActionRegexp->setText(tr("Use Regular Expressions")); + connect(m_filterActionRegexp, &QAction::toggled, this, &IOutputPane::setRegularExpressions); + Core::ActionManager::registerAction(m_filterActionRegexp, filterRegexpActionId()); + + m_filterActionCaseSensitive = new QAction(this); + m_filterActionCaseSensitive->setCheckable(true); + m_filterActionCaseSensitive->setText(tr("Case Sensitive")); + connect(m_filterActionCaseSensitive, &QAction::toggled, this, &IOutputPane::setCaseSensitive); + Core::ActionManager::registerAction(m_filterActionCaseSensitive, + filterCaseSensitivityActionId()); + + m_filterOutputLineEdit->setPlaceholderText(tr("Filter output...")); + m_filterOutputLineEdit->setButtonVisible(FancyLineEdit::Left, true); + m_filterOutputLineEdit->setButtonIcon(FancyLineEdit::Left, Icons::MAGNIFIER.icon()); + m_filterOutputLineEdit->setFiltering(true); + m_filterOutputLineEdit->setEnabled(false); + m_filterOutputLineEdit->setHistoryCompleter(historyKey); + connect(m_filterOutputLineEdit, &FancyLineEdit::textChanged, + this, &IOutputPane::updateFilter); + connect(m_filterOutputLineEdit, &FancyLineEdit::returnPressed, + this, &IOutputPane::updateFilter); + connect(m_filterOutputLineEdit, &FancyLineEdit::leftButtonClicked, + this, &IOutputPane::filterOutputButtonClicked); +} + +QString IOutputPane::filterText() const +{ + return m_filterOutputLineEdit->text(); +} + +void IOutputPane::setFilteringEnabled(bool enable) +{ + m_filterOutputLineEdit->setEnabled(enable); +} + +void IOutputPane::setupContext(const char *context, QWidget *widget) +{ + QTC_ASSERT(!m_context, return); + m_context = new IContext(this); + m_context->setContext(Context(context)); + m_context->setWidget(widget); + ICore::addContextObject(m_context); + + const auto zoomInAction = new QAction(this); + Core::ActionManager::registerAction(zoomInAction, Constants::ZOOM_IN, m_context->context()); + connect(zoomInAction, &QAction::triggered, this, [this] { emit zoomIn(1); }); + const auto zoomOutAction = new QAction(this); + Core::ActionManager::registerAction(zoomOutAction, Constants::ZOOM_OUT, m_context->context()); + connect(zoomOutAction, &QAction::triggered, this, [this] { emit zoomOut(1); }); + const auto resetZoomAction = new QAction(this); + Core::ActionManager::registerAction(resetZoomAction, Constants::ZOOM_RESET, + m_context->context()); + connect(resetZoomAction, &QAction::triggered, this, &IOutputPane::resetZoom); +} + +void IOutputPane::setZoomButtonsEnabled(bool enabled) +{ + m_zoomInButton->setEnabled(enabled); + m_zoomOutButton->setEnabled(enabled); +} + +void IOutputPane::updateFilter() +{ + QTC_ASSERT(false, qDebug() << "updateFilter() needs to get re-implemented"); +} + +void IOutputPane::filterOutputButtonClicked() +{ + auto popup = new Core::OptionsPopup(m_filterOutputLineEdit, + {filterRegexpActionId(), filterCaseSensitivityActionId()}); + popup->show(); +} + +void IOutputPane::setRegularExpressions(bool regularExpressions) +{ + m_filterRegexp = regularExpressions; + updateFilter(); +} + +Id IOutputPane::filterRegexpActionId() const +{ + return Id("OutputFilter.RegularExpressions").withSuffix(metaObject()->className()); +} + +Id IOutputPane::filterCaseSensitivityActionId() const +{ + return Id("OutputFilter.CaseSensitive").withSuffix(metaObject()->className()); +} + +void IOutputPane::setCaseSensitive(bool caseSensitive) +{ + m_filterCaseSensitivity = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + updateFilter(); } namespace Internal { @@ -324,7 +460,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : m_opToolBarWidgets->addWidget(toolButtonsContainer); - minTitleWidth = qMax(minTitleWidth, titleFm.width(outPane->displayName())); + minTitleWidth = qMax(minTitleWidth, titleFm.horizontalAdvance(outPane->displayName())); QString suffix = outPane->displayName().simplified(); suffix.remove(QLatin1Char(' ')); @@ -636,7 +772,7 @@ OutputPaneToggleButton::OutputPaneToggleButton(int number, const QString &text, m_flashTimer->setDirection(QTimeLine::Forward); m_flashTimer->setCurveShape(QTimeLine::SineCurve); m_flashTimer->setFrameRange(0, 92); - auto updateSlot = static_cast<void (QWidget::*)()>(&QWidget::update); + auto updateSlot = QOverload<>::of(&QWidget::update); connect(m_flashTimer, &QTimeLine::valueChanged, this, updateSlot); connect(m_flashTimer, &QTimeLine::finished, this, updateSlot); updateToolTip(); @@ -667,7 +803,7 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*) { const QFontMetrics fm = fontMetrics(); const int baseLine = (height() - fm.height() + 1) / 2 + fm.ascent(); - const int numberWidth = fm.width(m_number); + const int numberWidth = fm.horizontalAdvance(m_number); QPainter p(this); diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp index d53abdd32d..e89f999a47 100644 --- a/src/plugins/coreplugin/outputwindow.cpp +++ b/src/plugins/coreplugin/outputwindow.cpp @@ -33,6 +33,7 @@ #include <utils/synchronousprocess.h> #include <QAction> +#include <QRegularExpression> #include <QScrollBar> #include <QTextBlock> @@ -58,22 +59,28 @@ public: IContext *outputWindowContext = nullptr; Utils::OutputFormatter *formatter = nullptr; + QString settingsKey; bool enforceNewline = false; bool scrollToBottom = true; bool linksActive = true; - Qt::MouseButton mouseButtonPressed = Qt::NoButton; - bool m_zoomEnabled = false; - float m_originalFontSize = 0.; + bool zoomEnabled = false; + float originalFontSize = 0.; + bool originalReadOnly = false; int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; + Qt::MouseButton mouseButtonPressed = Qt::NoButton; QTextCursor cursor; + QString filterText; + int lastFilteredBlockNumber = -1; + QPalette originalPalette; + OutputWindow::FilterModeFlags filterMode = OutputWindow::FilterModeFlag::Default; }; } // namespace Internal /*******************/ -OutputWindow::OutputWindow(Context context, QWidget *parent) +OutputWindow::OutputWindow(Context context, const QString &settingsKey, QWidget *parent) : QPlainTextEdit(parent) , d(new Internal::OutputWindowPrivate(document())) { @@ -83,6 +90,8 @@ OutputWindow::OutputWindow(Context context, QWidget *parent) setMouseTracking(true); setUndoRedoEnabled(false); + d->settingsKey = settingsKey; + d->outputWindowContext = new IContext; d->outputWindowContext->setContext(context); d->outputWindowContext->setWidget(this); @@ -108,11 +117,16 @@ OutputWindow::OutputWindow(Context context, QWidget *parent) connect(copyAction, &QAction::triggered, this, &QPlainTextEdit::copy); connect(pasteAction, &QAction::triggered, this, &QPlainTextEdit::paste); connect(selectAllAction, &QAction::triggered, this, &QPlainTextEdit::selectAll); + connect(this, &QPlainTextEdit::blockCountChanged, this, &OutputWindow::filterNewContent); connect(this, &QPlainTextEdit::undoAvailable, undoAction, &QAction::setEnabled); connect(this, &QPlainTextEdit::redoAvailable, redoAction, &QAction::setEnabled); connect(this, &QPlainTextEdit::copyAvailable, cutAction, &QAction::setEnabled); // OutputWindow never read-only connect(this, &QPlainTextEdit::copyAvailable, copyAction, &QAction::setEnabled); + connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [this] { + if (!d->settingsKey.isEmpty()) + Core::ICore::settings()->setValue(d->settingsKey, fontZoom()); + }); undoAction->setEnabled(false); redoAction->setEnabled(false); @@ -125,7 +139,12 @@ OutputWindow::OutputWindow(Context context, QWidget *parent) this, &OutputWindow::scrollToBottom); m_lastMessage.start(); - d->m_originalFontSize = font().pointSizeF(); + d->originalFontSize = font().pointSizeF(); + + if (!d->settingsKey.isEmpty()) { + float zoom = Core::ICore::settings()->value(d->settingsKey).toFloat(); + setFontZoom(zoom); + } } OutputWindow::~OutputWindow() @@ -210,7 +229,7 @@ void OutputWindow::showEvent(QShowEvent *e) void OutputWindow::wheelEvent(QWheelEvent *e) { - if (d->m_zoomEnabled) { + if (d->zoomEnabled) { if (e->modifiers() & Qt::ControlModifier) { float delta = e->angleDelta().y() / 120.f; zoomInF(delta); @@ -225,31 +244,104 @@ void OutputWindow::wheelEvent(QWheelEvent *e) void OutputWindow::setBaseFont(const QFont &newFont) { float zoom = fontZoom(); - d->m_originalFontSize = newFont.pointSizeF(); + d->originalFontSize = newFont.pointSizeF(); QFont tmp = newFont; - float newZoom = qMax(d->m_originalFontSize + zoom, 4.0f); + float newZoom = qMax(d->originalFontSize + zoom, 4.0f); tmp.setPointSizeF(newZoom); setFont(tmp); } float OutputWindow::fontZoom() const { - return font().pointSizeF() - d->m_originalFontSize; + return font().pointSizeF() - d->originalFontSize; } void OutputWindow::setFontZoom(float zoom) { QFont f = font(); - if (f.pointSizeF() == d->m_originalFontSize + zoom) + if (f.pointSizeF() == d->originalFontSize + zoom) return; - float newZoom = qMax(d->m_originalFontSize + zoom, 4.0f); + float newZoom = qMax(d->originalFontSize + zoom, 4.0f); f.setPointSizeF(newZoom); setFont(f); } void OutputWindow::setWheelZoomEnabled(bool enabled) { - d->m_zoomEnabled = enabled; + d->zoomEnabled = enabled; +} + +void OutputWindow::updateFilterProperties(const QString &filterText, + Qt::CaseSensitivity caseSensitivity, bool isRegexp) +{ + FilterModeFlags flags; + flags.setFlag(FilterModeFlag::CaseSensitive, caseSensitivity == Qt::CaseSensitive) + .setFlag(FilterModeFlag::RegExp, isRegexp); + if (d->filterMode == flags && d->filterText == filterText) + return; + d->lastFilteredBlockNumber = -1; + if (d->filterText != filterText) { + const bool filterTextWasEmpty = d->filterText.isEmpty(); + d->filterText = filterText; + + // Update textedit's background color + if (filterText.isEmpty() && !filterTextWasEmpty) { + setPalette(d->originalPalette); + setReadOnly(d->originalReadOnly); + } + if (!filterText.isEmpty() && filterTextWasEmpty) { + d->originalReadOnly = isReadOnly(); + setReadOnly(true); + const auto newBgColor = [this] { + const QColor currentColor = palette().color(QPalette::Base); + const int factor = 120; + return currentColor.value() < 128 ? currentColor.lighter(factor) + : currentColor.darker(factor); + }; + QPalette p = palette(); + p.setColor(QPalette::Base, newBgColor()); + setPalette(p); + } + } + d->filterMode = flags; + filterNewContent(); +} + +void OutputWindow::filterNewContent() +{ + bool atBottom = isScrollbarAtBottom(); + + QTextBlock lastBlock = document()->findBlockByNumber(d->lastFilteredBlockNumber); + if (!lastBlock.isValid()) + lastBlock = document()->begin(); + + if (d->filterMode.testFlag(OutputWindow::FilterModeFlag::RegExp)) { + QRegularExpression regExp(d->filterText); + if (!d->filterMode.testFlag(OutputWindow::FilterModeFlag::CaseSensitive)) + regExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + + for (; lastBlock != document()->end(); lastBlock = lastBlock.next()) + lastBlock.setVisible(d->filterText.isEmpty() + || regExp.match(lastBlock.text()).hasMatch()); + } else { + if (d->filterMode.testFlag(OutputWindow::FilterModeFlag::CaseSensitive)) { + for (; lastBlock != document()->end(); lastBlock = lastBlock.next()) + lastBlock.setVisible(d->filterText.isEmpty() + || lastBlock.text().contains(d->filterText)); + } else { + for (; lastBlock != document()->end(); lastBlock = lastBlock.next()) + lastBlock.setVisible(d->filterText.isEmpty() + || lastBlock.text().toLower().contains(d->filterText.toLower())); + } + } + + d->lastFilteredBlockNumber = document()->lastBlock().blockNumber(); + + // FIXME: Why on earth is this necessary? We should probably do something else instead... + setDocument(document()); + + if (atBottom) + scrollToBottom(); } QString OutputWindow::doNewlineEnforcement(const QString &out) diff --git a/src/plugins/coreplugin/outputwindow.h b/src/plugins/coreplugin/outputwindow.h index 5e966c64ec..204a1106a9 100644 --- a/src/plugins/coreplugin/outputwindow.h +++ b/src/plugins/coreplugin/outputwindow.h @@ -30,9 +30,9 @@ #include <utils/outputformat.h> +#include <QElapsedTimer> #include <QPlainTextEdit> #include <QTimer> -#include <QTime> namespace Utils { class OutputFormatter; } @@ -45,7 +45,14 @@ class CORE_EXPORT OutputWindow : public QPlainTextEdit Q_OBJECT public: - OutputWindow(Context context, QWidget *parent = nullptr); + enum class FilterModeFlag { + Default = 0x00, // Plain text, non case sensitive, for initialization + RegExp = 0x01, + CaseSensitive = 0x02, + }; + Q_DECLARE_FLAGS(FilterModeFlags, FilterModeFlag) + + OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr); ~OutputWindow() override; Utils::OutputFormatter *formatter() const; @@ -68,8 +75,11 @@ public: void setBaseFont(const QFont &newFont); float fontZoom() const; void setFontZoom(float zoom); + void resetZoom() { setFontZoom(0); } void setWheelZoomEnabled(bool enabled); + void updateFilterProperties(const QString &filterText, Qt::CaseSensitivity caseSensitivity, bool regexp); + signals: void wheelZoom(); @@ -89,9 +99,10 @@ protected: private: using QPlainTextEdit::setFont; // call setBaseFont instead, which respects the zoom factor QTimer m_scrollTimer; - QTime m_lastMessage; + QElapsedTimer m_lastMessage; void enableUndoRedo(); QString doNewlineEnforcement(const QString &out); + void filterNewContent(); Internal::OutputWindowPrivate *d; }; diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp index 724fabd133..1442046325 100644 --- a/src/plugins/coreplugin/patchtool.cpp +++ b/src/plugins/coreplugin/patchtool.cpp @@ -88,7 +88,7 @@ static bool runPatchHelper(const QByteArray &input, const QString &workingDirect return false; } - if (!Utils::FileName::fromString(patch).exists() + if (!Utils::FilePath::fromString(patch).exists() && !Utils::Environment::systemEnvironment().searchInPath(patch).exists()) { MessageManager::write(QApplication::translate("Core::PatchTool", "The patch-command configured in the general \"Environment\" settings does not exist.")); return false; diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.cpp b/src/plugins/coreplugin/progressmanager/futureprogress.cpp index d7eeec9ff4..32193fd08b 100644 --- a/src/plugins/coreplugin/progressmanager/futureprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/futureprogress.cpp @@ -71,6 +71,7 @@ public: FutureProgress *m_q; bool m_fadeStarting; bool m_isFading; + bool m_isSubtitleVisibleInStatusBar = false; }; FutureProgressPrivate::FutureProgressPrivate(FutureProgress *q) : @@ -188,6 +189,33 @@ QString FutureProgress::title() const return d->m_progress->title(); } +void FutureProgress::setSubtitle(const QString &subtitle) +{ + if (subtitle != d->m_progress->subtitle()) { + d->m_progress->setSubtitle(subtitle); + if (d->m_isSubtitleVisibleInStatusBar) + emit subtitleInStatusBarChanged(); + } +} + +QString FutureProgress::subtitle() const +{ + return d->m_progress->subtitle(); +} + +void FutureProgress::setSubtitleVisibleInStatusBar(bool visible) +{ + if (visible != d->m_isSubtitleVisibleInStatusBar) { + d->m_isSubtitleVisibleInStatusBar = visible; + emit subtitleInStatusBarChanged(); + } +} + +bool FutureProgress::isSubtitleVisibleInStatusBar() const +{ + return d->m_isSubtitleVisibleInStatusBar; +} + void FutureProgress::cancel() { d->m_watcher.future().cancel(); diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.h b/src/plugins/coreplugin/progressmanager/futureprogress.h index 3f591b2b94..09b178dff2 100644 --- a/src/plugins/coreplugin/progressmanager/futureprogress.h +++ b/src/plugins/coreplugin/progressmanager/futureprogress.h @@ -56,6 +56,12 @@ public: void setTitle(const QString &title); QString title() const; + void setSubtitle(const QString &subtitle); + QString subtitle() const; + + void setSubtitleVisibleInStatusBar(bool visible); + bool isSubtitleVisibleInStatusBar() const; + void setType(Id type); Id type() const; @@ -83,6 +89,7 @@ signals: void fadeStarted(); void statusBarWidgetChanged(); + void subtitleInStatusBarChanged(); protected: void mousePressEvent(QMouseEvent *event) override; diff --git a/src/plugins/coreplugin/progressmanager/progressbar.cpp b/src/plugins/coreplugin/progressmanager/progressbar.cpp index 2dd28712e9..bf6f7d8e19 100644 --- a/src/plugins/coreplugin/progressmanager/progressbar.cpp +++ b/src/plugins/coreplugin/progressmanager/progressbar.cpp @@ -139,6 +139,18 @@ bool ProgressBar::isTitleVisible() const return m_titleVisible; } +void ProgressBar::setSubtitle(const QString &subtitle) +{ + m_subtitle = subtitle; + updateGeometry(); + update(); +} + +QString ProgressBar::subtitle() const +{ + return m_subtitle; +} + void ProgressBar::setSeparatorVisible(bool visible) { if (m_separatorVisible == visible) @@ -176,9 +188,13 @@ QSize ProgressBar::sizeHint() const int width = 50; int height = PROGRESSBAR_HEIGHT + 5; if (m_titleVisible) { - QFontMetrics fm(titleFont()); - width = qMax(width, fm.width(m_title) + 16); + const QFontMetrics fm(titleFont()); + width = qMax(width, fm.horizontalAdvance(m_title) + 16); height += fm.height() + 5; + if (!m_subtitle.isEmpty()) { + width = qMax(width, fm.horizontalAdvance(m_subtitle) + 16); + height += fm.height() + 5; + } } if (m_separatorVisible) height += SEPARATOR_HEIGHT; @@ -227,14 +243,13 @@ void ProgressBar::paintEvent(QPaintEvent *) percent = 1; QPainter p(this); - QFont fnt(titleFont()); - p.setFont(fnt); - QFontMetrics fm(fnt); + const QFont fnt(titleFont()); + const QFontMetrics fm(fnt); - int titleHeight = m_titleVisible ? fm.height() : 0; + const int titleHeight = m_titleVisible ? fm.height() + 5 : 4; // Draw separator - int separatorHeight = m_separatorVisible ? SEPARATOR_HEIGHT : 0; + const int separatorHeight = m_separatorVisible ? SEPARATOR_HEIGHT : 0; if (m_separatorVisible) { QRectF innerRect = QRectF(this->rect()).adjusted(0.5, 0.5, -0.5, -0.5); p.setPen(StyleHelper::sidebarShadow()); @@ -246,28 +261,37 @@ void ProgressBar::paintEvent(QPaintEvent *) } } - if (m_titleVisible) { - QRect textBounds = fm.boundingRect(m_title); - textBounds.moveCenter(rect().center()); - int alignment = Qt::AlignHCenter; + const int progressHeight = PROGRESSBAR_HEIGHT + ((PROGRESSBAR_HEIGHT % 2) + 1) % 2; // make odd + const int progressY = titleHeight + separatorHeight; - int textSpace = rect().width() - 8; + if (m_titleVisible) { + const int alignment = Qt::AlignHCenter; + const int textSpace = rect().width() - 8; // If there is not enough room when centered, we left align and // elide the text - QString elidedtitle = fm.elidedText(m_title, Qt::ElideRight, textSpace); + const QString elidedtitle = fm.elidedText(m_title, Qt::ElideRight, textSpace); QRect textRect = rect().adjusted(3, separatorHeight - 1, -3, 0); - textRect.setHeight(titleHeight + 4); + textRect.setHeight(fm.height() + 4); + p.setFont(fnt); p.setPen(creatorTheme()->color(Theme::ProgressBarTitleColor)); p.drawText(textRect, alignment | Qt::AlignBottom, elidedtitle); + + if (!m_subtitle.isEmpty()) { + const QString elidedsubtitle = fm.elidedText(m_subtitle, Qt::ElideRight, textSpace); + + QRect subtextRect = textRect; + subtextRect.moveTop(progressY + progressHeight); + + p.setFont(fnt); + p.setPen(creatorTheme()->color(Theme::ProgressBarTitleColor)); + p.drawText(subtextRect, alignment | Qt::AlignBottom, elidedsubtitle); + } } - m_progressHeight = PROGRESSBAR_HEIGHT; - m_progressHeight += ((m_progressHeight % 2) + 1) % 2; // make odd // draw outer rect - const QRect rect(INDENT - 1, titleHeight + separatorHeight + (m_titleVisible ? 5 : 4), - size().width() - 2 * INDENT + 1, m_progressHeight); + const QRect rect(INDENT - 1, progressY, size().width() - 2 * INDENT + 1, progressHeight); QRectF inner = rect.adjusted(2, 2, -2, -2); inner.adjust(0, 0, qRound((percent - 1) * inner.width()), 0); diff --git a/src/plugins/coreplugin/progressmanager/progressbar.h b/src/plugins/coreplugin/progressmanager/progressbar.h index 9ff599adbb..108181f54b 100644 --- a/src/plugins/coreplugin/progressmanager/progressbar.h +++ b/src/plugins/coreplugin/progressmanager/progressbar.h @@ -44,6 +44,8 @@ public: void setTitle(const QString &title); void setTitleVisible(bool visible); bool isTitleVisible() const; + void setSubtitle(const QString &subtitle); + QString subtitle() const; void setSeparatorVisible(bool visible); bool isSeparatorVisible() const; void setCancelEnabled(bool enabled); @@ -73,16 +75,17 @@ protected: private: QFont titleFont() const; + QFont subtitleFont() const; QString m_text; QString m_title; + QString m_subtitle; bool m_titleVisible = true; bool m_separatorVisible = true; bool m_cancelEnabled = true; bool m_finished = false; bool m_error = false; float m_cancelButtonFader = 0.0; - int m_progressHeight = 0; int m_minimum = 1; int m_maximum = 100; int m_value = 1; diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp index 725721852f..d822397b90 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp @@ -39,6 +39,7 @@ #include <utils/qtcassert.h> #include <utils/stylehelper.h> #include <utils/theme/theme.h> +#include <utils/utilsicons.h> #include <QAction> #include <QEvent> @@ -291,7 +292,7 @@ void ProgressManagerPrivate::readSettings() { QSettings *settings = ICore::settings(); settings->beginGroup(QLatin1String(kSettingsGroup)); - m_progressViewPinned = settings->value(QLatin1String(kDetailsPinned), false).toBool(); + m_progressViewPinned = settings->value(QLatin1String(kDetailsPinned), true).toBool(); settings->endGroup(); } @@ -319,7 +320,7 @@ void ProgressManagerPrivate::init() m_summaryProgressBar->setCancelEnabled(false); m_summaryProgressLayout->addWidget(m_summaryProgressBar); layout->addWidget(m_summaryProgressWidget); - auto toggleButton = new ToggleButton(m_statusBarWidget); + auto toggleButton = new QToolButton(m_statusBarWidget); layout->addWidget(toggleButton); m_statusBarWidget->installEventFilter(this); StatusBarManager::addStatusBarWidget(m_statusBarWidget, StatusBarManager::RightCorner); @@ -327,10 +328,7 @@ void ProgressManagerPrivate::init() QAction *toggleProgressView = new QAction(tr("Toggle Progress Details"), this); toggleProgressView->setCheckable(true); toggleProgressView->setChecked(m_progressViewPinned); - // we have to set an transparent icon to prevent the tool button to show text - QPixmap p(1, 1); - p.fill(Qt::transparent); - toggleProgressView->setIcon(QIcon(p)); + toggleProgressView->setIcon(Utils::Icons::TOGGLE_PROGRESSDETAILS_TOOLBAR.icon()); Command *cmd = ActionManager::registerAction(toggleProgressView, "QtCreator.ToggleProgressDetails"); @@ -457,6 +455,8 @@ FutureProgress *ProgressManagerPrivate::doAddTask(const QFuture<void> &future, c this, &ProgressManagerPrivate::updateSummaryProgressBar); connect(progress, &FutureProgress::statusBarWidgetChanged, this, &ProgressManagerPrivate::updateStatusDetailsWidget); + connect(progress, &FutureProgress::subtitleInStatusBarChanged, + this, &ProgressManagerPrivate::updateStatusDetailsWidget); updateStatusDetailsWidget(); emit taskStarted(type); @@ -657,9 +657,22 @@ void ProgressManagerPrivate::updateStatusDetailsWidget() QList<FutureProgress *>::iterator i = m_taskList.end(); while (i != m_taskList.begin()) { --i; - candidateWidget = (*i)->statusBarWidget(); + FutureProgress *progress = *i; + candidateWidget = progress->statusBarWidget(); if (candidateWidget) { - m_currentStatusDetailsProgress = *i; + m_currentStatusDetailsProgress = progress; + break; + } else if (progress->isSubtitleVisibleInStatusBar() && !progress->subtitle().isEmpty()) { + if (!m_statusDetailsLabel) { + m_statusDetailsLabel = new QLabel(m_summaryProgressWidget); + QFont font(m_statusDetailsLabel->font()); + font.setPointSizeF(StyleHelper::sidebarFontSize()); + font.setBold(true); + m_statusDetailsLabel->setFont(font); + } + m_statusDetailsLabel->setText(progress->subtitle()); + candidateWidget = m_statusDetailsLabel; + m_currentStatusDetailsProgress = progress; break; } } @@ -697,33 +710,6 @@ void ProgressManagerPrivate::progressDetailsToggled(bool checked) settings->endGroup(); } -ToggleButton::ToggleButton(QWidget *parent) - : QToolButton(parent) -{ - setToolButtonStyle(Qt::ToolButtonIconOnly); - if (creatorTheme()->flag(Theme::FlatToolBars)) { - QPalette p = palette(); - p.setBrush(QPalette::Base, creatorTheme()->color(Theme::ToggleButtonBackgroundColor)); - setPalette(p); - } -} - -QSize ToggleButton::sizeHint() const -{ - return QSize(13, 12); // Uneven width, because the arrow's width is also uneven. -} - -void ToggleButton::paintEvent(QPaintEvent *event) -{ - QToolButton::paintEvent(event); - QPainter p(this); - QStyleOption arrowOpt; - arrowOpt.initFrom(this); - arrowOpt.rect.adjust(2, 0, -1, -2); - StyleHelper::drawArrow(QStyle::PE_IndicatorArrowUp, &p, &arrowOpt); -} - - ProgressManager::ProgressManager() = default; ProgressManager::~ProgressManager() = default; diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_p.h b/src/plugins/coreplugin/progressmanager/progressmanager_p.h index 85bf378159..4eabe50204 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_p.h +++ b/src/plugins/coreplugin/progressmanager/progressmanager_p.h @@ -31,6 +31,7 @@ #include <QList> #include <QGraphicsOpacityEffect> #include <QHBoxLayout> +#include <QLabel> #include <QPointer> #include <QPropertyAnimation> #include <QToolButton> @@ -103,6 +104,7 @@ private: QHBoxLayout *m_summaryProgressLayout; QWidget *m_currentStatusDetailsWidget = nullptr; QPointer<FutureProgress> m_currentStatusDetailsProgress; + QLabel *m_statusDetailsLabel = nullptr; ProgressBar *m_summaryProgressBar; QGraphicsOpacityEffect *m_opacityEffect; QPointer<QPropertyAnimation> m_opacityAnimation; @@ -110,14 +112,5 @@ private: bool m_hovered = false; }; -class ToggleButton : public QToolButton -{ - Q_OBJECT -public: - ToggleButton(QWidget *parent); - QSize sizeHint() const override; - void paintEvent(QPaintEvent *event) override; -}; - } // namespace Internal } // namespace Core diff --git a/src/plugins/coreplugin/rightpane.cpp b/src/plugins/coreplugin/rightpane.cpp index e786051d04..403fe4b0c6 100644 --- a/src/plugins/coreplugin/rightpane.cpp +++ b/src/plugins/coreplugin/rightpane.cpp @@ -153,6 +153,11 @@ void RightPaneWidget::setWidget(QWidget *widget) } } +QWidget *RightPaneWidget::widget() const +{ + return m_widget; +} + int RightPaneWidget::storedWidth() { return m_width; diff --git a/src/plugins/coreplugin/rightpane.h b/src/plugins/coreplugin/rightpane.h index 14026a03e0..ad9117509a 100644 --- a/src/plugins/coreplugin/rightpane.h +++ b/src/plugins/coreplugin/rightpane.h @@ -72,6 +72,7 @@ public: static RightPaneWidget *instance(); void setWidget(QWidget *widget); + QWidget *widget() const; int storedWidth(); diff --git a/src/plugins/coreplugin/shellcommand.cpp b/src/plugins/coreplugin/shellcommand.cpp index a236c906e4..a1ddd083eb 100644 --- a/src/plugins/coreplugin/shellcommand.cpp +++ b/src/plugins/coreplugin/shellcommand.cpp @@ -40,12 +40,17 @@ ShellCommand::ShellCommand(const QString &workingDirectory, const QProcessEnviro this, &ShellCommand::coreAboutToClose); } +FutureProgress *ShellCommand::futureProgress() const +{ + return m_progress.data(); +} + void ShellCommand::addTask(QFuture<void> &future) { const QString name = displayName(); const auto id = Core::Id::fromString(name + QLatin1String(".action")); if (hasProgressParser()) { - ProgressManager::addTask(future, name, id); + m_progress = ProgressManager::addTask(future, name, id); } else { // add a timed tasked based on timeout // we cannot access the future interface directly, so we need to create a new one @@ -58,7 +63,7 @@ void ShellCommand::addTask(QFuture<void> &future) watcher->deleteLater(); }); watcher->setFuture(future); - ProgressManager::addTimedTask(*fi, name, id, qMax(2, timeoutS() / 5)/*itsmagic*/); + m_progress = ProgressManager::addTimedTask(*fi, name, id, qMax(2, timeoutS() / 5)/*itsmagic*/); } } diff --git a/src/plugins/coreplugin/shellcommand.h b/src/plugins/coreplugin/shellcommand.h index a5cd59bfa6..b960bae94d 100644 --- a/src/plugins/coreplugin/shellcommand.h +++ b/src/plugins/coreplugin/shellcommand.h @@ -27,8 +27,12 @@ #include "core_global.h" +#include "progressmanager/futureprogress.h" + #include <utils/shellcommand.h> +#include <QPointer> + namespace Core { class CORE_EXPORT ShellCommand : public Utils::ShellCommand @@ -38,10 +42,15 @@ class CORE_EXPORT ShellCommand : public Utils::ShellCommand public: ShellCommand(const QString &workingDirectory, const QProcessEnvironment &environment); + FutureProgress *futureProgress() const; + protected: void addTask(QFuture<void> &future) override; virtual void coreAboutToClose(); + +private: + QPointer<FutureProgress> m_progress; }; } // namespace Core diff --git a/src/plugins/coreplugin/sidebarwidget.cpp b/src/plugins/coreplugin/sidebarwidget.cpp index daad4cf813..fd561611c2 100644 --- a/src/plugins/coreplugin/sidebarwidget.cpp +++ b/src/plugins/coreplugin/sidebarwidget.cpp @@ -99,7 +99,7 @@ SideBarWidget::SideBarWidget(SideBar *sideBar, const QString &id) } setCurrentItem(t); - connect(m_comboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SideBarWidget::setCurrentIndex); } diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp index 584cfb0ca9..b6a435d36a 100644 --- a/src/plugins/coreplugin/systemsettings.cpp +++ b/src/plugins/coreplugin/systemsettings.cpp @@ -68,13 +68,13 @@ QWidget *SystemSettings::widget() m_widget = new QWidget; m_page->setupUi(m_widget); m_page->terminalOpenArgs->setToolTip( - tr("Command line arguments used for \"%1\".").arg(FileUtils::msgTerminalAction())); + tr("Command line arguments used for \"%1\".").arg(FileUtils::msgTerminalHereAction())); m_page->reloadBehavior->setCurrentIndex(EditorManager::reloadSetting()); if (HostOsInfo::isAnyUnixHost()) { const QVector<TerminalCommand> availableTerminals = ConsoleProcess::availableTerminalEmulators(); for (const TerminalCommand &term : availableTerminals) - m_page->terminalComboBox->addItem(term.command, qVariantFromValue(term)); + m_page->terminalComboBox->addItem(term.command, QVariant::fromValue(term)); updateTerminalUi(ConsoleProcess::terminalEmulator(ICore::settings())); connect(m_page->terminalComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), diff --git a/src/plugins/coreplugin/toolsettings.cpp b/src/plugins/coreplugin/toolsettings.cpp index 857d154125..200e425140 100644 --- a/src/plugins/coreplugin/toolsettings.cpp +++ b/src/plugins/coreplugin/toolsettings.cpp @@ -143,7 +143,7 @@ void ToolSettings::apply() if (tool->preset() && (*tool) != (*(tool->preset()))) { // check if we need to choose a new file name if (tool->preset()->fileName() == tool->fileName()) { - const QString &fileName = Utils::FileName::fromString(tool->preset()->fileName()).fileName(); + const QString &fileName = Utils::FilePath::fromString(tool->preset()->fileName()).fileName(); const QString &newFilePath = getUserFilePath(fileName); // TODO error handling if newFilePath.isEmpty() (i.e. failed to find a unused name) tool->setFileName(newFilePath); |