From 70f7e275e25cbecc53ebe509ffeb21a1aa8592f4 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 16 Feb 2024 16:00:12 +0100 Subject: [PATCH 001/128] EditorManager: Avoid warnings about already registered actions When opening new editor windows, we want the _window_ to have the EditorManager context as well as the individual window context, but the window _actions_ (close, etc) may only be registered for the individual window context. Add the corresponding option to ICore::registerWindow et al. Change-Id: I67d0a6b386603e0047a77dfb357c207e7ffe99e6 Reviewed-by: David Schulz Reviewed-by: Qt CI Bot --- .../coreplugin/editormanager/editorwindow.cpp | 6 ++++-- src/plugins/coreplugin/icore.cpp | 7 ++++--- src/plugins/coreplugin/icore.h | 4 +++- src/plugins/coreplugin/windowsupport.cpp | 15 ++++++++------- src/plugins/coreplugin/windowsupport.h | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/plugins/coreplugin/editormanager/editorwindow.cpp b/src/plugins/coreplugin/editormanager/editorwindow.cpp index 3e23a7285ed..fbd60ebc15b 100644 --- a/src/plugins/coreplugin/editormanager/editorwindow.cpp +++ b/src/plugins/coreplugin/editormanager/editorwindow.cpp @@ -47,9 +47,11 @@ EditorWindow::EditorWindow(QWidget *parent) : static int windowId = 0; + const Utils::Id windowContext + = Utils::Id("EditorManager.ExternalWindow.").withSuffix(++windowId); ICore::registerWindow(this, - Context(Utils::Id("EditorManager.ExternalWindow.").withSuffix(++windowId), - Constants::C_EDITORMANAGER)); + Context(windowContext, Constants::C_EDITORMANAGER), + Context(windowContext)); connect(m_area, &EditorArea::windowTitleNeedsUpdate, this, &EditorWindow::updateWindowTitle); diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index abee4c08ab0..ef11fd6d161 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -1011,14 +1011,15 @@ void ICore::removeAdditionalContext(const Context &context) Registers a \a window with the specified \a context. Registered windows are shown in the \uicontrol Window menu and get registered for the various window related actions, like the minimize, zoom, fullscreen and close - actions. + actions. The context for the actions is \a context by default, but can be + overridden with \a actionContext. Whenever the application focus is in \a window, its \a context is made active. */ -void ICore::registerWindow(QWidget *window, const Context &context) +void ICore::registerWindow(QWidget *window, const Context &context, const Context &actionContext) { - new WindowSupport(window, context); // deletes itself when widget is destroyed + new WindowSupport(window, context, actionContext); // deletes itself when widget is destroyed } /*! diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 215cb94ba86..8c713e156f4 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -98,7 +98,9 @@ public: static void addContextObject(IContext *context); static void removeContextObject(IContext *context); - static void registerWindow(QWidget *window, const Context &context); + static void registerWindow(QWidget *window, + const Context &context, + const Context &actionContext = {}); static void restartTrimmer(); enum OpenFilesFlags { diff --git a/src/plugins/coreplugin/windowsupport.cpp b/src/plugins/coreplugin/windowsupport.cpp index 4cbfb57d038..25ab34793c7 100644 --- a/src/plugins/coreplugin/windowsupport.cpp +++ b/src/plugins/coreplugin/windowsupport.cpp @@ -28,9 +28,9 @@ namespace Internal { Q_GLOBAL_STATIC(WindowList, m_windowList) -WindowSupport::WindowSupport(QWidget *window, const Context &context) - : QObject(window), - m_window(window) +WindowSupport::WindowSupport(QWidget *window, const Context &context, const Context &actionContext) + : QObject(window) + , m_window(window) { m_window->installEventFilter(this); @@ -38,14 +38,15 @@ WindowSupport::WindowSupport(QWidget *window, const Context &context) m_contextObject->setWidget(window); m_contextObject->setContext(context); ICore::addContextObject(m_contextObject); + const Context ac = actionContext.isEmpty() ? context : actionContext; if (useMacShortcuts) { m_minimizeAction = new QAction(this); - ActionManager::registerAction(m_minimizeAction, Constants::MINIMIZE_WINDOW, context); + ActionManager::registerAction(m_minimizeAction, Constants::MINIMIZE_WINDOW, ac); connect(m_minimizeAction, &QAction::triggered, m_window, &QWidget::showMinimized); m_zoomAction = new QAction(this); - ActionManager::registerAction(m_zoomAction, Constants::ZOOM_WINDOW, context); + ActionManager::registerAction(m_zoomAction, Constants::ZOOM_WINDOW, ac); connect(m_zoomAction, &QAction::triggered, m_window, [this] { if (m_window->isMaximized()) { // similar to QWidget::showMaximized @@ -58,13 +59,13 @@ WindowSupport::WindowSupport(QWidget *window, const Context &context) }); m_closeAction = new QAction(this); - ActionManager::registerAction(m_closeAction, Constants::CLOSE_WINDOW, context); + ActionManager::registerAction(m_closeAction, Constants::CLOSE_WINDOW, ac); connect(m_closeAction, &QAction::triggered, m_window, &QWidget::close, Qt::QueuedConnection); } m_toggleFullScreenAction = new QAction(this); updateFullScreenAction(); - ActionManager::registerAction(m_toggleFullScreenAction, Constants::TOGGLE_FULLSCREEN, context); + ActionManager::registerAction(m_toggleFullScreenAction, Constants::TOGGLE_FULLSCREEN, ac); connect(m_toggleFullScreenAction, &QAction::triggered, this, &WindowSupport::toggleFullScreen); m_windowList->addWindow(window); diff --git a/src/plugins/coreplugin/windowsupport.h b/src/plugins/coreplugin/windowsupport.h index c1b48d39179..bfdc969ffb0 100644 --- a/src/plugins/coreplugin/windowsupport.h +++ b/src/plugins/coreplugin/windowsupport.h @@ -40,7 +40,7 @@ class WindowSupport : public QObject { Q_OBJECT public: - WindowSupport(QWidget *window, const Context &context); + WindowSupport(QWidget *window, const Context &context, const Context &actionContext = {}); ~WindowSupport() override; void setCloseActionEnabled(bool enabled); From 814e84a5e7e1898962a989432f9d67f0bc3128c7 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 19 Feb 2024 09:39:22 +0100 Subject: [PATCH 002/128] Fix the window list when windows are closed After a window is closed we don't only have to update the texts of the action, but also update their visibility state and the checked item. Fixes: QTCREATORBUG-30381 Change-Id: Id0c343b8a5930ec2e3de03f71bdc8f2628b492b3 Reviewed-by: David Schulz Reviewed-by: Qt CI Bot --- src/plugins/coreplugin/windowsupport.cpp | 42 ++++++++++++++---------- src/plugins/coreplugin/windowsupport.h | 5 +-- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/plugins/coreplugin/windowsupport.cpp b/src/plugins/coreplugin/windowsupport.cpp index 25ab34793c7..ad296e65819 100644 --- a/src/plugins/coreplugin/windowsupport.cpp +++ b/src/plugins/coreplugin/windowsupport.cpp @@ -15,8 +15,8 @@ #include #include +#include #include -#include #include #include #include @@ -106,11 +106,8 @@ bool WindowSupport::eventFilter(QObject *obj, QEvent *event) updateFullScreenAction(); } else if (event->type() == QEvent::WindowActivate) { m_windowList->setActiveWindow(m_window); - } else if (event->type() == QEvent::Hide) { - // minimized windows are hidden, but we still want to show them - m_windowList->setWindowVisible(m_window, m_window->isMinimized()); - } else if (event->type() == QEvent::Show) { - m_windowList->setWindowVisible(m_window, true); + } else if (event->type() == QEvent::Hide || event->type() == QEvent::Show) { + m_windowList->updateVisibility(m_window); } return false; } @@ -182,9 +179,9 @@ void WindowList::activateWindow(QAction *action) ICore::raiseWindow(m_windows.at(index)); } -void WindowList::updateTitle(QWidget *window) +void WindowList::updateTitle(QWidget *window, int i) { - int index = m_windows.indexOf(window); + const int index = i < 0 ? m_windows.indexOf(window) : i; QTC_ASSERT(index >= 0, return); QTC_ASSERT(index < m_windowActions.size(), return); QString title = window->windowTitle(); @@ -193,6 +190,19 @@ void WindowList::updateTitle(QWidget *window) m_windowActions.at(index)->setText(Utils::quoteAmpersands(title.trimmed())); } +void WindowList::updateVisibility(QWidget *window) +{ + updateVisibility(window, m_windows.indexOf(window)); +} + +void WindowList::updateVisibility(QWidget *window, int index) +{ + QTC_ASSERT(index >= 0, return); + QTC_ASSERT(index < m_windowActions.size(), return); + // minimized windows are hidden, but we still want to show them + m_windowActions.at(index)->setVisible(window->isVisible() || window->isMinimized()); +} + void WindowList::removeWindow(QWidget *window) { // remove window from list, @@ -207,8 +217,12 @@ void WindowList::removeWindow(QWidget *window) m_windows.removeOne(window); - for (int i = index; i < m_windows.size(); ++i) - updateTitle(m_windows.at(i)); + for (int i = index; i < m_windows.size(); ++i) { + QWidget *window = m_windows.at(i); + updateTitle(window, i); + updateVisibility(window, i); + } + setActiveWindow(QApplication::activeWindow()); } void WindowList::setActiveWindow(QWidget *window) @@ -217,13 +231,5 @@ void WindowList::setActiveWindow(QWidget *window) m_windowActions.at(i)->setChecked(m_windows.at(i) == window); } -void WindowList::setWindowVisible(QWidget *window, bool visible) -{ - int index = m_windows.indexOf(window); - QTC_ASSERT(index >= 0, return); - QTC_ASSERT(index < m_windowActions.size(), return); - m_windowActions.at(index)->setVisible(visible); -} - } // Internal } // Core diff --git a/src/plugins/coreplugin/windowsupport.h b/src/plugins/coreplugin/windowsupport.h index bfdc969ffb0..8b7eb026afd 100644 --- a/src/plugins/coreplugin/windowsupport.h +++ b/src/plugins/coreplugin/windowsupport.h @@ -24,11 +24,12 @@ public: void addWindow(QWidget *window); void removeWindow(QWidget *window); void setActiveWindow(QWidget *window); - void setWindowVisible(QWidget *window, bool visible); + void updateVisibility(QWidget *window); private: void activateWindow(QAction *action); - void updateTitle(QWidget *window); + void updateTitle(QWidget *window, int index = -1); + void updateVisibility(QWidget *window, int index); QMenu *m_dockMenu = nullptr; QList m_windows; From 8a6d94235bb90f8fdea760ce7e75023270658d59 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 19 Feb 2024 09:47:06 +0100 Subject: [PATCH 003/128] Fix that window actions did not bring up minimized windows Selecting a window from the Window menu didn't bring up minimized windows from their minimized state, and resulted in two items being checked in the window list (because the active window did not change, so the checkmarks where not updated). Change-Id: Ie89b87fa1273378bb99f69026a58ee92ea0b8f50 Reviewed-by: Qt CI Bot Reviewed-by: David Schulz --- src/plugins/coreplugin/windowsupport.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/coreplugin/windowsupport.cpp b/src/plugins/coreplugin/windowsupport.cpp index ad296e65819..5c5cbbfe133 100644 --- a/src/plugins/coreplugin/windowsupport.cpp +++ b/src/plugins/coreplugin/windowsupport.cpp @@ -176,7 +176,10 @@ void WindowList::activateWindow(QAction *action) int index = m_windowActions.indexOf(action); QTC_ASSERT(index >= 0, return); QTC_ASSERT(index < m_windows.size(), return); - ICore::raiseWindow(m_windows.at(index)); + QWidget *window = m_windows.at(index); + if (window->isMinimized()) + window->setWindowState(window->windowState() & ~Qt::WindowMinimized); + ICore::raiseWindow(window); } void WindowList::updateTitle(QWidget *window, int i) From 76539a659e9cc10a6019b3f5d3a5851b761e58f5 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Thu, 15 Feb 2024 17:00:09 +0100 Subject: [PATCH 004/128] Doc: Update the IDE Overview Use sections instead of a table and move or copy text from other topics. This is to create a place for information that becomes homeless when I remove other topics. Task-number: QTCREATORBUG-29361 Change-Id: Ib3fcc2c345080e05f9f7ad4ed78e049c899e0855 Reviewed-by: Eike Ziller --- doc/qtcreator/images/heartgame-start.webp | Bin 0 -> 25644 bytes doc/qtcreator/images/qt-app-dev-flow.webp | Bin 0 -> 123844 bytes doc/qtcreator/images/qt-tools.webp | Bin 0 -> 53570 bytes .../creator-only/creator-debugger.qdoc | 12 - .../src/editors/creator-code-refactoring.qdoc | 14 - .../creator-only/creator-getting-started.qdoc | 4 +- .../creator-only/creator-overview.qdoc | 326 ++++++++++++------ .../creator-only/creator-testing.qdoc | 2 +- doc/qtcreator/src/qtcreator-toc.qdoc | 2 +- doc/qtcreator/src/qtcreator.qdoc | 2 +- 10 files changed, 216 insertions(+), 146 deletions(-) create mode 100644 doc/qtcreator/images/heartgame-start.webp create mode 100644 doc/qtcreator/images/qt-app-dev-flow.webp create mode 100644 doc/qtcreator/images/qt-tools.webp diff --git a/doc/qtcreator/images/heartgame-start.webp b/doc/qtcreator/images/heartgame-start.webp new file mode 100644 index 0000000000000000000000000000000000000000..9fef6fe00579fcc7917e8e3e7f5ef6c1628cf496 GIT binary patch literal 25644 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

~^I8!DQR-HzyyBy!YFHAOomyBoG5O}3) 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 literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qt-app-dev-flow.webp b/doc/qtcreator/images/qt-app-dev-flow.webp new file mode 100644 index 0000000000000000000000000000000000000000..14be8fb3d70f5e2fc397f0eb5abdd4dd79667286 GIT binary patch literal 123844 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 Di@l9X literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qt-tools.webp b/doc/qtcreator/images/qt-tools.webp new file mode 100644 index 0000000000000000000000000000000000000000..986846140fae6d8222333ba94d4b0abcbc52325c GIT binary patch literal 53570 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 literal 0 HcmV?d00001 diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc index 0676655ec52..914ee674650 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc @@ -14,18 +14,6 @@ \title Debugging - A debugger lets you see what happens \e inside an application while it runs - or when it crashes. A debugger can do the following to help you find errors - in the application: - - \list - \li Start the application with parameters that specify its behavior. - \li Stop the application when conditions are met. - \li Examine what happens when the application stops. - \li Make changes in the application when you fix an error and continue - to find the next one. - \endlist - The \QC debugger plugin acts as an interface between the \QC core and external native debuggers that you can use to: diff --git a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc index 285c36166bd..413ffe9ef54 100644 --- a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc +++ b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc @@ -8,20 +8,6 @@ \title Refactoring - \e {Code refactoring} is the process of improving and simplifying code - without modifying the existing functionality of an application. You - can easily find and rename symbols and apply predefined actions to - refactor code. - - Refactor code to: - - \list - \li Improve internal quality of your application - \li Improve performance and extensibility - \li Improve code readability and maintainability - \li Simplify code structure - \endlist - To quickly and conveniently apply actions to refactor your code, \l{Apply quick fixes}{select quick fixes in a context menu}. diff --git a/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc b/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc index 68ca9e4ef54..115e666d0b9 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc @@ -25,11 +25,11 @@ \li \inlineimage front-ui.png \li \inlineimage front-advanced.png \row - \li \b {\l{IDE Overview}} + \li \b {\l{Overview}} If you have not used an integrated development environment (IDE) before, or want to know what kind of IDE \QC is, go to - \l{IDE Overview}. + \l{Overview}. \li \b {\l{User Interface}} If you have not used \QC before, and want to become familiar diff --git a/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc b/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc index f951b4b52a7..eb0bcf8ec36 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -12,147 +12,243 @@ \page creator-overview.html \nextpage creator-quick-tour.html - \title IDE Overview + \title Overview - \QC is an integrated development environment (IDE) that has tools for - designing and developing applications with the Qt application framework. - With Qt you can develop applications and user interfaces once and deploy - them to several desktop, embedded, and mobile operating systems or - web browsers (experimental). \QC has the tools for accomplishing your tasks + \QC is a cross-platform, complete integrated development environment + (IDE) that you can use to create applications for desktop, embedded, + and mobile operating systems, or web browsers. + + With Qt, you can develop applications and user interfaces once and deploy + them to many platforms. \QC has the tools for accomplishing your tasks throughout the whole application development life-cycle, from creating a - project to deploying the application to the target platforms. + project, designing a UI, and writing code to building applications and + deploying them to the target platforms for running and debugging. - \table - \row - \li \inlineimage front-projects.png - \li \inlineimage front-ui.png - \li \inlineimage front-coding.png - \row - \li \b {Managing Projects} + \image qt-app-dev-flow.webp {Application development life-cycle} + \caption Application development life-cycle - To be able to build and run applications, \QC needs the same - information as a compiler would need. It stores the information - in the project settings. + \section1 Projects - You can share projects with other designers and developers across - different development platforms with a common tool for design, - development, and debugging. + First, you need a \e project. \QC relies on a separate build system, such as + CMake, qmake, or Qbs for building the project. From the build system, \QC + gets most of the information it needs to offer services for writing, editing, + and navigating source code, as well as to deploy and run applications. It + stores additional information in the project settings. - \list - \li \l{Creating Projects} + Share projects with other designers and developers across different + development platforms with a common tool for design, development, and + debugging. - To set up a project, you first have to decide what kind - of an application you want to develop: do you want a user - interface based on \l{User Interfaces} - {Qt Quick or Qt Widgets}. Second, you have to choose the - programming language to implement the application logic: - C++ or Python. - \li \l{Version Control Systems} + \list + \li \l{Creating Projects} - The recommended way to set up a project is to use a - version control system. Store and edit only project - source files and configuration files. Do not store - generated files. - \li \l{Configuring Projects} + To set up a project, you first have to decide what kind + of an application you want to develop: do you want a user + interface based on \l{User Interfaces} + {Qt Quick or Qt Widgets}. Second, you have to choose the + programming language to implement the application logic: + C++ or Python. + \li \l{Version Control Systems} - Installation programs and project wizards create default - configurations for \QC and your projects. You can change - the configurations in the \uicontrol Projects mode. - \endlist - For more information, see \l{Manage Projects} - {How To: Manage Projects}. - \li \b {Designing User Interfaces} + The recommended way to set up a project is to use a + version control system. Store and edit only project + source files and configuration files. Do not store + generated files. + \li \l{Configuring Projects} - To create intuitive, modern-looking, fluid user interfaces, you - can use \l{Qt Quick} and \l{Qt Design Studio Manual}{\QDS}: + Installation programs and project wizards create default + configurations for \QC and your projects. Change the + configurations in the \uicontrol Projects mode. + \endlist - \list - \li \l {\QMLD} + For more information, see \l{Manage Projects}{How To: Manage Projects}. - Or, you can enable the \QMLD plugin to visually edit - \l{UI Files}{UI files} (.ui.qml). - \li \l {Converting UI Projects to Applications} + \section1 User Interfaces - Qt Quick UI Prototype projects (.qmlproject) are useful - for creating user interfaces. To use them for application - development, you have to convert them to Qt Quick - Application projects that have project configuration - files (CMakeLists.txt or .pro), .cpp, and .qrc files. - \li \l {UI Files} + \image heartgame-start.webp {Heart Rate Game} - If you switch between \QC and \QDS or cooperate with - designers on a project, you might encounter .ui.qml files. - They are intended to be edited in \QDS only, so you need - to be careful not to break the code. To visually edit the - files in \QC, enable the \QMLD plugin. - \li \l{Using QML Modules with Plugins} + To create intuitive, modern-looking, fluid user interfaces, use \l{Qt Quick} + and \l{Qt Design Studio Manual}{\QDS}: - You can load C++ plugins for QML to simulate data. - \endlist + \list + \li \l {\QMLD} - If you need a traditional user interface that has a clear - structure and enforces a platform look and feel, use - \l{Qt Widgets} and the integrated \l{\QD}. + Or, enable the \QMLD plugin to visually edit \l{UI Files}{UI files} + (.ui.qml). + \li \l {Converting UI Projects to Applications} - For more information, see - \l{Design UIs}{How To: Design UIs}. - \li \b {\l{Coding}} + Qt Quick UI Prototype projects (.qmlproject) are useful + for creating user interfaces. To use them for application + development, you have to convert them to Qt Quick + Application projects that have project configuration + files (CMakeLists.txt or .pro), .cpp, and .qrc files. + \li \l {UI Files} - As an IDE, \QC differs from a text editor in that it knows how - to build and run applications. It understands the C++ and QML - languages as code, not just as plain text. Therefore, it can - offer useful features, such as semantic highlighting, - checking code syntax, code completion, and refactoring actions. - \QC supports some of these services also for other programming - languages, such as Python, for which a \e {language server} is - available that provides information about the code to IDEs. + If you switch between \QC and \QDS or cooperate with + designers on a project, you might encounter .ui.qml files. + They are intended to be edited in \QDS only, so you need + to be careful not to break the code. To visually edit the + files in \QC, enable the \QMLD plugin. + \li \l{Using QML Modules with Plugins} - For more information, see \l{Edit Code}{How To: Edit Code}. - \row - \li \inlineimage front-preview.png - \li \inlineimage front-testing.png - \li \inlineimage front-publishing.png - \row - \li \b {\l{Building and Running}} + Load C++ plugins for QML to simulate data. + \endlist - \QC integrates cross-platform systems for build - automation: qmake, Qbs, CMake, and Autotools. In addition, you - can import - projects as \e {generic projects} and fully control the steps - and commands used to build the project. + If you need a traditional user interface that has a clear structure and + enforces a platform look and feel, use \l{Qt Widgets} and the integrated + \l{\QD}. - You can build applications for, deploy them to, and run them on - the desktop environment or a \l{glossary-device}{device}. - \l{glossary-buildandrun-kit}{Kits}, build, run, and deployment - settings allow you to quickly switch between different setups and - target platforms. + For more information, see \l{Design UIs}{How To: Design UIs} and + \l{UI Design}. - For more information, see \l{Build and Run} - {How To: Build and Run}. - \li \b {\l{Testing}} + \section1 Code - \QC integrates several external native debuggers that you can use - to inspect the state of your application while debugging. + Writing, editing, and navigating in source code are core tasks in application + development. Therefore, the code editor is one of the key components of \QC. + Use the code editor in the \l {Edit Mode}{Edit mode}. - Devices have limited memory and CPU power, so you should use them - carefully. \QC integrates code analysis tools for detecting - memory leaks, profiling function execution, analyzing CPU use, - and eliminating unnecessary complexity of code. Other tools - provide code coverage and visualize trace events. + As an IDE, \QC differs from a text editor in that it knows how to build and + run applications. It understands the C++ and QML languages as code, not just + as plain text. Therefore, it can offer useful features, such as semantic + highlighting, checking code syntax, code completion, and refactoring actions. - \QC integrates several testing frameworks for unit testing - applications and libraries. You can use \QC to create, build, - and run autotests. + \QC supports some of these services also for other programming languages, + such as Python, for which a \e {language server} is available that provides + information about the code to IDEs. - For more information, see \l{Testing}. - \li \b {Publishing} + \section2 Find - \QC enables you to create installation packages for mobile - devices that you can publish to application stores - and other channels. You must make sure that the package contents - meet the requirements for publishing on the channel. + Use the incremental and advanced search to search in currently open projects + or files on the file system or use the locator to browse through projects, + files, classes, functions, documentation, and file systems. - For more information, see \l{Publishing to Google Play}. -\endtable + \section2 Refactor + \e {Code refactoring} is the process of improving and simplifying code + without modifying the existing functionality of an application. Find + and rename symbols and apply predefined actions to refactor code. + + Refactor code to: + + \list + \li Improve internal quality of your application + \li Improve performance and extensibility + \li Improve code readability and maintainability + \li Simplify code structure + \endlist + + \section2 Configure the Editor + + Configure the text editor to suit your specific needs. Change the fonts, + colors, highlighting, and indentation. + + If you are used to the Vim editor, run the main editor in the + \l {FakeVim Modes and Commands}{FakeVim mode}. + + For more information, see \l{Edit Code}{How To: Edit Code} and \l{Editors}. + + \section1 Build, Deploy, and Run + + Run and deploy Qt applications that you build for different target + platforms or with different compilers, debuggers, or Qt versions. + \l{glossary-buildandrun-kit}{Kits} define the tools, \l{glossary-device} + {device} type and other settings to use when building and running your + project. + + \QC integrates cross-platform systems for build automation: CMake, + qmake, Qbs, and Autotools. In addition, you can import projects as + \e {generic projects} and fully control the steps and commands to + build the project. + + Build applications for, deploy them to, and run them on the desktop + environment or a device. With kits, as well as build, run, and deployment + configurations, you can quickly switch between different setups and + target platforms. + + For more information, see \l{Build and Run}{How To: Build and Run}, + \l{Build Systems}, \l{Build Configurations}, and \l{Run Configurations}. + + \section2 On Devices + + When you install tool chains for device types as part of a Qt distribution, + the build and run configurations for the devices might be set up + automatically. However, you might need to install and configure some + additional software on the devices to be able to connect to them + from the computer. + + Deployment configurations handle the packaging and copying of the necessary + files to a location you want to run the executable at, such as the file + system of a device. + + For more information, see \l{Connecting Devices} and \l{Deploying to Devices}. + + \section2 Preview QML + + Use the QML live preview to preview a QML file or an entire Qt Quick + application on the desktop, as well as on Android and embedded Linux + devices. The changes you make to the UI are instantly visible to you + in the preview. + + For more information, see \l{Validating with Target Hardware}. + + \section1 Debug + + A debugger lets you see what happens \e inside an application while it runs + or when it crashes. A debugger can do the following to help you find errors + in the application: + + \list + \li Start the application with parameters that specify its behavior. + \li Stop the application when conditions are met. + \li Examine what happens when the application stops. + \li Make changes in the application when you fix an error and continue + to find the next one. + \endlist + + \QC integrates several external native debuggers for inspecting the state of + your application while debugging. The debugger plugin automatically selects + a suitable native debugger for each kit from the ones it finds on the + computer. Edit the kits to override this choice. + + Connect devices to your computer to debug processes running on the devices. + + For more information, see \l{Debugging}. + + \section1 Analyze + + Devices have limited memory and CPU power, so you should use them carefully. + \QC integrates code analysis tools for detecting memory leaks, profiling + function execution, analyzing CPU use, and eliminating unnecessary complexity + of code. Other tools provide code coverage and visualize trace events. + + Install and configure the tools on your system to use them from \QC. + However, the QML Profiler is installed as part of \QC for profiling + Qt Quick applications. + + For more information, see \l{Analyzing Code}. + + \section1 Autotest + + Create, build and run Qt tests, Qt Quick tests, Google tests, and Boost tests + to unit test applications and libraries. + + Map AUTs (Application Under Test) to \QC and run Squish test suites + and cases from it. + + For more information, see \l{Running Autotests} and \l{Using Squish}. + + \section1 Publish + + Create installation packages for mobile devices that you publish to + application stores and other channels. You must make sure that the + package contents meet the requirements for publishing on the channel. + + For more information, see \l{Publishing to Google Play}. + + \section1 Qt Tools + + \QC is one of many Qt tools for designing and developing applications. + + \image qt-tools.webp {Tools for Qt application development} + \caption Tools for Qt application development */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-testing.qdoc b/doc/qtcreator/src/overview/creator-only/creator-testing.qdoc index ec77c0c54aa..26ef6701d69 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-testing.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-testing.qdoc @@ -20,7 +20,7 @@ \li \l{Debugging} - If you install \QC as part of \QSDK, the GNU Symbolic Debugger + If you install \QC with \QOI, the GNU Symbolic Debugger is installed automatically and you should be ready to start debugging after you create a new project. However, you can change the setup to use debugging tools for Windows, for diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc index 9038a168073..933fadb3a0e 100644 --- a/doc/qtcreator/src/qtcreator-toc.qdoc +++ b/doc/qtcreator/src/qtcreator-toc.qdoc @@ -15,7 +15,7 @@ \list \li \l{Getting Started} \list - \li \l{IDE Overview} + \li \l{Overview} \list \li \l{Creating Projects} \li \l{Configuring Projects} diff --git a/doc/qtcreator/src/qtcreator.qdoc b/doc/qtcreator/src/qtcreator.qdoc index 85c60896ac7..eaceebf177d 100644 --- a/doc/qtcreator/src/qtcreator.qdoc +++ b/doc/qtcreator/src/qtcreator.qdoc @@ -41,7 +41,7 @@ \row \li \b {\l{Getting Started}} \list - \li \l{IDE Overview} + \li \l{Overview} \li \l{User Interface} \li \l{Configuring Qt Creator} \li \l{Building and Running an Example} From 5e2854e6858d7145de6e2f9cdf2586fbeb139093 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 13:27:47 +0100 Subject: [PATCH 005/128] SemanticHighlighter: Don't delete the watcher from its signal handler Use std::unique_ptr instead, as QScopedPointer doesn't offer release(). Change-Id: I8e885e477d1aeb8ce9462e8fd249a5196424df18 Reviewed-by: Qt CI Bot Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/semantichighlighter.cpp | 10 +++++----- src/plugins/cppeditor/semantichighlighter.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp index fe52bbdebd4..4cafaecd46f 100644 --- a/src/plugins/cppeditor/semantichighlighter.cpp +++ b/src/plugins/cppeditor/semantichighlighter.cpp @@ -232,25 +232,25 @@ void SemanticHighlighter::onHighlighterFinished() TextDocumentLayout::setParentheses(currentBlock, getClearedParentheses(currentBlock)); } - m_watcher.reset(); + m_watcher.release()->deleteLater(); qCDebug(log) << "onHighlighterFinished() took" << t.elapsed() << "ms"; } void SemanticHighlighter::connectWatcher() { using Watcher = QFutureWatcher; - connect(m_watcher.data(), &Watcher::resultsReadyAt, + connect(m_watcher.get(), &Watcher::resultsReadyAt, this, &SemanticHighlighter::onHighlighterResultAvailable); - connect(m_watcher.data(), &Watcher::finished, + connect(m_watcher.get(), &Watcher::finished, this, &SemanticHighlighter::onHighlighterFinished); } void SemanticHighlighter::disconnectWatcher() { using Watcher = QFutureWatcher; - disconnect(m_watcher.data(), &Watcher::resultsReadyAt, + disconnect(m_watcher.get(), &Watcher::resultsReadyAt, this, &SemanticHighlighter::onHighlighterResultAvailable); - disconnect(m_watcher.data(), &Watcher::finished, + disconnect(m_watcher.get(), &Watcher::finished, this, &SemanticHighlighter::onHighlighterFinished); } diff --git a/src/plugins/cppeditor/semantichighlighter.h b/src/plugins/cppeditor/semantichighlighter.h index ea49f289a01..a0d8e8db9ee 100644 --- a/src/plugins/cppeditor/semantichighlighter.h +++ b/src/plugins/cppeditor/semantichighlighter.h @@ -6,11 +6,11 @@ #include "cppeditor_global.h" #include -#include #include #include #include +#include #include namespace TextEditor { @@ -80,7 +80,7 @@ private: TextEditor::TextDocument *m_baseTextDocument; unsigned m_revision = 0; - QScopedPointer> m_watcher; + std::unique_ptr> m_watcher; QHash m_formatMap; std::set m_seenBlocks; int m_nextResultToHandle = 0; From 5b48d23a0ab8a75cd4edd159162e988466bdac75 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 17 Feb 2024 08:16:24 +0100 Subject: [PATCH 006/128] Axivion: Fix CredentialOperation::Get Don't report an error on non-existing key read. Finish with success and provide empty optional data in this case. Change-Id: I7c4ea50c0f05e13ce793384ec57d50dfff600c9a Reviewed-by: hjk --- src/plugins/axivion/credentialquery.cpp | 10 ++++++---- src/plugins/axivion/credentialquery.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins/axivion/credentialquery.cpp b/src/plugins/axivion/credentialquery.cpp index 7393845819a..14aabc3958e 100644 --- a/src/plugins/axivion/credentialquery.cpp +++ b/src/plugins/axivion/credentialquery.cpp @@ -24,7 +24,8 @@ void CredentialQueryTaskAdapter::start() } case CredentialOperation::Set: { WritePasswordJob *writer = new WritePasswordJob(task()->m_service); - writer->setBinaryData(task()->m_data); + if (task()->m_data) + writer->setBinaryData(*task()->m_data); job = writer; break; } @@ -38,11 +39,12 @@ void CredentialQueryTaskAdapter::start() m_guard.reset(job); connect(job, &Job::finished, this, [this, reader](Job *job) { - if (job->error() != NoError) + const bool success = job->error() == NoError || job->error() == EntryNotFound; + if (!success) task()->m_errorString = job->errorString(); - else if (reader) + else if (reader && job->error() == NoError) task()->m_data = reader->binaryData(); - emit done(toDoneResult(job->error() == NoError)); + emit done(toDoneResult(success)); m_guard.release()->deleteLater(); }); job->start(); diff --git a/src/plugins/axivion/credentialquery.h b/src/plugins/axivion/credentialquery.h index 6440cd51043..6b23c5f4669 100644 --- a/src/plugins/axivion/credentialquery.h +++ b/src/plugins/axivion/credentialquery.h @@ -17,14 +17,14 @@ public: void setKey(const QString &key) { m_key = key; } void setData(const QByteArray &data) { m_data = data; } - QByteArray data() const { return m_data; } + std::optional data() const { return m_data; } QString errorString() const { return m_errorString; } private: CredentialOperation m_operation = CredentialOperation::Get; QString m_service; QString m_key; - QByteArray m_data; // Used for input when Set and for output when Get. + std::optional m_data; // Used for input when Set and for output when Get. QString m_errorString; friend class CredentialQueryTaskAdapter; }; From 35c49afae2e0a1120a6d098e48a00130a74940dc Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sun, 11 Feb 2024 18:22:39 +0100 Subject: [PATCH 007/128] Axivion: Implement ApiToken querying and storing The ApiToken is stored locally safely by using QKeychain lib. If the local store doesn't contain the ApiToken, we show the password input dialog to the user. If the user pressed OK, we send a network query to axivion server to retrieve the ApiToken for the username + password. Change-Id: I0dc19aea67750cd674ae4db9a9f469e4621c713b Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 177 +++++++++++++++++++++----- 1 file changed, 148 insertions(+), 29 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 9b333af7c3e..9891bdc3c65 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -7,6 +7,7 @@ #include "axivionprojectsettings.h" #include "axivionsettings.h" #include "axiviontr.h" +#include "credentialquery.h" #include "dashboard/dto.h" #include "dashboard/error.h" @@ -31,11 +32,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -43,7 +46,8 @@ #include -constexpr char AxivionTextMarkId[] = "AxivionTextMark"; +constexpr char s_axivionTextMarkId[] = "AxivionTextMark"; +constexpr char s_axivionKeychainService[] = "keychain.axivion.qtcreator"; using namespace Core; using namespace ProjectExplorer; @@ -96,6 +100,42 @@ QString anyToSimpleString(const Dto::Any &any) return {}; } +static QString apiTokenDescription() +{ + const QString ua = "Axivion" + QCoreApplication::applicationName() + "Plugin/" + + QCoreApplication::applicationVersion(); + QString user = Utils::qtcEnvironmentVariable("USERNAME"); + if (user.isEmpty()) + user = Utils::qtcEnvironmentVariable("USER"); + return "Automatically created by " + ua + " on " + user + "@" + QSysInfo::machineHostName(); +} + +static QString credentialKey() +{ + const auto escape = [](const QString &string) { + QString escaped = string; + return escaped.replace('\\', "\\\\").replace('@', "\\@"); + }; + return escape(settings().server.dashboard) + '@' + escape(settings().server.username); +} + +static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInfoDto &infoDto) +{ + const QVersionNumber versionNumber = infoDto.dashboardVersionNumber + ? QVersionNumber::fromString(*infoDto.dashboardVersionNumber) : QVersionNumber(); + + QStringList projects; + QHash projectUrls; + + if (infoDto.projects) { + for (const Dto::ProjectReferenceDto &project : *infoDto.projects) { + projects.push_back(project.name); + projectUrls.insert(project.name, project.url); + } + } + return {source, versionNumber, projects, projectUrls, infoDto.checkCredentialsUrl}; +} + QString IssueListSearch::toQuery() const { if (kind.isEmpty()) @@ -142,6 +182,7 @@ public: void handleIssuesForFile(const Dto::FileViewDto &fileView); void fetchIssueInfo(const QString &id); + std::optional m_apiToken; // TODO: Should be cleared on settings modification NetworkAccessManager m_networkAccessManager; AxivionOutputPane m_axivionOutputPane; std::optional m_dashboardInfo; @@ -159,7 +200,7 @@ class AxivionTextMark : public TextMark { public: AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue) - : TextMark(filePath, issue.startLine, {Tr::tr("Axivion"), AxivionTextMarkId}) + : TextMark(filePath, issue.startLine, {Tr::tr("Axivion"), s_axivionTextMarkId}) { const QString markText = issue.description; const QString id = issue.kind + QString::number(issue.id.value_or(-1)); @@ -278,6 +319,7 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function storage; + // TODO: Refactor so that it's a common code with fetchDataRecipe(). const auto onCredentialSetup = [storage] { storage->credentials = QByteArrayLiteral("AxToken ") + settings().server.token.toUtf8(); }; @@ -383,15 +425,23 @@ static Group getDtoRecipe(const Storage> &dtoStorage) const auto onDeserializeSetup = [storage](Async &task) { const auto deserialize = [](QPromise &promise, const QByteArray &input) { - promise.addResult(DtoType::deserialize(input)); + try { + promise.addResult(DtoType::deserialize(input)); + } catch (const Dto::invalid_dto_exception &) { + promise.future().cancel(); + } }; task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); task.setConcurrentCallData(deserialize, *storage); }; const auto onDeserializeDone = [dtoStorage](const Async &task, DoneWith doneWith) { - if (doneWith == DoneWith::Success) - dtoStorage->dtoData = task.future().result(); + if (doneWith == DoneWith::Success && task.isResultAvailable()) { + dtoStorage->dtoData = task.result(); + } else { + MessageManager::writeFlashing(QString("Axivion: %1") + .arg(Tr::tr("Deserialization of an unexpected Dto structure."))); + } }; const Group recipe { @@ -424,6 +474,7 @@ static Group postDtoRecipe(const Storage> &dtoStorage) const QByteArray ua = "Axivion" + QCoreApplication::applicationName().toUtf8() + "Plugin/" + QCoreApplication::applicationVersion().toUtf8(); request.setRawHeader("X-Axivion-User-Agent", ua); + request.setRawHeader("Content-Type", "application/json"); request.setRawHeader("AX-CSRF-Token", dtoStorage->csrfToken); query.setRequest(request); query.setWriteData(dtoStorage->writeData); @@ -493,47 +544,115 @@ static Group postDtoRecipe(const Storage> &dtoStorage) template static Group fetchDataRecipe(const QUrl &url, const std::function &handler) { + const Storage passwordStorage; + const Storage> dashboardStorage; + const Storage> apiTokenStorage; const Storage> dtoStorage; - const auto onCredentialSetup = [dtoStorage, url] { - dtoStorage->credential = QByteArrayLiteral("AxToken ") + settings().server.token.toUtf8(); - dtoStorage->url = url; + const auto onGetCredentialSetup = [](CredentialQuery &credential) { + credential.setOperation(CredentialOperation::Get); + credential.setService(s_axivionKeychainService); + credential.setKey(credentialKey()); + }; + const auto onGetCredentialDone = [](const CredentialQuery &credential, DoneWith result) { + if (result == DoneWith::Success) + dd->m_apiToken = credential.data(); + // TODO: Show the message about keystore error and info that we can't authorize without it. + }; + const auto onDashboardGroupSetup = [passwordStorage, dashboardStorage] { + if (dd->m_apiToken) + return SetupResult::StopWithSuccess; + + bool ok = false; + const QString text(Tr::tr("Enter the password for:\nDashboard: %1\nUser: %2") + .arg(settings().server.dashboard, settings().server.username)); + *passwordStorage = QInputDialog::getText(ICore::mainWindow(), + Tr::tr("Axivion Server Password"), text, QLineEdit::Password, {}, &ok); + if (!ok) + return SetupResult::StopWithError; + + const QString credential = settings().server.username + ':' + *passwordStorage; + dashboardStorage->credential = "Basic " + credential.toUtf8().toBase64(); + dashboardStorage->url = QUrl(settings().server.dashboard); + return SetupResult::Continue; }; + const auto onApiTokenGroupSetup = [passwordStorage, dashboardStorage, apiTokenStorage] { + if (!dashboardStorage->dtoData) + return SetupResult::StopWithSuccess; + + dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, + *dashboardStorage->dtoData); + + const Dto::DashboardInfoDto &dashboardDto = *dashboardStorage->dtoData; + if (!dashboardDto.userApiTokenUrl) + return SetupResult::StopWithError; + + apiTokenStorage->credential = dashboardStorage->credential; + apiTokenStorage->url + = QUrl(settings().server.dashboard).resolved(*dashboardDto.userApiTokenUrl); + apiTokenStorage->csrfToken = dashboardDto.csrfToken.toUtf8(); + const Dto::ApiTokenCreationRequestDto requestDto{*passwordStorage, "IdePlugin", + apiTokenDescription(), 0}; + apiTokenStorage->writeData = requestDto.serialize(); + return SetupResult::Continue; + }; + + const auto onSetCredentialSetup = [apiTokenStorage](CredentialQuery &credential) { + if (!apiTokenStorage->dtoData || !apiTokenStorage->dtoData->token) + return SetupResult::StopWithSuccess; + + dd->m_apiToken = apiTokenStorage->dtoData->token->toUtf8(); + credential.setOperation(CredentialOperation::Set); + credential.setService(s_axivionKeychainService); + credential.setKey(credentialKey()); + credential.setData(*dd->m_apiToken); + return SetupResult::Continue; + }; + + const auto onDtoSetup = [dtoStorage, url] { + if (!dd->m_apiToken) + return SetupResult::StopWithError; + + dtoStorage->credential = "AxToken " + *dd->m_apiToken; + dtoStorage->url = url; + return SetupResult::Continue; + }; const auto onDtoDone = [dtoStorage, handler] { if (dtoStorage->dtoData) handler(*dtoStorage->dtoData); }; const Group recipe { - dtoStorage, - Sync(onCredentialSetup), Group { + LoopUntil([](int) { return !dd->m_apiToken; }), + CredentialQueryTask(onGetCredentialSetup, onGetCredentialDone), + Group { + passwordStorage, + dashboardStorage, + onGroupSetup(onDashboardGroupSetup), + Group { // GET DashboardInfoDto + finishAllAndSuccess, + getDtoRecipe(dashboardStorage), + }, + Group { // POST ApiTokenCreationRequestDto, GET ApiTokenInfoDto. + apiTokenStorage, + onGroupSetup(onApiTokenGroupSetup), + postDtoRecipe(apiTokenStorage), + CredentialQueryTask(onSetCredentialSetup) + } + } + }, + Group { + dtoStorage, + onGroupSetup(onDtoSetup), getDtoRecipe(dtoStorage), onGroupDone(onDtoDone) } }; - return recipe; } -static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInfoDto &infoDto) -{ - const QVersionNumber versionNumber = infoDto.dashboardVersionNumber - ? QVersionNumber::fromString(*infoDto.dashboardVersionNumber) : QVersionNumber(); - - QStringList projects; - QHash projectUrls; - - if (infoDto.projects) { - for (const Dto::ProjectReferenceDto &project : *infoDto.projects) { - projects.push_back(project.name); - projectUrls.insert(project.name, project.url); - } - } - return {source, versionNumber, projects, projectUrls, infoDto.checkCredentialsUrl}; -} - Group dashboardInfoRecipe(const DashboardInfoHandler &handler) { const auto onSetup = [handler] { @@ -718,7 +837,7 @@ void AxivionPluginPrivate::onDocumentClosed(IDocument *doc) const TextMarks &marks = document->marks(); for (TextMark *mark : marks) { - if (mark->category().id == AxivionTextMarkId) + if (mark->category().id == s_axivionTextMarkId) delete mark; } } From f790e337bafc86adcf9df82895db2baf8444179a Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 16 Feb 2024 15:22:41 +0100 Subject: [PATCH 008/128] Axivion: Isolate authorization recipe It's going to be used in other recipes. Change-Id: I314f97cc596ce158b7d20e3bb963ca4e35bcb818 Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 45 ++++++++++++++++----------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 9891bdc3c65..32b356847a8 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -541,13 +541,11 @@ static Group postDtoRecipe(const Storage> &dtoStorage) return recipe; }; -template -static Group fetchDataRecipe(const QUrl &url, const std::function &handler) +static Group authorizationRecipe() { const Storage passwordStorage; const Storage> dashboardStorage; const Storage> apiTokenStorage; - const Storage> dtoStorage; const auto onGetCredentialSetup = [](CredentialQuery &credential) { credential.setOperation(CredentialOperation::Get); @@ -610,20 +608,8 @@ static Group fetchDataRecipe(const QUrl &url, const std::functionm_apiToken) - return SetupResult::StopWithError; - - dtoStorage->credential = "AxToken " + *dd->m_apiToken; - dtoStorage->url = url; - return SetupResult::Continue; - }; - const auto onDtoDone = [dtoStorage, handler] { - if (dtoStorage->dtoData) - handler(*dtoStorage->dtoData); - }; - - const Group recipe { + return { + // TODO: Try unauthorized access first Group { LoopUntil([](int) { return !dd->m_apiToken; }), CredentialQueryTask(onGetCredentialSetup, onGetCredentialDone), @@ -642,7 +628,30 @@ static Group fetchDataRecipe(const QUrl &url, const std::function +static Group fetchDataRecipe(const QUrl &url, const std::function &handler) +{ + const Storage> dtoStorage; + + const auto onDtoSetup = [dtoStorage, url] { + if (!dd->m_apiToken) + return SetupResult::StopWithError; + + dtoStorage->credential = "AxToken " + *dd->m_apiToken; + dtoStorage->url = url; + return SetupResult::Continue; + }; + const auto onDtoDone = [dtoStorage, handler] { + if (dtoStorage->dtoData) + handler(*dtoStorage->dtoData); + }; + + const Group recipe { + authorizationRecipe(), Group { dtoStorage, onGroupSetup(onDtoSetup), From b508d2e959c6348767518b71e7447ad610b15fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Thu, 15 Feb 2024 23:03:04 +0100 Subject: [PATCH 009/128] SquishTests: Remove one iteration loop Qt Quick Application projects only allow CMake. Change-Id: I27ee545a572eadcf98e1ec8188a0d77a9872aa14 Reviewed-by: Reviewed-by: Jukka Nokso Reviewed-by: Christian Stenger --- .../tst_save_before_build/test.py | 69 ++++++++----------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/tests/system/suite_general/tst_save_before_build/test.py b/tests/system/suite_general/tst_save_before_build/test.py index 87c2d56d39a..0f5c1971433 100644 --- a/tests/system/suite_general/tst_save_before_build/test.py +++ b/tests/system/suite_general/tst_save_before_build/test.py @@ -29,44 +29,35 @@ def main(): if not startedWithoutPluginError(): return verifySaveBeforeBuildChecked(False) - for buildSystem in ["CMake"]: - projectName = "SampleApp-" + buildSystem - ensureSaveBeforeBuildChecked(False) - # create qt quick application - createNewQtQuickApplication(tempDir(), projectName, buildSystem=buildSystem) - lineForIteration = 1 # qbs project file holds line number of definition start - for expectDialog in [True, False]: - verifySaveBeforeBuildChecked(not expectDialog) - if buildSystem == "CMake": - files = ["%s.CMakeLists\\.txt" % projectName, - "%s.app%s.Source Files.main\\.cpp" % (projectName, projectName), - "%s.app%s.Main\\.qml" % (projectName, projectName)] - elif buildSystem == "Qbs": - lineForIteration += 1 # after opening the file we'll add a newline - lowerPN = projectName.lower() - files = ["%s.%s\\.qbs:%d" % (lowerPN, lowerPN, lineForIteration), - "%s.%s.main\\.cpp" % (lowerPN, lowerPN), - "%s.%s.Group 1.Main\\.qml" % (lowerPN, lowerPN)] - for i, file in enumerate(files): - if not openDocument(file): - test.fatal("Could not open file '%s'" % simpleFileName(file)) - continue + projectName = "SampleApp-CMake" + ensureSaveBeforeBuildChecked(False) + # create qt quick application + createNewQtQuickApplication(tempDir(), projectName) + for expectDialog in [True, False]: + verifySaveBeforeBuildChecked(not expectDialog) + files = ["%s.CMakeLists\\.txt" % projectName, + "%s.app%s.Source Files.main\\.cpp" % (projectName, projectName), + "%s.app%s.Main\\.qml" % (projectName, projectName)] + for i, file in enumerate(files): + if not openDocument(file): + test.fatal("Could not open file '%s'" % simpleFileName(file)) + continue - matching = re.match("^(.+)(:\\d+)", file) - if matching is not None: - file = matching.group(1) - test.log("Changing file '%s'" % simpleFileName(file)) - typeLines(getEditorForFileSuffix(file, True), "") - # try to compile - clickButton(waitForObject(":*Qt Creator.Build Project_Core::Internal::FancyToolButton")) - try: - ensureChecked(":Save Changes.Always save files before build_QCheckBox", - i == len(files) - 1, 5000) # At the last iteration, check the box - clickButton(waitForObject(":Save Changes.Save All_QPushButton")) - test.verify(expectDialog, "The 'Save Changes' dialog was shown.") - except: - test.verify(not expectDialog, "The 'Save Changes' dialog was not shown.") - waitForCompile() - verifySaveBeforeBuildChecked(True) - invokeMenuItem("File", "Close All Projects and Editors") + matching = re.match("^(.+)(:\\d+)", file) + if matching is not None: + file = matching.group(1) + test.log("Changing file '%s'" % simpleFileName(file)) + typeLines(getEditorForFileSuffix(file, True), "") + # try to compile + clickButton(waitForObject(":*Qt Creator.Build Project_Core::Internal::FancyToolButton")) + try: + ensureChecked(":Save Changes.Always save files before build_QCheckBox", + i == len(files) - 1, 5000) # At the last iteration, check the box + clickButton(waitForObject(":Save Changes.Save All_QPushButton")) + test.verify(expectDialog, "The 'Save Changes' dialog was shown.") + except: + test.verify(not expectDialog, "The 'Save Changes' dialog was not shown.") + waitForCompile() + verifySaveBeforeBuildChecked(True) + invokeMenuItem("File", "Close All Projects and Editors") invokeMenuItem("File", "Exit") From 84f4fb081878a173759bfd6a73415c5b2f4ceb36 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Fri, 16 Feb 2024 15:23:41 +0100 Subject: [PATCH 010/128] Debugger: Fix building custom allocator dumper tests for MSVC 3b5c377ce5d19531b15eb3969ee43620da77ca7f had fixed them for GCC >= 13, but unfortunately made them no longer compile on Windows with MSVC as reported in the Gerrit change, error below. Align the implementation of the custom allocator more with the "23_containers/vector/52591.cc" [1] ones referred to in the GCC commit [2], in particular define ctors, so this compiles with both GCC on Linux and MSVC on Windows. Move the definition of that custom allocator to a new macro "MY_ALLOCATOR" to avoid duplication. Previous error with MSVC: C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): error C2440: 'static_cast': cannot convert from 'myallocator' to 'myallocator<_Tp1>' with [ _Tp1=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): note: 'myallocator<_Tp1>::myallocator': no overloaded function could convert all the argument types with [ _Tp1=std::_Container_proxy ] C:\Users\davschul\Downloads\Archive\main.cpp(8): note: could be 'myallocator<_Tp1>::myallocator(myallocator<_Tp1> &&)' with [ _Tp1=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): note: 'myallocator<_Tp1>::myallocator(myallocator<_Tp1> &&)': cannot convert argument 1 from 'myallocator' to 'myallocator<_Tp1> &&' with [ _Tp1=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): note: Reason: cannot convert from 'myallocator' to 'myallocator<_Tp1>' with [ _Tp1=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called C:\Users\davschul\Downloads\Archive\main.cpp(8): note: or 'myallocator<_Tp1>::myallocator(const myallocator<_Tp1> &)' with [ _Tp1=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): note: 'myallocator<_Tp1>::myallocator(const myallocator<_Tp1> &)': cannot convert argument 1 from 'myallocator' to 'const myallocator<_Tp1> &' with [ _Tp1=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.38.33130\include\xstring(3141): note: Reason: cannot convert from 'myallocator' to 'const myallocator<_Tp1>' with [ _Tp1=std::_Container_proxy ] [1] https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/testsuite/23_containers/vector/52591.cc;h=ea80bb277c1f49190ae21b64e738428066eba1e0;hb=64c986b49558a7 [2] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=64c986b49558a7 Change-Id: I13a3ed226a411479ea1d24a2eda490c8f293bf8f Reviewed-by: David Schulz --- tests/auto/debugger/tst_dumpers.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 1cf8f1bbf2f..3f3c1daabaf 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -41,6 +41,15 @@ enum class Language Fortran90 }; +// for tests using custom allocator +#define MY_ALLOCATOR \ + "template\n" \ + "struct myallocator : public std::allocator {\n" \ + "template struct rebind { typedef myallocator other; };\n" \ + "myallocator() = default;\n" \ + "template myallocator(const myallocator&) {}\n" \ + "};\n" + // Copied from msvctoolchain.cpp to avoid plugin dependency. static bool generateEnvironmentSettings(Utils::Environment &env, const QString &batchFile, @@ -5263,13 +5272,7 @@ void tst_Dumpers::dumper_data() QTest::newRow("StdBasicString") << Data("#include \n" - "template\n" - "class myallocator : public std::allocator {\n" - "template\n" - "struct rebind {\n" - "typedef myallocator<_Tp1> other;\n" - "};\n" - "};\n", + MY_ALLOCATOR, "std::basic_string, myallocator> str(\"hello\");", @@ -5432,14 +5435,7 @@ void tst_Dumpers::dumper_data() QTest::newRow("StdVector") << Data("#include \n" "#include \n" - "template\n" - "class myallocator : public std::allocator {\n" - "using std::allocator::allocator;\n" - "template\n" - "struct rebind {\n" - "typedef myallocator<_Tp1> other;\n" - "};\n" - "};\n", + MY_ALLOCATOR, "std::vector v0, v1;\n" "v1.push_back(1);\n" From bc63b678bdc3e80b9d9e95d71e3f519ac522cb8d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 16 Feb 2024 16:17:27 +0100 Subject: [PATCH 011/128] Axivion: Implement trial connection with no authentication Introduce ServerAccess enum indicating the authentication method. Change-Id: Ic67bbf94504ec317cb6bf819cbc9dba34a01419c Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 54 ++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 32b356847a8..90c02ccead5 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -168,6 +168,8 @@ QString IssueListSearch::toQuery() const return result; } +enum class ServerAccess { Unknown, NoAuthorization, WithAuthorization }; + class AxivionPluginPrivate : public QObject { public: @@ -182,7 +184,10 @@ public: void handleIssuesForFile(const Dto::FileViewDto &fileView); void fetchIssueInfo(const QString &id); - std::optional m_apiToken; // TODO: Should be cleared on settings modification + // TODO: Should be set to Unknown on server address change in settings. + ServerAccess m_serverAccess = ServerAccess::Unknown; + // TODO: Should be cleared on username change in settings. + std::optional m_apiToken; NetworkAccessManager m_networkAccessManager; AxivionOutputPane m_axivionOutputPane; std::optional m_dashboardInfo; @@ -364,8 +369,8 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function struct GetDtoStorage { - QByteArray credential; QUrl url; + std::optional credential; std::optional dtoData; }; @@ -377,7 +382,8 @@ static Group getDtoRecipe(const Storage> &dtoStorage) const auto onNetworkQuerySetup = [dtoStorage](NetworkQuery &query) { QNetworkRequest request(dtoStorage->url); request.setRawHeader("Accept", s_jsonContentType); - request.setRawHeader("Authorization", dtoStorage->credential); + if (dtoStorage->credential) // Unauthorized access otherwise + request.setRawHeader("Authorization", *dtoStorage->credential); const QByteArray ua = "Axivion" + QCoreApplication::applicationName().toUtf8() + "Plugin/" + QCoreApplication::applicationVersion().toUtf8(); request.setRawHeader("X-Axivion-User-Agent", ua); @@ -455,8 +461,8 @@ static Group getDtoRecipe(const Storage> &dtoStorage) template struct PostDtoStorage { - QByteArray credential; QUrl url; + std::optional credential; QByteArray csrfToken; QByteArray writeData; std::optional dtoData; @@ -470,7 +476,8 @@ static Group postDtoRecipe(const Storage> &dtoStorage) const auto onNetworkQuerySetup = [dtoStorage](NetworkQuery &query) { QNetworkRequest request(dtoStorage->url); request.setRawHeader("Accept", s_jsonContentType); - request.setRawHeader("Authorization", dtoStorage->credential); + if (dtoStorage->credential) // Unauthorized access otherwise + request.setRawHeader("Authorization", *dtoStorage->credential); const QByteArray ua = "Axivion" + QCoreApplication::applicationName().toUtf8() + "Plugin/" + QCoreApplication::applicationVersion().toUtf8(); request.setRawHeader("X-Axivion-User-Agent", ua); @@ -543,10 +550,28 @@ static Group postDtoRecipe(const Storage> &dtoStorage) static Group authorizationRecipe() { - const Storage passwordStorage; - const Storage> dashboardStorage; - const Storage> apiTokenStorage; + const Storage> unauthorizedDashboardStorage; + const auto onUnauthorizedGroupSetup = [unauthorizedDashboardStorage] { + if (dd->m_serverAccess != ServerAccess::NoAuthorization) + return SetupResult::StopWithSuccess; + unauthorizedDashboardStorage->url = QUrl(settings().server.dashboard); + return SetupResult::Continue; + }; + const auto onUnauthorizedGroupDone = [unauthorizedDashboardStorage] { + if (unauthorizedDashboardStorage->dtoData) { + dd->m_serverAccess = ServerAccess::NoAuthorization; + dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, + *unauthorizedDashboardStorage->dtoData); + } else { + dd->m_serverAccess = ServerAccess::WithAuthorization; + } + return DoneResult::Success; + }; + + const auto onCredentialLoopCondition = [](int) { + return dd->m_serverAccess == ServerAccess::WithAuthorization && !dd->m_apiToken; + }; const auto onGetCredentialSetup = [](CredentialQuery &credential) { credential.setOperation(CredentialOperation::Get); credential.setService(s_axivionKeychainService); @@ -557,6 +582,9 @@ static Group authorizationRecipe() dd->m_apiToken = credential.data(); // TODO: Show the message about keystore error and info that we can't authorize without it. }; + + const Storage passwordStorage; + const Storage> dashboardStorage; const auto onDashboardGroupSetup = [passwordStorage, dashboardStorage] { if (dd->m_apiToken) return SetupResult::StopWithSuccess; @@ -575,6 +603,7 @@ static Group authorizationRecipe() return SetupResult::Continue; }; + const Storage> apiTokenStorage; const auto onApiTokenGroupSetup = [passwordStorage, dashboardStorage, apiTokenStorage] { if (!dashboardStorage->dtoData) return SetupResult::StopWithSuccess; @@ -609,9 +638,14 @@ static Group authorizationRecipe() }; return { - // TODO: Try unauthorized access first Group { - LoopUntil([](int) { return !dd->m_apiToken; }), + unauthorizedDashboardStorage, + onGroupSetup(onUnauthorizedGroupSetup), + getDtoRecipe(unauthorizedDashboardStorage), + onGroupDone(onUnauthorizedGroupDone) + }, + Group { + LoopUntil(onCredentialLoopCondition), CredentialQueryTask(onGetCredentialSetup, onGetCredentialDone), Group { passwordStorage, From ae20f9494988a75057358bf6bf1c51a589878b03 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 15 Feb 2024 00:08:16 +0100 Subject: [PATCH 012/128] Axivion: Reuse AxivionPluginPrivate::m_apiToken field in fetchHtmlRecipe Change-Id: I6bb37678000ba755d65b0275c4350815a9047880 Reviewed-by: Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 28 +++++++-------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 90c02ccead5..8060c7b7535 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -317,29 +317,22 @@ constexpr char s_jsonContentType[] = "application/json"; static Group fetchHtmlRecipe(const QUrl &url, const std::function &handler) { - struct StorageData - { - QByteArray credentials; - }; - - const Storage storage; - // TODO: Refactor so that it's a common code with fetchDataRecipe(). - const auto onCredentialSetup = [storage] { - storage->credentials = QByteArrayLiteral("AxToken ") + settings().server.token.toUtf8(); - }; + const auto onQuerySetup = [url](NetworkQuery &query) { + if (dd->m_serverAccess == ServerAccess::Unknown) + return SetupResult::StopWithError; // TODO: start authorizationRecipe()? - const auto onQuerySetup = [storage, url](NetworkQuery &query) { QNetworkRequest request(url); request.setRawHeader("Accept", s_htmlContentType); - request.setRawHeader("Authorization", storage->credentials); + if (dd->m_serverAccess == ServerAccess::WithAuthorization && dd->m_apiToken) + request.setRawHeader("Authorization", "AxToken " + *dd->m_apiToken); const QByteArray ua = "Axivion" + QCoreApplication::applicationName().toUtf8() + "Plugin/" + QCoreApplication::applicationVersion().toUtf8(); request.setRawHeader("X-Axivion-User-Agent", ua); query.setRequest(request); query.setNetworkAccessManager(&dd->m_networkAccessManager); + return SetupResult::Continue; }; - const auto onQueryDone = [url, handler](const NetworkQuery &query, DoneWith doneWith) { QNetworkReply *reply = query.reply(); const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -356,14 +349,7 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function From 0b2f7dd6fe777a5eab7709caf2675fb4fb586e2b Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 15 Feb 2024 00:11:25 +0100 Subject: [PATCH 013/128] Axivion: Get rid of AxivionServer::token setting Remove it also from aspect container. Change-Id: I0af093bbd5654083dac581e5793ec57e1c7a7b3d Reviewed-by: Andreas Loth Reviewed-by: hjk --- .../axivion/axivionprojectsettings.cpp | 3 +-- src/plugins/axivion/axivionsettings.cpp | 20 +++---------------- src/plugins/axivion/axivionsettings.h | 1 - 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/plugins/axivion/axivionprojectsettings.cpp b/src/plugins/axivion/axivionprojectsettings.cpp index 14d47eca53a..1413b3cfd3a 100644 --- a/src/plugins/axivion/axivionprojectsettings.cpp +++ b/src/plugins/axivion/axivionprojectsettings.cpp @@ -220,8 +220,7 @@ void AxivionProjectSettingsWidget::updateUi() void AxivionProjectSettingsWidget::updateEnabledStates() { - const bool hasDashboardSettings = !settings().server.dashboard.isEmpty() - && !settings().server.token.isEmpty(); + const bool hasDashboardSettings = !settings().server.dashboard.isEmpty(); const bool linked = !m_projectSettings->dashboardProjectName().isEmpty(); const bool linkable = m_dashboardProjects->topLevelItemCount() && !m_dashboardProjects->selectedItems().isEmpty(); diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index f742f0c5626..5958bfd4deb 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -28,7 +28,7 @@ namespace Axivion::Internal { bool AxivionServer::operator==(const AxivionServer &other) const { return id == other.id && dashboard == other.dashboard && username == other.username - && description == other.description && token == other.token; + && description == other.description; } bool AxivionServer::operator!=(const AxivionServer &other) const @@ -43,7 +43,6 @@ QJsonObject AxivionServer::toJson() const result.insert("dashboard", dashboard); result.insert("username", username); result.insert("description", description); - result.insert("token", token); return result; } @@ -62,11 +61,8 @@ AxivionServer AxivionServer::fromJson(const QJsonObject &json) const QJsonValue description = json.value("description"); if (description == QJsonValue::Undefined) return invalidServer; - const QJsonValue token = json.value("token"); - if (token == QJsonValue::Undefined) - return invalidServer; return {Id::fromString(id.toString()), dashboard.toString(), username.toString(), - description.toString(), token.toString()}; + description.toString()}; } static FilePath tokensFilePath() @@ -162,7 +158,6 @@ private: StringAspect m_dashboardUrl; StringAspect m_username; StringAspect m_description; - StringAspect m_token; BoolAspect m_valid; }; @@ -185,18 +180,12 @@ DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPu m_description.setDisplayStyle(labelStyle); m_description.setPlaceHolderText(Tr::tr("Non-empty description")); - m_token.setLabelText(Tr::tr("Access token:")); - m_token.setDisplayStyle(labelStyle); - m_token.setPlaceHolderText(Tr::tr("IDE Access Token")); - m_token.setVisible(mode == Edit); - using namespace Layouting; Form { m_dashboardUrl, br, m_username, br, m_description, br, - m_token, br, mode == Edit ? normalMargin : noMargin }.attachTo(this); @@ -209,7 +198,6 @@ DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPu connect(&m_dashboardUrl, &BaseAspect::changed, this, checkValidity); connect(&m_username, &BaseAspect::changed, this, checkValidity); connect(&m_description, &BaseAspect::changed, this, checkValidity); - connect(&m_token, &BaseAspect::changed, this, checkValidity); } } @@ -223,7 +211,6 @@ AxivionServer DashboardSettingsWidget::dashboardServer() const result.dashboard = m_dashboardUrl(); result.username = m_username(); result.description = m_description(); - result.token = m_token(); return result; } @@ -233,12 +220,11 @@ void DashboardSettingsWidget::setDashboardServer(const AxivionServer &server) m_dashboardUrl.setValue(server.dashboard); m_username.setValue(server.username); m_description.setValue(server.description); - m_token.setValue(server.token); } bool DashboardSettingsWidget::isValid() const { - return !m_token().isEmpty() && !m_description().isEmpty() && isUrlValid(m_dashboardUrl()); + return !m_description().isEmpty() && isUrlValid(m_dashboardUrl()); } class AxivionSettingsWidget : public IOptionsPageWidget diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h index 00a93843816..d692fde4a51 100644 --- a/src/plugins/axivion/axivionsettings.h +++ b/src/plugins/axivion/axivionsettings.h @@ -27,7 +27,6 @@ public: QString dashboard; QString username; QString description; - QString token; bool validateCert = true; }; From 065debd163dc98b9679cc2c751c1451499db099c Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 14 Feb 2024 14:48:13 +0100 Subject: [PATCH 014/128] RemoteLinux: Speed up rsync transfer Group files with the same target directory into a single rsync invocation. Fixes: QTCREATORBUG-29988 Change-Id: I3fc7883e55c9c3105d403d19d0b59cc70d158d6d Reviewed-by: Qt CI Bot Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/remotelinux/linuxdevice.cpp | 44 +++++++++++++------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 0872ff54d3d..dbad2b5bb18 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1485,23 +1485,24 @@ public: private: void startImpl() final { - m_currentIndex = 0; - startNextFile(); + // Note: This assumes that files do not get renamed when transferring. + for (auto it = m_setup.m_files.cbegin(); it != m_setup.m_files.cend(); ++it) + m_batches[it->m_target.parentDir()] << *it; + startNextBatch(); } void doneImpl() final { - if (m_setup.m_files.size() == 0 || m_currentIndex == m_setup.m_files.size() - 1) + if (m_batches.isEmpty()) return handleDone(); if (handleError()) return; - ++m_currentIndex; - startNextFile(); + startNextBatch(); } - void startNextFile() + void startNextBatch() { process().close(); @@ -1510,12 +1511,14 @@ private: << fullConnectionOptions(), OsTypeLinux); QStringList options{"-e", sshCmdLine, m_setup.m_rsyncFlags}; - if (!m_setup.m_files.isEmpty()) { // NormalRun - const FileToTransfer file = m_setup.m_files.at(m_currentIndex); - const FileToTransfer fixedFile = fixLocalFileOnWindows(file, options); - const auto fixedPaths = fixPaths(fixedFile, userAtHost()); - - options << fixedPaths.first << fixedPaths.second; + if (!m_batches.isEmpty()) { // NormalRun + const auto batchIt = m_batches.begin(); + for (auto filesIt = batchIt->cbegin(); filesIt != batchIt->cend(); ++filesIt) { + const FileToTransfer fixedFile = fixLocalFileOnWindows(*filesIt, options); + options << fixedLocalPath(fixedFile.m_source); + } + options << fixedRemotePath(batchIt.key(), userAtHost()); + m_batches.erase(batchIt); } else { // TestRun options << "-n" << "--exclude=*" << (userAtHost() + ":/tmp"); } @@ -1542,18 +1545,17 @@ private: return fixedFile; } - QPair fixPaths(const FileToTransfer &file, const QString &remoteHost) const + QString fixedLocalPath(const FilePath &file) const { - FilePath localPath = file.m_source; - FilePath remotePath = file.m_target; - const QString local = (localPath.isDir() && localPath.path().back() != '/') - ? localPath.path() + '/' : localPath.path(); - const QString remote = remoteHost + ':' + remotePath.path(); - - return qMakePair(local, remote); + return file.isDir() && file.path().back() != '/' ? file.path() + '/' : file.path(); } - int m_currentIndex = 0; + QString fixedRemotePath(const FilePath &file, const QString &remoteHost) const + { + return remoteHost + ':' + file.path(); + } + + QHash m_batches; }; class GenericTransferImpl : public FileTransferInterface From a69f7019a23a92e62f628b3991ce256e4ec3602e Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 16 Feb 2024 19:21:38 +0100 Subject: [PATCH 015/128] Axivion: Limit code repetition Introduce dtoRecipe() function which replaces both getDtoRecipe() and postDtoRecipe(). Change-Id: Icbf97779869598c4fcc9bc19a520c9bd67df75d4 Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 123 ++++++-------------------- 1 file changed, 25 insertions(+), 98 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 8060c7b7535..ec64dcc4ba5 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -361,7 +361,17 @@ struct GetDtoStorage }; template -static Group getDtoRecipe(const Storage> &dtoStorage) +struct PostDtoStorage +{ + QUrl url; + std::optional credential; + QByteArray csrfToken; + QByteArray writeData; + std::optional dtoData; +}; + +template typename DtoStorageType> +static Group dtoRecipe(const Storage> &dtoStorage) { const Storage storage; @@ -373,6 +383,14 @@ static Group getDtoRecipe(const Storage> &dtoStorage) const QByteArray ua = "Axivion" + QCoreApplication::applicationName().toUtf8() + "Plugin/" + QCoreApplication::applicationVersion().toUtf8(); request.setRawHeader("X-Axivion-User-Agent", ua); + + if constexpr (std::is_same_v, PostDtoStorage>) { + request.setRawHeader("Content-Type", "application/json"); + request.setRawHeader("AX-CSRF-Token", dtoStorage->csrfToken); + query.setWriteData(dtoStorage->writeData); + query.setOperation(NetworkOperation::Post); + } + query.setRequest(request); query.setNetworkAccessManager(&dd->m_networkAccessManager); }; @@ -436,103 +454,12 @@ static Group getDtoRecipe(const Storage> &dtoStorage) } }; - const Group recipe { + return { storage, NetworkQueryTask(onNetworkQuerySetup, onNetworkQueryDone), AsyncTask(onDeserializeSetup, onDeserializeDone) }; - return recipe; -}; - -template -struct PostDtoStorage -{ - QUrl url; - std::optional credential; - QByteArray csrfToken; - QByteArray writeData; - std::optional dtoData; -}; - -template -static Group postDtoRecipe(const Storage> &dtoStorage) -{ - const Storage storage; - - const auto onNetworkQuerySetup = [dtoStorage](NetworkQuery &query) { - QNetworkRequest request(dtoStorage->url); - request.setRawHeader("Accept", s_jsonContentType); - if (dtoStorage->credential) // Unauthorized access otherwise - request.setRawHeader("Authorization", *dtoStorage->credential); - const QByteArray ua = "Axivion" + QCoreApplication::applicationName().toUtf8() + - "Plugin/" + QCoreApplication::applicationVersion().toUtf8(); - request.setRawHeader("X-Axivion-User-Agent", ua); - request.setRawHeader("Content-Type", "application/json"); - request.setRawHeader("AX-CSRF-Token", dtoStorage->csrfToken); - query.setRequest(request); - query.setWriteData(dtoStorage->writeData); - query.setOperation(NetworkOperation::Post); - query.setNetworkAccessManager(&dd->m_networkAccessManager); - }; - - const auto onNetworkQueryDone = [storage](const NetworkQuery &query, DoneWith doneWith) { - QNetworkReply *reply = query.reply(); - const QNetworkReply::NetworkError error = reply->error(); - const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader) - .toString() - .split(';') - .constFirst() - .trimmed() - .toLower(); - if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk - && contentType == s_jsonContentType) { - *storage = reply->readAll(); - return DoneResult::Success; - } - - const auto getError = [&]() -> Error { - if (contentType == s_jsonContentType) { - try { - return DashboardError(reply->url(), statusCode, - reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(), - Dto::ErrorDto::deserialize(reply->readAll())); - } catch (const Dto::invalid_dto_exception &) { - // ignore - } - } - if (statusCode != 0) { - return HttpError(reply->url(), statusCode, - reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(), - QString::fromUtf8(reply->readAll())); // encoding? - } - return NetworkError(reply->url(), error, reply->errorString()); - }; - - MessageManager::writeFlashing(QStringLiteral("Axivion: %1").arg(getError().message())); - return DoneResult::Error; - }; - - const auto onDeserializeSetup = [storage](Async &task) { - const auto deserialize = [](QPromise &promise, const QByteArray &input) { - promise.addResult(DtoType::deserialize(input)); - }; - task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); - task.setConcurrentCallData(deserialize, *storage); - }; - - const auto onDeserializeDone = [dtoStorage](const Async &task, DoneWith doneWith) { - if (doneWith == DoneWith::Success) - dtoStorage->dtoData = task.future().result(); - }; - - const Group recipe { - storage, - NetworkQueryTask(onNetworkQuerySetup, onNetworkQueryDone), - AsyncTask(onDeserializeSetup, onDeserializeDone) - }; - return recipe; -}; +} static Group authorizationRecipe() { @@ -627,7 +554,7 @@ static Group authorizationRecipe() Group { unauthorizedDashboardStorage, onGroupSetup(onUnauthorizedGroupSetup), - getDtoRecipe(unauthorizedDashboardStorage), + dtoRecipe(unauthorizedDashboardStorage), onGroupDone(onUnauthorizedGroupDone) }, Group { @@ -639,12 +566,12 @@ static Group authorizationRecipe() onGroupSetup(onDashboardGroupSetup), Group { // GET DashboardInfoDto finishAllAndSuccess, - getDtoRecipe(dashboardStorage), + dtoRecipe(dashboardStorage) }, Group { // POST ApiTokenCreationRequestDto, GET ApiTokenInfoDto. apiTokenStorage, onGroupSetup(onApiTokenGroupSetup), - postDtoRecipe(apiTokenStorage), + dtoRecipe(apiTokenStorage), CredentialQueryTask(onSetCredentialSetup) } } @@ -675,7 +602,7 @@ static Group fetchDataRecipe(const QUrl &url, const std::function Date: Mon, 19 Feb 2024 17:01:47 +0100 Subject: [PATCH 016/128] Doc: Describe new functions in the Markdown editor - Activate the locator to jump to a line and column - Follow links to web sites Task-number: QTCREATORBUG-30209 Change-Id: I2514fb44c8216c9f40ad14a58450f9d5ce06922c Reviewed-by: Eike Ziller --- .../creator-only/creator-markdown-editor.qdoc | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc index 1942ec84af7..8f959548e98 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -9,9 +9,9 @@ \title Edit Markdown files - Open \l{https://www.markdownguide.org/basic-syntax/}{Markdown} (.md) files - or select \uicontrol File > \uicontrol {New File} > \uicontrol {General} > - \uicontrol {Markdown File} to create a new file. + Open \l{https://www.markdownguide.org/basic-syntax/}{Markdown} (.md) files, + or go to \uicontrol File > \uicontrol {New File} and select + \uicontrol {General} > \uicontrol {Markdown File} to create a new file. \image qtcreator-markdown-editor.webp {Markdown file in Preview and Editor views} @@ -26,4 +26,20 @@ Use the buttons on the editor toolbar to format text as italic (i), bold (b), or inline code (`), and to create links to web sites (\inlineimage icons/linkicon.png). + + \section1 Move to a line and column + + The line and column indicator shows information about the current cursor + position. Select it to activate the locator, and enter a line and column + number to move there. + + \section1 Follow links to web sites + + To follow a link to a web site in the editor: + + \list 1 + \li Place the cursor on the link. + \li Go to \uicontrol {Follow Symbol Under Cursor} in the context menu, or + press \key F2. + \endlist */ From a4f600c14d7be575d59b8d888d69804fdede45f9 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 17 Feb 2024 10:10:30 +0100 Subject: [PATCH 017/128] Axivion: Show error messages on CredentialQuery errors Use more writeDisrupting() for logging Axivion errors. Don't translate "Axivion". Change-Id: Ic04ff0afe66d79be3d319b16948f7861225e8927 Reviewed-by: hjk Reviewed-by: --- src/plugins/axivion/axivionplugin.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index ec64dcc4ba5..f3246fb8dc3 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -205,7 +205,7 @@ class AxivionTextMark : public TextMark { public: AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue) - : TextMark(filePath, issue.startLine, {Tr::tr("Axivion"), s_axivionTextMarkId}) + : TextMark(filePath, issue.startLine, {QString("Axivion"), s_axivionTextMarkId}) { const QString markText = issue.description; const QString id = issue.kind + QString::number(issue.id.value_or(-1)); @@ -308,7 +308,7 @@ static QUrl urlForProject(const QString &projectName) QString dashboard = settings().server.dashboard; if (!dashboard.endsWith(QLatin1Char('/'))) dashboard += QLatin1Char('/'); - return QUrl(dashboard).resolved(QStringLiteral("api/projects/")).resolved(projectName); + return QUrl(dashboard).resolved(QString("api/projects/")).resolved(projectName); } static constexpr int httpStatusCodeOk = 200; @@ -428,8 +428,7 @@ static Group dtoRecipe(const Storage> &dtoStorage) } return NetworkError(reply->url(), error, reply->errorString()); }; - - MessageManager::writeFlashing(QStringLiteral("Axivion: %1").arg(getError().message())); + MessageManager::writeDisrupting(QString("Axivion: %1").arg(getError().message())); return DoneResult::Error; }; @@ -493,7 +492,8 @@ static Group authorizationRecipe() const auto onGetCredentialDone = [](const CredentialQuery &credential, DoneWith result) { if (result == DoneWith::Success) dd->m_apiToken = credential.data(); - // TODO: Show the message about keystore error and info that we can't authorize without it. + else + MessageManager::writeDisrupting(QString("Axivion: %1").arg(credential.errorString())); }; const Storage passwordStorage; @@ -549,6 +549,9 @@ static Group authorizationRecipe() credential.setData(*dd->m_apiToken); return SetupResult::Continue; }; + const auto onSetCredentialDone = [](const CredentialQuery &credential) { + MessageManager::writeDisrupting(QString("Axivion: %1").arg(credential.errorString())); + }; return { Group { @@ -572,7 +575,7 @@ static Group authorizationRecipe() apiTokenStorage, onGroupSetup(onApiTokenGroupSetup), dtoRecipe(apiTokenStorage), - CredentialQueryTask(onSetCredentialSetup) + CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error) } } } From 31007b825987083eb5a3da037955f1349848f1ca Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sun, 18 Feb 2024 11:47:41 +0100 Subject: [PATCH 018/128] TaskTree: Introduce ExecutableItem ExecutableItem is a common base for Group, CustomTask and Sync elements. Move withTimeout() here, as this is relevant only for executable items. It's going to be a place for additional API, e.g. withLog(). Task-number: QTCREATORBUG-28741 Change-Id: Ibcd6b34c389af4f41f56fd242b14fad6c0c579b2 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: hjk --- src/libs/solutions/tasking/tasktree.cpp | 6 ++--- src/libs/solutions/tasking/tasktree.h | 34 ++++++++++++------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 255fe5243a8..d5d6903093b 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -1402,8 +1402,8 @@ void GroupItem::addChildren(const QList &children) } } -GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, - const std::function &handler) +ExecutableItem ExecutableItem::withTimeout(milliseconds timeout, + const std::function &handler) const { const auto onSetup = [timeout](milliseconds &timeoutData) { timeoutData = timeout; }; return Group { @@ -1414,7 +1414,7 @@ GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, handler ? TimeoutTask(onSetup, [handler] { handler(); }, CallDoneIf::Success) : TimeoutTask(onSetup) }, - item + *this }; } diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 7295c47e1ea..7b5c9058dae 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -261,8 +261,6 @@ protected: static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); } static GroupItem parallelLimit(int limit) { return GroupItem({{}, limit}); } static GroupItem workflowPolicy(WorkflowPolicy policy) { return GroupItem({{}, {}, policy}); } - static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout, - const std::function &handler = {}); // Checks if Function may be invoked with Args and if Function's return type is Result. template &handler = {}) const; + +protected: + ExecutableItem() = default; + ExecutableItem(const TaskHandler &handler) : GroupItem(handler) {} +}; + +class TASKING_EXPORT Group : public ExecutableItem { public: Group(const QList &children) { addChildren(children); } @@ -304,11 +313,6 @@ public: using GroupItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel). using GroupItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError. - GroupItem withTimeout(std::chrono::milliseconds timeout, - const std::function &handler = {}) const { - return GroupItem::withTimeout(*this, timeout, handler); - } - private: template static GroupSetupHandler wrapGroupSetup(Handler &&handler) @@ -387,7 +391,7 @@ public: }; // Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount() -class TASKING_EXPORT Sync final : public GroupItem +class TASKING_EXPORT Sync final : public ExecutableItem { public: template @@ -431,7 +435,7 @@ private: }; template -class CustomTask final : public GroupItem +class CustomTask final : public ExecutableItem { public: using Task = typename Adapter::TaskType; @@ -445,16 +449,10 @@ public: template CustomTask(SetupHandler &&setup = TaskSetupHandler(), DoneHandler &&done = TaskDoneHandler(), CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) - : GroupItem({&createAdapter, wrapSetup(std::forward(setup)), - wrapDone(std::forward(done)), callDoneIf}) + : ExecutableItem({&createAdapter, wrapSetup(std::forward(setup)), + wrapDone(std::forward(done)), callDoneIf}) {} - GroupItem withTimeout(std::chrono::milliseconds timeout, - const std::function &handler = {}) const - { - return GroupItem::withTimeout(*this, timeout, handler); - } - private: static Adapter *createAdapter() { return new Adapter; } From 243b268bea9d27d9bb53544a4917b2d929b5b01b Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sun, 18 Feb 2024 13:06:22 +0100 Subject: [PATCH 019/128] TaskTree: Add withLog() function This addresses the 7th and 8th points in the bugreport below. Task-number: QTCREATORBUG-28741 Change-Id: I6430bf330d117258654bf3fe101511d4bea68358 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: hjk --- src/libs/solutions/tasking/tasktree.cpp | 26 +++++++++++++++++++++++++ src/libs/solutions/tasking/tasktree.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index d5d6903093b..865b541de47 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -7,10 +7,12 @@ #include #include #include +#include #include #include #include #include +#include #include using namespace std::chrono; @@ -1418,6 +1420,30 @@ ExecutableItem ExecutableItem::withTimeout(milliseconds timeout, }; } +static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateWithMs); } + +ExecutableItem ExecutableItem::withLog(const QString &logName) const +{ + const auto header = [logName] { + return QString("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName); + }; + const Storage> storage; + return Group { + storage, + onGroupSetup([storage, header] { + *storage = system_clock::now(); + qDebug().noquote() << header() << "started."; + }), + *this, + onGroupDone([storage, header](DoneWith result) { + const auto elapsed = duration_cast(system_clock::now() - *storage); + const QMetaEnum doneWithEnum = QMetaEnum::fromType(); + qDebug().noquote().nospace() << header() << " finished with " + << doneWithEnum.valueToKey(int(result)) << " within " << elapsed.count() << "ms."; + }) + }; +} + class TaskTreePrivate; class TaskNode; class RuntimeContainer; diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 7b5c9058dae..4b067a03c4a 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -289,6 +289,7 @@ class TASKING_EXPORT ExecutableItem : public GroupItem public: ExecutableItem withTimeout(std::chrono::milliseconds timeout, const std::function &handler = {}) const; + ExecutableItem withLog(const QString &logName) const; protected: ExecutableItem() = default; From 47b3017979ecd8a1d321baf9e307f20469bf06df Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 20 Feb 2024 10:36:37 +0100 Subject: [PATCH 020/128] Doc: Add more options for following links to web sites in MD editor Task-number: QTCREATORBUG-30209 Change-Id: I0c9f49775377bfd3e8061112a77995a647590c1c Reviewed-by: Eike Ziller --- .../editors/creator-only/creator-markdown-editor.qdoc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc index 8f959548e98..a5cbf186f99 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc @@ -39,7 +39,12 @@ \list 1 \li Place the cursor on the link. - \li Go to \uicontrol {Follow Symbol Under Cursor} in the context menu, or - press \key F2. + \li Then, do one of the following: + \list + \li Press \key {Ctrl+Click} (or \key {Cmd+Click} on \macos). + \li Press \key F2. + \li Go to \uicontrol {Follow Symbol Under Cursor} in the context + menu. + \endlist \endlist */ From 7261c2889cd3988ac0aa3ba5001f708d3974762a Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 20 Feb 2024 11:10:04 +0100 Subject: [PATCH 021/128] Tests: Fix missing include Avoids a compile issue before being able to execute the test. Change-Id: Ibc76c16229107e2c23757de682bc78bfe4b7d6e7 Reviewed-by: David Schulz Reviewed-by: hjk --- tests/auto/debugger/tst_dumpers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 3f3c1daabaf..efd4e66a8ed 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -5352,7 +5352,8 @@ void tst_Dumpers::dumper_data() QTest::newRow("StdTuple") - << Data("#include \n", + << Data("#include \n" + "#include \n", "std::tuple tuple = std::make_tuple(123, std::string(\"hello\"), 456);\n", From 721d87911562a5dcc2ae3a4091e1f66be0204bcd Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 19 Feb 2024 13:37:20 +0100 Subject: [PATCH 022/128] Axivion: Move issue details widget Although it is not a navigation widget as such, it is expected to have this separated and beside the editor. This patch moves the widget to the right side bar by default. Change-Id: If46bedaf7bf475a46dbfd8bee1e121c659740856 Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 14 ------- src/plugins/axivion/axivionoutputpane.h | 1 - src/plugins/axivion/axivionplugin.cpp | 46 ++++++++++++++++++++++- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 9bd50784f3a..df8d87accdc 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -615,8 +614,6 @@ AxivionOutputPane::AxivionOutputPane(QObject *parent) m_outputWidget->addWidget(dashboardWidget); IssuesWidget *issuesWidget = new IssuesWidget(m_outputWidget); m_outputWidget->addWidget(issuesWidget); - QTextBrowser *browser = new QTextBrowser(m_outputWidget); - m_outputWidget->addWidget(browser); } AxivionOutputPane::~AxivionOutputPane() @@ -709,15 +706,4 @@ void AxivionOutputPane::updateDashboard() } } -void AxivionOutputPane::updateAndShowRule(const QString &ruleHtml) -{ - if (auto browser = static_cast(m_outputWidget->widget(2))) { - browser->setText(ruleHtml); - if (!ruleHtml.isEmpty()) { - m_outputWidget->setCurrentIndex(2); - popup(IOutputPane::NoModeSwitch); - } - } -} - } // Axivion::Internal diff --git a/src/plugins/axivion/axivionoutputpane.h b/src/plugins/axivion/axivionoutputpane.h index 140d50b7235..87000e5e187 100644 --- a/src/plugins/axivion/axivionoutputpane.h +++ b/src/plugins/axivion/axivionoutputpane.h @@ -32,7 +32,6 @@ public: void goToPrev() override; void updateDashboard(); - void updateAndShowRule(const QString &ruleHtml); private: QStackedWidget *m_outputWidget = nullptr; }; diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index f3246fb8dc3..d7ce85baf6f 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include @@ -42,6 +44,7 @@ #include #include #include +#include #include #include @@ -172,6 +175,7 @@ enum class ServerAccess { Unknown, NoAuthorization, WithAuthorization }; class AxivionPluginPrivate : public QObject { + Q_OBJECT public: AxivionPluginPrivate(); void handleSslErrors(QNetworkReply *reply, const QList &errors); @@ -183,7 +187,12 @@ public: void clearAllMarks(); void handleIssuesForFile(const Dto::FileViewDto &fileView); void fetchIssueInfo(const QString &id); + void setIssueDetails(const QString &issueDetailsHtml); +signals: + void issueDetailsChanged(const QString &issueDetailsHtml); + +public: // TODO: Should be set to Unknown on server address change in settings. ServerAccess m_serverAccess = ServerAccess::Unknown; // TODO: Should be cleared on username change in settings. @@ -739,12 +748,19 @@ void AxivionPluginPrivate::fetchIssueInfo(const QString &id) const int idx = htmlText.indexOf("

"); if (idx >= 0) fixedHtml = "" + htmlText.mid(idx); - dd->m_axivionOutputPane.updateAndShowRule(QString::fromUtf8(fixedHtml)); + + NavigationWidget::activateSubWidget("Axivion.Issue", Side::Right); + dd->setIssueDetails(QString::fromUtf8(fixedHtml)); }; m_issueInfoRunner.start(issueHtmlRecipe(id, ruleHandler)); } +void AxivionPluginPrivate::setIssueDetails(const QString &issueDetailsHtml) +{ + emit issueDetailsChanged(issueDetailsHtml); +} + void AxivionPluginPrivate::handleOpenedDocs() { const QList openDocuments = DocumentModel::openedDocuments(); @@ -820,6 +836,33 @@ void AxivionPluginPrivate::handleIssuesForFile(const Dto::FileViewDto &fileView) } } +class AxivionIssueWidgetFactory final : public INavigationWidgetFactory +{ +public: + AxivionIssueWidgetFactory() + { + setDisplayName(Tr::tr("Axivion")); + setId("Axivion.Issue"); + setPriority(555); + } + + NavigationView createWidget() final + { + QTC_ASSERT(dd, return {}); + QTextBrowser *browser = new QTextBrowser; + browser->setOpenLinks(false); + NavigationView view; + view.widget = browser; + connect(dd, &AxivionPluginPrivate::issueDetailsChanged, browser, &QTextBrowser::setHtml); + return view; + } +}; + +void setupAxivionIssueWidgetFactory() +{ + static AxivionIssueWidgetFactory issueWidgetFactory; +} + class AxivionPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT @@ -837,6 +880,7 @@ class AxivionPlugin final : public ExtensionSystem::IPlugin dd = new AxivionPluginPrivate; AxivionProjectSettings::setupProjectPanel(); + setupAxivionIssueWidgetFactory(); connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, dd, &AxivionPluginPrivate::onStartupProjectChanged); From 6561d3f691db7687b9f8ac1148322eeed802b90a Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 20 Feb 2024 10:15:13 +0100 Subject: [PATCH 023/128] Update clang-format test file ... such that it represents the output from a current clang-format with our current settings. Change-Id: I0bc75c80b01e991c10bc490f4dc0270b8292fae3 Reviewed-by: Reviewed-by: Eike Ziller --- tests/manual/clang-format-for-qtc/test.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/manual/clang-format-for-qtc/test.cpp b/tests/manual/clang-format-for-qtc/test.cpp index e205651a569..02119baef30 100644 --- a/tests/manual/clang-format-for-qtc/test.cpp +++ b/tests/manual/clang-format-for-qtc/test.cpp @@ -68,7 +68,7 @@ void lala(int foo) { Q_UNUSED(foo) Q_UNUSED(foo); - QTC_ASSERT(true, return ); // NOTE: Ops, extra space with QTC_ASSERT macro and return keyword + QTC_ASSERT(true, return); QTC_ASSERT(true, return;); while (true) QTC_ASSERT(true, break); // ...but this is fine @@ -184,7 +184,7 @@ template void fancyTemplateFunction(); template -void variadicTemplateFunction(Type *... arg); +void variadicTemplateFunction(Type *...arg); // ------------------------------------------------------------------------------------------------- // Inside functions @@ -325,11 +325,8 @@ void penaltyTests(bool isThatTrue) : (QIODevice::ReadOnly | QIODevice::Text); const auto someValue10 = functionToCall(valueX, valueY, valueXTimesY); - const auto someValue11 = functionToCall(valueX, - valueY, - valueXTimesY, - unbelievableBigValue, - unbelievableBigValue); + const auto someValue11 + = functionToCall(valueX, valueY, valueXTimesY, unbelievableBigValue, unbelievableBigValue); const auto someValue12 = functionToCall(valueX, valueY, valueXTimesY, @@ -370,9 +367,8 @@ void penaltyTests(bool isThatTrue) | unlimitedValueunbelievableBigValue); const QString path; - const bool someLongerNameNNNNNNNNNN = functionToCallSt(path, - QStringList( - QLatin1String("-print-env"))); + const bool someLongerNameNNNNNNNNNN + = functionToCallSt(path, QStringList(QLatin1String("-print-env"))); } // ------------------------------------------------------------------------------------------------- From fc5083cde9010b547e2150e68e473a1c274b7220 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 16 Feb 2024 13:27:36 +0100 Subject: [PATCH 024/128] Axivion: Get rid of AxivionServer::description setting Remove it also from aspect container. Change-Id: I948b79bc6b2ba3ae3d26a19fa61be5e4bd672a2a Reviewed-by: Christian Stenger --- src/plugins/axivion/axivionsettings.cpp | 21 +++------------------ src/plugins/axivion/axivionsettings.h | 1 - 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index 5958bfd4deb..3701a81d16d 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -27,8 +27,7 @@ namespace Axivion::Internal { bool AxivionServer::operator==(const AxivionServer &other) const { - return id == other.id && dashboard == other.dashboard && username == other.username - && description == other.description; + return id == other.id && dashboard == other.dashboard && username == other.username; } bool AxivionServer::operator!=(const AxivionServer &other) const @@ -42,7 +41,6 @@ QJsonObject AxivionServer::toJson() const result.insert("id", id.toString()); result.insert("dashboard", dashboard); result.insert("username", username); - result.insert("description", description); return result; } @@ -58,11 +56,7 @@ AxivionServer AxivionServer::fromJson(const QJsonObject &json) const QJsonValue username = json.value("username"); if (username == QJsonValue::Undefined) return invalidServer; - const QJsonValue description = json.value("description"); - if (description == QJsonValue::Undefined) - return invalidServer; - return {Id::fromString(id.toString()), dashboard.toString(), username.toString(), - description.toString()}; + return {Id::fromString(id.toString()), dashboard.toString(), username.toString()}; } static FilePath tokensFilePath() @@ -157,7 +151,6 @@ private: Id m_id; StringAspect m_dashboardUrl; StringAspect m_username; - StringAspect m_description; BoolAspect m_valid; }; @@ -176,16 +169,11 @@ DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPu m_username.setDisplayStyle(labelStyle); m_username.setPlaceHolderText(Tr::tr("User name")); - m_description.setLabelText(Tr::tr("Description:")); - m_description.setDisplayStyle(labelStyle); - m_description.setPlaceHolderText(Tr::tr("Non-empty description")); - using namespace Layouting; Form { m_dashboardUrl, br, m_username, br, - m_description, br, mode == Edit ? normalMargin : noMargin }.attachTo(this); @@ -197,7 +185,6 @@ DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPu }; connect(&m_dashboardUrl, &BaseAspect::changed, this, checkValidity); connect(&m_username, &BaseAspect::changed, this, checkValidity); - connect(&m_description, &BaseAspect::changed, this, checkValidity); } } @@ -210,7 +197,6 @@ AxivionServer DashboardSettingsWidget::dashboardServer() const result.id = m_mode == Edit ? Id::fromName(QUuid::createUuid().toByteArray()) : m_id; result.dashboard = m_dashboardUrl(); result.username = m_username(); - result.description = m_description(); return result; } @@ -219,12 +205,11 @@ void DashboardSettingsWidget::setDashboardServer(const AxivionServer &server) m_id = server.id; m_dashboardUrl.setValue(server.dashboard); m_username.setValue(server.username); - m_description.setValue(server.description); } bool DashboardSettingsWidget::isValid() const { - return !m_description().isEmpty() && isUrlValid(m_dashboardUrl()); + return isUrlValid(m_dashboardUrl()); } class AxivionSettingsWidget : public IOptionsPageWidget diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h index d692fde4a51..b4231aff901 100644 --- a/src/plugins/axivion/axivionsettings.h +++ b/src/plugins/axivion/axivionsettings.h @@ -26,7 +26,6 @@ public: Utils::Id id; QString dashboard; QString username; - QString description; bool validateCert = true; }; From accb8dc150aa3c156addcb6e23442d47762cbca5 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 15 Feb 2024 12:54:43 +0100 Subject: [PATCH 025/128] CPaster: Remove unused class Change-Id: I5b22a1bc616f3418fbc5298acb5c33ef01382555 Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger Reviewed-by: --- src/plugins/cpaster/CMakeLists.txt | 1 - src/plugins/cpaster/cpaster.qbs | 2 - .../cpaster/stickynotespasteprotocol.cpp | 249 ------------------ .../cpaster/stickynotespasteprotocol.h | 44 ---- 4 files changed, 296 deletions(-) delete mode 100644 src/plugins/cpaster/stickynotespasteprotocol.cpp delete mode 100644 src/plugins/cpaster/stickynotespasteprotocol.h diff --git a/src/plugins/cpaster/CMakeLists.txt b/src/plugins/cpaster/CMakeLists.txt index 2455ea28d6f..690862877e4 100644 --- a/src/plugins/cpaster/CMakeLists.txt +++ b/src/plugins/cpaster/CMakeLists.txt @@ -22,7 +22,6 @@ add_qtc_plugin(CodePaster pasteview.cpp pasteview.h protocol.cpp protocol.h settings.cpp settings.h - stickynotespasteprotocol.cpp stickynotespasteprotocol.h urlopenprotocol.cpp urlopenprotocol.h ../../shared/cpaster/cgi.cpp ../../shared/cpaster/cgi.h diff --git a/src/plugins/cpaster/cpaster.qbs b/src/plugins/cpaster/cpaster.qbs index 6862eacabdd..f2241ba1d0d 100644 --- a/src/plugins/cpaster/cpaster.qbs +++ b/src/plugins/cpaster/cpaster.qbs @@ -35,8 +35,6 @@ QtcPlugin { "protocol.h", "settings.cpp", "settings.h", - "stickynotespasteprotocol.cpp", - "stickynotespasteprotocol.h", "urlopenprotocol.cpp", "urlopenprotocol.h", ] diff --git a/src/plugins/cpaster/stickynotespasteprotocol.cpp b/src/plugins/cpaster/stickynotespasteprotocol.cpp deleted file mode 100644 index 5e3b2074a51..00000000000 --- a/src/plugins/cpaster/stickynotespasteprotocol.cpp +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "stickynotespasteprotocol.h" - -#include "cpastertr.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -enum { debug = 0 }; - -namespace CodePaster { - -static QByteArray expiryParameter(int daysRequested) -{ - // Obtained by 'pastebin.kde.org/api/json/parameter/expire' on 26.03.2014 - static const int expiryTimesSec[] = {1800, 21600, 86400, 604800, 2592000, 31536000}; - const int *end = expiryTimesSec + sizeof(expiryTimesSec) / sizeof(expiryTimesSec[0]); - // Find the first element >= requested span, search up to n - 1 such that 'end' defaults to last value. - const int *match = std::lower_bound(expiryTimesSec, end - 1, 24 * 60 * 60 * daysRequested); - return QByteArray("expire=") + QByteArray::number(*match); -} - -void StickyNotesPasteProtocol::setHostUrl(const QString &hostUrl) -{ - m_hostUrl = hostUrl; - if (!m_hostUrl.endsWith(QLatin1Char('/'))) - m_hostUrl.append(QLatin1Char('/')); -} - -unsigned StickyNotesPasteProtocol::capabilities() const -{ - return ListCapability | PostDescriptionCapability; -} - -bool StickyNotesPasteProtocol::checkConfiguration(QString *errorMessage) -{ - if (m_hostChecked) // Check the host once. - return true; - const bool ok = httpStatus(m_hostUrl, errorMessage, true); - if (ok) - m_hostChecked = true; - return ok; -} - -// Query 'http://pastebin.kde.org/api/json/parameter/language' to obtain the valid values. -static inline QByteArray pasteLanguage(Protocol::ContentType ct) -{ - switch (ct) { - case Protocol::Text: - break; - case Protocol::C: - return "language=c"; - case Protocol::Cpp: - return "language=cpp-qt"; - case Protocol::JavaScript: - return "language=javascript"; - case Protocol::Diff: - return "language=diff"; - case Protocol::Xml: - return "language=xml"; - } - return QByteArray("language=text"); -} - -void StickyNotesPasteProtocol::paste( - const QString &text, - ContentType ct, - int expiryDays, - const QString &username, - const QString &comment, - const QString &description - ) -{ - enum { maxDescriptionLength = 30 }; // Length of description is limited. - - Q_UNUSED(username) - Q_UNUSED(comment) - QTC_ASSERT(!m_pasteReply, return); - - // Format body - QByteArray pasteData = "&data="; - pasteData += QUrl::toPercentEncoding(fixNewLines(text)); - pasteData += '&'; - pasteData += pasteLanguage(ct); - pasteData += '&'; - pasteData += expiryParameter(expiryDays); - if (!description.isEmpty()) { - pasteData += "&title="; - pasteData += QUrl::toPercentEncoding(description.left(maxDescriptionLength)); - } - - m_pasteReply = httpPost(m_hostUrl + QLatin1String("api/json/create"), pasteData, true); - connect(m_pasteReply, &QNetworkReply::finished, this, &StickyNotesPasteProtocol::pasteFinished); - if (debug) - qDebug() << "paste: sending " << m_pasteReply << pasteData; -} - -// Parse for an element and return its contents -static QString parseElement(QIODevice *device, const QString &elementName) -{ - const QJsonDocument doc = QJsonDocument::fromJson(device->readAll()); - if (doc.isEmpty() || !doc.isObject()) - return QString(); - - QJsonObject obj= doc.object(); - const QString resultKey = QLatin1String("result"); - - if (obj.contains(resultKey)) { - QJsonValue value = obj.value(resultKey); - if (value.isObject()) { - obj = value.toObject(); - if (obj.contains(elementName)) { - value = obj.value(elementName); - return value.toString(); - } - } else if (value.isArray()) { - qWarning() << "JsonArray not expected."; - } - } - - return QString(); -} - -void StickyNotesPasteProtocol::pasteFinished() -{ - if (m_pasteReply->error()) { - qWarning("%s protocol error: %s", qPrintable(name()),qPrintable(m_pasteReply->errorString())); - } else { - // Parse id from '143204' - // No useful error reports have been observed. - const QString id = parseElement(m_pasteReply, QLatin1String("id")); - if (id.isEmpty()) - qWarning("%s protocol error: Could not send entry.", qPrintable(name())); - else - emit pasteDone(m_hostUrl + id); - } - - m_pasteReply->deleteLater(); - m_pasteReply = nullptr; -} - -void StickyNotesPasteProtocol::fetch(const QString &id) -{ - QTC_ASSERT(!m_fetchReply, return); - - // Did we get a complete URL or just an id? - m_fetchId = id; - const int lastSlashPos = m_fetchId.lastIndexOf(QLatin1Char('/')); - if (lastSlashPos != -1) - m_fetchId.remove(0, lastSlashPos + 1); - QString url = m_hostUrl + QLatin1String("api/json/show/") + m_fetchId; - if (debug) - qDebug() << "fetch: sending " << url; - - m_fetchReply = httpGet(url); - connect(m_fetchReply, &QNetworkReply::finished, - this, &StickyNotesPasteProtocol::fetchFinished); -} - -// Parse: '143228foo1320661026text -// bar' - -void StickyNotesPasteProtocol::fetchFinished() -{ - const QString title = name() + QLatin1String(": ") + m_fetchId; - QString content; - const bool error = m_fetchReply->error(); - if (error) { - content = m_fetchReply->errorString(); - if (debug) - qDebug() << "fetchFinished: error" << m_fetchId << content; - } else { - content = parseElement(m_fetchReply, QLatin1String("data")); - content.remove(QLatin1Char('\r')); - } - m_fetchReply->deleteLater(); - m_fetchReply = nullptr; - emit fetchDone(title, content, error); -} - -void StickyNotesPasteProtocol::list() -{ - QTC_ASSERT(!m_listReply, return); - - // Trailing slash is important to prevent redirection. - QString url = m_hostUrl + QLatin1String("api/json/list"); - m_listReply = httpGet(url); - connect(m_listReply, &QNetworkReply::finished, - this, &StickyNotesPasteProtocol::listFinished); - if (debug) - qDebug() << "list: sending " << url << m_listReply; -} - -// Parse 'result>id1id2...' -static inline QStringList parseList(QIODevice *device) -{ - QStringList result; - const QJsonDocument doc = QJsonDocument::fromJson(device->readAll()); - if (doc.isEmpty() || !doc.isObject()) - return result; - - QJsonObject obj= doc.object(); - const QString resultKey = QLatin1String("result"); - const QString pastesKey = QLatin1String("pastes"); - - if (obj.contains(resultKey)) { - QJsonValue value = obj.value(resultKey); - if (value.isObject()) { - obj = value.toObject(); - if (obj.contains(pastesKey)) { - value = obj.value(pastesKey); - if (value.isArray()) { - const QJsonArray array = value.toArray(); - for (const QJsonValue &val : array) - result.append(val.toString()); - } - } - } - } - return result; -} - -void StickyNotesPasteProtocol::listFinished() -{ - const bool error = m_listReply->error(); - if (error) { - if (debug) - qDebug() << "listFinished: error" << m_listReply->errorString(); - } else { - emit listDone(name(), parseList(m_listReply)); - } - m_listReply->deleteLater(); - m_listReply = nullptr; -} - -} // CodePaster diff --git a/src/plugins/cpaster/stickynotespasteprotocol.h b/src/plugins/cpaster/stickynotespasteprotocol.h deleted file mode 100644 index f212c883ef2..00000000000 --- a/src/plugins/cpaster/stickynotespasteprotocol.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "protocol.h" - -namespace CodePaster { - -class StickyNotesPasteProtocol : public NetworkProtocol -{ -public: - unsigned capabilities() const override; - - void fetch(const QString &id) override; - void paste(const QString &text, - ContentType ct = Text, - int expiryDays = 1, - const QString &username = QString(), - const QString &comment = QString(), - const QString &description = QString()) override; - void list() override; - - QString hostUrl() const { return m_hostUrl; } - void setHostUrl(const QString &hostUrl); - -private: - bool checkConfiguration(QString *errorMessage = nullptr) override; - - void fetchFinished(); - void pasteFinished(); - void listFinished(); - - QString m_hostUrl; - - QNetworkReply *m_fetchReply = nullptr; - QNetworkReply *m_pasteReply = nullptr; - QNetworkReply *m_listReply = nullptr; - - QString m_fetchId; - bool m_hostChecked = false; -}; - -} // CodePaster From 62a203c926c5d4ed565733a63f5a94a2e737d4df Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sun, 18 Feb 2024 16:15:48 +0100 Subject: [PATCH 026/128] TaskTree: Add tests for withLog() function This addresses the 7th and 8th points in the bugreport below. Task-number: QTCREATORBUG-28741 Change-Id: I5fe09227c1118e66c38bbe98ef720b21823c28ed Reviewed-by: hjk Reviewed-by: Reviewed-by: Qt CI Bot --- tests/auto/solutions/tasking/tst_tasking.cpp | 193 ++++++++++++++++++- 1 file changed, 188 insertions(+), 5 deletions(-) diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 2c20ce14d30..e95635b6dc8 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -63,6 +63,7 @@ Q_ENUM_NS(ThreadResult); using namespace PrintableEnums; using Log = QList>; +using MessageLog = QList>; struct CustomStorage { @@ -82,7 +83,8 @@ struct TestData Group root; Log expectedLog; int taskCount = 0; - DoneWith onDone = DoneWith::Success; + DoneWith result = DoneWith::Success; + std::optional messageLog = {}; }; class tst_Tasking : public QObject @@ -2970,6 +2972,84 @@ void tst_Tasking::testTree_data() << TestData{storage, recipe(false), {}, 0, DoneWith::Success}; } + { + // These tests confirms the expected message log + + const TestData testSuccess { + storage, + Group { + storage, + Group { + createSuccessTask(1, 2ms).withLog("Task 1") + }.withLog("Group 1") + }, + Log { + {1, Handler::Setup}, + {1, Handler::Success} + }, + 1, + DoneWith::Success, + MessageLog { + {"Group 1", Handler::Setup}, + {"Task 1", Handler::Setup}, + {"Task 1", Handler::Success}, + {"Group 1", Handler::Success} + } + }; + const TestData testError { + storage, + Group { + storage, + Group { + createFailingTask(1, 1ms).withLog("Task 1") + }.withLog("Group 1") + }, + Log { + {1, Handler::Setup}, + {1, Handler::Error} + }, + 1, + DoneWith::Error, + MessageLog { + {"Group 1", Handler::Setup}, + {"Task 1", Handler::Setup}, + {"Task 1", Handler::Error}, + {"Group 1", Handler::Error} + } + }; + const TestData testCancel { + storage, + Group { + storage, + parallel, + Group { + createSuccessTask(1).withLog("Task 1"), + }.withLog("Group 1"), + createSyncWithTweak(2, DoneResult::Error).withLog("Task 2") + }, + Log { + {1, Handler::Setup}, + {2, Handler::Sync}, + {2, Handler::TweakDoneToError}, + {1, Handler::Canceled} + }, + 1, + DoneWith::Error, + MessageLog { + {"Group 1", Handler::Setup}, + {"Task 1", Handler::Setup}, + {"Task 2", Handler::Setup}, + {"Task 2", Handler::Error}, + {"Task 1", Handler::Canceled}, + {"Group 1", Handler::Canceled} + } + }; + + QTest::newRow("LogSuccess") << testSuccess; + QTest::newRow("LogError") << testError; + QTest::newRow("LogCancel") << testCancel; + } + { const auto createRoot = [=](DoneResult doneResult, CallDoneIf callDoneIf) { return Group { @@ -3023,12 +3103,98 @@ void tst_Tasking::testTree_data() QTest::newRow("StorageShadowing") << storageShadowingData(); } + +static QtMessageHandler s_oldMessageHandler = nullptr; +static QStringList s_messages; + +class LogMessageHandler +{ +public: + LogMessageHandler() + { + s_messages.clear(); + s_oldMessageHandler = qInstallMessageHandler(handler); + } + ~LogMessageHandler() { qInstallMessageHandler(s_oldMessageHandler); } + +private: + static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) + { + QString message = msg; + message.remove('\r'); + s_messages.append(message); + // Defer to old message handler. + if (s_oldMessageHandler) + s_oldMessageHandler(type, context, msg); + } +}; + +static const QList s_logHandlers + = {Handler::Setup, Handler::Success, Handler::Error, Handler::Canceled}; + +static QString handlerToName(Handler handler) +{ + if (handler == Handler::Setup) + return "started"; + + DoneWith doneWith; + switch (handler) { + case Handler::Success: + doneWith = DoneWith::Success; + break; + case Handler::Error: + doneWith = DoneWith::Error; + break; + case Handler::Canceled: + doneWith = DoneWith::Cancel; + break; + default: + return {}; + } + const QMetaEnum doneWithEnum = QMetaEnum::fromType(); + return QString("finished with %1").arg(doneWithEnum.valueToKey(int(doneWith))); +} + +static bool matchesLogPattern(const QString &log, const QPair &pattern) +{ + QStringView message(log); + const static QLatin1StringView part1("TASK TREE LOG ["); + if (!message.startsWith(part1)) + return false; + + message = message.mid(part1.size()); + const static QLatin1StringView part2("HH:mm:ss.zzz"); // check only size + + message = message.mid(part2.size()); + const QString part3 = QString("] \"%1\" ").arg(pattern.first); + if (!message.startsWith(part3)) + return false; + + message = message.mid(part3.size()); + const QString part4(handlerToName(pattern.second)); + if (!message.startsWith(part4)) + return false; + + message = message.mid(part4.size()); + if (pattern.second == Handler::Setup) + return message == QLatin1StringView("."); + + const static QLatin1StringView part5(" within "); + if (!message.startsWith(part5)) + return false; + + return message.endsWith(QLatin1StringView("ms.")); +} + void tst_Tasking::testTree() { QFETCH(TestData, testData); + LogMessageHandler handler; + TaskTree taskTree({testData.root.withTimeout(1000ms)}); - QCOMPARE(taskTree.taskCount() - 1, testData.taskCount); // -1 for the timeout task above + const int taskCount = taskTree.taskCount() - 1; // -1 for the timeout task above + QCOMPARE(taskCount, testData.taskCount); Log actualLog; const auto collectLog = [&actualLog](const CustomStorage &storage) { actualLog = storage.m_log; @@ -3041,7 +3207,24 @@ void tst_Tasking::testTree() QCOMPARE(actualLog, testData.expectedLog); QCOMPARE(CustomStorage::instanceCount(), 0); - QCOMPARE(result, testData.onDone); + QCOMPARE(result, testData.result); + + if (testData.messageLog) { + QCOMPARE(s_messages.count(), testData.messageLog->count()); + + for (int i = 0; i < s_messages.count(); ++i) { + const auto &message = testData.messageLog->at(i); + QVERIFY(s_logHandlers.contains(message.second)); + const QString &log = s_messages.at(i); + if (!matchesLogPattern(log, message)) { + const QString failMessage + = QString("The log message at index %1: \"%2\" doesn't match the expected " + "pattern: \"%3\" %4.").arg(i) + .arg(log, message.first, handlerToName(message.second)); + QFAIL(failMessage.toUtf8()); + } + } + } } void tst_Tasking::testInThread_data() @@ -3094,7 +3277,7 @@ static void runInThread(QPromise &promise, const TestData &testData) promise.addResult(TestResult{i, ThreadResult::FailOnLogCheck, actualLog}); return; } - if (result != testData.onDone) { + if (result != testData.result) { promise.addResult(TestResult{i, ThreadResult::FailOnDoneStatusCheck}); return; } @@ -3126,7 +3309,7 @@ void tst_Tasking::testInThread() QCOMPARE(taskTree.isRunning(), false); QCOMPARE(CustomStorage::instanceCount(), 0); - QCOMPARE(result, testData.onDone); + QCOMPARE(result, testData.result); } struct StorageIO From b74bb782bbc001839502f879d97e07b4433d4a45 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 23:52:17 +0100 Subject: [PATCH 027/128] TaskTree: Implement asyncCount() This might be useful for checking the integrality of tasks' invocations and detecting the unwanted asynchronity in the task tree execution. Example: we may want to ensure that the setup handler of the 2nd task was executed synchronously after the done handler of the 1st task, without returning the control to the event loop. This addresses the 34th point in the bugreport below. Task-number: QTCREATORBUG-28741 Change-Id: I7213ee62a7555818cbe18fd7bb8362353a3be8ae Reviewed-by: hjk --- src/libs/solutions/tasking/tasktree.cpp | 74 +++++++++++++++++++------ src/libs/solutions/tasking/tasktree.h | 2 + 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 865b541de47..390c4382057 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -1516,9 +1516,8 @@ public: void start(); void stop(); + void bumpAsyncCount(); void advanceProgress(int byValue); - void emitStartedAndProgress(); - void emitProgress(); void emitDone(DoneWith result); void callSetupHandler(StorageBase storage, StoragePtr storagePtr) { callStorageHandler(storage, storagePtr, &StorageHandler::m_setupHandler); @@ -1578,6 +1577,7 @@ public: TaskTree *q = nullptr; Guard m_guard; int m_progressValue = 0; + int m_asyncCount = 0; QSet m_storages; QHash m_storageHandlers; std::optional m_root; @@ -1722,8 +1722,14 @@ void TaskTreePrivate::start() { QT_ASSERT(m_root, return); QT_ASSERT(!m_runtimeRoot, return); + m_asyncCount = 0; m_progressValue = 0; - emitStartedAndProgress(); + { + GuardLocker locker(m_guard); + emit q->started(); + emit q->asyncCountChanged(m_asyncCount); + emit q->progressValueChanged(m_progressValue); + } // TODO: check storage handlers for not existing storages in tree for (auto it = m_storageHandlers.cbegin(); it != m_storageHandlers.cend(); ++it) { QT_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't " @@ -1731,6 +1737,7 @@ void TaskTreePrivate::start() } m_runtimeRoot.reset(new RuntimeTask{*m_root}); start(m_runtimeRoot.get()); + bumpAsyncCount(); } void TaskTreePrivate::stop() @@ -1743,6 +1750,15 @@ void TaskTreePrivate::stop() emitDone(DoneWith::Cancel); } +void TaskTreePrivate::bumpAsyncCount() +{ + if (!m_runtimeRoot) + return; + ++m_asyncCount; + GuardLocker locker(m_guard); + emit q->asyncCountChanged(m_asyncCount); +} + void TaskTreePrivate::advanceProgress(int byValue) { if (byValue == 0) @@ -1750,18 +1766,6 @@ void TaskTreePrivate::advanceProgress(int byValue) QT_CHECK(byValue > 0); QT_CHECK(m_progressValue + byValue <= m_root->taskCount()); m_progressValue += byValue; - emitProgress(); -} - -void TaskTreePrivate::emitStartedAndProgress() -{ - GuardLocker locker(m_guard); - emit q->started(); - emit q->progressValueChanged(m_progressValue); -} - -void TaskTreePrivate::emitProgress() -{ GuardLocker locker(m_guard); emit q->progressValueChanged(m_progressValue); } @@ -2063,10 +2067,12 @@ SetupResult TaskTreePrivate::start(RuntimeTask *node) node->m_task.release()->deleteLater(); RuntimeIteration *parentIteration = node->m_parentIteration; parentIteration->deleteChild(node); - if (parentIteration->m_container->isStarting()) + if (parentIteration->m_container->isStarting()) { *unwindAction = toSetupResult(result); - else + } else { childDone(parentIteration, result); + bumpAsyncCount(); + } }); node->m_task->start(); @@ -2987,6 +2993,38 @@ DoneWith TaskTree::runBlocking(const Group &recipe, const QFuture &future, return taskTree.runBlocking(future); } +/*! + Returns the current real count of asynchronous chains of invocations. + + The returned value indicates how many times the control has returned to the caller's + event loop while the task tree is running. Initially, this value is 0. + If the execution of the task tree finishes fully synchronously, this value remains 0. + If the task tree contains any asynchronous task that is successfully started during + a call to start(), this value is bumped to 1 just before the call to start() finishes. + Later, when any asynchronous task finishes and any possible continuations are started, + this value is bumped again. The bumping continues until the task tree finishes. + After the task tree emitted the done() signal, this value isn't bumped anymore. + The asyncCountChanged() signal is emitted on every bump of this value. + + \sa asyncCountChanged() +*/ +int TaskTree::asyncCount() const +{ + return d->m_asyncCount; +} + +/*! + \fn void TaskTree::asyncCountChanged(int count) + + This signal is emitted when the running task tree is about to return control to the caller's + event loop. When the task tree is started, this signal is emitted with the value of 0, + and emitted later on every asyncCount() value bump. Every signal sent + (except the initial one with the value of 0) guarantees that the task tree is still running + asynchronously after the emission. + + \sa asyncCount() +*/ + /*! Returns the number of asynchronous tasks contained in the stored recipe. @@ -3032,7 +3070,7 @@ int TaskTree::taskCount() const When the task tree is started, this number is set to \c 0. When the task tree is finished, this number always equals progressMaximum(). - \sa progressMaximum() + \sa progressMaximum(), progressValueChanged() */ int TaskTree::progressValue() const { diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 4b067a03c4a..8ea255303ba 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -541,6 +541,7 @@ public: static DoneWith runBlocking(const Group &recipe, const QFuture &future, std::chrono::milliseconds timeout = std::chrono::milliseconds::max()); + int asyncCount() const; int taskCount() const; int progressMaximum() const { return taskCount(); } int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded @@ -565,6 +566,7 @@ public: signals: void started(); void done(DoneWith result); + void asyncCountChanged(int count); void progressValueChanged(int value); // updated whenever task finished / skipped / stopped private: From 042d87a1e11bac17a0be8758a93620e2efdbe3bb Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 13:42:37 +0100 Subject: [PATCH 028/128] CppUseSelectionsUpdater: Don't delete the watcher from its signal handler Use std::unique_ptr instead, as QScopedPointer doesn't offer release(). Task-number: QTCREATORBUG-30401 Change-Id: If8f922fb52363a2e866cfacf770f617a00aa7fe5 Reviewed-by: Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot --- src/plugins/cppeditor/cppuseselectionsupdater.cpp | 10 ++++------ src/plugins/cppeditor/cppuseselectionsupdater.h | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index e03475ea80d..3b9fe40a89e 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -3,18 +3,16 @@ #include "cppuseselectionsupdater.h" -#include "cppeditorwidget.h" #include "cppeditordocument.h" +#include "cppeditorwidget.h" #include "cppmodelmanager.h" -#include "cpptoolsreuse.h" +#include #include #include #include -#include - enum { updateUseSelectionsInternalInMs = 500 }; namespace CppEditor { @@ -67,7 +65,7 @@ CppUseSelectionsUpdater::RunnerInfo CppUseSelectionsUpdater::update(CallType cal m_runnerWatcher->cancel(); m_runnerWatcher.reset(new QFutureWatcher); - connect(m_runnerWatcher.data(), &QFutureWatcherBase::finished, + connect(m_runnerWatcher.get(), &QFutureWatcherBase::finished, this, &CppUseSelectionsUpdater::onFindUsesFinished); m_runnerRevision = m_editorWidget->document()->revision(); @@ -142,7 +140,7 @@ void CppUseSelectionsUpdater::onFindUsesFinished() processResults(m_runnerWatcher->result()); - m_runnerWatcher.reset(); + m_runnerWatcher.release()->deleteLater(); } CppUseSelectionsUpdater::ExtraSelections diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.h b/src/plugins/cppeditor/cppuseselectionsupdater.h index b303ce7c25e..88205c8974b 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.h +++ b/src/plugins/cppeditor/cppuseselectionsupdater.h @@ -54,7 +54,7 @@ private: QTimer m_timer; - QScopedPointer> m_runnerWatcher; + std::unique_ptr> m_runnerWatcher; int m_runnerRevision = -1; int m_runnerWordStartPosition = -1; bool m_updateSelections = true; From e42fa739f0f88201dc91f179bd34ce3ee9657f71 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 22:16:36 +0100 Subject: [PATCH 029/128] Axivion: Automatically fix the dashboard url Remove trailing spaces. Ensure the address ends with '/'. Change-Id: I5f51e89183e0e62c8d7d5c0cb7871477542e37d5 Reviewed-by: Christian Stenger --- src/plugins/axivion/axivionplugin.cpp | 19 +++++-------------- src/plugins/axivion/axivionsettings.cpp | 11 +++++++++-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index d7ce85baf6f..0ac6e6a8573 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -214,7 +214,7 @@ class AxivionTextMark : public TextMark { public: AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue) - : TextMark(filePath, issue.startLine, {QString("Axivion"), s_axivionTextMarkId}) + : TextMark(filePath, issue.startLine, {"Axivion", s_axivionTextMarkId}) { const QString markText = issue.description; const QString id = issue.kind + QString::number(issue.id.value_or(-1)); @@ -314,10 +314,7 @@ void AxivionPluginPrivate::onStartupProjectChanged(Project *project) static QUrl urlForProject(const QString &projectName) { - QString dashboard = settings().server.dashboard; - if (!dashboard.endsWith(QLatin1Char('/'))) - dashboard += QLatin1Char('/'); - return QUrl(dashboard).resolved(QString("api/projects/")).resolved(projectName); + return QUrl(settings().server.dashboard).resolved(QString("api/projects/")).resolved(projectName); } static constexpr int httpStatusCodeOk = 200; @@ -636,17 +633,15 @@ Group dashboardInfoRecipe(const DashboardInfoHandler &handler) handler(make_unexpected(QString("Error"))); // TODO: Collect error message in the storage. }; - const QUrl url(settings().server.dashboard); - - const auto resultHandler = [handler, url](const Dto::DashboardInfoDto &data) { - dd->m_dashboardInfo = toDashboardInfo(url, data); + const auto resultHandler = [handler](const Dto::DashboardInfoDto &data) { + dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, data); if (handler) handler(*dd->m_dashboardInfo); }; const Group root { onGroupSetup(onSetup), // Stops if cache exists. - fetchDataRecipe(url, resultHandler), + fetchDataRecipe(settings().server.dashboard, resultHandler), onGroupDone(onDone, CallDoneIf::Error) }; return root; @@ -681,10 +676,6 @@ Group issueHtmlRecipe(const QString &issueId, const HtmlHandler &handler) { QTC_ASSERT(dd->m_currentProjectInfo, return {}); // TODO: Call handler with unexpected? - QString dashboard = settings().server.dashboard; - if (!dashboard.endsWith(QLatin1Char('/'))) - dashboard += QLatin1Char('/'); - const QUrl url = urlForProject(dd->m_currentProjectInfo.value().name + '/') .resolved(QString("issues/")) .resolved(QString(issueId + '/')) diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index 3701a81d16d..86c821c439b 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -44,6 +45,12 @@ QJsonObject AxivionServer::toJson() const return result; } +static QString fixUrl(const QString &url) +{ + const QString trimmed = Utils::trimBack(url, ' '); + return trimmed.endsWith('/') ? trimmed : trimmed + '/'; +} + AxivionServer AxivionServer::fromJson(const QJsonObject &json) { const AxivionServer invalidServer; @@ -56,7 +63,7 @@ AxivionServer AxivionServer::fromJson(const QJsonObject &json) const QJsonValue username = json.value("username"); if (username == QJsonValue::Undefined) return invalidServer; - return {Id::fromString(id.toString()), dashboard.toString(), username.toString()}; + return {Id::fromString(id.toString()), fixUrl(dashboard.toString()), username.toString()}; } static FilePath tokensFilePath() @@ -195,7 +202,7 @@ AxivionServer DashboardSettingsWidget::dashboardServer() const result.id = m_id; else result.id = m_mode == Edit ? Id::fromName(QUuid::createUuid().toByteArray()) : m_id; - result.dashboard = m_dashboardUrl(); + result.dashboard = fixUrl(m_dashboardUrl()); result.username = m_username(); return result; } From c63acd9924d8617ab8332156e38c72110ed3d142 Mon Sep 17 00:00:00 2001 From: Andreas Loth Date: Tue, 20 Feb 2024 13:50:24 +0100 Subject: [PATCH 030/128] Axivion: Remove false throws comments As all dtos are json objects (or at least json arrays), the std::domain_error shouldn't appear in the wild either. Change-Id: Ib926e0c454f9eacabf8546fb0a4038fcd153715b Reviewed-by: Jarek Kobus --- src/plugins/axivion/dashboard/dto.cpp | 54 +++------------------------ 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/src/plugins/axivion/dashboard/dto.cpp b/src/plugins/axivion/dashboard/dto.cpp index 11494e67aef..3b0623fc044 100644 --- a/src/plugins/axivion/dashboard/dto.cpp +++ b/src/plugins/axivion/dashboard/dto.cpp @@ -158,6 +158,7 @@ namespace Axivion::Internal::Dto { return de_serializer::serialize(value); } + // throws std::domain_error template static QByteArray serialize_bytes(const T &value) { @@ -647,10 +648,10 @@ namespace Axivion::Internal::Dto { // version - constexpr std::array ApiVersion::number{7,7,2,13780}; - const QLatin1String ApiVersion::string{"7.7.2.13780"}; - const QLatin1String ApiVersion::name{"7.7.2"}; - const QLatin1String ApiVersion::timestamp{"2024-01-10 07:39:35 +00:00"}; + constexpr std::array ApiVersion::number{7,7,3,3857}; + const QLatin1String ApiVersion::string{"7.7.3.13857"}; + const QLatin1String ApiVersion::name{"7.7.3"}; + const QLatin1String ApiVersion::timestamp{"2024-02-07 09:28:43 +00:00"}; // AnalyzedFileDto @@ -700,7 +701,6 @@ namespace Axivion::Internal::Dto { languageName(std::move(languageName)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray AnalyzedFileDto::serialize() const { return serialize_bytes(*this); @@ -805,7 +805,6 @@ namespace Axivion::Internal::Dto { newPassword(std::move(newPassword)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ChangePasswordFormDto::serialize() const { return serialize_bytes(*this); @@ -936,7 +935,6 @@ namespace Axivion::Internal::Dto { displayColor(std::move(displayColor)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ColumnTypeOptionDto::serialize() const { return serialize_bytes(*this); @@ -980,7 +978,6 @@ namespace Axivion::Internal::Dto { text(std::move(text)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray CommentRequestDto::serialize() const { return serialize_bytes(*this); @@ -1024,7 +1021,6 @@ namespace Axivion::Internal::Dto { csrfToken(std::move(csrfToken)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray CsrfTokenDto::serialize() const { return serialize_bytes(*this); @@ -1088,7 +1084,6 @@ namespace Axivion::Internal::Dto { line(std::move(line)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray EntityDto::serialize() const { return serialize_bytes(*this); @@ -1172,7 +1167,6 @@ namespace Axivion::Internal::Dto { data(std::move(data)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ErrorDto::serialize() const { return serialize_bytes(*this); @@ -1246,7 +1240,6 @@ namespace Axivion::Internal::Dto { commentDeletionId(std::move(commentDeletionId)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueCommentDto::serialize() const { return serialize_bytes(*this); @@ -1453,7 +1446,6 @@ namespace Axivion::Internal::Dto { endColumn(std::move(endColumn)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueSourceLocationDto::serialize() const { return serialize_bytes(*this); @@ -1502,7 +1494,6 @@ namespace Axivion::Internal::Dto { color(std::move(color)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTagDto::serialize() const { return serialize_bytes(*this); @@ -1571,7 +1562,6 @@ namespace Axivion::Internal::Dto { selected(std::move(selected)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTagTypeDto::serialize() const { return serialize_bytes(*this); @@ -1686,7 +1676,6 @@ namespace Axivion::Internal::Dto { maxValue(std::move(maxValue)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricDto::serialize() const { return serialize_bytes(*this); @@ -1760,7 +1749,6 @@ namespace Axivion::Internal::Dto { entityId(std::move(entityId)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricValueTableRowDto::serialize() const { return serialize_bytes(*this); @@ -1846,7 +1834,6 @@ namespace Axivion::Internal::Dto { groups(std::move(groups)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterVisibilityDto::serialize() const { return serialize_bytes(*this); @@ -1895,7 +1882,6 @@ namespace Axivion::Internal::Dto { url(std::move(url)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ProjectReferenceDto::serialize() const { return serialize_bytes(*this); @@ -1949,7 +1935,6 @@ namespace Axivion::Internal::Dto { disabled(std::move(disabled)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RuleDto::serialize() const { return serialize_bytes(*this); @@ -2080,7 +2065,6 @@ namespace Axivion::Internal::Dto { buildDate(std::move(buildDate)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ToolsVersionDto::serialize() const { return serialize_bytes(*this); @@ -2176,7 +2160,6 @@ namespace Axivion::Internal::Dto { Removed(std::move(Removed)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray VersionKindCountDto::serialize() const { return serialize_bytes(*this); @@ -2260,7 +2243,6 @@ namespace Axivion::Internal::Dto { cloneRatio(std::move(cloneRatio)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray AnalysisVersionDto::serialize() const { return serialize_bytes(*this); @@ -2351,7 +2333,6 @@ namespace Axivion::Internal::Dto { type = ApiTokenTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ApiTokenCreationRequestDto::serialize() const { return serialize_bytes(*this); @@ -2505,7 +2486,6 @@ namespace Axivion::Internal::Dto { type = ApiTokenTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ApiTokenInfoDto::serialize() const { return serialize_bytes(*this); @@ -2658,7 +2638,6 @@ namespace Axivion::Internal::Dto { type = ColumnTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ColumnInfoDto::serialize() const { return serialize_bytes(*this); @@ -2772,7 +2751,6 @@ namespace Axivion::Internal::Dto { csrfTokenUrl(std::move(csrfTokenUrl)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray DashboardInfoDto::serialize() const { return serialize_bytes(*this); @@ -2816,7 +2794,6 @@ namespace Axivion::Internal::Dto { comments(std::move(comments)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueCommentListDto::serialize() const { return serialize_bytes(*this); @@ -2900,7 +2877,6 @@ namespace Axivion::Internal::Dto { prefix = IssueKindMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueKindInfoDto::serialize() const { return serialize_bytes(*this); @@ -2944,7 +2920,6 @@ namespace Axivion::Internal::Dto { tags(std::move(tags)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTagTypeListDto::serialize() const { return serialize_bytes(*this); @@ -3070,7 +3045,6 @@ namespace Axivion::Internal::Dto { kind = IssueKindMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray LineMarkerDto::serialize() const { return serialize_bytes(*this); @@ -3147,7 +3121,6 @@ namespace Axivion::Internal::Dto { severity = MessageSeverityMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RepositoryUpdateMessageDto::serialize() const { return serialize_bytes(*this); @@ -3191,7 +3164,6 @@ namespace Axivion::Internal::Dto { rules(std::move(rules)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RuleListDto::serialize() const { return serialize_bytes(*this); @@ -3268,7 +3240,6 @@ namespace Axivion::Internal::Dto { direction = SortDirectionMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray SortInfoDto::serialize() const { return serialize_bytes(*this); @@ -3361,7 +3332,6 @@ namespace Axivion::Internal::Dto { type = UserRefTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray UserRefDto::serialize() const { return serialize_bytes(*this); @@ -3410,7 +3380,6 @@ namespace Axivion::Internal::Dto { rows(std::move(rows)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray AnalyzedFileListDto::serialize() const { return serialize_bytes(*this); @@ -3459,7 +3428,6 @@ namespace Axivion::Internal::Dto { entities(std::move(entities)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray EntityListDto::serialize() const { return serialize_bytes(*this); @@ -3518,7 +3486,6 @@ namespace Axivion::Internal::Dto { lineMarkers(std::move(lineMarkers)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray FileViewDto::serialize() const { return serialize_bytes(*this); @@ -3630,7 +3597,6 @@ namespace Axivion::Internal::Dto { kind = IssueKindMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueDto::serialize() const { return serialize_bytes(*this); @@ -3709,7 +3675,6 @@ namespace Axivion::Internal::Dto { totalRemovedCount(std::move(totalRemovedCount)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTableDto::serialize() const { return serialize_bytes(*this); @@ -3758,7 +3723,6 @@ namespace Axivion::Internal::Dto { metrics(std::move(metrics)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricListDto::serialize() const { return serialize_bytes(*this); @@ -3822,7 +3786,6 @@ namespace Axivion::Internal::Dto { values(std::move(values)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricValueRangeDto::serialize() const { return serialize_bytes(*this); @@ -3871,7 +3834,6 @@ namespace Axivion::Internal::Dto { rows(std::move(rows)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricValueTableDto::serialize() const { return serialize_bytes(*this); @@ -3969,7 +3931,6 @@ namespace Axivion::Internal::Dto { kind = IssueKindForNamedFilterCreationMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterCreateDto::serialize() const { return serialize_bytes(*this); @@ -4111,7 +4072,6 @@ namespace Axivion::Internal::Dto { type = NamedFilterTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterInfoDto::serialize() const { return serialize_bytes(*this); @@ -4170,7 +4130,6 @@ namespace Axivion::Internal::Dto { visibility(std::move(visibility)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterUpdateDto::serialize() const { return serialize_bytes(*this); @@ -4244,7 +4203,6 @@ namespace Axivion::Internal::Dto { hasHiddenIssues(std::move(hasHiddenIssues)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ProjectInfoDto::serialize() const { return serialize_bytes(*this); @@ -4298,7 +4256,6 @@ namespace Axivion::Internal::Dto { hasWarnings(std::move(hasWarnings)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RepositoryUpdateResponseDto::serialize() const { return serialize_bytes(*this); @@ -4367,7 +4324,6 @@ namespace Axivion::Internal::Dto { axivionDefaultFilter(std::move(axivionDefaultFilter)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray TableInfoDto::serialize() const { return serialize_bytes(*this); From 2e276fb8a6842ae2fe1ad87fb1f44044acf671ad Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 13 Feb 2024 14:20:23 +0100 Subject: [PATCH 031/128] Axivion: Allow highlighting marks Adds a setting to enable highlighting issue markers on the highlighter scrollbar. Change-Id: I173510ccb75e684325135d8e587e8920ad22bd86 Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionplugin.cpp | 13 +++++++++---- src/plugins/axivion/axivionsettings.cpp | 17 +++++++++++++---- src/plugins/axivion/axivionsettings.h | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 0ac6e6a8573..1cd8c9fc140 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -213,13 +213,16 @@ static AxivionPluginPrivate *dd = nullptr; class AxivionTextMark : public TextMark { public: - AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue) + AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue, + std::optional color) : TextMark(filePath, issue.startLine, {"Axivion", s_axivionTextMarkId}) { const QString markText = issue.description; const QString id = issue.kind + QString::number(issue.id.value_or(-1)); - setToolTip(id + markText); + setToolTip(id + '\n' + markText); setIcon(iconForIssue(issue.kind)); + if (color) + setColor(*color); setPriority(TextMark::NormalPriority); setLineAnnotation(markText); setActionsProvider([id] { @@ -818,12 +821,14 @@ void AxivionPluginPrivate::handleIssuesForFile(const Dto::FileViewDto &fileView) return; const FilePath filePath = project->projectDirectory().pathAppended(fileView.fileName); - + std::optional color = std::nullopt; + if (settings().highlightMarks()) + color.emplace(Theme::Color(Theme::Bookmarks_TextMarkColor)); // FIXME! for (const Dto::LineMarkerDto &marker : std::as_const(fileView.lineMarkers)) { // FIXME the line location can be wrong (even the whole issue could be wrong) // depending on whether this line has been changed since the last axivion run and the // current state of the file - some magic has to happen here - new AxivionTextMark(filePath, marker); + new AxivionTextMark(filePath, marker, color); } } diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index 86c821c439b..a4db242c2dd 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -106,6 +106,10 @@ AxivionSettings::AxivionSettings() { setSettingsGroup("Axivion"); + highlightMarks.setSettingsKey("HighlightMarks"); + highlightMarks.setLabelText(Tr::tr("Highlight marks")); + highlightMarks.setToolTip(Tr::tr("Check to enable issue marks on the scroll bar.")); + highlightMarks.setDefaultValue(false); AspectContainer::readSettings(); server = readTokenFile(tokensFilePath()); @@ -240,10 +244,15 @@ AxivionSettingsWidget::AxivionSettingsWidget() m_dashboardDisplay = new DashboardSettingsWidget(DashboardSettingsWidget::Display, this); m_dashboardDisplay->setDashboardServer(settings().server); m_edit = new QPushButton(Tr::tr("Edit..."), this); - Row { - Form { - m_dashboardDisplay, br, - }, Column { m_edit, st } + Column { + Row { + Form { + m_dashboardDisplay, br + }, st, + Column { m_edit }, + }, + Space(10), br, + Row { settings().highlightMarks }, st }.attachTo(this); connect(m_edit, &QPushButton::clicked, this, &AxivionSettingsWidget::showEditServerDialog); diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h index b4231aff901..257b4056c56 100644 --- a/src/plugins/axivion/axivionsettings.h +++ b/src/plugins/axivion/axivionsettings.h @@ -38,6 +38,7 @@ public: void toSettings() const; AxivionServer server; // shall we have more than one? + Utils::BoolAspect highlightMarks{this}; }; AxivionSettings &settings(); From 5d630786852e1d653acea5e94cacfbb30d2dc791 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 14:47:02 +0100 Subject: [PATCH 032/128] SemanticHighlighter: Simplify disconnecting from the watcher Change-Id: I92c78f71a209ac428354284649e0f4b6522a947f Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/semantichighlighter.cpp | 27 +++++-------------- src/plugins/cppeditor/semantichighlighter.h | 3 --- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp index 4cafaecd46f..17cfb2c40e4 100644 --- a/src/plugins/cppeditor/semantichighlighter.cpp +++ b/src/plugins/cppeditor/semantichighlighter.cpp @@ -39,7 +39,7 @@ SemanticHighlighter::SemanticHighlighter(TextDocument *baseTextDocument) SemanticHighlighter::~SemanticHighlighter() { if (m_watcher) { - disconnectWatcher(); + m_watcher->disconnect(this); m_watcher->cancel(); m_watcher->waitForFinished(); } @@ -57,11 +57,14 @@ void SemanticHighlighter::run() qCDebug(log) << "SemanticHighlighter: run()"; if (m_watcher) { - disconnectWatcher(); + m_watcher->disconnect(this); m_watcher->cancel(); } m_watcher.reset(new QFutureWatcher); - connectWatcher(); + connect(m_watcher.get(), &QFutureWatcherBase::resultsReadyAt, + this, &SemanticHighlighter::onHighlighterResultAvailable); + connect(m_watcher.get(), &QFutureWatcherBase::finished, + this, &SemanticHighlighter::onHighlighterFinished); m_revision = documentRevision(); m_seenBlocks.clear(); @@ -236,24 +239,6 @@ void SemanticHighlighter::onHighlighterFinished() qCDebug(log) << "onHighlighterFinished() took" << t.elapsed() << "ms"; } -void SemanticHighlighter::connectWatcher() -{ - using Watcher = QFutureWatcher; - connect(m_watcher.get(), &Watcher::resultsReadyAt, - this, &SemanticHighlighter::onHighlighterResultAvailable); - connect(m_watcher.get(), &Watcher::finished, - this, &SemanticHighlighter::onHighlighterFinished); -} - -void SemanticHighlighter::disconnectWatcher() -{ - using Watcher = QFutureWatcher; - disconnect(m_watcher.get(), &Watcher::resultsReadyAt, - this, &SemanticHighlighter::onHighlighterResultAvailable); - disconnect(m_watcher.get(), &Watcher::finished, - this, &SemanticHighlighter::onHighlighterFinished); -} - unsigned SemanticHighlighter::documentRevision() const { return m_baseTextDocument->document()->revision(); diff --git a/src/plugins/cppeditor/semantichighlighter.h b/src/plugins/cppeditor/semantichighlighter.h index a0d8e8db9ee..60edcb22159 100644 --- a/src/plugins/cppeditor/semantichighlighter.h +++ b/src/plugins/cppeditor/semantichighlighter.h @@ -70,9 +70,6 @@ private: void handleHighlighterResults(); void onHighlighterFinished(); - void connectWatcher(); - void disconnectWatcher(); - unsigned documentRevision() const; QVector getClearedParentheses(const QTextBlock &block); From 7d035f34d408294b26c2f36223948285bcef93f2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 20 Feb 2024 14:55:56 +0100 Subject: [PATCH 033/128] TaskTree: Fix docs for asyncCount() Amends b74bb782bbc001839502f879d97e07b4433d4a45 Change-Id: I6da60e283c6bbd85ed109e37302611786f42c012 Reviewed-by: Leena Miettinen --- src/libs/solutions/tasking/tasktree.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 390c4382057..112c6dca75f 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -2996,14 +2996,14 @@ DoneWith TaskTree::runBlocking(const Group &recipe, const QFuture &future, /*! Returns the current real count of asynchronous chains of invocations. - The returned value indicates how many times the control has returned to the caller's + The returned value indicates how many times the control returns to the caller's event loop while the task tree is running. Initially, this value is 0. If the execution of the task tree finishes fully synchronously, this value remains 0. - If the task tree contains any asynchronous task that is successfully started during + If the task tree contains any asynchronous tasks that are successfully started during a call to start(), this value is bumped to 1 just before the call to start() finishes. Later, when any asynchronous task finishes and any possible continuations are started, this value is bumped again. The bumping continues until the task tree finishes. - After the task tree emitted the done() signal, this value isn't bumped anymore. + When the task tree emits the done() signal, the bumping stops. The asyncCountChanged() signal is emitted on every bump of this value. \sa asyncCountChanged() @@ -3017,10 +3017,10 @@ int TaskTree::asyncCount() const \fn void TaskTree::asyncCountChanged(int count) This signal is emitted when the running task tree is about to return control to the caller's - event loop. When the task tree is started, this signal is emitted with the value of 0, - and emitted later on every asyncCount() value bump. Every signal sent - (except the initial one with the value of 0) guarantees that the task tree is still running - asynchronously after the emission. + event loop. When the task tree is started, this signal is emitted with \a count value of 0, + and emitted later on every asyncCount() value bump with an updated \a count value. + Every signal sent (except the initial one with the value of 0) guarantees that the task tree + is still running asynchronously after the emission. \sa asyncCount() */ From e930b29d669af8bb03c59728c406446e3da3a9e5 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 20 Feb 2024 09:55:10 +0100 Subject: [PATCH 034/128] CppEditor: Do not access document manager when gathering local symbols As it turns out, that code runs in a dedicated thread. Task-number: QTCREATORBUG-30401 Change-Id: I66236fa00b8ddb55276d822357517fdccb0e61df Reviewed-by: Reviewed-by: Jarek Kobus Reviewed-by: Qt CI Bot --- src/plugins/cppeditor/builtincursorinfo.cpp | 42 ++++++++++++++----- src/plugins/cppeditor/builtincursorinfo.h | 2 +- .../builtineditordocumentprocessor.cpp | 2 +- src/plugins/cppeditor/cppchecksymbols.cpp | 30 +++++++++---- src/plugins/cppeditor/cppchecksymbols.h | 4 ++ .../cppeditor/cppfunctiondecldeflink.cpp | 2 +- src/plugins/cppeditor/cppindexingsupport.cpp | 2 +- src/plugins/cppeditor/cpplocalsymbols.cpp | 36 +++++++--------- src/plugins/cppeditor/cpplocalsymbols.h | 2 +- .../cppeditor/cpplocalsymbols_test.cpp | 2 +- .../checksymbols/tst_checksymbols.cpp | 2 +- 11 files changed, 78 insertions(+), 48 deletions(-) diff --git a/src/plugins/cppeditor/builtincursorinfo.cpp b/src/plugins/cppeditor/builtincursorinfo.cpp index 15e1e0f4921..0bff4406e76 100644 --- a/src/plugins/cppeditor/builtincursorinfo.cpp +++ b/src/plugins/cppeditor/builtincursorinfo.cpp @@ -135,17 +135,28 @@ private: class FindUses { public: - static CursorInfo find(const Document::Ptr document, const Snapshot &snapshot, - int line, int column, Scope *scope, const QString &expression) + static CursorInfo find(const Document::Ptr document, + const QString &content, + const Snapshot &snapshot, + int line, + int column, + Scope *scope, + const QString &expression) { - FindUses findUses(document, snapshot, line, column, scope, expression); + FindUses findUses(document, content, snapshot, line, column, scope, expression); return findUses.doFind(); } private: - FindUses(const Document::Ptr document, const Snapshot &snapshot, int line, int column, - Scope *scope, const QString &expression) + FindUses(const Document::Ptr document, + const QString &content, + const Snapshot &snapshot, + int line, + int column, + Scope *scope, + const QString &expression) : m_document(document) + , m_content(content) , m_line(line) , m_column(column) , m_scope(scope) @@ -160,7 +171,7 @@ private: // findLocalUses operates with 1-based line and 0-based column const SemanticInfo::LocalUseMap localUses - = BuiltinCursorInfo::findLocalUses(m_document, m_line, m_column - 1); + = BuiltinCursorInfo::findLocalUses(m_document, m_content, m_line, m_column - 1); result.localUses = localUses; splitLocalUses(localUses, &result.useRanges, &result.unusedVariablesRanges); @@ -230,6 +241,8 @@ private: // Shared Document::Ptr m_document; + const QString m_content; + // For local use calculation int m_line; int m_column; @@ -322,11 +335,20 @@ QFuture BuiltinCursorInfo::run(const CursorInfoParams &cursorInfoPar QString expression; Scope *scope = canonicalSymbol.getScopeAndExpression(textCursor, &expression); - return Utils::asyncRun(&FindUses::find, document, snapshot, line, column + 1, scope, expression); + return Utils::asyncRun(&FindUses::find, + document, + textCursor.document()->toPlainText(), + snapshot, + line, + column + 1, + scope, + expression); } -SemanticInfo::LocalUseMap -BuiltinCursorInfo::findLocalUses(const Document::Ptr &document, int line, int column) +SemanticInfo::LocalUseMap BuiltinCursorInfo::findLocalUses(const Document::Ptr &document, + const QString &content, + int line, + int column) { if (!document || !document->translationUnit() || !document->translationUnit()->ast()) return SemanticInfo::LocalUseMap(); @@ -334,7 +356,7 @@ BuiltinCursorInfo::findLocalUses(const Document::Ptr &document, int line, int co AST *ast = document->translationUnit()->ast(); FunctionDefinitionUnderCursor functionDefinitionUnderCursor(document->translationUnit()); DeclarationAST *declaration = functionDefinitionUnderCursor(ast, line, column); - return Internal::LocalSymbols(document, declaration).uses; + return Internal::LocalSymbols(document, content, declaration).uses; } } // namespace CppEditor diff --git a/src/plugins/cppeditor/builtincursorinfo.h b/src/plugins/cppeditor/builtincursorinfo.h index 4258f4854c2..731c66f4c17 100644 --- a/src/plugins/cppeditor/builtincursorinfo.h +++ b/src/plugins/cppeditor/builtincursorinfo.h @@ -18,7 +18,7 @@ public: static QFuture run(const CursorInfoParams ¶ms); static SemanticInfo::LocalUseMap - findLocalUses(const CPlusPlus::Document::Ptr &document, int line, int column); + findLocalUses(const CPlusPlus::Document::Ptr &document, const QString &content, int line, int column); }; } // namespace CppEditor diff --git a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp index 4d43546334a..b934d1e7aed 100644 --- a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp +++ b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp @@ -126,7 +126,7 @@ CheckSymbols *createHighlighter(const CPlusPlus::Document::Ptr &doc, } LookupContext context(doc, snapshot); - return CheckSymbols::create(doc, context, macroUses); + return CheckSymbols::create(doc, textDocument->toPlainText(), context, macroUses); } QList toTextEditorBlocks( diff --git a/src/plugins/cppeditor/cppchecksymbols.cpp b/src/plugins/cppeditor/cppchecksymbols.cpp index e9334b0db29..7fa1ed3f5bd 100644 --- a/src/plugins/cppeditor/cppchecksymbols.cpp +++ b/src/plugins/cppeditor/cppchecksymbols.cpp @@ -270,28 +270,40 @@ static bool acceptName(NameAST *ast, unsigned *referenceToken) && !ast->asOperatorFunctionId(); } -CheckSymbols::Future CheckSymbols::go(Document::Ptr doc, const LookupContext &context, const QList ¯oUses) +CheckSymbols::Future CheckSymbols::go(Document::Ptr doc, + const QString &content, + const LookupContext &context, + const QList ¯oUses) { QTC_ASSERT(doc, return Future()); QTC_ASSERT(doc->translationUnit(), return Future()); QTC_ASSERT(doc->translationUnit()->ast(), return Future()); - return (new CheckSymbols(doc, context, macroUses))->start(); + return (new CheckSymbols(doc, content, context, macroUses))->start(); } -CheckSymbols * CheckSymbols::create(Document::Ptr doc, const LookupContext &context, - const QList ¯oUses) +CheckSymbols *CheckSymbols::create(Document::Ptr doc, + const QString &content, + const LookupContext &context, + const QList ¯oUses) { QTC_ASSERT(doc, return nullptr); QTC_ASSERT(doc->translationUnit(), return nullptr); QTC_ASSERT(doc->translationUnit()->ast(), return nullptr); - return new CheckSymbols(doc, context, macroUses); + return new CheckSymbols(doc, content, context, macroUses); } -CheckSymbols::CheckSymbols(Document::Ptr doc, const LookupContext &context, const QList ¯oUses) - : ASTVisitor(doc->translationUnit()), _doc(doc), _context(context) - , _lineOfLastUsage(0), _macroUses(macroUses) +CheckSymbols::CheckSymbols(Document::Ptr doc, + const QString &content, + const LookupContext &context, + const QList ¯oUses) + : ASTVisitor(doc->translationUnit()) + , _doc(doc) + , _content(content) + , _context(context) + , _lineOfLastUsage(0) + , _macroUses(macroUses) { int line = 0; getTokenEndPosition(translationUnit()->ast()->lastToken(), &line, nullptr); @@ -1114,7 +1126,7 @@ bool CheckSymbols::visit(FunctionDefinitionAST *ast) accept(ast->ctor_initializer); accept(ast->function_body); - const Internal::LocalSymbols locals(_doc, ast); + const Internal::LocalSymbols locals(_doc, _content, ast); for (const QList &uses : std::as_const(locals.uses)) { for (const Result &u : uses) addUse(u); diff --git a/src/plugins/cppeditor/cppchecksymbols.h b/src/plugins/cppeditor/cppchecksymbols.h index f660da6dc90..38e04f989d6 100644 --- a/src/plugins/cppeditor/cppchecksymbols.h +++ b/src/plugins/cppeditor/cppchecksymbols.h @@ -42,9 +42,11 @@ public: } static Future go(CPlusPlus::Document::Ptr doc, + const QString &content, const CPlusPlus::LookupContext &context, const QList ¯oUses); static CheckSymbols * create(CPlusPlus::Document::Ptr doc, + const QString &content, const CPlusPlus::LookupContext &context, const QList ¯oUses); @@ -79,6 +81,7 @@ protected: }; CheckSymbols(CPlusPlus::Document::Ptr doc, + const QString &content, const CPlusPlus::LookupContext &context, const QList &otherUses); @@ -168,6 +171,7 @@ private: bool isConstructorDeclaration(CPlusPlus::Symbol *declaration); CPlusPlus::Document::Ptr _doc; + const QString _content; CPlusPlus::LookupContext _context; CPlusPlus::TypeOfExpression typeOfExpression; Utils::FilePath _filePath; diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index 4dc31087bc4..9552ba21984 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -890,7 +890,7 @@ ChangeSet FunctionDeclDefLink::changes(const Snapshot &snapshot, int targetOffse // for function definitions, rename the local usages FunctionDefinitionAST *targetDefinition = targetDeclaration->asFunctionDefinition(); if (targetDefinition && !renamedTargetParameters.isEmpty()) { - const LocalSymbols localSymbols(targetFile->cppDocument(), targetDefinition); + const LocalSymbols localSymbols(targetFile->cppDocument(), {}, targetDefinition); const int endOfArguments = targetFile->endOf(targetFunctionDeclarator->rparen_token); for (auto it = renamedTargetParameters.cbegin(), end = renamedTargetParameters.cend(); diff --git a/src/plugins/cppeditor/cppindexingsupport.cpp b/src/plugins/cppeditor/cppindexingsupport.cpp index 21a5da4109f..3a418a5b782 100644 --- a/src/plugins/cppeditor/cppindexingsupport.cpp +++ b/src/plugins/cppeditor/cppindexingsupport.cpp @@ -147,7 +147,7 @@ static void indexFindErrors(QPromise &promise, const ParseParams params) // Look up symbols CPlusPlus::LookupContext context(document, parser.snapshot()); - CheckSymbols::go(document, context, QList()).waitForFinished(); + CheckSymbols::go(document, {}, context, QList()).waitForFinished(); document->releaseSourceAndAST(); diff --git a/src/plugins/cppeditor/cpplocalsymbols.cpp b/src/plugins/cppeditor/cpplocalsymbols.cpp index 9983f9fba11..ec91dbe349a 100644 --- a/src/plugins/cppeditor/cpplocalsymbols.cpp +++ b/src/plugins/cppeditor/cpplocalsymbols.cpp @@ -7,12 +7,12 @@ #include "cpptoolsreuse.h" #include "semantichighlighter.h" -#include #include #include -#include #include +#include + using namespace CPlusPlus; namespace CppEditor::Internal { @@ -22,8 +22,8 @@ namespace { class FindLocalSymbols: protected ASTVisitor { public: - explicit FindLocalSymbols(Document::Ptr doc) - : ASTVisitor(doc->translationUnit()), _doc(doc) + explicit FindLocalSymbols(Document::Ptr doc, const QString &content) + : ASTVisitor(doc->translationUnit()), _doc(doc), _content(content) { } // local and external uses. @@ -49,35 +49,26 @@ public: if (localUses.isEmpty()) return; - // For tst_checkSymbols - if (!Core::DocumentManager::instance()) - return; - // Look for parameter occurrences in function comments. - const TextEditor::TextDocument * const editorDoc - = TextEditor::TextDocument::textDocumentForFilePath(_doc->filePath()); - if (!editorDoc) + if (_content.isEmpty()) return; - QTextDocument * const textDoc = editorDoc->document(); - if (!textDoc) - return; - const QString &content = textDoc->toPlainText(); - const QStringView docView(content); + QTextDocument textDoc(_content); + const QStringView docView(_content); for (auto it = localUses.begin(); it != localUses.end(); ++it) { Symbol * const symbol = it.key(); if (!symbol->asArgument()) continue; - const QList commentTokens = commentsForDeclaration(symbol, ast, *textDoc, _doc); + const QList commentTokens = commentsForDeclaration(symbol, ast, textDoc, _doc); if (commentTokens.isEmpty()) continue; const QString symbolName = Overview().prettyName(symbol->name()); for (const Token &tok : commentTokens) { - const int commentPos = translationUnit()->getTokenPositionInDocument(tok, textDoc); + const int commentPos = translationUnit()->getTokenPositionInDocument(tok, &textDoc); const int commentEndPos = translationUnit()->getTokenEndPositionInDocument( - tok, textDoc); + tok, &textDoc); const QStringView commentView = docView.mid(commentPos, commentEndPos - commentPos); const QList ranges = symbolOccurrencesInText( - *textDoc, commentView, commentPos, symbolName); + textDoc, commentView, commentPos, symbolName); for (const Utils::Text::Range &range : ranges) { it.value().append(HighlightingResult(range.begin.line, range.begin.column + 1, symbolName.size(), @@ -323,14 +314,15 @@ protected: private: QList _scopeStack; Document::Ptr _doc; + const QString _content; }; } // end of anonymous namespace -LocalSymbols::LocalSymbols(Document::Ptr doc, DeclarationAST *ast) +LocalSymbols::LocalSymbols(Document::Ptr doc, const QString &content, DeclarationAST *ast) { - FindLocalSymbols findLocalSymbols(doc); + FindLocalSymbols findLocalSymbols(doc, content); findLocalSymbols(ast); uses = findLocalSymbols.localUses; } diff --git a/src/plugins/cppeditor/cpplocalsymbols.h b/src/plugins/cppeditor/cpplocalsymbols.h index 3c178f2f40e..26cd185b729 100644 --- a/src/plugins/cppeditor/cpplocalsymbols.h +++ b/src/plugins/cppeditor/cpplocalsymbols.h @@ -12,7 +12,7 @@ class LocalSymbols Q_DISABLE_COPY(LocalSymbols) public: - LocalSymbols(CPlusPlus::Document::Ptr doc, CPlusPlus::DeclarationAST *ast); + LocalSymbols(CPlusPlus::Document::Ptr doc, const QString &content, CPlusPlus::DeclarationAST *ast); SemanticInfo::LocalUseMap uses; }; diff --git a/src/plugins/cppeditor/cpplocalsymbols_test.cpp b/src/plugins/cppeditor/cpplocalsymbols_test.cpp index 9605f24ffa8..740fd8233c9 100644 --- a/src/plugins/cppeditor/cpplocalsymbols_test.cpp +++ b/src/plugins/cppeditor/cpplocalsymbols_test.cpp @@ -160,7 +160,7 @@ void LocalSymbolsTest::test() FindFirstFunctionDefinition find(document->translationUnit()); CPlusPlus::DeclarationAST *functionDefinition = find(); - LocalSymbols localSymbols(document, functionDefinition); + LocalSymbols localSymbols(document, QString::fromUtf8(source), functionDefinition); const QList actualUses = Result::fromLocalUses(localSymbols.uses); // for (const Result &result : actualUses) diff --git a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp index 0e0705c5e56..4a49e8852f4 100644 --- a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp +++ b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp @@ -109,7 +109,7 @@ public: const UseList &expectedUsesMacros = UseList()) { LookupContext context(document, snapshot); - CheckSymbols::Future future = CheckSymbols::go(document, context, expectedUsesMacros); + CheckSymbols::Future future = CheckSymbols::go(document, {}, context, expectedUsesMacros); future.waitForFinished(); return future; } From af54f62166c67b471243177b8caad21ac4634cf2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 14:54:25 +0100 Subject: [PATCH 035/128] SemanticHighlighter: Add internal FutureSynchronizer As it's not clear whether canceled future may continue to run when the SemanticHighlighter instance is destructed, add internal FutureSynchronizer that ensures that all old, canceled futures are finished from the SemanticHighlighter's d'tor. Change-Id: I3128999f1250d666fcc3aa04599bb4a9c675ca3e Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/semantichighlighter.cpp | 14 +++----------- src/plugins/cppeditor/semantichighlighter.h | 5 ++++- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp index 17cfb2c40e4..d841ae19c4e 100644 --- a/src/plugins/cppeditor/semantichighlighter.cpp +++ b/src/plugins/cppeditor/semantichighlighter.cpp @@ -36,14 +36,7 @@ SemanticHighlighter::SemanticHighlighter(TextDocument *baseTextDocument) updateFormatMapFromFontSettings(); } -SemanticHighlighter::~SemanticHighlighter() -{ - if (m_watcher) { - m_watcher->disconnect(this); - m_watcher->cancel(); - m_watcher->waitForFinished(); - } -} +SemanticHighlighter::~SemanticHighlighter() = default; void SemanticHighlighter::setHighlightingRunner(HighlightingRunner highlightingRunner) { @@ -56,10 +49,8 @@ void SemanticHighlighter::run() qCDebug(log) << "SemanticHighlighter: run()"; - if (m_watcher) { - m_watcher->disconnect(this); + if (m_watcher) m_watcher->cancel(); - } m_watcher.reset(new QFutureWatcher); connect(m_watcher.get(), &QFutureWatcherBase::resultsReadyAt, this, &SemanticHighlighter::onHighlighterResultAvailable); @@ -71,6 +62,7 @@ void SemanticHighlighter::run() m_nextResultToHandle = m_resultCount = 0; qCDebug(log) << "starting runner for document revision" << m_revision; m_watcher->setFuture(m_highlightingRunner()); + m_futureSynchronizer.addFuture(m_watcher->future()); } Parentheses SemanticHighlighter::getClearedParentheses(const QTextBlock &block) diff --git a/src/plugins/cppeditor/semantichighlighter.h b/src/plugins/cppeditor/semantichighlighter.h index 60edcb22159..f4fb25ddca3 100644 --- a/src/plugins/cppeditor/semantichighlighter.h +++ b/src/plugins/cppeditor/semantichighlighter.h @@ -5,6 +5,8 @@ #include "cppeditor_global.h" +#include + #include #include #include @@ -77,13 +79,14 @@ private: TextEditor::TextDocument *m_baseTextDocument; unsigned m_revision = 0; - std::unique_ptr> m_watcher; QHash m_formatMap; std::set m_seenBlocks; int m_nextResultToHandle = 0; int m_resultCount = 0; HighlightingRunner m_highlightingRunner; + Utils::FutureSynchronizer m_futureSynchronizer; // Keep before m_watcher. + std::unique_ptr> m_watcher; }; } // namespace CppEditor From 2cb18715ebbcacb99c5aec5db5e4ca12daad491f Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 19 Feb 2024 13:49:03 +0100 Subject: [PATCH 036/128] CppUseSelectionsUpdater: Ensure the old futures are synchronized When scheduling a new async run (via FindUses::find()), keep track for the canceled futures, otherwise they may still be running after the shutdown. So, add all new futures into the future synchronizer (without canceling them). Task-number: QTCREATORBUG-30401 Change-Id: I83a82f706175060f7f29886b57c69c77667a0805 Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/cppeditor/cppuseselectionsupdater.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index 3b9fe40a89e..67fb6d7a1bc 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -7,6 +7,9 @@ #include "cppeditorwidget.h" #include "cppmodelmanager.h" +#include + +#include #include #include @@ -72,6 +75,7 @@ CppUseSelectionsUpdater::RunnerInfo CppUseSelectionsUpdater::update(CallType cal m_runnerWordStartPosition = params.textCursor.position(); m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params)); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_runnerWatcher->future()); return RunnerInfo::Started; } else { // synchronous case abortSchedule(); From 2ff4eec9ac4a2fd13db06c78d788996c7e7c4c5e Mon Sep 17 00:00:00 2001 From: Andreas Loth Date: Tue, 20 Feb 2024 16:13:34 +0100 Subject: [PATCH 037/128] Axivion: Provide non-throwing overloads for DTO deserializing Change-Id: I8dadd76b1e6f0b22293c4e8758759c2b86092926 Reviewed-by: Jarek Kobus --- src/plugins/axivion/dashboard/dto.cpp | 294 ++++++++++++++++++++++++++ src/plugins/axivion/dashboard/dto.h | 114 +++++++++- 2 files changed, 407 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/dashboard/dto.cpp b/src/plugins/axivion/dashboard/dto.cpp index 3b0623fc044..d578e3dff10 100644 --- a/src/plugins/axivion/dashboard/dto.cpp +++ b/src/plugins/axivion/dashboard/dto.cpp @@ -127,6 +127,26 @@ namespace Axivion::Internal::Dto { return de_serializer::deserialize(json); } + template + static Utils::expected_str deserializeExp(const QByteArray &json) + { + try { + return T::deserialize(json); + } catch (const Dto::invalid_dto_exception &e) { + return Utils::make_unexpected(QString::fromUtf8(e.what())); + } + } + + template> + static std::optional strToOptionalEn(QAnyStringView str) + { + try { + return M::strToEnum(str); + } catch (const std::range_error &) { + return std::nullopt; + } + } + // throws Axivion::Internal::Dto::invalid_dto_exception template static T deserialize_bytes(const QByteArray &json) @@ -549,6 +569,10 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str Any::deserializeExpected(const QByteArray &json) { + return deserializeExp(json); + } + Any::Any() {} Any::Any(QString value) : data(std::move(value)) {} @@ -691,6 +715,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str AnalyzedFileDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + AnalyzedFileDto::AnalyzedFileDto( QString path, std::optional isSystemHeader, @@ -740,6 +769,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown ApiTokenType str: ", to_std_string(str) })); } + std::optional ApiTokenTypeMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String ApiTokenTypeMeta::enumToStr(ApiTokenType e) { switch (e) @@ -797,6 +831,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ChangePasswordFormDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ChangePasswordFormDto::ChangePasswordFormDto( QString currentPassword, QString newPassword @@ -859,6 +898,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown ColumnType str: ", to_std_string(str) })); } + std::optional ColumnTypeMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String ColumnTypeMeta::enumToStr(ColumnType e) { switch (e) @@ -925,6 +969,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ColumnTypeOptionDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ColumnTypeOptionDto::ColumnTypeOptionDto( QString key, std::optional displayName, @@ -972,6 +1021,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str CommentRequestDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + CommentRequestDto::CommentRequestDto( QString text ) : @@ -1015,6 +1069,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str CsrfTokenDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + CsrfTokenDto::CsrfTokenDto( QString csrfToken ) : @@ -1070,6 +1129,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str EntityDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + EntityDto::EntityDto( QString id, QString name, @@ -1145,6 +1209,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ErrorDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ErrorDto::ErrorDto( std::optional dashboardVersionNumber, QString type, @@ -1222,6 +1291,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueCommentDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueCommentDto::IssueCommentDto( QString username, QString userDisplayName, @@ -1284,6 +1358,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown IssueKind str: ", to_std_string(str) })); } + std::optional IssueKindMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String IssueKindMeta::enumToStr(IssueKind e) { switch (e) @@ -1352,6 +1431,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown IssueKindForNamedFilterCreation str: ", to_std_string(str) })); } + std::optional IssueKindForNamedFilterCreationMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String IssueKindForNamedFilterCreationMeta::enumToStr(IssueKindForNamedFilterCreation e) { switch (e) @@ -1428,6 +1512,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueSourceLocationDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueSourceLocationDto::IssueSourceLocationDto( QString fileName, std::optional role, @@ -1486,6 +1575,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueTagDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueTagDto::IssueTagDto( QString tag, QString color @@ -1546,6 +1640,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueTagTypeDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueTagTypeDto::IssueTagTypeDto( QString id, std::optional text, @@ -1601,6 +1700,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown MessageSeverity str: ", to_std_string(str) })); } + std::optional MessageSeverityMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String MessageSeverityMeta::enumToStr(MessageSeverity e) { switch (e) @@ -1664,6 +1768,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str MetricDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + MetricDto::MetricDto( QString name, QString displayName, @@ -1731,6 +1840,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str MetricValueTableRowDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + MetricValueTableRowDto::MetricValueTableRowDto( QString metric, std::optional path, @@ -1778,6 +1892,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown NamedFilterType str: ", to_std_string(str) })); } + std::optional NamedFilterTypeMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String NamedFilterTypeMeta::enumToStr(NamedFilterType e) { switch (e) @@ -1828,6 +1947,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str NamedFilterVisibilityDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + NamedFilterVisibilityDto::NamedFilterVisibilityDto( std::optional> groups ) : @@ -1874,6 +1998,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ProjectReferenceDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ProjectReferenceDto::ProjectReferenceDto( QString name, QString url @@ -1925,6 +2054,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str RuleDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + RuleDto::RuleDto( QString name, QString original_name, @@ -1959,6 +2093,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown SortDirection str: ", to_std_string(str) })); } + std::optional SortDirectionMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String SortDirectionMeta::enumToStr(SortDirection e) { switch (e) @@ -1999,6 +2138,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown TableCellAlignment str: ", to_std_string(str) })); } + std::optional TableCellAlignmentMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String TableCellAlignmentMeta::enumToStr(TableCellAlignment e) { switch (e) @@ -2055,6 +2199,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ToolsVersionDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ToolsVersionDto::ToolsVersionDto( QString name, QString number, @@ -2094,6 +2243,11 @@ namespace Axivion::Internal::Dto { throw std::range_error(concat({ "Unknown UserRefType str: ", to_std_string(str) })); } + std::optional UserRefTypeMeta::strToOptionalEnum(QAnyStringView str) + { + return strToOptionalEn(str); + } + QLatin1String UserRefTypeMeta::enumToStr(UserRefType e) { switch (e) @@ -2150,6 +2304,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str VersionKindCountDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + VersionKindCountDto::VersionKindCountDto( qint32 Total, qint32 Added, @@ -2221,6 +2380,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str AnalysisVersionDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + AnalysisVersionDto::AnalysisVersionDto( QString date, std::optional label, @@ -2289,6 +2453,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ApiTokenCreationRequestDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ApiTokenCreationRequestDto::ApiTokenCreationRequestDto( QString password, QString type, @@ -2406,6 +2575,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ApiTokenInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ApiTokenInfoDto::ApiTokenInfoDto( QString id, QString url, @@ -2550,6 +2724,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ColumnInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ColumnInfoDto::ColumnInfoDto( QString key, std::optional header, @@ -2717,6 +2896,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str DashboardInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + DashboardInfoDto::DashboardInfoDto( std::optional mainUrl, QString dashboardVersion, @@ -2788,6 +2972,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueCommentListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueCommentListDto::IssueCommentListDto( std::vector comments ) : @@ -2837,6 +3026,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueKindInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueKindInfoDto::IssueKindInfoDto( QString prefix, QString niceSingularName, @@ -2914,6 +3108,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueTagTypeListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueTagTypeListDto::IssueTagTypeListDto( std::vector tags ) : @@ -2981,6 +3180,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str LineMarkerDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + LineMarkerDto::LineMarkerDto( QString kind, std::optional id, @@ -3085,6 +3289,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str RepositoryUpdateMessageDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + RepositoryUpdateMessageDto::RepositoryUpdateMessageDto( QString severity, QString message @@ -3158,6 +3367,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str RuleListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + RuleListDto::RuleListDto( std::vector rules ) : @@ -3204,6 +3418,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str SortInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + SortInfoDto::SortInfoDto( QString key, QString direction @@ -3286,6 +3505,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str UserRefDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + UserRefDto::UserRefDto( QString name, QString displayName, @@ -3372,6 +3596,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str AnalyzedFileListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + AnalyzedFileListDto::AnalyzedFileListDto( AnalysisVersionDto version, std::vector rows @@ -3420,6 +3649,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str EntityListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + EntityListDto::EntityListDto( std::optional version, std::vector entities @@ -3474,6 +3708,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str FileViewDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + FileViewDto::FileViewDto( QString fileName, std::optional version, @@ -3541,6 +3780,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueDto::IssueDto( QString kind, qint64 id, @@ -3655,6 +3899,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueTableDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueTableDto::IssueTableDto( std::optional startVersion, AnalysisVersionDto endVersion, @@ -3715,6 +3964,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str MetricListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + MetricListDto::MetricListDto( std::optional version, std::vector metrics @@ -3772,6 +4026,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str MetricValueRangeDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + MetricValueRangeDto::MetricValueRangeDto( AnalysisVersionDto startVersion, AnalysisVersionDto endVersion, @@ -3826,6 +4085,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str MetricValueTableDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + MetricValueTableDto::MetricValueTableDto( std::vector columns, std::vector rows @@ -3883,6 +4147,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str NamedFilterCreateDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + NamedFilterCreateDto::NamedFilterCreateDto( QString displayName, QString kind, @@ -3998,6 +4267,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str NamedFilterInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + NamedFilterInfoDto::NamedFilterInfoDto( QString key, QString displayName, @@ -4118,6 +4392,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str NamedFilterUpdateDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + NamedFilterUpdateDto::NamedFilterUpdateDto( std::optional name, std::optional> filters, @@ -4185,6 +4464,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str ProjectInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + ProjectInfoDto::ProjectInfoDto( QString name, std::optional issueFilterHelp, @@ -4246,6 +4530,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str RepositoryUpdateResponseDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + RepositoryUpdateResponseDto::RepositoryUpdateResponseDto( std::vector messages, bool hasErrors, @@ -4308,6 +4597,11 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str TableInfoDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + TableInfoDto::TableInfoDto( QString tableDataUri, std::optional issueBaseViewUri, diff --git a/src/plugins/axivion/dashboard/dto.h b/src/plugins/axivion/dashboard/dto.h index fea5ab4ee18..8617d2d61df 100644 --- a/src/plugins/axivion/dashboard/dto.h +++ b/src/plugins/axivion/dashboard/dto.h @@ -14,6 +14,8 @@ * /projects/libs/dashboard_cpp_api/generator/generate_dashboard_cpp_api.py */ +#include + #include #include #include @@ -70,6 +72,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static Any deserialize(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); + Any(); Any(QString value); @@ -171,6 +175,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static AnalyzedFileDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + AnalyzedFileDto( QString path, std::optional isSystemHeader, @@ -219,6 +225,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static ApiTokenType strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(ApiTokenType e); ApiTokenTypeMeta() = delete; @@ -245,6 +253,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ChangePasswordFormDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ChangePasswordFormDto( QString currentPassword, QString newPassword @@ -295,6 +305,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static ColumnType strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(ColumnType e); ColumnTypeMeta() = delete; @@ -332,6 +344,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ColumnTypeOptionDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ColumnTypeOptionDto( QString key, std::optional displayName, @@ -356,6 +370,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static CommentRequestDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + CommentRequestDto( QString text ); @@ -384,6 +400,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static CsrfTokenDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + CsrfTokenDto( QString csrfToken ); @@ -427,6 +445,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static EntityDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + EntityDto( QString id, QString name, @@ -544,6 +564,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ErrorDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ErrorDto( std::optional dashboardVersionNumber, QString type, @@ -613,6 +635,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueCommentDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueCommentDto( QString username, QString userDisplayName, @@ -654,6 +678,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static IssueKind strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(IssueKind e); IssueKindMeta() = delete; @@ -690,6 +716,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static IssueKindForNamedFilterCreation strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(IssueKindForNamedFilterCreation e); IssueKindForNamedFilterCreationMeta() = delete; @@ -759,6 +787,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueSourceLocationDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueSourceLocationDto( QString fileName, std::optional role, @@ -795,6 +825,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTagDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueTagDto( QString tag, QString color @@ -851,6 +883,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTagTypeDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueTagTypeDto( QString id, std::optional text, @@ -893,6 +927,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static MessageSeverity strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(MessageSeverity e); MessageSeverityMeta() = delete; @@ -937,6 +973,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + MetricDto( QString name, QString displayName, @@ -996,6 +1034,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricValueTableRowDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + MetricValueTableRowDto( QString metric, std::optional path, @@ -1033,6 +1073,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static NamedFilterType strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(NamedFilterType e); NamedFilterTypeMeta() = delete; @@ -1062,6 +1104,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterVisibilityDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + NamedFilterVisibilityDto( std::optional> groups ); @@ -1089,6 +1133,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ProjectReferenceDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ProjectReferenceDto( QString name, QString url @@ -1127,6 +1173,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RuleDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + RuleDto( QString name, QString original_name, @@ -1157,6 +1205,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static SortDirection strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(SortDirection e); SortDirectionMeta() = delete; @@ -1187,6 +1237,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static TableCellAlignment strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(TableCellAlignment e); TableCellAlignmentMeta() = delete; @@ -1218,6 +1270,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ToolsVersionDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ToolsVersionDto( QString name, QString number, @@ -1253,6 +1307,8 @@ namespace Axivion::Internal::Dto // Throws std::range_error static UserRefType strToEnum(QAnyStringView str); + static std::optional strToOptionalEnum(QAnyStringView str); + static QLatin1String enumToStr(UserRefType e); UserRefTypeMeta() = delete; @@ -1284,6 +1340,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static VersionKindCountDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + VersionKindCountDto( qint32 Total, qint32 Added, @@ -1392,6 +1450,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static AnalysisVersionDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + AnalysisVersionDto( QString date, std::optional label, @@ -1450,6 +1510,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ApiTokenCreationRequestDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ApiTokenCreationRequestDto( QString password, QString type, @@ -1568,6 +1630,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ApiTokenInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ApiTokenInfoDto( QString id, QString url, @@ -1693,6 +1757,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ColumnInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ColumnInfoDto( QString key, std::optional header, @@ -1726,7 +1792,7 @@ namespace Axivion::Internal::Dto void setAlignmentEnum(TableCellAlignment newValue); - // Throws std::range_error + // Throws std::range_error ColumnType getTypeEnum() const; std::optional getOptionalTypeEnum() const; @@ -1859,6 +1925,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static DashboardInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + DashboardInfoDto( std::optional mainUrl, QString dashboardVersion, @@ -1895,6 +1963,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueCommentListDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueCommentListDto( std::vector comments ); @@ -1929,6 +1999,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueKindInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueKindInfoDto( QString prefix, QString niceSingularName, @@ -1966,6 +2038,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTagTypeListDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueTagTypeListDto( std::vector tags ); @@ -2056,6 +2130,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static LineMarkerDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + LineMarkerDto( QString kind, std::optional id, @@ -2115,6 +2191,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RepositoryUpdateMessageDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + RepositoryUpdateMessageDto( QString severity, QString message @@ -2150,6 +2228,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RuleListDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + RuleListDto( std::vector rules ); @@ -2179,6 +2259,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static SortInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + SortInfoDto( QString key, QString direction @@ -2235,6 +2317,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static UserRefDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + UserRefDto( QString name, QString displayName, @@ -2279,6 +2363,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static AnalyzedFileListDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + AnalyzedFileListDto( AnalysisVersionDto version, std::vector rows @@ -2307,6 +2393,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static EntityListDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + EntityListDto( std::optional version, std::vector entities @@ -2355,6 +2443,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static FileViewDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + FileViewDto( QString fileName, std::optional version, @@ -2418,6 +2508,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueDto( QString kind, qint64 id, @@ -2532,6 +2624,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTableDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + IssueTableDto( std::optional startVersion, AnalysisVersionDto endVersion, @@ -2566,6 +2660,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricListDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + MetricListDto( std::optional version, std::vector metrics @@ -2617,6 +2713,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricValueRangeDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + MetricValueRangeDto( AnalysisVersionDto startVersion, AnalysisVersionDto endVersion, @@ -2650,6 +2748,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricValueTableDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + MetricValueTableDto( std::vector columns, std::vector rows @@ -2712,6 +2812,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterCreateDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + NamedFilterCreateDto( QString displayName, QString kind, @@ -2834,6 +2936,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + NamedFilterInfoDto( QString key, QString displayName, @@ -2924,6 +3028,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterUpdateDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + NamedFilterUpdateDto( std::optional name, std::optional> filters, @@ -2983,6 +3089,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ProjectInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + ProjectInfoDto( QString name, std::optional issueFilterHelp, @@ -3024,6 +3132,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RepositoryUpdateResponseDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + RepositoryUpdateResponseDto( std::vector messages, bool hasErrors, @@ -3091,6 +3201,8 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static TableInfoDto deserialize(const QByteArray &json); + Utils::expected_str deserializeExpected(const QByteArray &json); + TableInfoDto( QString tableDataUri, std::optional issueBaseViewUri, From c76fc8e1e53ce45261b8a64edd663efd06ca487b Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 19 Feb 2024 09:50:25 +0100 Subject: [PATCH 038/128] Debugger: fix dumping char arrays The calculation for the size of the memory to fetch for the dumper was missing the char size information. Amends a26aff7afd17b1f7ddda917ad07e1c1a9292ec9d Change-Id: I8ceb127efaf7effa94fc53e6782f75580ab8cdb4 Reviewed-by: Reviewed-by: hjk --- share/qtcreator/debugger/dumper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index ad25ad2dbeb..ca6b7f40737 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -635,7 +635,7 @@ class DumperBase(): def putCharArrayValue(self, data, length, charSize, displayFormat=DisplayFormat.Automatic): shown = self.computeLimit(length, self.displayStringLimit) - mem = self.readMemory(data, shown) + mem = self.readMemory(data, shown * charSize) if charSize == 1: if displayFormat in (DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String): encodingType = 'latin1' From 5c8b87bf9de775ede865b90496270d707e5af8ed Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 20 Feb 2024 21:29:18 +0100 Subject: [PATCH 039/128] TaskTree: Simplify some test code Change-Id: Id81969d00a6e183e0d7cdcfddc64fc65aab6e020 Reviewed-by: hjk --- tests/auto/solutions/tasking/tst_tasking.cpp | 205 ++++++++----------- 1 file changed, 84 insertions(+), 121 deletions(-) diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index e95635b6dc8..f9d9cef5071 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -990,148 +990,111 @@ void tst_Tasking::testTree_data() } { - const auto createRoot = [storage, groupDone](WorkflowPolicy policy) { - return Group { + const auto testData = [storage, groupDone](WorkflowPolicy policy, DoneWith result) { + return TestData { storage, - workflowPolicy(policy), - groupDone(0) + Group { + storage, + workflowPolicy(policy), + groupDone(0) + }, + Log {{0, result == DoneWith::Success ? Handler::GroupSuccess : Handler::GroupError}}, + 0, + result }; }; const Log doneLog = {{0, Handler::GroupSuccess}}; const Log errorLog = {{0, Handler::GroupError}}; - const Group root1 = createRoot(WorkflowPolicy::StopOnError); - QTest::newRow("EmptyStopOnError") << TestData{storage, root1, doneLog, 0, - DoneWith::Success}; - - const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); - QTest::newRow("EmptyContinueOnError") << TestData{storage, root2, doneLog, 0, - DoneWith::Success}; - - const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); - QTest::newRow("EmptyStopOnSuccess") << TestData{storage, root3, errorLog, 0, - DoneWith::Error}; - - const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); - QTest::newRow("EmptyContinueOnSuccess") << TestData{storage, root4, errorLog, 0, - DoneWith::Error}; - - const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); - QTest::newRow("EmptyStopOnSuccessOrError") << TestData{storage, root5, errorLog, 0, - DoneWith::Error}; - - const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); - QTest::newRow("EmptyFinishAllAndSuccess") << TestData{storage, root6, doneLog, 0, - DoneWith::Success}; - - const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); - QTest::newRow("EmptyFinishAllAndError") << TestData{storage, root7, errorLog, 0, - DoneWith::Error}; + QTest::newRow("EmptyStopOnError") + << testData(WorkflowPolicy::StopOnError, DoneWith::Success); + QTest::newRow("EmptyContinueOnError") + << testData(WorkflowPolicy::ContinueOnError, DoneWith::Success); + QTest::newRow("EmptyStopOnSuccess") + << testData(WorkflowPolicy::StopOnSuccess, DoneWith::Error); + QTest::newRow("EmptyContinueOnSuccess") + << testData(WorkflowPolicy::ContinueOnSuccess, DoneWith::Error); + QTest::newRow("EmptyStopOnSuccessOrError") + << testData(WorkflowPolicy::StopOnSuccessOrError, DoneWith::Error); + QTest::newRow("EmptyFinishAllAndSuccess") + << testData(WorkflowPolicy::FinishAllAndSuccess, DoneWith::Success); + QTest::newRow("EmptyFinishAllAndError") + << testData(WorkflowPolicy::FinishAllAndError, DoneWith::Error); } { - const auto createRoot = [storage, createSuccessTask, groupDone]( - WorkflowPolicy policy) { - return Group { + const auto testData = [storage, groupDone, createSuccessTask](WorkflowPolicy policy, + DoneWith result) { + return TestData { storage, - workflowPolicy(policy), - createSuccessTask(1), - groupDone(0) + Group { + storage, + workflowPolicy(policy), + createSuccessTask(1), + groupDone(0) + }, + Log { + {1, Handler::Setup}, + {1, Handler::Success}, + {0, result == DoneWith::Success ? Handler::GroupSuccess : Handler::GroupError} + }, + 1, + result }; }; - const Log doneLog = { - {1, Handler::Setup}, - {1, Handler::Success}, - {0, Handler::GroupSuccess} - }; - - const Log errorLog = { - {1, Handler::Setup}, - {1, Handler::Success}, - {0, Handler::GroupError} - }; - - const Group root1 = createRoot(WorkflowPolicy::StopOnError); - QTest::newRow("DoneStopOnError") << TestData{storage, root1, doneLog, 1, - DoneWith::Success}; - - const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); - QTest::newRow("DoneContinueOnError") << TestData{storage, root2, doneLog, 1, - DoneWith::Success}; - - const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); - QTest::newRow("DoneStopOnSuccess") << TestData{storage, root3, doneLog, 1, - DoneWith::Success}; - - const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); - QTest::newRow("DoneContinueOnSuccess") << TestData{storage, root4, doneLog, 1, - DoneWith::Success}; - - const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); - QTest::newRow("DoneStopOnSuccessOrError") << TestData{storage, root5, doneLog, 1, - DoneWith::Success}; - - const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); - QTest::newRow("DoneFinishAllAndSuccess") << TestData{storage, root6, doneLog, 1, - DoneWith::Success}; - - const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); - QTest::newRow("DoneFinishAllAndError") << TestData{storage, root7, errorLog, 1, - DoneWith::Error}; + QTest::newRow("DoneStopOnError") + << testData(WorkflowPolicy::StopOnError, DoneWith::Success); + QTest::newRow("DoneContinueOnError") + << testData(WorkflowPolicy::ContinueOnError, DoneWith::Success); + QTest::newRow("DoneStopOnSuccess") + << testData(WorkflowPolicy::StopOnSuccess, DoneWith::Success); + QTest::newRow("DoneContinueOnSuccess") + << testData(WorkflowPolicy::ContinueOnSuccess, DoneWith::Success); + QTest::newRow("DoneStopOnSuccessOrError") + << testData(WorkflowPolicy::StopOnSuccessOrError, DoneWith::Success); + QTest::newRow("DoneFinishAllAndSuccess") + << testData(WorkflowPolicy::FinishAllAndSuccess, DoneWith::Success); + QTest::newRow("DoneFinishAllAndError") + << testData(WorkflowPolicy::FinishAllAndError, DoneWith::Error); } { - const auto createRoot = [storage, createFailingTask, groupDone]( - WorkflowPolicy policy) { - return Group { + const auto testData = [storage, groupDone, createFailingTask](WorkflowPolicy policy, + DoneWith result) { + return TestData { storage, - workflowPolicy(policy), - createFailingTask(1), - groupDone(0) + Group { + storage, + workflowPolicy(policy), + createFailingTask(1), + groupDone(0) + }, + Log { + {1, Handler::Setup}, + {1, Handler::Error}, + {0, result == DoneWith::Success ? Handler::GroupSuccess : Handler::GroupError} + }, + 1, + result }; }; - const Log doneLog = { - {1, Handler::Setup}, - {1, Handler::Error}, - {0, Handler::GroupSuccess} - }; - - const Log errorLog = { - {1, Handler::Setup}, - {1, Handler::Error}, - {0, Handler::GroupError} - }; - - const Group root1 = createRoot(WorkflowPolicy::StopOnError); - QTest::newRow("ErrorStopOnError") << TestData{storage, root1, errorLog, 1, - DoneWith::Error}; - - const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); - QTest::newRow("ErrorContinueOnError") << TestData{storage, root2, errorLog, 1, - DoneWith::Error}; - - const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); - QTest::newRow("ErrorStopOnSuccess") << TestData{storage, root3, errorLog, 1, - DoneWith::Error}; - - const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); - QTest::newRow("ErrorContinueOnSuccess") << TestData{storage, root4, errorLog, 1, - DoneWith::Error}; - - const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); - QTest::newRow("ErrorStopOnSuccessOrError") << TestData{storage, root5, errorLog, 1, - DoneWith::Error}; - - const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); - QTest::newRow("ErrorFinishAllAndSuccess") << TestData{storage, root6, doneLog, 1, - DoneWith::Success}; - - const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); - QTest::newRow("ErrorFinishAllAndError") << TestData{storage, root7, errorLog, 1, - DoneWith::Error}; + QTest::newRow("ErrorStopOnError") + << testData(WorkflowPolicy::StopOnError, DoneWith::Error); + QTest::newRow("ErrorContinueOnError") + << testData(WorkflowPolicy::ContinueOnError, DoneWith::Error); + QTest::newRow("ErrorStopOnSuccess") + << testData(WorkflowPolicy::StopOnSuccess, DoneWith::Error); + QTest::newRow("ErrorContinueOnSuccess") + << testData(WorkflowPolicy::ContinueOnSuccess, DoneWith::Error); + QTest::newRow("ErrorStopOnSuccessOrError") + << testData(WorkflowPolicy::StopOnSuccessOrError, DoneWith::Error); + QTest::newRow("ErrorFinishAllAndSuccess") + << testData(WorkflowPolicy::FinishAllAndSuccess, DoneWith::Success); + QTest::newRow("ErrorFinishAllAndError") + << testData(WorkflowPolicy::FinishAllAndError, DoneWith::Error); } { From d4b5a1efdaa5ec9eee0256cde573d9703f915f88 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 20 Feb 2024 21:53:49 +0100 Subject: [PATCH 040/128] TaskTree: Check the right asyncCount() in tests This is quite crucial test checking the right internal behavior. The expected asyncCount numbers were added to these tests after processing each recipe by hand and deducing the expected scenario and the number of times each recipe should return the control to the main event loop before the task tree finished. Be very careful when correcting any of these numbers - this might mean the internal change inside the task tree forcing the asyncCount adjustment may break the task tree architecture. Change-Id: Ia3acfbf3cb232ed95df97fe5822305df98e2271a Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: hjk --- tests/auto/solutions/tasking/tst_tasking.cpp | 298 ++++++++++--------- 1 file changed, 154 insertions(+), 144 deletions(-) diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index f9d9cef5071..b2e99a7c220 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -84,6 +84,7 @@ struct TestData Log expectedLog; int taskCount = 0; DoneWith result = DoneWith::Success; + std::optional asyncCount = {}; std::optional messageLog = {}; }; @@ -472,7 +473,7 @@ static TestData storageShadowingData() {1, Handler::Storage}, }; - return {storage, root, log, 0, DoneWith::Success}; + return {storage, root, log, 0, DoneWith::Success, 0}; } static TestData parallelData() @@ -653,10 +654,10 @@ void tst_Tasking::testTree_data() const Log logDone {{0, Handler::GroupSuccess}}; const Log logError {{0, Handler::GroupError}}; - QTest::newRow("Empty") << TestData{storage, root1, logDone, 0, DoneWith::Success}; - QTest::newRow("EmptyContinue") << TestData{storage, root2, logDone, 0, DoneWith::Success}; - QTest::newRow("EmptyDone") << TestData{storage, root3, logDone, 0, DoneWith::Success}; - QTest::newRow("EmptyError") << TestData{storage, root4, logError, 0, DoneWith::Error}; + QTest::newRow("Empty") << TestData{storage, root1, logDone, 0, DoneWith::Success, 0}; + QTest::newRow("EmptyContinue") << TestData{storage, root2, logDone, 0, DoneWith::Success, 0}; + QTest::newRow("EmptyDone") << TestData{storage, root3, logDone, 0, DoneWith::Success, 0}; + QTest::newRow("EmptyError") << TestData{storage, root4, logError, 0, DoneWith::Error, 0}; } { @@ -671,11 +672,11 @@ void tst_Tasking::testTree_data() const auto doneData = [storage, setupGroup](WorkflowPolicy policy) { return TestData{storage, setupGroup(SetupResult::StopWithSuccess, policy), - Log{{0, Handler::GroupSuccess}}, 0, DoneWith::Success}; + Log{{0, Handler::GroupSuccess}}, 0, DoneWith::Success, 0}; }; const auto errorData = [storage, setupGroup](WorkflowPolicy policy) { return TestData{storage, setupGroup(SetupResult::StopWithError, policy), - Log{{0, Handler::GroupError}}, 0, DoneWith::Error}; + Log{{0, Handler::GroupError}}, 0, DoneWith::Error, 0}; }; QTest::newRow("DoneAndStopOnError") << doneData(WorkflowPolicy::StopOnError); @@ -707,7 +708,7 @@ void tst_Tasking::testTree_data() {2, Handler::Setup}, {2, Handler::TweakSetupToSuccess} }; - QTest::newRow("TweakTaskSuccess") << TestData{storage, root, log, 2, DoneWith::Success}; + QTest::newRow("TweakTaskSuccess") << TestData{storage, root, log, 2, DoneWith::Success, 0}; } { @@ -720,7 +721,7 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {1, Handler::TweakSetupToError} }; - QTest::newRow("TweakTaskError") << TestData{storage, root, log, 2, DoneWith::Error}; + QTest::newRow("TweakTaskError") << TestData{storage, root, log, 2, DoneWith::Error, 0}; } { @@ -741,7 +742,7 @@ void tst_Tasking::testTree_data() {3, Handler::Setup}, {3, Handler::TweakSetupToError} }; - QTest::newRow("TweakMixed") << TestData{storage, root, log, 4, DoneWith::Error}; + QTest::newRow("TweakMixed") << TestData{storage, root, log, 4, DoneWith::Error, 2}; } { @@ -763,7 +764,7 @@ void tst_Tasking::testTree_data() {1, Handler::Canceled}, {2, Handler::Canceled} }; - QTest::newRow("TweakParallel") << TestData{storage, root, log, 4, DoneWith::Error}; + QTest::newRow("TweakParallel") << TestData{storage, root, log, 4, DoneWith::Error, 0}; } { @@ -787,7 +788,7 @@ void tst_Tasking::testTree_data() {1, Handler::Canceled}, {2, Handler::Canceled} }; - QTest::newRow("TweakParallelGroup") << TestData{storage, root, log, 4, DoneWith::Error}; + QTest::newRow("TweakParallelGroup") << TestData{storage, root, log, 4, DoneWith::Error, 0}; } { @@ -813,7 +814,7 @@ void tst_Tasking::testTree_data() {2, Handler::Canceled} }; QTest::newRow("TweakParallelGroupSetup") - << TestData{storage, root, log, 4, DoneWith::Error}; + << TestData{storage, root, log, 4, DoneWith::Error, 0}; } { @@ -857,7 +858,7 @@ void tst_Tasking::testTree_data() {1, Handler::GroupSuccess}, {0, Handler::GroupSuccess} }; - QTest::newRow("Nested") << TestData{storage, root, log, 1, DoneWith::Success}; + QTest::newRow("Nested") << TestData{storage, root, log, 1, DoneWith::Success, 1}; } QTest::newRow("Parallel") << parallelData(); @@ -915,10 +916,10 @@ void tst_Tasking::testTree_data() {5, Handler::Success}, {0, Handler::GroupSuccess} }; - QTest::newRow("Sequential") << TestData{storage, root1, log, 5, DoneWith::Success}; - QTest::newRow("SequentialEncapsulated") << TestData{storage, root2, log, 5, DoneWith::Success}; + QTest::newRow("Sequential") << TestData{storage, root1, log, 5, DoneWith::Success, 5}; + QTest::newRow("SequentialEncapsulated") << TestData{storage, root2, log, 5, DoneWith::Success, 5}; // We don't inspect subtrees, so taskCount is 3, not 5. - QTest::newRow("SequentialSubTree") << TestData{storage, root3, log, 3, DoneWith::Success}; + QTest::newRow("SequentialSubTree") << TestData{storage, root3, log, 3, DoneWith::Success, 3}; } { @@ -964,7 +965,7 @@ void tst_Tasking::testTree_data() {1, Handler::GroupSuccess}, {0, Handler::GroupSuccess} }; - QTest::newRow("SequentialNested") << TestData{storage, root, log, 5, DoneWith::Success}; + QTest::newRow("SequentialNested") << TestData{storage, root, log, 5, DoneWith::Success, 5}; } { @@ -986,7 +987,7 @@ void tst_Tasking::testTree_data() {3, Handler::Error}, {0, Handler::GroupError} }; - QTest::newRow("SequentialError") << TestData{storage, root, log, 5, DoneWith::Error}; + QTest::newRow("SequentialError") << TestData{storage, root, log, 5, DoneWith::Error, 3}; } { @@ -1000,7 +1001,8 @@ void tst_Tasking::testTree_data() }, Log {{0, result == DoneWith::Success ? Handler::GroupSuccess : Handler::GroupError}}, 0, - result + result, + 0 }; }; @@ -1040,7 +1042,8 @@ void tst_Tasking::testTree_data() {0, result == DoneWith::Success ? Handler::GroupSuccess : Handler::GroupError} }, 1, - result + result, + 1 }; }; @@ -1077,7 +1080,8 @@ void tst_Tasking::testTree_data() {0, result == DoneWith::Success ? Handler::GroupSuccess : Handler::GroupError} }, 1, - result + result, + 1 }; }; @@ -1139,31 +1143,31 @@ void tst_Tasking::testTree_data() const Group root1 = createRoot(WorkflowPolicy::StopOnError); QTest::newRow("StopRootWithStopOnError") - << TestData{storage, root1, errorErrorLog, 2, DoneWith::Error}; + << TestData{storage, root1, errorErrorLog, 2, DoneWith::Error, 1}; const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); QTest::newRow("StopRootWithContinueOnError") - << TestData{storage, root2, errorDoneLog, 2, DoneWith::Error}; + << TestData{storage, root2, errorDoneLog, 2, DoneWith::Error, 2}; const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); QTest::newRow("StopRootWithStopOnSuccess") - << TestData{storage, root3, doneLog, 2, DoneWith::Success}; + << TestData{storage, root3, doneLog, 2, DoneWith::Success, 2}; const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); QTest::newRow("StopRootWithContinueOnSuccess") - << TestData{storage, root4, doneLog, 2, DoneWith::Success}; + << TestData{storage, root4, doneLog, 2, DoneWith::Success, 2}; const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); QTest::newRow("StopRootWithStopOnSuccessOrError") - << TestData{storage, root5, errorErrorLog, 2, DoneWith::Error}; + << TestData{storage, root5, errorErrorLog, 2, DoneWith::Error, 1}; const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); QTest::newRow("StopRootWithFinishAllAndSuccess") - << TestData{storage, root6, doneLog, 2, DoneWith::Success}; + << TestData{storage, root6, doneLog, 2, DoneWith::Success, 2}; const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); QTest::newRow("StopRootWithFinishAllAndError") - << TestData{storage, root7, errorDoneLog, 2, DoneWith::Error}; + << TestData{storage, root7, errorDoneLog, 2, DoneWith::Error, 2}; } { @@ -1226,31 +1230,31 @@ void tst_Tasking::testTree_data() const Group root1 = createRoot(WorkflowPolicy::StopOnError); QTest::newRow("StopRootAfterDoneWithStopOnError") - << TestData{storage, root1, errorErrorLog, 3, DoneWith::Error}; + << TestData{storage, root1, errorErrorLog, 3, DoneWith::Error, 2}; const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); QTest::newRow("StopRootAfterDoneWithContinueOnError") - << TestData{storage, root2, errorDoneLog, 3, DoneWith::Error}; + << TestData{storage, root2, errorDoneLog, 3, DoneWith::Error, 3}; const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); QTest::newRow("StopRootAfterDoneWithStopOnSuccess") - << TestData{storage, root3, doneErrorLog, 3, DoneWith::Success}; + << TestData{storage, root3, doneErrorLog, 3, DoneWith::Success, 1}; const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); QTest::newRow("StopRootAfterDoneWithContinueOnSuccess") - << TestData{storage, root4, doneDoneLog, 3, DoneWith::Success}; + << TestData{storage, root4, doneDoneLog, 3, DoneWith::Success, 3}; const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); QTest::newRow("StopRootAfterDoneWithStopOnSuccessOrError") - << TestData{storage, root5, doneErrorLog, 3, DoneWith::Success}; + << TestData{storage, root5, doneErrorLog, 3, DoneWith::Success, 1}; const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); QTest::newRow("StopRootAfterDoneWithFinishAllAndSuccess") - << TestData{storage, root6, doneDoneLog, 3, DoneWith::Success}; + << TestData{storage, root6, doneDoneLog, 3, DoneWith::Success, 3}; const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); QTest::newRow("StopRootAfterDoneWithFinishAllAndError") - << TestData{storage, root7, errorDoneLog, 3, DoneWith::Error}; + << TestData{storage, root7, errorDoneLog, 3, DoneWith::Error, 3}; } { @@ -1283,33 +1287,33 @@ void tst_Tasking::testTree_data() const Group root1 = createRoot(WorkflowPolicy::StopOnError); QTest::newRow("StopGroupWithStopOnError") - << TestData{storage, root1, log, 2, DoneWith::Error}; + << TestData{storage, root1, log, 2, DoneWith::Error, 1}; const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); QTest::newRow("StopGroupWithContinueOnError") - << TestData{storage, root2, log, 2, DoneWith::Error}; + << TestData{storage, root2, log, 2, DoneWith::Error, 1}; const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); QTest::newRow("StopGroupWithStopOnSuccess") - << TestData{storage, root3, log, 2, DoneWith::Error}; + << TestData{storage, root3, log, 2, DoneWith::Error, 1}; const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); QTest::newRow("StopGroupWithContinueOnSuccess") - << TestData{storage, root4, log, 2, DoneWith::Error}; + << TestData{storage, root4, log, 2, DoneWith::Error, 1}; const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); QTest::newRow("StopGroupWithStopOnSuccessOrError") - << TestData{storage, root5, log, 2, DoneWith::Error}; + << TestData{storage, root5, log, 2, DoneWith::Error, 1}; // TODO: Behavioral change! Fix Docs! // Cancellation always invokes error handler (i.e. DoneWith is Canceled) const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); QTest::newRow("StopGroupWithFinishAllAndSuccess") - << TestData{storage, root6, log, 2, DoneWith::Error}; + << TestData{storage, root6, log, 2, DoneWith::Error, 1}; const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); QTest::newRow("StopGroupWithFinishAllAndError") - << TestData{storage, root7, log, 2, DoneWith::Error}; + << TestData{storage, root7, log, 2, DoneWith::Error, 1}; } { @@ -1354,33 +1358,33 @@ void tst_Tasking::testTree_data() const Group root1 = createRoot(WorkflowPolicy::StopOnError); QTest::newRow("StopGroupAfterDoneWithStopOnError") - << TestData{storage, root1, errorLog, 3, DoneWith::Error}; + << TestData{storage, root1, errorLog, 3, DoneWith::Error, 2}; const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); QTest::newRow("StopGroupAfterDoneWithContinueOnError") - << TestData{storage, root2, errorLog, 3, DoneWith::Error}; + << TestData{storage, root2, errorLog, 3, DoneWith::Error, 2}; const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); QTest::newRow("StopGroupAfterDoneWithStopOnSuccess") - << TestData{storage, root3, doneLog, 3, DoneWith::Error}; + << TestData{storage, root3, doneLog, 3, DoneWith::Error, 2}; // TODO: Behavioral change! const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); QTest::newRow("StopGroupAfterDoneWithContinueOnSuccess") - << TestData{storage, root4, errorLog, 3, DoneWith::Error}; + << TestData{storage, root4, errorLog, 3, DoneWith::Error, 2}; const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); QTest::newRow("StopGroupAfterDoneWithStopOnSuccessOrError") - << TestData{storage, root5, doneLog, 3, DoneWith::Error}; + << TestData{storage, root5, doneLog, 3, DoneWith::Error, 2}; // TODO: Behavioral change! const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); QTest::newRow("StopGroupAfterDoneWithFinishAllAndSuccess") - << TestData{storage, root6, errorLog, 3, DoneWith::Error}; + << TestData{storage, root6, errorLog, 3, DoneWith::Error, 2}; const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); QTest::newRow("StopGroupAfterDoneWithFinishAllAndError") - << TestData{storage, root7, errorLog, 3, DoneWith::Error}; + << TestData{storage, root7, errorLog, 3, DoneWith::Error, 2}; } { @@ -1425,32 +1429,32 @@ void tst_Tasking::testTree_data() const Group root1 = createRoot(WorkflowPolicy::StopOnError); QTest::newRow("StopGroupAfterErrorWithStopOnError") - << TestData{storage, root1, shortLog, 3, DoneWith::Error}; + << TestData{storage, root1, shortLog, 3, DoneWith::Error, 1}; const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); QTest::newRow("StopGroupAfterErrorWithContinueOnError") - << TestData{storage, root2, longLog, 3, DoneWith::Error}; + << TestData{storage, root2, longLog, 3, DoneWith::Error, 2}; const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); QTest::newRow("StopGroupAfterErrorWithStopOnSuccess") - << TestData{storage, root3, longLog, 3, DoneWith::Error}; + << TestData{storage, root3, longLog, 3, DoneWith::Error, 2}; const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); QTest::newRow("StopGroupAfterErrorWithContinueOnSuccess") - << TestData{storage, root4, longLog, 3, DoneWith::Error}; + << TestData{storage, root4, longLog, 3, DoneWith::Error, 2}; const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); QTest::newRow("StopGroupAfterErrorWithStopOnSuccessOrError") - << TestData{storage, root5, shortLog, 3, DoneWith::Error}; + << TestData{storage, root5, shortLog, 3, DoneWith::Error, 1}; // TODO: Behavioral change! const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); QTest::newRow("StopGroupAfterErrorWithFinishAllAndSuccess") - << TestData{storage, root6, longLog, 3, DoneWith::Error}; + << TestData{storage, root6, longLog, 3, DoneWith::Error, 2}; const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); QTest::newRow("StopGroupAfterErrorWithFinishAllAndError") - << TestData{storage, root7, longLog, 3, DoneWith::Error}; + << TestData{storage, root7, longLog, 3, DoneWith::Error, 2}; } { @@ -1474,7 +1478,7 @@ void tst_Tasking::testTree_data() {2, Handler::Error}, {0, Handler::GroupError} }; - QTest::newRow("StopOnError") << TestData{storage, root1, log1, 3, DoneWith::Error}; + QTest::newRow("StopOnError") << TestData{storage, root1, log1, 3, DoneWith::Error, 2}; const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); const Log errorLog { @@ -1486,7 +1490,7 @@ void tst_Tasking::testTree_data() {3, Handler::Success}, {0, Handler::GroupError} }; - QTest::newRow("ContinueOnError") << TestData{storage, root2, errorLog, 3, DoneWith::Error}; + QTest::newRow("ContinueOnError") << TestData{storage, root2, errorLog, 3, DoneWith::Error, 3}; const Group root3 = createRoot(WorkflowPolicy::StopOnSuccess); const Log log3 { @@ -1494,7 +1498,7 @@ void tst_Tasking::testTree_data() {1, Handler::Success}, {0, Handler::GroupSuccess} }; - QTest::newRow("StopOnSuccess") << TestData{storage, root3, log3, 3, DoneWith::Success}; + QTest::newRow("StopOnSuccess") << TestData{storage, root3, log3, 3, DoneWith::Success, 1}; const Group root4 = createRoot(WorkflowPolicy::ContinueOnSuccess); const Log doneLog { @@ -1506,7 +1510,7 @@ void tst_Tasking::testTree_data() {3, Handler::Success}, {0, Handler::GroupSuccess} }; - QTest::newRow("ContinueOnSuccess") << TestData{storage, root4, doneLog, 3, DoneWith::Success}; + QTest::newRow("ContinueOnSuccess") << TestData{storage, root4, doneLog, 3, DoneWith::Success, 3}; const Group root5 = createRoot(WorkflowPolicy::StopOnSuccessOrError); const Log log5 { @@ -1514,13 +1518,13 @@ void tst_Tasking::testTree_data() {1, Handler::Success}, {0, Handler::GroupSuccess} }; - QTest::newRow("StopOnSuccessOrError") << TestData{storage, root5, log5, 3, DoneWith::Success}; + QTest::newRow("StopOnSuccessOrError") << TestData{storage, root5, log5, 3, DoneWith::Success, 1}; const Group root6 = createRoot(WorkflowPolicy::FinishAllAndSuccess); - QTest::newRow("FinishAllAndSuccess") << TestData{storage, root6, doneLog, 3, DoneWith::Success}; + QTest::newRow("FinishAllAndSuccess") << TestData{storage, root6, doneLog, 3, DoneWith::Success, 3}; const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); - QTest::newRow("FinishAllAndError") << TestData{storage, root7, errorLog, 3, DoneWith::Error}; + QTest::newRow("FinishAllAndError") << TestData{storage, root7, errorLog, 3, DoneWith::Error, 3}; } { @@ -1557,13 +1561,13 @@ void tst_Tasking::testTree_data() }; QTest::newRow("StopOnSuccessOrError1") - << TestData{storage, root1, success, 2, DoneWith::Success}; + << TestData{storage, root1, success, 2, DoneWith::Success, 1}; QTest::newRow("StopOnSuccessOrError2") - << TestData{storage, root2, failure, 2, DoneWith::Error}; + << TestData{storage, root2, failure, 2, DoneWith::Error, 1}; QTest::newRow("StopOnSuccessOrError3") - << TestData{storage, root3, success, 2, DoneWith::Success}; + << TestData{storage, root3, success, 2, DoneWith::Success, 1}; QTest::newRow("StopOnSuccessOrError4") - << TestData{storage, root4, failure, 2, DoneWith::Error}; + << TestData{storage, root4, failure, 2, DoneWith::Error, 1}; } { @@ -1587,7 +1591,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupSuccess} }; QTest::newRow("GroupSetupTweakToSuccess") - << TestData{storage, root1, log1, 1, DoneWith::Success}; + << TestData{storage, root1, log1, 1, DoneWith::Success, 0}; const Group root2 = createRoot(SetupResult::StopWithError); const Log log2 { @@ -1596,7 +1600,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupError} }; QTest::newRow("GroupSetupTweakToError") - << TestData{storage, root2, log2, 1, DoneWith::Error}; + << TestData{storage, root2, log2, 1, DoneWith::Error, 0}; const Group root3 = createRoot(SetupResult::Continue); const Log log3 { @@ -1607,7 +1611,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupSuccess} }; QTest::newRow("GroupSetupTweakToContinue") - << TestData{storage, root3, log3, 1, DoneWith::Success}; + << TestData{storage, root3, log3, 1, DoneWith::Success, 1}; } { @@ -1633,7 +1637,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupSuccess} }; QTest::newRow("GroupDoneWithSuccessTweakToSuccess") - << TestData{storage, root1, log1, 1, DoneWith::Success}; + << TestData{storage, root1, log1, 1, DoneWith::Success, 1}; const Group root2 = createRoot(DoneResult::Success, DoneResult::Error); const Log log2 { @@ -1644,7 +1648,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupError} }; QTest::newRow("GroupDoneWithSuccessTweakToError") - << TestData{storage, root2, log2, 1, DoneWith::Error}; + << TestData{storage, root2, log2, 1, DoneWith::Error, 1}; const Group root3 = createRoot(DoneResult::Error, DoneResult::Success); const Log log3 { @@ -1655,7 +1659,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupSuccess} }; QTest::newRow("GroupDoneWithErrorTweakToSuccess") - << TestData{storage, root3, log3, 1, DoneWith::Success}; + << TestData{storage, root3, log3, 1, DoneWith::Success, 1}; const Group root4 = createRoot(DoneResult::Error, DoneResult::Error); const Log log4 { @@ -1666,7 +1670,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupError} }; QTest::newRow("GroupDoneWithErrorTweakToError") - << TestData{storage, root4, log4, 1, DoneWith::Error}; + << TestData{storage, root4, log4, 1, DoneWith::Error, 1}; } { @@ -1692,7 +1696,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupSuccess} }; QTest::newRow("TaskSetupTweakToSuccess") - << TestData{storage, root1, log1, 2, DoneWith::Success}; + << TestData{storage, root1, log1, 2, DoneWith::Success, 1}; const Group root2 = createRoot(SetupResult::StopWithError); const Log log2 { @@ -1701,7 +1705,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupError} }; QTest::newRow("TaskSetupTweakToError") - << TestData{storage, root2, log2, 2, DoneWith::Error}; + << TestData{storage, root2, log2, 2, DoneWith::Error, 0}; const Group root3 = createRoot(SetupResult::Continue); const Log log3 { @@ -1713,7 +1717,7 @@ void tst_Tasking::testTree_data() {0, Handler::GroupSuccess} }; QTest::newRow("TaskSetupTweakToContinue") - << TestData{storage, root3, log3, 2, DoneWith::Success}; + << TestData{storage, root3, log3, 2, DoneWith::Success, 2}; } { @@ -1751,7 +1755,7 @@ void tst_Tasking::testTree_data() {3, Handler::Success}, {4, Handler::Success} }; - QTest::newRow("NestedParallel") << TestData{storage, root, log, 4, DoneWith::Success}; + QTest::newRow("NestedParallel") << TestData{storage, root, log, 4, DoneWith::Success, 4}; } { @@ -1796,7 +1800,7 @@ void tst_Tasking::testTree_data() {4, Handler::Success}, {5, Handler::Success} }; - QTest::newRow("NestedParallelDone") << TestData{storage, root, log, 5, DoneWith::Success}; + QTest::newRow("NestedParallelDone") << TestData{storage, root, log, 5, DoneWith::Success, 4}; } { @@ -1926,11 +1930,11 @@ void tst_Tasking::testTree_data() {5, Handler::Success} }; QTest::newRow("NestedParallelError1") - << TestData{storage, root1, log1, 5, DoneWith::Error}; + << TestData{storage, root1, log1, 5, DoneWith::Error, 1}; QTest::newRow("NestedParallelError2") - << TestData{storage, root2, log2, 5, DoneWith::Error}; + << TestData{storage, root2, log2, 5, DoneWith::Error, 1}; QTest::newRow("NestedParallelError3") - << TestData{storage, root3, log3, 5, DoneWith::Error}; + << TestData{storage, root3, log3, 5, DoneWith::Error, 2}; } { @@ -1980,7 +1984,7 @@ void tst_Tasking::testTree_data() {3, Handler::Success}, {4, Handler::Success} }; - QTest::newRow("DeeplyNestedParallel") << TestData{storage, root, log, 4, DoneWith::Success}; + QTest::newRow("DeeplyNestedParallel") << TestData{storage, root, log, 4, DoneWith::Success, 4}; } { @@ -2026,7 +2030,7 @@ void tst_Tasking::testTree_data() {5, Handler::Success} }; QTest::newRow("DeeplyNestedParallelSuccess") - << TestData{storage, root, log, 5, DoneWith::Success}; + << TestData{storage, root, log, 5, DoneWith::Success, 4}; } { @@ -2066,7 +2070,7 @@ void tst_Tasking::testTree_data() {2, Handler::Canceled} }; QTest::newRow("DeeplyNestedParallelError") - << TestData{storage, root, log, 5, DoneWith::Error}; + << TestData{storage, root, log, 5, DoneWith::Error, 1}; } { @@ -2085,7 +2089,7 @@ void tst_Tasking::testTree_data() {4, Handler::Sync}, {5, Handler::Sync} }; - QTest::newRow("SyncSequential") << TestData{storage, root, log, 0, DoneWith::Success}; + QTest::newRow("SyncSequential") << TestData{storage, root, log, 0, DoneWith::Success, 0}; } { @@ -2109,7 +2113,7 @@ void tst_Tasking::testTree_data() {5, Handler::Sync}, {5, Handler::TweakDoneToSuccess} }; - QTest::newRow("SyncWithReturn") << TestData{storage, root, log, 0, DoneWith::Success}; + QTest::newRow("SyncWithReturn") << TestData{storage, root, log, 0, DoneWith::Success, 0}; } { @@ -2129,7 +2133,7 @@ void tst_Tasking::testTree_data() {4, Handler::Sync}, {5, Handler::Sync} }; - QTest::newRow("SyncParallel") << TestData{storage, root, log, 0, DoneWith::Success}; + QTest::newRow("SyncParallel") << TestData{storage, root, log, 0, DoneWith::Success, 0}; } { @@ -2148,7 +2152,7 @@ void tst_Tasking::testTree_data() {3, Handler::Sync}, {3, Handler::TweakDoneToError} }; - QTest::newRow("SyncError") << TestData{storage, root, log, 0, DoneWith::Error}; + QTest::newRow("SyncError") << TestData{storage, root, log, 0, DoneWith::Error, 0}; } { @@ -2171,7 +2175,7 @@ void tst_Tasking::testTree_data() {5, Handler::Sync}, {0, Handler::GroupSuccess} }; - QTest::newRow("SyncAndAsync") << TestData{storage, root, log, 2, DoneWith::Success}; + QTest::newRow("SyncAndAsync") << TestData{storage, root, log, 2, DoneWith::Success, 2}; } { @@ -2192,7 +2196,7 @@ void tst_Tasking::testTree_data() {3, Handler::TweakDoneToError}, {0, Handler::GroupError} }; - QTest::newRow("SyncAndAsyncError") << TestData{storage, root, log, 2, DoneWith::Error}; + QTest::newRow("SyncAndAsyncError") << TestData{storage, root, log, 2, DoneWith::Error, 1}; } { @@ -2343,15 +2347,15 @@ void tst_Tasking::testTree_data() // Notice the different log order for each scenario. QTest::newRow("BarrierSequential") - << TestData{storage, root1, log1, 4, DoneWith::Success}; + << TestData{storage, root1, log1, 4, DoneWith::Success, 3}; QTest::newRow("BarrierParallelAdvanceFirst") - << TestData{storage, root2, log2, 4, DoneWith::Success}; + << TestData{storage, root2, log2, 4, DoneWith::Success, 4}; QTest::newRow("BarrierParallelWaitForFirst") - << TestData{storage, root3, log3, 4, DoneWith::Success}; + << TestData{storage, root3, log3, 4, DoneWith::Success, 4}; QTest::newRow("BarrierParallelMultiWaitFor") - << TestData{storage, root4, log4, 5, DoneWith::Success}; + << TestData{storage, root4, log4, 5, DoneWith::Success, 5}; QTest::newRow("BarrierParallelTwoSingleBarriers") - << TestData{storage, root5, log5, 5, DoneWith::Success}; + << TestData{storage, root5, log5, 5, DoneWith::Success, 5}; } { @@ -2483,13 +2487,13 @@ void tst_Tasking::testTree_data() // Notice the different log order for each scenario. QTest::newRow("MultiBarrierSequential") - << TestData{storage, root1, log1, 5, DoneWith::Success}; + << TestData{storage, root1, log1, 5, DoneWith::Success, 4}; QTest::newRow("MultiBarrierParallelAdvanceFirst") - << TestData{storage, root2, log2, 5, DoneWith::Success}; + << TestData{storage, root2, log2, 5, DoneWith::Success, 5}; QTest::newRow("MultiBarrierParallelWaitForFirst") - << TestData{storage, root3, log3, 5, DoneWith::Success}; + << TestData{storage, root3, log3, 5, DoneWith::Success, 5}; QTest::newRow("MultiBarrierParallelMultiWaitFor") - << TestData{storage, root4, log4, 6, DoneWith::Success}; + << TestData{storage, root4, log4, 6, DoneWith::Success, 6}; } { @@ -2505,8 +2509,8 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {1, Handler::Canceled} }; - QTest::newRow("TaskErrorWithTimeout") << TestData{storage, root1, log1, 2, - DoneWith::Error}; + QTest::newRow("TaskErrorWithTimeout") + << TestData{storage, root1, log1, 2, DoneWith::Error, 1}; const Group root2 { storage, @@ -2518,8 +2522,8 @@ void tst_Tasking::testTree_data() {1, Handler::Timeout}, {1, Handler::Canceled} }; - QTest::newRow("TaskErrorWithTimeoutHandler") << TestData{storage, root2, log2, 2, - DoneWith::Error}; + QTest::newRow("TaskErrorWithTimeoutHandler") + << TestData{storage, root2, log2, 2, DoneWith::Error, 1}; const Group root3 { storage, @@ -2530,16 +2534,16 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {1, Handler::Success} }; - QTest::newRow("TaskDoneWithTimeout") << TestData{storage, root3, doneLog, 2, - DoneWith::Success}; + QTest::newRow("TaskDoneWithTimeout") + << TestData{storage, root3, doneLog, 2, DoneWith::Success, 1}; const Group root4 { storage, TestTask(setupTask(1, 1ms), setupDone(1)) .withTimeout(1000ms, setupTimeout(1)) }; - QTest::newRow("TaskDoneWithTimeoutHandler") << TestData{storage, root4, doneLog, 2, - DoneWith::Success}; + QTest::newRow("TaskDoneWithTimeoutHandler") + << TestData{storage, root4, doneLog, 2, DoneWith::Success, 1}; } { @@ -2556,8 +2560,8 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {1, Handler::Canceled} }; - QTest::newRow("GroupErrorWithTimeout") << TestData{storage, root1, log1, 2, - DoneWith::Error}; + QTest::newRow("GroupErrorWithTimeout") + << TestData{storage, root1, log1, 2, DoneWith::Error, 1}; // Test Group::withTimeout(), passing custom handler const Group root2 { @@ -2571,8 +2575,8 @@ void tst_Tasking::testTree_data() {1, Handler::Timeout}, {1, Handler::Canceled} }; - QTest::newRow("GroupErrorWithTimeoutHandler") << TestData{storage, root2, log2, 2, - DoneWith::Error}; + QTest::newRow("GroupErrorWithTimeoutHandler") + << TestData{storage, root2, log2, 2, DoneWith::Error, 1}; const Group root3 { storage, @@ -2584,8 +2588,8 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {1, Handler::Success} }; - QTest::newRow("GroupDoneWithTimeout") << TestData{storage, root3, doneLog, 2, - DoneWith::Success}; + QTest::newRow("GroupDoneWithTimeout") + << TestData{storage, root3, doneLog, 2, DoneWith::Success, 1}; // Test Group::withTimeout(), passing custom handler const Group root4 { @@ -2594,8 +2598,8 @@ void tst_Tasking::testTree_data() createSuccessTask(1, 1ms) }.withTimeout(1000ms, setupTimeout(1)) }; - QTest::newRow("GroupDoneWithTimeoutHandler") << TestData{storage, root4, doneLog, 2, - DoneWith::Success}; + QTest::newRow("GroupDoneWithTimeoutHandler") + << TestData{storage, root4, doneLog, 2, DoneWith::Success, 1}; } { @@ -2630,7 +2634,7 @@ void tst_Tasking::testTree_data() {1, Handler::GroupSetup}, {0, Handler::GroupSuccess} }; - QTest::newRow("CommonStorage") << TestData{storage, root, log, 0, DoneWith::Success}; + QTest::newRow("CommonStorage") << TestData{storage, root, log, 0, DoneWith::Success, 0}; } { @@ -2640,12 +2644,12 @@ void tst_Tasking::testTree_data() parallel, createSuccessTask(1, 100ms), Group { - createFailingTask(2, 10ms), - createSuccessTask(3, 10ms) + createFailingTask(2, 1ms), + createSuccessTask(3, 1ms) }, - createSuccessTask(4, 10ms) + createSuccessTask(4, 1ms) }, - createSuccessTask(5, 10ms) + createSuccessTask(5, 1ms) }; const Log log { {1, Handler::Setup}, @@ -2655,7 +2659,7 @@ void tst_Tasking::testTree_data() {1, Handler::Canceled}, {4, Handler::Canceled} }; - QTest::newRow("NestedCancel") << TestData{storage, root, log, 5, DoneWith::Error}; + QTest::newRow("NestedCancel") << TestData{storage, root, log, 5, DoneWith::Error, 1}; } { @@ -2758,17 +2762,17 @@ void tst_Tasking::testTree_data() }; QTest::newRow("RepeatSequentialSuccess") - << TestData{storage, rootSequentialSuccess, logSequentialSuccess, 4, DoneWith::Success}; + << TestData{storage, rootSequentialSuccess, logSequentialSuccess, 4, DoneWith::Success, 4}; QTest::newRow("RepeatParallelSuccess") - << TestData{storage, rootParallelSuccess, logParallelSuccess, 4, DoneWith::Success}; + << TestData{storage, rootParallelSuccess, logParallelSuccess, 4, DoneWith::Success, 4}; QTest::newRow("RepeatParallelLimitSuccess") - << TestData{storage, rootParallelLimitSuccess, logParallelLimitSuccess, 4, DoneWith::Success}; + << TestData{storage, rootParallelLimitSuccess, logParallelLimitSuccess, 4, DoneWith::Success, 4}; QTest::newRow("RepeatSequentialError") - << TestData{storage, rootSequentialError, logSequentialError, 4, DoneWith::Error}; + << TestData{storage, rootSequentialError, logSequentialError, 4, DoneWith::Error, 2}; QTest::newRow("RepeatParallelError") - << TestData{storage, rootParallelError, logParallelError, 4, DoneWith::Error}; + << TestData{storage, rootParallelError, logParallelError, 4, DoneWith::Error, 2}; QTest::newRow("RepeatParallelLimitError") - << TestData{storage, rootParallelLimitError, logParallelLimitError, 4, DoneWith::Error}; + << TestData{storage, rootParallelLimitError, logParallelLimitError, 4, DoneWith::Error, 2}; } { @@ -2859,11 +2863,11 @@ void tst_Tasking::testTree_data() }; QTest::newRow("LoopSequential") - << TestData{storage, rootSequential, logSequential, 2, DoneWith::Success}; + << TestData{storage, rootSequential, logSequential, 2, DoneWith::Success, 5}; QTest::newRow("LoopParallel") - << TestData{storage, rootParallel, logParallel, 2, DoneWith::Success}; + << TestData{storage, rootParallel, logParallel, 2, DoneWith::Success, 5}; QTest::newRow("LoopParallelLimit") - << TestData{storage, rootParallelLimit, logParallelLimit, 2, DoneWith::Success}; + << TestData{storage, rootParallelLimit, logParallelLimit, 2, DoneWith::Success, 5}; } { @@ -2874,7 +2878,7 @@ void tst_Tasking::testTree_data() createSuccessTask(1) }; QTest::newRow("ProgressWithLoopUntilFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2888,7 +2892,7 @@ void tst_Tasking::testTree_data() } }; QTest::newRow("ProgressWithNestedLoopUntilFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2899,7 +2903,7 @@ void tst_Tasking::testTree_data() createSuccessTask(1) }; QTest::newRow("ProgressWithGroupSetupFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2913,7 +2917,7 @@ void tst_Tasking::testTree_data() } }; QTest::newRow("ProgressWithNestedGroupSetupFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2930,9 +2934,9 @@ void tst_Tasking::testTree_data() }; QTest::newRow("RecipeWithTask") - << TestData{storage, recipe(true), withTaskLog, 1, DoneWith::Success}; + << TestData{storage, recipe(true), withTaskLog, 1, DoneWith::Success, 1}; QTest::newRow("RecipeWithNull") - << TestData{storage, recipe(false), {}, 0, DoneWith::Success}; + << TestData{storage, recipe(false), {}, 0, DoneWith::Success, 0}; } { @@ -2952,6 +2956,7 @@ void tst_Tasking::testTree_data() }, 1, DoneWith::Success, + 1, MessageLog { {"Group 1", Handler::Setup}, {"Task 1", Handler::Setup}, @@ -2973,6 +2978,7 @@ void tst_Tasking::testTree_data() }, 1, DoneWith::Error, + 1, MessageLog { {"Group 1", Handler::Setup}, {"Task 1", Handler::Setup}, @@ -2998,6 +3004,7 @@ void tst_Tasking::testTree_data() }, 1, DoneWith::Error, + 0, MessageLog { {"Group 1", Handler::Setup}, {"Task 1", Handler::Setup}, @@ -3044,22 +3051,22 @@ void tst_Tasking::testTree_data() }; QTest::newRow("CallDoneIfGroupSuccessOrErrorAfterSuccess") << TestData{storage, createRoot(DoneResult::Success, CallDoneIf::SuccessOrError), - logSuccessLong, 1, DoneWith::Success}; + logSuccessLong, 1, DoneWith::Success, 1}; QTest::newRow("CallDoneIfGroupSuccessAfterSuccess") << TestData{storage, createRoot(DoneResult::Success, CallDoneIf::Success), - logSuccessLong, 1, DoneWith::Success}; + logSuccessLong, 1, DoneWith::Success, 1}; QTest::newRow("CallDoneIfGroupErrorAfterSuccess") << TestData{storage, createRoot(DoneResult::Success, CallDoneIf::Error), - logSuccessShort, 1, DoneWith::Success}; + logSuccessShort, 1, DoneWith::Success, 1}; QTest::newRow("CallDoneIfGroupSuccessOrErrorAfterError") << TestData{storage, createRoot(DoneResult::Error, CallDoneIf::SuccessOrError), - logErrorLong, 1, DoneWith::Error}; + logErrorLong, 1, DoneWith::Error, 1}; QTest::newRow("CallDoneIfGroupSuccessAfterError") << TestData{storage, createRoot(DoneResult::Error, CallDoneIf::Success), - logErrorShort, 1, DoneWith::Error}; + logErrorShort, 1, DoneWith::Error, 1}; QTest::newRow("CallDoneIfGroupErrorAfterError") << TestData{storage, createRoot(DoneResult::Error, CallDoneIf::Error), - logErrorLong, 1, DoneWith::Error}; + logErrorLong, 1, DoneWith::Error, 1}; } // This test checks if storage shadowing works OK. @@ -3172,6 +3179,9 @@ void tst_Tasking::testTree() QCOMPARE(result, testData.result); + if (testData.asyncCount) + QCOMPARE(taskTree.asyncCount(), *testData.asyncCount); + if (testData.messageLog) { QCOMPARE(s_messages.count(), testData.messageLog->count()); From 7d5523a59b7da9512c2977f6f96f540364082785 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 20 Feb 2024 13:22:32 +0100 Subject: [PATCH 041/128] Axivion: Use LayoutBuilder for project settings page Also, add a stretch at the end of the buttons row. Change-Id: I51392359b183462b52ad9d8b0775f59cf4a94a82 Reviewed-by: Reviewed-by: Jarek Kobus --- .../axivion/axivionprojectsettings.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/plugins/axivion/axivionprojectsettings.cpp b/src/plugins/axivion/axivionprojectsettings.cpp index 1413b3cfd3a..8cd63ba7ac0 100644 --- a/src/plugins/axivion/axivionprojectsettings.cpp +++ b/src/plugins/axivion/axivionprojectsettings.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -117,34 +118,33 @@ AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(Project *project) setUseGlobalSettingsCheckBoxVisible(false); setUseGlobalSettingsLabelVisible(true); setGlobalSettingsId("Axivion.Settings.General"); // FIXME move id to constants - // setup ui - auto verticalLayout = new QVBoxLayout(this); - verticalLayout->setContentsMargins(0, 0, 0, 0); m_linkedProject = new QLabel(this); - verticalLayout->addWidget(m_linkedProject); m_dashboardProjects = new QTreeWidget(this); m_dashboardProjects->setHeaderHidden(true); m_dashboardProjects->setRootIsDecorated(false); - verticalLayout->addWidget(new QLabel(Tr::tr("Dashboard projects:"))); - verticalLayout->addWidget(m_dashboardProjects); m_infoLabel = new InfoLabel(this); m_infoLabel->setVisible(false); - verticalLayout->addWidget(m_infoLabel); - auto horizontalLayout = new QHBoxLayout; - horizontalLayout->setContentsMargins(0, 0, 0, 0); m_fetchProjects = new QPushButton(Tr::tr("Fetch Projects")); - horizontalLayout->addWidget(m_fetchProjects); + m_link = new QPushButton(Tr::tr("Link Project")); m_link->setEnabled(false); - horizontalLayout->addWidget(m_link); + m_unlink = new QPushButton(Tr::tr("Unlink Project")); m_unlink->setEnabled(false); - horizontalLayout->addWidget(m_unlink); - verticalLayout->addLayout(horizontalLayout); + + using namespace Layouting; + Column { + noMargin, + m_linkedProject, + Tr::tr("Dashboard projects:"), + m_dashboardProjects, + m_infoLabel, + Row { m_fetchProjects, m_link, m_unlink, st } + }.attachTo(this); connect(m_dashboardProjects, &QTreeWidget::itemSelectionChanged, this, &AxivionProjectSettingsWidget::updateEnabledStates); From 88aacbb562ea1a8fe736e181ff4ec3904d910d98 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 20 Feb 2024 14:15:39 +0100 Subject: [PATCH 042/128] Debugger: fix char type dumper optimization Change-Id: I600f4ddc9a4539e19b70664fd9354c17e64cd0d6 Reviewed-by: Reviewed-by: Christian Stenger --- share/qtcreator/debugger/dumper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index ca6b7f40737..2fa7d31da50 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -420,7 +420,7 @@ class DumperBase(): def charType(self): result = self.lookupType('char') - self.intType = lambda: result + self.charType = lambda: result return result def ptrSize(self): From a1f317798dd698720bbfb1f7bee9cc9775e70cd1 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 20 Feb 2024 17:00:53 +0100 Subject: [PATCH 043/128] Doc: Link to "Add custom output parsers" ...from "CMake Build Configuration" Task-number: QTCREATORBUG-30209 Change-Id: Ic10125f84eee29ddae05b18d80512fbde530dd2d Reviewed-by: Cristian Adam --- doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index a51d6623f88..c8da5117fb6 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -275,6 +275,7 @@ The build errors and warnings are parsed and displayed in \l Issues. - \sa {Activate kits for a project}, {Configure projects for building}, - {Configure projects for running}, {Open projects}, {CMake} + \sa {Activate kits for a project}, {Add custom output parsers}, + {Configure projects for building}, {Configure projects for running}, + {Open projects}, {CMake} */ From f686ca7aa8e597fb2d6cc15d6e2116f6ee6c23c1 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 20 Feb 2024 12:15:36 +0100 Subject: [PATCH 044/128] Nanotrace: Add LGPL-3.0-only as a licensing option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I203d93c23d5799835b6a122b9329f16af84c895c Reviewed-by: Eike Ziller Reviewed-by: Reviewed-by: Kai Köhne --- src/libs/nanotrace/nanotrace.cpp | 2 +- src/libs/nanotrace/nanotrace.h | 2 +- src/libs/nanotrace/nanotraceglobals.h | 2 +- src/libs/nanotrace/nanotracehr.cpp | 2 +- src/libs/nanotrace/nanotracehr.h | 2 +- src/libs/nanotrace/python/figures.py | 2 +- src/libs/nanotrace/python/nanotrace.py | 2 +- src/libs/nanotrace/python/reader.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/nanotrace/nanotrace.cpp b/src/libs/nanotrace/nanotrace.cpp index c5adeb4d724..8a96b125845 100644 --- a/src/libs/nanotrace/nanotrace.cpp +++ b/src/libs/nanotrace/nanotrace.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "nanotrace.h" diff --git a/src/libs/nanotrace/nanotrace.h b/src/libs/nanotrace/nanotrace.h index ed17797e344..76b0120ae1a 100644 --- a/src/libs/nanotrace/nanotrace.h +++ b/src/libs/nanotrace/nanotrace.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once diff --git a/src/libs/nanotrace/nanotraceglobals.h b/src/libs/nanotrace/nanotraceglobals.h index 649408d69c9..64af31aab9d 100644 --- a/src/libs/nanotrace/nanotraceglobals.h +++ b/src/libs/nanotrace/nanotraceglobals.h @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 3f02bd527d3..ada5bf65f52 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "nanotracehr.h" diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 74b1381b064..62ec7dc03cd 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once diff --git a/src/libs/nanotrace/python/figures.py b/src/libs/nanotrace/python/figures.py index cb806ea40d6..7f92a3b57ee 100644 --- a/src/libs/nanotrace/python/figures.py +++ b/src/libs/nanotrace/python/figures.py @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only import pandas as pd import plotly.graph_objects as go import plotly.subplots as sp diff --git a/src/libs/nanotrace/python/nanotrace.py b/src/libs/nanotrace/python/nanotrace.py index 880f17d7bfc..a173c3c52b7 100644 --- a/src/libs/nanotrace/python/nanotrace.py +++ b/src/libs/nanotrace/python/nanotrace.py @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only import reader as rd import figures as fgs diff --git a/src/libs/nanotrace/python/reader.py b/src/libs/nanotrace/python/reader.py index 023bddb15f7..ed6b8b5b1e1 100644 --- a/src/libs/nanotrace/python/reader.py +++ b/src/libs/nanotrace/python/reader.py @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only import os import io import json From 2fe085871beebcfd23ae17fe3867a7c2caa4935b Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 20 Feb 2024 15:22:13 +0100 Subject: [PATCH 045/128] Axivion: Fix internal layout of DashboardSettingsWidget Make internal margins a bit smaller so that they look consistent with the rest of UI coming from Qt. Change-Id: Iaeaccd1e0a4141a96657e32cebb619b969e43cab Reviewed-by: Reviewed-by: hjk --- src/plugins/axivion/axivionsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index a4db242c2dd..8597fac9150 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -185,7 +185,7 @@ DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPu Form { m_dashboardUrl, br, m_username, br, - mode == Edit ? normalMargin : noMargin + noMargin }.attachTo(this); if (mode == Edit) { From 370fb4d550dafd8ededf4b2ecde23d3f48cbcb46 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 21 Feb 2024 09:14:39 +0100 Subject: [PATCH 046/128] Axivion: Open issue information semi-automatic This patch adds another way of opening issue information. Double clicking an issue on the issues table will now open the related information automatically beside jumping to the respective location. The original way of using a button on the tool tip of the issue's text mark is clumsy, but still present. Change-Id: I7b0fcafb8b01675fc020f8cc8174121ba7f52adc Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 31 +++++++++++++++-------- src/plugins/axivion/axivionplugin.cpp | 6 +++++ src/plugins/axivion/axivionplugin.h | 1 + 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index df8d87accdc..42046f78b14 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -189,28 +189,34 @@ void DashboardWidget::updateUi() class IssueTreeItem final : public StaticTreeItem { public: - IssueTreeItem(const QStringList &data, const QStringList &toolTips) + IssueTreeItem(const QString &id, const QStringList &data, const QStringList &toolTips) : StaticTreeItem(data, toolTips) + , m_id(id) {} void setLinks(const Links &links) { m_links = links; } bool setData(int column, const QVariant &value, int role) final { - if (role == BaseTreeView::ItemActivatedRole && !m_links.isEmpty()) { - // TODO for now only simple - just the first.. - Link link = m_links.first(); - Project *project = ProjectManager::startupProject(); - FilePath baseDir = project ? project->projectDirectory() : FilePath{}; - link.targetFilePath = baseDir.resolvePath(link.targetFilePath); - if (link.targetFilePath.exists()) - EditorManager::openEditorAt(link); + if (role == BaseTreeView::ItemActivatedRole) { + if (!m_links.isEmpty()) { + // TODO for now only simple - just the first.. + Link link = m_links.first(); + Project *project = ProjectManager::startupProject(); + FilePath baseDir = project ? project->projectDirectory() : FilePath{}; + link.targetFilePath = baseDir.resolvePath(link.targetFilePath); + if (link.targetFilePath.exists()) + EditorManager::openEditorAt(link); + } + if (!m_id.isEmpty()) + fetchIssueInfo(m_id); return true; } return StaticTreeItem::setData(column, value, role); } private: + const QString m_id; Links m_links; }; @@ -425,17 +431,20 @@ void IssuesWidget::addIssues(const Dto::IssueTableDto &dto) const std::vector &tableColumns = m_currentTableInfo->columns; const std::vector> &rows = dto.rows; for (const auto &row : rows) { + QString id; QStringList data; for (const auto &column : tableColumns) { const auto it = row.find(column.key); if (it != row.end()) { QString value = anyToSimpleString(it->second); - if (column.key == "id") + if (column.key == "id") { value.prepend(m_currentPrefix); + id = value; + } data << value; } } - IssueTreeItem *it = new IssueTreeItem(data, data); + IssueTreeItem *it = new IssueTreeItem(id, data, data); it->setLinks(linksForIssue(row)); m_issuesModel->rootItem()->appendChild(it); } diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 1cd8c9fc140..6d6d5c98808 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -887,6 +887,12 @@ class AxivionPlugin final : public ExtensionSystem::IPlugin } }; +void fetchIssueInfo(const QString &id) +{ + QTC_ASSERT(dd, return); + dd->fetchIssueInfo(id); +} + } // Axivion::Internal #include "axivionplugin.moc" diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h index 1d5245a5034..1d2ebe12129 100644 --- a/src/plugins/axivion/axivionplugin.h +++ b/src/plugins/axivion/axivionplugin.h @@ -72,6 +72,7 @@ bool handleCertificateIssue(); QIcon iconForIssue(const QString &prefix); QString anyToSimpleString(const Dto::Any &any); +void fetchIssueInfo(const QString &id); } // Axivion::Internal From a130343ab55b9edb4303edabf46b1b87340712f7 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 21 Feb 2024 10:15:28 +0100 Subject: [PATCH 047/128] Fix i18n issues Fix some missing Tr::, and some namespace usages that confused lupdate. Change-Id: Ib5a411fc53a28a6b807600db50aacc68955ca5dc Reviewed-by: hjk Reviewed-by: Leena Miettinen --- src/libs/utils/macroexpander.cpp | 2 +- src/plugins/coreplugin/editormanager/editormanager.cpp | 6 +++--- src/plugins/coreplugin/find/ifindfilter.cpp | 2 +- src/plugins/cvs/cvsplugin.cpp | 10 +++++----- src/plugins/effectcomposer/effectcomposerview.h | 1 + src/plugins/languageclient/callhierarchy.cpp | 2 +- src/plugins/mercurial/mercurialplugin.cpp | 8 ++++---- .../projectexplorer/jsonwizard/jsonwizardfactory.cpp | 2 +- .../appmanagerinstallpackagestep.cpp | 4 ++-- .../qtapplicationmanager/appmanagerruncontrol.cpp | 2 +- src/plugins/remotelinux/linuxdevice.cpp | 4 ++-- src/plugins/subversion/subversionplugin.cpp | 6 +++--- 12 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index f6d3dad3623..dd57cd6b5a0 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -375,7 +375,7 @@ void MacroExpander::registerIntVariable(const QByteArray &variable, * Convenience function to register several variables with the same \a prefix, that have a file * as a value. Takes the prefix and registers variables like \c{prefix:FilePath} and * \c{prefix:Path}, with descriptions that start with the given \a heading. - * For example \c{registerFileVariables("CurrentDocument", tr("Current Document"))} registers + * For example \c{registerFileVariables("CurrentDocument", Tr::tr("Current Document"))} registers * variables such as \c{CurrentDocument:FilePath} with description * "Current Document: Full path including file name." * diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 10cec9f79ba..6ec37a6cb8c 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -579,7 +579,7 @@ void EditorManagerPrivate::init() // Go back in navigation history ActionBuilder goBack(this, Constants::GO_BACK); goBack.setIcon(Utils::Icons::PREV.icon()); - goBack.setText(Core::Tr::tr("Go Back")); + goBack.setText(::Core::Tr::tr("Go Back")); goBack.bindContextAction(&m_goBackAction); goBack.setContext(editDesignContext); goBack.setDefaultKeySequence(::Core::Tr::tr("Ctrl+Alt+Left"), ::Core::Tr::tr("Alt+Left")); @@ -589,7 +589,7 @@ void EditorManagerPrivate::init() // Go forward in navigation history ActionBuilder goForward(this, Constants::GO_FORWARD); goForward.setIcon(Utils::Icons::NEXT.icon()); - goForward.setText(Core::Tr::tr("Go Forward")); + goForward.setText(::Core::Tr::tr("Go Forward")); goForward.bindContextAction(&m_goForwardAction); goForward.setContext(editDesignContext); goForward.setDefaultKeySequence(::Core::Tr::tr("Ctrl+Alt+Right"), ::Core::Tr::tr("Alt+Right")); @@ -618,7 +618,7 @@ void EditorManagerPrivate::init() splitSideBySide.setText(::Core::Tr::tr("Split Side by Side")); splitSideBySide.bindContextAction(&m_splitSideBySideAction); splitSideBySide.setContext(editManagerContext); - splitSideBySide.setDefaultKeySequence(::Core::Tr::tr("Meta+E,3"), Core::Tr::tr("Ctrl+E,3")); + splitSideBySide.setDefaultKeySequence(::Core::Tr::tr("Meta+E,3"), ::Core::Tr::tr("Ctrl+E,3")); splitSideBySide.addToContainer(Constants::M_WINDOW, Constants::G_WINDOW_SPLIT); splitSideBySide.addOnTriggered(this, &EditorManager::splitSideBySide); diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp index a849598cfdd..704f827bf83 100644 --- a/src/plugins/coreplugin/find/ifindfilter.cpp +++ b/src/plugins/coreplugin/find/ifindfilter.cpp @@ -92,7 +92,7 @@ using namespace Utils; Returns the name of the find filter or scope as presented to the user. This is the name that appears in the scope selection combo box, for example. - Always return a translatable string. That is, use \c tr() for the return + Always return a translatable string. That is, use \c {Tr::tr()} for the return value. */ diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index 4ca9dd83b39..2688def71e6 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -292,7 +292,7 @@ public: VcsEditorFactory commandLogEditorFactory {{ OtherContent, CVS_COMMANDLOG_EDITOR_ID, - VcsBase::Tr::tr("CVS Command Log Editor"), // display name + ::VcsBase::Tr::tr("CVS Command Log Editor"), // display name "text/vnd.qtcreator.cvs.commandlog", [] { return new CvsEditorWidget; }, std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2) @@ -301,7 +301,7 @@ public: VcsEditorFactory logEditorFactory {{ LogOutput, CVS_FILELOG_EDITOR_ID, - VcsBase::Tr::tr("CVS File Log Editor"), // display name + ::VcsBase::Tr::tr("CVS File Log Editor"), // display name "text/vnd.qtcreator.cvs.log", [] { return new CvsEditorWidget; }, std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2) @@ -310,7 +310,7 @@ public: VcsEditorFactory annotateEditorFactory {{ AnnotateOutput, CVS_ANNOTATION_EDITOR_ID, - VcsBase::Tr::tr("CVS Annotation Editor"), // display name + ::VcsBase::Tr::tr("CVS Annotation Editor"), // display name "text/vnd.qtcreator.cvs.annotation", [] { return new CvsEditorWidget; }, std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2) @@ -319,7 +319,7 @@ public: VcsEditorFactory diffEditorFactory {{ DiffOutput, CVS_DIFF_EDITOR_ID, - VcsBase::Tr::tr("CVS Diff Editor"), // display name + ::VcsBase::Tr::tr("CVS Diff Editor"), // display name "text/x-patch", [] { return new CvsEditorWidget; }, std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2) @@ -449,7 +449,7 @@ CvsPluginPrivate::CvsPluginPrivate() setupVcsSubmitEditor(this, { CVS_SUBMIT_MIMETYPE, CVSCOMMITEDITOR_ID, - VcsBase::Tr::tr("CVS Commit Editor"), + ::VcsBase::Tr::tr("CVS Commit Editor"), VcsBaseSubmitEditorParameters::DiffFiles, [] { return new CvsSubmitEditor; }, }); diff --git a/src/plugins/effectcomposer/effectcomposerview.h b/src/plugins/effectcomposer/effectcomposerview.h index a16225dfec4..c7a381cb7db 100644 --- a/src/plugins/effectcomposer/effectcomposerview.h +++ b/src/plugins/effectcomposer/effectcomposerview.h @@ -24,6 +24,7 @@ public: class EffectComposerView : public QmlDesigner::AbstractView { + Q_DECLARE_TR_FUNCTIONS(EffectComposer::EffectComposerView) public: EffectComposerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies); ~EffectComposerView() override; diff --git a/src/plugins/languageclient/callhierarchy.cpp b/src/plugins/languageclient/callhierarchy.cpp index a310fde2a4e..32023e6a98f 100644 --- a/src/plugins/languageclient/callhierarchy.cpp +++ b/src/plugins/languageclient/callhierarchy.cpp @@ -280,7 +280,7 @@ public: Icons::RELOAD_TOOLBAR.icon(); auto button = new QToolButton; button->setIcon(Icons::RELOAD_TOOLBAR.icon()); - button->setToolTip(Tr::tr("Reloads the call hierarchy for the symbol under cursor position.")); + button->setToolTip(::LanguageClient::Tr::tr("Reloads the call hierarchy for the symbol under cursor position.")); connect(button, &QToolButton::clicked, this, [h] { h->updateHierarchyAtCursorPosition(); }); return {h, {button}}; } diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index 76c8f2ba8c8..a9ab891c023 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -146,7 +146,7 @@ public: VcsEditorFactory logEditorFactory {{ LogOutput, Constants::FILELOG_ID, - VcsBase::Tr::tr("Mercurial File Log Editor"), + ::VcsBase::Tr::tr("Mercurial File Log Editor"), Constants::LOGAPP, [] { return new MercurialEditorWidget; }, std::bind(&MercurialPluginPrivate::vcsDescribe, this, _1, _2) @@ -155,7 +155,7 @@ public: VcsEditorFactory annotateEditorFactory {{ AnnotateOutput, Constants::ANNOTATELOG_ID, - VcsBase::Tr::tr("Mercurial Annotation Editor"), + ::VcsBase::Tr::tr("Mercurial Annotation Editor"), Constants::ANNOTATEAPP, [] { return new MercurialEditorWidget; }, std::bind(&MercurialPluginPrivate::vcsDescribe, this, _1, _2) @@ -164,7 +164,7 @@ public: VcsEditorFactory diffEditorFactory {{ DiffOutput, Constants::DIFFLOG_ID, - VcsBase::Tr::tr("Mercurial Diff Editor"), + ::VcsBase::Tr::tr("Mercurial Diff Editor"), Constants::DIFFAPP, [] { return new MercurialEditorWidget; }, std::bind(&MercurialPluginPrivate::vcsDescribe, this, _1, _2) @@ -181,7 +181,7 @@ MercurialPluginPrivate::MercurialPluginPrivate() setupVcsSubmitEditor(this, { Constants::COMMITMIMETYPE, Constants::COMMIT_ID, - VcsBase::Tr::tr("Mercurial Commit Log Editor"), + ::VcsBase::Tr::tr("Mercurial Commit Log Editor"), VcsBaseSubmitEditorParameters::DiffFiles, [] { return new CommitEditor; } }); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp index 0febbcd4149..7f8ff6baf54 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp @@ -504,7 +504,7 @@ QList JsonWizardFactory::createWizardFactories() currentFile.parentDir(), &errorMessage); if (!factory) { - verboseLog.append(tr("* Failed to create: %1\n").arg(errorMessage)); + verboseLog.append(Tr::tr("* Failed to create: %1\n").arg(errorMessage)); continue; } diff --git a/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp b/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp index 0f995144ec9..f89514ba005 100644 --- a/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp @@ -52,7 +52,7 @@ private: AppManagerInstallPackageStep::AppManagerInstallPackageStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - setDisplayName(tr("Install Application Manager package")); + setDisplayName(Tr::tr("Install Application Manager package")); controller.setDefaultPathValue(getToolFilePath(Constants::APPMAN_CONTROLLER, target()->kit(), @@ -125,7 +125,7 @@ GroupItem AppManagerInstallPackageStep::deployRecipe() }; const auto doneHandler = [this](const Process &process, DoneWith result) { if (result == DoneWith::Success) { - addProgressMessage(tr("Command finished successfully.")); + addProgressMessage(Tr::tr("Command finished successfully.")); } else { if (process.error() != QProcess::UnknownError || process.exitStatus() != QProcess::NormalExit) { diff --git a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp index ec1aa7244ed..9c485536a44 100644 --- a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp @@ -232,7 +232,7 @@ private: void start() override { if (m_symbolFile.isEmpty()) { - reportFailure(tr("Cannot debug: Local executable is not set.")); + reportFailure(Tr::tr("Cannot debug: Local executable is not set.")); return; } diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index dbad2b5bb18..30fac20a9d9 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -476,8 +476,8 @@ ProcessResult SshProcessInterface::runInShell(const CommandLine &command, const using namespace std::chrono_literals; process.runBlocking(2s); if (process.result() == ProcessResult::Canceled) { - Core::MessageManager::writeFlashing(tr("Can't send control signal to the %1 device. " - "The device might have been disconnected.") + Core::MessageManager::writeFlashing(Tr::tr("Can't send control signal to the %1 device. " + "The device might have been disconnected.") .arg(d->m_device->displayName())); } return process.result(); diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index fc9d5ed1222..64f32a11ff2 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -248,7 +248,7 @@ public: VcsEditorFactory logEditorFactory {{ LogOutput, Constants::SUBVERSION_LOG_EDITOR_ID, - VcsBase::Tr::tr("Subversion File Log Editor"), + ::VcsBase::Tr::tr("Subversion File Log Editor"), Constants::SUBVERSION_LOG_MIMETYPE, [] { return new SubversionEditorWidget; }, std::bind(&SubversionPluginPrivate::vcsDescribe, this, _1, _2) @@ -257,7 +257,7 @@ public: VcsEditorFactory blameEditorFactory {{ AnnotateOutput, Constants::SUBVERSION_BLAME_EDITOR_ID, - VcsBase::Tr::tr("Subversion Annotation Editor"), + ::VcsBase::Tr::tr("Subversion Annotation Editor"), Constants::SUBVERSION_BLAME_MIMETYPE, [] { return new SubversionEditorWidget; }, std::bind(&SubversionPluginPrivate::vcsDescribe, this, _1, _2) @@ -468,7 +468,7 @@ SubversionPluginPrivate::SubversionPluginPrivate() setupVcsSubmitEditor(this, { Constants::SUBVERSION_SUBMIT_MIMETYPE, Constants::SUBVERSION_COMMIT_EDITOR_ID, - VcsBase::Tr::tr("Subversion Commit Editor"), + ::VcsBase::Tr::tr("Subversion Commit Editor"), VcsBaseSubmitEditorParameters::DiffFiles, [] { return new SubversionSubmitEditor; }, }); From 04beeaf082fa0a8f287b5b2605fe796cae009936 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 21 Feb 2024 09:54:31 +0100 Subject: [PATCH 048/128] Axivion: Handle local URLs of issue details Issue details provide a usually at least one or depending on the issue kind more file locations related to the issue. Handle files which are present inside the current project. Change-Id: I375ee99dfa1ca697d04146efde3081360e6cb582 Reviewed-by: Jarek Kobus Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 6d6d5c98808..9f142a17671 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include @@ -188,6 +189,7 @@ public: void handleIssuesForFile(const Dto::FileViewDto &fileView); void fetchIssueInfo(const QString &id); void setIssueDetails(const QString &issueDetailsHtml); + void handleAnchorClicked(const QUrl &url); signals: void issueDetailsChanged(const QString &issueDetailsHtml); @@ -832,6 +834,23 @@ void AxivionPluginPrivate::handleIssuesForFile(const Dto::FileViewDto &fileView) } } +void AxivionPluginPrivate::handleAnchorClicked(const QUrl &url) +{ + QTC_ASSERT(dd, return); + QTC_ASSERT(dd->m_project, return); + const QUrlQuery query(url); + if (query.isEmpty()) + return; + Link link; + if (const QString path = query.queryItemValue("filename", QUrl::FullyDecoded); !path.isEmpty()) + link.targetFilePath = m_project->projectDirectory().pathAppended(path); + if (const QString line = query.queryItemValue("line"); !line.isEmpty()) + link.targetLine = line.toInt(); + // column entry is wrong - so, ignore it + if (link.hasValidTarget() && link.targetFilePath.exists()) + EditorManager::openEditorAt(link); +} + class AxivionIssueWidgetFactory final : public INavigationWidgetFactory { public: @@ -850,6 +869,8 @@ public: NavigationView view; view.widget = browser; connect(dd, &AxivionPluginPrivate::issueDetailsChanged, browser, &QTextBrowser::setHtml); + connect(browser, &QTextBrowser::anchorClicked, + dd, &AxivionPluginPrivate::handleAnchorClicked); return view; } }; From 504bed284d17f2641c7c21200442a9dba3d786de Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 21 Feb 2024 10:40:02 +0100 Subject: [PATCH 049/128] Axivion: Allow opening external links ..outside of Qt Creator, by using the default browser or similar. Change-Id: Ibaa379d2a26976fe717ee38b056794dd54838685 Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 9f142a17671..a5da9cdba2c 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -34,12 +34,14 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -838,6 +840,19 @@ void AxivionPluginPrivate::handleAnchorClicked(const QUrl &url) { QTC_ASSERT(dd, return); QTC_ASSERT(dd->m_project, return); + if (!url.scheme().isEmpty()) { + const QString detail = Tr::tr("The activated link appears to be external.\n" + "Do you want to open \"%1\" with its default application?") + .arg(url.toString()); + const QMessageBox::StandardButton pressed + = CheckableMessageBox::question(Core::ICore::dialogParent(), + Tr::tr("Open External Links"), + detail, + Key("AxivionOpenExternalLinks")); + if (pressed == QMessageBox::Yes) + QDesktopServices::openUrl(url); + return; + } const QUrlQuery query(url); if (query.isEmpty()) return; From 1a7e1b5dbd9f2a8ac0cce1c9ec79ddea765485c1 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 21 Feb 2024 08:54:54 +0100 Subject: [PATCH 050/128] Cmake: include textwrap in the generated python zip file textwrap is required for traceback that is used when the last command gets debugged Amends 0a2b6a910acd8d75bb14c1860c60c248aa5e073e Change-Id: I7d62dbe2bc0f4e1839f07b9bff687ee1f4e4c885 Reviewed-by: Cristian Adam Reviewed-by: --- cmake/CreatePythonXY.cmake | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/cmake/CreatePythonXY.cmake b/cmake/CreatePythonXY.cmake index 637c4940c83..4e234703cb8 100644 --- a/cmake/CreatePythonXY.cmake +++ b/cmake/CreatePythonXY.cmake @@ -24,34 +24,34 @@ function(create_python_xy PythonExe PythonZipFilePath) cgi.py nntplib.py tarfile.py cgitb.py nturl2path.py telnetlib.py chunk.py numbers.py tempfile.py - cmd.py optparse.py textwrap.py - code.py pathlib.py this.py - codeop.py pdb.py timeit.py - colorsys.py pickle.py trace.py - compileall.py pickletools.py tracemalloc.py - configparser.py pipes.py tty.py - contextvars.py plistlib.py turtle.py - cProfile.py poplib.py typing.py - crypt.py pprint.py uu.py - csv.py profile.py uuid.py - dataclasses.py pstats.py wave.py - datetime.py pty.py webbrowser.py - decimal.py pyclbr.py xdrlib.py - difflib.py py_compile.py zipapp.py - doctest.py queue.py zipfile.py - dummy_threading.py quopri.py zipimport.py - filecmp.py random.py _compat_pickle.py - fileinput.py rlcompleter.py _compression.py - formatter.py runpy.py _dummy_thread.py - fractions.py sched.py _markupbase.py - ftplib.py secrets.py _osx_support.py - getopt.py selectors.py _pydecimal.py - getpass.py shelve.py _pyio.py - gettext.py shlex.py _py_abc.py - gzip.py shutil.py _strptime.py - hashlib.py smtpd.py _threading_local.py - hmac.py smtplib.py __future__.py - imaplib.py sndhdr.py __phello__.foo.py + cmd.py optparse.py this.py + code.py pathlib.py timeit.py + codeop.py pdb.py trace.py + colorsys.py pickle.py tracemalloc.py + compileall.py pickletools.py tty.py + configparser.py pipes.py turtle.py + contextvars.py plistlib.py typing.py + cProfile.py poplib.py uu.py + crypt.py pprint.py uuid.py + csv.py profile.py wave.py + dataclasses.py pstats.py webbrowser.py + datetime.py pty.py xdrlib.py + decimal.py pyclbr.py zipapp.py + difflib.py py_compile.py zipfile.py + doctest.py queue.py zipimport.py + dummy_threading.py quopri.py _compat_pickle.py + filecmp.py random.py _compression.py + fileinput.py rlcompleter.py _dummy_thread.py + formatter.py runpy.py _markupbase.py + fractions.py sched.py _osx_support.py + ftplib.py secrets.py _pydecimal.py + getopt.py selectors.py _pyio.py + getpass.py shelve.py _py_abc.py + gettext.py shlex.py _strptime.py + gzip.py shutil.py _threading_local.py + hashlib.py smtpd.py __future__.py + hmac.py smtplib.py __phello__.foo.py + imaplib.py sndhdr.py ) list(FIND python_lib_files "${python_lib_dir}/${not_needed}" found_not_needed) if (NOT found_not_needed STREQUAL "-1") From c9208932d71eada289c47f876089168981aff20d Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Tue, 20 Feb 2024 13:45:48 +0100 Subject: [PATCH 051/128] CMakePM: Don't show anything but CMake Preset Kits during initial configure This is for projects that use CMake Presets, and matches the expectations of the users using CMake Presets. The user can still enable a Kit from the left list of Kits, which is not filtered. Amends 87c67fc6d7193883edb9a0244296cd429f0473de Task-number: QTCREATORBUG-29535 Change-Id: If97eef867a687c877b1cbd08cd4537fe6459136f Reviewed-by: Marcus Tillmanns Reviewed-by: --- src/plugins/cmakeprojectmanager/cmakeproject.cpp | 13 +++++++++++++ .../cmakeprojectmanager/cmakeprojectimporter.cpp | 4 +--- .../cmakeprojectmanager/cmakeprojectimporter.h | 1 - src/plugins/cmakeprojectmanager/presetsparser.h | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 7318c7a791f..25352f0b5d6 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -7,6 +7,7 @@ #include "cmakeprojectconstants.h" #include "cmakeprojectimporter.h" #include "cmakeprojectmanagertr.h" +#include "presetsmacros.h" #include #include @@ -301,6 +302,18 @@ void CMakeProject::readPresets() m_presetsData = combinePresets(cmakePresetsData, cmakeUserPresetsData); setupBuildPresets(m_presetsData); + + for (const auto &configPreset : m_presetsData.configurePresets) { + if (configPreset.hidden.value()) + continue; + + if (configPreset.condition) { + if (!CMakePresets::Macros::evaluatePresetCondition(configPreset, projectFilePath())) + continue; + } + m_presetsData.havePresets = true; + break; + } } bool CMakeProject::setupTarget(Target *t) diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 450d1540102..52ed82f8d77 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -201,8 +201,6 @@ FilePaths CMakeProjectImporter::presetCandidates() } } - m_hasCMakePresets = !candidates.isEmpty(); - return candidates; } @@ -223,7 +221,7 @@ Target *CMakeProjectImporter::preferredTarget(const QList &possibleTar bool CMakeProjectImporter::filter(ProjectExplorer::Kit *k) const { - if (!m_hasCMakePresets) + if (!m_project->presetsData().havePresets) return true; const auto presetConfigItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h index 82e1835ba7b..63c2403f4b4 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h @@ -49,7 +49,6 @@ private: const CMakeProject *m_project; Utils::TemporaryDirectory m_presetsTempDir; - bool m_hasCMakePresets = false; }; #ifdef WITH_TESTS diff --git a/src/plugins/cmakeprojectmanager/presetsparser.h b/src/plugins/cmakeprojectmanager/presetsparser.h index 20d36e389ec..6df09014af3 100644 --- a/src/plugins/cmakeprojectmanager/presetsparser.h +++ b/src/plugins/cmakeprojectmanager/presetsparser.h @@ -140,6 +140,7 @@ class PresetsData { public: int version = 0; + bool havePresets = false; QVersionNumber cmakeMinimimRequired; QHash vendor; std::optional include; From 0f5eae1d33a87cea975d00f1975076f70abf1f0b Mon Sep 17 00:00:00 2001 From: Mehdi Salem Date: Wed, 21 Feb 2024 12:43:40 +0100 Subject: [PATCH 052/128] Axivion: use static for deserialize in dto classes Change-Id: Ide5b1ff535c1e2bdba7d7f480bf1c19fd3c19fb8 Reviewed-by: Jarek Kobus --- src/plugins/axivion/dashboard/dto.cpp | 282 +++++++++++++++++--------- src/plugins/axivion/dashboard/dto.h | 90 ++++---- 2 files changed, 233 insertions(+), 139 deletions(-) diff --git a/src/plugins/axivion/dashboard/dto.cpp b/src/plugins/axivion/dashboard/dto.cpp index d578e3dff10..747ea2d03da 100644 --- a/src/plugins/axivion/dashboard/dto.cpp +++ b/src/plugins/axivion/dashboard/dto.cpp @@ -77,7 +77,8 @@ namespace Axivion::Internal::Dto { // throws Axivion::Internal::Dto::invalid_dto_exception template - [[noreturn]] static void throw_json_type_conversion(QJsonValue::Type type) { + [[noreturn]] static void throw_json_type_conversion(QJsonValue::Type type) + { throw_invalid_dto_exception(concat({ "Error parsing JSON: Cannot convert type ", to_std_string(type) @@ -86,7 +87,8 @@ namespace Axivion::Internal::Dto { // throws Axivion::Internal::Dto::invalid_dto_exception template - [[noreturn]] static void throw_json_value_conversion(const V &raw_value) { + [[noreturn]] static void throw_json_value_conversion(const V &raw_value) + { throw_invalid_dto_exception(concat({ "Error parsing JSON: Cannot convert raw value ", to_std_string(raw_value) @@ -543,7 +545,8 @@ namespace Axivion::Internal::Dto { })); } - static QJsonValue serialize(const Any &value) { + static QJsonValue serialize(const Any &value) + { if (value.isNull()) return serialize_json(nullptr); if (value.isString()) @@ -569,7 +572,8 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } - Utils::expected_str Any::deserializeExpected(const QByteArray &json) { + Utils::expected_str Any::deserializeExpected(const QByteArray &json) + { return deserializeExp(json); } @@ -688,7 +692,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static AnalyzedFileDto deserialize(const QJsonValue &json) { + static AnalyzedFileDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, analyzedFileKeyPath), @@ -697,7 +702,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const AnalyzedFileDto &value) { + static QJsonValue serialize(const AnalyzedFileDto &value) + { QJsonObject jo; serialize_field(jo, analyzedFileKeyPath, value.path); serialize_field(jo, analyzedFileKeyIsSystemHeader, value.isSystemHeader); @@ -806,7 +812,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ChangePasswordFormDto deserialize(const QJsonValue &json) { + static ChangePasswordFormDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, changePasswordFormKeyCurrentPassword), @@ -814,7 +821,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ChangePasswordFormDto &value) { + static QJsonValue serialize(const ChangePasswordFormDto &value) + { QJsonObject jo; serialize_field(jo, changePasswordFormKeyCurrentPassword, value.currentPassword); serialize_field(jo, changePasswordFormKeyNewPassword, value.newPassword); @@ -942,7 +950,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ColumnTypeOptionDto deserialize(const QJsonValue &json) { + static ColumnTypeOptionDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, columnTypeOptionKeyKey), @@ -951,7 +960,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ColumnTypeOptionDto &value) { + static QJsonValue serialize(const ColumnTypeOptionDto &value) + { QJsonObject jo; serialize_field(jo, columnTypeOptionKeyKey, value.key); serialize_field(jo, columnTypeOptionKeyDisplayName, value.displayName); @@ -998,14 +1008,16 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static CommentRequestDto deserialize(const QJsonValue &json) { + static CommentRequestDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, commentRequestKeyText) }; } - static QJsonValue serialize(const CommentRequestDto &value) { + static QJsonValue serialize(const CommentRequestDto &value) + { QJsonObject jo; serialize_field(jo, commentRequestKeyText, value.text); return { jo }; @@ -1046,14 +1058,16 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static CsrfTokenDto deserialize(const QJsonValue &json) { + static CsrfTokenDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, csrfTokenKeyCsrfToken) }; } - static QJsonValue serialize(const CsrfTokenDto &value) { + static QJsonValue serialize(const CsrfTokenDto &value) + { QJsonObject jo; serialize_field(jo, csrfTokenKeyCsrfToken, value.csrfToken); return { jo }; @@ -1098,7 +1112,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static EntityDto deserialize(const QJsonValue &json) { + static EntityDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, entityKeyId), @@ -1109,7 +1124,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const EntityDto &value) { + static QJsonValue serialize(const EntityDto &value) + { QJsonObject jo; serialize_field(jo, entityKeyId, value.id); serialize_field(jo, entityKeyName, value.name); @@ -1170,7 +1186,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ErrorDto deserialize(const QJsonValue &json) { + static ErrorDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, errorKeyDashboardVersionNumber), @@ -1185,7 +1202,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ErrorDto &value) { + static QJsonValue serialize(const ErrorDto &value) + { QJsonObject jo; serialize_field(jo, errorKeyDashboardVersionNumber, value.dashboardVersionNumber); serialize_field(jo, errorKeyType, value.type); @@ -1256,7 +1274,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueCommentDto deserialize(const QJsonValue &json) { + static IssueCommentDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, issueCommentKeyUsername), @@ -1269,7 +1288,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueCommentDto &value) { + static QJsonValue serialize(const IssueCommentDto &value) + { QJsonObject jo; serialize_field(jo, issueCommentKeyUsername, value.username); serialize_field(jo, issueCommentKeyUserDisplayName, value.userDisplayName); @@ -1477,7 +1497,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueSourceLocationDto deserialize(const QJsonValue &json) { + static IssueSourceLocationDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, issueSourceLocationKeyFileName), @@ -1490,7 +1511,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueSourceLocationDto &value) { + static QJsonValue serialize(const IssueSourceLocationDto &value) + { QJsonObject jo; serialize_field(jo, issueSourceLocationKeyFileName, value.fileName); serialize_field(jo, issueSourceLocationKeyRole, value.role); @@ -1550,7 +1572,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueTagDto deserialize(const QJsonValue &json) { + static IssueTagDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, issueTagKeyTag), @@ -1558,7 +1581,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueTagDto &value) { + static QJsonValue serialize(const IssueTagDto &value) + { QJsonObject jo; serialize_field(jo, issueTagKeyTag, value.tag); serialize_field(jo, issueTagKeyColor, value.color); @@ -1607,7 +1631,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueTagTypeDto deserialize(const QJsonValue &json) { + static IssueTagTypeDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, issueTagTypeKeyId), @@ -1619,7 +1644,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueTagTypeDto &value) { + static QJsonValue serialize(const IssueTagTypeDto &value) + { QJsonObject jo; serialize_field(jo, issueTagTypeKeyId, value.id); serialize_field(jo, issueTagTypeKeyText, value.text); @@ -1739,7 +1765,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static MetricDto deserialize(const QJsonValue &json) { + static MetricDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, metricKeyName), @@ -1749,7 +1776,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const MetricDto &value) { + static QJsonValue serialize(const MetricDto &value) + { QJsonObject jo; serialize_field(jo, metricKeyName, value.name); serialize_field(jo, metricKeyDisplayName, value.displayName); @@ -1805,7 +1833,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static MetricValueTableRowDto deserialize(const QJsonValue &json) { + static MetricValueTableRowDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, metricValueTableRowKeyMetric), @@ -1818,7 +1847,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const MetricValueTableRowDto &value) { + static QJsonValue serialize(const MetricValueTableRowDto &value) + { QJsonObject jo; serialize_field(jo, metricValueTableRowKeyMetric, value.metric); serialize_field(jo, metricValueTableRowKeyPath, value.path); @@ -1924,14 +1954,16 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static NamedFilterVisibilityDto deserialize(const QJsonValue &json) { + static NamedFilterVisibilityDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>>(jo, namedFilterVisibilityKeyGroups) }; } - static QJsonValue serialize(const NamedFilterVisibilityDto &value) { + static QJsonValue serialize(const NamedFilterVisibilityDto &value) + { QJsonObject jo; serialize_field(jo, namedFilterVisibilityKeyGroups, value.groups); return { jo }; @@ -1973,7 +2005,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ProjectReferenceDto deserialize(const QJsonValue &json) { + static ProjectReferenceDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, projectReferenceKeyName), @@ -1981,7 +2014,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ProjectReferenceDto &value) { + static QJsonValue serialize(const ProjectReferenceDto &value) + { QJsonObject jo; serialize_field(jo, projectReferenceKeyName, value.name); serialize_field(jo, projectReferenceKeyUrl, value.url); @@ -2027,7 +2061,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static RuleDto deserialize(const QJsonValue &json) { + static RuleDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, ruleKeyName), @@ -2036,7 +2071,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const RuleDto &value) { + static QJsonValue serialize(const RuleDto &value) + { QJsonObject jo; serialize_field(jo, ruleKeyName, value.name); serialize_field(jo, ruleKeyOriginal_name, value.original_name); @@ -2172,7 +2208,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ToolsVersionDto deserialize(const QJsonValue &json) { + static ToolsVersionDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, toolsVersionKeyName), @@ -2181,7 +2218,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ToolsVersionDto &value) { + static QJsonValue serialize(const ToolsVersionDto &value) + { QJsonObject jo; serialize_field(jo, toolsVersionKeyName, value.name); serialize_field(jo, toolsVersionKeyNumber, value.number); @@ -2277,7 +2315,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static VersionKindCountDto deserialize(const QJsonValue &json) { + static VersionKindCountDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, versionKindCountKeyTotal), @@ -2286,7 +2325,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const VersionKindCountDto &value) { + static QJsonValue serialize(const VersionKindCountDto &value) + { QJsonObject jo; serialize_field(jo, versionKindCountKeyTotal, value.Total); serialize_field(jo, versionKindCountKeyAdded, value.Added); @@ -2341,7 +2381,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static AnalysisVersionDto deserialize(const QJsonValue &json) { + static AnalysisVersionDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, analysisVersionKeyDate), @@ -2356,7 +2397,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const AnalysisVersionDto &value) { + static QJsonValue serialize(const AnalysisVersionDto &value) + { QJsonObject jo; serialize_field(jo, analysisVersionKeyDate, value.date); serialize_field(jo, analysisVersionKeyLabel, value.label); @@ -2424,7 +2466,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ApiTokenCreationRequestDto deserialize(const QJsonValue &json) { + static ApiTokenCreationRequestDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, apiTokenCreationRequestKeyPassword), @@ -2434,7 +2477,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ApiTokenCreationRequestDto &value) { + static QJsonValue serialize(const ApiTokenCreationRequestDto &value) + { QJsonObject jo; serialize_field(jo, apiTokenCreationRequestKeyPassword, value.password); serialize_field(jo, apiTokenCreationRequestKeyType, value.type); @@ -2528,7 +2572,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ApiTokenInfoDto deserialize(const QJsonValue &json) { + static ApiTokenInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, apiTokenInfoKeyId), @@ -2547,7 +2592,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ApiTokenInfoDto &value) { + static QJsonValue serialize(const ApiTokenInfoDto &value) + { QJsonObject jo; serialize_field(jo, apiTokenInfoKeyId, value.id); serialize_field(jo, apiTokenInfoKeyUrl, value.url); @@ -2683,7 +2729,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ColumnInfoDto deserialize(const QJsonValue &json) { + static ColumnInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, columnInfoKeyKey), @@ -2699,7 +2746,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ColumnInfoDto &value) { + static QJsonValue serialize(const ColumnInfoDto &value) + { QJsonObject jo; serialize_field(jo, columnInfoKeyKey, value.key); serialize_field(jo, columnInfoKeyHeader, value.header); @@ -2845,7 +2893,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static DashboardInfoDto deserialize(const QJsonValue &json) { + static DashboardInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, dashboardInfoKeyMainUrl), @@ -2866,7 +2915,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const DashboardInfoDto &value) { + static QJsonValue serialize(const DashboardInfoDto &value) + { QJsonObject jo; serialize_field(jo, dashboardInfoKeyMainUrl, value.mainUrl); serialize_field(jo, dashboardInfoKeyDashboardVersion, value.dashboardVersion); @@ -2949,14 +2999,16 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueCommentListDto deserialize(const QJsonValue &json) { + static IssueCommentListDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, issueCommentListKeyComments) }; } - static QJsonValue serialize(const IssueCommentListDto &value) { + static QJsonValue serialize(const IssueCommentListDto &value) + { QJsonObject jo; serialize_field(jo, issueCommentListKeyComments, value.comments); return { jo }; @@ -2999,7 +3051,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueKindInfoDto deserialize(const QJsonValue &json) { + static IssueKindInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, issueKindInfoKeyPrefix), @@ -3008,7 +3061,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueKindInfoDto &value) { + static QJsonValue serialize(const IssueKindInfoDto &value) + { QJsonObject jo; serialize_field(jo, issueKindInfoKeyPrefix, value.prefix); serialize_field(jo, issueKindInfoKeyNiceSingularName, value.niceSingularName); @@ -3085,14 +3139,16 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueTagTypeListDto deserialize(const QJsonValue &json) { + static IssueTagTypeListDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, issueTagTypeListKeyTags) }; } - static QJsonValue serialize(const IssueTagTypeListDto &value) { + static QJsonValue serialize(const IssueTagTypeListDto &value) + { QJsonObject jo; serialize_field(jo, issueTagTypeListKeyTags, value.tags); return { jo }; @@ -3141,7 +3197,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static LineMarkerDto deserialize(const QJsonValue &json) { + static LineMarkerDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, lineMarkerKeyKind), @@ -3156,7 +3213,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const LineMarkerDto &value) { + static QJsonValue serialize(const LineMarkerDto &value) + { QJsonObject jo; serialize_field(jo, lineMarkerKeyKind, value.kind); serialize_field(jo, lineMarkerKeyId, value.id); @@ -3264,7 +3322,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static RepositoryUpdateMessageDto deserialize(const QJsonValue &json) { + static RepositoryUpdateMessageDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, repositoryUpdateMessageKeySeverity), @@ -3272,7 +3331,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const RepositoryUpdateMessageDto &value) { + static QJsonValue serialize(const RepositoryUpdateMessageDto &value) + { QJsonObject jo; serialize_field(jo, repositoryUpdateMessageKeySeverity, value.severity); serialize_field(jo, repositoryUpdateMessageKeyMessage, value.message); @@ -3344,14 +3404,16 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static RuleListDto deserialize(const QJsonValue &json) { + static RuleListDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, ruleListKeyRules) }; } - static QJsonValue serialize(const RuleListDto &value) { + static QJsonValue serialize(const RuleListDto &value) + { QJsonObject jo; serialize_field(jo, ruleListKeyRules, value.rules); return { jo }; @@ -3393,7 +3455,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static SortInfoDto deserialize(const QJsonValue &json) { + static SortInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, sortInfoKeyKey), @@ -3401,7 +3464,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const SortInfoDto &value) { + static QJsonValue serialize(const SortInfoDto &value) + { QJsonObject jo; serialize_field(jo, sortInfoKeyKey, value.key); serialize_field(jo, sortInfoKeyDirection, value.direction); @@ -3476,7 +3540,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static UserRefDto deserialize(const QJsonValue &json) { + static UserRefDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, userRefKeyName), @@ -3486,7 +3551,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const UserRefDto &value) { + static QJsonValue serialize(const UserRefDto &value) + { QJsonObject jo; serialize_field(jo, userRefKeyName, value.name); serialize_field(jo, userRefKeyDisplayName, value.displayName); @@ -3571,7 +3637,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static AnalyzedFileListDto deserialize(const QJsonValue &json) { + static AnalyzedFileListDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, analyzedFileListKeyVersion), @@ -3579,7 +3646,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const AnalyzedFileListDto &value) { + static QJsonValue serialize(const AnalyzedFileListDto &value) + { QJsonObject jo; serialize_field(jo, analyzedFileListKeyVersion, value.version); serialize_field(jo, analyzedFileListKeyRows, value.rows); @@ -3624,7 +3692,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static EntityListDto deserialize(const QJsonValue &json) { + static EntityListDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, entityListKeyVersion), @@ -3632,7 +3701,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const EntityListDto &value) { + static QJsonValue serialize(const EntityListDto &value) + { QJsonObject jo; serialize_field(jo, entityListKeyVersion, value.version); serialize_field(jo, entityListKeyEntities, value.entities); @@ -3679,7 +3749,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static FileViewDto deserialize(const QJsonValue &json) { + static FileViewDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, fileViewKeyFileName), @@ -3689,7 +3760,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const FileViewDto &value) { + static QJsonValue serialize(const FileViewDto &value) + { QJsonObject jo; serialize_field(jo, fileViewKeyFileName, value.fileName); serialize_field(jo, fileViewKeyVersion, value.version); @@ -3745,7 +3817,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueDto deserialize(const QJsonValue &json) { + static IssueDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, issueKeyKind), @@ -3758,7 +3831,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueDto &value) { + static QJsonValue serialize(const IssueDto &value) + { QJsonObject jo; serialize_field(jo, issueKeyKind, value.kind); serialize_field(jo, issueKeyId, value.id); @@ -3862,7 +3936,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static IssueTableDto deserialize(const QJsonValue &json) { + static IssueTableDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, issueTableKeyStartVersion), @@ -3876,7 +3951,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const IssueTableDto &value) { + static QJsonValue serialize(const IssueTableDto &value) + { QJsonObject jo; serialize_field(jo, issueTableKeyStartVersion, value.startVersion); serialize_field(jo, issueTableKeyEndVersion, value.endVersion); @@ -3939,7 +4015,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static MetricListDto deserialize(const QJsonValue &json) { + static MetricListDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, metricListKeyVersion), @@ -3947,7 +4024,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const MetricListDto &value) { + static QJsonValue serialize(const MetricListDto &value) + { QJsonObject jo; serialize_field(jo, metricListKeyVersion, value.version); serialize_field(jo, metricListKeyMetrics, value.metrics); @@ -3995,7 +4073,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static MetricValueRangeDto deserialize(const QJsonValue &json) { + static MetricValueRangeDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, metricValueRangeKeyStartVersion), @@ -4006,7 +4085,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const MetricValueRangeDto &value) { + static QJsonValue serialize(const MetricValueRangeDto &value) + { QJsonObject jo; serialize_field(jo, metricValueRangeKeyStartVersion, value.startVersion); serialize_field(jo, metricValueRangeKeyEndVersion, value.endVersion); @@ -4060,7 +4140,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static MetricValueTableDto deserialize(const QJsonValue &json) { + static MetricValueTableDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, metricValueTableKeyColumns), @@ -4068,7 +4149,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const MetricValueTableDto &value) { + static QJsonValue serialize(const MetricValueTableDto &value) + { QJsonObject jo; serialize_field(jo, metricValueTableKeyColumns, value.columns); serialize_field(jo, metricValueTableKeyRows, value.rows); @@ -4116,7 +4198,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static NamedFilterCreateDto deserialize(const QJsonValue &json) { + static NamedFilterCreateDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, namedFilterCreateKeyDisplayName), @@ -4127,7 +4210,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const NamedFilterCreateDto &value) { + static QJsonValue serialize(const NamedFilterCreateDto &value) + { QJsonObject jo; serialize_field(jo, namedFilterCreateKeyDisplayName, value.displayName); serialize_field(jo, namedFilterCreateKeyKind, value.kind); @@ -4224,7 +4308,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static NamedFilterInfoDto deserialize(const QJsonValue &json) { + static NamedFilterInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, namedFilterInfoKeyKey), @@ -4241,7 +4326,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const NamedFilterInfoDto &value) { + static QJsonValue serialize(const NamedFilterInfoDto &value) + { QJsonObject jo; serialize_field(jo, namedFilterInfoKeyKey, value.key); serialize_field(jo, namedFilterInfoKeyDisplayName, value.displayName); @@ -4363,7 +4449,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static NamedFilterUpdateDto deserialize(const QJsonValue &json) { + static NamedFilterUpdateDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, namedFilterUpdateKeyName), @@ -4373,7 +4460,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const NamedFilterUpdateDto &value) { + static QJsonValue serialize(const NamedFilterUpdateDto &value) + { QJsonObject jo; serialize_field(jo, namedFilterUpdateKeyName, value.name); serialize_field(jo, namedFilterUpdateKeyFilters, value.filters); @@ -4429,7 +4517,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static ProjectInfoDto deserialize(const QJsonValue &json) { + static ProjectInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, projectInfoKeyName), @@ -4442,7 +4531,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const ProjectInfoDto &value) { + static QJsonValue serialize(const ProjectInfoDto &value) + { QJsonObject jo; serialize_field(jo, projectInfoKeyName, value.name); serialize_field(jo, projectInfoKeyIssueFilterHelp, value.issueFilterHelp); @@ -4503,7 +4593,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static RepositoryUpdateResponseDto deserialize(const QJsonValue &json) { + static RepositoryUpdateResponseDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field>(jo, repositoryUpdateResponseKeyMessages), @@ -4512,7 +4603,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const RepositoryUpdateResponseDto &value) { + static QJsonValue serialize(const RepositoryUpdateResponseDto &value) + { QJsonObject jo; serialize_field(jo, repositoryUpdateResponseKeyMessages, value.messages); serialize_field(jo, repositoryUpdateResponseKeyHasErrors, value.hasErrors); @@ -4564,7 +4656,8 @@ namespace Axivion::Internal::Dto { { public: // throws Axivion::Internal::Dto::invalid_dto_exception - static TableInfoDto deserialize(const QJsonValue &json) { + static TableInfoDto deserialize(const QJsonValue &json) + { const QJsonObject jo = toJsonObject(json); return { deserialize_field(jo, tableInfoKeyTableDataUri), @@ -4576,7 +4669,8 @@ namespace Axivion::Internal::Dto { }; } - static QJsonValue serialize(const TableInfoDto &value) { + static QJsonValue serialize(const TableInfoDto &value) + { QJsonObject jo; serialize_field(jo, tableInfoKeyTableDataUri, value.tableDataUri); serialize_field(jo, tableInfoKeyIssueBaseViewUri, value.issueBaseViewUri); diff --git a/src/plugins/axivion/dashboard/dto.h b/src/plugins/axivion/dashboard/dto.h index 8617d2d61df..4543df7d541 100644 --- a/src/plugins/axivion/dashboard/dto.h +++ b/src/plugins/axivion/dashboard/dto.h @@ -175,7 +175,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static AnalyzedFileDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); AnalyzedFileDto( QString path, @@ -253,7 +253,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ChangePasswordFormDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ChangePasswordFormDto( QString currentPassword, @@ -344,7 +344,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ColumnTypeOptionDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ColumnTypeOptionDto( QString key, @@ -370,7 +370,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static CommentRequestDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); CommentRequestDto( QString text @@ -400,7 +400,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static CsrfTokenDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); CsrfTokenDto( QString csrfToken @@ -445,7 +445,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static EntityDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); EntityDto( QString id, @@ -564,7 +564,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ErrorDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ErrorDto( std::optional dashboardVersionNumber, @@ -635,7 +635,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueCommentDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueCommentDto( QString username, @@ -787,7 +787,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueSourceLocationDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueSourceLocationDto( QString fileName, @@ -825,7 +825,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTagDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueTagDto( QString tag, @@ -883,7 +883,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTagTypeDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueTagTypeDto( QString id, @@ -973,7 +973,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); MetricDto( QString name, @@ -1034,7 +1034,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricValueTableRowDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); MetricValueTableRowDto( QString metric, @@ -1104,7 +1104,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterVisibilityDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); NamedFilterVisibilityDto( std::optional> groups @@ -1133,7 +1133,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ProjectReferenceDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ProjectReferenceDto( QString name, @@ -1173,7 +1173,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RuleDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); RuleDto( QString name, @@ -1270,7 +1270,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ToolsVersionDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ToolsVersionDto( QString name, @@ -1340,7 +1340,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static VersionKindCountDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); VersionKindCountDto( qint32 Total, @@ -1450,7 +1450,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static AnalysisVersionDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); AnalysisVersionDto( QString date, @@ -1510,7 +1510,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ApiTokenCreationRequestDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ApiTokenCreationRequestDto( QString password, @@ -1630,7 +1630,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ApiTokenInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ApiTokenInfoDto( QString id, @@ -1757,7 +1757,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ColumnInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ColumnInfoDto( QString key, @@ -1925,7 +1925,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static DashboardInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); DashboardInfoDto( std::optional mainUrl, @@ -1963,7 +1963,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueCommentListDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueCommentListDto( std::vector comments @@ -1999,7 +1999,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueKindInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueKindInfoDto( QString prefix, @@ -2038,7 +2038,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTagTypeListDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueTagTypeListDto( std::vector tags @@ -2130,7 +2130,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static LineMarkerDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); LineMarkerDto( QString kind, @@ -2191,7 +2191,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RepositoryUpdateMessageDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); RepositoryUpdateMessageDto( QString severity, @@ -2228,7 +2228,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RuleListDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); RuleListDto( std::vector rules @@ -2259,7 +2259,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static SortInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); SortInfoDto( QString key, @@ -2317,7 +2317,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static UserRefDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); UserRefDto( QString name, @@ -2363,7 +2363,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static AnalyzedFileListDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); AnalyzedFileListDto( AnalysisVersionDto version, @@ -2393,7 +2393,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static EntityListDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); EntityListDto( std::optional version, @@ -2443,7 +2443,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static FileViewDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); FileViewDto( QString fileName, @@ -2508,7 +2508,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueDto( QString kind, @@ -2624,7 +2624,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static IssueTableDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); IssueTableDto( std::optional startVersion, @@ -2660,7 +2660,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricListDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); MetricListDto( std::optional version, @@ -2713,7 +2713,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricValueRangeDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); MetricValueRangeDto( AnalysisVersionDto startVersion, @@ -2748,7 +2748,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static MetricValueTableDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); MetricValueTableDto( std::vector columns, @@ -2812,7 +2812,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterCreateDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); NamedFilterCreateDto( QString displayName, @@ -2936,7 +2936,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); NamedFilterInfoDto( QString key, @@ -3028,7 +3028,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static NamedFilterUpdateDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); NamedFilterUpdateDto( std::optional name, @@ -3089,7 +3089,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static ProjectInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); ProjectInfoDto( QString name, @@ -3132,7 +3132,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static RepositoryUpdateResponseDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); RepositoryUpdateResponseDto( std::vector messages, @@ -3201,7 +3201,7 @@ namespace Axivion::Internal::Dto // Throws Axivion::Internal::Dto::invalid_dto_exception static TableInfoDto deserialize(const QByteArray &json); - Utils::expected_str deserializeExpected(const QByteArray &json); + static Utils::expected_str deserializeExpected(const QByteArray &json); TableInfoDto( QString tableDataUri, From f8b419138439ab659d230071fea5324c25e50a71 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 20 Feb 2024 18:45:03 +0100 Subject: [PATCH 053/128] Axivion: Show more detailed message on deserialization error Instead of a general deserialization error. Reuse newly added Dto::deserializeExpected() methods. Change-Id: Ie828b54b24659f2168aa7245a98ab92e71e7a3eb Reviewed-by: hjk Reviewed-by: Reviewed-by: Andreas Loth --- src/plugins/axivion/axivionplugin.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index a5da9cdba2c..77261faffc0 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -445,31 +445,34 @@ static Group dtoRecipe(const Storage> &dtoStorage) return DoneResult::Error; }; - const auto onDeserializeSetup = [storage](Async &task) { - const auto deserialize = [](QPromise &promise, const QByteArray &input) { - try { - promise.addResult(DtoType::deserialize(input)); - } catch (const Dto::invalid_dto_exception &) { - promise.future().cancel(); - } + const auto onDeserializeSetup = [storage](Async> &task) { + const auto deserialize = [](QPromise> &promise, const QByteArray &input) { + promise.addResult(DtoType::deserializeExpected(input)); }; task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); task.setConcurrentCallData(deserialize, *storage); }; - const auto onDeserializeDone = [dtoStorage](const Async &task, DoneWith doneWith) { + const auto onDeserializeDone = [dtoStorage](const Async> &task, + DoneWith doneWith) { if (doneWith == DoneWith::Success && task.isResultAvailable()) { - dtoStorage->dtoData = task.result(); + const auto result = task.result(); + if (result) { + dtoStorage->dtoData = *result; + return DoneResult::Success; + } + MessageManager::writeFlashing(QString("Axivion: %1").arg(result.error())); } else { MessageManager::writeFlashing(QString("Axivion: %1") - .arg(Tr::tr("Deserialization of an unexpected Dto structure."))); + .arg(Tr::tr("Unknown Dto structure deserialization error."))); } + return DoneResult::Error; }; return { storage, NetworkQueryTask(onNetworkQuerySetup, onNetworkQueryDone), - AsyncTask(onDeserializeSetup, onDeserializeDone) + AsyncTask>(onDeserializeSetup, onDeserializeDone) }; } From d67290860848f4ade033555f591b2881b37fc2dd Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 21 Feb 2024 10:13:03 +0100 Subject: [PATCH 054/128] TaskTree: Add info about sync/async execution into withLog() Add tests for it. This addresses the 35th point in the bugreport below. Task-number: QTCREATORBUG-28741 Change-Id: Id35d51155e0bfd413c06b3a2c5bb57b8bcea8487 Reviewed-by: hjk Reviewed-by: --- src/libs/solutions/tasking/tasktree.cpp | 53 +++++++++-- tests/auto/solutions/tasking/tst_tasking.cpp | 92 ++++++++++++-------- 2 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 112c6dca75f..c6e87d27746 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -1193,6 +1193,17 @@ const GroupItem stopOnSuccessOrError = workflowPolicy(WorkflowPolicy::StopOnSucc const GroupItem finishAllAndSuccess = workflowPolicy(WorkflowPolicy::FinishAllAndSuccess); const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError); +// Please note the thread_local keyword below guarantees a separate instance per thread. +// The s_activeTaskTrees is currently used internally only and is not exposed in the public API. +// It serves for withLog() implementation now. Add a note here when a new usage is introduced. +static thread_local QList s_activeTaskTrees = {}; + +static TaskTree *activeTaskTree() +{ + QT_ASSERT(s_activeTaskTrees.size(), return nullptr); + return s_activeTaskTrees.back(); +} + DoneResult toDoneResult(bool success) { return success ? DoneResult::Success : DoneResult::Error; @@ -1427,18 +1438,28 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const const auto header = [logName] { return QString("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName); }; - const Storage> storage; + struct LogStorage + { + time_point start; + int asyncCount = 0; + }; + const Storage storage; return Group { storage, onGroupSetup([storage, header] { - *storage = system_clock::now(); + storage->start = system_clock::now(); + storage->asyncCount = activeTaskTree()->asyncCount(); qDebug().noquote() << header() << "started."; }), *this, onGroupDone([storage, header](DoneWith result) { - const auto elapsed = duration_cast(system_clock::now() - *storage); + const auto elapsed = duration_cast(system_clock::now() - storage->start); + const int asyncCountDiff = activeTaskTree()->asyncCount() - storage->asyncCount; + QT_CHECK(asyncCountDiff >= 0); const QMetaEnum doneWithEnum = QMetaEnum::fromType(); - qDebug().noquote().nospace() << header() << " finished with " + const QString syncType = asyncCountDiff ? QString("asynchronously") + : QString("synchronously"); + qDebug().noquote().nospace() << header() << " finished " << syncType << " with " << doneWithEnum.valueToKey(int(result)) << " within " << elapsed.count() << "ms."; }) }; @@ -1453,16 +1474,26 @@ class RuntimeTask; class ExecutionContextActivator { public: - ExecutionContextActivator(RuntimeIteration *iteration) { activateContext(iteration); } - ExecutionContextActivator(RuntimeContainer *container) { activateContext(container); } + ExecutionContextActivator(RuntimeIteration *iteration) { + activateTaskTree(iteration); + activateContext(iteration); + } + ExecutionContextActivator(RuntimeContainer *container) { + activateTaskTree(container); + activateContext(container); + } ~ExecutionContextActivator() { for (int i = m_activeStorages.size() - 1; i >= 0; --i) // iterate in reverse order m_activeStorages[i].m_storageData->threadData().popStorage(); for (int i = m_activeLoops.size() - 1; i >= 0; --i) // iterate in reverse order m_activeLoops[i].m_loopData->threadData().popIteration(); + QT_ASSERT(s_activeTaskTrees.size(), return); + s_activeTaskTrees.pop_back(); } private: + void activateTaskTree(RuntimeIteration *iteration); + void activateTaskTree(RuntimeContainer *container); void activateContext(RuntimeIteration *iteration); void activateContext(RuntimeContainer *container); QList m_activeLoops; @@ -1692,6 +1723,16 @@ static bool isProgressive(RuntimeContainer *container) return iteration ? iteration->m_isProgressive : true; } +void ExecutionContextActivator::activateTaskTree(RuntimeIteration *iteration) +{ + activateTaskTree(iteration->m_container); +} + +void ExecutionContextActivator::activateTaskTree(RuntimeContainer *container) +{ + s_activeTaskTrees.push_back(container->m_containerNode.m_taskTreePrivate->q); +} + void ExecutionContextActivator::activateContext(RuntimeIteration *iteration) { std::optional loop = iteration->loop(); diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index b2e99a7c220..279400f0218 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -58,12 +58,26 @@ enum class ThreadResult }; Q_ENUM_NS(ThreadResult); +enum class Execution +{ + Sync, + Async +}; + } // namespace PrintableEnums using namespace PrintableEnums; using Log = QList>; -using MessageLog = QList>; + +struct Message +{ + QString name; + Handler handler; + std::optional execution = {}; +}; + +using MessageLog = QList; struct CustomStorage { @@ -2960,8 +2974,8 @@ void tst_Tasking::testTree_data() MessageLog { {"Group 1", Handler::Setup}, {"Task 1", Handler::Setup}, - {"Task 1", Handler::Success}, - {"Group 1", Handler::Success} + {"Task 1", Handler::Success, Execution::Async}, + {"Group 1", Handler::Success, Execution::Async} } }; const TestData testError { @@ -2982,8 +2996,8 @@ void tst_Tasking::testTree_data() MessageLog { {"Group 1", Handler::Setup}, {"Task 1", Handler::Setup}, - {"Task 1", Handler::Error}, - {"Group 1", Handler::Error} + {"Task 1", Handler::Error, Execution::Async}, + {"Group 1", Handler::Error, Execution::Async} } }; const TestData testCancel { @@ -3009,9 +3023,9 @@ void tst_Tasking::testTree_data() {"Group 1", Handler::Setup}, {"Task 1", Handler::Setup}, {"Task 2", Handler::Setup}, - {"Task 2", Handler::Error}, - {"Task 1", Handler::Canceled}, - {"Group 1", Handler::Canceled} + {"Task 2", Handler::Error, Execution::Sync}, + {"Task 1", Handler::Canceled, Execution::Sync}, + {"Group 1", Handler::Canceled, Execution::Sync} } }; @@ -3102,13 +3116,13 @@ private: static const QList s_logHandlers = {Handler::Setup, Handler::Success, Handler::Error, Handler::Canceled}; -static QString handlerToName(Handler handler) +static QString messageInfix(const Message &message) { - if (handler == Handler::Setup) + if (message.handler == Handler::Setup) return "started"; DoneWith doneWith; - switch (handler) { + switch (message.handler) { case Handler::Success: doneWith = DoneWith::Success; break; @@ -3121,41 +3135,46 @@ static QString handlerToName(Handler handler) default: return {}; } + + const QString execution = message.execution == Execution::Async ? "asynchronously" + : "synchronously"; const QMetaEnum doneWithEnum = QMetaEnum::fromType(); - return QString("finished with %1").arg(doneWithEnum.valueToKey(int(doneWith))); + return QString("finished %1 with %2").arg(execution, doneWithEnum.valueToKey(int(doneWith))); } -static bool matchesLogPattern(const QString &log, const QPair &pattern) +static bool matchesLogPattern(const QString &log, const Message &message) { - QStringView message(log); + QStringView logView(log); const static QLatin1StringView part1("TASK TREE LOG ["); - if (!message.startsWith(part1)) + if (!logView.startsWith(part1)) return false; - message = message.mid(part1.size()); + logView = logView.mid(part1.size()); const static QLatin1StringView part2("HH:mm:ss.zzz"); // check only size - message = message.mid(part2.size()); - const QString part3 = QString("] \"%1\" ").arg(pattern.first); - if (!message.startsWith(part3)) + logView = logView.mid(part2.size()); + const QString part3 = QString("] \"%1\" ").arg(message.name); + if (!logView.startsWith(part3)) return false; - message = message.mid(part3.size()); - const QString part4(handlerToName(pattern.second)); - if (!message.startsWith(part4)) + logView = logView.mid(part3.size()); + const QString part4(messageInfix(message)); + if (!logView.startsWith(part4)) return false; - message = message.mid(part4.size()); - if (pattern.second == Handler::Setup) - return message == QLatin1StringView("."); + logView = logView.mid(part4.size()); + if (message.handler == Handler::Setup) + return logView == QLatin1StringView("."); const static QLatin1StringView part5(" within "); - if (!message.startsWith(part5)) + if (!logView.startsWith(part5)) return false; - return message.endsWith(QLatin1StringView("ms.")); + return logView.endsWith(QLatin1StringView("ms.")); } +const QMetaEnum s_handlerEnum = QMetaEnum::fromType(); + void tst_Tasking::testTree() { QFETCH(TestData, testData); @@ -3187,15 +3206,18 @@ void tst_Tasking::testTree() for (int i = 0; i < s_messages.count(); ++i) { const auto &message = testData.messageLog->at(i); - QVERIFY(s_logHandlers.contains(message.second)); - const QString &log = s_messages.at(i); - if (!matchesLogPattern(log, message)) { - const QString failMessage - = QString("The log message at index %1: \"%2\" doesn't match the expected " - "pattern: \"%3\" %4.").arg(i) - .arg(log, message.first, handlerToName(message.second)); - QFAIL(failMessage.toUtf8()); + QVERIFY2(s_logHandlers.contains(message.handler), qPrintable(QString( + "The expected log message at index %1 contains invalid %2 handler.") + .arg(i).arg(s_handlerEnum.valueToKey(int(message.handler))))); + if (message.handler != Handler::Setup) { + QVERIFY2(message.execution, qPrintable(QString( + "The expected log message at index %1 of %2 type doesn't specify the required " + "execution mode.").arg(i).arg(s_handlerEnum.valueToKey(int(message.handler))))); } + const QString &log = s_messages.at(i); + QVERIFY2(matchesLogPattern(log, message), qPrintable(QString( + "The log message at index %1: \"%2\" doesn't match the expected pattern: " + "\"%3\" %4.").arg(i).arg(log, message.name, messageInfix(message)))); } } } From d02e75e7ab8baa95aabfa2dd6d978269c11441e9 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 21 Feb 2024 15:32:30 +0100 Subject: [PATCH 055/128] Axivion: Add more error messages on different failures Change-Id: Ibfb77b5921a58ba2d439a6d2e313538eb8484c3b Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 77261faffc0..0f39be06bd0 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -707,12 +707,18 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) } const auto onTaskTreeSetup = [this, projectName](TaskTree &taskTree) { - if (!m_dashboardInfo) + if (!m_dashboardInfo) { + MessageManager::writeDisrupting(QString("Axivion: %1") + .arg(Tr::tr("Fetching DashboardInfo error."))); return SetupResult::StopWithError; + } const auto it = m_dashboardInfo->projectUrls.constFind(projectName); - if (it == m_dashboardInfo->projectUrls.constEnd()) + if (it == m_dashboardInfo->projectUrls.constEnd()) { + MessageManager::writeDisrupting(QString("Axivion: %1") + .arg(Tr::tr("The DashboardInfo doesn't contain project \"%1\".").arg(projectName))); return SetupResult::StopWithError; + } const auto handler = [this](const Dto::ProjectInfoDto &data) { m_currentProjectInfo = data; From 862795d69c1e7eca8d0952607c36ec8f860e8b6b Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 21 Feb 2024 16:50:34 +0100 Subject: [PATCH 056/128] Axivion: Ignore the failure on storing the new ApiToken in QtKeyChain Change-Id: Iefd25e07e2f028d32802cf68d69cf46d17ebee22 Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 0f39be06bd0..79a349fed66 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -566,7 +566,13 @@ static Group authorizationRecipe() return SetupResult::Continue; }; const auto onSetCredentialDone = [](const CredentialQuery &credential) { - MessageManager::writeDisrupting(QString("Axivion: %1").arg(credential.errorString())); + const QString keyChainMessage = credential.errorString().isEmpty() ? QString() + : QString(" %1").arg(Tr::tr("Key chain message: \"%1\".").arg(credential.errorString())); + MessageManager::writeDisrupting(QString("Axivion: %1") + .arg(Tr::tr("The ApiToken cannot be stored in a secure way.") + keyChainMessage)); + // TODO: We are multiplying the ApiTokens on Axivion server for each Creator run, + // but at least things should continue to work OK in the current session. + return DoneResult::Success; }; return { From a18522334c2770936cd23d6a66448ceb977a4f35 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 9 Feb 2024 06:11:27 +0100 Subject: [PATCH 057/128] TextEditor: fix multi text cursor unindent via backspace Change-Id: Iec2e9251b977ccbd7433009ac3e706a9327c704c (cherry picked from commit 971bcb1a5abd396819510644e55ef437978adcd5) Reviewed-by: David Schulz --- src/plugins/texteditor/texteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index e50311b1371..c8ef69c63ef 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -6845,7 +6845,7 @@ void TextEditorWidgetPrivate::handleBackspaceKey() if (cursorWithinSnippet) m_snippetOverlay->accept(); cursorWithinSnippet = false; - q->unindent(); + c = m_document->unindent(MultiTextCursor({c})).mainCursor(); } handled = true; } From 32cad6d3892abc97ce939cc40beb382897c07c73 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 22 Feb 2024 07:18:19 +0100 Subject: [PATCH 058/128] Tests: Fix build with Qt6.2 Amends d67290860848f4ade033555f591b2881b37fc2dd. Change-Id: Ie80c69ecb1e96a5c86800e8f431f01f6ed611e6d Reviewed-by: Jarek Kobus --- tests/auto/solutions/tasking/tst_tasking.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 279400f0218..ee1972c0616 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -3145,12 +3145,12 @@ static QString messageInfix(const Message &message) static bool matchesLogPattern(const QString &log, const Message &message) { QStringView logView(log); - const static QLatin1StringView part1("TASK TREE LOG ["); + const static QLatin1String part1("TASK TREE LOG ["); if (!logView.startsWith(part1)) return false; logView = logView.mid(part1.size()); - const static QLatin1StringView part2("HH:mm:ss.zzz"); // check only size + const static QLatin1String part2("HH:mm:ss.zzz"); // check only size logView = logView.mid(part2.size()); const QString part3 = QString("] \"%1\" ").arg(message.name); @@ -3164,13 +3164,13 @@ static bool matchesLogPattern(const QString &log, const Message &message) logView = logView.mid(part4.size()); if (message.handler == Handler::Setup) - return logView == QLatin1StringView("."); + return logView == QLatin1String("."); - const static QLatin1StringView part5(" within "); + const static QLatin1String part5(" within "); if (!logView.startsWith(part5)) return false; - return logView.endsWith(QLatin1StringView("ms.")); + return logView.endsWith(QLatin1String("ms.")); } const QMetaEnum s_handlerEnum = QMetaEnum::fromType(); From a0517bca48104c35b9498a171064c1906cdb5755 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 21 Feb 2024 17:20:17 +0100 Subject: [PATCH 059/128] Fix Axivion tr context Prefix with QtC:: Change-Id: I0cc6b2e4a679c92235ba5c3f471a13f14057418a Reviewed-by: Christian Stenger --- share/qtcreator/translations/qtcreator_fr.ts | 2 +- src/plugins/axivion/axiviontr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index adc57cc41ef..6f9ade5c4d3 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -798,7 +798,7 @@ Une valeur positive augmente la réverbération pour les hautes fréquences et - Axivion + QtC::Axivion Project: Projet : diff --git a/src/plugins/axivion/axiviontr.h b/src/plugins/axivion/axiviontr.h index 1f3475cb456..48798aaa2ad 100644 --- a/src/plugins/axivion/axiviontr.h +++ b/src/plugins/axivion/axiviontr.h @@ -9,7 +9,7 @@ namespace Axivion { struct Tr { - Q_DECLARE_TR_FUNCTIONS(Axivion) + Q_DECLARE_TR_FUNCTIONS(QtC::Axivion) }; } // Axivion From e0c8982b7e89274fa1188bae66b4da59ae2d4009 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 15 Feb 2024 14:43:07 +0100 Subject: [PATCH 060/128] Core: Add Proxy authentication Dialog Change-Id: If48387e9adb9ca9b979dce71e949af1d7e1956cc Reviewed-by: Eike Ziller --- src/plugins/coreplugin/coreplugin.cpp | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 76a808d9a60..4b45c3e6f42 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -41,6 +43,7 @@ #include #include +#include #include #include #include @@ -136,6 +139,30 @@ void CorePlugin::loadMimeFromPlugin(const ExtensionSystem::PluginSpec *plugin) Utils::addMimeTypes(plugin->name() + ".mimetypes", mimetypeString.trimmed().toUtf8()); } +static void initProxyAuthDialog() +{ + QObject::connect(Utils::NetworkAccessManager::instance(), + &QNetworkAccessManager::proxyAuthenticationRequired, + Utils::NetworkAccessManager::instance(), + [](const QNetworkProxy &, QAuthenticator *authenticator) { + static bool doNotAskAgain = false; + + std::optional> answer + = Utils::PasswordDialog::getUserAndPassword( + Tr::tr("Proxy Authentication Required"), + authenticator->realm(), + Tr::tr("Do not ask again."), + {}, + &doNotAskAgain, + Core::ICore::dialogParent()); + + if (answer) { + authenticator->setUser(answer->first); + authenticator->setPassword(answer->second); + } + }); +} + bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) { // register all mime types from all plugins @@ -145,6 +172,8 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) loadMimeFromPlugin(plugin); } + initProxyAuthDialog(); + if (ThemeEntry::availableThemes().isEmpty()) { *errorMessage = Tr::tr("No themes found in installation."); return false; From bdbe585e59d8347eec0fb195daed683743f91f32 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 22 Feb 2024 10:48:09 +0100 Subject: [PATCH 061/128] Appmanager: Fix translatable strings Added missing full stops and capitalization. Changed "Appman" and "AppMan" to "Application Manager". It is "Qt Application Manager". "appman" (all lowercase) is the daemon executable, which I think we never actual refer to in the UI. Change-Id: Icb9a192084c13f96d9faef0a3d4617a4d194c624 Reviewed-by: Leena Miettinen Reviewed-by: --- .../appmanagercmakepackagestep.cpp | 2 +- .../appmanagerdeployconfigurationfactory.cpp | 2 +- .../appmanagerdeploypackagestep.cpp | 4 ++-- .../appmanagerrunconfiguration.cpp | 4 ++-- .../qtapplicationmanager/appmanagerruncontrol.cpp | 6 +++--- .../qtapplicationmanager/appmanagerstringaspect.cpp | 12 ++++++------ 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/plugins/qtapplicationmanager/appmanagercmakepackagestep.cpp b/src/plugins/qtapplicationmanager/appmanagercmakepackagestep.cpp index fee8cd49718..94f91a15856 100644 --- a/src/plugins/qtapplicationmanager/appmanagercmakepackagestep.cpp +++ b/src/plugins/qtapplicationmanager/appmanagercmakepackagestep.cpp @@ -42,7 +42,7 @@ public: QObject::connect(step->project(), &Project::displayNameChanged, step, updaterSlot); }); - setDisplayName(Tr::tr("Create Appman package with CMake")); + setDisplayName(Tr::tr("Create Application Manager package with CMake")); setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); } }; diff --git a/src/plugins/qtapplicationmanager/appmanagerdeployconfigurationfactory.cpp b/src/plugins/qtapplicationmanager/appmanagerdeployconfigurationfactory.cpp index 8b2792edba2..ed524cae286 100644 --- a/src/plugins/qtapplicationmanager/appmanagerdeployconfigurationfactory.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerdeployconfigurationfactory.cpp @@ -33,7 +33,7 @@ public: AppManagerDeployConfigurationFactory() { setConfigBaseId(Constants::DEPLOYCONFIGURATION_ID); - setDefaultDisplayName(Tr::tr("Automatic AppMan Deploy Configuration")); + setDefaultDisplayName(Tr::tr("Automatic Application Manager Deploy Configuration")); addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); addSupportedTargetDeviceType(RemoteLinux::Constants::GenericLinuxOsType); addSupportedTargetDeviceType(Qdb::Constants::QdbLinuxOsType); diff --git a/src/plugins/qtapplicationmanager/appmanagerdeploypackagestep.cpp b/src/plugins/qtapplicationmanager/appmanagerdeploypackagestep.cpp index da6c6052008..6263028a6e4 100644 --- a/src/plugins/qtapplicationmanager/appmanagerdeploypackagestep.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerdeploypackagestep.cpp @@ -95,9 +95,9 @@ private: }; const auto onDone = [this](DoneWith result) { if (result == DoneWith::Success) - emit addOutput(Tr::tr("Uploading finished"), OutputFormat::NormalMessage); + emit addOutput(Tr::tr("Uploading finished."), OutputFormat::NormalMessage); else - emit addOutput(Tr::tr("Uploading failed"), OutputFormat::ErrorMessage); + emit addOutput(Tr::tr("Uploading failed."), OutputFormat::ErrorMessage); }; return FileStreamerTask(onSetup, onDone); } diff --git a/src/plugins/qtapplicationmanager/appmanagerrunconfiguration.cpp b/src/plugins/qtapplicationmanager/appmanagerrunconfiguration.cpp index 77bddffb055..bf901128f7b 100644 --- a/src/plugins/qtapplicationmanager/appmanagerrunconfiguration.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerrunconfiguration.cpp @@ -33,7 +33,7 @@ public: AppManagerRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - setDefaultDisplayName(Tr::tr("Run an Appman Package")); + setDefaultDisplayName(Tr::tr("Run an Application Manager Package")); setUpdater([this, target] { QList tis = TargetInformation::readFromProject(target, buildKey()); @@ -66,7 +66,7 @@ public: AppManagerRunAndDebugConfiguration(Target *target, Id id) : AppManagerRunConfiguration(target, id) { - setDefaultDisplayName(Tr::tr("Run and Debug an Appman Package")); + setDefaultDisplayName(Tr::tr("Run and Debug an Application Manager Package")); environment.addPreferredBaseEnvironment(Tr::tr("Clean Environment"), {}); } diff --git a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp index 9c485536a44..5780b6043af 100644 --- a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp @@ -48,7 +48,7 @@ public: { setId("ApplicationManagerPlugin.Run.TargetRunner"); connect(this, &RunWorker::stopped, this, [this, runControl] { - appendMessage(Tr::tr("%1 exited").arg(runControl->runnable().command.toUserOutput()), + appendMessage(Tr::tr("%1 exited.").arg(runControl->runnable().command.toUserOutput()), OutputFormat::NormalMessageFormat); }); @@ -170,8 +170,8 @@ public: setProcessMode(ProcessMode::Writer); setCommandLine(cmd); - appendMessage(Tr::tr("Starting AppMan Debugging..."), NormalMessageFormat); - appendMessage(Tr::tr("Using: %1").arg(cmd.toUserOutput()), NormalMessageFormat); + appendMessage(Tr::tr("Starting Application Manager debugging..."), NormalMessageFormat); + appendMessage(Tr::tr("Using: %1.").arg(cmd.toUserOutput()), NormalMessageFormat); }); } diff --git a/src/plugins/qtapplicationmanager/appmanagerstringaspect.cpp b/src/plugins/qtapplicationmanager/appmanagerstringaspect.cpp index 23645224c38..6127a7658a9 100644 --- a/src/plugins/qtapplicationmanager/appmanagerstringaspect.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerstringaspect.cpp @@ -24,7 +24,7 @@ AppManagerIdAspect::AppManagerIdAspect(Utils::AspectContainer *container) { setSettingsKey("ApplicationManagerPlugin.ApplicationId"); setDisplayStyle(StringAspect::LineEditDisplay); - setLabelText(Tr::tr("Application id:")); + setLabelText(Tr::tr("Application ID:")); // setReadOnly(true); } @@ -33,9 +33,9 @@ AppManagerInstanceIdAspect::AppManagerInstanceIdAspect(Utils::AspectContainer *c { setSettingsKey("ApplicationManagerPlugin.InstanceId"); setDisplayStyle(StringAspect::LineEditDisplay); - setLabelText(Tr::tr("AppMan instance id:")); + setLabelText(Tr::tr("Application Manager instance ID:")); - makeCheckable(Utils::CheckBoxPlacement::Right, Tr::tr("Default Instance"), + makeCheckable(Utils::CheckBoxPlacement::Right, Tr::tr("Default instance"), "ApplicationManagerPlugin.InstanceIdDefault"); setChecked(true); @@ -52,16 +52,16 @@ AppManagerDocumentUrlAspect::AppManagerDocumentUrlAspect(Utils::AspectContainer { setSettingsKey("ApplicationManagerPlugin.DocumentUrl"); setDisplayStyle(StringAspect::LineEditDisplay); - setLabelText(Tr::tr("Document url:")); + setLabelText(Tr::tr("Document URL:")); } AppManagerCustomizeAspect::AppManagerCustomizeAspect(Utils::AspectContainer *container) : BoolAspect(container) { setSettingsKey("ApplicationManagerPlugin.CustomizeStep"); - setLabelText(Tr::tr("Customize Step")); + setLabelText(Tr::tr("Customize step")); setToolTip(Tr::tr("Disables the automatic updates based on the current run configuration and " - "allows customizing the values")); + "allows customizing the values.")); } AppManagerControllerAspect::AppManagerControllerAspect(Utils::AspectContainer *container) From 455a9986df67ed23eaf67784c30ca763082feb61 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 21 Feb 2024 17:20:58 +0100 Subject: [PATCH 062/128] QmlDesigner: Use qsTr instead of qsTrId The latter is used nowhere else, so stay consistent Change-Id: Iaaf30d8d4c283363a05011a868fc75c773c2ed3c Reviewed-by: Reviewed-by: Alessandro Portale --- share/qtcreator/qmldesigner/welcomepage/TourRestartButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/welcomepage/TourRestartButton.qml b/share/qtcreator/qmldesigner/welcomepage/TourRestartButton.qml index 55f367ffb9c..d81dafe7bb3 100644 --- a/share/qtcreator/qmldesigner/welcomepage/TourRestartButton.qml +++ b/share/qtcreator/qmldesigner/welcomepage/TourRestartButton.qml @@ -18,7 +18,7 @@ Rectangle { Text { id: text2 color: "#ffffff" - text: qsTrId("Restart") + text: qsTr("Restart") anchors.verticalCenter: parent.verticalCenter font.pixelSize: 12 anchors.horizontalCenter: parent.horizontalCenter From ea26cd13b97e4bc628f75fb115394e1264382a3e Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Tue, 20 Feb 2024 12:17:28 +0200 Subject: [PATCH 063/128] Android: get essential platforms and build-tools packages from BuiltWith The commit 1180d5b8a270cfe7bd1c501c892d1c38ee7425de added information about the ndk and android api level used at build time, ndk version has already been accounted for in b73d6f3be80461f7e72326d622a07f8d2dfee10e. This now accounts for "platforms;android-xx" and "build-tools;xx.x.x" packages. Fixes: QTCREATORBUG-30404 Change-Id: I78b8885b88294404bc29c41a7b9491a331fcd709 Reviewed-by: Alessandro Portale --- src/plugins/android/androidconfigurations.cpp | 59 ++++++++++++++++++- src/plugins/android/androidconstants.h | 2 + 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 21a20ff645c..fae6c64bbe3 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -126,6 +126,15 @@ static QString ndkPackageMarker() return QLatin1String(Constants::ndkPackageName) + ";"; } +static QString platformsPackageMarker() +{ + return QLatin1String(Constants::platformsPackageName) + ";"; +} + +static QString buildToolsPackageMarker() +{ + return QLatin1String(Constants::buildToolsPackageName) + ";"; +} ////////////////////////////////// // AndroidConfig @@ -951,15 +960,59 @@ bool AndroidConfig::sdkToolsOk() const return exists && writable && sdkToolsExist; } +static QStringList packagesExcludingBuiltWithDefaults(const QStringList &packages) +{ + return Utils::filtered(packages, [] (const QString &p) { + return !p.startsWith(ndkPackageMarker()) && !p.startsWith(platformsPackageMarker()) + && !p.startsWith(buildToolsPackageMarker()); }); +} + +static QString essentialBuiltWithBuildToolsPackage(int builtWithApiVersion) +{ + // For build-tools, to avoid the situation of potentially having the essential packages + // invalidated whenever a new minor version is released, check if any version with major + // version matching builtWith apiVersion and use it as essential, otherwise use the any + // other one that has an minimum major version of builtWith apiVersion. + const BuildToolsList buildTools = + AndroidConfigurations::sdkManager()->filteredBuildTools(builtWithApiVersion); + const BuildToolsList apiBuildTools + = Utils::filtered(buildTools, [builtWithApiVersion] (const BuildTools *pkg) { + return pkg->revision().majorVersion() == builtWithApiVersion; }); + const QString installedBuildTool = [apiBuildTools] () -> QString { + for (const BuildTools *pkg : apiBuildTools) { + if (pkg->state() == AndroidSdkPackage::Installed) + return pkg->sdkStylePath(); + } + return {}; + }(); + + if (installedBuildTool.isEmpty()) { + if (!apiBuildTools.isEmpty()) + return apiBuildTools.first()->sdkStylePath(); + else if (!buildTools.isEmpty()) + return buildTools.first()->sdkStylePath(); + // This means there's something wrong with sdkmanager, return a default version anyway + else + return buildToolsPackageMarker() + QString::number(builtWithApiVersion) + ".0.0"; + } + + return installedBuildTool; +} + QStringList AndroidConfig::essentialsFromQtVersion(const QtVersion &version) const { if (auto androidQtVersion = dynamic_cast(&version)) { bool ok; const AndroidQtVersion::BuiltWith bw = androidQtVersion->builtWith(&ok); if (ok) { - const QString ndkPackage = ndkPackageMarker() + bw.ndkVersion.toString(); - return QStringList(ndkPackage) - + packagesWithoutNdks(m_defaultSdkDepends.essentialPackages); + QStringList builtWithPackages; + builtWithPackages.append(ndkPackageMarker() + bw.ndkVersion.toString()); + const QString apiVersion = QString::number(bw.apiVersion); + builtWithPackages.append(platformsPackageMarker() + "android-" + apiVersion); + builtWithPackages.append(essentialBuiltWithBuildToolsPackage(bw.apiVersion)); + + return builtWithPackages + packagesExcludingBuiltWithDefaults( + m_defaultSdkDepends.essentialPackages); } } diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h index e2b15e6358d..813b6a924c0 100644 --- a/src/plugins/android/androidconstants.h +++ b/src/plugins/android/androidconstants.h @@ -78,6 +78,8 @@ const Utils::Id AndroidAvdPath = "AndroidAvdPath"; // SDK Tools const char cmdlineToolsName[] = "cmdline-tools"; const char ndkPackageName[] = "ndk"; +const char platformsPackageName[] = "platforms"; +const char buildToolsPackageName[] = "build-tools"; // For AndroidQtVersion const char ArmToolsDisplayName[] = "arm"; From c400664ab277d6012619e25cdbe7ca6083831aad Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 22 Feb 2024 12:28:59 +0100 Subject: [PATCH 064/128] Axivion: Don't report error on semi working setup so intrusively Might be annoying when typing in filter field when storing keychain isn't full functional. Change-Id: I36a1a8e8e8eefcea381a6392e3df032648ed8dc4 Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 79a349fed66..3e6a9814bce 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -568,7 +568,7 @@ static Group authorizationRecipe() const auto onSetCredentialDone = [](const CredentialQuery &credential) { const QString keyChainMessage = credential.errorString().isEmpty() ? QString() : QString(" %1").arg(Tr::tr("Key chain message: \"%1\".").arg(credential.errorString())); - MessageManager::writeDisrupting(QString("Axivion: %1") + MessageManager::writeFlashing(QString("Axivion: %1") .arg(Tr::tr("The ApiToken cannot be stored in a secure way.") + keyChainMessage)); // TODO: We are multiplying the ApiTokens on Axivion server for each Creator run, // but at least things should continue to work OK in the current session. From c829bf76436771b5aa793e8c95ea572ec9d7cb20 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 22 Feb 2024 12:38:01 +0100 Subject: [PATCH 065/128] Core: Small tr fix Use "convert to", and the standard is ASCII Change-Id: I0a687f9bcb8f2c97932a03b45e10a80adbd993dc Reviewed-by: Leena Miettinen --- src/plugins/coreplugin/coreplugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 4b45c3e6f42..c5623134814 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -263,9 +263,9 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) [] { return QUuid::createUuid().toString(); }); expander->registerPrefix("#:", Tr::tr("A comment."), [](const QString &) { return QString(); }); - expander->registerPrefix("Asciify:", Tr::tr("Convert string into pure ascii."), - [expander] (const QString &s) { - return asciify(expander->expand(s)); }); + expander->registerPrefix("Asciify:", + Tr::tr("Convert string to pure ASCII."), + [expander](const QString &s) { return asciify(expander->expand(s)); }); Utils::PathChooser::setAboutToShowContextMenuHandler(&CorePlugin::addToPathChooserContextMenu); From ecf1ada3f564bfd9d4125248f3eb81bbae0ccdcd Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 22 Feb 2024 10:33:40 +0100 Subject: [PATCH 066/128] Axivion: Fix path filter query If the issue kind has multiple path columns filtering by path failed as it used just the single path filter. Use common approach for all. Fixes: QTCREATORBUG-30420 Change-Id: I60460cebdc4a39b9af57c4f1331cb8cb9eedb67a Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 3e6a9814bce..5034da51ffb 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -164,7 +164,7 @@ QString IssueListSearch::toQuery() const QString::fromUtf8((QUrl::toPercentEncoding(owner))))); } if (!filter_path.isEmpty()) { - result.append(QString("&filter_path=%1").arg( + result.append(QString("&filter_any path=%1").arg( QString::fromUtf8(QUrl::toPercentEncoding(filter_path)))); } if (!state.isEmpty()) From 1531d2f010c063def258ffff93ffcb78b3bc3aca Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 22 Feb 2024 13:33:32 +0100 Subject: [PATCH 067/128] Terminal: Only react to Double click of Left Button Change-Id: I94131389851b2939c2a3e91d9d9a70890928f882 Reviewed-by: David Schulz --- src/libs/solutions/terminal/terminalview.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/solutions/terminal/terminalview.cpp b/src/libs/solutions/terminal/terminalview.cpp index 08a85d4f119..0cebe6d9b99 100644 --- a/src/libs/solutions/terminal/terminalview.cpp +++ b/src/libs/solutions/terminal/terminalview.cpp @@ -1199,6 +1199,9 @@ void TerminalView::mouseReleaseEvent(QMouseEvent *event) void TerminalView::mouseDoubleClickEvent(QMouseEvent *event) { + if (event->button() != Qt::LeftButton) + return; + if (d->m_allowMouseTracking) { d->m_surface->mouseMove(toGridPos(event), event->modifiers()); d->m_surface->mouseButton(event->button(), true, event->modifiers()); From 47b5d7eab3824c808ff38c2a483912ec251ad272 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 21 Feb 2024 18:45:16 +0100 Subject: [PATCH 068/128] Axivion: Handle the error on key read like we do on key storing Change-Id: Icb53d4e005f9bfa2dd1a20c763def1e6ac04e784 Reviewed-by: hjk --- src/plugins/axivion/axivionplugin.cpp | 35 +++++++++++++++++++++------ src/plugins/axivion/credentialquery.h | 1 + 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 5034da51ffb..10364b86f3f 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -476,6 +476,27 @@ static Group dtoRecipe(const Storage> &dtoStorage) }; } +static QString credentialOperationMessage(CredentialOperation operation) +{ + switch (operation) { + case CredentialOperation::Get: + return Tr::tr("The ApiToken cannot be read in a secure way."); + case CredentialOperation::Set: + return Tr::tr("The ApiToken cannot be stored in a secure way."); + case CredentialOperation::Delete: + return Tr::tr("The ApiToken cannot be deleted in a secure way."); + } + return {}; +} + +static void handleCredentialError(const CredentialQuery &credential) +{ + const QString keyChainMessage = credential.errorString().isEmpty() ? QString() + : QString(" %1").arg(Tr::tr("Key chain message: \"%1\".").arg(credential.errorString())); + MessageManager::writeFlashing(QString("Axivion: %1") + .arg(credentialOperationMessage(credential.operation()) + keyChainMessage)); +} + static Group authorizationRecipe() { const Storage> unauthorizedDashboardStorage; @@ -509,7 +530,10 @@ static Group authorizationRecipe() if (result == DoneWith::Success) dd->m_apiToken = credential.data(); else - MessageManager::writeDisrupting(QString("Axivion: %1").arg(credential.errorString())); + handleCredentialError(credential); + // TODO: In case of an error we are multiplying the ApiTokens on Axivion server for each + // Creator run, but at least things should continue to work OK in the current session. + return DoneResult::Success; }; const Storage passwordStorage; @@ -566,12 +590,9 @@ static Group authorizationRecipe() return SetupResult::Continue; }; const auto onSetCredentialDone = [](const CredentialQuery &credential) { - const QString keyChainMessage = credential.errorString().isEmpty() ? QString() - : QString(" %1").arg(Tr::tr("Key chain message: \"%1\".").arg(credential.errorString())); - MessageManager::writeFlashing(QString("Axivion: %1") - .arg(Tr::tr("The ApiToken cannot be stored in a secure way.") + keyChainMessage)); - // TODO: We are multiplying the ApiTokens on Axivion server for each Creator run, - // but at least things should continue to work OK in the current session. + handleCredentialError(credential); + // TODO: In case of an error we are multiplying the ApiTokens on Axivion server for each + // Creator run, but at least things should continue to work OK in the current session. return DoneResult::Success; }; diff --git a/src/plugins/axivion/credentialquery.h b/src/plugins/axivion/credentialquery.h index 6b23c5f4669..b5ea338c3bd 100644 --- a/src/plugins/axivion/credentialquery.h +++ b/src/plugins/axivion/credentialquery.h @@ -17,6 +17,7 @@ public: void setKey(const QString &key) { m_key = key; } void setData(const QByteArray &data) { m_data = data; } + CredentialOperation operation() const { return m_operation; } std::optional data() const { return m_data; } QString errorString() const { return m_errorString; } From 957319fdbc50cfcb132bedffb1ddc247eef7e093 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 22 Feb 2024 14:51:01 +0100 Subject: [PATCH 069/128] Axivion: Make the dashboard and issues pane button checkable Change-Id: Idec60b73fa4bfe0057e7d8f9eef3fa403ce6a5cd Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionoutputpane.cpp | 47 +++++++++++++---------- src/plugins/axivion/axivionoutputpane.h | 5 +++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 42046f78b14..ae4a5057424 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -623,6 +623,32 @@ AxivionOutputPane::AxivionOutputPane(QObject *parent) m_outputWidget->addWidget(dashboardWidget); IssuesWidget *issuesWidget = new IssuesWidget(m_outputWidget); m_outputWidget->addWidget(issuesWidget); + + m_showDashboard = new QToolButton(m_outputWidget); + m_showDashboard->setIcon(Icons::HOME_TOOLBAR.icon()); + m_showDashboard->setToolTip(Tr::tr("Show dashboard")); + m_showDashboard->setCheckable(true); + m_showDashboard->setChecked(true); + connect(m_showDashboard, &QToolButton::clicked, this, [this] { + QTC_ASSERT(m_outputWidget, return); + m_outputWidget->setCurrentIndex(0); + }); + + m_showIssues = new QToolButton(m_outputWidget); + m_showIssues->setIcon(Icons::ZOOM_TOOLBAR.icon()); + m_showIssues->setToolTip(Tr::tr("Search for issues")); + m_showIssues->setCheckable(true); + connect(m_showIssues, &QToolButton::clicked, this, [this] { + QTC_ASSERT(m_outputWidget, return); + m_outputWidget->setCurrentIndex(1); + if (auto issues = static_cast(m_outputWidget->widget(1))) + issues->updateUi(); + }); + + connect(m_outputWidget, &QStackedWidget::currentChanged, this, [this](int idx) { + m_showDashboard->setChecked(idx == 0); + m_showIssues->setChecked(idx == 1); + }); } AxivionOutputPane::~AxivionOutputPane() @@ -642,26 +668,7 @@ QWidget *AxivionOutputPane::outputWidget(QWidget *parent) QList AxivionOutputPane::toolBarWidgets() const { - QList buttons; - auto showDashboard = new QToolButton(m_outputWidget); - showDashboard->setIcon(Icons::HOME_TOOLBAR.icon()); - showDashboard->setToolTip(Tr::tr("Show dashboard")); - connect(showDashboard, &QToolButton::clicked, this, [this]{ - QTC_ASSERT(m_outputWidget, return); - m_outputWidget->setCurrentIndex(0); - }); - buttons.append(showDashboard); - auto showIssues = new QToolButton(m_outputWidget); - showIssues->setIcon(Icons::ZOOM_TOOLBAR.icon()); - showIssues->setToolTip(Tr::tr("Search for issues")); - connect(showIssues, &QToolButton::clicked, this, [this]{ - QTC_ASSERT(m_outputWidget, return); - m_outputWidget->setCurrentIndex(1); - if (auto issues = static_cast(m_outputWidget->widget(1))) - issues->updateUi(); - }); - buttons.append(showIssues); - return buttons; + return {m_showDashboard, m_showIssues}; } void AxivionOutputPane::clearContents() diff --git a/src/plugins/axivion/axivionoutputpane.h b/src/plugins/axivion/axivionoutputpane.h index 87000e5e187..040a52e193f 100644 --- a/src/plugins/axivion/axivionoutputpane.h +++ b/src/plugins/axivion/axivionoutputpane.h @@ -7,6 +7,7 @@ QT_BEGIN_NAMESPACE class QStackedWidget; +class QToolButton; QT_END_NAMESPACE namespace Axivion::Internal { @@ -14,6 +15,7 @@ namespace Axivion::Internal { class AxivionOutputPane : public Core::IOutputPane { Q_OBJECT + public: explicit AxivionOutputPane(QObject *parent = nullptr); ~AxivionOutputPane(); @@ -32,8 +34,11 @@ public: void goToPrev() override; void updateDashboard(); + private: QStackedWidget *m_outputWidget = nullptr; + QToolButton *m_showDashboard = nullptr; + QToolButton *m_showIssues = nullptr; }; } // Axivion::Internal From df78a36e3e36e6b5307366b55123217ecee00a64 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 22 Feb 2024 15:35:03 +0100 Subject: [PATCH 070/128] Axivion: Hide the output pane implementation Change-Id: I4aea1a974cfbbe7aaef370e012b921d5e125e0b8 Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionoutputpane.cpp | 199 +++++++++++----------- src/plugins/axivion/axivionoutputpane.h | 37 +--- src/plugins/axivion/axivionplugin.cpp | 9 +- 3 files changed, 104 insertions(+), 141 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index ae4a5057424..7ceaf04b8c9 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -8,6 +8,7 @@ #include "dashboard/dto.h" #include +#include #include #include @@ -611,115 +612,107 @@ void IssuesWidget::fetchMoreIssues() fetchIssues(search); } -AxivionOutputPane::AxivionOutputPane(QObject *parent) - : IOutputPane(parent) +class AxivionOutputPane final : public IOutputPane { - setId("Axivion"); - setDisplayName(Tr::tr("Axivion")); - setPriorityInStatusBar(-50); +public: + explicit AxivionOutputPane(QObject *parent) + : IOutputPane(parent) + { + setId("Axivion"); + setDisplayName(Tr::tr("Axivion")); + setPriorityInStatusBar(-50); - m_outputWidget = new QStackedWidget; - DashboardWidget *dashboardWidget = new DashboardWidget(m_outputWidget); - m_outputWidget->addWidget(dashboardWidget); - IssuesWidget *issuesWidget = new IssuesWidget(m_outputWidget); - m_outputWidget->addWidget(issuesWidget); + m_outputWidget = new QStackedWidget; + DashboardWidget *dashboardWidget = new DashboardWidget(m_outputWidget); + m_outputWidget->addWidget(dashboardWidget); + IssuesWidget *issuesWidget = new IssuesWidget(m_outputWidget); + m_outputWidget->addWidget(issuesWidget); - m_showDashboard = new QToolButton(m_outputWidget); - m_showDashboard->setIcon(Icons::HOME_TOOLBAR.icon()); - m_showDashboard->setToolTip(Tr::tr("Show dashboard")); - m_showDashboard->setCheckable(true); - m_showDashboard->setChecked(true); - connect(m_showDashboard, &QToolButton::clicked, this, [this] { - QTC_ASSERT(m_outputWidget, return); - m_outputWidget->setCurrentIndex(0); - }); + m_showDashboard = new QToolButton(m_outputWidget); + m_showDashboard->setIcon(Icons::HOME_TOOLBAR.icon()); + m_showDashboard->setToolTip(Tr::tr("Show dashboard")); + m_showDashboard->setCheckable(true); + m_showDashboard->setChecked(true); + connect(m_showDashboard, &QToolButton::clicked, this, [this] { + QTC_ASSERT(m_outputWidget, return); + m_outputWidget->setCurrentIndex(0); + }); - m_showIssues = new QToolButton(m_outputWidget); - m_showIssues->setIcon(Icons::ZOOM_TOOLBAR.icon()); - m_showIssues->setToolTip(Tr::tr("Search for issues")); - m_showIssues->setCheckable(true); - connect(m_showIssues, &QToolButton::clicked, this, [this] { - QTC_ASSERT(m_outputWidget, return); - m_outputWidget->setCurrentIndex(1); - if (auto issues = static_cast(m_outputWidget->widget(1))) - issues->updateUi(); - }); + m_showIssues = new QToolButton(m_outputWidget); + m_showIssues->setIcon(Icons::ZOOM_TOOLBAR.icon()); + m_showIssues->setToolTip(Tr::tr("Search for issues")); + m_showIssues->setCheckable(true); + connect(m_showIssues, &QToolButton::clicked, this, [this] { + QTC_ASSERT(m_outputWidget, return); + m_outputWidget->setCurrentIndex(1); + if (auto issues = static_cast(m_outputWidget->widget(1))) + issues->updateUi(); + }); - connect(m_outputWidget, &QStackedWidget::currentChanged, this, [this](int idx) { - m_showDashboard->setChecked(idx == 0); - m_showIssues->setChecked(idx == 1); - }); -} - -AxivionOutputPane::~AxivionOutputPane() -{ - if (!m_outputWidget->parent()) - delete m_outputWidget; -} - -QWidget *AxivionOutputPane::outputWidget(QWidget *parent) -{ - if (m_outputWidget) - m_outputWidget->setParent(parent); - else - QTC_CHECK(false); - return m_outputWidget; -} - -QList AxivionOutputPane::toolBarWidgets() const -{ - return {m_showDashboard, m_showIssues}; -} - -void AxivionOutputPane::clearContents() -{ -} - -void AxivionOutputPane::setFocus() -{ -} - -bool AxivionOutputPane::hasFocus() const -{ - return false; -} - -bool AxivionOutputPane::canFocus() const -{ - return true; -} - -bool AxivionOutputPane::canNavigate() const -{ - return true; -} - -bool AxivionOutputPane::canNext() const -{ - return false; -} - -bool AxivionOutputPane::canPrevious() const -{ - return false; -} - -void AxivionOutputPane::goToNext() -{ -} - -void AxivionOutputPane::goToPrev() -{ -} - -void AxivionOutputPane::updateDashboard() -{ - if (auto dashboard = static_cast(m_outputWidget->widget(0))) { - dashboard->updateUi(); - m_outputWidget->setCurrentIndex(0); - if (dashboard->hasProject()) - flash(); + connect(m_outputWidget, &QStackedWidget::currentChanged, this, [this](int idx) { + m_showDashboard->setChecked(idx == 0); + m_showIssues->setChecked(idx == 1); + }); } + + ~AxivionOutputPane() + { + if (!m_outputWidget->parent()) + delete m_outputWidget; + } + + QWidget *outputWidget(QWidget *parent) final + { + if (m_outputWidget) + m_outputWidget->setParent(parent); + else + QTC_CHECK(false); + return m_outputWidget; + } + + QList toolBarWidgets() const final + { + return {m_showDashboard, m_showIssues}; + } + + void clearContents() final {} + void setFocus() final {} + bool hasFocus() const final { return false; } + bool canFocus() const final { return true; } + bool canNavigate() const final { return true; } + bool canNext() const final { return false; } + bool canPrevious() const final { return false; } + void goToNext() final {} + void goToPrev() final {} + + void updateDashboard() + { + if (auto dashboard = static_cast(m_outputWidget->widget(0))) { + dashboard->updateUi(); + m_outputWidget->setCurrentIndex(0); + if (dashboard->hasProject()) + flash(); + } + } + +private: + QStackedWidget *m_outputWidget = nullptr; + QToolButton *m_showDashboard = nullptr; + QToolButton *m_showIssues = nullptr; +}; + + +static QPointer theAxivionOutputPane; + +void setupAxivionOutputPane(QObject *guard) +{ + theAxivionOutputPane = new AxivionOutputPane(guard); +} + +void updateDashboard() +{ + QTC_ASSERT(theAxivionOutputPane, return); + theAxivionOutputPane->updateDashboard(); } } // Axivion::Internal diff --git a/src/plugins/axivion/axivionoutputpane.h b/src/plugins/axivion/axivionoutputpane.h index 040a52e193f..a13b125c183 100644 --- a/src/plugins/axivion/axivionoutputpane.h +++ b/src/plugins/axivion/axivionoutputpane.h @@ -3,42 +3,11 @@ #pragma once -#include - -QT_BEGIN_NAMESPACE -class QStackedWidget; -class QToolButton; -QT_END_NAMESPACE +#include namespace Axivion::Internal { -class AxivionOutputPane : public Core::IOutputPane -{ - Q_OBJECT - -public: - explicit AxivionOutputPane(QObject *parent = nullptr); - ~AxivionOutputPane(); - - // IOutputPane interface - QWidget *outputWidget(QWidget *parent) override; - QList toolBarWidgets() const override; - void clearContents() override; - void setFocus() override; - bool hasFocus() const override; - bool canFocus() const override; - bool canNavigate() const override; - bool canNext() const override; - bool canPrevious() const override; - void goToNext() override; - void goToPrev() override; - - void updateDashboard(); - -private: - QStackedWidget *m_outputWidget = nullptr; - QToolButton *m_showDashboard = nullptr; - QToolButton *m_showIssues = nullptr; -}; +void setupAxivionOutputPane(QObject *guard); +void updateDashboard(); } // Axivion::Internal diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 10364b86f3f..b5c6634c339 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -202,7 +202,6 @@ public: // TODO: Should be cleared on username change in settings. std::optional m_apiToken; NetworkAccessManager m_networkAccessManager; - AxivionOutputPane m_axivionOutputPane; std::optional m_dashboardInfo; std::optional m_currentProjectInfo; Project *m_project = nullptr; @@ -309,7 +308,7 @@ void AxivionPluginPrivate::onStartupProjectChanged(Project *project) m_project = project; clearAllMarks(); m_currentProjectInfo = {}; - m_axivionOutputPane.updateDashboard(); + updateDashboard(); if (!m_project) return; @@ -729,7 +728,7 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) clearAllMarks(); if (projectName.isEmpty()) { m_currentProjectInfo = {}; - m_axivionOutputPane.updateDashboard(); + updateDashboard(); return; } @@ -749,7 +748,7 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) const auto handler = [this](const Dto::ProjectInfoDto &data) { m_currentProjectInfo = data; - m_axivionOutputPane.updateDashboard(); + updateDashboard(); handleOpenedDocs(); }; @@ -945,6 +944,8 @@ class AxivionPlugin final : public ExtensionSystem::IPlugin void initialize() final { + setupAxivionOutputPane(this); + dd = new AxivionPluginPrivate; AxivionProjectSettings::setupProjectPanel(); From c0dd8f9aa4d7920477302dd6bc393c4e3a859a53 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 22 Feb 2024 15:45:07 +0100 Subject: [PATCH 071/128] Axivion: Use normal background for the output pane widgets Change-Id: Iffea125f27eb64bfb9003c3a96fa78dedb6b9ebf Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionoutputpane.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 7ceaf04b8c9..104439b9d24 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -628,6 +628,10 @@ public: IssuesWidget *issuesWidget = new IssuesWidget(m_outputWidget); m_outputWidget->addWidget(issuesWidget); + QPalette pal = m_outputWidget->palette(); + pal.setColor(QPalette::Window, creatorTheme()->color(Theme::Color::BackgroundColorNormal)); + m_outputWidget->setPalette(pal); + m_showDashboard = new QToolButton(m_outputWidget); m_showDashboard->setIcon(Icons::HOME_TOOLBAR.icon()); m_showDashboard->setToolTip(Tr::tr("Show dashboard")); From 82724f60d1e7b8ed66902f5598f271b4eee0d841 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 22 Feb 2024 16:09:11 +0100 Subject: [PATCH 072/128] Axivion: Use LayoutBuilder for output pane items Change-Id: I6422a6d30a1b273810f8cf18cf899ee3dcb5bd2e Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionoutputpane.cpp | 75 +++++++++++------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 104439b9d24..334df135405 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -59,22 +60,24 @@ DashboardWidget::DashboardWidget(QWidget *parent) : QScrollArea(parent) { QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); - QFormLayout *projectLayout = new QFormLayout; m_project = new QLabel(this); - projectLayout->addRow(Tr::tr("Project:"), m_project); m_loc = new QLabel(this); - projectLayout->addRow(Tr::tr("Lines of code:"), m_loc); m_timestamp = new QLabel(this); - projectLayout->addRow(Tr::tr("Analysis timestamp:"), m_timestamp); - layout->addLayout(projectLayout); - layout->addSpacing(10); - auto row = new QHBoxLayout; + m_gridLayout = new QGridLayout; - row->addLayout(m_gridLayout); - row->addStretch(1); - layout->addLayout(row); - layout->addStretch(1); + + using namespace Layouting; + Column { + Form { + Tr::tr("Project:"), m_project, br, + Tr::tr("Lines of code:"), m_loc, br, + Tr::tr("Analysis timestamp:"), m_timestamp + }, + Space(10), + Row { m_gridLayout, st }, + st + }.attachTo(widget); + setWidget(widget); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setWidgetResizable(true); @@ -243,7 +246,6 @@ private: std::optional m_currentTableInfo; QHBoxLayout *m_typesLayout = nullptr; QButtonGroup *m_typesButtonGroup = nullptr; - QHBoxLayout *m_filtersLayout = nullptr; QPushButton *m_addedFilter = nullptr; QPushButton *m_removedFilter = nullptr; QComboBox *m_ownerFilter = nullptr; @@ -264,59 +266,51 @@ IssuesWidget::IssuesWidget(QWidget *parent) : QScrollArea(parent) { QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); // row with issue types (-> depending on choice, tables below change) // and a selectable range (start version, end version) // row with added/removed and some filters (assignee, path glob, (named filter)) // table, columns depend on chosen issue type - QHBoxLayout *top = new QHBoxLayout; - layout->addLayout(top); m_typesButtonGroup = new QButtonGroup(this); m_typesButtonGroup->setExclusive(true); m_typesLayout = new QHBoxLayout; - top->addLayout(m_typesLayout); - top->addStretch(1); + m_versionStart = new QComboBox(this); m_versionStart->setMinimumContentsLength(25); - top->addWidget(m_versionStart); + connect(m_versionStart, &QComboBox::activated, this, &IssuesWidget::onSearchParameterChanged); + m_versionEnd = new QComboBox(this); m_versionEnd->setMinimumContentsLength(25); - connect(m_versionStart, &QComboBox::activated, this, &IssuesWidget::onSearchParameterChanged); connect(m_versionEnd, &QComboBox::activated, this, &IssuesWidget::onSearchParameterChanged); - top->addWidget(m_versionEnd); - top->addStretch(1); - m_filtersLayout = new QHBoxLayout; + m_addedFilter = new QPushButton(this); m_addedFilter->setIcon(trendIcon(1, 0)); m_addedFilter->setText("0"); m_addedFilter->setCheckable(true); - m_filtersLayout->addWidget(m_addedFilter); - m_removedFilter = new QPushButton(this); - m_removedFilter->setIcon(trendIcon(0, 1)); - m_removedFilter->setText("0"); - m_removedFilter->setCheckable(true); - m_filtersLayout->addWidget(m_removedFilter); connect(m_addedFilter, &QPushButton::clicked, this, [this](bool checked) { if (checked && m_removedFilter->isChecked()) m_removedFilter->setChecked(false); onSearchParameterChanged(); }); + + m_removedFilter = new QPushButton(this); + m_removedFilter->setIcon(trendIcon(0, 1)); + m_removedFilter->setText("0"); + m_removedFilter->setCheckable(true); connect(m_removedFilter, &QPushButton::clicked, this, [this](bool checked) { if (checked && m_addedFilter->isChecked()) m_addedFilter->setChecked(false); onSearchParameterChanged(); }); - m_filtersLayout->addSpacing(1); + m_ownerFilter = new QComboBox(this); m_ownerFilter->setToolTip(Tr::tr("Owner")); m_ownerFilter->setMinimumContentsLength(25); connect(m_ownerFilter, &QComboBox::activated, this, &IssuesWidget::onSearchParameterChanged); - m_filtersLayout->addWidget(m_ownerFilter); + m_pathGlobFilter = new QLineEdit(this); m_pathGlobFilter->setPlaceholderText(Tr::tr("Path globbing")); connect(m_pathGlobFilter, &QLineEdit::textEdited, this, &IssuesWidget::onSearchParameterChanged); - m_filtersLayout->addWidget(m_pathGlobFilter); - layout->addLayout(m_filtersLayout); + m_issuesView = new BaseTreeView(this); m_issuesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_issuesView->enableColumnHiding(); @@ -331,12 +325,17 @@ IssuesWidget::IssuesWidget(QWidget *parent) } }); } - layout->addWidget(m_issuesView); + m_totalRows = new QLabel(Tr::tr("Total rows:"), this); - QHBoxLayout *bottom = new QHBoxLayout; - layout->addLayout(bottom); - bottom->addStretch(1); - bottom->addWidget(m_totalRows); + + using namespace Layouting; + Column { + Row { m_typesLayout, st, m_versionStart, m_versionEnd, st }, + Row { m_addedFilter, m_removedFilter, Space(1), m_ownerFilter, m_pathGlobFilter }, + m_issuesView, + Row { st, m_totalRows } + }.attachTo(widget); + setWidget(widget); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setWidgetResizable(true); From 289ac94653eadf73f790f3b2d521863109bfb8d9 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 22 Feb 2024 15:08:35 +0100 Subject: [PATCH 073/128] QtKeychain: Disable gcc compiler warning for the qbs build CMake build disables anything, so we can disable a specific warning as well. Change-Id: Ia350d96708f371323499d645f8cc31e07e4d6c9d Reviewed-by: Christian Kandeler --- src/libs/3rdparty/qtkeychain/qtkeychain.qbs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/3rdparty/qtkeychain/qtkeychain.qbs b/src/libs/3rdparty/qtkeychain/qtkeychain.qbs index 6a1073a953e..b5f8e7dc131 100644 --- a/src/libs/3rdparty/qtkeychain/qtkeychain.qbs +++ b/src/libs/3rdparty/qtkeychain/qtkeychain.qbs @@ -72,6 +72,7 @@ QtcLibrary { Group { name: "qtkeychain dbus support" cpp.defines: outer.concat(["KEYCHAIN_DBUS=1"]) + cpp.cxxFlags: outer.concat("-Wno-cast-function-type") files: [ "gnomekeyring.cpp", "gnomekeyring_p.h", From fdedbdd3d454608a0c94cfaef5665fdea7ccecc1 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 22 Feb 2024 21:54:00 +0100 Subject: [PATCH 074/128] Axivion: Fix a possible crash when rerunning task tree from its handler Don't call setTableDto() directly from tableInfoRecipe's handler. Store the new table dto in the handler and update the Gui afterwards, on task tree done. Fixes: QTCREATORBUG-30429 Change-Id: Id267c8375bf7c9a107450ac34ecba37d696f45e1 Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 31 ++++++++++++++--------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 334df135405..3d43df982ce 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -229,15 +229,15 @@ class IssuesWidget : public QScrollArea public: explicit IssuesWidget(QWidget *parent = nullptr); void updateUi(); - void setTableDto(const Dto::TableInfoDto &dto); + void updateTable(); void addIssues(const Dto::IssueTableDto &dto); private: void onSearchParameterChanged(); void updateBasicProjectInfo(std::optional info); - void updateTableView(); void setFiltersEnabled(bool enabled); IssueListSearch searchFromUi() const; + void fetchTable(); void fetchIssues(const IssueListSearch &search); void fetchMoreIssues(); @@ -360,18 +360,19 @@ void IssuesWidget::updateUi() if (info.issueKinds.size()) m_currentPrefix = info.issueKinds.front().prefix; - updateTableView(); + fetchTable(); } -void IssuesWidget::setTableDto(const Dto::TableInfoDto &dto) +void IssuesWidget::updateTable() { - m_currentTableInfo.emplace(dto); + if (!m_currentTableInfo) + return; // update issues table layout - for now just simple approach TreeModel<> *issuesModel = new TreeModel(this); QStringList columnHeaders; QStringList hiddenColumns; - for (const Dto::ColumnInfoDto &column : dto.columns) { + for (const Dto::ColumnInfoDto &column : m_currentTableInfo->columns) { columnHeaders << column.header.value_or(column.key); if (!column.showByDefault) hiddenColumns << column.key; @@ -511,7 +512,7 @@ void IssuesWidget::updateBasicProjectInfo(std::optional inf button->setCheckable(true); connect(button, &QToolButton::clicked, this, [this, prefix = kind.prefix]{ m_currentPrefix = prefix; - updateTableView(); + fetchTable(); }); m_typesButtonGroup->addButton(button, ++buttonId); m_typesLayout->addWidget(button); @@ -543,20 +544,26 @@ void IssuesWidget::updateBasicProjectInfo(std::optional inf m_versionStart->setCurrentIndex(m_versionDates.count() - 1); } -void IssuesWidget::updateTableView() +void IssuesWidget::fetchTable() { QTC_ASSERT(!m_currentPrefix.isEmpty(), return); // fetch table dto and apply, on done fetch first data for the selected issues - const auto tableHandler = [this](const Dto::TableInfoDto &dto) { setTableDto(dto); }; - const auto setupHandler = [this](TaskTree *) { m_issuesView->showProgressIndicator(); }; + const auto tableHandler = [this](const Dto::TableInfoDto &dto) { + m_currentTableInfo.emplace(dto); + }; + const auto setupHandler = [this](TaskTree *) { + m_totalRowCount = 0; + m_lastRequestedOffset = 0; + m_currentTableInfo.reset(); + m_issuesView->showProgressIndicator(); + }; const auto doneHandler = [this](DoneWith result) { if (result == DoneWith::Error) { m_issuesView->hideProgressIndicator(); return; } // first time lookup... should we cache and maybe represent old data? - m_totalRowCount = 0; - m_lastRequestedOffset = 0; + updateTable(); IssueListSearch search = searchFromUi(); search.computeTotalRowCount = true; fetchIssues(search); From 6ed29d6714114abe4c0c28fa3afebaec62236f39 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 22 Feb 2024 22:14:38 +0100 Subject: [PATCH 075/128] Axivion: Hide more IssuesWidget functions in private section Move the fetching method closer to the other fetching methods. Change-Id: I2a3b76dd4924a490e58ceab85263ee2ccd96c0e3 Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 58 +++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 3d43df982ce..b9a0df2229d 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -229,10 +229,10 @@ class IssuesWidget : public QScrollArea public: explicit IssuesWidget(QWidget *parent = nullptr); void updateUi(); - void updateTable(); - void addIssues(const Dto::IssueTableDto &dto); private: + void updateTable(); + void addIssues(const Dto::IssueTableDto &dto); void onSearchParameterChanged(); void updateBasicProjectInfo(std::optional info); void setFiltersEnabled(bool enabled); @@ -544,33 +544,6 @@ void IssuesWidget::updateBasicProjectInfo(std::optional inf m_versionStart->setCurrentIndex(m_versionDates.count() - 1); } -void IssuesWidget::fetchTable() -{ - QTC_ASSERT(!m_currentPrefix.isEmpty(), return); - // fetch table dto and apply, on done fetch first data for the selected issues - const auto tableHandler = [this](const Dto::TableInfoDto &dto) { - m_currentTableInfo.emplace(dto); - }; - const auto setupHandler = [this](TaskTree *) { - m_totalRowCount = 0; - m_lastRequestedOffset = 0; - m_currentTableInfo.reset(); - m_issuesView->showProgressIndicator(); - }; - const auto doneHandler = [this](DoneWith result) { - if (result == DoneWith::Error) { - m_issuesView->hideProgressIndicator(); - return; - } - // first time lookup... should we cache and maybe represent old data? - updateTable(); - IssueListSearch search = searchFromUi(); - search.computeTotalRowCount = true; - fetchIssues(search); - }; - m_taskTreeRunner.start(tableInfoRecipe(m_currentPrefix, tableHandler), setupHandler, doneHandler); -} - void IssuesWidget::setFiltersEnabled(bool enabled) { m_addedFilter->setEnabled(enabled); @@ -599,6 +572,33 @@ IssueListSearch IssuesWidget::searchFromUi() const return search; } +void IssuesWidget::fetchTable() +{ + QTC_ASSERT(!m_currentPrefix.isEmpty(), return); + // fetch table dto and apply, on done fetch first data for the selected issues + const auto tableHandler = [this](const Dto::TableInfoDto &dto) { + m_currentTableInfo.emplace(dto); + }; + const auto setupHandler = [this](TaskTree *) { + m_totalRowCount = 0; + m_lastRequestedOffset = 0; + m_currentTableInfo.reset(); + m_issuesView->showProgressIndicator(); + }; + const auto doneHandler = [this](DoneWith result) { + if (result == DoneWith::Error) { + m_issuesView->hideProgressIndicator(); + return; + } + // first time lookup... should we cache and maybe represent old data? + updateTable(); + IssueListSearch search = searchFromUi(); + search.computeTotalRowCount = true; + fetchIssues(search); + }; + m_taskTreeRunner.start(tableInfoRecipe(m_currentPrefix, tableHandler), setupHandler, doneHandler); +} + void IssuesWidget::fetchIssues(const IssueListSearch &search) { const auto issuesHandler = [this](const Dto::IssueTableDto &dto) { addIssues(dto); }; From 0a3c0ad1f5a08bd544fe70c204df96566ea23101 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 21 Feb 2024 17:35:18 +0100 Subject: [PATCH 076/128] Axivion: Provide basic sorting for issues table For now this enables just the single column sorting, but this is supposed to be enhanced by multi-column sorting and filtering later on. Change-Id: I7603ae5c412d7a0a669d1ba4ba7e001a059056f4 Reviewed-by: Christian Stenger Reviewed-by: Reviewed-by: Jarek Kobus --- src/plugins/axivion/CMakeLists.txt | 1 + src/plugins/axivion/axivion.qbs | 2 + src/plugins/axivion/axivion.qrc | 4 + src/plugins/axivion/axivionoutputpane.cpp | 17 +++ src/plugins/axivion/axivionplugin.cpp | 3 + src/plugins/axivion/axivionplugin.h | 1 + src/plugins/axivion/images/sortAsc.png | Bin 0 -> 113 bytes src/plugins/axivion/images/sortAsc@2x.png | Bin 0 -> 137 bytes src/plugins/axivion/images/sortDesc.png | Bin 0 -> 118 bytes src/plugins/axivion/images/sortDesc@2x.png | Bin 0 -> 142 bytes src/plugins/axivion/issueheaderview.cpp | 139 +++++++++++++++++++++ src/plugins/axivion/issueheaderview.h | 41 ++++++ 12 files changed, 208 insertions(+) create mode 100644 src/plugins/axivion/images/sortAsc.png create mode 100644 src/plugins/axivion/images/sortAsc@2x.png create mode 100644 src/plugins/axivion/images/sortDesc.png create mode 100644 src/plugins/axivion/images/sortDesc@2x.png create mode 100644 src/plugins/axivion/issueheaderview.cpp create mode 100644 src/plugins/axivion/issueheaderview.h diff --git a/src/plugins/axivion/CMakeLists.txt b/src/plugins/axivion/CMakeLists.txt index a71b984520f..c3e2f5e7eaa 100644 --- a/src/plugins/axivion/CMakeLists.txt +++ b/src/plugins/axivion/CMakeLists.txt @@ -13,4 +13,5 @@ add_qtc_plugin(Axivion dashboard/dto.cpp dashboard/dto.h dashboard/concat.cpp dashboard/concat.h dashboard/error.h dashboard/error.cpp + issueheaderview.cpp issueheaderview.h ) diff --git a/src/plugins/axivion/axivion.qbs b/src/plugins/axivion/axivion.qbs index 7b8034c26c4..70188feb3e4 100644 --- a/src/plugins/axivion/axivion.qbs +++ b/src/plugins/axivion/axivion.qbs @@ -25,6 +25,8 @@ QtcPlugin { "axiviontr.h", "credentialquery.h", "credentialquery.cpp", + "issueheaderview.cpp", + "issueheaderview.h", ] cpp.includePaths: base.concat(["."]) // needed for the generated stuff below diff --git a/src/plugins/axivion/axivion.qrc b/src/plugins/axivion/axivion.qrc index c0d56f8f7b0..fa3ad146d37 100644 --- a/src/plugins/axivion/axivion.qrc +++ b/src/plugins/axivion/axivion.qrc @@ -14,5 +14,9 @@ images/button-mv@2x.png images/button-sv.png images/button-sv@2x.png + images/sortAsc.png + images/sortAsc@2x.png + images/sortDesc.png + images/sortDesc@2x.png diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index b9a0df2229d..5be8521b67f 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -6,6 +6,7 @@ #include "axivionplugin.h" #include "axiviontr.h" #include "dashboard/dto.h" +#include "issueheaderview.h" #include #include @@ -254,6 +255,7 @@ private: QLineEdit *m_pathGlobFilter = nullptr; // FancyLineEdit instead? QLabel *m_totalRows = nullptr; BaseTreeView *m_issuesView = nullptr; + IssueHeaderView *m_headerView = nullptr; TreeModel<> *m_issuesModel = nullptr; int m_totalRowCount = 0; int m_lastRequestedOffset = 0; @@ -312,6 +314,10 @@ IssuesWidget::IssuesWidget(QWidget *parent) connect(m_pathGlobFilter, &QLineEdit::textEdited, this, &IssuesWidget::onSearchParameterChanged); m_issuesView = new BaseTreeView(this); + m_headerView = new IssueHeaderView(this); + connect(m_headerView, &IssueHeaderView::sortTriggered, + this, &IssuesWidget::onSearchParameterChanged); + m_issuesView->setHeader(m_headerView); m_issuesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_issuesView->enableColumnHiding(); m_issuesModel = new TreeModel(this); @@ -372,10 +378,12 @@ void IssuesWidget::updateTable() TreeModel<> *issuesModel = new TreeModel(this); QStringList columnHeaders; QStringList hiddenColumns; + QList sortableColumns; for (const Dto::ColumnInfoDto &column : m_currentTableInfo->columns) { columnHeaders << column.header.value_or(column.key); if (!column.showByDefault) hiddenColumns << column.key; + sortableColumns << column.canSort; } m_addedFilter->setText("0"); m_removedFilter->setText("0"); @@ -386,10 +394,12 @@ void IssuesWidget::updateTable() auto oldModel = m_issuesModel; m_issuesModel = issuesModel; m_issuesView->setModel(issuesModel); + m_headerView->setSortableColumns(sortableColumns); delete oldModel; int counter = 0; for (const QString &header : std::as_const(columnHeaders)) m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header)); + m_issuesView->header()->resizeSections(QHeaderView::ResizeToContents); } static Links linksForIssue(const std::map &issueRow) @@ -569,6 +579,13 @@ IssueListSearch IssuesWidget::searchFromUi() const search.state = "added"; else if (m_removedFilter->isChecked()) search.state = "removed"; + if (int column = m_headerView->currentSortColumn() != -1) { + QTC_ASSERT(m_currentTableInfo, return search); + QTC_ASSERT((ulong)column < m_currentTableInfo->columns.size(), return search); + search.sort = m_currentTableInfo->columns.at(m_headerView->currentSortColumn()).key + + (m_headerView->currentSortOrder() == SortOrder::Ascending ? " asc" : " desc"); + } + return search; } diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index b5c6634c339..558f7909744 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -171,6 +171,9 @@ QString IssueListSearch::toQuery() const result.append(QString("&state=%1").arg(state)); if (computeTotalRowCount) result.append("&computeTotalRowCount=true"); + if (!sort.isEmpty()) + result.append(QString("&sort=%1").arg( + QString::fromUtf8(QUrl::toPercentEncoding(sort)))); return result; } diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h index 1d2ebe12129..c60aad6e9e9 100644 --- a/src/plugins/axivion/axivionplugin.h +++ b/src/plugins/axivion/axivionplugin.h @@ -31,6 +31,7 @@ struct IssueListSearch QString versionEnd; QString owner; QString filter_path; + QString sort; int offset = 0; int limit = 150; bool computeTotalRowCount = false; diff --git a/src/plugins/axivion/images/sortAsc.png b/src/plugins/axivion/images/sortAsc.png new file mode 100644 index 0000000000000000000000000000000000000000..c907297279033bd84a2e985e9121832fc4eb74cb GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`%FD~^>+Apj z|34>nV+jKTgMz1vV+elq|_4WV% z|DTh(v4nwv!NSwUF+?If`3FBM&xxM;uK~ySD>WW7T2HxnfFtowa>5FB<2nO_6U~zR o5)wBK_OP+BxtTDx>o7CSNPaJPSyV=xfq{X+)78&qol`;+0EOfymjD0& literal 0 HcmV?d00001 diff --git a/src/plugins/axivion/images/sortDesc.png b/src/plugins/axivion/images/sortDesc.png new file mode 100644 index 0000000000000000000000000000000000000000..a92d018d4e191790d5fc6a4bd950a64ae379509d GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu#id_r6q7#Qm7>l+#xIyyT3 z|NlQ#%sQWefkDaB#W92AM?zx80iJX=HqnkLjKU$ki#awWG5Gh%e|+_` S^AiID1B0ilpUXO@geCyWry=qH literal 0 HcmV?d00001 diff --git a/src/plugins/axivion/images/sortDesc@2x.png b/src/plugins/axivion/images/sortDesc@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9991ff2955a1a2b8f4c5592039c5b611d0713ef2 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8`~f~8t_%ze_4V})4GkR~ z9smFTpDJdZ&%nT7<>}%WqA@YKz>%3}F;j_`D_+KvWe7YEjbM;X>JXg%NwXE46P tu#7?N0Z%Z)_G=BCXJz}8Ty~2vw3*azKKJ$L69xtb22WQ%mvv4FO#lj + +#include +#include + +namespace Axivion::Internal { + +constexpr int ICON_SIZE = 16; + +static QIcon iconForSorted(SortOrder order) +{ + const Utils::Icon UNSORTED( + {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteButtonTextDisabled}, + {":/axivion/images/sortDesc.png", Utils::Theme::PaletteButtonTextDisabled}}); + const Utils::Icon SORT_ASC( + {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteButtonText}, + {":/axivion/images/sortDesc.png", Utils::Theme::PaletteButtonTextDisabled}}); + const Utils::Icon SORT_DESC( + {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteButtonTextDisabled}, + {":/axivion/images/sortDesc.png", Utils::Theme::PaletteButtonText}}); + static const QIcon unsorted = UNSORTED.icon(); + static const QIcon sortedAsc = SORT_ASC.icon(); + static const QIcon sortedDesc = SORT_DESC.icon(); + + switch (order) { + case SortOrder::None: + return unsorted; + case SortOrder::Ascending: + return sortedAsc; + case SortOrder::Descending: + return sortedDesc; + } + return {}; +} + +void IssueHeaderView::setSortableColumns(const QList &sortable) +{ + m_sortableColumns = sortable; + int oldIndex = m_currentSortIndex; + m_currentSortIndex = -1; + m_currentSortOrder = SortOrder::None; + if (oldIndex != -1) + headerDataChanged(Qt::Horizontal, oldIndex, oldIndex); +} + +int IssueHeaderView::currentSortColumn() const +{ + return m_currentSortOrder == SortOrder::None ? -1 : m_currentSortIndex; +} + +void IssueHeaderView::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + const QPoint position = event->position().toPoint(); + const int y = position.y(); + if (y > 1 && y < height() - 2) { // TODO improve + const int pos = position.x(); + const int logical = logicalIndexAt(pos); + const int end = sectionPosition(logical) + sectionSize(logical); + const int start = end - ICON_SIZE - 2; + m_maybeToggleSort = start < pos && end > pos; + } + } + QHeaderView::mousePressEvent(event); +} + +void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event) +{ + bool dontSkip = !m_dragging && m_maybeToggleSort; + m_dragging = false; + m_maybeToggleSort = false; + + if (dontSkip) { + const QPoint position = event->position().toPoint(); + const int y = position.y(); + const int logical = logicalIndexAt(position.x()); + if (logical > -1 && logical < m_sortableColumns.size()) { + if (m_sortableColumns.at(logical)) { // ignore non-sortable + if (y < height() / 2) // TODO improve + onToggleSort(logical, SortOrder::Ascending); + else + onToggleSort(logical, SortOrder::Descending); + } + } + } + QHeaderView::mouseReleaseEvent(event); +} + +void IssueHeaderView::mouseMoveEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_dragging = true; + QHeaderView::mouseMoveEvent(event); +} + +void IssueHeaderView::onToggleSort(int index, SortOrder order) +{ + if (m_currentSortIndex == index) + m_currentSortOrder = (order == m_currentSortOrder) ? SortOrder::None : order; + else + m_currentSortOrder = order; + + int oldIndex = m_currentSortIndex; + m_currentSortIndex = index; + if (oldIndex != -1) + headerDataChanged(Qt::Horizontal, oldIndex, oldIndex); + headerDataChanged(Qt::Horizontal, index, index); + emit sortTriggered(); +} + +QSize IssueHeaderView::sectionSizeFromContents(int logicalIndex) const +{ + QSize size = QHeaderView::sectionSizeFromContents(logicalIndex); + // add icon size and margin (2) + return QSize{size.width() + ICON_SIZE + 2, qMax(size.height(), ICON_SIZE)}; +} + +void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const +{ + painter->save(); + QHeaderView::paintSection(painter, rect, logicalIndex); + painter->restore(); + if (logicalIndex < 0 || logicalIndex >= m_sortableColumns.size()) + return; + if (!m_sortableColumns.at(logicalIndex)) + return; + + const QIcon icon = iconForSorted(logicalIndex == m_currentSortIndex ? m_currentSortOrder : SortOrder::None); + const int offset = qMax((rect.height() - ICON_SIZE), 0) / 2; + const QRect iconRect(rect.left() + rect.width() - ICON_SIZE - 2, offset, ICON_SIZE, ICON_SIZE); + icon.paint(painter, iconRect); +} + +} // namespace Axivion::Internal diff --git a/src/plugins/axivion/issueheaderview.h b/src/plugins/axivion/issueheaderview.h new file mode 100644 index 00000000000..3d6f271a762 --- /dev/null +++ b/src/plugins/axivion/issueheaderview.h @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace Axivion::Internal { + +enum class SortOrder { None, Ascending, Descending }; + +class IssueHeaderView : public QHeaderView +{ + Q_OBJECT +public: + explicit IssueHeaderView(QWidget *parent = nullptr) : QHeaderView(Qt::Horizontal, parent) {} + void setSortableColumns(const QList &sortable); + + SortOrder currentSortOrder() const { return m_currentSortOrder; } + int currentSortColumn() const; +signals: + void sortTriggered(); + +protected: + void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override; + QSize sectionSizeFromContents(int logicalIndex) const override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +private: + void onToggleSort(int index, SortOrder order); + bool m_dragging = false; + bool m_maybeToggleSort = false; + int m_currentSortIndex = -1; + SortOrder m_currentSortOrder = SortOrder::None; + QList m_sortableColumns; +}; + +} // namespace Axivion::Internal From 97ebd272538c23672c1f925ec4484cdbdb4235d3 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 22 Feb 2024 18:22:10 +0100 Subject: [PATCH 077/128] QmlJsPluginDumper: Ensure we don't take results from the canceled futures Amends 91c1c244a1256b2dc9979edb1a3ce83c28ef5636 Fixes: QTCREATORBUG-30424 Change-Id: Ie0bddb79362945a040c6cb95edf37fbec3ed064d Reviewed-by: hjk Reviewed-by: --- src/libs/qmljs/qmljsplugindumper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp index b2591fbf0ab..36f2cd802db 100644 --- a/src/libs/qmljs/qmljsplugindumper.cpp +++ b/src/libs/qmljs/qmljsplugindumper.cpp @@ -569,6 +569,9 @@ void PluginDumper::loadQmltypesFile(const FilePaths &qmltypesFilePaths, Utils::onFinished(loadQmlTypeDescription(qmltypesFilePaths), this, [this, qmltypesFilePaths, libraryPath, libraryInfo] (const QFuture &typesFuture) { + if (typesFuture.isCanceled() || typesFuture.resultCount() == 0) + return; + PluginDumper::QmlTypeDescription typesResult = typesFuture.result(); if (!typesResult.dependencies.isEmpty()) { @@ -576,6 +579,9 @@ void PluginDumper::loadQmltypesFile(const FilePaths &qmltypesFilePaths, QSharedPointer>()), this, [typesResult, libraryInfo, libraryPath, this] (const QFuture &loadFuture) { + if (loadFuture.isCanceled() || loadFuture.resultCount() == 0) + return; + PluginDumper::DependencyInfo loadResult = loadFuture.result(); QStringList errors = typesResult.errors; QStringList warnings = typesResult.errors; From 55c12d022c9388a635f2a955746156eec1d5d7e2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 22 Feb 2024 23:39:56 +0100 Subject: [PATCH 078/128] Expected: Add a test ensuring the default c'tor has value When docs aren't easily available, let's have a test for it. Change-Id: Ic9cec669cc93a38e79b68ddec7cfcc5bccae5de5 Reviewed-by: Reviewed-by: Marcus Tillmanns --- tests/auto/utils/expected/tst_expected.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/auto/utils/expected/tst_expected.cpp b/tests/auto/utils/expected/tst_expected.cpp index ae084036bdc..073f6b7ce7e 100644 --- a/tests/auto/utils/expected/tst_expected.cpp +++ b/tests/auto/utils/expected/tst_expected.cpp @@ -51,6 +51,17 @@ private slots: QVERIFY(e1 == e2); QVERIFY(!(e1 != e2)); } + + void defaultConstructorHasValue() + { + expected_str e1; + QVERIFY(e1.has_value()); + QVERIFY(e1->isEmpty()); + + expected_str e2{}; + QVERIFY(e2.has_value()); + QVERIFY(e2->isEmpty()); + } }; QTEST_GUILESS_MAIN(tst_expected) From b7a8ee92beeddf765d5e6000b9f8188ca8541038 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Wed, 21 Feb 2024 16:31:46 +0100 Subject: [PATCH 079/128] Core: Mention Ctrl+Shift+K into the hide menubar messagebox This way the user can trigger the "Show Menu Bar" action when the Ctrl+Alt+M doesn't work. Task-number: QTCREATORBUG-30114 Change-Id: I4e1d14b7bf7554ce1a262c4b1d2671f8d0b81b85 Reviewed-by: Leena Miettinen Reviewed-by: --- src/plugins/coreplugin/icore.cpp | 34 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index ef11fd6d161..54462f30512 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -1971,14 +1971,32 @@ void ICorePrivate::registerDefaultActions() toggleMenubarAction.addToContainer(Constants::M_VIEW, Constants::G_VIEW_VIEWS); toggleMenubarAction.addOnToggled(this, [](bool visible) { if (!visible) { - const QString keys = ActionManager::command(Constants::TOGGLE_MENUBAR) - ->keySequence().toString(QKeySequence::NativeText); - CheckableMessageBox::information(Core::ICore::dialogParent(), - Tr::tr("Hide Menu Bar"), - Tr::tr("This will hide the menu bar completely. " - "You can show it again by typing %1.") - .arg(keys), - Key("ToogleMenuBarHint")); + auto keySequenceAndText = [](const Utils::Id &actionName) { + const auto command = ActionManager::command(actionName); + + const QString keySequence = command->keySequence().toString( + QKeySequence::NativeText); + const QString text = command->action()->text(); + + return QPair(keySequence, text); + }; + + auto [menuBarKeys, menuBarText] = keySequenceAndText(Constants::TOGGLE_MENUBAR); + auto [actionsFromMenuKeys, actionsFromMenuText] = keySequenceAndText( + "Locator.Actions from the menu"); + + CheckableMessageBox::information( + Core::ICore::dialogParent(), + Tr::tr("Hide Menu Bar"), + Tr::tr("This will hide the menu bar completely. " + "You can show it again by typing %1." + "

" + "Or, trigger the \"%2\" action from the \"%3\" locator filter (%4).") + .arg(menuBarKeys) + .arg(menuBarText) + .arg(actionsFromMenuText) + .arg(actionsFromMenuKeys), + Key("ToogleMenuBarHint")); } globalMenuBar()->setVisible(visible); }); From 787c2ccee9b003c580877b150be07e7f6b5db414 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 9 Feb 2024 06:33:25 +0100 Subject: [PATCH 080/128] Debugger: ensure final new line after debug messages collected by cdb Since recently cdb seems to report messages without a final new line. Change-Id: I6b15d6c5668b7a4bec207d56d86da5a0afa77cb4 Reviewed-by: Christian Stenger --- src/plugins/debugger/cdb/cdbengine.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index d292028050a..c7e20aae980 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -2096,9 +2096,11 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QString &what, c } if (what == "debuggee_output") { - const QByteArray decoded = QByteArray::fromHex(message.toUtf8()); - showMessage(QString::fromUtf16(reinterpret_cast(decoded.data()), decoded.size() / 2), - AppOutput); + const QByteArray encoded = QByteArray::fromHex(message.toUtf8()); + const QString message = QString::fromUtf16(reinterpret_cast( + encoded.data()), + encoded.size() / 2); + showMessage(message.endsWith('\n') ? message : message + '\n', AppOutput); return; } From 9f7d8b06b26fc87e5f588519e53d39a0ed790142 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 23 Feb 2024 11:42:10 +0100 Subject: [PATCH 081/128] Doc: Mention using CMake presets in "Share project settings" You cannot use a .shared file to share CMake project settings. Change-Id: I605ead2f42ed012da789c1eef3a4e6a51de62243 Reviewed-by: Cristian Adam --- .../creator-only/creator-projects-settings-sharing.qdoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-sharing.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-sharing.qdoc index 161874f4b59..3e48923ad36 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-sharing.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-sharing.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -20,6 +20,8 @@ has the same XML structure as a \e {.user} file, but only has the settings to share. + \note Use \l{CMake Presets} to share CMake project settings. + \section1 Create a shared settings file The easiest way to create a \e {.shared} file is to copy settings from the @@ -70,5 +72,5 @@ a permanent sticky setting that was created just because you wanted to try something out. - \sa {Configuring Projects} + \sa {Configuring Projects}, {CMake Presets} */ From 8feb31b2ac194af3b5da09e6c1636a7953b40ffb Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 23 Feb 2024 12:36:11 +0100 Subject: [PATCH 082/128] TextEditor: bound increase and decreaseFontZoom to a 10% grid This allows to get back to a 100% font zoom with the keyboard shortcuts by zooming to an odd zoom factor by other means. Change-Id: Ie90853367b17c207e9c47fc108b8d6f451e0f838 Reviewed-by: Marcus Tillmanns --- src/plugins/texteditor/texteditor.cpp | 12 +++++++ src/plugins/texteditor/texteditor.h | 2 ++ .../texteditor/texteditoractionhandler.cpp | 4 +-- src/plugins/texteditor/texteditorsettings.cpp | 31 +++++++++++++------ src/plugins/texteditor/texteditorsettings.h | 2 ++ 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index c8ef69c63ef..70239b286e0 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -6891,6 +6891,18 @@ static void showZoomIndicator(QWidget *editor, const int newZoom) Utils::FadingIndicator::SmallText); } +void TextEditorWidget::increaseFontZoom() +{ + d->clearVisibleFoldedBlock(); + showZoomIndicator(this, TextEditorSettings::increaseFontZoom()); +} + +void TextEditorWidget::decreaseFontZoom() +{ + d->clearVisibleFoldedBlock(); + showZoomIndicator(this, TextEditorSettings::decreaseFontZoom()); +} + void TextEditorWidget::zoomF(float delta) { d->clearVisibleFoldedBlock(); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index a2a1aafb2b8..33b8cb84f09 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -349,6 +349,8 @@ public: void pasteWithoutFormat(); void switchUtf8bom(); + void increaseFontZoom(); + void decreaseFontZoom(); void zoomF(float delta); void zoomReset(); diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp index 2e48670bf4d..240e8498ed1 100644 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -394,11 +394,11 @@ void TextEditorActionHandlerPrivate::createActions() QKeySequence(), G_EDIT_COLLAPSING, advancedEditMenu); registerAction(INCREASE_FONT_SIZE, - [] (TextEditorWidget *w) { w->zoomF(1.f); }, false, Tr::tr("Increase Font Size"), + [] (TextEditorWidget *w) { w->increaseFontZoom(); }, false, Tr::tr("Increase Font Size"), QKeySequence(Tr::tr("Ctrl++")), G_EDIT_FONT, advancedEditMenu); registerAction(DECREASE_FONT_SIZE, - [] (TextEditorWidget *w) { w->zoomF(-1.f); }, false, Tr::tr("Decrease Font Size"), + [] (TextEditorWidget *w) { w->decreaseFontZoom(); }, false, Tr::tr("Decrease Font Size"), QKeySequence(Tr::tr("Ctrl+-")), G_EDIT_FONT, advancedEditMenu); registerAction(RESET_FONT_SIZE, diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp index 1d7dd89386a..811852b8a84 100644 --- a/src/plugins/texteditor/texteditorsettings.cpp +++ b/src/plugins/texteditor/texteditorsettings.cpp @@ -560,20 +560,33 @@ Utils::Id TextEditorSettings::languageId(const QString &mimeType) return d->m_mimeTypeToLanguage.value(mimeType); } -static void setFontZoom(int zoom) +static int setFontZoom(int zoom) { - d->m_fontSettings.setFontZoom(zoom); - d->m_fontSettings.toSettings(Core::ICore::settings()); - emit textEditorSettings().fontSettingsChanged(d->m_fontSettings); + zoom = qMax(10, zoom); + if (d->m_fontSettings.fontZoom() != zoom) { + d->m_fontSettings.setFontZoom(zoom); + d->m_fontSettings.toSettings(Core::ICore::settings()); + emit textEditorSettings().fontSettingsChanged(d->m_fontSettings); + } + return zoom; +} + +int TextEditorSettings::increaseFontZoom() +{ + const int previousZoom = d->m_fontSettings.fontZoom(); + return setFontZoom(previousZoom + 10 - previousZoom % 10); +} + +int TextEditorSettings::decreaseFontZoom() +{ + const int previousZoom = d->m_fontSettings.fontZoom(); + const int delta = previousZoom % 10; + return setFontZoom(previousZoom - (delta == 0 ? 10 : delta)); } int TextEditorSettings::increaseFontZoom(int step) { - const int previousZoom = d->m_fontSettings.fontZoom(); - const int newZoom = qMax(10, previousZoom + step); - if (newZoom != previousZoom) - setFontZoom(newZoom); - return newZoom; + return setFontZoom(d->m_fontSettings.fontZoom() + step); } void TextEditorSettings::resetFontZoom() diff --git a/src/plugins/texteditor/texteditorsettings.h b/src/plugins/texteditor/texteditorsettings.h index 88dd057608f..1599fcbba5a 100644 --- a/src/plugins/texteditor/texteditorsettings.h +++ b/src/plugins/texteditor/texteditorsettings.h @@ -76,6 +76,8 @@ public: static void registerMimeTypeForLanguageId(const char *mimeType, Utils::Id languageId); static Utils::Id languageId(const QString &mimeType); + static int increaseFontZoom(); + static int decreaseFontZoom(); static int increaseFontZoom(int step); static void resetFontZoom(); From a9c2be10713fc4cba647c91d50cc6d9e4d1d8136 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 23 Feb 2024 11:49:23 +0100 Subject: [PATCH 083/128] Projects: Fix misc settings widgets if vanished targets are shown When vanished targets where shown in Projects, clicking on the project specific settings showed a blank page. There were hardcoded indices for the target and the misc settings. Get the right child for the index directly instead. Change-Id: I725d470ad04f8a227c91938efada76a757a7442c Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/projectwindow.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp index c0577c5c52b..66dca5ecd56 100644 --- a/src/plugins/projectexplorer/projectwindow.cpp +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -527,11 +527,11 @@ public: } case PanelWidgetRole: - case ActiveItemRole: - if (m_currentChildIndex == 0) - return m_targetsItem->data(column, role); - if (m_currentChildIndex == 1) - return m_miscItem->data(column, role); + case ActiveItemRole: { + TreeItem *child = childAt(m_currentChildIndex); + if (child) + return child->data(column, role); + } } return {}; } From 11c1e071d7ceee7e2bd3457a05814d7c82ca2584 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Wed, 21 Feb 2024 16:59:07 +0100 Subject: [PATCH 084/128] Doc: Update the docs about Python development Task-number: QTCREATORBUG-30209 Change-Id: I3023fb6b9005092ecd76f62774942f366e1bd7d9 Reviewed-by: David Schulz --- dist/changelog/changes-13.0.0.md | 5 +- ...w-project-qt-for-python-kit-selection.webp | Bin 0 -> 5796 bytes ...new-qt-for-python-app-project-details.webp | Bin 0 -> 4374 bytes ...on-app-qt-quick-empty-project-details.webp | Bin 7176 -> 0 bytes ...or-python-app-widgets-project-details.webp | Bin 7464 -> 0 bytes .../images/qtcreator-python-interpreters.webp | Bin 7062 -> 5362 bytes .../images/qtcreator-python-run-settings.png | Bin 8396 -> 0 bytes .../images/qtcreator-python-run-settings.webp | Bin 0 -> 5836 bytes .../python/creator-python-development.qdoc | 62 +++++++----------- .../python/creator-python-run-settings.qdoc | 4 +- ...utorial-python-application-qt-widgets.qdoc | 27 ++++---- ...r-tutorial-python-application-qtquick.qdoc | 32 ++++----- 12 files changed, 57 insertions(+), 73 deletions(-) create mode 100644 doc/qtcreator/images/qtcreator-new-project-qt-for-python-kit-selection.webp create mode 100644 doc/qtcreator/images/qtcreator-new-qt-for-python-app-project-details.webp delete mode 100644 doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp delete mode 100644 doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp delete mode 100644 doc/qtcreator/images/qtcreator-python-run-settings.png create mode 100644 doc/qtcreator/images/qtcreator-python-run-settings.webp diff --git a/dist/changelog/changes-13.0.0.md b/dist/changelog/changes-13.0.0.md index f1199018f8b..b8aa6aa1160 100644 --- a/dist/changelog/changes-13.0.0.md +++ b/dist/changelog/changes-13.0.0.md @@ -169,12 +169,13 @@ Projects * Added `Generate Kit` to the Python interpreter preferences for generating a Python kit with this interpreter - ([Documentation](https://doc-snapshots.qt.io/qtcreator-13.0/creator-python-development.html#create-kits-for-python-interpreters)) -* Added the target setup page when loading unconfigured Python projects +* Added the `Kit Selection` page for creating and opening Python projects * Added a `requirements.txt` file to the application wizard * Fixed that the same Python interpreter could be auto-detected multiple times under different names + ([Documentation](https://doc-snapshots.qt.io/qtcreator-13.0/creator-python-development.html)) + Debugging --------- diff --git a/doc/qtcreator/images/qtcreator-new-project-qt-for-python-kit-selection.webp b/doc/qtcreator/images/qtcreator-new-project-qt-for-python-kit-selection.webp new file mode 100644 index 0000000000000000000000000000000000000000..f7c3be17d5b7658dc6ca0d7490ff45a10eb40a0a GIT binary patch literal 5796 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 literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-project-details.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-project-details.webp new file mode 100644 index 0000000000000000000000000000000000000000..14494a04d374d609a6341b4a83fc49e101488a35 GIT binary patch literal 4374 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 I1{3%g0XACU`2YX_ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp deleted file mode 100644 index 3890e8e75bad09b22ea44c443c7fa9aca68dc92f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7464 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 diff --git a/doc/qtcreator/images/qtcreator-python-interpreters.webp b/doc/qtcreator/images/qtcreator-python-interpreters.webp index 597b99e720d2d39f85eb77cc21912a95391a76bb..b5027b0a0d481060fc0c7a6205d727071e066192 100644 GIT binary patch literal 5362 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 U?Q|`dyp;HuEhTw;ce)`j0P~z@9{>OV literal 7062 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 A;{X5v diff --git a/doc/qtcreator/images/qtcreator-python-run-settings.webp b/doc/qtcreator/images/qtcreator-python-run-settings.webp new file mode 100644 index 0000000000000000000000000000000000000000..69f2d7676d6295e9dfe7807000afdbd4936d9a91 GIT binary patch literal 5836 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 F1OU + \uicontrol {Build & Run} set the Python version to use. - \image qtcreator-new-qt-for-python-app-widgets-project-details.webp {Define Project Details dialog} + The \l {Edit Mode}{Edit mode} toolbar shows the current Python version. - You can see the current Python interpreter on the \uicontrol Edit mode - toolbar. + \image qtcreator-python-interpreter-edit-mode.webp {Python version on the Edit mode toolbar} - \image qtcreator-python-interpreter-edit-mode.webp {Python interpreter on the Edit mode toolbar} + To use another Python version, activate another kit for the project. - You can change the interpreter to use for a particular project in - \uicontrol Projects > \uicontrol Run > \uicontrol Interpreter. + \section1 Create kits for Python - \image qtcreator-python-run-settings.png {Python run settings} - - To see the available interpreters and choose another interpreter, select the - current interpreter, and then select \uicontrol {Manage Python Interpreters}. - Or, select \preferences > \uicontrol Python > \uicontrol Interpreters. + \QC automatically adds all Python versions it can find to the list of + Python versions in \preferences > \uicontrol Python > \uicontrol Interpreters. + It generates kits for the global Python versions that are not inside a + virtual environment. \image qtcreator-python-interpreters.webp {Python Interpreters in Preferences} - You can add and remove interpreters and clean up references to interpreters - that you uninstalled, but that still appear in the list. + You can add and remove Python versions and clean up references to Python + versions that you uninstalled, but that still appear in the list. - To use the selected Python interpreter by default, select - \uicontrol {Make Default}. - - \section1 Create a virtual environment - - To use a clean \l{https://docs.python.org/3/library/venv.html}{Python} - virtual environment (\c venv) that is independent of your global Python - installation for a Qt for Python project, select the - \uicontrol {Create new virtual environment} check box in the project wizard. - Set the directory where to create the environment in - \uicontrol {Path to virtual environment}. - - \section1 Create kits for Python interpreters - - \QC automatically adds all Python interpreters it can find to the list of - interpreters in \preferences > \uicontrol Python > \uicontrol Interpreters. - It generates \l{kits-tab}{kits} for the global Python interpreters that are - not inside a virtual environment. + To use the selected Python version when opening \c {.py} files that don't + belong to a project, select \uicontrol {Make Default}. To use a virtual environment as a kit, select it in \uicontrol Interpreters, and then select \uicontrol {Generate Kit}. @@ -133,5 +114,6 @@ the file, select \uicontrol {REPL Import *}. \sa {Creating a Qt for Python Application with Qt Widgets}, - {Creating a Qt for Python Application with Qt Quick} + {Creating a Qt for Python Application with Qt Quick}, + {Activate kits for a project} */ diff --git a/doc/qtcreator/src/python/creator-python-run-settings.qdoc b/doc/qtcreator/src/python/creator-python-run-settings.qdoc index 3d5d5eab27b..6c6c3b4fe2b 100644 --- a/doc/qtcreator/src/python/creator-python-run-settings.qdoc +++ b/doc/qtcreator/src/python/creator-python-run-settings.qdoc @@ -35,7 +35,7 @@ you select for a kit in \uicontrol Projects > \uicontrol {Build & Run} > \uicontrol Run > \uicontrol {Run Settings}. - \image qtcreator-python-run-settings.png {Python run settings} + \image qtcreator-python-run-settings.webp {Python run settings} The following table summarizes the settings for running Qt for Python applications. @@ -45,7 +45,7 @@ \li Setting \li Value \row - \li \uicontrol Interpreter + \li \uicontrol Python \li Path to the Python executable. \row \li \uicontrol {Buffered output} diff --git a/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc b/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc index ae3ef8b93ec..51275cfea4d 100644 --- a/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc +++ b/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -28,11 +28,10 @@ class: \list 1 - \li Select \uicontrol File > \uicontrol {New Project} > - \uicontrol {Application (Qt for Python)} > \uicontrol {Empty Window} - > \uicontrol Choose. - - The \uicontrol {Project Location} dialog opens. + \li Go to \uicontrol File > \uicontrol {New Project}. + \li Select \uicontrol {Application (Qt for Python)} > + \uicontrol {Empty Window} > \uicontrol Choose to open the + \uicontrol {Project Location} dialog. \image qtcreator-new-qt-for-python-app-widgets-project-location.webp {Project Location dialog} \li In \uicontrol {Name}, enter the project name. For example, \e {hello_world}. @@ -49,16 +48,14 @@ \li In \uicontrol {Project file}, enter a name for the project file. \li Select \uicontrol{Next} or \uicontrol Continue to open the \uicontrol {Define Project Details} dialog. - \image qtcreator-new-qt-for-python-app-widgets-project-details.webp {Define Project Details dialog} + \image qtcreator-new-qt-for-python-app-project-details.webp {Define Project Details dialog} \li In \uicontrol {PySide version}, select the PySide version of the generated code. - \li In \uicontrol {Interpreter}, select the Python interpreter to use for - the project. - \li Select the \uicontrol {Create new virtual environment} check box to - use a clean \l{https://docs.python.org/3/library/venv.html}{Python} - environment that is independent of your global Python installation. - \li In \uicontrol {Path to virtual environment}, specify the directory - where to create the environment. + \li Select \uicontrol{Next} or \uicontrol Continue to open the + \uicontrol {Kit Selection} dialog. + \image qtcreator-new-project-qt-for-python-kit-selection.webp {Selecting a kit for a Python project} + \li Select Qt for Python kits for building, deploying, and running the + project. \li Select \uicontrol{Next} or \uicontrol Continue. \li Review the project settings, and select \uicontrol {Finish} (on Windows and Linux) or \uicontrol Done (on \macos) to create the @@ -71,6 +68,8 @@ \li \c {hellow_world.pyproject}, which lists the files in the Python project. \li \c {mywidget.py}, which has some boilerplate code for a class. + \li \c {reguirements.txt}, which stores the PySide version of the + generated code. \endlist \section1 Adding Qt Widgets Imports diff --git a/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc b/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc index 868d500ae6c..9357ad2ea70 100644 --- a/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc +++ b/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -18,16 +18,19 @@ \image qtcreator-new-qt-for-python-app-qt-quick-empty-project-ready.webp {A small Qt Quick application} + For more examples of creating Qt for Python applications, see + \l {https://doc.qt.io/qtforpython/tutorials/index.html} + {Qt for Python Examples and Tutorials}. + \section1 Creating an Empty Project To create a Qt for Python application that has a main QML file: \list 1 - \li Select \uicontrol File > \uicontrol {New Project} > - \uicontrol {Application (Qt for Python)} > - \uicontrol {Qt Quick Application - Empty} > \uicontrol Choose. - - The \uicontrol {Project Location} dialog opens. + \li Go to \uicontrol File > \uicontrol {New Project}. + \li Select \uicontrol {Application (Qt for Python)} > + \uicontrol {Qt Quick Application - Empty} > \uicontrol Choose to + open the \uicontrol {Project Location} dialog. \image qtcreator-new-qt-for-python-app-qt-quick-empty-project-location.webp {Project Location dialog} \li In \uicontrol {Name}, enter the project name. For example, \e {hello_world_quick}. @@ -35,17 +38,14 @@ For example, \c {C:\Qt\examples}. \li Select \uicontrol{Next} (on Windows and Linux) or \uicontrol Continue (on \macos) to open the \uicontrol {Define Project Details} dialog. - \image qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp {Define Project Details dialog} + \image qtcreator-new-qt-for-python-app-project-details.webp {Define Project Details dialog} \li In \uicontrol {PySide version}, select the PySide version of the generated code. - \li In \uicontrol {Interpreter}, select the Python interpreter to use for - the project. - \li Select the \uicontrol {Create new virtual environment} check box to - use a clean \l{https://docs.python.org/3/library/venv.html}{Python} - environment that is independent of your global Python installation. - \li In \uicontrol {Path to virtual environment}, specify the directory - where to create the environment. - \li Select \uicontrol{Next} or \uicontrol Continue. + \li Select \uicontrol{Next} or \uicontrol Continue to open the + \uicontrol {Kit Selection} dialog. + \image qtcreator-new-project-qt-for-python-kit-selection.webp {Selecting a kit for a Python project} + \li Select Qt for Python kits for building, deploying, and running the + project. \li Review the project settings, and select \uicontrol {Finish} (on Windows and Linux) or \uicontrol Done (on \macos) to create the project. @@ -58,6 +58,8 @@ project. \li \c {main.py}, which has some boilerplate code. \li \c {main.qml}, which imports Qt Quick controls. + \li \c {reguirements.txt}, which stores the PySide version of the + generated code. \endlist \section1 Adding Qt Quick Imports From d167b16ac2561982a1b304ae523f701662bafd6a Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 14 Feb 2024 16:10:03 +0100 Subject: [PATCH 085/128] Axivion: Redo lazy fetching of issues Change-Id: I8c29c6494c651602fa443c76a6c9e3848f352a66 Reviewed-by: Marcus Tillmanns Reviewed-by: hjk Reviewed-by: Jarek Kobus --- src/plugins/axivion/CMakeLists.txt | 4 +- src/plugins/axivion/axivion.qbs | 4 +- src/plugins/axivion/axivionoutputpane.cpp | 87 +++++----- src/plugins/axivion/axivionplugin.cpp | 1 - src/plugins/axivion/dynamiclistmodel.cpp | 184 ++++++++++++++++++++++ src/plugins/axivion/dynamiclistmodel.h | 67 ++++++++ 6 files changed, 303 insertions(+), 44 deletions(-) create mode 100644 src/plugins/axivion/dynamiclistmodel.cpp create mode 100644 src/plugins/axivion/dynamiclistmodel.h diff --git a/src/plugins/axivion/CMakeLists.txt b/src/plugins/axivion/CMakeLists.txt index c3e2f5e7eaa..561ad680e61 100644 --- a/src/plugins/axivion/CMakeLists.txt +++ b/src/plugins/axivion/CMakeLists.txt @@ -6,12 +6,14 @@ add_qtc_plugin(Axivion axivion.qrc axivionoutputpane.cpp axivionoutputpane.h axivionplugin.cpp axivionplugin.h - axivionprojectsettings.h axivionprojectsettings.cpp + axivionprojectsettings.cpp axivionprojectsettings.h axivionsettings.cpp axivionsettings.h axiviontr.h credentialquery.h credentialquery.cpp dashboard/dto.cpp dashboard/dto.h dashboard/concat.cpp dashboard/concat.h dashboard/error.h dashboard/error.cpp + dashboard/error.cpp dashboard/error.h + dynamiclistmodel.cpp dynamiclistmodel.h issueheaderview.cpp issueheaderview.h ) diff --git a/src/plugins/axivion/axivion.qbs b/src/plugins/axivion/axivion.qbs index 70188feb3e4..0e808f65806 100644 --- a/src/plugins/axivion/axivion.qbs +++ b/src/plugins/axivion/axivion.qbs @@ -23,8 +23,10 @@ QtcPlugin { "axivionsettings.cpp", "axivionsettings.h", "axiviontr.h", - "credentialquery.h", "credentialquery.cpp", + "credentialquery.h", + "dynamiclistmodel.cpp", + "dynamiclistmodel.h", "issueheaderview.cpp", "issueheaderview.h", ] diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 5be8521b67f..7a45584bace 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -7,6 +7,7 @@ #include "axiviontr.h" #include "dashboard/dto.h" #include "issueheaderview.h" +#include "dynamiclistmodel.h" #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -191,16 +190,27 @@ void DashboardWidget::updateUi() addValuesWidgets(Tr::tr("Total:"), allTotal, allAdded, allRemoved, row); } -class IssueTreeItem final : public StaticTreeItem +class IssueListItem final : public ListItem { public: - IssueTreeItem(const QString &id, const QStringList &data, const QStringList &toolTips) - : StaticTreeItem(data, toolTips) + IssueListItem(int row, const QString &id, const QStringList &data, const QStringList &toolTips) + : ListItem(row) , m_id(id) + , m_data(data) + , m_toolTips(toolTips) {} void setLinks(const Links &links) { m_links = links; } + QVariant data(int column, int role) const + { + if (role == Qt::DisplayRole && column >= 0 && column < m_data.size()) + return m_data.at(column); + if (role == Qt::ToolTipRole && column >= 0 && column < m_toolTips.size()) + return m_toolTips.at(column); + return {}; + } + bool setData(int column, const QVariant &value, int role) final { if (role == BaseTreeView::ItemActivatedRole) { @@ -217,11 +227,13 @@ public: fetchIssueInfo(m_id); return true; } - return StaticTreeItem::setData(column, value, role); + return ListItem::setData(column, value, role); } private: const QString m_id; + QStringList m_data; + QStringList m_toolTips; Links m_links; }; @@ -233,14 +245,14 @@ public: private: void updateTable(); - void addIssues(const Dto::IssueTableDto &dto); + void addIssues(const Dto::IssueTableDto &dto, int startRow); void onSearchParameterChanged(); void updateBasicProjectInfo(std::optional info); void setFiltersEnabled(bool enabled); IssueListSearch searchFromUi() const; void fetchTable(); void fetchIssues(const IssueListSearch &search); - void fetchMoreIssues(); + void onFetchRequested(int startRow, int limit); QString m_currentPrefix; QString m_currentProject; @@ -256,9 +268,9 @@ private: QLabel *m_totalRows = nullptr; BaseTreeView *m_issuesView = nullptr; IssueHeaderView *m_headerView = nullptr; - TreeModel<> *m_issuesModel = nullptr; + DynamicListModel *m_issuesModel = nullptr; int m_totalRowCount = 0; - int m_lastRequestedOffset = 0; + bool m_fetchingMore = false; QStringList m_userNames; QStringList m_versionDates; TaskTreeRunner m_taskTreeRunner; @@ -320,18 +332,9 @@ IssuesWidget::IssuesWidget(QWidget *parent) m_issuesView->setHeader(m_headerView); m_issuesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_issuesView->enableColumnHiding(); - m_issuesModel = new TreeModel(this); + m_issuesModel = new DynamicListModel(this); m_issuesView->setModel(m_issuesModel); - auto sb = m_issuesView->verticalScrollBar(); - if (QTC_GUARD(sb)) { - connect(sb, &QAbstractSlider::valueChanged, sb, [this, sb](int value) { - if (value >= sb->maximum() - 50) { - if (m_issuesModel->rowCount() < m_totalRowCount) - fetchMoreIssues(); - } - }); - } - + connect(m_issuesModel, &DynamicListModel::fetchRequested, this, &IssuesWidget::onFetchRequested); m_totalRows = new QLabel(Tr::tr("Total rows:"), this); using namespace Layouting; @@ -374,8 +377,6 @@ void IssuesWidget::updateTable() if (!m_currentTableInfo) return; - // update issues table layout - for now just simple approach - TreeModel<> *issuesModel = new TreeModel(this); QStringList columnHeaders; QStringList hiddenColumns; QList sortableColumns; @@ -389,17 +390,14 @@ void IssuesWidget::updateTable() m_removedFilter->setText("0"); m_totalRows->setText(Tr::tr("Total rows:")); - issuesModel->setHeader(columnHeaders); - - auto oldModel = m_issuesModel; - m_issuesModel = issuesModel; - m_issuesView->setModel(issuesModel); + m_issuesModel->clear(); + m_issuesModel->setHeader(columnHeaders); m_headerView->setSortableColumns(sortableColumns); - delete oldModel; + int counter = 0; for (const QString &header : std::as_const(columnHeaders)) m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header)); - m_issuesView->header()->resizeSections(QHeaderView::ResizeToContents); + m_headerView->resizeSections(QHeaderView::ResizeToContents); } static Links linksForIssue(const std::map &issueRow) @@ -427,11 +425,13 @@ static Links linksForIssue(const std::map &issueRow) return links; } -void IssuesWidget::addIssues(const Dto::IssueTableDto &dto) +void IssuesWidget::addIssues(const Dto::IssueTableDto &dto, int startRow) { + m_fetchingMore = false; QTC_ASSERT(m_currentTableInfo.has_value(), return); if (dto.totalRowCount.has_value()) { m_totalRowCount = dto.totalRowCount.value(); + m_issuesModel->setExpectedRowCount(m_totalRowCount); m_totalRows->setText(Tr::tr("Total rows:") + ' ' + QString::number(m_totalRowCount)); } if (dto.totalAddedCount.has_value()) @@ -441,6 +441,7 @@ void IssuesWidget::addIssues(const Dto::IssueTableDto &dto) const std::vector &tableColumns = m_currentTableInfo->columns; const std::vector> &rows = dto.rows; + QList items; for (const auto &row : rows) { QString id; QStringList data; @@ -455,10 +456,11 @@ void IssuesWidget::addIssues(const Dto::IssueTableDto &dto) data << value; } } - IssueTreeItem *it = new IssueTreeItem(id, data, data); + IssueListItem *it = new IssueListItem(startRow++, id, data, data); it->setLinks(linksForIssue(row)); - m_issuesModel->rootItem()->appendChild(it); + items.append(it); } + m_issuesModel->setItems(items); } void IssuesWidget::onSearchParameterChanged() @@ -467,10 +469,10 @@ void IssuesWidget::onSearchParameterChanged() m_removedFilter->setText("0"); m_totalRows->setText(Tr::tr("Total rows:")); - m_issuesModel->rootItem()->removeChildren(); + m_issuesModel->clear(); // new "first" time lookup m_totalRowCount = 0; - m_lastRequestedOffset = 0; + m_fetchingMore = false; IssueListSearch search = searchFromUi(); search.computeTotalRowCount = true; fetchIssues(search); @@ -598,7 +600,7 @@ void IssuesWidget::fetchTable() }; const auto setupHandler = [this](TaskTree *) { m_totalRowCount = 0; - m_lastRequestedOffset = 0; + m_fetchingMore = false; m_currentTableInfo.reset(); m_issuesView->showProgressIndicator(); }; @@ -618,20 +620,23 @@ void IssuesWidget::fetchTable() void IssuesWidget::fetchIssues(const IssueListSearch &search) { - const auto issuesHandler = [this](const Dto::IssueTableDto &dto) { addIssues(dto); }; + const auto issuesHandler = [this, startRow = search.offset](const Dto::IssueTableDto &dto) { + addIssues(dto, startRow); + }; const auto setupHandler = [this](TaskTree *) { m_issuesView->showProgressIndicator(); }; const auto doneHandler = [this](DoneWith) { m_issuesView->hideProgressIndicator(); }; m_taskTreeRunner.start(issueTableRecipe(search, issuesHandler), setupHandler, doneHandler); } -void IssuesWidget::fetchMoreIssues() +void IssuesWidget::onFetchRequested(int startRow, int limit) { - if (m_lastRequestedOffset == m_issuesModel->rowCount()) + if (m_fetchingMore) return; + m_fetchingMore = true; IssueListSearch search = searchFromUi(); - m_lastRequestedOffset = m_issuesModel->rowCount(); - search.offset = m_lastRequestedOffset; + search.offset = startRow; + search.limit = limit; fetchIssues(search); } diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 558f7909744..3fb567e22cd 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -696,7 +696,6 @@ Group issueTableRecipe(const IssueListSearch &search, const IssueTableHandler &h const QUrl url = urlForProject(dd->m_currentProjectInfo.value().name + '/') .resolved(QString("issues" + query)); - return fetchDataRecipe(url, handler); } diff --git a/src/plugins/axivion/dynamiclistmodel.cpp b/src/plugins/axivion/dynamiclistmodel.cpp new file mode 100644 index 00000000000..45819995963 --- /dev/null +++ b/src/plugins/axivion/dynamiclistmodel.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "dynamiclistmodel.h" + +#include "axiviontr.h" + +#include +#include + +namespace Axivion::Internal { + +constexpr int pageSize = 150; + +DynamicListModel::DynamicListModel(QObject *parent) + : QAbstractItemModel(parent) +{ + m_fetchMoreTimer.setSingleShot(true); + m_fetchMoreTimer.setInterval(50); + connect(&m_fetchMoreTimer, &QTimer::timeout, this, &DynamicListModel::fetchNow); +} + +DynamicListModel::~DynamicListModel() +{ + clear(); +} + +QModelIndex DynamicListModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) + return {}; + if (row < m_expectedRowCount.value_or(m_children.size())) { + auto it = m_children.constFind(row); + return createIndex(row, column, it != m_children.constEnd() ? it.value() : nullptr); + } + return {}; +} + +QModelIndex DynamicListModel::parent(const QModelIndex &/*child*/) const +{ + return {}; +} + +int DynamicListModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) // for simplicity only single level + return 0; + + return m_expectedRowCount.value_or(m_children.size()); +} + +int DynamicListModel::columnCount(const QModelIndex &/*parent*/) const +{ + return m_columnCount; +} + +QVariant DynamicListModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (!index.isValid() || row < 0 || row > m_expectedRowCount.value_or(m_children.size())) + return {}; + + auto item = m_children.constFind(row); + if (item != m_children.cend()) + return item.value()->data(index.column(), role); + + if ((row < m_lastFetch || row > m_lastFetchEnd) && (row < m_fetchStart || row > m_fetchEnd)) + const_cast(this)->onNeedFetch(row); + if (role == Qt::DisplayRole && index.column() == 0) + return Tr::tr("Fetching..."); // TODO improve/customize? + if (role == Qt::ForegroundRole && index.column() == 0) + return Utils::creatorTheme()->color(Utils::Theme::TextColorDisabled); + return {}; +} + +bool DynamicListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + auto found = m_children.constFind(index.row()); + if (found == m_children.constEnd()) + return false; + return found.value()->setData(index.column(), value, role); +} + +QVariant DynamicListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section < m_header.size()) + return m_header.at(section); + return {}; +} + +void DynamicListModel::setItems(const QList &items) +{ + m_lastFetchEnd = -1; // FIXME wrong.. better a callback? - should happen for failed requests too + // for simplicity we assume an ordered list and no non-existing items between first and last + if (items.isEmpty()) + return; + // to keep it simple here, we expect the expectedRowCount to be set *before* adding items + QTC_ASSERT(m_expectedRowCount, setExpectedRowCount(items.size())); + if (int lastRow = items.last()->row; lastRow > *m_expectedRowCount) + m_expectedRowCount.emplace(lastRow); + + emit layoutAboutToBeChanged(); + auto end = m_children.end(); + for (ListItem *it : items) { + auto found = m_children.find(it->row); // check for old data to be removed + ListItem *old = nullptr; + if (found != end) + old = found.value(); + m_children.insert(it->row, it); + delete old; + } + emit dataChanged(indexForItem(items.first()), indexForItem(items.last())); + emit layoutChanged(); +} + +void DynamicListModel::clear() +{ + beginResetModel(); + qDeleteAll(m_children); + m_children.clear(); + m_expectedRowCount.reset(); + endResetModel(); +} + +void DynamicListModel::setExpectedRowCount(int expected) +{ + QTC_ASSERT(expected >= m_children.size(), return); + if (expected == m_children.size()) + return; + beginInsertRows({}, m_children.size(), expected); + m_expectedRowCount.emplace(expected); + endInsertRows(); +} + +void DynamicListModel::setHeader(const QStringList &header) +{ + m_header = header; + m_columnCount = m_header.size(); +} + +QModelIndex DynamicListModel::indexForItem(const ListItem *item) const +{ + QTC_ASSERT(item, return {}); + auto found = m_children.constFind(item->row); + if (found == m_children.cend()) + return {}; + QTC_ASSERT(found.value() == item, return {}); + return createIndex(item->row, 0, item); +} + +void DynamicListModel::onNeedFetch(int row) +{ + m_fetchStart = row; + m_fetchEnd = row + pageSize; + if (m_fetchStart < 0) + return; + m_fetchMoreTimer.start(); +} + +void DynamicListModel::fetchNow() +{ + const int old = m_lastFetch; + m_lastFetch = m_fetchStart; // we need the "original" fetch request to avoid endless loop + m_lastFetchEnd = m_fetchStart + pageSize; + + if (old != -1) { + const int diff = old - m_fetchStart; + if (0 < diff && diff < pageSize) { + m_fetchStart = qMax(old - pageSize, 0); + } else if (0 > diff && diff > -pageSize) { + m_fetchStart = old + pageSize; + if (m_expectedRowCount && m_fetchStart > *m_expectedRowCount) + m_fetchStart = *m_expectedRowCount; + } + } + + QTC_CHECK(m_expectedRowCount ? m_fetchStart <= *m_expectedRowCount + : m_fetchStart >= m_children.size()); + emit fetchRequested(m_fetchStart, pageSize); + m_fetchStart = -1; + m_fetchEnd = -1; +} + +} // namespace Axivion::Internal diff --git a/src/plugins/axivion/dynamiclistmodel.h b/src/plugins/axivion/dynamiclistmodel.h new file mode 100644 index 00000000000..1e83a572c35 --- /dev/null +++ b/src/plugins/axivion/dynamiclistmodel.h @@ -0,0 +1,67 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include +#include +#include + +#include + +namespace Axivion::Internal { + +class ListItem +{ +public: + explicit ListItem(int row) : row(row) {} + virtual ~ListItem() = default; + virtual bool setData(int /*column*/, const QVariant &/*value*/, int /*role*/) { return false; } + virtual QVariant data(int /*column*/, int /*role*/) const { return {}; } + + const int row; +}; + +class DynamicListModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit DynamicListModel(QObject *parent = nullptr); + ~DynamicListModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex&, const QVariant &value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + void setItems(const QList &items); + void clear(); + + void setExpectedRowCount(int expected); + void setHeader(const QStringList &header); + + QModelIndex indexForItem(const ListItem *item) const; + +signals: + void fetchRequested(int startRow, int limit); + +private: + void onNeedFetch(int row); + void fetchNow(); + + QHash m_children; + QStringList m_header; + QTimer m_fetchMoreTimer; + std::optional m_expectedRowCount = {}; + int m_fetchStart = -1; + int m_fetchEnd = -1; + int m_lastFetch = -1; + int m_lastFetchEnd = -1; + int m_columnCount = 0; +}; + +} // namespace Axivion::Internal From 09c8bd841b5fe9280d18227a714c011426b18ac3 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 23 Feb 2024 10:37:54 +0100 Subject: [PATCH 086/128] Axivion: Improve sort icons Palette colors may not be correctly populated depending on the platform, so use some common available one. Change-Id: I5f660aaefbd5882a255da4a25c3e15edc3fe3643 Reviewed-by: hjk --- src/plugins/axivion/issueheaderview.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp index 460dd711489..dfb4c11eec3 100644 --- a/src/plugins/axivion/issueheaderview.cpp +++ b/src/plugins/axivion/issueheaderview.cpp @@ -15,14 +15,14 @@ constexpr int ICON_SIZE = 16; static QIcon iconForSorted(SortOrder order) { const Utils::Icon UNSORTED( - {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteButtonTextDisabled}, - {":/axivion/images/sortDesc.png", Utils::Theme::PaletteButtonTextDisabled}}); + {{":/axivion/images/sortAsc.png", Utils::Theme::IconsDisabledColor}, + {":/axivion/images/sortDesc.png", Utils::Theme::IconsDisabledColor}}); const Utils::Icon SORT_ASC( - {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteButtonText}, - {":/axivion/images/sortDesc.png", Utils::Theme::PaletteButtonTextDisabled}}); + {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteText}, + {":/axivion/images/sortDesc.png", Utils::Theme::IconsDisabledColor}}); const Utils::Icon SORT_DESC( - {{":/axivion/images/sortAsc.png", Utils::Theme::PaletteButtonTextDisabled}, - {":/axivion/images/sortDesc.png", Utils::Theme::PaletteButtonText}}); + {{":/axivion/images/sortAsc.png", Utils::Theme::IconsDisabledColor}, + {":/axivion/images/sortDesc.png", Utils::Theme::PaletteText}}); static const QIcon unsorted = UNSORTED.icon(); static const QIcon sortedAsc = SORT_ASC.icon(); static const QIcon sortedDesc = SORT_DESC.icon(); From feb70c1d7b78a622586642223d93296ad63a8171 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 23 Feb 2024 12:11:13 +0100 Subject: [PATCH 087/128] Update qbs submodule to HEAD of 2.3 branch Change-Id: I62099a9605b2d9deeb6d36be3c43e088b9cd2578 Reviewed-by: Reviewed-by: Christian Stenger Reviewed-by: Qt CI Bot --- src/shared/qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/qbs b/src/shared/qbs index bd0c59e572f..60a18f09fa5 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit bd0c59e572f95953337177e68fa32cb0c4aa1e29 +Subproject commit 60a18f09fa547af064fb851e72b816ee25bf71a3 From c0bbf285a7eecfbc461290042015e0b23db2909e Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 23 Feb 2024 11:30:55 +0100 Subject: [PATCH 088/128] CppEditor: Fix indentation with inline namespaces After encountering the namespace keyword, we have to backtrack the state change introduced by the inline keyword. Fixes: QTCREATORBUG-22071 Change-Id: I543976622d1a56b2c61d68da6ec3eee8b6d0d5b3 Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger Reviewed-by: --- src/plugins/cppeditor/cppcodeformatter.cpp | 1 + tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/cppeditor/cppcodeformatter.cpp b/src/plugins/cppeditor/cppcodeformatter.cpp index 6b30e78fa6c..e0e22fc6fe1 100644 --- a/src/plugins/cppeditor/cppcodeformatter.cpp +++ b/src/plugins/cppeditor/cppcodeformatter.cpp @@ -151,6 +151,7 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) case T_OPERATOR: enter(operator_declaration); break; case T_GREATER_GREATER: break; case T_LBRACKET: break; + case T_NAMESPACE: leave(); enter(namespace_start); break; default: tryExpression(true); break; } break; diff --git a/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp b/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp index ce57e939ee5..82b5cba5b19 100644 --- a/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp +++ b/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp @@ -1544,8 +1544,8 @@ void tst_CodeFormatter::indentNamespace() << Line("};") << Line("}") << Line("int j;") - << Line("namespace {") - << Line("namespace Foo {") + << Line("inline namespace {") + << Line("inline namespace Foo {") << Line("int j;") << Line("}") << Line("int j;") @@ -1585,9 +1585,9 @@ void tst_CodeFormatter::indentNamespace2() << Line(" };") << Line("}") << Line("int j;") - << Line("namespace {") + << Line("inline namespace {") << Line(" int j;") - << Line(" namespace Foo {") + << Line(" inline namespace Foo {") << Line(" int j;") << Line(" }") << Line(" int j;") From 026de59be9d7fdc07cc4f96eebab3d9a448d9845 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 23 Feb 2024 11:07:27 +0100 Subject: [PATCH 089/128] Meson: Fix reading of tool settings We were accumulating copies of the auto-detected system ninja setting since the QVariantMap retrieved with toMap() was empty as it is a Store nowadays. Change-Id: Id983b5f1bf5ff18d6f43a71ebb406520e7655705 Reviewed-by: Reviewed-by: Christian Stenger --- .../toolssettingsaccessor.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp index 1fe381d0255..8e941ae21b3 100644 --- a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp +++ b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp @@ -77,14 +77,14 @@ std::vector ToolsSettingsAccessor::loadMesonTools() std::vector result; for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) { Key name = entryName(toolIndex); - if (data.contains(name)) { - const auto map = data[name].toMap(); - auto type = map.value(ToolsSettings::TOOL_TYPE_KEY, ToolsSettings::TOOL_TYPE_MESON); - if (type == ToolsSettings::TOOL_TYPE_NINJA) - result.emplace_back(fromVariantMap(storeFromVariant(data[name]))); - else - result.emplace_back(fromVariantMap(storeFromVariant(data[name]))); - } + Store store = storeFromVariant(data[name]); + QString type = store.value(ToolsSettings::TOOL_TYPE_KEY).toString(); + if (type == ToolsSettings::TOOL_TYPE_NINJA) + result.emplace_back(fromVariantMap(storeFromVariant(data[name]))); + else if (type == ToolsSettings::TOOL_TYPE_MESON) + result.emplace_back(fromVariantMap(storeFromVariant(data[name]))); + else + QTC_CHECK(false); } return result; } From 0acc91a6e89e1d6c7ccea4c718a283b6b3813291 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Fri, 23 Feb 2024 11:46:19 +0100 Subject: [PATCH 090/128] LLVM: Use lldb-dap.exe on Windows for qtcreatorcdbext LLVM 18 has renamed lldb-vscode.exe to lldb-dap.exe. Change-Id: I4b0b108ebebc552a4c843f4c8a2fa563132176cd Reviewed-by: Reviewed-by: David Schulz --- src/libs/qtcreatorcdbext/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt index 97307164f65..4359d79d546 100644 --- a/src/libs/qtcreatorcdbext/CMakeLists.txt +++ b/src/libs/qtcreatorcdbext/CMakeLists.txt @@ -205,7 +205,7 @@ if (_library_enabled) # Deploy lldb.exe and its Python dependency find_package(Clang QUIET) if (LLVM_TOOLS_BINARY_DIR AND LLVM_LIBRARY_DIRS) - foreach(lldb_file lldb.exe lldb-vscode.exe liblldb.dll python311.zip python311.dll) + foreach(lldb_file lldb.exe lldb-dap.exe liblldb.dll python311.zip python311.dll) if (EXISTS ${LLVM_TOOLS_BINARY_DIR}/${lldb_file}) install(FILES ${LLVM_TOOLS_BINARY_DIR}/${lldb_file} DESTINATION bin/clang/bin From 5ff64ed95cf781d92e995d208d1d7a4229757208 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 14:42:20 +0100 Subject: [PATCH 091/128] Axivion: Remove unneeded m_fetchingMore field Use m_taskTreeRunner.isRunning() instead. Change-Id: I2feed612cf8eabfc5ec160a4586b3158aa4d90b8 Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 7a45584bace..5164cd743b6 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -270,7 +270,6 @@ private: IssueHeaderView *m_headerView = nullptr; DynamicListModel *m_issuesModel = nullptr; int m_totalRowCount = 0; - bool m_fetchingMore = false; QStringList m_userNames; QStringList m_versionDates; TaskTreeRunner m_taskTreeRunner; @@ -427,7 +426,6 @@ static Links linksForIssue(const std::map &issueRow) void IssuesWidget::addIssues(const Dto::IssueTableDto &dto, int startRow) { - m_fetchingMore = false; QTC_ASSERT(m_currentTableInfo.has_value(), return); if (dto.totalRowCount.has_value()) { m_totalRowCount = dto.totalRowCount.value(); @@ -472,7 +470,6 @@ void IssuesWidget::onSearchParameterChanged() m_issuesModel->clear(); // new "first" time lookup m_totalRowCount = 0; - m_fetchingMore = false; IssueListSearch search = searchFromUi(); search.computeTotalRowCount = true; fetchIssues(search); @@ -600,7 +597,6 @@ void IssuesWidget::fetchTable() }; const auto setupHandler = [this](TaskTree *) { m_totalRowCount = 0; - m_fetchingMore = false; m_currentTableInfo.reset(); m_issuesView->showProgressIndicator(); }; @@ -630,10 +626,9 @@ void IssuesWidget::fetchIssues(const IssueListSearch &search) void IssuesWidget::onFetchRequested(int startRow, int limit) { - if (m_fetchingMore) + if (m_taskTreeRunner.isRunning()) return; - m_fetchingMore = true; IssueListSearch search = searchFromUi(); search.offset = startRow; search.limit = limit; From e3e8b624179e572c96f7599c7a1c725fc3aabe00 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 23 Feb 2024 12:52:48 +0100 Subject: [PATCH 092/128] RemoteLinux: Make deployment method downgrade transparent Fixes: QTCREATORBUG-29710 Change-Id: I27f3252f8a3d9bc03f2abd096aae6f0df4a6448c Reviewed-by: Qt CI Bot Reviewed-by: hjk Reviewed-by: --- src/plugins/remotelinux/genericdeploystep.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/remotelinux/genericdeploystep.cpp b/src/plugins/remotelinux/genericdeploystep.cpp index 23e8a1b8c75..bddc7bfcd3d 100644 --- a/src/plugins/remotelinux/genericdeploystep.cpp +++ b/src/plugins/remotelinux/genericdeploystep.cpp @@ -72,6 +72,7 @@ private: StringAspect flags{this}; BoolAspect ignoreMissingFiles{this}; SelectionAspect method{this}; + bool m_emittedDowngradeWarning = false; }; GroupItem GenericDeployStep::mkdirTask(const Storage &storage) @@ -153,6 +154,14 @@ GroupItem GenericDeployStep::transferTask(const Storage &storag break; } } + if (!m_emittedDowngradeWarning && transferMethod != preferredTransferMethod) { + addWarningMessage(Tr::tr("Transfer method was downgraded from \"%1\" to \"%2\". If " + "this is unexpected, please re-test device \"%3\".") + .arg(FileTransfer::transferMethodName(preferredTransferMethod), + FileTransfer::transferMethodName(transferMethod), + deviceConfiguration()->displayName())); + m_emittedDowngradeWarning = true; + } transfer.setTransferMethod(transferMethod); transfer.setRsyncFlags(flags()); From 09cc4507973165154e88622ded59d6b5ec8ca29f Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 23 Feb 2024 13:45:15 +0100 Subject: [PATCH 093/128] Axivion: Handle links more appropriate If the user clicks on a row's column holding a location information then use this explicitly instead of always using the first available link. Change-Id: Ifecbecac632a4bfd4d31614a8c43ab6c80de4819 Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 44 +++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 5164cd743b6..063fafdc988 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -190,6 +191,12 @@ void DashboardWidget::updateUi() addValuesWidgets(Tr::tr("Total:"), allTotal, allAdded, allRemoved, row); } +struct LinkWithColumns +{ + Link link; + QList columns; +}; + class IssueListItem final : public ListItem { public: @@ -200,7 +207,7 @@ public: , m_toolTips(toolTips) {} - void setLinks(const Links &links) { m_links = links; } + void setLinks(const QList &links) { m_links = links; } QVariant data(int column, int role) const { @@ -215,8 +222,10 @@ public: { if (role == BaseTreeView::ItemActivatedRole) { if (!m_links.isEmpty()) { - // TODO for now only simple - just the first.. - Link link = m_links.first(); + Link link + = Utils::findOr(m_links, m_links.first(), [column](const LinkWithColumns &link) { + return link.columns.contains(column); + }).link; Project *project = ProjectManager::startupProject(); FilePath baseDir = project ? project->projectDirectory() : FilePath{}; link.targetFilePath = baseDir.resolvePath(link.targetFilePath); @@ -234,7 +243,7 @@ private: const QString m_id; QStringList m_data; QStringList m_toolTips; - Links m_links; + QList m_links; }; class IssuesWidget : public QScrollArea @@ -399,19 +408,34 @@ void IssuesWidget::updateTable() m_headerView->resizeSections(QHeaderView::ResizeToContents); } -static Links linksForIssue(const std::map &issueRow) +static QList linksForIssue(const std::map &issueRow, + const std::vector &columnInfos) { - Links links; + QList links; auto end = issueRow.end(); - auto findAndAppend = [&links, &issueRow, &end](const QString &path, const QString &line) { + auto findColumn = [columnInfos](const QString &columnKey) { + int col = 0; + for (auto it = columnInfos.cbegin(), end = columnInfos.cend(); it != end; ++it) { + if (it->key == columnKey) + return col; + ++col; + } + return -1; + }; + auto findAndAppend = [&links, &issueRow, &columnInfos, &findColumn, &end](const QString &path, + const QString &line) { + QList columns; auto it = issueRow.find(path); if (it != end) { Link link{ FilePath::fromUserInput(it->second.getString()) }; + columns.append(findColumn(it->first)); it = issueRow.find(line); - if (it != end) + if (it != end) { link.targetLine = it->second.getDouble(); - links.append(link); + columns.append(findColumn(it->first)); + } + links.append({link, columns}); } }; // do these always? or just for their "expected" kind @@ -455,7 +479,7 @@ void IssuesWidget::addIssues(const Dto::IssueTableDto &dto, int startRow) } } IssueListItem *it = new IssueListItem(startRow++, id, data, data); - it->setLinks(linksForIssue(row)); + it->setLinks(linksForIssue(row, tableColumns)); items.append(it); } m_issuesModel->setItems(items); From 51998c52392df63d2ca354b06077df00ee52e13d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 22 Feb 2024 20:34:28 +0100 Subject: [PATCH 094/128] Axivion: Make use of the column widths coming from the server Change-Id: Idcce80ed987ed02cb0197cfca93d32deb715f95c Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 3 ++- src/plugins/axivion/issueheaderview.cpp | 6 ++++-- src/plugins/axivion/issueheaderview.h | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 063fafdc988..d4462f06133 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -388,11 +388,13 @@ void IssuesWidget::updateTable() QStringList columnHeaders; QStringList hiddenColumns; QList sortableColumns; + QList columnWidths; for (const Dto::ColumnInfoDto &column : m_currentTableInfo->columns) { columnHeaders << column.header.value_or(column.key); if (!column.showByDefault) hiddenColumns << column.key; sortableColumns << column.canSort; + columnWidths << column.width; } m_addedFilter->setText("0"); m_removedFilter->setText("0"); @@ -401,7 +403,6 @@ void IssuesWidget::updateTable() m_issuesModel->clear(); m_issuesModel->setHeader(columnHeaders); m_headerView->setSortableColumns(sortableColumns); - int counter = 0; for (const QString &header : std::as_const(columnHeaders)) m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header)); diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp index dfb4c11eec3..ceb2d520afa 100644 --- a/src/plugins/axivion/issueheaderview.cpp +++ b/src/plugins/axivion/issueheaderview.cpp @@ -115,9 +115,11 @@ void IssueHeaderView::onToggleSort(int index, SortOrder order) QSize IssueHeaderView::sectionSizeFromContents(int logicalIndex) const { - QSize size = QHeaderView::sectionSizeFromContents(logicalIndex); + const QSize oldSize = QHeaderView::sectionSizeFromContents(logicalIndex); + const QSize newSize = logicalIndex < m_columnWidths.size() + ? QSize(qMax(m_columnWidths.at(logicalIndex), oldSize.width()), oldSize.height()) : oldSize; // add icon size and margin (2) - return QSize{size.width() + ICON_SIZE + 2, qMax(size.height(), ICON_SIZE)}; + return QSize{newSize.width() + ICON_SIZE + 2, qMax(newSize.height(), ICON_SIZE)}; } void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const diff --git a/src/plugins/axivion/issueheaderview.h b/src/plugins/axivion/issueheaderview.h index 3d6f271a762..1fbbd9b76fd 100644 --- a/src/plugins/axivion/issueheaderview.h +++ b/src/plugins/axivion/issueheaderview.h @@ -16,9 +16,11 @@ class IssueHeaderView : public QHeaderView public: explicit IssueHeaderView(QWidget *parent = nullptr) : QHeaderView(Qt::Horizontal, parent) {} void setSortableColumns(const QList &sortable); + void setColumnWidths(const QList &widths) { m_columnWidths = widths; } SortOrder currentSortOrder() const { return m_currentSortOrder; } int currentSortColumn() const; + signals: void sortTriggered(); @@ -36,6 +38,7 @@ private: int m_currentSortIndex = -1; SortOrder m_currentSortOrder = SortOrder::None; QList m_sortableColumns; + QList m_columnWidths; }; } // namespace Axivion::Internal From d5f50a3fa44bdf9230561290c66cfe890d1994c1 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 15:54:00 +0100 Subject: [PATCH 095/128] Axivion: Remove warning about unused lambda capture Amends 09cc4507973165154e88622ded59d6b5ec8ca29f Change-Id: I44a5ba2a0f83f9f75ba84b1b6470e875027c290b Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index d4462f06133..1b69fce8b55 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -424,7 +424,7 @@ static QList linksForIssue(const std::map &i } return -1; }; - auto findAndAppend = [&links, &issueRow, &columnInfos, &findColumn, &end](const QString &path, + auto findAndAppend = [&links, &issueRow, &findColumn, &end](const QString &path, const QString &line) { QList columns; auto it = issueRow.find(path); From 8b4578f0c4a0096c70fcc275b27c27f96b6e00ce Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 16:06:43 +0100 Subject: [PATCH 096/128] Axivion: Amends the commit regarding column sizes When resolving a conflit the key line got removed by mistake. Amends 51998c52392df63d2ca354b06077df00ee52e13d Change-Id: I74af26d23e7ac0180d1b351d7ecf6d6876499d89 Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 1b69fce8b55..e6272d46ccf 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -403,6 +403,7 @@ void IssuesWidget::updateTable() m_issuesModel->clear(); m_issuesModel->setHeader(columnHeaders); m_headerView->setSortableColumns(sortableColumns); + m_headerView->setColumnWidths(columnWidths); int counter = 0; for (const QString &header : std::as_const(columnHeaders)) m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header)); From d061e29dd307c42a7fd79091be14f745752c27c5 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 23 Feb 2024 16:27:58 +0100 Subject: [PATCH 097/128] Utils: Allow different codec for Process stdout and stderr Change-Id: Ie6a3fb74a447a599c492e38ec83a2330f3cb4cd8 Reviewed-by: hjk --- src/libs/utils/process.cpp | 27 +++++++++++++++++++++------ src/libs/utils/process.h | 6 ++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/libs/utils/process.cpp b/src/libs/utils/process.cpp index 4eacb28986f..651dd5b49b1 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/process.cpp @@ -847,7 +847,9 @@ public: qint64 m_applicationMainThreadId = 0; ProcessResultData m_resultData; - QTextCodec *m_codec = QTextCodec::codecForLocale(); + QTextCodec *m_stdOutCodec = QTextCodec::codecForLocale(); + QTextCodec *m_stdErrCodec = QTextCodec::codecForLocale(); + ProcessResult m_result = ProcessResult::StartFailed; ChannelBuffer m_stdOut; ChannelBuffer m_stdErr; @@ -1102,9 +1104,9 @@ void ProcessPrivate::sendControlSignal(ControlSignal controlSignal) void ProcessPrivate::clearForRun() { m_stdOut.clearForRun(); - m_stdOut.codec = m_codec; + m_stdOut.codec = m_stdOutCodec; m_stdErr.clearForRun(); - m_stdErr.codec = m_codec; + m_stdErr.codec = m_stdErrCodec; m_result = ProcessResult::StartFailed; m_startTimestamp = {}; m_doneTimestamp = {}; @@ -1729,13 +1731,13 @@ QByteArray Process::rawStdErr() const QString Process::stdOut() const { QTC_CHECK(d->m_stdOut.keepRawData); - return d->m_codec->toUnicode(d->m_stdOut.rawData); + return d->m_stdOutCodec->toUnicode(d->m_stdOut.rawData); } QString Process::stdErr() const { QTC_CHECK(d->m_stdErr.keepRawData); - return d->m_codec->toUnicode(d->m_stdErr.rawData); + return d->m_stdErrCodec->toUnicode(d->m_stdErr.rawData); } QString Process::cleanedStdOut() const @@ -1850,7 +1852,20 @@ void ChannelBuffer::handleRest() void Process::setCodec(QTextCodec *c) { QTC_ASSERT(c, return); - d->m_codec = c; + d->m_stdOutCodec = c; + d->m_stdErrCodec = c; +} + +void Process::setStdOutCodec(QTextCodec *c) +{ + QTC_ASSERT(c, return); + d->m_stdOutCodec = c; +} + +void Process::setStdErrCodec(QTextCodec *c) +{ + QTC_ASSERT(c, return); + d->m_stdErrCodec = c; } void Process::setTimeOutMessageBoxEnabled(bool v) diff --git a/src/libs/utils/process.h b/src/libs/utils/process.h index d425f76eed3..694c1a12a86 100644 --- a/src/libs/utils/process.h +++ b/src/libs/utils/process.h @@ -148,8 +148,10 @@ public: void runBlocking(std::chrono::seconds timeout = std::chrono::seconds(10), EventLoopMode eventLoopMode = EventLoopMode::Off); - // TODO: We should specify the purpose of the codec, e.g. setCodecForStandardChannel() - void setCodec(QTextCodec *c); + void setCodec(QTextCodec *c); // for stdOut and stdErr + void setStdOutCodec(QTextCodec *c); + void setStdErrCodec(QTextCodec *c); + void setTimeOutMessageBoxEnabled(bool); void setStdOutCallback(const TextChannelCallback &callback); From 5590efa30bc28bff6c530b280258dfc6945f061d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 16:54:14 +0100 Subject: [PATCH 098/128] FunctionDeclDefLinkFinder: Don't delete the watcher from its signal handler Use std::unique_ptr instead, as QScopedPointer doesn't offer release(). Change-Id: Ifbe42dca5b266930e1000a50441995023b89b802 Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/cppfunctiondecldeflink.cpp | 11 +++++------ src/plugins/cppeditor/cppfunctiondecldeflink.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index 9552ba21984..710397e3a81 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -41,21 +41,20 @@ namespace Internal { FunctionDeclDefLinkFinder::FunctionDeclDefLinkFinder(QObject *parent) : QObject(parent) -{ -} +{} void FunctionDeclDefLinkFinder::onFutureDone() { std::shared_ptr link = m_watcher->result(); - m_watcher.reset(); + m_watcher.release()->deleteLater(); if (link) { link->linkSelection = m_scannedSelection; link->nameSelection = m_nameSelection; if (m_nameSelection.selectedText() != link->nameInitial) link.reset(); } - m_scannedSelection = QTextCursor(); - m_nameSelection = QTextCursor(); + m_scannedSelection = {}; + m_nameSelection = {}; if (link) emit foundLink(link); } @@ -234,7 +233,7 @@ void FunctionDeclDefLinkFinder::startFindLinkAt( // handle the rest in a thread m_watcher.reset(new QFutureWatcher >()); - connect(m_watcher.data(), &QFutureWatcherBase::finished, this, &FunctionDeclDefLinkFinder::onFutureDone); + connect(m_watcher.get(), &QFutureWatcherBase::finished, this, &FunctionDeclDefLinkFinder::onFutureDone); m_watcher->setFuture(Utils::asyncRun(findLinkHelper, result, refactoringChanges)); } diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.h b/src/plugins/cppeditor/cppfunctiondecldeflink.h index 4ffce9bf915..8fe6219e339 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.h +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.h @@ -36,7 +36,7 @@ private: QTextCursor m_scannedSelection; QTextCursor m_nameSelection; - QScopedPointer>> m_watcher; + std::unique_ptr>> m_watcher; }; class FunctionDeclDefLink From 53f8956fb8a4fd606a140e804a9d67a20c55f380 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 17:01:03 +0100 Subject: [PATCH 099/128] FunctionDeclDefLinkFinder: Ensure the old futures are synchronized Change-Id: I68b271f85d2bb319230529d4b0b074c05c12ba4c Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/cppfunctiondecldeflink.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index 710397e3a81..e489045f351 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -14,8 +14,6 @@ #include #include -#include -#include #include #include @@ -23,7 +21,13 @@ #include #include +#include + +#include +#include + #include +#include #include #include #include @@ -235,6 +239,7 @@ void FunctionDeclDefLinkFinder::startFindLinkAt( m_watcher.reset(new QFutureWatcher >()); connect(m_watcher.get(), &QFutureWatcherBase::finished, this, &FunctionDeclDefLinkFinder::onFutureDone); m_watcher->setFuture(Utils::asyncRun(findLinkHelper, result, refactoringChanges)); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future()); } bool FunctionDeclDefLink::isValid() const From b0592abbf04280dc26f1c8f5936b69ff10fb7b5d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 17:24:44 +0100 Subject: [PATCH 100/128] DebuggerItemConfigWidget: Ensure the futures are synchronized Don't leave possibly running futures on Creator shutdown. Change-Id: I6f4253d657b2b61112c15c9c144be10fcc8ed0cf Reviewed-by: hjk --- src/plugins/debugger/debuggeritemmanager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index 2644e3fa125..b4fb2af2dea 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include #include @@ -19,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -480,14 +483,14 @@ void DebuggerItemConfigWidget::binaryPathHasChanged() if (!m_generic) { m_updateWatcher.cancel(); - DebuggerItem tmp; if (m_binaryChooser->filePath().isExecutableFile()) { - tmp = item(); - m_updateWatcher.setFuture(Utils::asyncRun([tmp]() mutable { + m_updateWatcher.setFuture(Utils::asyncRun([tmp = item()]() mutable { tmp.reinitializeFromFile(); return tmp; })); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_updateWatcher.future()); } else { + const DebuggerItem tmp; setAbis(tmp.abiNames()); m_version->setText(tmp.version()); m_engineType = tmp.engineType(); From 5406f11a2b77b1088f8d9b9a525f25b204cfbad7 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 22 Feb 2024 17:30:39 +0100 Subject: [PATCH 101/128] Debugger: Robustify lldb line parser The original version might have missed to recognize a @\r\n combination or multi-byte UTF-8 code units when they were split across to readyRead() handlers. Change-Id: Ib543fa0070b63d25b44be429a3759964d65e878e Reviewed-by: Marcus Tillmanns Reviewed-by: Alessandro Portale Reviewed-by: --- src/plugins/debugger/lldb/lldbengine.cpp | 25 ++++++++++++++---------- src/plugins/debugger/lldb/lldbengine.h | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index deb42544a87..1bb409bf948 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -841,18 +841,23 @@ void LldbEngine::readLldbStandardError() void LldbEngine::readLldbStandardOutput() { - QByteArray outba = m_lldbProc.readAllRawStandardOutput(); - outba.replace("\r\n", "\n"); - QString out = QString::fromUtf8(outba); - showMessage(out, LogOutput); + const QByteArray out = m_lldbProc.readAllRawStandardOutput(); + showMessage(QString::fromUtf8(out), LogOutput); m_inbuffer.append(out); while (true) { - int pos = m_inbuffer.indexOf("@\n"); - if (pos == -1) - break; - QString response = m_inbuffer.left(pos).trimmed(); - m_inbuffer = m_inbuffer.mid(pos + 2); - emit outputReady(response); + if (int pos = m_inbuffer.indexOf("@\n"); pos >= 0) { + const QByteArray response = m_inbuffer.left(pos).trimmed(); + m_inbuffer = m_inbuffer.mid(pos + 2); + emit outputReady(QString::fromUtf8(response)); + continue; + } + if (int pos = m_inbuffer.indexOf("@\r\n"); pos >= 0) { + const QByteArray response = m_inbuffer.left(pos).trimmed(); + m_inbuffer = m_inbuffer.mid(pos + 3); + emit outputReady(QString::fromUtf8(response)); + continue; + } + break; } } diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index db11af9c104..57cbd1e7c75 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -111,7 +111,7 @@ private: private: DebuggerCommand m_lastDebuggableCommand; - QString m_inbuffer; + QByteArray m_inbuffer; QString m_scriptFileName; Utils::Process m_lldbProc; From 462caec2789a52656ca31f8d52a87d35729b67eb Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 26 Feb 2024 09:37:00 +0100 Subject: [PATCH 102/128] Utils: Small string fixes Change-Id: I2fd6bf14b1cb35f61ced841e3deea011fd54ff03 Reviewed-by: Leena Miettinen Reviewed-by: Jarek Kobus --- src/libs/utils/devicefileaccess.cpp | 4 ++-- src/libs/utils/process.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index 39bdc736e09..a38a098d380 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -568,8 +568,8 @@ static bool checkToRefuseRemoveStandardLocationDirectory(const QString &dirPath, { if (QStandardPaths::standardLocations(location).contains(dirPath)) { if (error) { - *error = Tr::tr("Refusing to remove your %1 directory.").arg( - QStandardPaths::displayName(location)); + *error = Tr::tr("Refusing to remove the standard directory \"%1\".") + .arg(QStandardPaths::displayName(location)); } return false; } diff --git a/src/libs/utils/process.cpp b/src/libs/utils/process.cpp index 651dd5b49b1..5bf44513654 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/process.cpp @@ -1665,8 +1665,7 @@ QString Process::exitMessage(const CommandLine &command, ProcessResult result, case ProcessResult::Canceled: // TODO: We might want to format it nicely when bigger than 1 second, e.g. 1,324 s. // Also when it's bigger than 1 minute, 1 hour, etc... - return Tr::tr("The command \"%1\" was canceled after (%2 ms).") - .arg(cmd).arg(duration.count()); + return Tr::tr("The command \"%1\" was canceled after %2 ms.").arg(cmd).arg(duration.count()); } return {}; } From 2d7af40007eae8088aabfd75bbe0dc4bfc47cf2e Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Thu, 22 Feb 2024 16:22:46 +0100 Subject: [PATCH 103/128] CMakePM: Consider envvar QTC_CMAKE_USE_JUNCTIONS This envrionment variable can be set globally or locally for a project via '.shared' configuration. See https://doc.qt.io/qtcreator/creator-sharing-project-settings.html for details. Task-number: QTCREATORBUG-30385 Change-Id: Ie28caac8f4232678c5761b54ce370d1aeb139389 Reviewed-by: Thomas Hartmann Reviewed-by: hjk --- .../cmakeprojectmanager/cmaketoolmanager.cpp | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp index 16473a6e79a..e20c3c58eba 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -69,6 +70,7 @@ typedef struct _REPARSE_DATA_BUFFER { #endif using namespace Core; +using namespace ProjectExplorer; using namespace Utils; namespace CMakeProjectManager { @@ -451,8 +453,19 @@ FilePath CMakeToolManager::mappedFilePath(const FilePath &path) if (path.needsDevice()) return path; - Internal::settings(); - if (!Internal::settings().useJunctionsForSourceAndBuildDirectories()) + auto project = ProjectManager::startupProject(); + auto environment = Environment::systemEnvironment(); + if (project) + environment.modify(project->additionalEnvironment()); + const bool enableJunctions + = QVariant( + environment.value_or("QTC_CMAKE_USE_JUNCTIONS", + Internal::settings().useJunctionsForSourceAndBuildDirectories() + ? "1" + : "0")) + .toBool(); + + if (!enableJunctions) return path; if (!d->m_junctionsDir.isDir()) @@ -583,14 +596,17 @@ CMakeToolManagerPrivate::CMakeToolManagerPrivate() m_junctionsDir = FilePath::fromString(*std::min_element(locations.begin(), locations.end())) .pathAppended("QtCreator/Links"); - if (Utils::qtcEnvironmentVariableIsSet("QTC_CMAKE_JUNCTIONS_DIR")) { - m_junctionsDir = FilePath::fromUserInput( - Utils::qtcEnvironmentVariable("QTC_CMAKE_JUNCTIONS_DIR")); - } - if (Utils::qtcEnvironmentVariableIsSet("QTC_CMAKE_JUNCTIONS_HASH_LENGTH")) { + auto project = ProjectManager::startupProject(); + auto environment = Environment::systemEnvironment(); + if (project) + environment.modify(project->additionalEnvironment()); + + if (environment.hasKey("QTC_CMAKE_JUNCTIONS_DIR")) + m_junctionsDir = FilePath::fromUserInput(environment.value("QTC_CMAKE_JUNCTIONS_DIR")); + + if (environment.hasKey("QTC_CMAKE_JUNCTIONS_HASH_LENGTH")) { bool ok = false; - const int hashLength - = Utils::qtcEnvironmentVariableIntValue("QTC_CMAKE_JUNCTIONS_HASH_LENGTH", &ok); + const int hashLength = environment.value("QTC_CMAKE_JUNCTIONS_HASH_LENGTH").toInt(&ok); if (ok && hashLength >= 4 && hashLength < 32) m_junctionsHashLength = hashLength; } From 44f6bbf2607cdc8bdac204e3ee9d987fb75d9243 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 26 Feb 2024 09:00:08 +0100 Subject: [PATCH 104/128] Python: Wrap some file paths and kit name with quotes Change-Id: If147eae864013d410239d69184cdb9a4bc4479eb Reviewed-by: Leena Miettinen --- src/plugins/python/pythonkitaspect.cpp | 15 +++++++++------ src/plugins/python/pythonproject.cpp | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/plugins/python/pythonkitaspect.cpp b/src/plugins/python/pythonkitaspect.cpp index 2c700f58ae5..26ad44bad48 100644 --- a/src/plugins/python/pythonkitaspect.cpp +++ b/src/plugins/python/pythonkitaspect.cpp @@ -108,15 +108,17 @@ public: result << BuildSystemTask(Task::Error, Tr::tr("No Python setup.")); } else if (!path.exists()) { result << BuildSystemTask(Task::Error, - Tr::tr("Python %1 not found.").arg(path.toUserOutput())); + Tr::tr("Python \"%1\" not found.").arg(path.toUserOutput())); } else if (!path.isExecutableFile()) { result << BuildSystemTask(Task::Error, - Tr::tr("Python %1 not executable.").arg(path.toUserOutput())); + Tr::tr("Python \"%1\" is not executable.") + .arg(path.toUserOutput())); } else { if (!pipIsUsable(path)) { result << BuildSystemTask( Task::Warning, - Tr::tr("Python %1 does not contain a usable pip. Pip is used to install python " + Tr::tr("Python \"%1\" does not contain a usable pip. Pip is used to install " + "python " "packages from the Python Package Index, like PySide and the python " "language server. If you want to use any of that functionality " "ensure pip is installed for that python.") @@ -125,9 +127,10 @@ public: if (!venvIsUsable(path)) { result << BuildSystemTask( Task::Warning, - Tr::tr("Python %1 does not contain a usable venv. venv is the recommended way " - "to isolate a development environment for a project from the globally " - "installed python.") + Tr::tr( + "Python \"%1\" does not contain a usable venv. venv is the recommended way " + "to isolate a development environment for a project from the globally " + "installed python.") .arg(path.toUserOutput())); } } diff --git a/src/plugins/python/pythonproject.cpp b/src/plugins/python/pythonproject.cpp index 49216b2d6a3..129782773d3 100644 --- a/src/plugins/python/pythonproject.cpp +++ b/src/plugins/python/pythonproject.cpp @@ -33,8 +33,9 @@ Tasks PythonProject::projectIssues(const Kit *k) const { if (PythonKitAspect::python(k)) return {}; - return {BuildSystemTask{Task::Error, - Tr::tr("No python interpreter set for kit %1").arg(k->displayName())}}; + return { + BuildSystemTask{Task::Error, + Tr::tr("No python interpreter set for kit \"%1\"").arg(k->displayName())}}; } PythonProjectNode::PythonProjectNode(const FilePath &path) From 902a42a0f2d7f6e1e627fb72aae0286320995fdb Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 26 Feb 2024 08:52:10 +0100 Subject: [PATCH 105/128] Perforce: Add missing full stop Change-Id: Ic8e78fd3465ad3a0301f4bce65ced5f9948753f3 Reviewed-by: Leena Miettinen Reviewed-by: Jarek Kobus --- src/plugins/perforce/perforceplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 42e2bbfcb18..7e8dca82656 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -1499,7 +1499,7 @@ QString fileNameFromPerforceName(const QString &perforceName, bool quiet) //: Failed to run p4 "where" to resolve a Perforce file name to a local //: file system name. VcsOutputWindow::appendError( - Tr::tr("Error running \"where\" on %1: The file is not mapped") + Tr::tr("Error running \"where\" on %1: The file is not mapped.") .arg(QDir::toNativeSeparators(perforceName))); } return {}; From d2cec31f0872199defcb7c9bd6dd3eada27f490e Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 23 Feb 2024 15:16:06 +0100 Subject: [PATCH 106/128] Git: Fix a label Settings labels are lower case and end with ":" Change-Id: I729d25569790899a8d26966914ee8b872a48bcb0 Reviewed-by: Leena Miettinen --- src/plugins/git/gitsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 95938a41d88..2e7f8bf2de4 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -33,7 +33,7 @@ GitSettings::GitSettings() path.setLabelText(Tr::tr("Prepend to PATH:")); path.setDisplayStyle(StringAspect::LineEditDisplay); - binaryPath.setLabelText(Tr::tr("Git Command")); + binaryPath.setLabelText(Tr::tr("Git command:")); binaryPath.setDefaultValue("git"); binaryPath.setExpectedKind(PathChooser::ExistingCommand); binaryPath.setHistoryCompleter("Git.Command.History"); From 76cd84b7cd631fcb4331e9558ca9b89a695bc942 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 23 Feb 2024 15:21:41 +0100 Subject: [PATCH 107/128] iOS: Fix some missing full stops Change-Id: Ie6c1b9dafc3e3fe349adcbe6011244136002cc01 Reviewed-by: Leena Miettinen --- share/qtcreator/translations/qtcreator_da.ts | 4 ++-- share/qtcreator/translations/qtcreator_de.ts | 2 +- share/qtcreator/translations/qtcreator_fr.ts | 4 ++-- share/qtcreator/translations/qtcreator_hr.ts | 4 ++-- share/qtcreator/translations/qtcreator_pl.ts | 4 ++-- share/qtcreator/translations/qtcreator_ru.ts | 4 ++-- share/qtcreator/translations/qtcreator_zh_CN.ts | 2 +- src/plugins/ios/devicectlutils.cpp | 2 +- src/plugins/ios/iosrunner.cpp | 2 +- src/plugins/ios/iossettingspage.cpp | 8 +++++--- 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index 8421c0a6242..6e004586ba2 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -18346,8 +18346,8 @@ Id'er skal begynde med et lille bogstav. Simulator start - Cannot start simulator (%1, %2) in current state: %3 - Kan ikke starte simulator (%1, %2) i aktuelle tilstand: %3 + Cannot start simulator (%1, %2) in current state: %3. + Kan ikke starte simulator (%1, %2) i aktuelle tilstand: %3. simulator start diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 89ac1623b2d..3e349e87747 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -33897,7 +33897,7 @@ Möchten Sie sie überschreiben? Simulator starten - Cannot start simulator (%1, %2) in current state: %3 + Cannot start simulator (%1, %2) in current state: %3. Der Simulator (%1, %2) kann im momentanen Zustand (%3) nicht gestartet werden. diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index 6f9ade5c4d3..5a5c4dd6dd7 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -33467,8 +33467,8 @@ Souhaitez-vous les écraser ? - Cannot start simulator (%1, %2) in current state: %3 - Impossible de démarrer le simulateur (%1, %2) dans l'état actuel : %3 + Cannot start simulator (%1, %2) in current state: %3. + Impossible de démarrer le simulateur (%1, %2) dans l'état actuel : %3. simulator start diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index 5cb6489b7ed..08d49c74894 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -4546,8 +4546,8 @@ Dodaj, izmijeni i ukloni filtre dokumenata koji određuju skup dokumentacije pri - Cannot start simulator (%1, %2) in current state: %3 - Nije moguće pokrenuti simulatora (%1, %2) u trenutačnom stanju: %3 + Cannot start simulator (%1, %2) in current state: %3. + Nije moguće pokrenuti simulatora (%1, %2) u trenutačnom stanju: %3. simulator start diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts index 91aba3691e2..15ae93c1b15 100644 --- a/share/qtcreator/translations/qtcreator_pl.ts +++ b/share/qtcreator/translations/qtcreator_pl.ts @@ -33930,8 +33930,8 @@ Czy nadpisać je? Uruchomienie symulatora - Cannot start simulator (%1, %2) in current state: %3 - Nie można uruchomić symulatora (%1, %2) w bieżącym stanie: %3 + Cannot start simulator (%1, %2) in current state: %3. + Nie można uruchomić symulatora (%1, %2) w bieżącym stanie: %3. simulator start diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index ed5d78ba7a2..047034c9052 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -22984,8 +22984,8 @@ Ids must begin with a lowercase letter. Запустить эмулятор - Cannot start simulator (%1, %2) in current state: %3 - Невозможно запустить эмулятор (%1, %2) в текущем состоянии: %3 + Cannot start simulator (%1, %2) in current state: %3. + Невозможно запустить эмулятор (%1, %2) в текущем состоянии: %3. simulator start diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts index 4e361b52d34..43021c4804e 100644 --- a/share/qtcreator/translations/qtcreator_zh_CN.ts +++ b/share/qtcreator/translations/qtcreator_zh_CN.ts @@ -23217,7 +23217,7 @@ Id必须以小写字母开头。 - Cannot start simulator (%1, %2) in current state: %3 + Cannot start simulator (%1, %2) in current state: %3. diff --git a/src/plugins/ios/devicectlutils.cpp b/src/plugins/ios/devicectlutils.cpp index f8d47caf0be..7bbed859b27 100644 --- a/src/plugins/ios/devicectlutils.cpp +++ b/src/plugins/ios/devicectlutils.cpp @@ -46,7 +46,7 @@ expected_str parseDevicectlResult(const QByteArray &rawOutput) } const QJsonValue resultValue = jsonOutput["result"]; if (resultValue.isUndefined()) { - return make_unexpected(Tr::tr("Failed to parse devicectl output: 'result' is missing")); + return make_unexpected(Tr::tr("Failed to parse devicectl output: \"result\" is missing.")); } return resultValue; } diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index e2ed4191292..02d47e914fc 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -264,7 +264,7 @@ GroupItem DeviceCtlRunner::launchTask(const QString &bundleIdentifier) void DeviceCtlRunner::reportStoppedImpl() { - appendMessage(Tr::tr("\"%1\" exited").arg(m_bundlePath.toUserOutput()), + appendMessage(Tr::tr("\"%1\" exited.").arg(m_bundlePath.toUserOutput()), Utils::NormalMessageFormat); reportStopped(); } diff --git a/src/plugins/ios/iossettingspage.cpp b/src/plugins/ios/iossettingspage.cpp index ad81b81b19d..042dfca9af6 100644 --- a/src/plugins/ios/iossettingspage.cpp +++ b/src/plugins/ios/iossettingspage.cpp @@ -204,9 +204,11 @@ void IosSettingsWidget::onStart() QList> futureList; for (const SimulatorInfo &info : simulatorInfoList) { if (!info.isShutdown()) { - statusDialog->addMessage(Tr::tr("Cannot start simulator (%1, %2) in current state: %3") - .arg(info.name).arg(info.runtimeName).arg(info.state), - Utils::StdErrFormat); + statusDialog->addMessage(Tr::tr("Cannot start simulator (%1, %2) in current state: %3.") + .arg(info.name) + .arg(info.runtimeName) + .arg(info.state), + Utils::StdErrFormat); } else { futureList << QFuture(Utils::onResultReady( SimulatorControl::startSimulator(info.identifier), this, From fdc84e84580f7640d2dcc6e2d8065abf57596da3 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 26 Feb 2024 08:20:08 +0100 Subject: [PATCH 108/128] Axivion: Tweak path display Change-Id: I9148530eff5dbc4757c2f4b46c1b55a895043f1f Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index e6272d46ccf..132ebebf65d 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -469,6 +469,7 @@ void IssuesWidget::addIssues(const Dto::IssueTableDto &dto, int startRow) for (const auto &row : rows) { QString id; QStringList data; + QStringList toolTips; for (const auto &column : tableColumns) { const auto it = row.find(column.key); if (it != row.end()) { @@ -477,10 +478,15 @@ void IssuesWidget::addIssues(const Dto::IssueTableDto &dto, int startRow) value.prepend(m_currentPrefix); id = value; } + toolTips << value; + if (column.key.toLower().endsWith("path")) { + const FilePath fp = FilePath::fromUserInput(value); + value = QString("%1 [%2]").arg(fp.fileName(), fp.path()); + } data << value; } } - IssueListItem *it = new IssueListItem(startRow++, id, data, data); + IssueListItem *it = new IssueListItem(startRow++, id, data, toolTips); it->setLinks(linksForIssue(row, tableColumns)); items.append(it); } From 6dda17a2b3547a21f27893c7ff94952a719ffe21 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 26 Feb 2024 08:46:37 +0100 Subject: [PATCH 109/128] Axivion: Take alignments into account Change-Id: I3d14b88562dbd9eca79ba954e6d01206aaec450e Reviewed-by: hjk --- src/plugins/axivion/axivionoutputpane.cpp | 14 ++++++++++++++ src/plugins/axivion/dynamiclistmodel.cpp | 12 +++++++++++- src/plugins/axivion/dynamiclistmodel.h | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 132ebebf65d..a13b63dd3a2 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -380,6 +380,17 @@ void IssuesWidget::updateUi() fetchTable(); } +static Qt::Alignment alignmentFromString(const QString &str) +{ + if (str == "left") + return Qt::AlignLeft; + if (str == "right") + return Qt::AlignRight; + if (str == "center") + return Qt::AlignHCenter; + return Qt::AlignLeft; +} + void IssuesWidget::updateTable() { if (!m_currentTableInfo) @@ -389,12 +400,14 @@ void IssuesWidget::updateTable() QStringList hiddenColumns; QList sortableColumns; QList columnWidths; + QList alignments; for (const Dto::ColumnInfoDto &column : m_currentTableInfo->columns) { columnHeaders << column.header.value_or(column.key); if (!column.showByDefault) hiddenColumns << column.key; sortableColumns << column.canSort; columnWidths << column.width; + alignments << alignmentFromString(column.alignment); } m_addedFilter->setText("0"); m_removedFilter->setText("0"); @@ -402,6 +415,7 @@ void IssuesWidget::updateTable() m_issuesModel->clear(); m_issuesModel->setHeader(columnHeaders); + m_issuesModel->setAlignments(alignments); m_headerView->setSortableColumns(sortableColumns); m_headerView->setColumnWidths(columnWidths); int counter = 0; diff --git a/src/plugins/axivion/dynamiclistmodel.cpp b/src/plugins/axivion/dynamiclistmodel.cpp index 45819995963..70abe087bdb 100644 --- a/src/plugins/axivion/dynamiclistmodel.cpp +++ b/src/plugins/axivion/dynamiclistmodel.cpp @@ -61,8 +61,13 @@ QVariant DynamicListModel::data(const QModelIndex &index, int role) const return {}; auto item = m_children.constFind(row); - if (item != m_children.cend()) + if (item != m_children.cend()) { + if (role == Qt::TextAlignmentRole) { + if (!m_alignments.isEmpty() && index.column() < m_alignments.size()) + return QVariant::fromValue(m_alignments.at(index.column())); + } return item.value()->data(index.column(), role); + } if ((row < m_lastFetch || row > m_lastFetchEnd) && (row < m_fetchStart || row > m_fetchEnd)) const_cast(this)->onNeedFetch(row); @@ -138,6 +143,11 @@ void DynamicListModel::setHeader(const QStringList &header) m_columnCount = m_header.size(); } +void DynamicListModel::setAlignments(const QList &alignments) +{ + m_alignments = alignments; +} + QModelIndex DynamicListModel::indexForItem(const ListItem *item) const { QTC_ASSERT(item, return {}); diff --git a/src/plugins/axivion/dynamiclistmodel.h b/src/plugins/axivion/dynamiclistmodel.h index 1e83a572c35..d60f9f16159 100644 --- a/src/plugins/axivion/dynamiclistmodel.h +++ b/src/plugins/axivion/dynamiclistmodel.h @@ -43,6 +43,7 @@ public: void setExpectedRowCount(int expected); void setHeader(const QStringList &header); + void setAlignments(const QList &alignments); QModelIndex indexForItem(const ListItem *item) const; @@ -55,6 +56,7 @@ private: QHash m_children; QStringList m_header; + QList m_alignments; QTimer m_fetchMoreTimer; std::optional m_expectedRowCount = {}; int m_fetchStart = -1; From 2bee865f807605e56aae8e5b0bd642b28d72357c Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 18:55:02 +0100 Subject: [PATCH 110/128] CompilationDbParser: Ensure the futures are synchronized Don't leave possibly running futures on Creator shutdown. Change-Id: I8b0f800518edde638376013f993f5846df4d1753 Reviewed-by: Reviewed-by: Christian Kandeler --- .../compilationdatabaseprojectmanager/compilationdbparser.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp index 5f860956026..4ab893a9e6c 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp @@ -8,9 +8,12 @@ #include +#include + #include #include +#include #include #include @@ -187,6 +190,7 @@ void CompilationDbParser::start() "CompilationDatabase.Parse"); ++m_runningParserJobs; m_parserWatcher.setFuture(future); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future); } void CompilationDbParser::stop() From ff6e231170c992a184829bd9c94fbffcca15df7a Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 23 Feb 2024 18:38:42 +0100 Subject: [PATCH 111/128] CompilationDatabaseBuildSystem: Remove unused QFutureWatcher Change-Id: I6dd6769cb5aa71149735a7920701c8f6ad98ee03 Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot Reviewed-by: --- .../compilationdatabaseproject.cpp | 6 +----- .../compilationdatabaseproject.h | 3 --- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index 914c6f705f0..ebe7ff44753 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -332,11 +332,7 @@ CompilationDatabaseBuildSystem::CompilationDatabaseBuildSystem(Target *target) this, &CompilationDatabaseBuildSystem::updateDeploymentData); } -CompilationDatabaseBuildSystem::~CompilationDatabaseBuildSystem() -{ - m_parserWatcher.cancel(); - m_parserWatcher.waitForFinished(); -} +CompilationDatabaseBuildSystem::~CompilationDatabaseBuildSystem() = default; void CompilationDatabaseBuildSystem::triggerParsing() { diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h index 25561f5aac1..2b8faec0208 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.h @@ -13,8 +13,6 @@ #include -#include - namespace ProjectExplorer { class Kit; class ProjectUpdater; @@ -51,7 +49,6 @@ public: void updateDeploymentData(); void buildTreeAndProjectParts(); - QFutureWatcher m_parserWatcher; std::unique_ptr m_cppCodeModelUpdater; MimeBinaryCache m_mimeBinaryCache; QByteArray m_projectFileHash; From 51ddffefd87770c6d13a75fdaa8687e0587cd525 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 26 Feb 2024 10:57:08 +0100 Subject: [PATCH 112/128] QLiteHtml: Update submodule sha to include the crash fix Task-number: QTCREATORBUG-30427 Change-Id: Icc166c8bb895e5ce37887b12af97a5b785df2b29 Reviewed-by: Eike Ziller --- src/libs/qlitehtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/qlitehtml b/src/libs/qlitehtml index 9248bd827b3..7e8eb0f5eae 160000 --- a/src/libs/qlitehtml +++ b/src/libs/qlitehtml @@ -1 +1 @@ -Subproject commit 9248bd827b3859e6898860c15a63c6cd57ca5434 +Subproject commit 7e8eb0f5eaee53b0aeb04208bdaba74fcffc3a3f From 17a9cce898e19587d229e5f9b9f88aea6e1fa729 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 26 Feb 2024 11:30:42 +0100 Subject: [PATCH 113/128] Async: Avoid potential leak of QFutureWatcher Make QFutureWatcher a child of the receiver / guard inside the onResultReady() / onFinished() handlers. This prevents the leak of QFutureWatcher when the receiver / guard got deleted and the future never finishes. Change-Id: I5653593777ee394dff8cc3c4b7506d8c1907232f Reviewed-by: Eike Ziller --- src/libs/utils/async.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h index e1aacb126ef..af6fb3fd85d 100644 --- a/src/libs/utils/async.h +++ b/src/libs/utils/async.h @@ -54,7 +54,7 @@ auto asyncRun(Function &&function, Args &&...args) template const QFuture &onResultReady(const QFuture &future, R *receiver, void(R::*member)(const T &)) { - auto watcher = new QFutureWatcher(); + auto watcher = new QFutureWatcher(receiver); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, receiver, [=](int index) { (receiver->*member)(watcher->future().resultAt(index)); @@ -72,7 +72,7 @@ const QFuture &onResultReady(const QFuture &future, R *receiver, void(R::* template const QFuture &onResultReady(const QFuture &future, QObject *guard, Function f) { - auto watcher = new QFutureWatcher(); + auto watcher = new QFutureWatcher(guard); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, guard, [f, watcher](int index) { f(watcher->future().resultAt(index)); @@ -90,7 +90,7 @@ template const QFuture &onFinished(const QFuture &future, R *receiver, void (R::*member)(const QFuture &)) { - auto watcher = new QFutureWatcher(); + auto watcher = new QFutureWatcher(receiver); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::finished, receiver, [=] { (receiver->*member)(watcher->future()); }); @@ -107,7 +107,7 @@ const QFuture &onFinished(const QFuture &future, template const QFuture &onFinished(const QFuture &future, QObject *guard, Function f) { - auto watcher = new QFutureWatcher(); + auto watcher = new QFutureWatcher(guard); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::finished, guard, [f, watcher] { f(watcher->future()); From 98ffe3d9d05cbcb3b7f12d9912e108a8e19b203d Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 22 Feb 2024 12:08:37 +0100 Subject: [PATCH 114/128] AutoTest: Small fixes in translatable strings Make the tool tip for useXMLOutput, quickCheckForDerivedTests, and parseMessages consistent, use rich text instead of manual line breaks. We use plural for the things that a locator filter locates (for filters that don't trigger an action). Beautify tool tip. Change-Id: I6276e9a0894d59670a45937e1c4227405c346f38 Reviewed-by: Leena Miettinen Reviewed-by: Reviewed-by: Christian Stenger --- share/qtcreator/translations/qtcreator_da.ts | 8 ++---- share/qtcreator/translations/qtcreator_de.ts | 14 +++-------- share/qtcreator/translations/qtcreator_fr.ts | 14 +++-------- share/qtcreator/translations/qtcreator_hr.ts | 8 ++---- share/qtcreator/translations/qtcreator_pl.ts | 7 ++---- share/qtcreator/translations/qtcreator_ru.ts | 8 ++---- .../qtcreator/translations/qtcreator_zh_CN.ts | 8 ++---- .../autotest/qtest/datataglocatorfilter.cpp | 2 +- .../autotest/qtest/qttestframework.cpp | 25 +++++++++++-------- 9 files changed, 34 insertions(+), 60 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index 6e004586ba2..43f21468465 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -1717,12 +1717,8 @@ Se Google Test-dokumentation for yderligere information om GTest-filtre.Perf - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. - XML-output anbefales, fordi det forhindre parsing-problemer, mens ren tekst er lettere at læse for mennesker. - -Advarsel: Ren tekst mangle nogle informationer, såsom varighed. + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. + XML-output anbefales, fordi det forhindre parsing-problemer, mens ren tekst er lettere at læse for mennesker.<p>Advarsel: Ren tekst mangle nogle informationer, såsom varighed. Select Run Configuration diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 3e349e87747..b15b6a1af2e 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -12413,10 +12413,8 @@ Weitere Informationen über GTest-Filter finden Sie in der Dokumenation von Goog Auf abgeleitete Qt Quick-Tests überprüfen - Search for Qt Quick tests that are derived from TestCase. -Warning: Enabling this feature significantly increases scan time. - Sucht nach Qt Quick-Tests, die von TestCase abgeleitet sind. -Achtung: Dies erhöht die zum Durchsuchen benötigte Zeit erheblich. + Search for Qt Quick tests that are derived from TestCase.<p>Warning: Enabling this feature significantly increases scan time. + Sucht nach Qt Quick-Tests, die von TestCase abgeleitet sind.<p>Achtung: Dies erhöht die zum Durchsuchen benötigte Zeit erheblich. Benchmark Metrics @@ -12463,12 +12461,8 @@ Achtung: Dies erhöht die zum Durchsuchen benötigte Zeit erheblich.Perf - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. - Die XML-Ausgabe ist empfehlenswert, weil sie Probleme beim Einlesen vermeidet. Reiner Text ist hingegen besser lesbar für Menschen. - -Warnung: Reinem Text fehlen manche Informationen, etwa die Dauer. + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. + Die XML-Ausgabe ist empfehlenswert, weil sie Probleme beim Einlesen vermeidet. Reiner Text ist hingegen besser lesbar für Menschen.<p>Warnung: Reinem Text fehlen manche Informationen, etwa die Dauer. Select Run Configuration diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index 5a5c4dd6dd7..fe4d87c0326 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -12063,12 +12063,8 @@ Voir la documentation de Google Test pour plus d'informations sur les filtr Utilise la sortie XML - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. - La sortie XML est recommandée : elle évite des problèmes d'analyse, alors que le texte brut est plus lisible pour un humain. - -Avertissement : le texte brut ne contient pas toutes les informations, telle que la durée. + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. + La sortie XML est recommandée : elle évite des problèmes d'analyse, alors que le texte brut est plus lisible pour un humain.<p>Avertissement : le texte brut ne contient pas toutes les informations, telle que la durée. Verbose benchmarks @@ -12099,10 +12095,8 @@ Avertissement : le texte brut ne contient pas toutes les informations, tell Vérifier la présence de tests dérivés de Qt Quick - Search for Qt Quick tests that are derived from TestCase. -Warning: Enabling this feature significantly increases scan time. - Recherche des tests Qt Quick dérivé de TestCase. -Avertissement : l'activation de cette fonctionnalité augmente significativement le temps de recherche. + Search for Qt Quick tests that are derived from TestCase.<p>Warning: Enabling this feature significantly increases scan time. + Recherche des tests Qt Quick dérivé de TestCase.<p>Avertissement : l'activation de cette fonctionnalité augmente significativement le temps de recherche. Benchmark Metrics diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index 08d49c74894..6d69f112adb 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -1135,12 +1135,8 @@ Dodatne dokumente o GTest filtrima potraži u Google Test dokumentaciji.Deaktiviraj rukovatelja urušivanja tijekom uklanjanja grešaka - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. - Preporuča se XML izlaz, jer izbjegava probleme s raščlanjivanjem, dok je običan tekst čitljiviji za čitanje. - -Upozorenje: Običan tekst propušta neke informacije, kao što je trajanje. + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. + Preporuča se XML izlaz, jer izbjegava probleme s raščlanjivanjem, dok je običan tekst čitljiviji za čitanje.<p>Upozorenje: Običan tekst propušta neke informacije, kao što je trajanje. Use XML output diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts index 15ae93c1b15..a59ebcb424c 100644 --- a/share/qtcreator/translations/qtcreator_pl.ts +++ b/share/qtcreator/translations/qtcreator_pl.ts @@ -12692,8 +12692,7 @@ See also Google Test settings. - Search for Qt Quick tests that are derived from TestCase. -Warning: Enabling this feature significantly increases scan time. + Search for Qt Quick tests that are derived from TestCase.<p>Warning: Enabling this feature significantly increases scan time. @@ -12713,9 +12712,7 @@ Warning: Enabling this feature significantly increases scan time. Używaj XML na wyjściu - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index 047034c9052..ac9a564f8b3 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -2745,12 +2745,8 @@ See Google Test documentation for further information on GTest filters. Логировать сигналы и слоты - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. - Рекомендуется вывод в формате XML, так как исключает проблемы при разборе. Простой же текст более удобен для чтения человеком. - -Предупреждение: простой текст не содержит некоторую информацию, например, длительность. + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. + Рекомендуется вывод в формате XML, так как исключает проблемы при разборе. Простой же текст более удобен для чтения человеком.<p>Предупреждение: простой текст не содержит некоторую информацию, например, длительность. Select Run Configuration diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts index 43021c4804e..7526f78c968 100644 --- a/share/qtcreator/translations/qtcreator_zh_CN.ts +++ b/share/qtcreator/translations/qtcreator_zh_CN.ts @@ -2950,12 +2950,8 @@ See Google Test documentation for further information on GTest filters. 使用 XML 输出 - XML output is recommended, because it avoids parsing issues, while plain text is more human readable. - -Warning: Plain text misses some information, such as duration. - 建议使用 XML 输出,它避免了一些解析问题,虽然纯文本可读性更好。 - -警告:纯文本丢失了一些信息,比如持续时间。 + XML output is recommended, because it avoids parsing issues, while plain text is more human readable.<p>Warning: Plain text misses some information, such as duration. + 建议使用 XML 输出,它避免了一些解析问题,虽然纯文本可读性更好。<p>警告:纯文本丢失了一些信息,比如持续时间。 Verbose benchmarks diff --git a/src/plugins/autotest/qtest/datataglocatorfilter.cpp b/src/plugins/autotest/qtest/datataglocatorfilter.cpp index bb0745d189e..e6a436666ce 100644 --- a/src/plugins/autotest/qtest/datataglocatorfilter.cpp +++ b/src/plugins/autotest/qtest/datataglocatorfilter.cpp @@ -70,7 +70,7 @@ DataTagLocatorFilter::DataTagLocatorFilter() { setId("Locate Qt Test data tags"); setDisplayName(Tr::tr("Locate Qt Test data tags")); - setDescription(Tr::tr("Locates a Qt Test data tag found inside the active project.")); + setDescription(Tr::tr("Locates Qt Test data tags found inside the active project.")); setDefaultShortcutString("qdt"); setPriority(Medium); using namespace ProjectExplorer; diff --git a/src/plugins/autotest/qtest/qttestframework.cpp b/src/plugins/autotest/qtest/qttestframework.cpp index 009acc81ab1..17970e19d70 100644 --- a/src/plugins/autotest/qtest/qttestframework.cpp +++ b/src/plugins/autotest/qtest/qttestframework.cpp @@ -73,9 +73,10 @@ QtTestFramework::QtTestFramework() useXMLOutput.setSettingsKey("UseXMLOutput"); useXMLOutput.setDefaultValue(true); useXMLOutput.setLabelText(Tr::tr("Use XML output")); - useXMLOutput.setToolTip(Tr::tr("XML output is recommended, because it avoids parsing issues, " - "while plain text is more human readable.\n\nWarning: " - "Plain text misses some information, such as duration.")); + useXMLOutput.setToolTip("" + + Tr::tr("XML output is recommended, because it avoids parsing issues, " + "while plain text is more human readable.

Warning: " + "Plain text misses some information, such as duration.")); verboseBench.setSettingsKey("VerboseBench"); verboseBench.setLabelText(Tr::tr("Verbose benchmarks")); @@ -98,18 +99,22 @@ QtTestFramework::QtTestFramework() quickCheckForDerivedTests.setDefaultValue(false); quickCheckForDerivedTests.setLabelText(Tr::tr("Check for derived Qt Quick tests")); quickCheckForDerivedTests.setToolTip( - Tr::tr("Search for Qt Quick tests that are derived from TestCase.\nWarning: Enabling this " - "feature significantly increases scan time.")); + "" + + Tr::tr( + "Search for Qt Quick tests that are derived from TestCase.

Warning: Enabling this " + "feature significantly increases scan time.")); parseMessages.setSettingsKey("ParseMessages"); parseMessages.setDefaultValue(false); parseMessages.setLabelText(Tr::tr("Find user-defined locations")); parseMessages.setToolTip( - Tr::tr("Parse messages for the pattern \"file://filepath:line\", where \":line\" is " - "optional, and use this as location information.\n" - "Warning: If the patterns are used in code, the location information for debug " - "messages and other messages might improve,\n" - "at the risk of some incorrect locations and lower performance.")); + "" + + Tr::tr("Parse messages for the following pattern and use it as location information:" + "

file://filepath:line
" + "where \":line\" is optional." + "

Warning: If the patterns are used in code, the location information for debug " + "messages and other messages might improve," + "at the risk of some incorrect locations and lower performance.")); readSettings(); maxWarnings.setEnabler(&limitWarnings); From 1b53c931c5bd544811cbdb51441c76b17c482b2a Mon Sep 17 00:00:00 2001 From: Semih Yavuz Date: Fri, 23 Feb 2024 11:44:17 +0100 Subject: [PATCH 115/128] qmllssettings: Fix incorrect detecting of latest version Update the latestVersion in the loop. Otherwise we always compare to initial value and consequently the last installed version incorrecyly becomes the latest version. Fixes: QTCREATORBUG-30423 Change-Id: I7083928c5e371f0337677eacf1b3b4da50358a7c Reviewed-by: Reviewed-by: Fabian Kosmale Reviewed-by: Ulf Hermann --- src/plugins/qmljseditor/qmllssettings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmljseditor/qmllssettings.cpp b/src/plugins/qmljseditor/qmllssettings.cpp index 76ce535fa79..15cb8908afb 100644 --- a/src/plugins/qmljseditor/qmllssettings.cpp +++ b/src/plugins/qmljseditor/qmllssettings.cpp @@ -49,6 +49,7 @@ static FilePath evaluateLatestQmlls() if (latestQmakeFilePath == qmakeNow && latestUniqueId >= v->uniqueId()) continue; } + latestVersion = vNow; latestQmlls = qmllsNow; latestQmakeFilePath = qmakeNow; latestUniqueId = uniqueIdNow; From b890b35e116ef9349afc78920b9e460e628eaa2f Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Sun, 25 Feb 2024 12:22:41 +0200 Subject: [PATCH 116/128] SyntaxHighlighting: Fix MSVC warning Implicit size_t -> int conversion. Upstream MR: https://invent.kde.org/frameworks/syntax-highlighting/-/merge_requests/601 Change-Id: I4208f0240c8a223fccf40add828499597e137c26 Reviewed-by: David Schulz --- src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h index 4aee1416818..9971f7f6602 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h @@ -36,7 +36,7 @@ public: return state.d.data(); } - int size() const + std::size_t size() const { return m_contextStack.size(); } From 6df43e8b61c9bc257c851d490cc43f5832ce2578 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 26 Feb 2024 11:53:09 +0100 Subject: [PATCH 117/128] Axivion: Improve tooltip Remove wordiness and just explain what enabling the checkbox does. Change-Id: Id4c03c1aa7fe7b87919036ffe39717e1a184d5f2 Reviewed-by: Eike Ziller Reviewed-by: Christian Stenger --- src/plugins/axivion/axivionsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index 8597fac9150..9b24fa51f02 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -108,7 +108,7 @@ AxivionSettings::AxivionSettings() highlightMarks.setSettingsKey("HighlightMarks"); highlightMarks.setLabelText(Tr::tr("Highlight marks")); - highlightMarks.setToolTip(Tr::tr("Check to enable issue marks on the scroll bar.")); + highlightMarks.setToolTip(Tr::tr("Marks issues on the scroll bar.")); highlightMarks.setDefaultValue(false); AspectContainer::readSettings(); From b10df4c1b3cdb0d2d9c36a0655af95085abe615e Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 23 Feb 2024 17:01:58 +0100 Subject: [PATCH 118/128] ProjectExplorer: Show full lines during compilation Especially on Windows with VC2022 & Ninja, the output would often display lines that ended in the middle until the next batch of output was added. This patch uses the Process facilities to only show full lines. Change-Id: I38fcb2e8cb115637d15181b39374c533db150c1a Reviewed-by: Reviewed-by: David Schulz --- .../projectexplorer/abstractprocessstep.cpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index 84a31135790..547599bc9d6 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -79,8 +79,6 @@ public: std::function m_environmentModifier; bool m_ignoreReturnValue = false; bool m_lowPriority = false; - std::unique_ptr stdOutDecoder; - std::unique_ptr stdErrDecoder; OutputFormatter *outputFormatter = nullptr; }; @@ -146,9 +144,6 @@ bool AbstractProcessStep::init() if (!setupProcessParameters(processParameters())) return false; - d->stdOutDecoder = std::make_unique(buildEnvironment().hasKey("VSLANG") - ? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForLocale()); - d->stdErrDecoder = std::make_unique(QTextCodec::codecForLocale()); return true; } @@ -197,14 +192,18 @@ bool AbstractProcessStep::setupProcess(Process &process) if (d->m_lowPriority && ProjectExplorerPlugin::projectExplorerSettings().lowBuildPriority) process.setLowPriority(); - connect(&process, &Process::readyReadStandardOutput, this, [this, &process] { - emit addOutput(d->stdOutDecoder->toUnicode(process.readAllRawStandardOutput()), - OutputFormat::Stdout, DontAppendNewline); + process.setStdOutCodec(buildEnvironment().hasKey("VSLANG") + ? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForLocale()); + process.setStdErrCodec(QTextCodec::codecForLocale()); + + process.setStdOutLineCallback([this](const QString &s){ + emit addOutput(s, OutputFormat::Stdout, DontAppendNewline); }); - connect(&process, &Process::readyReadStandardError, this, [this, &process] { - emit addOutput(d->stdErrDecoder->toUnicode(process.readAllRawStandardError()), - OutputFormat::Stderr, DontAppendNewline); + + process.setStdErrLineCallback([this](const QString &s){ + emit addOutput(s, OutputFormat::Stderr, DontAppendNewline); }); + connect(&process, &Process::started, this, [this] { ProcessParameters *params = d->m_displayedParams; emit addOutput(Tr::tr("Starting: \"%1\" %2") From b5e03b8fe2ce7bfe3ef782591bf19a6e452ef32d Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 26 Feb 2024 10:28:28 +0100 Subject: [PATCH 119/128] QtSupport: Reduce ExtraCompiler life time to plugin life time again Amends 6467797af2. The created extra compilers were parented to their factories, who gained a life time extension from 'end of plugin live' to static destruction when the factories were made static. However, this that time the likewise static QObjectCache in GeneratedCodeModelSupport::update might be destroyed already, leading to a potential crash in m_cache.remove(dead). This change here re-establishes the original timing by using the plugin itself as parent for the extra compilers. Change-Id: Id868b7b87f00440c67af551b71359c47a5c29cba Reviewed-by: Christian Kandeler Reviewed-by: --- src/plugins/qtsupport/qscxmlcgenerator.cpp | 11 +++++++---- src/plugins/qtsupport/qscxmlcgenerator.h | 4 +++- src/plugins/qtsupport/qtsupportplugin.cpp | 4 ++-- src/plugins/qtsupport/uicgenerator.cpp | 11 +++++++---- src/plugins/qtsupport/uicgenerator.h | 4 +++- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/plugins/qtsupport/qscxmlcgenerator.cpp b/src/plugins/qtsupport/qscxmlcgenerator.cpp index 8aa1c0198fd..3bfd201a04e 100644 --- a/src/plugins/qtsupport/qscxmlcgenerator.cpp +++ b/src/plugins/qtsupport/qscxmlcgenerator.cpp @@ -129,8 +129,9 @@ FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Process *process) class QScxmlcGeneratorFactory final : public ExtraCompilerFactory { public: - QScxmlcGeneratorFactory() = default; + explicit QScxmlcGeneratorFactory(QObject *guard) : m_guard(guard) {} +private: FileType sourceType() const final { return FileType::StateChart; } QString sourceTag() const final { return QStringLiteral("scxml"); } @@ -139,13 +140,15 @@ public: const FilePath &source, const FilePaths &targets) final { - return new QScxmlcGenerator(project, source, targets, this); + return new QScxmlcGenerator(project, source, targets, m_guard); } + + QObject *m_guard; }; -void setupQScxmlcGenerator() +void setupQScxmlcGenerator(QObject *guard) { - static QScxmlcGeneratorFactory theQScxmlcGeneratorFactory; + static QScxmlcGeneratorFactory theQScxmlcGeneratorFactory(guard); } } // QtSupport::Internal diff --git a/src/plugins/qtsupport/qscxmlcgenerator.h b/src/plugins/qtsupport/qscxmlcgenerator.h index 811f485689d..c7c8cf8265b 100644 --- a/src/plugins/qtsupport/qscxmlcgenerator.h +++ b/src/plugins/qtsupport/qscxmlcgenerator.h @@ -3,8 +3,10 @@ #pragma once +#include + namespace QtSupport::Internal { -void setupQScxmlcGenerator(); +void setupQScxmlcGenerator(QObject *guard); } // QtSupport::Internal diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp index 6a34e466d14..db0f8888af4 100644 --- a/src/plugins/qtsupport/qtsupportplugin.cpp +++ b/src/plugins/qtsupport/qtsupportplugin.cpp @@ -88,8 +88,8 @@ void QtSupportPlugin::initialize() setupGettingStartedWelcomePage(); setupQtSettingsPage(); setupQtOutputFormatter(); - setupUicGenerator(); - setupQScxmlcGenerator(); + setupUicGenerator(this); + setupQScxmlcGenerator(this); setupExternalDesigner(this); setupExternalLinguist(); diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp index dee12b9f9c9..74b8355af9a 100644 --- a/src/plugins/qtsupport/uicgenerator.cpp +++ b/src/plugins/qtsupport/uicgenerator.cpp @@ -72,8 +72,9 @@ FileNameToContentsHash UicGenerator::handleProcessFinished(Process *process) class UicGeneratorFactory final : public ExtraCompilerFactory { public: - UicGeneratorFactory() = default; + explicit UicGeneratorFactory(QObject *guard) : m_guard(guard) {} +private: FileType sourceType() const final { return FileType::Form; } QString sourceTag() const final { return QLatin1String("ui"); } @@ -82,13 +83,15 @@ public: const FilePath &source, const FilePaths &targets) final { - return new UicGenerator(project, source, targets, this); + return new UicGenerator(project, source, targets, m_guard); } + + QObject *m_guard; }; -void setupUicGenerator() +void setupUicGenerator(QObject *guard) { - static UicGeneratorFactory theUicGeneratorFactory; + static UicGeneratorFactory theUicGeneratorFactory(guard); } } // QtSupport::Internal diff --git a/src/plugins/qtsupport/uicgenerator.h b/src/plugins/qtsupport/uicgenerator.h index f8fdeb5ca05..3f3d4c20b51 100644 --- a/src/plugins/qtsupport/uicgenerator.h +++ b/src/plugins/qtsupport/uicgenerator.h @@ -3,8 +3,10 @@ #pragma once +#include + namespace QtSupport::Internal { -void setupUicGenerator(); +void setupUicGenerator(QObject *guard); } // QtSupport::Internal From 97ec97ff27c72ed6feb41a1d3d9264d3f36f36ad Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 26 Feb 2024 13:12:45 +0100 Subject: [PATCH 120/128] ClangModelManagerSupport: Skip explicit call to waitForFinished() This is done by the FutureSynchronizer d'tor, so no need to call it explicitly. Move the FutureSynchronizer as the last field of ClangModelManagerSupport so that its d'tor is executed first. Change-Id: Id38b8ec08579be8e4ade99ecadb511850ff37f8c Reviewed-by: Christian Kandeler --- src/plugins/clangcodemodel/clangmodelmanagersupport.cpp | 5 +---- src/plugins/clangcodemodel/clangmodelmanagersupport.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 53d0e6f3784..477ab9104b9 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -293,10 +293,7 @@ ClangModelManagerSupport::ClangModelManagerSupport() new ClangdQuickFixFactory(); // memory managed by CppEditor::g_cppQuickFixFactories } -ClangModelManagerSupport::~ClangModelManagerSupport() -{ - m_generatorSynchronizer.waitForFinished(); -} +ClangModelManagerSupport::~ClangModelManagerSupport() = default; void ClangModelManagerSupport::followSymbol(const CursorInEditor &data, const LinkHandler &processLinkCallback, diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index a598bea8bbd..67fc12868cb 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -94,10 +94,10 @@ private: void scheduleClientRestart(ClangdClient *client); static ClangdClient *clientWithProject(const ProjectExplorer::Project *project); - Utils::FutureSynchronizer m_generatorSynchronizer; QList> m_clientsToRestart; QTimer * const m_clientRestartTimer; QHash m_potentialShadowDocuments; + Utils::FutureSynchronizer m_generatorSynchronizer; // Keep me last }; } // namespace Internal From 0bcc5378e5e9a829f1a4d658e4720e1c26dea83f Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 26 Feb 2024 13:35:24 +0100 Subject: [PATCH 121/128] CMake: Fix setting label and tooltip Change-Id: Ia5ca9472054ec366110086bf79f630c5716b83fb Reviewed-by: Cristian Adam --- .../cmakeprojectmanager/cmakespecificsettings.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp index d814ee81350..28668b3567d 100644 --- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp +++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp @@ -93,16 +93,16 @@ CMakeSpecificSettings::CMakeSpecificSettings() "UseJunctionsForSourceAndBuildDirectories"); useJunctionsForSourceAndBuildDirectories.setDefaultValue(false); useJunctionsForSourceAndBuildDirectories.setLabelText(::CMakeProjectManager::Tr::tr( - "Use Junctions for CMake configuration and build operations")); + "Use junctions for CMake configuration and build operations")); useJunctionsForSourceAndBuildDirectories.setVisible(Utils::HostOsInfo().isWindowsHost()); useJunctionsForSourceAndBuildDirectories.setToolTip(::CMakeProjectManager::Tr::tr( - "Create and use junctions for the source and build directories. This helps to overcome " + "Create and use junctions for the source and build directories to overcome " "issues with long paths on Windows.

" - "They are stored under C:\\ProgramData\\QtCreator\\Links (overridable via " - "QTC_CMAKE_JUNCTIONS_DIR environment variable).

" - "With QTC_CMAKE_JUNCTIONS_HASH_LENGTH the MD5 hash key length can be shortened " + "Junctions are stored under C:\\ProgramData\\QtCreator\\Links (overridable via " + "the QTC_CMAKE_JUNCTIONS_DIR environment variable).

" + "With QTC_CMAKE_JUNCTIONS_HASH_LENGTH, you can shorten the MD5 hash key length " "to a value smaller than the default length value of 32.

" - "They are used for CMake configure, build and install operations.")); + "Junctions are used for CMake configure, build and install operations.")); readSettings(); } From e8bc1fa78e7d4eef23ed8912d3a7b72a244f7186 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 26 Feb 2024 09:12:22 +0100 Subject: [PATCH 122/128] Squish: Avoid yes/no questions, actions are clearer Change-Id: Ia0e23a08f074d61db8ead4566753fb27e4a97a71 Reviewed-by: Christian Stenger Reviewed-by: Leena Miettinen --- src/plugins/squish/squishnavigationwidget.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/squish/squishnavigationwidget.cpp b/src/plugins/squish/squishnavigationwidget.cpp index 7921938c37a..d3fb523196e 100644 --- a/src/plugins/squish/squishnavigationwidget.cpp +++ b/src/plugins/squish/squishnavigationwidget.cpp @@ -340,7 +340,12 @@ void SquishNavigationWidget::onRemoveSharedFileTriggered(const QModelIndex &idx) = CheckableMessageBox::question(Core::ICore::dialogParent(), Tr::tr("Remove Shared File"), detail, - Key("RemoveSharedSquishScript")); + Key("RemoveSharedSquishScript"), + QMessageBox::Yes | QMessageBox::No, + /*defaultButton=*/QMessageBox::No, + /*acceptButton=*/QMessageBox::Yes, + {{QMessageBox::Yes, Tr::tr("Delete")}, + {QMessageBox::No, Tr::tr("Cancel")}}); if (pressed != QMessageBox::Yes) return; From dafb8f2231694abae6cb573e5448462c2273acc0 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 26 Feb 2024 10:20:18 +0100 Subject: [PATCH 123/128] Axivion: Fix toggling sort If the horizontal scroll bar is not at position 0 we got the wrong offset for the position of the column. Change-Id: Id333584f9d509b67eeef39bc7966f095b577e0fe Reviewed-by: Jarek Kobus --- src/plugins/axivion/issueheaderview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp index ceb2d520afa..3f8efd86c6e 100644 --- a/src/plugins/axivion/issueheaderview.cpp +++ b/src/plugins/axivion/issueheaderview.cpp @@ -61,7 +61,7 @@ void IssueHeaderView::mousePressEvent(QMouseEvent *event) if (y > 1 && y < height() - 2) { // TODO improve const int pos = position.x(); const int logical = logicalIndexAt(pos); - const int end = sectionPosition(logical) + sectionSize(logical); + const int end = sectionViewportPosition(logical) + sectionSize(logical); const int start = end - ICON_SIZE - 2; m_maybeToggleSort = start < pos && end > pos; } From 5df9dee69a2f63a09f64ee4829f4322868bff916 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 26 Feb 2024 14:54:41 +0100 Subject: [PATCH 124/128] Axivion: Remove assert for empty relative doc path Change-Id: I4364e691c78eb52a2c880e416521458b66931f47 Reviewed-by: Christian Stenger --- src/plugins/axivion/axivionplugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 3fb567e22cd..c559e2b7d3a 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -816,7 +816,8 @@ void AxivionPluginPrivate::onDocumentOpened(IDocument *doc) return; const FilePath filePath = doc->filePath().relativeChildPath(m_project->projectDirectory()); - QTC_ASSERT(!filePath.isEmpty(), return); + if (filePath.isEmpty()) + return; // Empty is fine const auto handler = [this](const Dto::FileViewDto &data) { if (data.lineMarkers.empty()) From 290232bfd3d378f434161adc5452732a183c1454 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 27 Feb 2024 06:52:33 +0100 Subject: [PATCH 125/128] Android: Warn if parsing packages failed Change-Id: I61f249e1b45f1b9322b189b2781dc2375e9d13ca Reviewed-by: hjk --- src/plugins/android/androidsdkmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index c952ff90978..029cfd83524 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -405,7 +405,10 @@ void AndroidSdkManagerPrivate::reloadSdkPackages() if (m_packageListingSuccessful) { SdkManagerOutputParser parser(m_allPackages); parser.parsePackageListing(packageListing); + } else { + qCWarning(sdkManagerLog) << "Failed parsing packages:" << packageListing; } + emit m_sdkManager.packageReloadFinished(); } From 795d144227eaf20356376e3f160ded5cac53f7a3 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 26 Feb 2024 17:16:02 +0100 Subject: [PATCH 126/128] Translation Wizard: Be less polite Do not use "please" in UI text. Change-Id: I5332579ab297ae35a18fed88097313c8a50a1f6d Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/qtsupport/translationwizardpage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qtsupport/translationwizardpage.cpp b/src/plugins/qtsupport/translationwizardpage.cpp index be35a90dec0..bde480edd09 100644 --- a/src/plugins/qtsupport/translationwizardpage.cpp +++ b/src/plugins/qtsupport/translationwizardpage.cpp @@ -58,10 +58,10 @@ TranslationWizardPage::TranslationWizardPage(const QString &enabledExpr, bool si { const auto mainLayout = new QVBoxLayout(this); const auto descriptionLabel = new QLabel( - singleFile ? Tr::tr("Please select a language for which a corresponding " + singleFile ? Tr::tr("Select a language for which a corresponding " "translation (.ts) file will be generated for you.") : Tr::tr("If you plan to provide translations for your project's " - "user interface via the Qt Linguist tool, please select a " + "user interface via the Qt Linguist tool, select a " "language here. A corresponding translation (.ts) file will be " "generated for you.")); descriptionLabel->setWordWrap(true); From 3d8592edd1bb3f211a3f53584fb2c5d394c9941a Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 26 Feb 2024 16:54:52 +0100 Subject: [PATCH 127/128] Project Explorer: Fix quotes Use double quotes for emphasis in UI text. Change-Id: Idb34140816c8dfffaa87837055a1b76ceda09ef0 Reviewed-by: Christian Kandeler Reviewed-by: --- src/plugins/projectexplorer/buildaspects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp index 18f7fe2b851..0f9c025fce2 100644 --- a/src/plugins/projectexplorer/buildaspects.cpp +++ b/src/plugins/projectexplorer/buildaspects.cpp @@ -194,7 +194,7 @@ QString BuildDirectoryAspect::updateProblemLabelsHelper(const QString &value) const auto isInvalid = [](QChar c) { return c.isSpace() || !isascii(c.toLatin1()); }; if (const auto invalidChar = Utils::findOr(value, std::nullopt, isInvalid)) { genericProblem = Tr::tr( - "Build directory contains potentially problematic character '%1'.") + "Build directory contains potentially problematic character \"%1\".") .arg(*invalidChar); genericProblemLabelString = genericProblem From fdabbfcbcfe35bce731a2eff3fe13e2d6df238b9 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 23 Feb 2024 12:17:37 +0100 Subject: [PATCH 128/128] Diff: Fix that dialog for "Revert Chunk" closes right away MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS. Change-Id: Ifbe402c44779e4062a5dfb5d7c09da7ac845acce Reviewed-by: Jarek Kobus Reviewed-by: Reviewed-by: Orgad Shaneh Reviewed-by: André Hartmann --- .../diffeditor/diffeditorwidgetcontroller.cpp | 9 ++++++--- src/plugins/vcsbase/vcsbaseeditor.cpp | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp b/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp index 0f3a57ee086..9decc5f1a60 100644 --- a/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp +++ b/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp @@ -267,9 +267,12 @@ void DiffEditorWidgetController::addPatchAction(QMenu *menu, int fileIndex, int const QString actionName = patchAction == PatchAction::Apply ? Tr::tr("Apply Chunk...") : Tr::tr("Revert Chunk..."); QAction *action = menu->addAction(actionName); - connect(action, &QAction::triggered, this, [this, fileIndex, chunkIndex, patchAction] { - patch(patchAction, fileIndex, chunkIndex); - }); + connect( + action, + &QAction::triggered, + this, + [this, fileIndex, chunkIndex, patchAction] { patch(patchAction, fileIndex, chunkIndex); }, + Qt::QueuedConnection); const bool enabled = chunkExists(fileIndex, chunkIndex) && (patchAction == PatchAction::Revert || fileNamesAreDifferent(fileIndex)); action->setEnabled(enabled); diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp index b10758233a0..38bb004a2d6 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.cpp +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -995,14 +995,20 @@ void VcsBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e) // the user has "Open With" and choose the right diff editor so that // fileNameFromDiffSpecification() works. QAction *applyAction = menu->addAction(Tr::tr("Apply Chunk...")); - connect(applyAction, &QAction::triggered, this, [this, chunk] { - slotApplyDiffChunk(chunk, PatchAction::Apply); - }); + connect( + applyAction, + &QAction::triggered, + this, + [this, chunk] { slotApplyDiffChunk(chunk, PatchAction::Apply); }, + Qt::QueuedConnection); // Revert a chunk from a VCS diff, which might be linked to reloading the diff. QAction *revertAction = menu->addAction(Tr::tr("Revert Chunk...")); - connect(revertAction, &QAction::triggered, this, [this, chunk] { - slotApplyDiffChunk(chunk, PatchAction::Revert); - }); + connect( + revertAction, + &QAction::triggered, + this, + [this, chunk] { slotApplyDiffChunk(chunk, PatchAction::Revert); }, + Qt::QueuedConnection); // Custom diff actions addDiffActions(menu, chunk); break;