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") 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/heartgame-start.webp b/doc/qtcreator/images/heartgame-start.webp new file mode 100644 index 00000000000..9fef6fe0057 Binary files /dev/null and b/doc/qtcreator/images/heartgame-start.webp differ 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 00000000000..14be8fb3d70 Binary files /dev/null and b/doc/qtcreator/images/qt-app-dev-flow.webp differ diff --git a/doc/qtcreator/images/qt-tools.webp b/doc/qtcreator/images/qt-tools.webp new file mode 100644 index 00000000000..986846140fa Binary files /dev/null and b/doc/qtcreator/images/qt-tools.webp differ 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 00000000000..f7c3be17d5b Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-project-qt-for-python-kit-selection.webp differ 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 00000000000..14494a04d37 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-project-details.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp deleted file mode 100644 index 1dd0962f4f3..00000000000 Binary files a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp and /dev/null differ 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 3890e8e75ba..00000000000 Binary files a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-python-interpreters.webp b/doc/qtcreator/images/qtcreator-python-interpreters.webp index 597b99e720d..b5027b0a0d4 100644 Binary files a/doc/qtcreator/images/qtcreator-python-interpreters.webp and b/doc/qtcreator/images/qtcreator-python-interpreters.webp differ diff --git a/doc/qtcreator/images/qtcreator-python-run-settings.png b/doc/qtcreator/images/qtcreator-python-run-settings.png deleted file mode 100644 index 5d3875f4e35..00000000000 Binary files a/doc/qtcreator/images/qtcreator-python-run-settings.png and /dev/null differ 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 00000000000..69f2d7676d6 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-python-run-settings.webp differ 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} */ 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/editors/creator-only/creator-markdown-editor.qdoc b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc index 1942ec84af7..a5cbf186f99 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,25 @@ 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 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 */ 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/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} */ diff --git a/doc/qtcreator/src/python/creator-python-development.qdoc b/doc/qtcreator/src/python/creator-python-development.qdoc index ce2452e34a1..d5cd5fe31c9 100644 --- a/doc/qtcreator/src/python/creator-python-development.qdoc +++ b/doc/qtcreator/src/python/creator-python-development.qdoc @@ -19,9 +19,8 @@ \list \li \l{Set up PySide6} \li \l{Create Qt for Python applications} - \li \l{Select the Python interpreter} - \li \l{Create a virtual environment} - \li \l{Create kits for Python interpreters} + \li \l{Select the Python version} + \li \l{Create kits for Python} \li \l{Use Python interactive shell} \li \l{Configure Python language servers} \li \l{Run Python applications} @@ -48,9 +47,10 @@ \section1 Create Qt for Python applications - You can use wizards to create Qt for Python application projects. The wizards + Use wizards to create Qt for Python application projects. The wizards generate a project file, \c {.pyproject}, that lists the files in the Python - project. They also generate a \c {.py} file that has some boilerplate code. + project. They also generate a \c {.py} file that has some boilerplate code + and \c {reguirements.txt} that stores the PySide version of the generated code. In addition, the widget-based UI wizard creates a \c {.ui} file that has a \QD form, and the Qt Quick Application wizard creates a \c {.qml} file that imports Qt Quick controls. @@ -72,50 +72,31 @@ use \c {.pyqtc} files, but we recommend that you choose \c{.pyproject} files for new projects. - \section1 Select the Python interpreter + \section1 Select the Python version - You select the initial Python interpreter when you use the Qt for Python - Application wizard templates to create Python projects. + The \l{kits-tab}{kits} you select for the project in \uicontrol Projects > + \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 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} diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index ad25ad2dbeb..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): @@ -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' 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 diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index 8421c0a6242..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 @@ -18346,8 +18342,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..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 @@ -33897,7 +33891,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 adc57cc41ef..fe4d87c0326 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 : @@ -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 @@ -33467,8 +33461,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..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 @@ -4546,8 +4542,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..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. @@ -33930,8 +33927,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..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 @@ -22984,8 +22980,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..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 @@ -23217,7 +23213,7 @@ Id必须以小写字母开头。 - Cannot start simulator (%1, %2) in current state: %3 + Cannot start simulator (%1, %2) in current state: %3. 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", 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(); } 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 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 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; diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt index 8a737e3fadb..47eca2cd163 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 diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 255fe5243a8..c6e87d27746 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; @@ -1191,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; @@ -1402,8 +1415,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 +1427,41 @@ GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, handler ? TimeoutTask(onSetup, [handler] { handler(); }, CallDoneIf::Success) : TimeoutTask(onSetup) }, - item + *this + }; +} + +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); + }; + struct LogStorage + { + time_point start; + int asyncCount = 0; + }; + const Storage storage; + return Group { + storage, + onGroupSetup([storage, header] { + 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->start); + const int asyncCountDiff = activeTaskTree()->asyncCount() - storage->asyncCount; + QT_CHECK(asyncCountDiff >= 0); + const QMetaEnum doneWithEnum = QMetaEnum::fromType(); + const QString syncType = asyncCountDiff ? QString("asynchronously") + : QString("synchronously"); + qDebug().noquote().nospace() << header() << " finished " << syncType << " with " + << doneWithEnum.valueToKey(int(result)) << " within " << elapsed.count() << "ms."; + }) }; } @@ -1427,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; @@ -1490,9 +1547,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); @@ -1552,6 +1608,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; @@ -1666,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(); @@ -1696,8 +1763,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 " @@ -1705,6 +1778,7 @@ void TaskTreePrivate::start() } m_runtimeRoot.reset(new RuntimeTask{*m_root}); start(m_runtimeRoot.get()); + bumpAsyncCount(); } void TaskTreePrivate::stop() @@ -1717,6 +1791,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) @@ -1724,18 +1807,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); } @@ -2037,10 +2108,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(); @@ -2961,6 +3034,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 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 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. + When the task tree emits the done() signal, the bumping stops. + 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 \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() +*/ + /*! Returns the number of asynchronous tasks contained in the stored recipe. @@ -3006,7 +3111,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 7295c47e1ea..8ea255303ba 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; + ExecutableItem withLog(const QString &logName) 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 +314,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 +392,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 +436,7 @@ private: }; template -class CustomTask final : public GroupItem +class CustomTask final : public ExecutableItem { public: using Task = typename Adapter::TaskType; @@ -445,16 +450,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; } @@ -542,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 @@ -566,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: 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()); 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()); diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index d3a8360136e..c2475853e7a 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -566,8 +566,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/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/libs/utils/process.cpp b/src/libs/utils/process.cpp index 4eacb28986f..5bf44513654 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 = {}; @@ -1663,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 {}; } @@ -1729,13 +1730,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 +1851,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); 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"; 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(); } 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); diff --git a/src/plugins/axivion/CMakeLists.txt b/src/plugins/axivion/CMakeLists.txt index a71b984520f..561ad680e61 100644 --- a/src/plugins/axivion/CMakeLists.txt +++ b/src/plugins/axivion/CMakeLists.txt @@ -6,11 +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 7b8034c26c4..0e808f65806 100644 --- a/src/plugins/axivion/axivion.qbs +++ b/src/plugins/axivion/axivion.qbs @@ -23,8 +23,12 @@ QtcPlugin { "axivionsettings.cpp", "axivionsettings.h", "axiviontr.h", - "credentialquery.h", "credentialquery.cpp", + "credentialquery.h", + "dynamiclistmodel.cpp", + "dynamiclistmodel.h", + "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 9bd50784f3a..a13b63dd3a2 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -6,17 +6,21 @@ #include "axivionplugin.h" #include "axiviontr.h" #include "dashboard/dto.h" +#include "issueheaderview.h" +#include "dynamiclistmodel.h" #include +#include #include #include #include +#include +#include #include #include -#include #include #include @@ -28,9 +32,7 @@ #include #include #include -#include #include -#include #include #include @@ -59,22 +61,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); @@ -187,32 +191,59 @@ void DashboardWidget::updateUi() addValuesWidgets(Tr::tr("Total:"), allTotal, allAdded, allRemoved, row); } -class IssueTreeItem final : public StaticTreeItem +struct LinkWithColumns +{ + Link link; + QList columns; +}; + +class IssueListItem final : public ListItem { public: - IssueTreeItem(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; } + void setLinks(const QList &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 && !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()) { + 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); + if (link.targetFilePath.exists()) + EditorManager::openEditorAt(link); + } + if (!m_id.isEmpty()) + fetchIssueInfo(m_id); return true; } - return StaticTreeItem::setData(column, value, role); + return ListItem::setData(column, value, role); } private: - Links m_links; + const QString m_id; + QStringList m_data; + QStringList m_toolTips; + QList m_links; }; class IssuesWidget : public QScrollArea @@ -220,24 +251,23 @@ class IssuesWidget : public QScrollArea public: explicit IssuesWidget(QWidget *parent = nullptr); void updateUi(); - void setTableDto(const Dto::TableInfoDto &dto); - void addIssues(const Dto::IssueTableDto &dto); private: + void updateTable(); + void addIssues(const Dto::IssueTableDto &dto, int startRow); 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(); + void onFetchRequested(int startRow, int limit); QString m_currentPrefix; QString m_currentProject; 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; @@ -246,9 +276,9 @@ private: QLineEdit *m_pathGlobFilter = nullptr; // FancyLineEdit instead? QLabel *m_totalRows = nullptr; BaseTreeView *m_issuesView = nullptr; - TreeModel<> *m_issuesModel = nullptr; + IssueHeaderView *m_headerView = nullptr; + DynamicListModel *m_issuesModel = nullptr; int m_totalRowCount = 0; - int m_lastRequestedOffset = 0; QStringList m_userNames; QStringList m_versionDates; TaskTreeRunner m_taskTreeRunner; @@ -258,79 +288,71 @@ 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_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); + 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(); - } - }); - } - layout->addWidget(m_issuesView); + connect(m_issuesModel, &DynamicListModel::fetchRequested, this, &IssuesWidget::onFetchRequested); 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); @@ -355,50 +377,81 @@ void IssuesWidget::updateUi() if (info.issueKinds.size()) m_currentPrefix = info.issueKinds.front().prefix; - updateTableView(); + fetchTable(); } -void IssuesWidget::setTableDto(const Dto::TableInfoDto &dto) +static Qt::Alignment alignmentFromString(const QString &str) { - m_currentTableInfo.emplace(dto); + 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) + 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) { + 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"); m_totalRows->setText(Tr::tr("Total rows:")); - issuesModel->setHeader(columnHeaders); - - auto oldModel = m_issuesModel; - m_issuesModel = issuesModel; - m_issuesView->setModel(issuesModel); - delete oldModel; + m_issuesModel->clear(); + m_issuesModel->setHeader(columnHeaders); + m_issuesModel->setAlignments(alignments); + 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)); + 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, &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 @@ -411,11 +464,12 @@ 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) { 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()) @@ -425,21 +479,32 @@ 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; + QStringList toolTips; 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; + } + 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; } } - IssueTreeItem *it = new IssueTreeItem(data, data); - it->setLinks(linksForIssue(row)); - m_issuesModel->rootItem()->appendChild(it); + IssueListItem *it = new IssueListItem(startRow++, id, data, toolTips); + it->setLinks(linksForIssue(row, tableColumns)); + items.append(it); } + m_issuesModel->setItems(items); } void IssuesWidget::onSearchParameterChanged() @@ -448,10 +513,9 @@ 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; IssueListSearch search = searchFromUi(); search.computeTotalRowCount = true; fetchIssues(search); @@ -503,7 +567,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); @@ -535,27 +599,6 @@ void IssuesWidget::updateBasicProjectInfo(std::optional inf m_versionStart->setCurrentIndex(m_versionDates.count() - 1); } -void IssuesWidget::updateTableView() -{ - 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 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; - 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); @@ -581,143 +624,168 @@ 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; } +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_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); }; + 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_taskTreeRunner.isRunning()) return; IssueListSearch search = searchFromUi(); - m_lastRequestedOffset = m_issuesModel->rowCount(); - search.offset = m_lastRequestedOffset; + search.offset = startRow; + search.limit = limit; 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); - QTextBrowser *browser = new QTextBrowser(m_outputWidget); - m_outputWidget->addWidget(browser); -} + m_outputWidget = new QStackedWidget; + DashboardWidget *dashboardWidget = new DashboardWidget(m_outputWidget); + m_outputWidget->addWidget(dashboardWidget); + IssuesWidget *issuesWidget = new IssuesWidget(m_outputWidget); + m_outputWidget->addWidget(issuesWidget); -AxivionOutputPane::~AxivionOutputPane() -{ - if (!m_outputWidget->parent()) - delete m_outputWidget; -} + QPalette pal = m_outputWidget->palette(); + pal.setColor(QPalette::Window, creatorTheme()->color(Theme::Color::BackgroundColorNormal)); + m_outputWidget->setPalette(pal); -QWidget *AxivionOutputPane::outputWidget(QWidget *parent) -{ - if (m_outputWidget) - m_outputWidget->setParent(parent); - else - QTC_CHECK(false); - return m_outputWidget; -} + 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); + }); -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; -} + 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(); + }); -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); + }); } -} -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); + ~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 140d50b7235..a13b125c183 100644 --- a/src/plugins/axivion/axivionoutputpane.h +++ b/src/plugins/axivion/axivionoutputpane.h @@ -3,38 +3,11 @@ #pragma once -#include - -QT_BEGIN_NAMESPACE -class QStackedWidget; -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(); - void updateAndShowRule(const QString &ruleHtml); -private: - QStackedWidget *m_outputWidget = nullptr; -}; +void setupAxivionOutputPane(QObject *guard); +void updateDashboard(); } // Axivion::Internal diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 9b333af7c3e..c559e2b7d3a 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -7,13 +7,16 @@ #include "axivionprojectsettings.h" #include "axivionsettings.h" #include "axiviontr.h" +#include "credentialquery.h" #include "dashboard/dto.h" #include "dashboard/error.h" #include #include #include +#include #include +#include #include #include @@ -31,19 +34,26 @@ #include #include +#include +#include #include #include #include #include +#include +#include #include #include #include +#include #include +#include #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 +106,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()) @@ -118,18 +164,24 @@ 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()) 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; } +enum class ServerAccess { Unknown, NoAuthorization, WithAuthorization }; + class AxivionPluginPrivate : public QObject { + Q_OBJECT public: AxivionPluginPrivate(); void handleSslErrors(QNetworkReply *reply, const QList &errors); @@ -141,9 +193,18 @@ public: void clearAllMarks(); 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); + +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. + std::optional m_apiToken; NetworkAccessManager m_networkAccessManager; - AxivionOutputPane m_axivionOutputPane; std::optional m_dashboardInfo; std::optional m_currentProjectInfo; Project *m_project = nullptr; @@ -158,13 +219,16 @@ static AxivionPluginPrivate *dd = nullptr; class AxivionTextMark : public TextMark { public: - AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue) - : TextMark(filePath, issue.startLine, {Tr::tr("Axivion"), AxivionTextMarkId}) + 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] { @@ -247,7 +311,7 @@ void AxivionPluginPrivate::onStartupProjectChanged(Project *project) m_project = project; clearAllMarks(); m_currentProjectInfo = {}; - m_axivionOutputPane.updateDashboard(); + updateDashboard(); if (!m_project) return; @@ -259,10 +323,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(QStringLiteral("api/projects/")).resolved(projectName); + return QUrl(settings().server.dashboard).resolved(QString("api/projects/")).resolved(projectName); } static constexpr int httpStatusCodeOk = 200; @@ -271,28 +332,22 @@ constexpr char s_jsonContentType[] = "application/json"; static Group fetchHtmlRecipe(const QUrl &url, const std::function &handler) { - struct StorageData - { - QByteArray credentials; - }; + // TODO: Refactor so that it's a common code with fetchDataRecipe(). + const auto onQuerySetup = [url](NetworkQuery &query) { + if (dd->m_serverAccess == ServerAccess::Unknown) + return SetupResult::StopWithError; // TODO: start authorizationRecipe()? - const Storage storage; - - const auto onCredentialSetup = [storage] { - storage->credentials = QByteArrayLiteral("AxToken ") + settings().server.token.toUtf8(); - }; - - 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(); @@ -309,125 +364,49 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function struct GetDtoStorage { - QByteArray credential; QUrl url; + std::optional credential; std::optional dtoData; }; -template -static Group getDtoRecipe(const Storage> &dtoStorage) -{ - const Storage storage; - - const auto onNetworkQuerySetup = [dtoStorage](NetworkQuery &query) { - QNetworkRequest request(dtoStorage->url); - request.setRawHeader("Accept", s_jsonContentType); - request.setRawHeader("Authorization", dtoStorage->credential); - 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); - }; - - 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; -}; - template struct PostDtoStorage { - QByteArray credential; QUrl url; + std::optional credential; QByteArray csrfToken; QByteArray writeData; std::optional dtoData; }; -template -static Group postDtoRecipe(const Storage> &dtoStorage) +template typename DtoStorageType> +static Group dtoRecipe(const Storage> &dtoStorage) { const Storage storage; 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); - request.setRawHeader("AX-CSRF-Token", dtoStorage->csrfToken); + + 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.setWriteData(dtoStorage->writeData); - query.setOperation(NetworkOperation::Post); query.setNetworkAccessManager(&dd->m_networkAccessManager); }; @@ -464,76 +443,220 @@ static Group postDtoRecipe(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; }; - const auto onDeserializeSetup = [storage](Async &task) { - const auto deserialize = [](QPromise &promise, const QByteArray &input) { - promise.addResult(DtoType::deserialize(input)); + 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) { - if (doneWith == DoneWith::Success) - dtoStorage->dtoData = task.future().result(); + const auto onDeserializeDone = [dtoStorage](const Async> &task, + DoneWith doneWith) { + if (doneWith == DoneWith::Success && task.isResultAvailable()) { + 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("Unknown Dto structure deserialization error."))); + } + return DoneResult::Error; }; - const Group recipe { + return { storage, NetworkQueryTask(onNetworkQuerySetup, onNetworkQueryDone), - AsyncTask(onDeserializeSetup, onDeserializeDone) + AsyncTask>(onDeserializeSetup, onDeserializeDone) }; - return recipe; -}; +} + +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; + 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); + credential.setKey(credentialKey()); + }; + const auto onGetCredentialDone = [](const CredentialQuery &credential, DoneWith result) { + if (result == DoneWith::Success) + dd->m_apiToken = credential.data(); + else + 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; + const Storage> dashboardStorage; + 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 Storage> apiTokenStorage; + 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 onSetCredentialDone = [](const CredentialQuery &credential) { + 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; + }; + + return { + Group { + unauthorizedDashboardStorage, + onGroupSetup(onUnauthorizedGroupSetup), + dtoRecipe(unauthorizedDashboardStorage), + onGroupDone(onUnauthorizedGroupDone) + }, + Group { + LoopUntil(onCredentialLoopCondition), + CredentialQueryTask(onGetCredentialSetup, onGetCredentialDone), + Group { + passwordStorage, + dashboardStorage, + onGroupSetup(onDashboardGroupSetup), + Group { // GET DashboardInfoDto + finishAllAndSuccess, + dtoRecipe(dashboardStorage) + }, + Group { // POST ApiTokenCreationRequestDto, GET ApiTokenInfoDto. + apiTokenStorage, + onGroupSetup(onApiTokenGroupSetup), + dtoRecipe(apiTokenStorage), + CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error) + } + } + } + }; +} template static Group fetchDataRecipe(const QUrl &url, const std::function &handler) { const Storage> dtoStorage; - const auto onCredentialSetup = [dtoStorage, url] { - dtoStorage->credential = QByteArrayLiteral("AxToken ") + settings().server.token.toUtf8(); - dtoStorage->url = url; - }; + 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), + authorizationRecipe(), Group { - getDtoRecipe(dtoStorage), + dtoStorage, + onGroupSetup(onDtoSetup), + dtoRecipe(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] { @@ -549,17 +672,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; @@ -575,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); } @@ -594,10 +714,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 + '/')) @@ -614,21 +730,27 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) clearAllMarks(); if (projectName.isEmpty()) { m_currentProjectInfo = {}; - m_axivionOutputPane.updateDashboard(); + updateDashboard(); return; } 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; - m_axivionOutputPane.updateDashboard(); + updateDashboard(); handleOpenedDocs(); }; @@ -661,12 +783,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(); @@ -687,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()) @@ -718,7 +848,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; } } @@ -733,15 +863,76 @@ 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); } } +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; + 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: + 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); + connect(browser, &QTextBrowser::anchorClicked, + dd, &AxivionPluginPrivate::handleAnchorClicked); + return view; + } +}; + +void setupAxivionIssueWidgetFactory() +{ + static AxivionIssueWidgetFactory issueWidgetFactory; +} + class AxivionPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT @@ -756,9 +947,12 @@ class AxivionPlugin final : public ExtensionSystem::IPlugin void initialize() final { + setupAxivionOutputPane(this); + dd = new AxivionPluginPrivate; AxivionProjectSettings::setupProjectPanel(); + setupAxivionIssueWidgetFactory(); connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, dd, &AxivionPluginPrivate::onStartupProjectChanged); @@ -769,6 +963,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..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; @@ -72,6 +73,7 @@ bool handleCertificateIssue(); QIcon iconForIssue(const QString &prefix); QString anyToSimpleString(const Dto::Any &any); +void fetchIssueInfo(const QString &id); } // Axivion::Internal diff --git a/src/plugins/axivion/axivionprojectsettings.cpp b/src/plugins/axivion/axivionprojectsettings.cpp index 14d47eca53a..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); @@ -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..9b24fa51f02 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -27,8 +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; + return id == other.id && dashboard == other.dashboard && username == other.username; } bool AxivionServer::operator!=(const AxivionServer &other) const @@ -42,11 +42,15 @@ QJsonObject AxivionServer::toJson() const result.insert("id", id.toString()); result.insert("dashboard", dashboard); result.insert("username", username); - result.insert("description", description); - result.insert("token", token); 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; @@ -59,14 +63,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; - 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()}; + return {Id::fromString(id.toString()), fixUrl(dashboard.toString()), username.toString()}; } static FilePath tokensFilePath() @@ -109,6 +106,10 @@ AxivionSettings::AxivionSettings() { setSettingsGroup("Axivion"); + highlightMarks.setSettingsKey("HighlightMarks"); + highlightMarks.setLabelText(Tr::tr("Highlight marks")); + highlightMarks.setToolTip(Tr::tr("Marks issues on the scroll bar.")); + highlightMarks.setDefaultValue(false); AspectContainer::readSettings(); server = readTokenFile(tokensFilePath()); @@ -161,8 +162,6 @@ private: Id m_id; StringAspect m_dashboardUrl; StringAspect m_username; - StringAspect m_description; - StringAspect m_token; BoolAspect m_valid; }; @@ -181,23 +180,12 @@ 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")); - - 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 + noMargin }.attachTo(this); if (mode == Edit) { @@ -208,8 +196,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); } } @@ -220,10 +206,8 @@ 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(); - result.description = m_description(); - result.token = m_token(); return result; } @@ -232,13 +216,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); - m_token.setValue(server.token); } bool DashboardSettingsWidget::isValid() const { - return !m_token().isEmpty() && !m_description().isEmpty() && isUrlValid(m_dashboardUrl()); + return isUrlValid(m_dashboardUrl()); } class AxivionSettingsWidget : public IOptionsPageWidget @@ -262,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 00a93843816..257b4056c56 100644 --- a/src/plugins/axivion/axivionsettings.h +++ b/src/plugins/axivion/axivionsettings.h @@ -26,8 +26,6 @@ public: Utils::Id id; QString dashboard; QString username; - QString description; - QString token; bool validateCert = true; }; @@ -40,6 +38,7 @@ public: void toSettings() const; AxivionServer server; // shall we have more than one? + Utils::BoolAspect highlightMarks{this}; }; AxivionSettings &settings(); 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 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..b5ea338c3bd 100644 --- a/src/plugins/axivion/credentialquery.h +++ b/src/plugins/axivion/credentialquery.h @@ -17,14 +17,15 @@ public: void setKey(const QString &key) { m_key = key; } void setData(const QByteArray &data) { m_data = data; } - QByteArray data() const { return m_data; } + CredentialOperation operation() const { return m_operation; } + 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; }; diff --git a/src/plugins/axivion/dashboard/dto.cpp b/src/plugins/axivion/dashboard/dto.cpp index 11494e67aef..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) @@ -127,6 +129,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) @@ -158,6 +180,7 @@ namespace Axivion::Internal::Dto { return de_serializer::serialize(value); } + // throws std::domain_error template static QByteArray serialize_bytes(const T &value) { @@ -522,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()) @@ -548,6 +572,11 @@ 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)) {} @@ -647,10 +676,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 @@ -663,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), @@ -672,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); @@ -690,6 +721,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, @@ -700,7 +736,6 @@ namespace Axivion::Internal::Dto { languageName(std::move(languageName)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray AnalyzedFileDto::serialize() const { return serialize_bytes(*this); @@ -740,6 +775,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) @@ -772,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), @@ -780,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); @@ -797,6 +839,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 @@ -805,7 +852,6 @@ namespace Axivion::Internal::Dto { newPassword(std::move(newPassword)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ChangePasswordFormDto::serialize() const { return serialize_bytes(*this); @@ -860,6 +906,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) @@ -899,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), @@ -908,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); @@ -926,6 +979,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, @@ -936,7 +994,6 @@ namespace Axivion::Internal::Dto { displayColor(std::move(displayColor)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ColumnTypeOptionDto::serialize() const { return serialize_bytes(*this); @@ -951,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 }; @@ -974,13 +1033,17 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str CommentRequestDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + CommentRequestDto::CommentRequestDto( QString text ) : text(std::move(text)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray CommentRequestDto::serialize() const { return serialize_bytes(*this); @@ -995,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 }; @@ -1018,13 +1083,17 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str CsrfTokenDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + CsrfTokenDto::CsrfTokenDto( QString csrfToken ) : csrfToken(std::move(csrfToken)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray CsrfTokenDto::serialize() const { return serialize_bytes(*this); @@ -1043,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), @@ -1054,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); @@ -1074,6 +1145,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, @@ -1088,7 +1164,6 @@ namespace Axivion::Internal::Dto { line(std::move(line)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray EntityDto::serialize() const { return serialize_bytes(*this); @@ -1111,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), @@ -1126,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); @@ -1150,6 +1227,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, @@ -1172,7 +1254,6 @@ namespace Axivion::Internal::Dto { data(std::move(data)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ErrorDto::serialize() const { return serialize_bytes(*this); @@ -1193,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), @@ -1206,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); @@ -1228,6 +1311,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, @@ -1246,7 +1334,6 @@ namespace Axivion::Internal::Dto { commentDeletionId(std::move(commentDeletionId)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueCommentDto::serialize() const { return serialize_bytes(*this); @@ -1291,6 +1378,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) @@ -1359,6 +1451,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) @@ -1400,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), @@ -1413,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); @@ -1435,6 +1534,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, @@ -1453,7 +1557,6 @@ namespace Axivion::Internal::Dto { endColumn(std::move(endColumn)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueSourceLocationDto::serialize() const { return serialize_bytes(*this); @@ -1469,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), @@ -1477,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); @@ -1494,6 +1599,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 @@ -1502,7 +1612,6 @@ namespace Axivion::Internal::Dto { color(std::move(color)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTagDto::serialize() const { return serialize_bytes(*this); @@ -1522,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), @@ -1534,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); @@ -1555,6 +1666,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, @@ -1571,7 +1687,6 @@ namespace Axivion::Internal::Dto { selected(std::move(selected)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTagTypeDto::serialize() const { return serialize_bytes(*this); @@ -1611,6 +1726,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) @@ -1645,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), @@ -1655,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); @@ -1674,6 +1796,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, @@ -1686,7 +1813,6 @@ namespace Axivion::Internal::Dto { maxValue(std::move(maxValue)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricDto::serialize() const { return serialize_bytes(*this); @@ -1707,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), @@ -1720,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); @@ -1742,6 +1870,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, @@ -1760,7 +1893,6 @@ namespace Axivion::Internal::Dto { entityId(std::move(entityId)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricValueTableRowDto::serialize() const { return serialize_bytes(*this); @@ -1790,6 +1922,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) @@ -1817,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 }; @@ -1840,13 +1979,17 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str NamedFilterVisibilityDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + NamedFilterVisibilityDto::NamedFilterVisibilityDto( std::optional> groups ) : groups(std::move(groups)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterVisibilityDto::serialize() const { return serialize_bytes(*this); @@ -1862,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), @@ -1870,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); @@ -1887,6 +2032,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 @@ -1895,7 +2045,6 @@ namespace Axivion::Internal::Dto { url(std::move(url)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ProjectReferenceDto::serialize() const { return serialize_bytes(*this); @@ -1912,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), @@ -1921,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); @@ -1939,6 +2090,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, @@ -1949,7 +2105,6 @@ namespace Axivion::Internal::Dto { disabled(std::move(disabled)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RuleDto::serialize() const { return serialize_bytes(*this); @@ -1974,6 +2129,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) @@ -2014,6 +2174,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) @@ -2043,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), @@ -2052,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); @@ -2070,6 +2237,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, @@ -2080,7 +2252,6 @@ namespace Axivion::Internal::Dto { buildDate(std::move(buildDate)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ToolsVersionDto::serialize() const { return serialize_bytes(*this); @@ -2110,6 +2281,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) @@ -2139,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), @@ -2148,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); @@ -2166,6 +2344,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, @@ -2176,7 +2359,6 @@ namespace Axivion::Internal::Dto { Removed(std::move(Removed)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray VersionKindCountDto::serialize() const { return serialize_bytes(*this); @@ -2199,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), @@ -2214,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); @@ -2238,6 +2422,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, @@ -2260,7 +2449,6 @@ namespace Axivion::Internal::Dto { cloneRatio(std::move(cloneRatio)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray AnalysisVersionDto::serialize() const { return serialize_bytes(*this); @@ -2278,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), @@ -2288,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); @@ -2307,6 +2497,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, @@ -2351,7 +2546,6 @@ namespace Axivion::Internal::Dto { type = ApiTokenTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ApiTokenCreationRequestDto::serialize() const { return serialize_bytes(*this); @@ -2378,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), @@ -2397,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); @@ -2425,6 +2621,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, @@ -2505,7 +2706,6 @@ namespace Axivion::Internal::Dto { type = ApiTokenTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ApiTokenInfoDto::serialize() const { return serialize_bytes(*this); @@ -2529,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), @@ -2545,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); @@ -2570,6 +2772,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, @@ -2658,7 +2865,6 @@ namespace Axivion::Internal::Dto { type = ColumnTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ColumnInfoDto::serialize() const { return serialize_bytes(*this); @@ -2687,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), @@ -2708,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); @@ -2738,6 +2946,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, @@ -2772,7 +2985,6 @@ namespace Axivion::Internal::Dto { csrfTokenUrl(std::move(csrfTokenUrl)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray DashboardInfoDto::serialize() const { return serialize_bytes(*this); @@ -2787,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 }; @@ -2810,13 +3024,17 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueCommentListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueCommentListDto::IssueCommentListDto( std::vector comments ) : comments(std::move(comments)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueCommentListDto::serialize() const { return serialize_bytes(*this); @@ -2833,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), @@ -2842,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); @@ -2860,6 +3080,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, @@ -2900,7 +3125,6 @@ namespace Axivion::Internal::Dto { prefix = IssueKindMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueKindInfoDto::serialize() const { return serialize_bytes(*this); @@ -2915,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 }; @@ -2938,13 +3164,17 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str IssueTagTypeListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + IssueTagTypeListDto::IssueTagTypeListDto( std::vector tags ) : tags(std::move(tags)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTagTypeListDto::serialize() const { return serialize_bytes(*this); @@ -2967,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), @@ -2982,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); @@ -3006,6 +3238,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, @@ -3070,7 +3307,6 @@ namespace Axivion::Internal::Dto { kind = IssueKindMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray LineMarkerDto::serialize() const { return serialize_bytes(*this); @@ -3086,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), @@ -3094,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); @@ -3111,6 +3349,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 @@ -3147,7 +3390,6 @@ namespace Axivion::Internal::Dto { severity = MessageSeverityMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RepositoryUpdateMessageDto::serialize() const { return serialize_bytes(*this); @@ -3162,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 }; @@ -3185,13 +3429,17 @@ namespace Axivion::Internal::Dto { return deserialize_bytes(json); } + Utils::expected_str RuleListDto::deserializeExpected(const QByteArray &json) + { + return deserializeExp(json); + } + RuleListDto::RuleListDto( std::vector rules ) : rules(std::move(rules)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RuleListDto::serialize() const { return serialize_bytes(*this); @@ -3207,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), @@ -3215,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); @@ -3232,6 +3482,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 @@ -3268,7 +3523,6 @@ namespace Axivion::Internal::Dto { direction = SortDirectionMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray SortInfoDto::serialize() const { return serialize_bytes(*this); @@ -3286,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), @@ -3296,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); @@ -3315,6 +3571,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, @@ -3361,7 +3622,6 @@ namespace Axivion::Internal::Dto { type = UserRefTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray UserRefDto::serialize() const { return serialize_bytes(*this); @@ -3377,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), @@ -3385,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); @@ -3402,6 +3664,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 @@ -3410,7 +3677,6 @@ namespace Axivion::Internal::Dto { rows(std::move(rows)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray AnalyzedFileListDto::serialize() const { return serialize_bytes(*this); @@ -3426,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), @@ -3434,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); @@ -3451,6 +3719,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 @@ -3459,7 +3732,6 @@ namespace Axivion::Internal::Dto { entities(std::move(entities)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray EntityListDto::serialize() const { return serialize_bytes(*this); @@ -3477,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), @@ -3487,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); @@ -3506,6 +3780,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, @@ -3518,7 +3797,6 @@ namespace Axivion::Internal::Dto { lineMarkers(std::move(lineMarkers)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray FileViewDto::serialize() const { return serialize_bytes(*this); @@ -3539,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), @@ -3552,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); @@ -3574,6 +3854,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, @@ -3630,7 +3915,6 @@ namespace Axivion::Internal::Dto { kind = IssueKindMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueDto::serialize() const { return serialize_bytes(*this); @@ -3652,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), @@ -3666,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); @@ -3689,6 +3975,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, @@ -3709,7 +4000,6 @@ namespace Axivion::Internal::Dto { totalRemovedCount(std::move(totalRemovedCount)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray IssueTableDto::serialize() const { return serialize_bytes(*this); @@ -3725,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), @@ -3733,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); @@ -3750,6 +4042,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 @@ -3758,7 +4055,6 @@ namespace Axivion::Internal::Dto { metrics(std::move(metrics)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricListDto::serialize() const { return serialize_bytes(*this); @@ -3777,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), @@ -3788,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); @@ -3808,6 +4106,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, @@ -3822,7 +4125,6 @@ namespace Axivion::Internal::Dto { values(std::move(values)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricValueRangeDto::serialize() const { return serialize_bytes(*this); @@ -3838,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), @@ -3846,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); @@ -3863,6 +4167,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 @@ -3871,7 +4180,6 @@ namespace Axivion::Internal::Dto { rows(std::move(rows)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray MetricValueTableDto::serialize() const { return serialize_bytes(*this); @@ -3890,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), @@ -3901,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); @@ -3921,6 +4231,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, @@ -3969,7 +4284,6 @@ namespace Axivion::Internal::Dto { kind = IssueKindForNamedFilterCreationMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterCreateDto::serialize() const { return serialize_bytes(*this); @@ -3994,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), @@ -4011,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); @@ -4037,6 +4353,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, @@ -4111,7 +4432,6 @@ namespace Axivion::Internal::Dto { type = NamedFilterTypeMeta::enumToStr(newValue); } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterInfoDto::serialize() const { return serialize_bytes(*this); @@ -4129,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), @@ -4139,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); @@ -4158,6 +4480,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, @@ -4170,7 +4497,6 @@ namespace Axivion::Internal::Dto { visibility(std::move(visibility)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray NamedFilterUpdateDto::serialize() const { return serialize_bytes(*this); @@ -4191,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), @@ -4204,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); @@ -4226,6 +4554,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, @@ -4244,7 +4577,6 @@ namespace Axivion::Internal::Dto { hasHiddenIssues(std::move(hasHiddenIssues)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray ProjectInfoDto::serialize() const { return serialize_bytes(*this); @@ -4261,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), @@ -4270,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); @@ -4288,6 +4622,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, @@ -4298,7 +4637,6 @@ namespace Axivion::Internal::Dto { hasWarnings(std::move(hasWarnings)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray RepositoryUpdateResponseDto::serialize() const { return serialize_bytes(*this); @@ -4318,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), @@ -4330,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); @@ -4351,6 +4691,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, @@ -4367,7 +4712,6 @@ namespace Axivion::Internal::Dto { axivionDefaultFilter(std::move(axivionDefaultFilter)) { } - // throws Axivion::Internal::Dto::invalid_dto_exception QByteArray TableInfoDto::serialize() const { return serialize_bytes(*this); diff --git a/src/plugins/axivion/dashboard/dto.h b/src/plugins/axivion/dashboard/dto.h index fea5ab4ee18..4543df7d541 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static 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); + static Utils::expected_str deserializeExpected(const QByteArray &json); + TableInfoDto( QString tableDataUri, std::optional issueBaseViewUri, diff --git a/src/plugins/axivion/dynamiclistmodel.cpp b/src/plugins/axivion/dynamiclistmodel.cpp new file mode 100644 index 00000000000..70abe087bdb --- /dev/null +++ b/src/plugins/axivion/dynamiclistmodel.cpp @@ -0,0 +1,194 @@ +// 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()) { + 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); + 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(); +} + +void DynamicListModel::setAlignments(const QList &alignments) +{ + m_alignments = alignments; +} + +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..d60f9f16159 --- /dev/null +++ b/src/plugins/axivion/dynamiclistmodel.h @@ -0,0 +1,69 @@ +// 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); + void setAlignments(const QList &alignments); + + 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; + QList m_alignments; + 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 diff --git a/src/plugins/axivion/images/sortAsc.png b/src/plugins/axivion/images/sortAsc.png new file mode 100644 index 00000000000..c9072972790 Binary files /dev/null and b/src/plugins/axivion/images/sortAsc.png differ diff --git a/src/plugins/axivion/images/sortAsc@2x.png b/src/plugins/axivion/images/sortAsc@2x.png new file mode 100644 index 00000000000..b99d922b32c Binary files /dev/null and b/src/plugins/axivion/images/sortAsc@2x.png differ diff --git a/src/plugins/axivion/images/sortDesc.png b/src/plugins/axivion/images/sortDesc.png new file mode 100644 index 00000000000..a92d018d4e1 Binary files /dev/null and b/src/plugins/axivion/images/sortDesc.png differ diff --git a/src/plugins/axivion/images/sortDesc@2x.png b/src/plugins/axivion/images/sortDesc@2x.png new file mode 100644 index 00000000000..9991ff2955a Binary files /dev/null and b/src/plugins/axivion/images/sortDesc@2x.png differ diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp new file mode 100644 index 00000000000..3f8efd86c6e --- /dev/null +++ b/src/plugins/axivion/issueheaderview.cpp @@ -0,0 +1,141 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "issueheaderview.h" + +#include + +#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::IconsDisabledColor}, + {":/axivion/images/sortDesc.png", Utils::Theme::IconsDisabledColor}}); + const Utils::Icon SORT_ASC( + {{":/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::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(); + + 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 = sectionViewportPosition(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 +{ + 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{newSize.width() + ICON_SIZE + 2, qMax(newSize.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..1fbbd9b76fd --- /dev/null +++ b/src/plugins/axivion/issueheaderview.h @@ -0,0 +1,44 @@ +// 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); + void setColumnWidths(const QList &widths) { m_columnWidths = widths; } + + 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; + QList m_columnWidths; +}; + +} // namespace Axivion::Internal diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index ddb928f5e99..c034ad82f6c 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -294,10 +294,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 839e71deb62..f1cb849202b 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 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/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(); } 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; } 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; 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; 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() diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 3deaab59f21..dd99f474503 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; @@ -238,9 +267,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); 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/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/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/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index abee4c08ab0..54462f30512 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 } /*! @@ -1970,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); }); 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..5c5cbbfe133 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 @@ -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); @@ -105,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; } @@ -178,12 +176,15 @@ 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) +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(); @@ -192,6 +193,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, @@ -206,8 +220,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) @@ -216,13 +234,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 c1b48d39179..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; @@ -40,7 +41,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); 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 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/cppcodeformatter.cpp b/src/plugins/cppeditor/cppcodeformatter.cpp index d09fa37d455..ca14d22a7c1 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/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index 4dc31087bc4..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 @@ -41,21 +45,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,8 +237,9 @@ 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)); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future()); } bool FunctionDeclDefLink::isValid() const @@ -890,7 +894,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/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 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/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index e03475ea80d..67fb6d7a1bc 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -3,18 +3,19 @@ #include "cppuseselectionsupdater.h" -#include "cppeditorwidget.h" #include "cppeditordocument.h" +#include "cppeditorwidget.h" #include "cppmodelmanager.h" -#include "cpptoolsreuse.h" +#include + +#include +#include #include #include #include -#include - enum { updateUseSelectionsInternalInMs = 500 }; namespace CppEditor { @@ -67,13 +68,14 @@ 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(); 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(); @@ -142,7 +144,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; diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp index fe52bbdebd4..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) { - disconnectWatcher(); - m_watcher->cancel(); - m_watcher->waitForFinished(); - } -} +SemanticHighlighter::~SemanticHighlighter() = default; void SemanticHighlighter::setHighlightingRunner(HighlightingRunner highlightingRunner) { @@ -56,18 +49,20 @@ void SemanticHighlighter::run() qCDebug(log) << "SemanticHighlighter: run()"; - if (m_watcher) { - disconnectWatcher(); + if (m_watcher) 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(); 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) @@ -232,28 +227,10 @@ 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, - this, &SemanticHighlighter::onHighlighterResultAvailable); - connect(m_watcher.data(), &Watcher::finished, - this, &SemanticHighlighter::onHighlighterFinished); -} - -void SemanticHighlighter::disconnectWatcher() -{ - using Watcher = QFutureWatcher; - disconnect(m_watcher.data(), &Watcher::resultsReadyAt, - this, &SemanticHighlighter::onHighlighterResultAvailable); - disconnect(m_watcher.data(), &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 ea49f289a01..f4fb25ddca3 100644 --- a/src/plugins/cppeditor/semantichighlighter.h +++ b/src/plugins/cppeditor/semantichighlighter.h @@ -5,12 +5,14 @@ #include "cppeditor_global.h" +#include + #include -#include #include #include #include +#include #include namespace TextEditor { @@ -70,9 +72,6 @@ private: void handleHighlighterResults(); void onHighlighterFinished(); - void connectWatcher(); - void disconnectWatcher(); - unsigned documentRevision() const; QVector getClearedParentheses(const QTextBlock &block); @@ -80,13 +79,14 @@ private: TextEditor::TextDocument *m_baseTextDocument; unsigned m_revision = 0; - QScopedPointer> 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 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/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 3d9777f51e1..7a428c6a857 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; } diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index 87b62472985..d9926ed59b1 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(); 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; 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/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/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"); 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 c58c5731ba3..cf8c5b70e26 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, 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/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; } 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 {}; 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") 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 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/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 {}; } 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) 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; 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/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/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 37ade563161..f1bbebf575a 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); }); } @@ -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/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) 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/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); 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 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()); diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 0872ff54d3d..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(); @@ -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 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; 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; }, }); 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(); 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; 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 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; } diff --git a/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp b/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp index 344f6f1bef4..b61f91fc3bc 100644 --- a/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp +++ b/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp @@ -1545,8 +1545,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;") @@ -1586,9 +1586,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;") diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 1cf8f1bbf2f..efd4e66a8ed 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\");", @@ -5349,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", @@ -5432,14 +5436,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" diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 2c20ce14d30..ee1972c0616 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -58,12 +58,27 @@ enum class ThreadResult }; Q_ENUM_NS(ThreadResult); +enum class Execution +{ + Sync, + Async +}; + } // namespace PrintableEnums using namespace PrintableEnums; using Log = QList>; +struct Message +{ + QString name; + Handler handler; + std::optional execution = {}; +}; + +using MessageLog = QList; + struct CustomStorage { CustomStorage() { s_count.fetch_add(1); } @@ -82,7 +97,9 @@ struct TestData Group root; Log expectedLog; int taskCount = 0; - DoneWith onDone = DoneWith::Success; + DoneWith result = DoneWith::Success; + std::optional asyncCount = {}; + std::optional messageLog = {}; }; class tst_Tasking : public QObject @@ -470,7 +487,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() @@ -651,10 +668,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}; } { @@ -669,11 +686,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); @@ -705,7 +722,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}; } { @@ -718,7 +735,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}; } { @@ -739,7 +756,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}; } { @@ -761,7 +778,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}; } { @@ -785,7 +802,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}; } { @@ -811,7 +828,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}; } { @@ -855,7 +872,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(); @@ -913,10 +930,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}; } { @@ -962,7 +979,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}; } { @@ -984,152 +1001,118 @@ 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}; } { - 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, + 0 }; }; 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, + 1 }; }; - 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, + 1 }; }; - 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); } { @@ -1174,31 +1157,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}; } { @@ -1261,31 +1244,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}; } { @@ -1318,33 +1301,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}; } { @@ -1389,33 +1372,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}; } { @@ -1460,32 +1443,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}; } { @@ -1509,7 +1492,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 { @@ -1521,7 +1504,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 { @@ -1529,7 +1512,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 { @@ -1541,7 +1524,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 { @@ -1549,13 +1532,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}; } { @@ -1592,13 +1575,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}; } { @@ -1622,7 +1605,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 { @@ -1631,7 +1614,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 { @@ -1642,7 +1625,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}; } { @@ -1668,7 +1651,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 { @@ -1679,7 +1662,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 { @@ -1690,7 +1673,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 { @@ -1701,7 +1684,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}; } { @@ -1727,7 +1710,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 { @@ -1736,7 +1719,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 { @@ -1748,7 +1731,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}; } { @@ -1786,7 +1769,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}; } { @@ -1831,7 +1814,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}; } { @@ -1961,11 +1944,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}; } { @@ -2015,7 +1998,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}; } { @@ -2061,7 +2044,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}; } { @@ -2101,7 +2084,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}; } { @@ -2120,7 +2103,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}; } { @@ -2144,7 +2127,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}; } { @@ -2164,7 +2147,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}; } { @@ -2183,7 +2166,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}; } { @@ -2206,7 +2189,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}; } { @@ -2227,7 +2210,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}; } { @@ -2378,15 +2361,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}; } { @@ -2518,13 +2501,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}; } { @@ -2540,8 +2523,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, @@ -2553,8 +2536,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, @@ -2565,16 +2548,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}; } { @@ -2591,8 +2574,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 { @@ -2606,8 +2589,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, @@ -2619,8 +2602,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 { @@ -2629,8 +2612,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}; } { @@ -2665,7 +2648,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}; } { @@ -2675,12 +2658,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}, @@ -2690,7 +2673,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}; } { @@ -2793,17 +2776,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}; } { @@ -2894,11 +2877,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}; } { @@ -2909,7 +2892,7 @@ void tst_Tasking::testTree_data() createSuccessTask(1) }; QTest::newRow("ProgressWithLoopUntilFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2923,7 +2906,7 @@ void tst_Tasking::testTree_data() } }; QTest::newRow("ProgressWithNestedLoopUntilFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2934,7 +2917,7 @@ void tst_Tasking::testTree_data() createSuccessTask(1) }; QTest::newRow("ProgressWithGroupSetupFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2948,7 +2931,7 @@ void tst_Tasking::testTree_data() } }; QTest::newRow("ProgressWithNestedGroupSetupFalse") - << TestData{storage, root, {}, 1, DoneWith::Success}; + << TestData{storage, root, {}, 1, DoneWith::Success, 0}; } { @@ -2965,9 +2948,90 @@ 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}; + } + + { + // 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, + 1, + MessageLog { + {"Group 1", Handler::Setup}, + {"Task 1", Handler::Setup}, + {"Task 1", Handler::Success, Execution::Async}, + {"Group 1", Handler::Success, Execution::Async} + } + }; + 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, + 1, + MessageLog { + {"Group 1", Handler::Setup}, + {"Task 1", Handler::Setup}, + {"Task 1", Handler::Error, Execution::Async}, + {"Group 1", Handler::Error, Execution::Async} + } + }; + 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, + 0, + MessageLog { + {"Group 1", Handler::Setup}, + {"Task 1", Handler::Setup}, + {"Task 2", Handler::Setup}, + {"Task 2", Handler::Error, Execution::Sync}, + {"Task 1", Handler::Canceled, Execution::Sync}, + {"Group 1", Handler::Canceled, Execution::Sync} + } + }; + + QTest::newRow("LogSuccess") << testSuccess; + QTest::newRow("LogError") << testError; + QTest::newRow("LogCancel") << testCancel; } { @@ -3001,34 +3065,125 @@ 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. 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 messageInfix(const Message &message) +{ + if (message.handler == Handler::Setup) + return "started"; + + DoneWith doneWith; + switch (message.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 QString execution = message.execution == Execution::Async ? "asynchronously" + : "synchronously"; + const QMetaEnum doneWithEnum = QMetaEnum::fromType(); + return QString("finished %1 with %2").arg(execution, doneWithEnum.valueToKey(int(doneWith))); +} + +static bool matchesLogPattern(const QString &log, const Message &message) +{ + QStringView logView(log); + const static QLatin1String part1("TASK TREE LOG ["); + if (!logView.startsWith(part1)) + return false; + + logView = logView.mid(part1.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); + if (!logView.startsWith(part3)) + return false; + + logView = logView.mid(part3.size()); + const QString part4(messageInfix(message)); + if (!logView.startsWith(part4)) + return false; + + logView = logView.mid(part4.size()); + if (message.handler == Handler::Setup) + return logView == QLatin1String("."); + + const static QLatin1String part5(" within "); + if (!logView.startsWith(part5)) + return false; + + return logView.endsWith(QLatin1String("ms.")); +} + +const QMetaEnum s_handlerEnum = QMetaEnum::fromType(); + 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 +3196,30 @@ void tst_Tasking::testTree() QCOMPARE(actualLog, testData.expectedLog); QCOMPARE(CustomStorage::instanceCount(), 0); - QCOMPARE(result, testData.onDone); + QCOMPARE(result, testData.result); + + if (testData.asyncCount) + QCOMPARE(taskTree.asyncCount(), *testData.asyncCount); + + 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); + 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)))); + } + } } void tst_Tasking::testInThread_data() @@ -3094,7 +3272,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 +3304,7 @@ void tst_Tasking::testInThread() QCOMPARE(taskTree.isRunning(), false); QCOMPARE(CustomStorage::instanceCount(), 0); - QCOMPARE(result, testData.onDone); + QCOMPARE(result, testData.result); } struct StorageIO 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) 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"))); } // ------------------------------------------------------------------------------------------------- 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")