Merge "Merge remote-tracking branch 'origin/13.0'"

This commit is contained in:
The Qt Project
2024-02-15 07:00:47 +00:00
187 changed files with 2876 additions and 2316 deletions

View File

@@ -34,18 +34,18 @@ General
dock area
* Added the option to show file paths relative to the active project to the
search results view
(QTCREATORBUG-29462)
([QTCREATORBUG-29462](https://bugreports.qt.io/browse/QTCREATORBUG-29462))
* Added a `Current` button for selecting the directory of the current document
for searching in `Files in File System`
* Added `Copy to Clipboard` to the `About Qt Creator` dialog
(QTCREATORBUG-29886)
([QTCREATORBUG-29886](https://bugreports.qt.io/browse/QTCREATORBUG-29886))
Editing
-------
* Made syntax highlighting asynchronous
* Fixed that `Shift+Tab` did not always unindent
(QTCREATORBUG-29742)
([QTCREATORBUG-29742](https://bugreports.qt.io/browse/QTCREATORBUG-29742))
* Fixed that `Surround text selection with brackets` did nothing for `<`
* Fixed following links without a file name in documents without a file name
@@ -53,34 +53,39 @@ Editing
* Added the `Move Definition Here` refactoring action that moves an existing
function definition to its declaration
(QTCREATORBUG-9515)
([QTCREATORBUG-9515](https://bugreports.qt.io/browse/QTCREATORBUG-9515))
* Added the `Enclose in QByteArrayLiteral` refactoring action
([QTCREATORBUG-12995](https://bugreports.qt.io/browse/QTCREATORBUG-12995))
* Enabled the completion inside comments and strings by falling back to the
built-in code model
(QTCREATORBUG-20828)
([QTCREATORBUG-20828](https://bugreports.qt.io/browse/QTCREATORBUG-20828))
* Improved the position of headers inserted by refactoring operations
(QTCREATORBUG-21826)
([QTCREATORBUG-21826](https://bugreports.qt.io/browse/QTCREATORBUG-21826))
* Improved the coding style settings by separating Clang Format and other coding
style settings, and using a plain text editor for custom Clang Format settings
* Fixed that the class wizards used the class name for the include guard
instead of the file name
(QTCREATORBUG-30140)
([QTCREATORBUG-30140](https://bugreports.qt.io/browse/QTCREATORBUG-30140))
* Fixed that renaming classes did not change the include directive for the
renamed header in the source file
(QTCREATORBUG-30154)
([QTCREATORBUG-30154](https://bugreports.qt.io/browse/QTCREATORBUG-30154))
* Fixed issues with refactoring template functions
(QTCREATORBUG-29408)
([QTCREATORBUG-29408](https://bugreports.qt.io/browse/QTCREATORBUG-29408))
* Fixed the `Add Definition` refactoring action for member functions of a
template class in a namespace
([QTCREATORBUG-22076](https://bugreports.qt.io/browse/QTCREATORBUG-22076))
* Clangd
* Fixed that `Follow Symbol Under Cursor` only worked for exact matches
(QTCREATORBUG-29814)
([QTCREATORBUG-29814](https://bugreports.qt.io/browse/QTCREATORBUG-29814))
### QML
* Added navigation from QML components to the C++ code in the project
(QTCREATORBUG-28086)
([QTCREATORBUG-28086](https://bugreports.qt.io/browse/QTCREATORBUG-28086))
* Added a button for launching the QML Preview on the current document to
the editor tool bar
* Added color previews when hovering Qt color functions
(QTCREATORBUG-29966)
([QTCREATORBUG-29966](https://bugreports.qt.io/browse/QTCREATORBUG-29966))
### Python
@@ -95,7 +100,7 @@ Editing
### Widget Designer
* Fixed the indentation of the code that is inserted by `Go to slot`
(QTCREATORBUG-11730)
([QTCREATORBUG-11730](https://bugreports.qt.io/browse/QTCREATORBUG-11730))
### Compiler Explorer
@@ -108,6 +113,10 @@ Editing
endings) to the tool bar
* Added support for following links to the text editor
### Binary Files
* Fixed issues with large addresses
Projects
--------
@@ -115,41 +124,62 @@ Projects
was configured for kits that have vanished, as a replacement for the automatic
creation of "Replacement" kits
* Added the status of devices to the device lists
(QTCREATORBUG-20941)
([QTCREATORBUG-20941](https://bugreports.qt.io/browse/QTCREATORBUG-20941))
* Added the `Preferences > Build & Run > General > Application environment`
option for globally modifying the environment for all run configurations
(QTCREATORBUG-29530)
([QTCREATORBUG-29530](https://bugreports.qt.io/browse/QTCREATORBUG-29530))
* Added a file wizard for Qt translation (`.ts`) files
(QTCREATORBUG-29775)
([QTCREATORBUG-29775](https://bugreports.qt.io/browse/QTCREATORBUG-29775))
* Increased the maximum width of the target selector
(QTCREATORBUG-30038)
([QTCREATORBUG-30038](https://bugreports.qt.io/browse/QTCREATORBUG-30038))
* Fixed that the `Left` cursor key did not always collapse the current item
(QTBUG-118515)
([QTBUG-118515](https://bugreports.qt.io/browse/QTBUG-118515))
* Fixed inconsistent folder hierarchies in the project tree
(QTCREATORBUG-29923)
([QTCREATORBUG-29923](https://bugreports.qt.io/browse/QTCREATORBUG-29923))
### CMake
* Added support for custom output parsers for the configuration of projects
(QTCREATORBUG-29992)
([QTCREATORBUG-29992](https://bugreports.qt.io/browse/QTCREATORBUG-29992))
* Made cache variables available even if project configuration failed
* CMake Presets
* Fixed `Reload CMake Presets` if the project was not configured yet
([QTCREATORBUG-30238](https://bugreports.qt.io/browse/QTCREATORBUG-30238))
* Fixed that kits were accumulating on the project setup page
([QTCREATORBUG-29535](https://bugreports.qt.io/browse/QTCREATORBUG-29535))
* Fixed that `binaryDir` was not handled for all presets
([QTCREATORBUG-30236](https://bugreports.qt.io/browse/QTCREATORBUG-30236))
* Fixed a freeze with nested presets
([QTCREATORBUG-30288](https://bugreports.qt.io/browse/QTCREATORBUG-30288))
* Conan
* Fixed that backslashes were wrongly used for paths on Windows
([QTCREATORBUG-30326](https://bugreports.qt.io/browse/QTCREATORBUG-30326))
### Qbs
* Added support for code completion with the Qbs language server
(QBS-395)
### Python
* Added `Generate Kit` to the Python interpreter preferences for generating a
Python kit with this interpreter
* Added the target setup page when loading unconfigured 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
Debugging
---------
* Added a `dr` locator filter for debugging a project
### C++
* Added a pretty printer for `std::tuple`
* Fixed that breakpoints were not hit while the message dialog about missing
debug information was shown
(QTCREATORBUG-30168)
([QTCREATORBUG-30168](https://bugreports.qt.io/browse/QTCREATORBUG-30168))
### Debug Adapter Protocol
@@ -161,7 +191,7 @@ Analyzer
### Clang
* Added `Edit Checks as Strings` for Clazy
(QTCREATORBUG-24846)
([QTCREATORBUG-24846](https://bugreports.qt.io/browse/QTCREATORBUG-24846))
### Axivion
@@ -171,16 +201,19 @@ Terminal
--------
* Added `Select All` to the context menu
(QTCREATORBUG-29922)
([QTCREATORBUG-29922](https://bugreports.qt.io/browse/QTCREATORBUG-29922))
* Fixed the startup performance on Windows
(QTCREATORBUG-29840)
([QTCREATORBUG-29840](https://bugreports.qt.io/browse/QTCREATORBUG-29840))
* Fixed the integration of the `fish` shell
* Fixed that `Ctrl+W` closed the terminal even when shortcuts were blocked
(QTCREATORBUG-30070)
([QTCREATORBUG-30070](https://bugreports.qt.io/browse/QTCREATORBUG-30070))
* Fixed issues with Windows Powershell
Version Control Systems
-----------------------
* Added support for remote version control operations
### Git
* Added the upstream status for untracked branches to `Branches` view
@@ -195,22 +228,27 @@ Test Integration
Platforms
---------
### Windows
* Fixed Clang compiler ABI detection for WOA64
([QTCREATORBUG-30060](https://bugreports.qt.io/browse/QTCREATORBUG-30060))
### Android
* Add support for target-based android-build directories (??? is that ready? Qt 6.8+ ???)
(QTBUG-117443)
([QTBUG-117443](https://bugreports.qt.io/browse/QTBUG-117443))
### iOS
* Fixed the detection of iOS 17 devices
* Fixed deployment and running applications for iOS 17 devices
(application output, debugging, and profiling are not supported)
(QTCREATORBUG-29682)
([QTCREATORBUG-29682](https://bugreports.qt.io/browse/QTCREATORBUG-29682))
### Remote Linux
* Fixed that debugging unnecessarily downloaded files from the remote system
(QTCREATORBUG-29614)
([QTCREATORBUG-29614](https://bugreports.qt.io/browse/QTCREATORBUG-29614))
Credits for these changes go to:
--------------------------------
@@ -238,6 +276,7 @@ Esa Törmänen
Fabian Kosmale
Friedemann Kleint
Henning Gruendl
Ilya Kulakov
Jaroslaw Kobus
Johanna Vanhatapio
Karim Abdelrahman
@@ -249,6 +288,7 @@ Marcus Tillmanns
Mathias Hasselmann
Mats Honkamaa
Mehdi Salem
Michael Weghorn
Miikka Heikkinen
Mitch Curtis
Olivier De Cannière
@@ -257,6 +297,7 @@ Pranta Dastider
Robert Löhning
Sami Shalayel
Samuel Jose Raposo Vieira Mira
Samuel Mira
Serg Kryvonos
Shrief Gabr
Sivert Krøvel

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -183,8 +183,7 @@
skipped when you type, and removed when you press \key Backspace.
\if defined(qtcreator)
\sa {Complete CMake code},
{Enclose selected code in curly braces, parentheses, or double quotes}, {Nim}
\sa {Complete CMake code}, {Enclose code in brackets or quotes}, {Nim}
\endif
\sa {Complete code}, {Snippets}

View File

@@ -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,30 +18,39 @@
To specify global indentation settings for the C++ editor:
\list 1
\li Select \preferences > \uicontrol {C++}.
\li Go to \preferences > \uicontrol {C++}.
\image qtcreator-code-style-clang-format-global.webp {Code Style preferences}
\li In \uicontrol {Formatting mode}, select:
\list
\li \uicontrol {Indenting Only} to only indent code.
\li \uicontrol {Full Formatting} to use the \key {Ctrl+I}
keyboard shortcut to format code instead of indenting.
\li \uicontrol Disable to turn off ClangFormat.
\li \uicontrol {Use Built-In Indenter} to turn off ClangFormat.
\endlist
\li Select \uicontrol {Ignore files greater than} to make parsing faster
by ignoring big files. Specify the maximum size of files to parse.
\li To apply the formatting while you type, select
\uicontrol {Format while typing}.
\li To apply the formatting to the edited code when you save the file,
select \uicontrol {Format edited code on file save}.
\li To change the ClangFormat style globally for all projects,
select \uicontrol {Override ClangFormat configuration file}.
\li In the \uicontrol {Current settings} field, select the settings to
modify and click \uicontrol Copy.
\li Give a name to the settings and click \uicontrol OK.
\li Click \uicontrol Edit to set
select \uicontrol {Use custom settings}.
\li In \uicontrol {Custom settings}, select the settings to change, and
then select \uicontrol Copy.
\li Give a name to the settings, and select \uicontrol OK.
\li In \uicontrol ClangFormat, edit the
\l{https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
{ClangFormat Style Options}.
{ClangFormat Style Options}. The live preview shows how the
preferences change the indentation.
If you enter invalid values, you see warning messages.
\endlist
In the other tabs, you can specify how to:
\section1 Using Built-In Indenter
\image qtcreator-code-style-built-in-indenter.webp {Code Style preferences for built-in indenter}
If you select \uicontrol {Use Built-In Indenter} in
\uicontrol {Formatting mode}, you can specify how to:
\list
\li Interpret the \key Tab and \key Backspace key presses.
@@ -52,12 +61,9 @@
\li Bind pointers (*) and references (&) in types and declarations to
identifiers, type names, or left or right \c const or \c volatile
keywords.
\li Name getter functions.
\endlist
Use the live preview to see how the preferences change the indentation.
\section1 Specifying Settings for Content
\section2 Specifying Settings for Content
You can indent public, protected, and private statements and declarations
related to them within classes.
@@ -67,24 +73,24 @@
\image qtcreator-code-style-content.png {Content preferences}
\section1 Specifying Settings for Braces
\section2 Specifying Settings for Braces
You can indent class, namespace, enum and function declarations and code
blocks.
\image qtcreator-code-style-braces.png {Braces preferences}
\section1 Specifying Settings for Switch Statements
\section2 Specifying Settings for Switch Statements
You can indent case or default statements, or statements or blocks related
to them within switch statements.
\image qtcreator-code-style-switch.png {Switch preferences}
\section1 Specifying Alignment
\section2 Specifying Alignment
To align continuation lines to tokens after assignments, such as \c = or
\c +=, select the \uicontrol {Align after assignments} check box. You can
\c +=, select \uicontrol {Align after assignments}. You can
specify additional settings for aligning continuation lines in the
\uicontrol General tab.
@@ -94,7 +100,7 @@
\image qtcreator-code-style-alignment.png {Alignment preferences}
\section1 Binding Pointers and References
\section2 Binding Pointers and References
To bind pointers (\c *) and references (\c &) in types and declarations to
identifiers, type names, or left or right \c const or \c volatile keywords,
@@ -105,15 +111,6 @@
\image qtcreator-pointers-references.png {Pointers and References preferences}
\section1 Creating Project-Specific ClangFormat Files
To override the \c {.clang-format} file for a particular project, create a
copy of the built-in style and edit its settings by selecting
\uicontrol Projects > \uicontrol {Project Settings} >
\uicontrol {Code Style} > \uicontrol Copy > \uicontrol Edit >
\uicontrol {ClangFormat} >
\uicontrol {Override ClangFormat configuration file}.
\section1 Creating ClangFormat Files from Command Line
You can create \c {.clang-format} files that have the configuration
@@ -124,5 +121,6 @@
clang-format -style=llvm -dump-config > .clang-format
\endcode
\sa {Indent text or code}, {Behavior}, {Qt Quick Code Style}, {Nim}
\sa {Indent text or code}, {Specify code style}, {Behavior},
{Qt Quick Code Style}, {Nim}
*/

View File

@@ -16,11 +16,11 @@
To specify preferences for the Nim editor (experimental):
\list 1
\li Select \preferences > \uicontrol Nim.
\li In the \uicontrol {Current settings} field, select the settings to
modify and click \uicontrol Copy.
\li Go to \preferences > \uicontrol Nim.
\li In \uicontrol {Custom settings}, select the settings to
modify, and then select \uicontrol Copy.
\image qtcreator-preferences-nim-code-style.webp {Nim Code Style preferences}
\li Give a name to the settings and click \uicontrol OK.
\li Give a name to the settings, and select \uicontrol OK.
\li Specify how to interpret the \key Tab key presses and how to align
continuation lines.
\li Select \uicontrol OK to save the settings.
@@ -36,7 +36,7 @@
completion.
To use Nimsuggest, you must install it on the development PC and enter the
path to the tool executable in the \uicontrol Path field.
path to the tool executable in \uicontrol Path.
\image qtcreator-preferences-nim-tools.webp

View File

@@ -134,18 +134,30 @@
\title Use Qt Linguist
Most of the text to translate in an application consists of either single
words or short phrases. These typically appear as window titles, menu items,
tooltips, and labels to buttons, checkboxes, and radio buttons.
You mark the phrases as translatable in the QML and C++ source code. Qt
localization tools provide context information for each of the phrases to
help the translator understand their meaning. You can add comments to the
phrases.
Translation files contain all the user-visible text and keyboard shortcuts
in an application and translations of that text.
When you \l{Creating Projects}{create a new project}, you can automatically
generate a translation source file (TS) for one language. You can add other
languages later by editing the project file.
generate a translation source file (TS) for one language. To add other
languages, edit the project file or go to \uicontrol File >
\uicontrol {New File}.
To open TS files in Qt Linguist, right-click a TS file in the
\uicontrol Projects or \uicontrol {File System} view and select
\uicontrol Projects or \uicontrol {File System} view and go to
\uicontrol {Open With} > \uicontrol {Qt Linguist} in the context menu.
For more information about Qt Linguist, see \l{Qt Linguist Manual}.
\section1 Use lupdate and lrelease
You can use the Qt Linguist release manager tools, lupdate and lrelease,
Use the Qt Linguist release manager tools, lupdate and lrelease,
directly from \QC. The lupdate tool synchronizes source
code and translations. The lrelease tool creates run-time
translation files for use by the released application.
@@ -156,22 +168,27 @@
{Qt6::LinguistTools}.
By default, the project .pro file is passed to the tools as an argument. To
specify other command-line arguments for the tools, select \uicontrol Tools >
specify other command-line arguments for the tools, go to \uicontrol Tools >
\uicontrol External > \uicontrol Configure.
\section2 Synchronize TS files
To synchronize TS files from a translator with the
application code, select \uicontrol Tools > \uicontrol External >
application code, go to \uicontrol Tools > \uicontrol External >
\uicontrol Linguist > \uicontrol {Update Translations (lupdate)}.
\section2 Generate QM files
To generate from the TS files Qt message (QM) files that can be used by an
application, select \uicontrol Tools > \uicontrol External >
application, go to \uicontrol Tools > \uicontrol External >
\uicontrol Linguist > \uicontrol {Release Translations (lrelease)}.
\sa {Use external tools}
\if defined(qtcreator)
\sa {Add translation files}
\endif
\sa {Use external tools}, {Internationalization with Qt},
{Qt Linguist Manual}
*/
/*!

View File

@@ -0,0 +1,49 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page creator-how-to-contact-qt.html
\previouspage creator-how-tos.html
\ingroup creator-how-to-use
\title Contact Qt
To report bugs and tell us what you think about \QC and Qt,
go to \uicontrol Help.
\section1 Report bugs and suggest improvements
To report bugs and add suggestions for improvement to the
\l{https://bugreports.qt.io/}{Qt Project Bug Tracker},
go to \uicontrol Help > \uicontrol {Report Bug}.
To copy information about your \QC version that you can paste to the bug
report, go to \uicontrol Help > \uicontrol {About \QC} and select
\uicontrol {Copy and Close}.
To copy detailed information about your system that you can paste to the bug
report, go to \uicontrol Help > \uicontrol {System Information} and select
\uicontrol {Copy to Clipboard}.
To get commercial Qt support, go to \uicontrol Help >
\uicontrol {Commercial Qt Support} and create a service request.
To check your licenses and services, go to \uicontrol Help >
\uicontrol {Qt Account}.
\section1 Give feedback
To rate \QC and send us feedback, go to \uicontrol Help >
\uicontrol {Give Feedback}.
Or, give feedback from your Qt account.
\section1 Join discussions
To join the \l{https://lists.qt-project.org/listinfo/qt-creator}
{\QC mailing list} or \l{https://web.libera.chat/#qt-creator}
{#qt-creator} channel on Libera.Chat IRC, go to \uicontrol Help >
\uicontrol Contact.
\sa {Pasting and Fetching Code Snippets}, {Technical Support}
*/

View File

@@ -77,7 +77,7 @@
\generatelist creator-how-to-projects-create
\section2 Create Files
\section2 Add Files
\generatelist creator-how-to-projects-files
@@ -288,16 +288,18 @@
\ingroup creator-how-to-edit
\title Enclose selected code in curly braces, parentheses, or double quotes
\title Enclose code in brackets or quotes
When you have selected code and enter one of the following opening
characters, the matching closing character is added automatically
at the end of the selection:
Select code and enter one of the following opening characters to add
the matching closing character at the end of the selection:
\list
\li {
\li (
\li {
\li [
\li <
\li "
\li '
\endlist
To specify whether to automatically insert matching characters,

View File

@@ -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
// **********************************************************************
@@ -8,20 +8,22 @@
// **********************************************************************
/*!
\previouspage creator-how-tos.html
\page creator-known-issues.html
\nextpage creator-glossary.html
\previouspage creator-reference.html
\ingroup creator-reference
\title Known Issues
This section lists known issues in \QC version \qtcversion. The development
team is aware of them, and therefore, you do not need to report them as
bugs.
\brief Known issues in \QC version \qtcversion.
For a list of fixed issues and added features, see the changelog file in
the \c{qtcreator\dist} folder or the \l{https://bugreports.qt.io}
The \QC development team is aware of the issues described here, and
therefore, you do not need to report them in the \l{https://bugreports.qt.io}
{Qt Project Bug Tracker}.
For a list of fixed issues and added features, go to \uicontrol Help >
\uicontrol {Change Log}.
\section1 General Issues
\list

View File

@@ -43,8 +43,8 @@
\li \uicontrol {Qt}
\li Source and header files for item, table,
or list models, \QD forms and a matching classes for Qt Widgets
projects, Qt resource files, as well as QML and JavaScript files
for Qt Quick projects.
projects, Qt resource and translation files, as well as QML and
JavaScript files for Qt Quick projects.
\row
\li \uicontrol {Compiler Explorer}
\li Example setup for using Compiler Explorer to compile C++ or Python
@@ -74,9 +74,7 @@
\li Empty Nim source and script files.
\endtable
\sa {Create compiler explorer sessions}, {Create C++ classes},
{Create OpenGL fragment and vertex shaders}, {Create resource files},
{Create UML-style models}, {Create vcpkg manifest files},
\sa {Add Files}{How To: Add Files}, {Create UML-style models},
{Use project wizards}
*/

View File

@@ -0,0 +1,30 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page creator-how-to-add-ts-files.html
\previouspage creator-how-tos.html
\ingroup creator-how-to-projects-files
\title Add translation files
When you \l{Creating Projects}{create a new project}, you can automatically
generate a translation source file (TS) for one language. To add other
languages later, use a file wizard.
To create a translation source (TS) file for a language:
\list 1
\li Go to \uicontrol File > \uicontrol {New File}.
\li Select \uicontrol Qt > \uicontrol {Qt Translation File} >
\uicontrol Choose.
\li In \uicontrol Language, select a language and territory (\e locale).
\image qtcreator-new-file-ts.webp {Select language for a TS file}
\endlist
You can see the file name in \uicontrol {Translation file}.
\sa {Create files}, {Use project wizards}, {Use Qt Linguist},
{Internationalization with Qt}, {Qt Linguist Manual}
*/

View File

@@ -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
// **********************************************************************
@@ -19,16 +19,15 @@
and editor the file opens in. \QC opens C++ files in \uicontrol Edit mode
in the C++ code editor and QML files in the Qt Quick editor.
You can specify indentation for:
Specify indentation for:
\list
\li C++ files
\li QML files
\li Nim files
\li Other text files
\endlist
You can specify code style either globally or separately for each project.
Specify code style either globally or separately for each project.
You can specify several sets of code style settings and easily switch between
them. In addition, you can import and export code style settings.
@@ -36,31 +35,30 @@
\list 1
\li Select \uicontrol Projects > \uicontrol {Project Settings} >
\li Go to \uicontrol Projects > \uicontrol {Project Settings} >
\uicontrol {Code Style}.
\image qtcreator-code-style-clang-format-project.webp {Code Style settings in Projects mode}
\li In the \uicontrol Language field, select \uicontrol C++,
\li In \uicontrol Language, select \uicontrol C++,
\uicontrol {Qt Quick}, or \uicontrol Nim.
\li Deselect the \uicontrol {Use global settings} check box.
\li For C++, clear \uicontrol {Use global settings} to use the
\c {.clang-format} file for the project.
\li In the \uicontrol {Current settings} field, select the settings to modify
and click \uicontrol Copy.
\li Give a name to the settings and click \uicontrol OK.
\li Click \uicontrol Edit to specify a code style for the project.
\li To override the project's \c {.clang-format} file,
select \uicontrol {Use custom settings}.
\li In \uicontrol {Custom settings}, select the settings to use for the
project.
\endlist
In rare cases, ClangFormat can trip over a code construct and
trigger a \QC crash. If that happens for your project, select
\uicontrol Disable as the formatting mode to switch
ClangFormat off for the project. If you can reproduce the crash,
please select \uicontrol Help > \uicontrol {Report Bug} to report
the bug and and attach the code that triggers the crash.
\uicontrol {Use Built-In Indenter} in \uicontrol {Formatting mode} to
turn off ClangFormat for the project. If you can reproduce the crash,
go to \uicontrol Help > \uicontrol {Report Bug} to report
the bug and attach the code that triggers the crash to the bug report.
\sa {Indent text or code}, {Edit MIME types}, {C++ Code Style},
{Qt Quick Code Style}, {Nim}

View File

@@ -87,7 +87,25 @@
To run in a clean system environment, select \uicontrol {Clean Environment}.
\section2 Run on devices
\section2 Set the environment for all run configurations
To set environment variables for running and debugging applications, so
that they don't affect \QC itself, set environment variables for run
configurations of all projects:
\list 1
\li Go to \preferences > \uicontrol {Build & Run} > \uicontrol General.
\li Select \uicontrol Change in \uicontrol {Application environment}.
\li Set environment variables in \uicontrol {Edit Environment}.
\image qtcreator-edit-environment.webp {Edit Environment dialog}
\endlist
For example, set \c QT_FORCE_STDERR_LOGGING=1 to see application output
in \l {Application Output} instead of a journal or system log.
Or, set \c QT_MESSAGE_PATTERN to add information to debugging messages.
\section2 Use device environment
When running on a mobile device connected to the development host, \QC
fetches information about the \uicontrol {Device Environment} from the device.

View File

@@ -112,21 +112,13 @@
lists the kits that are compatible with your project. To activate one or more
kits, click them.
\image qtcreator-project-kits.png {List of kits in Projects mode sidebar}
\image qtcreator-projects-kits.webp {List of kits in Projects mode sidebar}
The list displays kits from \preferences >
\uicontrol Kits. Warning and error icons indicate that the kit configuration
is not suitable for the project type. To view the warning and error messages,
move the mouse pointer over the kit name.
In the list of kits, you may see entries described as \e {Replacement for
<kit-name>}. \QC generates them to save your project-specific settings,
such as custom build flags or run configuration arguments that would
disappear if the corresponding kits were simply removed when you remove
Qt versions while updating your Qt installation. You can modify the kit
configuration to use a currently installed Qt version and save the kit
under a new name.
\section1 Manage kits
To modify kit configuration or to \l{Add kits}{add kits} to the list or to
@@ -134,10 +126,11 @@
Each kit consists of a set of values that define one environment, such as a
\l{glossary-device}{device}, \l{Add compilers}{compiler},
\l{Add debuggers}{debugger}, and \l{Add Qt versions}{Qt version}.
\l{Add debuggers}{debugger}, and \l{Add Qt versions}{Qt version}, as well
as steps for building, deploying, and running applications.
To copy the build and run settings for a kit to another kit, select
\uicontrol {Copy Steps from Other Kit} in the context menu.
To copy the build, deploy, and run steps from another kit, select
\uicontrol {Copy Steps from Another Kit} in the context menu.
To deactivate a kit, select \uicontrol {Disable Kit for Project} in the
context menu.
@@ -148,5 +141,32 @@
To import an existing build for the project, select
\uicontrol {Import Existing Build}.
\section1 Copy custom settings from vanished targets
\QC creates a list of \uicontrol {Vanished Targets} to save project-specific
settings, such as custom build flags or run configuration arguments, that
would disappear if \QOI removes the corresponding kits when you update your
Qt installation.
\image qtcreator-projects-vanished-targets.webp {Vanished Targets in Projects}
Go to one of the following options in the context menu to restore the
project's settings:
\list
\li \uicontrol {Create a New Kit} creates a new kit with the same name
for the same device type, with the original build, deploy, and run
steps. Other kit settings are not restored.
\li \uicontrol {Copy Steps to Another Kit} copies the build, deploy, and
run steps to another kit.
\endlist
To remove vanished targets, go to \uicontrol {Remove Vanished Target} or
\uicontrol {Remove All Vanished Targets} in the context menu.
\note Since version 13.0, \QC does not create \e {replacement kits}, but you
might still see them listed for existing projects. You can copy the build,
deploy, and run steps from them to other kits.
\sa {Add kits}, {Configuring Projects}, {kits-tab}{Kits}
*/

View File

@@ -157,7 +157,7 @@
\list
\li Create Projects
\generatelist creator-how-to-projects-create
\li Create Files
\li Add Files
\generatelist creator-how-to-projects-files
\li Configure Projects
\generatelist creator-how-to-projects-configure

View File

@@ -86,6 +86,7 @@
\endlist
\li \b {\l{Reference}}
\list
\li \l {Acknowledgements}
\li \l {Build Systems}
\li \l {Command-Line Options}
\li \l {Custom Wizards}
@@ -94,21 +95,5 @@
\li \l {Supported Platforms}
\li \l {Reference}{See More...}
\endlist
\row
\li {4,1} \b {Contact Us}
\list
\li To report bugs and suggestions to the
\l{https://bugreports.qt.io/}{Qt Project Bug Tracker},
select \uicontrol Help > \uicontrol {Report Bug}.
\li To copy and paste detailed information about your
system to the bug report, select \uicontrol Help >
\uicontrol {System Information}.
\li To join the \l{https://lists.qt-project.org/listinfo/qt-creator}
{\QC mailing list} or \l{https://web.libera.chat/#qt-creator}
{#qt-creator} channel on Libera.Chat IRC, select
\uicontrol Help > \uicontrol Contact.
\li For credits and a list of third-party libraries, see
\l {Acknowledgements}.
\endlist
\endtable
*/

View File

@@ -19,21 +19,17 @@
To specify QML code style globally:
\list 1
\li Select \preferences > \uicontrol {Qt Quick}.
\li In the \uicontrol {Current settings} field, select the settings to
modify and click \uicontrol Copy.
\image qtcreator-options-code-style-qml.png {QML Code Style preferences}
\li Give a name to the settings and click \uicontrol OK.
\li Click \uicontrol Edit to specify code style settings for the project.
\image qtcreator-code-style-settings-edit-qtquick.png {Edit Code Style dialog}
\li Go to \preferences > \uicontrol {Qt Quick} > \uicontrol {Code Style}.
\li In \uicontrol {Custom settings}, select the settings to
modify, and then select \uicontrol Copy.
\image qtcreator-preferences-qtquick-code-style.webp {Qt Quick Code Style preferences}
\li Give a name to the settings, and select \uicontrol OK.
\li Specify how to interpret the \key Tab key presses and how to align
continuation lines.
\li In \uicontrol {Line length}, set the maximum line length for
code lines.
\endlist
You can specify how to interpret the \key Tab key presses and how to align
continuation lines.
In \uicontrol {Line length}, you can adjust the maximum line length for
code lines.
To override the global preferences for a particular project, select
\uicontrol Projects > \uicontrol {Code Style}.

View File

@@ -204,7 +204,7 @@ def qdump__CPlusPlus__Internal__Value(d, value):
def qdump__Utils__FilePath(d, value):
data, path_len, scheme_len, host_len = d.split("{@QString}IHH", value)
elided, enc = d.encodeStringHelper(data, d.displayStringLimit)
length, enc = d.encodeStringHelper(data, d.displayStringLimit)
# enc is concatenated path + scheme + host
if scheme_len:
scheme_pos = path_len * 4
@@ -221,7 +221,7 @@ def qdump__Utils__FilePath(d, value):
val += path_enc
else:
val = enc
d.putValue(val, "utf16", elided=elided)
d.putValue(val, "utf16", length=length)
d.putPlainChildren(value)

View File

@@ -44,15 +44,15 @@ class ReportItem():
subsequent better guesses during a putItem() run.
"""
def __init__(self, value=None, encoding=None, priority=-100, elided=None):
def __init__(self, value=None, encoding=None, priority=-100, length=None):
self.value = value
self.priority = priority
self.encoding = encoding
self.elided = elided
self.length = length
def __str__(self):
return 'Item(value: %s, encoding: %s, priority: %s, elided: %s)' \
% (self.value, self.encoding, self.priority, self.elided)
return 'Item(value: %s, encoding: %s, priority: %s, length: %s)' \
% (self.value, self.encoding, self.priority, self.length)
class Timer():
@@ -349,8 +349,8 @@ class DumperBase():
else:
if self.currentValue.encoding is not None:
self.put('valueencoded="%s",' % self.currentValue.encoding)
if self.currentValue.elided:
self.put('valueelided="%s",' % self.currentValue.elided)
if self.currentValue.length:
self.put('valuelen="%s",' % self.currentValue.length)
self.put('value="%s",' % self.currentValue.value)
except:
pass
@@ -376,7 +376,7 @@ class DumperBase():
b = bytes(bytearray.fromhex(value))
value = codecs.decode(b, 'utf-16')
self.put('"%s"' % value)
if self.currentValue.elided:
if self.currentValue.length:
self.put('...')
if self.currentType.value:
@@ -545,40 +545,40 @@ class DumperBase():
# assume no Qt 3 support by default
return False
# Clamps size to limit.
def computeLimit(self, size, limit):
# Clamps length to limit.
def computeLimit(self, length, limit=0):
if limit == 0:
limit = self.displayStringLimit
if limit is None or size <= limit:
return 0, size
return size, limit
if limit is None or length <= limit:
return length
return limit
def vectorData(self, value):
if self.qtVersion() >= 0x060000:
data, size, alloc = self.qArrayData(value)
data, length, alloc = self.qArrayData(value)
elif self.qtVersion() >= 0x050000:
vector_data_ptr = self.extractPointer(value)
if self.ptrSize() == 4:
(ref, size, alloc, offset) = self.split('IIIp', vector_data_ptr)
(ref, length, alloc, offset) = self.split('IIIp', vector_data_ptr)
else:
(ref, size, alloc, pad, offset) = self.split('IIIIp', vector_data_ptr)
(ref, length, alloc, pad, offset) = self.split('IIIIp', vector_data_ptr)
alloc = alloc & 0x7ffffff
data = vector_data_ptr + offset
else:
vector_data_ptr = self.extractPointer(value)
(ref, alloc, size) = self.split('III', vector_data_ptr)
(ref, alloc, length) = self.split('III', vector_data_ptr)
data = vector_data_ptr + 16
self.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000)
return data, size
self.check(0 <= length and length <= alloc and alloc <= 1000 * 1000 * 1000)
return data, length
def qArrayData(self, value):
if self.qtVersion() >= 0x60000:
dd, data, size = self.split('ppp', value)
dd, data, length = self.split('ppp', value)
if dd:
_, _, alloc = self.split('iip', dd)
else: # fromRawData
alloc = size
return data, size, alloc
alloc = length
return data, length, alloc
return self.qArrayDataHelper(self.extractPointer(value))
def qArrayDataHelper(self, array_data_ptr):
@@ -586,10 +586,10 @@ class DumperBase():
if self.qtVersion() >= 0x050000:
# QTypedArray:
# - QtPrivate::RefCount ref
# - int size
# - int length
# - uint alloc : 31, capacityReserved : 1
# - qptrdiff offset
(ref, size, alloc, offset) = self.split('IIpp', array_data_ptr)
(ref, length, alloc, offset) = self.split('IIpp', array_data_ptr)
alloc = alloc & 0x7ffffff
data = array_data_ptr + offset
if self.ptrSize() == 4:
@@ -599,43 +599,42 @@ class DumperBase():
elif self.qtVersion() >= 0x040000:
# Data:
# - QBasicAtomicInt ref;
# - int alloc, size;
# - int alloc, length;
# - [padding]
# - char *data;
if self.ptrSize() == 4:
(ref, alloc, size, data) = self.split('IIIp', array_data_ptr)
(ref, alloc, length, data) = self.split('IIIp', array_data_ptr)
else:
(ref, alloc, size, pad, data) = self.split('IIIIp', array_data_ptr)
(ref, alloc, length, pad, data) = self.split('IIIIp', array_data_ptr)
else:
# Data:
# - QShared count;
# - QChar *unicode
# - char *ascii
# - uint len: 30
(dummy, dummy, dummy, size) = self.split('IIIp', array_data_ptr)
size = self.extractInt(array_data_ptr + 3 * self.ptrSize()) & 0x3ffffff
alloc = size # pretend.
(dummy, dummy, dummy, length) = self.split('IIIp', array_data_ptr)
length = self.extractInt(array_data_ptr + 3 * self.ptrSize()) & 0x3ffffff
alloc = length # pretend.
data = self.extractPointer(array_data_ptr + self.ptrSize())
return data, size, alloc
return data, length, alloc
def encodeStringHelper(self, value, limit):
data, size, alloc = self.qArrayData(value)
data, length, alloc = self.qArrayData(value)
if alloc != 0:
self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000)
elided, shown = self.computeLimit(2 * size, 2 * limit)
return elided, self.readMemory(data, shown)
self.check(0 <= length and length <= alloc and alloc <= 100 * 1000 * 1000)
shown = self.computeLimit(2 * length, 2 * limit)
return length, self.readMemory(data, shown)
def encodeByteArrayHelper(self, value, limit):
data, size, alloc = self.qArrayData(value)
data, length, alloc = self.qArrayData(value)
if alloc != 0:
self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000)
elided, shown = self.computeLimit(size, limit)
return elided, self.readMemory(data, shown)
self.check(0 <= length and length <= alloc and alloc <= 100 * 1000 * 1000)
shown = self.computeLimit(length, limit)
return length, self.readMemory(data, shown)
def putCharArrayValue(self, data, size, charSize,
def putCharArrayValue(self, data, length, charSize,
displayFormat=DisplayFormat.Automatic):
bytelen = size * charSize
elided, shown = self.computeLimit(bytelen, self.displayStringLimit)
shown = self.computeLimit(length, self.displayStringLimit)
mem = self.readMemory(data, shown)
if charSize == 1:
if displayFormat in (DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String):
@@ -650,13 +649,13 @@ class DumperBase():
encodingType = 'ucs4'
#childType = 'int'
self.putValue(mem, encodingType, elided=elided)
self.putValue(mem, encodingType, length=length)
if displayFormat in (
DisplayFormat.SeparateLatin1String,
DisplayFormat.SeparateUtf8String,
DisplayFormat.Separate):
elided, shown = self.computeLimit(bytelen, 100000)
shown = self.computeLimit(length, 100000)
self.putDisplay(encodingType + ':separate', self.readMemory(data, shown))
def putCharArrayHelper(self, data, size, charType,
@@ -676,15 +675,15 @@ class DumperBase():
return self.hexencode(bytes(self.readRawMemory(addr, size)))
def encodeByteArray(self, value, limit=0):
elided, data = self.encodeByteArrayHelper(value, limit)
_, data = self.encodeByteArrayHelper(value, limit)
return data
def putByteArrayValue(self, value):
elided, data = self.encodeByteArrayHelper(value, self.displayStringLimit)
self.putValue(data, 'latin1', elided=elided)
length, data = self.encodeByteArrayHelper(value, self.displayStringLimit)
self.putValue(data, 'latin1', length=length)
def encodeString(self, value, limit=0):
elided, data = self.encodeStringHelper(value, limit)
_, data = self.encodeStringHelper(value, limit)
return data
def encodedUtf16ToUtf8(self, s):
@@ -730,8 +729,8 @@ class DumperBase():
return inner
def putStringValue(self, value):
elided, data = self.encodeStringHelper(value, self.displayStringLimit)
self.putValue(data, 'utf16', elided=elided)
length, data = self.encodeStringHelper(value, self.displayStringLimit)
self.putValue(data, 'utf16', length=length)
def putPtrItem(self, name, value):
with SubItem(self, name):
@@ -900,12 +899,12 @@ class DumperBase():
if not self.isInt(thing):
raise RuntimeError('Expected an integral value, got %s' % type(thing))
def readToFirstZero(self, base, tsize, maximum):
def readToFirstZero(self, base, typesize, maximum):
self.checkIntType(base)
self.checkIntType(tsize)
self.checkIntType(typesize)
self.checkIntType(maximum)
code = self.packCode + (None, 'b', 'H', None, 'I')[tsize]
code = self.packCode + (None, 'b', 'H', None, 'I')[typesize]
#blob = self.readRawMemory(base, 1)
blob = bytes()
while maximum > 1:
@@ -916,8 +915,8 @@ class DumperBase():
maximum = int(maximum / 2)
self.warn('REDUCING READING MAXIMUM TO %s' % maximum)
#DumperBase.warn('BASE: 0x%x TSIZE: %s MAX: %s' % (base, tsize, maximum))
for i in range(0, maximum, tsize):
#DumperBase.warn('BASE: 0x%x TSIZE: %s MAX: %s' % (base, typesize, maximum))
for i in range(0, maximum, typesize):
t = struct.unpack_from(code, blob, i)[0]
if t == 0:
return 0, i, self.hexencode(blob[:i])
@@ -925,9 +924,9 @@ class DumperBase():
# Real end is unknown.
return -1, maximum, self.hexencode(blob[:maximum])
def encodeCArray(self, p, tsize, limit):
elided, shown, blob = self.readToFirstZero(p, tsize, limit)
return elided, blob
def encodeCArray(self, p, typesize, limit):
length, shown, blob = self.readToFirstZero(p, typesize, limit)
return length, blob
def putItemCount(self, count, maximum=1000000000):
# This needs to override the default value, so don't use 'put' directly.
@@ -1043,12 +1042,12 @@ class DumperBase():
self.currentType.value = typish.name
self.currentType.priority = priority
def putValue(self, value, encoding=None, priority=0, elided=None):
def putValue(self, value, encoding=None, priority=0, length=None):
# Higher priority values override lower ones.
# elided = 0 indicates all data is available in value,
# length = None indicates all data is available in value,
# otherwise it's the true length.
if priority >= self.currentValue.priority:
self.currentValue = ReportItem(value, encoding, priority, elided)
self.currentValue = ReportItem(value, encoding, priority, length)
def putSpecialValue(self, encoding, value='', children=None):
self.putValue(value, encoding)
@@ -1226,13 +1225,13 @@ class DumperBase():
return False
def putSimpleCharArray(self, base, size=None):
if size is None:
elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
def putSimpleCharArray(self, base, length=None):
if length is None:
length, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
else:
elided, shown = self.computeLimit(int(size), self.displayStringLimit)
shown = self.computeLimit(length)
data = self.readMemory(base, shown)
self.putValue(data, 'latin1', elided=elided)
self.putValue(data, 'latin1', length=length)
def putDisplay(self, editFormat, value):
self.putField('editformat', editFormat)
@@ -1248,8 +1247,8 @@ class DumperBase():
if targetType.name in ('char', 'signed char', 'unsigned char', 'uint8_t', 'CHAR'):
# Use UTF-8 as default for char *.
self.putType(typeName)
(elided, shown, data) = self.readToFirstZero(ptr, 1, limit)
self.putValue(data, 'utf8', elided=elided)
(length, shown, data) = self.readToFirstZero(ptr, 1, limit)
self.putValue(data, 'utf8', length=length)
if self.isExpanded():
self.putArrayData(ptr, shown, innerType)
return True
@@ -1257,55 +1256,55 @@ class DumperBase():
if targetType.name in ('wchar_t', 'WCHAR'):
self.putType(typeName)
charSize = self.lookupType('wchar_t').size()
(elided, data) = self.encodeCArray(ptr, charSize, limit)
(length, data) = self.encodeCArray(ptr, charSize, limit)
if charSize == 2:
self.putValue(data, 'utf16', elided=elided)
self.putValue(data, 'utf16', length=length)
else:
self.putValue(data, 'ucs4', elided=elided)
self.putValue(data, 'ucs4', length=length)
return True
if displayFormat == DisplayFormat.Latin1String:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'latin1', elided=elided)
(length, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'latin1', length=length)
return True
if displayFormat == DisplayFormat.SeparateLatin1String:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'latin1', elided=elided)
(length, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'latin1', length=length)
self.putDisplay('latin1:separate', data)
return True
if displayFormat == DisplayFormat.Utf8String:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'utf8', elided=elided)
(length, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'utf8', length=length)
return True
if displayFormat == DisplayFormat.SeparateUtf8String:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'utf8', elided=elided)
(length, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'utf8', length=length)
self.putDisplay('utf8:separate', data)
return True
if displayFormat == DisplayFormat.Local8BitString:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'local8bit', elided=elided)
(length, data) = self.encodeCArray(ptr, 1, limit)
self.putValue(data, 'local8bit', length=length)
return True
if displayFormat == DisplayFormat.Utf16String:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 2, limit)
self.putValue(data, 'utf16', elided=elided)
(length, data) = self.encodeCArray(ptr, 2, limit)
self.putValue(data, 'utf16', length=length)
return True
if displayFormat == DisplayFormat.Ucs4String:
self.putType(typeName)
(elided, data) = self.encodeCArray(ptr, 4, limit)
self.putValue(data, 'ucs4', elided=elided)
(length, data) = self.encodeCArray(ptr, 4, limit)
self.putValue(data, 'ucs4', length=length)
return True
return False
@@ -2577,17 +2576,17 @@ class DumperBase():
def extractQStringFromQDataStream(self, buf, offset):
""" Read a QString from the stream """
size = struct.unpack_from('!I', buf, offset)[0]
length = struct.unpack_from('!I', buf, offset)[0]
offset += 4
string = buf[offset:offset + size].decode('utf-16be')
return (string, offset + size)
string = buf[offset:offset + length].decode('utf-16be')
return (string, offset + length)
def extractQByteArrayFromQDataStream(self, buf, offset):
""" Read a QByteArray from the stream """
size = struct.unpack_from('!I', buf, offset)[0]
length = struct.unpack_from('!I', buf, offset)[0]
offset += 4
string = buf[offset:offset + size].decode('latin1')
return (string, offset + size)
string = buf[offset:offset + length].decode('latin1')
return (string, offset + length)
def extractIntFromQDataStream(self, buf, offset):
""" Read an int from the stream """

View File

@@ -28,46 +28,46 @@ def qform__QByteArray():
def qedit__QByteArray(d, value, data):
d.call('void', value, 'resize', str(len(data)))
(base, size, alloc) = d.stringData(value)
(base, length, alloc) = d.stringData(value)
d.setValues(base, 'char', [ord(c) for c in data])
def qdump__QByteArray(d, value):
if d.qtVersion() >= 0x60000:
dd, data, size = value.split('ppi')
dd, data, length = value.split('ppi')
if dd:
_, _, alloc = d.split('iii', dd)
else: # fromRawData
alloc = size
alloc = length
else:
data, size, alloc = d.qArrayData(value)
data, length, alloc = d.qArrayData(value)
d.check(alloc == 0 or (0 <= size and size <= alloc and alloc <= 100000000))
if size > 0:
d.check(alloc == 0 or (0 <= length and length <= alloc and alloc <= 100000000))
if length > 0:
d.putExpandable()
elided, shown = d.computeLimit(size, d.displayStringLimit)
shown = d.computeLimit(length, d.displayStringLimit)
p = d.readMemory(data, shown)
displayFormat = d.currentItemFormat()
if displayFormat == DisplayFormat.Automatic or displayFormat == DisplayFormat.Latin1String:
d.putValue(p, 'latin1', elided=elided)
d.putValue(p, 'latin1', length=length)
elif displayFormat == DisplayFormat.SeparateLatin1String:
d.putValue(p, 'latin1', elided=elided)
d.putValue(p, 'latin1', length=length)
d.putDisplay('latin1:separate', d.encodeByteArray(value, limit=100000))
elif displayFormat == DisplayFormat.Utf8String:
d.putValue(p, 'utf8', elided=elided)
d.putValue(p, 'utf8', length=length)
elif displayFormat == DisplayFormat.SeparateUtf8String:
d.putValue(p, 'utf8', elided=elided)
d.putValue(p, 'utf8', length=length)
d.putDisplay('utf8:separate', d.encodeByteArray(value, limit=100000))
if d.isExpanded():
d.putArrayData(data, size, d.charType())
d.putArrayData(data, length, d.charType())
#def qdump__QArrayData(d, value):
# data, size, alloc = d.qArrayDataHelper(value.address())
# d.check(alloc == 0 or (0 <= size and size <= alloc and alloc <= 100000000))
# d.putValue(d.readMemory(data, size), 'latin1')
# data, length, alloc = d.qArrayDataHelper(value.address())
# d.check(alloc == 0 or (0 <= length and length <= alloc and alloc <= 100000000))
# d.putValue(d.readMemory(data, length), 'latin1')
# d.putPlainChildren(value)
@@ -81,10 +81,10 @@ def qdump__QBitArray(d, value):
else:
data, basize, _ = d.qArrayData(value['d'])
unused = d.extractByte(data) if data else 0
size = basize * 8 - unused
d.putItemCount(size)
length = basize * 8 - unused
d.putItemCount(length)
if d.isExpanded():
with Children(d, size, maxNumChild=10000):
with Children(d, length, maxNumChild=10000):
for i in d.childRange():
q = data + 1 + int(i / 8)
with SubItem(d, i):
@@ -1621,17 +1621,17 @@ def qdumpHelper_QSet45(d, value):
ptrSize = d.ptrSize()
dptr = d.extractPointer(value)
(fakeNext, buckets, ref, size, nodeSize, userNumBits, numBits, numBuckets) = \
(fakeNext, buckets, ref, length, nodeSize, userNumBits, numBits, numBuckets) = \
d.split('ppiiihhi', dptr)
d.check(0 <= size and size <= 100 * 1000 * 1000)
d.check(0 <= length and length <= 100 * 1000 * 1000)
d.check(-1 <= ref and ref < 100000)
d.putItemCount(size)
d.putItemCount(length)
if d.isExpanded():
keyType = value.type[0]
isShort = d.qtVersion() < 0x050000 and keyType.name == 'int'
with Children(d, size, childType=keyType):
with Children(d, length, childType=keyType):
node = hashDataFirstNode()
for i in d.childRange():
if isShort:
@@ -1714,15 +1714,15 @@ def qdump__QStack(d, value):
def qdump__QPolygonF(d, value):
data, size = d.vectorData(value)
d.putItemCount(size)
d.putPlotData(data, size, d.createType('@QPointF'))
data, length = d.vectorData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType('@QPointF'))
def qdump__QPolygon(d, value):
data, size = d.vectorData(value)
d.putItemCount(size)
d.putPlotData(data, size, d.createType('@QPoint'))
data, length = d.vectorData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType('@QPoint'))
def qdump__QGraphicsPolygonItem(d, value):
@@ -1741,14 +1741,14 @@ def qdump__QGraphicsPolygonItem(d, value):
offset = 328 if d.isMsvcTarget() else 320
else:
offset = 308
data, size = d.vectorData(dptr + offset)
d.putItemCount(size)
d.putPlotData(data, size, d.createType('@QPointF'))
data, length = d.vectorData(dptr + offset)
d.putItemCount(length)
d.putPlotData(data, length, d.createType('@QPointF'))
def qedit__QString(d, value, data):
d.call('void', value, 'resize', str(len(data)))
(base, size, alloc) = d.stringData(value)
(base, length, alloc) = d.stringData(value)
d.setValues(base, 'short', [ord(c) for c in data])
@@ -1758,14 +1758,14 @@ def qform__QString():
def qdump__QString(d, value):
d.putStringValue(value)
data, size, _ = d.stringData(value)
data, length, _ = d.stringData(value)
displayFormat = d.currentItemFormat()
if displayFormat == DisplayFormat.Separate:
d.putDisplay('utf16:separate', d.encodeString(value, limit=100000))
if (size > 0):
if (length > 0):
d.putExpandable()
if d.isExpanded():
d.putArrayData(data, size, d.createType('@QChar'))
d.putArrayData(data, length, d.createType('@QChar'))
def qdump__QSettingsKey(d, value):
@@ -1774,8 +1774,8 @@ def qdump__QSettingsKey(d, value):
def qdump__QStaticStringData(d, value):
size = value.type[0]
(ref, size, alloc, pad, offset, data) = value.split('iii@p%ss' % (2 * size))
length = value.type[0]
(ref, length, alloc, pad, offset, data) = value.split('iii@p%ss' % (2 * length))
d.putValue(d.hexencode(data), 'utf16')
d.putPlainChildren(value)
@@ -1788,28 +1788,28 @@ def qdump__QTypedArrayData(d, value):
def qdump__QStringData(d, value):
(ref, size, alloc, pad, offset) = value.split('III@p')
elided, shown = d.computeLimit(size, d.displayStringLimit)
(ref, length, alloc, pad, offset) = value.split('III@p')
shown = d.computeLimit(length, d.displayStringLimit)
data = d.readMemory(value.address() + offset, shown * 2)
d.putValue(data, 'utf16', elided=elided)
d.putValue(data, 'utf16', length=length)
d.putPlainChildren(value)
def qdump__QAnyStringView(d, value):
data, size = value.split('pp')
data, length = value.split('pp')
bits = d.ptrSize() * 8 - 2
tag = size >> bits
size = size & (2**bits - 1)
elided, shown = d.computeLimit(size, d.displayStringLimit)
tag = length >> bits
length = length & (2**bits - 1)
shown = d.computeLimit(length, d.displayStringLimit)
if tag == 0:
mem = d.readMemory(data, shown)
d.putValue(mem, 'utf8', elided=elided)
d.putValue(mem, 'utf8', length=length)
elif tag == 1:
mem = d.readMemory(data, shown)
d.putValue(mem, 'latin1', elided=elided)
d.putValue(mem, 'latin1', length=length)
elif tag == 2:
mem = d.readMemory(data, shown * 2)
d.putValue(mem, 'utf16', elided=elided)
d.putValue(mem, 'utf16', length=length)
else:
d.putSpecialValue('empty')
d.putPlainChildren(value)
@@ -1825,16 +1825,15 @@ def qdump__QStringView(d, value):
if idata == 0:
d.putValue('(null)')
return
size = value['m_size']
isize = size.integer()
elided, shown = d.computeLimit(isize, d.displayStringLimit)
length = value['m_size'].integer()
shown = d.computeLimit(length, d.displayStringLimit)
mem = d.readMemory(idata, shown * 2)
d.putValue(mem, 'utf16', elided=elided)
d.putValue(mem, 'utf16', length=length)
if d.currentItemFormat() == DisplayFormat.Separate:
d.putDisplay('utf16:separate', mem)
d.putExpandable()
if d.isExpanded():
d.putArrayData(idata, isize, d.createType('char16_t'))
d.putArrayData(idata, length, d.createType('char16_t'))
def qdump__QHashedString(d, value):
@@ -1848,12 +1847,12 @@ def qdump__QQmlRefCount(d, value):
def qdump__QStringRef(d, value):
(stringptr, pos, size) = value.split('pii')
(stringptr, pos, length) = value.split('pii')
if stringptr == 0:
d.putValue('(null)')
return
data, ssize, alloc = d.stringData(d.createValue(stringptr, '@QString'))
d.putValue(d.readMemory(data + 2 * pos, 2 * size), 'utf16')
d.putValue(d.readMemory(data + 2 * pos, 2 * length), 'utf16')
d.putPlainChildren(value)
@@ -1937,7 +1936,7 @@ def qdump__QUrl(d, value):
userNameEnc = d.encodeString(userName)
hostEnc = d.encodeString(host)
elided, pathEnc = d.encodeStringHelper(path, d.displayStringLimit)
length, pathEnc = d.encodeStringHelper(path, d.displayStringLimit)
url = d.encodeString(scheme)
url += '3a002f002f00' # '://'
if len(userNameEnc):
@@ -1946,7 +1945,7 @@ def qdump__QUrl(d, value):
if port >= 0:
url += '3a00' + ''.join(['%02x00' % ord(c) for c in str(port)])
url += pathEnc
d.putValue(url, 'utf16', elided=elided)
d.putValue(url, 'utf16', length=length)
displayFormat = d.currentItemFormat()
if displayFormat == DisplayFormat.Separate:
@@ -2162,7 +2161,7 @@ def qdumpHelper__QVariant6(d, value):
qdumpHelper_QVariant_0(d, value)
return
revision, alignment, size, flags, variantType, metaObjectPtr, name = \
revision, alignment, length, flags, variantType, metaObjectPtr, name = \
d.split('HHIIIpp', metaTypeInterface)
# Well-known simple type.
@@ -2241,7 +2240,7 @@ def qdumpHelper__QVariant45(d, value):
base1 = d.extractPointer(value)
#DumperBase.warn('BASE 1: %s %s' % (base1, innert))
base = d.extractPointer(base1)
#DumperBase.warn('SIZE 1: %s' % size)
#DumperBase.warn('SIZE 1: %s' % length)
val = d.createValue(base, innerType)
else:
#DumperBase.warn('DIRECT ITEM 1: %s' % innerType)
@@ -2268,7 +2267,7 @@ def qdumpHelper__QVariant45(d, value):
d.putSpecialValue('notcallable')
return None
ptr = p.pointer()
(elided, blob) = d.encodeCArray(ptr, 1, 100)
(_, blob) = d.encodeCArray(ptr, 1, 100)
innerType = d.hexdecode(blob)
# Prefer namespaced version.
@@ -2303,34 +2302,34 @@ def qform__QVector():
def qdump__QVector(d, value):
if d.qtVersion() >= 0x060000:
data, size = d.listData(value)
d.putItemCount(size)
d.putPlotData(data, size, d.createType(value.type.ltarget[0]))
data, length = d.listData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType(value.type.ltarget[0]))
# g++ 9.3 does not add the template parameter list to the debug info.
# Fake it for the common case:
if value.type.name == d.qtNamespace() + "QVector":
d.putBetterType(value.type.name + '<' + value.type.ltarget[0].name + '>')
else:
data, size = d.vectorData(value)
d.putItemCount(size)
d.putPlotData(data, size, d.createType(value.type[0]))
data, length = d.vectorData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType(value.type[0]))
if False:
def qdump__QObjectConnectionList(d, value):
data, size = d.vectorData(value)
d.putItemCount(size)
d.putPlotData(data, size, d.createType('@QObjectPrivate::ConnectionList'))
data, length = d.vectorData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType('@QObjectPrivate::ConnectionList'))
def qdump__QVarLengthArray(d, value):
if d.qtVersion() >= 0x060000:
cap, size, data = value.split('QQp')
cap, length, data = value.split('QQp')
else:
cap, size, data = value.split('iip')
d.check(0 <= size)
d.putItemCount(size)
d.putPlotData(data, size, value.type[0])
cap, length, data = value.split('iip')
d.check(0 <= length)
d.putItemCount(length)
d.putPlotData(data, length, value.type[0])
def qdump__QSharedPointer(d, value):
@@ -2401,20 +2400,20 @@ def qdump__QXmlAttributes(d, value):
def qdump__QXmlStreamStringRef(d, value):
s = value['m_string']
(data, size, alloc) = d.stringData(s)
(data, length, alloc) = d.stringData(s)
data += 2 * int(value['m_position'])
size = int(value['m_size'])
s = d.readMemory(data, 2 * size)
length = int(value['m_size'])
s = d.readMemory(data, 2 * length)
d.putValue(s, 'utf16')
d.putPlainChildren(value)
def qdump__QXmlStreamAttribute(d, value):
s = value['m_name']['m_string']
(data, size, alloc) = d.stringData(s)
(data, length, alloc) = d.stringData(s)
data += 2 * int(value['m_name']['m_position'])
size = int(value['m_name']['m_size'])
s = d.readMemory(data, 2 * size)
length = int(value['m_name']['m_size'])
s = d.readMemory(data, 2 * length)
d.putValue(s, 'utf16')
d.putPlainChildren(value)
@@ -2505,8 +2504,8 @@ def qdump__QV4__ExecutionContext(d, value):
def qdump__QQmlSourceLocation(d, value):
(sourceFile, line, col) = value.split('pHH')
(data, size, alloc) = d.stringData(value)
d.putValue(d.readMemory(data, 2 * size), 'utf16')
(data, length, alloc) = d.stringData(value)
d.putValue(d.readMemory(data, 2 * length), 'utf16')
d.putField('valuesuffix', ':%s:%s' % (line, col))
d.putPlainChildren(value)
@@ -3345,9 +3344,7 @@ def qdump__QJsonValue(d, value):
return
if t == 3:
d.putType('QJsonValue (String)')
string = value.split('{@QString}')[0]
elided, base = d.encodeString(string, d.displayStringLimit)
d.putValue(base, 'utf16', elided=elided)
d.putStringValue(value.split('{@QString}')[0])
return
if t == 4:
d.putType('QJsonValue (Array)')
@@ -3472,9 +3469,9 @@ def qdumpHelper_QCbor_string(d, container_ptr, element_index, is_bytes):
bytedata_len = d.extractInt(bytedata)
bytedata_data = bytedata + 4 # sizeof(QtCbor::ByteData) header part
elided, shown = d.computeLimit(bytedata_len, d.displayStringLimit)
shown = d.computeLimit(bytedata_len, d.displayStringLimit)
res = d.readMemory(bytedata_data, shown)
d.putValue(res, enc, elided=elided)
d.putValue(res, enc, length=bytedata_len)
def qdumpHelper_QCborArray_valueAt(d, container_ptr, elements_data_ptr, idx, bytedata, is_cbor):

View File

@@ -697,6 +697,36 @@ def qdump__std__pair(d, value):
d.putValue('(%s, %s)' % (key, value))
def qdumpHelper_get_tuple_elements(d, tuple, value_typename, value_member):
"""
Helper method that returns the elements of a tuple.
"""
elems = []
other_members = []
for member in tuple.members(True):
if not member.type.templateArguments():
continue
if member.type.name.startswith(value_typename):
elems.append(member[value_member])
else:
other_members.append(member)
for member in other_members:
sub_elems = qdumpHelper_get_tuple_elements(d, member, value_typename, value_member)
elems = elems + sub_elems
return elems
def qdump__std__tuple(d, value):
if d.isMsvcTarget():
elems = qdumpHelper_get_tuple_elements(d, value, "std::_Tuple_val", "_Val")
else:
elems = qdumpHelper_get_tuple_elements(d, value, "std::_Head_base", "_M_head_impl")
d.putItemCount(len(elems))
with Children(d):
for elem in elems:
d.putSubItem(0, elem)
def qform__std__unordered_map():
return [DisplayFormat.CompactMap]

View File

@@ -0,0 +1,86 @@
QtcLibrary {
name: "qtkeychain"
property bool useWinCredentialsStore: qbs.targetOS.contains("windows")
Depends { name: "cpp" }
Depends { name: "Qt.core" }
Depends { name: "Qt.dbus"; condition: qbs.targetOS.contains("linux") }
Depends { name: "libsecret-1"; required: false }
cpp.defines: base.concat(["QTKEYCHAIN_LIBRARY"])
Properties {
condition: useWinCredentialsStore
cpp.defines: outer.concat(["USE_CREDENTIAL_STORE=1"])
cpp.dynamicLibraries: ["advapi32"]
}
Properties {
condition: qbs.targetOS.contains("windows") && !useWinCredentialsStore
cpp.dynamicLibraries: ["crypt32"]
}
Properties {
condition: qbs.targetOS.contains("macos")
cpp.frameworks: [ "Foundation", "Security" ]
}
files: [
"keychain.cpp",
"keychain.h",
"keychain_p.h",
"qkeychain_export.h",
]
Group {
name: "qtkeychain Windows files"
condition: qbs.targetOS.contains("windows")
files: [
"keychain_win.cpp",
"plaintextstore_p.h",
]
Group {
name: "qtkeychain Windows no credentials store"
condition: !product.useWinCredentialsStore
files: [ "plaintextstore.cpp" ]
}
}
Group {
name: "qtkeychain macOS files"
condition: qbs.targetOS.contains("macos")
files: [ "keychain_apple.mm" ]
}
Group {
name: "qtkeychain Linux files"
condition: qbs.targetOS.contains("linux")
Group {
name: "qtkeychain libsecret support"
condition: "libsecret-1".present
cpp.defines: outer.concat(["HAVE_LIBSECRET=1"])
}
Group {
name: "dbus sources"
fileTags: "qt.dbus.interface"
files: ["org.kde.KWallet.xml"]
}
Group {
name: "qtkeychain dbus support"
cpp.defines: outer.concat(["KEYCHAIN_DBUS=1"])
files: [
"gnomekeyring.cpp",
"gnomekeyring_p.h",
"keychain_unix.cpp",
"libsecret.cpp",
"libsecret_p.h",
"plaintextstore.cpp",
"plaintextstore_p.h",
]
}
}
}

View File

@@ -363,12 +363,11 @@ void TypePrettyPrinter::visit(Function *type)
{
bool showTemplateParameters = _overview->showTemplateParameters;
QStringList nameParts = _name.split("::");
int i = nameParts.length() - 1;
Scope *s = type->enclosingScope();
if (s && s->asTemplate())
s = s->enclosingScope();
for (; s && i >= 0; s = s->enclosingScope()) {
for (int i = nameParts.length() - 1; s && i >= 0; s = s->enclosingScope()) {
if (s->asClass())
showTemplateParameters = true;
@@ -433,7 +432,8 @@ void TypePrettyPrinter::visit(Function *type)
}
if (_overview->showEnclosingTemplate) {
for (Scope *s = type->enclosingScope(); s && i >= 0; s = s->enclosingScope()) {
for (auto [s, i] = std::tuple{type->enclosingScope(), nameParts.length() - 1}; s && i >= 0;
s = s->enclosingScope()) {
if (Template *templ = s->asTemplate()) {
QString templateScope = "template<";
const int paramCount = templ->templateParameterCount();

View File

@@ -26,6 +26,7 @@ Project {
"utils/utils.qbs",
"3rdparty/libptyqt/ptyqt.qbs",
"3rdparty/libvterm/vterm.qbs",
"3rdparty/qtkeychain/qtkeychain.qbs",
"3rdparty/syntax-highlighting/syntax-highlighting.qbs",
"3rdparty/winpty/winpty.qbs",
"3rdparty/yaml-cpp/yaml-cpp.qbs",

View File

@@ -3,7 +3,6 @@ add_qtc_library(Nanotrace
SOURCES
nanotraceglobals.h
nanotrace.cpp nanotrace.h
nanotracehr.cpp nanotracehr.h
PUBLIC_DEPENDS Qt::Core Qt::Gui
PROPERTIES
CXX_VISIBILITY_PRESET default
@@ -16,3 +15,9 @@ extend_qtc_library(Nanotrace
CONDITION DESIGN_STUDIO_USE_NANOTRACE
PUBLIC_DEFINES NANOTRACE_DESIGNSTUDIO_ENABLED
)
option(NANOTRACEHR_ENABLED "Enables collecting high resolution performance data" OFF)
extend_qtc_library(Nanotrace
SOURCES
nanotracehr.cpp nanotracehr.h
)

View File

@@ -34,7 +34,7 @@ enum class Tracing { IsDisabled, IsEnabled };
constexpr Tracing tracingStatus()
{
#ifdef NANOTRACE_ENABLED
#ifdef NANOTRACEHR_ENABLED
return Tracing::IsEnabled;
#else
return Tracing::IsDisabled;
@@ -1569,7 +1569,7 @@ template<typename Category, typename... Arguments>
Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...)
-> Tracer<Category, typename Category::IsActive>;
#ifdef NANOTRACE_ENABLED
#ifdef NANOTRACEHR_ENABLED
class GlobalTracer
{
public:

View File

@@ -317,6 +317,16 @@ void ModelManagerInterface::setDefaultProject(const ModelManagerInterface::Proje
});
}
void ModelManagerInterface::cancelAllThreads()
{
m_cppQmlTypesUpdater.cancel();
// Don't execute the scheduled updates for the old session anymore
m_updateCppQmlTypesTimer->stop();
m_asyncResetTimer->stop();
QMutexLocker locker(&m_futuresMutex);
m_futureSynchronizer.cancelAllFutures();
}
Snapshot ModelManagerInterface::snapshot() const
{
return m_syncedData.readLocked()->m_validSnapshot;

View File

@@ -238,7 +238,7 @@ protected:
void updateImportPaths();
void loadQmlTypeDescriptionsInternal(const QString &path);
void setDefaultProject(const ProjectInfo &pInfo, ProjectExplorer::Project *p);
void cancelAllThreads();
private:
void joinAllThreads(bool cancelOnWait = false);
void iterateQrcFiles(ProjectExplorer::Project *project,

View File

@@ -19,7 +19,20 @@ void NetworkQuery::start()
emit done(DoneResult::Error);
return;
}
m_reply.reset(m_manager->get(m_request));
switch (m_operation) {
case NetworkOperation::Get:
m_reply.reset(m_manager->get(m_request));
break;
case NetworkOperation::Put:
m_reply.reset(m_manager->put(m_request, m_writeData));
break;
case NetworkOperation::Post:
m_reply.reset(m_manager->post(m_request, m_writeData));
break;
case NetworkOperation::Delete:
m_reply.reset(m_manager->deleteResource(m_request));
break;
}
connect(m_reply.get(), &QNetworkReply::finished, this, [this] {
disconnect(m_reply.get(), &QNetworkReply::finished, this, nullptr);
emit done(toDoneResult(m_reply->error() == QNetworkReply::NoError));

View File

@@ -22,6 +22,8 @@ namespace Tasking {
// is independent on Qt::Network.
// Possibly, it could be placed inside Qt::Network library, as a wrapper around QNetworkReply.
enum class NetworkOperation { Get, Put, Post, Delete };
class TASKING_EXPORT NetworkQuery final : public QObject
{
Q_OBJECT
@@ -29,6 +31,8 @@ class TASKING_EXPORT NetworkQuery final : public QObject
public:
~NetworkQuery();
void setRequest(const QNetworkRequest &request) { m_request = request; }
void setOperation(NetworkOperation operation) { m_operation = operation; }
void setWriteData(const QByteArray &data) { m_writeData = data; }
void setNetworkAccessManager(QNetworkAccessManager *manager) { m_manager = manager; }
QNetworkReply *reply() const { return m_reply.get(); }
void start();
@@ -39,6 +43,8 @@ signals:
private:
QNetworkRequest m_request;
NetworkOperation m_operation = NetworkOperation::Get;
QByteArray m_writeData; // Used by Put and Post
QNetworkAccessManager *m_manager = nullptr;
std::unique_ptr<QNetworkReply> m_reply;
};

View File

@@ -43,8 +43,8 @@ add_qtc_library(Utils
elidinglabel.cpp elidinglabel.h
environment.cpp environment.h
environmentdialog.cpp environmentdialog.h
environmentfwd.h
environmentmodel.cpp environmentmodel.h
environmentfwd.h
execmenu.cpp execmenu.h
expected.h
externalterminalprocessimpl.cpp externalterminalprocessimpl.h
@@ -103,12 +103,7 @@ add_qtc_library(Utils
namevaluedictionary.cpp namevaluedictionary.h
namevaluedictionary.cpp namevaluedictionary.h
namevalueitem.cpp namevalueitem.h
namevalueitem.cpp namevalueitem.h
namevaluemodel.cpp namevaluemodel.h
namevaluemodel.cpp namevaluemodel.h
namevaluesdialog.cpp namevaluesdialog.h
namevaluesdialog.cpp namevaluesdialog.h
namevaluevalidator.cpp namevaluevalidator.h
namevaluevalidator.cpp namevaluevalidator.h
navigationtreeview.cpp navigationtreeview.h
networkaccessmanager.cpp networkaccessmanager.h

View File

@@ -7,6 +7,7 @@
#include "checkablemessagebox.h"
#include "environment.h"
#include "fancylineedit.h"
#include "guard.h"
#include "iconbutton.h"
#include "layoutbuilder.h"
#include "passworddialog.h"
@@ -1181,13 +1182,12 @@ void StringAspect::addToLayout(LayoutItem &parent)
connect(lineEditDisplay, &FancyLineEdit::validChanged, this, &StringAspect::validChanged);
bufferToGui();
if (isAutoApply() && d->m_autoApplyOnEditingFinished) {
connect(lineEditDisplay,
&FancyLineEdit::editingFinished,
this,
[this, lineEditDisplay] {
d->undoable.set(undoStack(), lineEditDisplay->text());
handleGuiChanged();
});
connect(lineEditDisplay, &FancyLineEdit::editingFinished, this, [this, lineEditDisplay] {
if (lineEditDisplay->text() != d->undoable.get()) {
d->undoable.set(undoStack(), lineEditDisplay->text());
handleGuiChanged();
}
});
} else {
connect(lineEditDisplay, &QLineEdit::textChanged, this, [this, lineEditDisplay] {
d->undoable.set(undoStack(), lineEditDisplay->text());
@@ -1386,6 +1386,8 @@ public:
bool m_autoApplyOnEditingFinished = false;
bool m_allowPathFromDevice = true;
bool m_validatePlaceHolder = false;
Guard m_editFinishedGuard;
};
FilePathAspect::FilePathAspect(AspectContainer *container)
@@ -1559,8 +1561,12 @@ void FilePathAspect::addToLayout(Layouting::LayoutItem &parent)
connect(d->m_pathChooserDisplay, &PathChooser::validChanged, this, &FilePathAspect::validChanged);
bufferToGui();
if (isAutoApply() && d->m_autoApplyOnEditingFinished) {
connect(d->m_pathChooserDisplay, &PathChooser::editingFinished,
this, &FilePathAspect::handleGuiChanged);
connect(d->m_pathChooserDisplay, &PathChooser::editingFinished, this, [this] {
if (d->m_editFinishedGuard.isLocked())
return;
GuardLocker lk(d->m_editFinishedGuard);
handleGuiChanged();
});
connect(d->m_pathChooserDisplay, &PathChooser::browsingFinished,
this, &FilePathAspect::handleGuiChanged);
} else {

View File

@@ -878,52 +878,73 @@ QByteArray DesktopDeviceFileAccess::fileId(const FilePath &filePath) const
// UnixDeviceAccess
static Utils::unexpected<QString> make_unexpected_disconnected()
{
return make_unexpected(Tr::tr("Device is not connected"));
}
UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
bool UnixDeviceFileAccess::runInShellSuccess(const CommandLine &cmdLine,
const QByteArray &stdInData) const
{
if (disconnected())
return false;
return runInShell(cmdLine, stdInData).exitCode == 0;
}
bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-x", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-r", path, "-a", "-f", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-w", path, "-a", "-f", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-r", path, "-a", "-d", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-w", path, "-a", "-d", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-f", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
{
if (disconnected())
return false;
if (filePath.isRootPath())
return true;
@@ -933,12 +954,16 @@ bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-h", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
{
if (disconnected())
return false;
const QStringList args = statArgs(filePath, "%h", "%l");
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
return result.stdOut.toLongLong() > 1;
@@ -946,29 +971,39 @@ bool UnixDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"touch", {path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"mkdir", {"-p", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::exists(const FilePath &filePath) const
{
if (disconnected())
return false;
const QString path = filePath.path();
return runInShellSuccess({"test", {"-e", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const
{
if (disconnected())
return false;
return runInShellSuccess({"rm", {filePath.path()}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
{
if (disconnected())
return false;
QTC_ASSERT(filePath.path().startsWith('/'), return false);
const QString path = filePath.cleanPath().path();
@@ -989,6 +1024,8 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *
expected_str<void> UnixDeviceFileAccess::copyFile(const FilePath &filePath,
const FilePath &target) const
{
if (disconnected())
return make_unexpected_disconnected();
const RunResult result = runInShell(
{"cp", {filePath.path(), target.path()}, OsType::OsTypeLinux});
@@ -1003,11 +1040,17 @@ expected_str<void> UnixDeviceFileAccess::copyFile(const FilePath &filePath,
bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
{
if (disconnected())
return false;
return runInShellSuccess({"mv", {filePath.path(), target.path()}, OsType::OsTypeLinux});
}
FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
{
if (disconnected())
return {};
const RunResult result = runInShell(
{"readlink", {"-n", "-e", filePath.path()}, OsType::OsTypeLinux});
const QString out = QString::fromUtf8(result.stdOut);
@@ -1018,6 +1061,9 @@ expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &file
qint64 limit,
qint64 offset) const
{
if (disconnected())
return make_unexpected_disconnected();
QStringList args = {"if=" + filePath.path()};
if (limit > 0 || offset > 0) {
const qint64 gcd = std::gcd(limit, offset);
@@ -1045,6 +1091,9 @@ expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &fil
const QByteArray &data,
qint64 offset) const
{
if (disconnected())
return make_unexpected_disconnected();
QStringList args = {"of=" + filePath.path()};
if (offset != 0) {
args.append("bs=1");
@@ -1061,6 +1110,9 @@ expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &fil
expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &filePath)
{
if (disconnected())
return make_unexpected_disconnected();
if (!m_hasMkTemp.has_value())
m_hasMkTemp = runInShellSuccess({"which", {"mktemp"}, OsType::OsTypeLinux});
@@ -1120,6 +1172,9 @@ expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &file
QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
{
if (disconnected())
return {};
const RunResult result = runInShell(
{"stat", {"-L", "-c", "%Y", filePath.path()}, OsType::OsTypeLinux});
qint64 secs = result.stdOut.toLongLong();
@@ -1131,6 +1186,9 @@ QStringList UnixDeviceFileAccess::statArgs(const FilePath &filePath,
const QString &linuxFormat,
const QString &macFormat) const
{
if (disconnected())
return {};
return (filePath.osType() == OsTypeMac ? QStringList{"-f", macFormat}
: QStringList{"-c", linuxFormat})
<< "-L" << filePath.path();
@@ -1138,6 +1196,9 @@ QStringList UnixDeviceFileAccess::statArgs(const FilePath &filePath,
QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const
{
if (disconnected())
return {};
QStringList args = statArgs(filePath, "%a", "%p");
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
@@ -1161,6 +1222,9 @@ QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) c
bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const
{
if (disconnected())
return false;
const int flags = int(perms);
return runInShellSuccess(
{"chmod", {QString::number(flags, 16), filePath.path()}, OsType::OsTypeLinux});
@@ -1168,6 +1232,9 @@ bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permi
qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
{
if (disconnected())
return -1;
const QStringList args = statArgs(filePath, "%s", "%z");
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
return result.stdOut.toLongLong();
@@ -1175,12 +1242,18 @@ qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
{
if (disconnected())
return -1;
const RunResult result = runInShell({"df", {"-k", filePath.path()}, OsType::OsTypeLinux});
return FileUtils::bytesAvailableFromDFOutput(result.stdOut);
}
QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
{
if (disconnected())
return {};
const QStringList args = statArgs(filePath, "%D:%i", "%d:%i");
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
@@ -1192,6 +1265,9 @@ QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const
{
if (disconnected())
return {};
if (filePath.path() == "/") // TODO: Add FilePath::isRoot()
{
const FilePathInfo r{4096,
@@ -1218,6 +1294,9 @@ bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath,
const FileFilter &filter,
const FilePath::IterateDirCallback &callBack) const
{
if (disconnected())
return false;
QTC_CHECK(filePath.isAbsolutePath());
CommandLine cmdLine{"find", filter.asFindArguments(filePath.path()), OsType::OsTypeLinux};
@@ -1290,6 +1369,9 @@ void UnixDeviceFileAccess::findUsingLs(const QString &current,
QStringList *found,
const QString &start) const
{
if (disconnected())
return;
const RunResult result = runInShell(
{"ls", {"-1", "-a", "-p", "--", current}, OsType::OsTypeLinux});
const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
@@ -1349,6 +1431,9 @@ void UnixDeviceFileAccess::iterateDirectory(const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const
{
if (disconnected())
return;
// We try to use 'find' first, because that can filter better directly.
// Unfortunately, it's not installed on all devices by default.
if (m_tryUseFind) {
@@ -1366,9 +1451,17 @@ void UnixDeviceFileAccess::iterateDirectory(const FilePath &filePath,
Environment UnixDeviceFileAccess::deviceEnvironment() const
{
if (disconnected())
return {};
const RunResult result = runInShell({"env", {}, OsType::OsTypeLinux});
const QString out = QString::fromUtf8(result.stdOut);
return Environment(out.split('\n', Qt::SkipEmptyParts), OsTypeLinux);
}
bool UnixDeviceFileAccess::disconnected() const
{
return false;
}
} // namespace Utils

View File

@@ -188,6 +188,8 @@ protected:
QStringList *found,
const QString &start) const;
virtual bool disconnected() const;
private:
bool iterateWithFind(const FilePath &filePath,
const FileFilter &filter,

View File

@@ -50,7 +50,7 @@ Environment::Environment(const NameValueDictionary &dict)
m_changeItems.append(dict);
}
NameValueItems Environment::diff(const Environment &other, bool checkAppendPrepend) const
EnvironmentItems Environment::diff(const Environment &other, bool checkAppendPrepend) const
{
const NameValueDictionary &dict = resolved();
const NameValueDictionary &otherDict = other.resolved();
@@ -393,7 +393,7 @@ void Environment::unset(const QString &key)
addItem(Item{std::in_place_index_t<UnsetValue>(), key});
}
void Environment::modify(const NameValueItems &items)
void Environment::modify(const EnvironmentItems &items)
{
addItem(Item{std::in_place_index_t<Modify>(), items});
}
@@ -485,7 +485,7 @@ const NameValueDictionary &Environment::resolved() const
break;
}
case Modify: {
NameValueItems items = std::get<Modify>(item);
EnvironmentItems items = std::get<Modify>(item);
m_dict.modify(items);
break;
}

View File

@@ -37,7 +37,7 @@ public:
void set(const QString &key, const QString &value, bool enabled = true);
void setFallback(const QString &key, const QString &value);
void unset(const QString &key);
void modify(const NameValueItems &items);
void modify(const EnvironmentItems &items);
bool hasChanges() const;
@@ -79,7 +79,7 @@ public:
QStringList expandVariables(const QStringList &input) const;
NameValueDictionary toDictionary() const; // FIXME: avoid
NameValueItems diff(const Environment &other, bool checkAppendPrepend = false) const; // FIXME: avoid
EnvironmentItems diff(const Environment &other, bool checkAppendPrepend = false) const; // FIXME: avoid
struct Entry { QString key; QString value; bool enabled; };
using FindResult = std::optional<Entry>;
@@ -117,7 +117,7 @@ public:
QString, // UnsetValue (key)
std::tuple<QString, QString, PathSeparator>, // PrependOrSet (key, value, separator)
std::tuple<QString, QString, PathSeparator>, // AppendOrSet (key, value, separator)
NameValueItems, // Modify
EnvironmentItems, // Modify
std::monostate, // SetupEnglishOutput
FilePath // SetupSudoAskPass (file path of qtc-askpass or ssh-askpass)
>;

View File

@@ -7,19 +7,10 @@
namespace Utils {
class NameValueDictionary;
class NameValueItem;
using NameValueItems = QList<NameValueItem>;
class Environment;
using EnvironmentItem = NameValueItem;
using EnvironmentItems = NameValueItems;
class EnvironmentItem;
using EnvironmentItems = QList<EnvironmentItem>;
class PreprocessorMacroDictionary;
using PreprocessorMacroItem = NameValueItem;
using PreprocessorMacroItems = NameValueItems;
class NameValueModel;
class EnvironmentModel;
class NameValueDictionary;
} // namespace Utils

View File

@@ -1,20 +1,458 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "environmentmodel.h"
#include "algorithm.h"
#include "environment.h"
#include "hostosinfo.h"
#include "namevaluedictionary.h"
#include "namevalueitem.h"
#include "qtcassert.h"
#include "utilstr.h"
#include <QFileInfo>
#include <QFont>
#include <QGuiApplication>
#include <QPalette>
#include <QString>
namespace Utils {
Environment EnvironmentModel::baseEnvironment() const
namespace Internal {
class EnvironmentModelPrivate
{
return Environment(baseNameValueDictionary());
public:
void updateResultNameValueDictionary()
{
m_resultNameValueDictionary = m_baseNameValueDictionary;
m_resultNameValueDictionary.modify(m_items);
// Add removed variables again and mark them as "<UNSET>" so
// that the user can actually see those removals:
for (const EnvironmentItem &item : std::as_const(m_items)) {
if (item.operation == EnvironmentItem::Unset)
m_resultNameValueDictionary.set(item.name, Tr::tr("<UNSET>"));
}
}
int findInChanges(const QString &name) const
{
for (int i = 0; i < m_items.size(); ++i)
if (m_items.at(i).name.compare(name,
m_baseNameValueDictionary.nameCaseSensitivity()) == 0) {
return i;
}
return -1;
}
int findInResultInsertPosition(const QString &name) const
{
NameValueDictionary::const_iterator it;
int i = 0;
for (it = m_resultNameValueDictionary.constBegin();
it != m_resultNameValueDictionary.constEnd();
++it, ++i)
if (it.key() > DictKey(name, m_resultNameValueDictionary.nameCaseSensitivity()))
return i;
return m_resultNameValueDictionary.size();
}
int findInResult(const QString &name) const
{
NameValueDictionary::const_iterator it;
int i = 0;
for (it = m_resultNameValueDictionary.constBegin();
it != m_resultNameValueDictionary.constEnd();
++it, ++i)
if (m_resultNameValueDictionary.key(it)
.compare(name, m_resultNameValueDictionary.nameCaseSensitivity()) == 0) {
return i;
}
return -1;
}
NameValueDictionary m_baseNameValueDictionary;
NameValueDictionary m_resultNameValueDictionary;
EnvironmentItems m_items;
};
} // namespace Internal
EnvironmentModel::EnvironmentModel(QObject *parent)
: QAbstractTableModel(parent)
, d(std::make_unique<Internal::EnvironmentModelPrivate>())
{}
EnvironmentModel::~EnvironmentModel() = default;
QString EnvironmentModel::indexToVariable(const QModelIndex &index) const
{
const auto it = std::next(d->m_resultNameValueDictionary.constBegin(), index.row());
return d->m_resultNameValueDictionary.key(it);
}
void EnvironmentModel::setBaseEnvironment(const Environment &env)
{
setBaseNameValueDictionary(env.toDictionary());
const NameValueDictionary dictionary = env.toDictionary();
if (d->m_baseNameValueDictionary == dictionary)
return;
beginResetModel();
d->m_baseNameValueDictionary = dictionary;
d->updateResultNameValueDictionary();
endResetModel();
}
int EnvironmentModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return d->m_resultNameValueDictionary.size();
}
int EnvironmentModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 2;
}
bool EnvironmentModel::changes(const QString &name) const
{
return d->findInChanges(name) >= 0;
}
Environment EnvironmentModel::baseEnvironment() const
{
return Environment(d->m_baseNameValueDictionary);
}
QVariant EnvironmentModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
const auto resultIterator = std::next(d->m_resultNameValueDictionary.constBegin(), index.row());
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole:
if (index.column() == 0)
return d->m_resultNameValueDictionary.key(resultIterator);
if (index.column() == 1) {
// Do not return "<UNSET>" when editing a previously unset variable:
if (role == Qt::EditRole) {
int pos = d->findInChanges(indexToVariable(index));
if (pos != -1 && d->m_items.at(pos).operation == EnvironmentItem::Unset)
return QString();
}
QString value = d->m_resultNameValueDictionary.value(resultIterator);
if (role == Qt::ToolTipRole && value.length() > 80) {
if (currentEntryIsPathList(index)) {
// For path lists, display one entry per line without separator
const QChar sep = Utils::HostOsInfo::pathListSeparator();
value = value.replace(sep, '\n');
} else {
// Use html to enable text wrapping
value = value.toHtmlEscaped();
value.prepend(QLatin1String("<html><body>"));
value.append(QLatin1String("</body></html>"));
}
}
return value;
}
break;
case Qt::FontRole: {
QFont f;
f.setStrikeOut(!d->m_resultNameValueDictionary.isEnabled(resultIterator));
return f;
}
case Qt::ForegroundRole: {
const QPalette p = QGuiApplication::palette();
return p.color(changes(d->m_resultNameValueDictionary.key(resultIterator))
? QPalette::Link : QPalette::Text);
}
}
return QVariant();
}
Qt::ItemFlags EnvironmentModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index)
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
QVariant EnvironmentModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
return QVariant();
return section == 0 ? Tr::tr("Variable") : Tr::tr("Value");
}
/// *****************
/// Utility functions
/// *****************
QModelIndex EnvironmentModel::variableToIndex(const QString &name) const
{
int row = d->findInResult(name);
if (row == -1)
return QModelIndex();
return index(row, 0);
}
bool EnvironmentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::EditRole)
return false;
// ignore changes to already set values:
if (data(index, role) == value)
return true;
const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
int changesPos = d->findInChanges(oldName);
if (index.column() == 0) {
//fail if a variable with the same name already exists
const QString &newName = HostOsInfo::isWindowsHost() ? value.toString().toUpper()
: value.toString();
if (newName.isEmpty() || newName.contains('='))
return false;
// Does the new name exist already?
if (d->m_resultNameValueDictionary.hasKey(newName) || newName.isEmpty())
return false;
EnvironmentItem newVariable(newName, oldValue);
if (changesPos != -1)
resetVariable(oldName); // restore the original base variable again
QModelIndex newIndex = addVariable(newVariable); // add the new variable
emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
return true;
} else if (index.column() == 1) {
// We are changing an existing value:
const QString stringValue = value.toString();
if (changesPos != -1) {
const auto oldIt = d->m_baseNameValueDictionary.constFind(oldName);
const auto newIt = d->m_resultNameValueDictionary.constFind(oldName);
// We have already changed this value
if (oldIt != d->m_baseNameValueDictionary.constEnd()
&& stringValue == d->m_baseNameValueDictionary.value(oldIt)
&& d->m_baseNameValueDictionary.isEnabled(oldIt)
== d->m_resultNameValueDictionary.isEnabled(newIt)) {
// ... and now went back to the base value
d->m_items.removeAt(changesPos);
} else {
// ... and changed it again
d->m_items[changesPos].value = stringValue;
if (d->m_items[changesPos].operation == EnvironmentItem::Unset)
d->m_items[changesPos].operation = EnvironmentItem::SetEnabled;
}
} else {
// Add a new change item:
d->m_items.append(EnvironmentItem(oldName, stringValue));
}
d->updateResultNameValueDictionary();
emit dataChanged(index, index);
emit userChangesChanged();
return true;
}
return false;
}
QModelIndex EnvironmentModel::addVariable()
{
return addVariable(EnvironmentItem("NEWVAR", "VALUE"));
}
QModelIndex EnvironmentModel::addVariable(const EnvironmentItem &item)
{
// Return existing index if the name is already in the result set:
int pos = d->findInResult(item.name);
if (pos >= 0 && pos < d->m_resultNameValueDictionary.size())
return index(pos, 0, QModelIndex());
int insertPos = d->findInResultInsertPosition(item.name);
int changePos = d->findInChanges(item.name);
if (d->m_baseNameValueDictionary.hasKey(item.name)) {
// We previously unset this!
Q_ASSERT(changePos >= 0);
// Do not insert a line here as we listed the variable as <UNSET> before!
Q_ASSERT(d->m_items.at(changePos).name == item.name);
Q_ASSERT(d->m_items.at(changePos).operation == EnvironmentItem::Unset);
Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
d->m_items[changePos] = item;
emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
} else {
// We add something that is not in the base dictionary
// Insert a new line!
beginInsertRows(QModelIndex(), insertPos, insertPos);
Q_ASSERT(changePos < 0);
d->m_items.append(item);
d->updateResultNameValueDictionary();
endInsertRows();
}
emit userChangesChanged();
return index(insertPos, 0, QModelIndex());
}
void EnvironmentModel::resetVariable(const QString &name)
{
int rowInChanges = d->findInChanges(name);
if (rowInChanges < 0)
return;
int rowInResult = d->findInResult(name);
if (rowInResult < 0)
return;
if (d->m_baseNameValueDictionary.hasKey(name)) {
d->m_items.removeAt(rowInChanges);
d->updateResultNameValueDictionary();
emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
emit userChangesChanged();
} else {
// Remove the line completely!
beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
d->m_items.removeAt(rowInChanges);
d->updateResultNameValueDictionary();
endRemoveRows();
emit userChangesChanged();
}
}
void EnvironmentModel::unsetVariable(const QString &name)
{
// This does not change the number of rows as we will display a <UNSET>
// in place of the original variable!
int row = d->findInResult(name);
if (row < 0)
return;
// look in d->m_items for the variable
int pos = d->findInChanges(name);
if (pos != -1) {
d->m_items[pos].operation = EnvironmentItem::Unset;
d->m_items[pos].value.clear();
d->updateResultNameValueDictionary();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
return;
}
d->m_items.append(EnvironmentItem(name, QString(), EnvironmentItem::Unset));
d->updateResultNameValueDictionary();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
}
void EnvironmentModel::toggleVariable(const QModelIndex &idx)
{
const QString name = indexToVariable(idx);
const auto newIt = d->m_resultNameValueDictionary.constFind(name);
QTC_ASSERT(newIt != d->m_resultNameValueDictionary.constEnd(), return);
const auto op = d->m_resultNameValueDictionary.isEnabled(newIt)
? EnvironmentItem::SetDisabled : EnvironmentItem::SetEnabled;
const int changesPos = d->findInChanges(name);
if (changesPos != -1) {
const auto oldIt = d->m_baseNameValueDictionary.constFind(name);
if (oldIt == d->m_baseNameValueDictionary.constEnd()
|| oldIt.value().first != newIt.value().first) {
d->m_items[changesPos].operation = op;
} else {
d->m_items.removeAt(changesPos);
}
} else {
d->m_items.append({name, d->m_resultNameValueDictionary.value(newIt), op});
}
d->updateResultNameValueDictionary();
emit dataChanged(index(idx.row(), 0), index(idx.row(), 1));
emit userChangesChanged();
}
bool EnvironmentModel::isUnset(const QString &name)
{
const int pos = d->findInChanges(name);
return pos == -1 ? false : d->m_items.at(pos).operation == EnvironmentItem::Unset;
}
bool EnvironmentModel::isEnabled(const QString &name) const
{
return d->m_resultNameValueDictionary.isEnabled(d->m_resultNameValueDictionary.constFind(name));
}
bool EnvironmentModel::canReset(const QString &name)
{
return d->m_baseNameValueDictionary.hasKey(name);
}
EnvironmentItems EnvironmentModel::userChanges() const
{
return d->m_items;
}
void EnvironmentModel::setUserChanges(const EnvironmentItems &items)
{
EnvironmentItems filtered = Utils::filtered(items, [](const EnvironmentItem &i) {
return i.name != "export " && !i.name.contains('=');
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)
return;
beginResetModel();
d->m_items = filtered;
for (EnvironmentItem &item : d->m_items) {
QString &name = item.name;
name = name.trimmed();
if (name.startsWith("export "))
name = name.mid(7).trimmed();
if (d->m_baseNameValueDictionary.osType() == OsTypeWindows) {
// NameValueDictionary variable names are case-insensitive under windows, but we still
// want to preserve the case of pre-existing variables.
auto it = d->m_baseNameValueDictionary.constFind(name);
if (it != d->m_baseNameValueDictionary.constEnd())
name = d->m_baseNameValueDictionary.key(it);
}
}
d->updateResultNameValueDictionary();
endResetModel();
emit userChangesChanged();
}
bool EnvironmentModel::currentEntryIsPathList(const QModelIndex &current) const
{
if (!current.isValid())
return false;
// Look at the name first and check it against some well-known path variables. Extend as needed.
const QString varName = indexToVariable(current);
if (varName.compare("PATH", Utils::HostOsInfo::fileNameCaseSensitivity()) == 0)
return true;
if (Utils::HostOsInfo::isMacHost() && varName == "DYLD_LIBRARY_PATH")
return true;
if (Utils::HostOsInfo::isAnyUnixHost() && varName == "LD_LIBRARY_PATH")
return true;
if (varName == "PKG_CONFIG_DIR")
return true;
if (Utils::HostOsInfo::isWindowsHost()
&& QStringList{"INCLUDE", "LIB", "LIBPATH"}.contains(varName)) {
return true;
}
// Now check the value: If it's a list of strings separated by the platform's path separator
// and at least one of the strings is an existing directory, then that's enough proof for us.
QModelIndex valueIndex = current;
if (valueIndex.column() == 0)
valueIndex = valueIndex.siblingAtColumn(1);
const QStringList entries = data(valueIndex).toString()
.split(Utils::HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
if (entries.length() < 2)
return false;
return Utils::anyOf(entries, [](const QString &d) { return QFileInfo(d).isDir(); });
}
} // namespace Utils

View File

@@ -1,19 +1,64 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "utils_global.h"
#include "namevaluemodel.h"
#include "environmentfwd.h"
#include <QAbstractTableModel>
#include <memory>
namespace Utils {
class QTCREATOR_UTILS_EXPORT EnvironmentModel : public NameValueModel
namespace Internal { class EnvironmentModelPrivate; }
class QTCREATOR_UTILS_EXPORT EnvironmentModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit EnvironmentModel(QObject *parent = nullptr);
~EnvironmentModel() override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section,
Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
Environment baseEnvironment() const;
void setBaseEnvironment(const Environment &env);
QModelIndex addVariable();
QModelIndex addVariable(const EnvironmentItem &item);
void resetVariable(const QString &name);
void unsetVariable(const QString &name);
void toggleVariable(const QModelIndex &index);
bool isUnset(const QString &name);
bool isEnabled(const QString &name) const;
bool canReset(const QString &name);
QString indexToVariable(const QModelIndex &index) const;
QModelIndex variableToIndex(const QString &name) const;
bool changes(const QString &key) const;
EnvironmentItems userChanges() const;
void setUserChanges(const EnvironmentItems &items);
bool currentEntryIsPathList(const QModelIndex &current) const;
signals:
void userChangesChanged();
/// Hint to the view where it should make sense to focus on next
// This is a hack since there is no way for a model to suggest
// the next interesting place to focus on to the view.
void focusIndex(const QModelIndex &index);
private:
std::unique_ptr<Internal::EnvironmentModelPrivate> d;
};
} // namespace Utils

View File

@@ -2334,12 +2334,30 @@ QTCREATOR_UTILS_EXPORT bool operator!=(const FilePath &first, const FilePath &se
QTCREATOR_UTILS_EXPORT bool operator<(const FilePath &first, const FilePath &second)
{
const int cmp = first.pathView().compare(second.pathView(), first.caseSensitivity());
if (cmp != 0)
return cmp < 0;
if (first.host() != second.host())
return first.host() < second.host();
return first.scheme() < second.scheme();
const bool firstNeedsDevice = first.needsDevice();
const bool secondNeedsDevice = second.needsDevice();
// If either needs a device, we have to compare host and scheme first.
if (firstNeedsDevice || secondNeedsDevice) {
// Paths needing a device are "larger" than those not needing one.
if (firstNeedsDevice < secondNeedsDevice)
return true;
else if (firstNeedsDevice > secondNeedsDevice)
return false;
// First we sort by scheme ...
const int s = first.scheme().compare(second.scheme());
if (s != 0)
return s < 0;
// than by host ...
const int h = first.host().compare(second.host());
if (h != 0)
return h < 0;
}
const int p = first.pathView().compare(second.pathView(), first.caseSensitivity());
return p < 0;
}
QTCREATOR_UTILS_EXPORT bool operator<=(const FilePath &first, const FilePath &second)

View File

@@ -238,6 +238,11 @@ bool FileSaver::finalize()
}
TempFileSaver::TempFileSaver(const QString &templ)
{
initFromString(templ);
}
void TempFileSaver::initFromString(const QString &templ)
{
m_file.reset(new QTemporaryFile{});
auto tempFile = static_cast<QTemporaryFile *>(m_file.get());
@@ -253,6 +258,29 @@ TempFileSaver::TempFileSaver(const QString &templ)
m_filePath = FilePath::fromString(tempFile->fileName());
}
TempFileSaver::TempFileSaver(const FilePath &templ)
{
if (templ.isEmpty() || !templ.needsDevice()) {
initFromString(templ.path());
} else {
expected_str<FilePath> result = templ.createTempFile();
if (!result) {
m_errorString = Tr::tr("Cannot create temporary file %1: %2")
.arg(templ.toUserOutput(), result.error());
m_hasError = true;
return;
}
m_file.reset(new QFile(result->toFSPathString()));
if (!m_file->open(QIODevice::WriteOnly)) {
m_errorString = Tr::tr("Cannot create temporary file %1: %2")
.arg(result->toUserOutput(), m_file->errorString());
m_hasError = true;
}
m_filePath = *result;
}
}
TempFileSaver::~TempFileSaver()
{
m_file.reset();

View File

@@ -222,10 +222,14 @@ class QTCREATOR_UTILS_EXPORT TempFileSaver : public FileSaverBase
{
public:
explicit TempFileSaver(const QString &templ = QString());
explicit TempFileSaver(const FilePath &templ);
~TempFileSaver() override;
void setAutoRemove(bool on) { m_autoRemove = on; }
protected:
void initFromString(const QString &templ);
private:
bool m_autoRemove = true;
};

View File

@@ -97,34 +97,34 @@ int NameValueDictionary::size() const
return m_values.size();
}
void NameValueDictionary::modify(const NameValueItems &items)
void NameValueDictionary::modify(const EnvironmentItems &items)
{
NameValueDictionary resultKeyValueDictionary = *this;
for (const NameValueItem &item : items)
for (const EnvironmentItem &item : items)
item.apply(&resultKeyValueDictionary);
*this = resultKeyValueDictionary;
}
NameValueItems NameValueDictionary::diff(const NameValueDictionary &other, bool checkAppendPrepend) const
EnvironmentItems NameValueDictionary::diff(const NameValueDictionary &other, bool checkAppendPrepend) const
{
NameValueMap::const_iterator thisIt = constBegin();
NameValueMap::const_iterator otherIt = other.constBegin();
NameValueItems result;
EnvironmentItems result;
while (thisIt != constEnd() || otherIt != other.constEnd()) {
if (thisIt == constEnd()) {
result.append({other.key(otherIt), other.value(otherIt),
otherIt.value().second ? NameValueItem::SetEnabled : NameValueItem::SetDisabled});
otherIt.value().second ? EnvironmentItem::SetEnabled : EnvironmentItem::SetDisabled});
++otherIt;
} else if (otherIt == other.constEnd()) {
result.append(NameValueItem(key(thisIt), QString(), NameValueItem::Unset));
result.append(EnvironmentItem(key(thisIt), QString(), EnvironmentItem::Unset));
++thisIt;
} else if (thisIt.key() < otherIt.key()) {
result.append(NameValueItem(key(thisIt), QString(), NameValueItem::Unset));
result.append(EnvironmentItem(key(thisIt), QString(), EnvironmentItem::Unset));
++thisIt;
} else if (thisIt.key() > otherIt.key()) {
result.append({other.key(otherIt), otherIt.value().first,
otherIt.value().second ? NameValueItem::SetEnabled : NameValueItem::SetDisabled});
otherIt.value().second ? EnvironmentItem::SetEnabled : EnvironmentItem::SetDisabled});
++otherIt;
} else {
const QString &oldValue = thisIt.value().first;
@@ -137,16 +137,16 @@ NameValueItems NameValueDictionary::diff(const NameValueDictionary &other, bool
QString appended = newValue.right(newValue.size() - oldValue.size());
if (appended.startsWith(OsSpecificAspects::pathListSeparator(osType())))
appended.remove(0, 1);
result.append(NameValueItem(other.key(otherIt), appended, NameValueItem::Append));
result.append(EnvironmentItem(other.key(otherIt), appended, EnvironmentItem::Append));
} else if (checkAppendPrepend && newValue.endsWith(oldValue)
&& oldEnabled == newEnabled) {
QString prepended = newValue.left(newValue.size() - oldValue.size());
if (prepended.endsWith(OsSpecificAspects::pathListSeparator(osType())))
prepended.chop(1);
result.append(NameValueItem(other.key(otherIt), prepended, NameValueItem::Prepend));
result.append(EnvironmentItem(other.key(otherIt), prepended, EnvironmentItem::Prepend));
} else {
result.append({other.key(otherIt), newValue, newEnabled
? NameValueItem::SetEnabled : NameValueItem::SetDisabled});
? EnvironmentItem::SetEnabled : EnvironmentItem::SetDisabled});
}
}
++otherIt;

View File

@@ -48,9 +48,9 @@ public:
QString value(const QString &key) const;
void set(const QString &key, const QString &value, bool enabled = true);
void unset(const QString &key);
void modify(const NameValueItems &items);
void modify(const EnvironmentItems &items);
/// Return the KeyValueDictionary changes necessary to modify this into the other environment.
NameValueItems diff(const NameValueDictionary &other, bool checkAppendPrepend = false) const;
EnvironmentItems diff(const NameValueDictionary &other, bool checkAppendPrepend = false) const;
bool hasKey(const QString &key) const;
OsType osType() const;
Qt::CaseSensitivity nameCaseSensitivity() const;

View File

@@ -10,34 +10,34 @@
namespace Utils {
void NameValueItem::sort(NameValueItems *list)
void EnvironmentItem::sort(EnvironmentItems *list)
{
Utils::sort(*list, &NameValueItem::name);
Utils::sort(*list, &EnvironmentItem::name);
}
NameValueItems NameValueItem::fromStringList(const QStringList &list)
EnvironmentItems EnvironmentItem::fromStringList(const QStringList &list)
{
NameValueItems result;
EnvironmentItems result;
for (const QString &string : list) {
int pos = string.indexOf("+=");
if (pos != -1) {
result.append({string.left(pos), string.mid(pos + 2), NameValueItem::Append});
result.append({string.left(pos), string.mid(pos + 2), EnvironmentItem::Append});
continue;
}
pos = string.indexOf("=+");
if (pos != -1) {
result.append({string.left(pos), string.mid(pos + 2), NameValueItem::Prepend});
result.append({string.left(pos), string.mid(pos + 2), EnvironmentItem::Prepend});
continue;
}
pos = string.indexOf('=', 1);
if (pos == -1) {
result.append(NameValueItem(string, QString(), NameValueItem::Unset));
result.append(EnvironmentItem(string, QString(), EnvironmentItem::Unset));
continue;
}
const int hashPos = string.indexOf('#');
if (hashPos != -1 && hashPos < pos) {
result.append({string.mid(hashPos + 1, pos - hashPos - 1), string.mid(pos + 1),
NameValueItem::SetDisabled});
EnvironmentItem::SetDisabled});
} else {
result.append({string.left(pos), string.mid(pos + 1)});
}
@@ -45,49 +45,49 @@ NameValueItems NameValueItem::fromStringList(const QStringList &list)
return result;
}
QStringList NameValueItem::toStringList(const NameValueItems &list)
QStringList EnvironmentItem::toStringList(const EnvironmentItems &list)
{
return Utils::transform<QStringList>(list, [](const NameValueItem &item) {
return Utils::transform<QStringList>(list, [](const EnvironmentItem &item) {
switch (item.operation) {
case NameValueItem::Unset:
case EnvironmentItem::Unset:
return item.name;
case NameValueItem::Append:
case EnvironmentItem::Append:
return QString(item.name + "+=" + item.value);
case NameValueItem::Prepend:
case EnvironmentItem::Prepend:
return QString(item.name + "=+" + item.value);
case NameValueItem::SetDisabled:
case EnvironmentItem::SetDisabled:
return QString('#' + item.name + '=' + item.value);
case NameValueItem::SetEnabled:
case EnvironmentItem::SetEnabled:
return QString(item.name + '=' + item.value);
}
return QString();
});
}
NameValueItems NameValueItem::itemsFromVariantList(const QVariantList &list)
EnvironmentItems EnvironmentItem::itemsFromVariantList(const QVariantList &list)
{
return Utils::transform<NameValueItems>(list, [](const QVariant &item) {
return Utils::transform<EnvironmentItems>(list, [](const QVariant &item) {
return itemFromVariantList(item.toList());
});
}
QVariantList NameValueItem::toVariantList(const NameValueItems &list)
QVariantList EnvironmentItem::toVariantList(const EnvironmentItems &list)
{
return Utils::transform<QVariantList>(list, [](const NameValueItem &item) {
return Utils::transform<QVariantList>(list, [](const EnvironmentItem &item) {
return QVariant(toVariantList(item));
});
}
NameValueItem NameValueItem::itemFromVariantList(const QVariantList &list)
EnvironmentItem EnvironmentItem::itemFromVariantList(const QVariantList &list)
{
QTC_ASSERT(list.size() == 3, return NameValueItem("", ""));
QTC_ASSERT(list.size() == 3, return EnvironmentItem("", ""));
QString key = list.value(0).toString();
Operation operation = Operation(list.value(1).toInt());
QString value = list.value(2).toString();
return NameValueItem(key, value, operation);
return EnvironmentItem(key, value, operation);
}
QVariantList NameValueItem::toVariantList(const NameValueItem &item)
QVariantList EnvironmentItem::toVariantList(const EnvironmentItem &item)
{
return QVariantList() << item.name << item.operation << item.value;
}
@@ -118,7 +118,7 @@ static QString expand(const NameValueDictionary *dictionary, QString value)
return value;
}
void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
void EnvironmentItem::apply(NameValueDictionary *dictionary, Operation op) const
{
switch (op) {
case SetEnabled:
@@ -173,26 +173,26 @@ void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
}
}
QDebug operator<<(QDebug debug, const NameValueItem &i)
QDebug operator<<(QDebug debug, const EnvironmentItem &i)
{
QDebugStateSaver saver(debug);
debug.noquote();
debug.nospace();
debug << "KeyValueItem(";
switch (i.operation) {
case NameValueItem::SetEnabled:
case EnvironmentItem::SetEnabled:
debug << "set \"" << i.name << "\" to \"" << i.value << '"';
break;
case NameValueItem::SetDisabled:
case EnvironmentItem::SetDisabled:
debug << "set \"" << i.name << "\" to \"" << i.value << '"' << "[disabled]";
break;
case NameValueItem::Unset:
case EnvironmentItem::Unset:
debug << "unset \"" << i.name << '"';
break;
case NameValueItem::Prepend:
case EnvironmentItem::Prepend:
debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
break;
case NameValueItem::Append:
case EnvironmentItem::Append:
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
break;
}

View File

@@ -13,12 +13,12 @@
namespace Utils {
class QTCREATOR_UTILS_EXPORT NameValueItem
class QTCREATOR_UTILS_EXPORT EnvironmentItem
{
public:
enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled };
NameValueItem() = default;
NameValueItem(const QString &key, const QString &value, Operation operation = SetEnabled)
EnvironmentItem() = default;
EnvironmentItem(const QString &key, const QString &value, Operation operation = SetEnabled)
: name(key)
, value(value)
, operation(operation)
@@ -26,25 +26,25 @@ public:
void apply(NameValueDictionary *dictionary) const { apply(dictionary, operation); }
static void sort(NameValueItems *list);
static NameValueItems fromStringList(const QStringList &list);
static QStringList toStringList(const NameValueItems &list);
static NameValueItems itemsFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const NameValueItems &list);
static NameValueItem itemFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const NameValueItem &item);
static void sort(EnvironmentItems *list);
static EnvironmentItems fromStringList(const QStringList &list);
static QStringList toStringList(const EnvironmentItems &list);
static EnvironmentItems itemsFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const EnvironmentItems &list);
static EnvironmentItem itemFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const EnvironmentItem &item);
friend bool operator==(const NameValueItem &first, const NameValueItem &second)
friend bool operator==(const EnvironmentItem &first, const EnvironmentItem &second)
{
return first.operation == second.operation && first.name == second.name
&& first.value == second.value;
}
friend bool operator!=(const NameValueItem &first, const NameValueItem &second)
friend bool operator!=(const EnvironmentItem &first, const EnvironmentItem &second)
{
return !(first == second);
}
friend QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const NameValueItem &i);
friend QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const EnvironmentItem &i);
public:
QString name;

View File

@@ -1,456 +0,0 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "namevaluemodel.h"
#include "algorithm.h"
#include "hostosinfo.h"
#include "namevaluedictionary.h"
#include "namevalueitem.h"
#include "qtcassert.h"
#include "utilstr.h"
#include <QFileInfo>
#include <QFont>
#include <QGuiApplication>
#include <QPalette>
#include <QString>
namespace Utils {
namespace Internal {
class NameValueModelPrivate
{
public:
void updateResultNameValueDictionary()
{
m_resultNameValueDictionary = m_baseNameValueDictionary;
m_resultNameValueDictionary.modify(m_items);
// Add removed variables again and mark them as "<UNSET>" so
// that the user can actually see those removals:
for (const NameValueItem &item : std::as_const(m_items)) {
if (item.operation == NameValueItem::Unset)
m_resultNameValueDictionary.set(item.name, Tr::tr("<UNSET>"));
}
}
int findInChanges(const QString &name) const
{
for (int i = 0; i < m_items.size(); ++i)
if (m_items.at(i).name.compare(name,
m_baseNameValueDictionary.nameCaseSensitivity()) == 0) {
return i;
}
return -1;
}
int findInResultInsertPosition(const QString &name) const
{
NameValueDictionary::const_iterator it;
int i = 0;
for (it = m_resultNameValueDictionary.constBegin();
it != m_resultNameValueDictionary.constEnd();
++it, ++i)
if (it.key() > DictKey(name, m_resultNameValueDictionary.nameCaseSensitivity()))
return i;
return m_resultNameValueDictionary.size();
}
int findInResult(const QString &name) const
{
NameValueDictionary::const_iterator it;
int i = 0;
for (it = m_resultNameValueDictionary.constBegin();
it != m_resultNameValueDictionary.constEnd();
++it, ++i)
if (m_resultNameValueDictionary.key(it)
.compare(name, m_resultNameValueDictionary.nameCaseSensitivity()) == 0) {
return i;
}
return -1;
}
NameValueDictionary m_baseNameValueDictionary;
NameValueDictionary m_resultNameValueDictionary;
NameValueItems m_items;
};
} // namespace Internal
NameValueModel::NameValueModel(QObject *parent)
: QAbstractTableModel(parent)
, d(std::make_unique<Internal::NameValueModelPrivate>())
{}
NameValueModel::~NameValueModel() = default;
QString NameValueModel::indexToVariable(const QModelIndex &index) const
{
const auto it = std::next(d->m_resultNameValueDictionary.constBegin(), index.row());
return d->m_resultNameValueDictionary.key(it);
}
void NameValueModel::setBaseNameValueDictionary(const NameValueDictionary &dictionary)
{
if (d->m_baseNameValueDictionary == dictionary)
return;
beginResetModel();
d->m_baseNameValueDictionary = dictionary;
d->updateResultNameValueDictionary();
endResetModel();
}
int NameValueModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return d->m_resultNameValueDictionary.size();
}
int NameValueModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 2;
}
bool NameValueModel::changes(const QString &name) const
{
return d->findInChanges(name) >= 0;
}
const NameValueDictionary &NameValueModel::baseNameValueDictionary() const
{
return d->m_baseNameValueDictionary;
}
QVariant NameValueModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
const auto resultIterator = std::next(d->m_resultNameValueDictionary.constBegin(), index.row());
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole:
if (index.column() == 0)
return d->m_resultNameValueDictionary.key(resultIterator);
if (index.column() == 1) {
// Do not return "<UNSET>" when editing a previously unset variable:
if (role == Qt::EditRole) {
int pos = d->findInChanges(indexToVariable(index));
if (pos != -1 && d->m_items.at(pos).operation == NameValueItem::Unset)
return QString();
}
QString value = d->m_resultNameValueDictionary.value(resultIterator);
if (role == Qt::ToolTipRole && value.length() > 80) {
if (currentEntryIsPathList(index)) {
// For path lists, display one entry per line without separator
const QChar sep = Utils::HostOsInfo::pathListSeparator();
value = value.replace(sep, '\n');
} else {
// Use html to enable text wrapping
value = value.toHtmlEscaped();
value.prepend(QLatin1String("<html><body>"));
value.append(QLatin1String("</body></html>"));
}
}
return value;
}
break;
case Qt::FontRole: {
QFont f;
f.setStrikeOut(!d->m_resultNameValueDictionary.isEnabled(resultIterator));
return f;
}
case Qt::ForegroundRole: {
const QPalette p = QGuiApplication::palette();
return p.color(changes(d->m_resultNameValueDictionary.key(resultIterator))
? QPalette::Link : QPalette::Text);
}
}
return QVariant();
}
Qt::ItemFlags NameValueModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index)
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
QVariant NameValueModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
return QVariant();
return section == 0 ? Tr::tr("Variable") : Tr::tr("Value");
}
/// *****************
/// Utility functions
/// *****************
QModelIndex NameValueModel::variableToIndex(const QString &name) const
{
int row = d->findInResult(name);
if (row == -1)
return QModelIndex();
return index(row, 0);
}
bool NameValueModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::EditRole)
return false;
// ignore changes to already set values:
if (data(index, role) == value)
return true;
const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
int changesPos = d->findInChanges(oldName);
if (index.column() == 0) {
//fail if a variable with the same name already exists
const QString &newName = HostOsInfo::isWindowsHost() ? value.toString().toUpper()
: value.toString();
if (newName.isEmpty() || newName.contains('='))
return false;
// Does the new name exist already?
if (d->m_resultNameValueDictionary.hasKey(newName) || newName.isEmpty())
return false;
NameValueItem newVariable(newName, oldValue);
if (changesPos != -1)
resetVariable(oldName); // restore the original base variable again
QModelIndex newIndex = addVariable(newVariable); // add the new variable
emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
return true;
} else if (index.column() == 1) {
// We are changing an existing value:
const QString stringValue = value.toString();
if (changesPos != -1) {
const auto oldIt = d->m_baseNameValueDictionary.constFind(oldName);
const auto newIt = d->m_resultNameValueDictionary.constFind(oldName);
// We have already changed this value
if (oldIt != d->m_baseNameValueDictionary.constEnd()
&& stringValue == d->m_baseNameValueDictionary.value(oldIt)
&& d->m_baseNameValueDictionary.isEnabled(oldIt)
== d->m_resultNameValueDictionary.isEnabled(newIt)) {
// ... and now went back to the base value
d->m_items.removeAt(changesPos);
} else {
// ... and changed it again
d->m_items[changesPos].value = stringValue;
if (d->m_items[changesPos].operation == NameValueItem::Unset)
d->m_items[changesPos].operation = NameValueItem::SetEnabled;
}
} else {
// Add a new change item:
d->m_items.append(NameValueItem(oldName, stringValue));
}
d->updateResultNameValueDictionary();
emit dataChanged(index, index);
emit userChangesChanged();
return true;
}
return false;
}
QModelIndex NameValueModel::addVariable()
{
return addVariable(NameValueItem("NEWVAR", "VALUE"));
}
QModelIndex NameValueModel::addVariable(const NameValueItem &item)
{
// Return existing index if the name is already in the result set:
int pos = d->findInResult(item.name);
if (pos >= 0 && pos < d->m_resultNameValueDictionary.size())
return index(pos, 0, QModelIndex());
int insertPos = d->findInResultInsertPosition(item.name);
int changePos = d->findInChanges(item.name);
if (d->m_baseNameValueDictionary.hasKey(item.name)) {
// We previously unset this!
Q_ASSERT(changePos >= 0);
// Do not insert a line here as we listed the variable as <UNSET> before!
Q_ASSERT(d->m_items.at(changePos).name == item.name);
Q_ASSERT(d->m_items.at(changePos).operation == NameValueItem::Unset);
Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
d->m_items[changePos] = item;
emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
} else {
// We add something that is not in the base dictionary
// Insert a new line!
beginInsertRows(QModelIndex(), insertPos, insertPos);
Q_ASSERT(changePos < 0);
d->m_items.append(item);
d->updateResultNameValueDictionary();
endInsertRows();
}
emit userChangesChanged();
return index(insertPos, 0, QModelIndex());
}
void NameValueModel::resetVariable(const QString &name)
{
int rowInChanges = d->findInChanges(name);
if (rowInChanges < 0)
return;
int rowInResult = d->findInResult(name);
if (rowInResult < 0)
return;
if (d->m_baseNameValueDictionary.hasKey(name)) {
d->m_items.removeAt(rowInChanges);
d->updateResultNameValueDictionary();
emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
emit userChangesChanged();
} else {
// Remove the line completely!
beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
d->m_items.removeAt(rowInChanges);
d->updateResultNameValueDictionary();
endRemoveRows();
emit userChangesChanged();
}
}
void NameValueModel::unsetVariable(const QString &name)
{
// This does not change the number of rows as we will display a <UNSET>
// in place of the original variable!
int row = d->findInResult(name);
if (row < 0)
return;
// look in d->m_items for the variable
int pos = d->findInChanges(name);
if (pos != -1) {
d->m_items[pos].operation = NameValueItem::Unset;
d->m_items[pos].value.clear();
d->updateResultNameValueDictionary();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
return;
}
d->m_items.append(NameValueItem(name, QString(), NameValueItem::Unset));
d->updateResultNameValueDictionary();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
}
void NameValueModel::toggleVariable(const QModelIndex &idx)
{
const QString name = indexToVariable(idx);
const auto newIt = d->m_resultNameValueDictionary.constFind(name);
QTC_ASSERT(newIt != d->m_resultNameValueDictionary.constEnd(), return);
const auto op = d->m_resultNameValueDictionary.isEnabled(newIt)
? NameValueItem::SetDisabled : NameValueItem::SetEnabled;
const int changesPos = d->findInChanges(name);
if (changesPos != -1) {
const auto oldIt = d->m_baseNameValueDictionary.constFind(name);
if (oldIt == d->m_baseNameValueDictionary.constEnd()
|| oldIt.value().first != newIt.value().first) {
d->m_items[changesPos].operation = op;
} else {
d->m_items.removeAt(changesPos);
}
} else {
d->m_items.append({name, d->m_resultNameValueDictionary.value(newIt), op});
}
d->updateResultNameValueDictionary();
emit dataChanged(index(idx.row(), 0), index(idx.row(), 1));
emit userChangesChanged();
}
bool NameValueModel::isUnset(const QString &name)
{
const int pos = d->findInChanges(name);
return pos == -1 ? false : d->m_items.at(pos).operation == NameValueItem::Unset;
}
bool NameValueModel::isEnabled(const QString &name) const
{
return d->m_resultNameValueDictionary.isEnabled(d->m_resultNameValueDictionary.constFind(name));
}
bool NameValueModel::canReset(const QString &name)
{
return d->m_baseNameValueDictionary.hasKey(name);
}
NameValueItems NameValueModel::userChanges() const
{
return d->m_items;
}
void NameValueModel::setUserChanges(const NameValueItems &items)
{
NameValueItems filtered = Utils::filtered(items, [](const NameValueItem &i) {
return i.name != "export " && !i.name.contains('=');
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)
return;
beginResetModel();
d->m_items = filtered;
for (NameValueItem &item : d->m_items) {
QString &name = item.name;
name = name.trimmed();
if (name.startsWith("export "))
name = name.mid(7).trimmed();
if (d->m_baseNameValueDictionary.osType() == OsTypeWindows) {
// NameValueDictionary variable names are case-insensitive under windows, but we still
// want to preserve the case of pre-existing variables.
auto it = d->m_baseNameValueDictionary.constFind(name);
if (it != d->m_baseNameValueDictionary.constEnd())
name = d->m_baseNameValueDictionary.key(it);
}
}
d->updateResultNameValueDictionary();
endResetModel();
emit userChangesChanged();
}
bool NameValueModel::currentEntryIsPathList(const QModelIndex &current) const
{
if (!current.isValid())
return false;
// Look at the name first and check it against some well-known path variables. Extend as needed.
const QString varName = indexToVariable(current);
if (varName.compare("PATH", Utils::HostOsInfo::fileNameCaseSensitivity()) == 0)
return true;
if (Utils::HostOsInfo::isMacHost() && varName == "DYLD_LIBRARY_PATH")
return true;
if (Utils::HostOsInfo::isAnyUnixHost() && varName == "LD_LIBRARY_PATH")
return true;
if (varName == "PKG_CONFIG_DIR")
return true;
if (Utils::HostOsInfo::isWindowsHost()
&& QStringList{"INCLUDE", "LIB", "LIBPATH"}.contains(varName)) {
return true;
}
// Now check the value: If it's a list of strings separated by the platform's path separator
// and at least one of the strings is an existing directory, then that's enough proof for us.
QModelIndex valueIndex = current;
if (valueIndex.column() == 0)
valueIndex = valueIndex.siblingAtColumn(1);
const QStringList entries = data(valueIndex).toString()
.split(Utils::HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
if (entries.length() < 2)
return false;
return Utils::anyOf(entries, [](const QString &d) { return QFileInfo(d).isDir(); });
}
} // namespace Utils

View File

@@ -1,65 +0,0 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "utils_global.h"
#include "environmentfwd.h"
#include <QAbstractTableModel>
#include <memory>
namespace Utils {
namespace Internal {
class NameValueModelPrivate;
}
class QTCREATOR_UTILS_EXPORT NameValueModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit NameValueModel(QObject *parent = nullptr);
~NameValueModel() override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section,
Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex addVariable();
QModelIndex addVariable(const NameValueItem &item);
void resetVariable(const QString &name);
void unsetVariable(const QString &name);
void toggleVariable(const QModelIndex &index);
bool isUnset(const QString &name);
bool isEnabled(const QString &name) const;
bool canReset(const QString &name);
QString indexToVariable(const QModelIndex &index) const;
QModelIndex variableToIndex(const QString &name) const;
bool changes(const QString &key) const;
const NameValueDictionary &baseNameValueDictionary() const;
void setBaseNameValueDictionary(const NameValueDictionary &dictionary);
NameValueItems userChanges() const;
void setUserChanges(const NameValueItems &items);
bool currentEntryIsPathList(const QModelIndex &current) const;
signals:
void userChangesChanged();
/// Hint to the view where it should make sense to focus on next
// This is a hack since there is no way for a model to suggest
// the next interesting place to focus on to the view.
void focusIndex(const QModelIndex &index);
private:
std::unique_ptr<Internal::NameValueModelPrivate> d;
};
} // namespace Utils

View File

@@ -70,7 +70,7 @@ NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
layout->addWidget(new QLabel(helpText, this));
const auto checkForItemChange = [this] {
const NameValueItems newItems = environmentItems();
const EnvironmentItems newItems = environmentItems();
if (newItems != m_originalItems) {
m_originalItems = newItems;
emit userChangedItems(newItems);
@@ -190,8 +190,8 @@ void NameValuesDialog::setPlaceholderText(const QString &text)
m_editor->setPlaceholderText(text);
}
std::optional<NameValueItems> NameValuesDialog::getNameValueItems(QWidget *parent,
const NameValueItems &initial,
std::optional<EnvironmentItems> NameValuesDialog::getNameValueItems(QWidget *parent,
const EnvironmentItems &initial,
const QString &placeholderText,
Polisher polisher,
const QString &windowTitle)

View File

@@ -34,20 +34,20 @@ signals:
private:
Internal::TextEditHelper *m_editor;
NameValueItems m_originalItems;
EnvironmentItems m_originalItems;
};
class QTCREATOR_UTILS_EXPORT NameValuesDialog : public QDialog
{
public:
void setNameValueItems(const NameValueItems &items);
NameValueItems nameValueItems() const;
void setNameValueItems(const EnvironmentItems &items);
EnvironmentItems nameValueItems() const;
void setPlaceholderText(const QString &text);
using Polisher = std::function<void(QWidget *)>;
static std::optional<NameValueItems> getNameValueItems(QWidget *parent = nullptr,
const NameValueItems &initial = {},
static std::optional<EnvironmentItems> getNameValueItems(QWidget *parent = nullptr,
const EnvironmentItems &initial = {},
const QString &placeholderText = {},
Polisher polish = {},
const QString &windowTitle = {});

View File

@@ -2,7 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "namevaluevalidator.h"
#include "namevaluemodel.h"
#include "environmentmodel.h"
#include "tooltip/tooltip.h"
#include <QTreeView>
@@ -10,7 +11,7 @@
namespace Utils {
NameValueValidator::NameValueValidator(QWidget *parent,
NameValueModel *model,
EnvironmentModel *model,
QTreeView *view,
const QModelIndex &index,
const QString &toolTipText)

View File

@@ -16,13 +16,13 @@ QT_END_NAMESPACE
namespace Utils {
class NameValueModel;
class EnvironmentModel;
class QTCREATOR_UTILS_EXPORT NameValueValidator : public QValidator
{
public:
NameValueValidator(QWidget *parent,
NameValueModel *model,
EnvironmentModel *model,
QTreeView *view,
const QModelIndex &index,
const QString &toolTipText);
@@ -33,7 +33,7 @@ public:
private:
const QString m_toolTipText;
NameValueModel *m_model;
EnvironmentModel *m_model;
QTreeView *m_view;
QPersistentModelIndex m_index;
mutable QTimer m_hideTipTimer;

View File

@@ -82,6 +82,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
d->m_pathChooser->setObjectName("baseFolder"); // used by Squish
d->m_pathChooser->setExpectedKind(PathChooser::Directory);
d->m_pathChooser->setDisabled(d->m_forceSubProject);
d->m_pathChooser->setAllowPathFromDevice(true);
d->m_projectsDirectoryCheckBox = new QCheckBox(Tr::tr("Use as default project location"));
d->m_projectsDirectoryCheckBox->setObjectName("projectsDirectoryCheckBox");

View File

@@ -420,6 +420,7 @@ void TerminalInterface::start()
connect(d->stubConnectTimeoutTimer.get(), &QTimer::timeout, this, [this] {
killInferiorProcess();
killStubProcess();
emitFinished(-1, QProcess::ExitStatus::CrashExit);
});
d->stubConnectTimeoutTimer->setSingleShot(true);
d->stubConnectTimeoutTimer->start(10000);

View File

@@ -198,8 +198,6 @@ QtcLibrary {
"namevaluedictionary.h",
"namevalueitem.cpp",
"namevalueitem.h",
"namevaluemodel.cpp",
"namevaluemodel.h",
"namevaluesdialog.cpp",
"namevaluesdialog.h",
"namevaluevalidator.cpp",

View File

@@ -1479,9 +1479,12 @@ FilePath AndroidConfig::getJdkPath()
// Look for Android Studio's jdk first
const FilePath androidStudioSdkPath = androidStudioPath();
if (!androidStudioSdkPath.isEmpty()) {
const FilePath androidStudioSdkJrePath = androidStudioSdkPath / "jre";
if (androidStudioSdkJrePath.exists())
jdkHome = androidStudioSdkJrePath;
const QStringList allVersions{"jbr", "jre"};
for (const QString &version : allVersions) {
const FilePath androidStudioSdkJbrPath = androidStudioSdkPath / version;
if (androidStudioSdkJbrPath.exists())
return androidStudioSdkJbrPath;
}
}
if (jdkHome.isEmpty()) {

View File

@@ -100,8 +100,8 @@ void AndroidSdkDownloader::downloadAndExtractSdk()
m_progressDialog->setFixedSize(m_progressDialog->sizeHint());
m_progressDialog->setAutoClose(false);
connect(m_progressDialog.get(), &QProgressDialog::canceled, this, [this] {
m_progressDialog.release()->deleteLater();
m_taskTreeRunner.reset();
m_progressDialog.release()->deleteLater();
});
Storage<std::optional<FilePath>> storage;
@@ -116,6 +116,8 @@ void AndroidSdkDownloader::downloadAndExtractSdk()
return;
connect(reply, &QNetworkReply::downloadProgress,
this, [this](qint64 received, qint64 max) {
if (!m_progressDialog)
return;
m_progressDialog->setRange(0, max);
m_progressDialog->setValue(received);
});

View File

@@ -35,6 +35,8 @@
#include <map>
using namespace Core;
using namespace ProjectExplorer;
using namespace Tasking;
using namespace Utils;
@@ -199,11 +201,11 @@ public:
if (role == BaseTreeView::ItemActivatedRole && !m_links.isEmpty()) {
// TODO for now only simple - just the first..
Link link = m_links.first();
ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
Project *project = ProjectManager::startupProject();
FilePath baseDir = project ? project->projectDirectory() : FilePath{};
link.targetFilePath = baseDir.resolvePath(link.targetFilePath);
if (link.targetFilePath.exists())
Core::EditorManager::openEditorAt(link);
EditorManager::openEditorAt(link);
return true;
}
return StaticTreeItem::setData(column, value, role);
@@ -591,7 +593,7 @@ void IssuesWidget::fetchMoreIssues()
}
AxivionOutputPane::AxivionOutputPane(QObject *parent)
: Core::IOutputPane(parent)
: IOutputPane(parent)
{
setId("Axivion");
setDisplayName(Tr::tr("Axivion"));
@@ -702,7 +704,7 @@ void AxivionOutputPane::updateAndShowRule(const QString &ruleHtml)
browser->setText(ruleHtml);
if (!ruleHtml.isEmpty()) {
m_outputWidget->setCurrentIndex(2);
popup(Core::IOutputPane::NoModeSwitch);
popup(IOutputPane::NoModeSwitch);
}
}
}

View File

@@ -53,21 +53,6 @@ using namespace Utils;
namespace Axivion::Internal {
class Issue
{
public:
QString id;
QString state;
QString errorNumber;
QString message;
QString entity;
QString filePath;
QString severity;
int lineNumber = 0;
};
using Issues = QList<Issue>;
QIcon iconForIssue(const QString &prefix)
{
static QHash<QString, QIcon> prefixToIcon;
@@ -154,7 +139,7 @@ public:
void onDocumentOpened(IDocument *doc);
void onDocumentClosed(IDocument * doc);
void clearAllMarks();
void handleIssuesForFile(const Issues &issues);
void handleIssuesForFile(const Dto::FileViewDto &fileView);
void fetchIssueInfo(const QString &id);
NetworkAccessManager m_networkAccessManager;
@@ -173,18 +158,18 @@ static AxivionPluginPrivate *dd = nullptr;
class AxivionTextMark : public TextMark
{
public:
AxivionTextMark(const FilePath &filePath, const Issue &issue)
: TextMark(filePath, issue.lineNumber, {Tr::tr("Axivion"), AxivionTextMarkId})
AxivionTextMark(const FilePath &filePath, const Dto::LineMarkerDto &issue)
: TextMark(filePath, issue.startLine, {Tr::tr("Axivion"), AxivionTextMarkId})
{
const QString markText = issue.entity.isEmpty() ? issue.message
: issue.entity + ": " + issue.message;
setToolTip(issue.errorNumber + " " + markText);
setIcon(iconForIssue("SV")); // FIXME adapt to the issue
const QString markText = issue.description;
const QString id = issue.kind + QString::number(issue.id.value_or(-1));
setToolTip(id + markText);
setIcon(iconForIssue(issue.kind));
setPriority(TextMark::NormalPriority);
setLineAnnotation(markText);
setActionsProvider([id = issue.id] {
setActionsProvider([id] {
auto action = new QAction;
action->setIcon(Utils::Icons::INFO.icon());
action->setIcon(Icons::INFO.icon());
action->setToolTip(Tr::tr("Show rule details"));
QObject::connect(action, &QAction::triggered, dd, [id] { dd->fetchIssueInfo(id); });
return QList{action};
@@ -281,8 +266,8 @@ static QUrl urlForProject(const QString &projectName)
}
static constexpr int httpStatusCodeOk = 200;
static const QLatin1String jsonContentType{ "application/json" };
static const QLatin1String htmlContentType{ "text/html" };
constexpr char s_htmlContentType[] = "text/html";
constexpr char s_jsonContentType[] = "application/json";
static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QByteArray &)> &handler)
{
@@ -299,15 +284,11 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QBy
const auto onQuerySetup = [storage, url](NetworkQuery &query) {
QNetworkRequest request(url);
request.setRawHeader(QByteArrayLiteral("Accept"),
QByteArray(htmlContentType.data(), htmlContentType.size()));
request.setRawHeader(QByteArrayLiteral("Authorization"),
storage->credentials);
const QByteArray ua = QByteArrayLiteral("Axivion")
+ QCoreApplication::applicationName().toUtf8()
+ QByteArrayLiteral("Plugin/")
+ QCoreApplication::applicationVersion().toUtf8();
request.setRawHeader(QByteArrayLiteral("X-Axivion-User-Agent"), ua);
request.setRawHeader("Accept", s_htmlContentType);
request.setRawHeader("Authorization", storage->credentials);
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);
};
@@ -322,11 +303,10 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QBy
.trimmed()
.toLower();
if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk
&& contentType == htmlContentType) {
&& contentType == s_htmlContentType) {
handler(reply->readAll());
return DoneResult::Success;
}
return DoneResult::Error;
};
@@ -355,22 +335,19 @@ static Group fetchDataRecipe(const QUrl &url,
storage->credentials = QByteArrayLiteral("AxToken ") + settings().server.token.toUtf8();
};
const auto onQuerySetup = [storage, url](NetworkQuery &query) {
const auto onNetworkQuerySetup = [storage, url](NetworkQuery &query) {
QNetworkRequest request(url);
request.setRawHeader(QByteArrayLiteral("Accept"),
QByteArray(jsonContentType.data(), jsonContentType.size()));
request.setRawHeader(QByteArrayLiteral("Authorization"),
storage->credentials);
const QByteArray ua = QByteArrayLiteral("Axivion")
+ QCoreApplication::applicationName().toUtf8()
+ QByteArrayLiteral("Plugin/")
+ QCoreApplication::applicationVersion().toUtf8();
request.setRawHeader(QByteArrayLiteral("X-Axivion-User-Agent"), ua);
request.setRawHeader("Accept", s_jsonContentType);
request.setRawHeader("Authorization", storage->credentials);
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 = [storage, url](const NetworkQuery &query, DoneWith doneWith) {
const auto onNetworkQueryDone = [storage, url](const NetworkQuery &query, DoneWith doneWith) {
QNetworkReply *reply = query.reply();
const QNetworkReply::NetworkError error = reply->error();
const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@@ -381,13 +358,13 @@ static Group fetchDataRecipe(const QUrl &url,
.trimmed()
.toLower();
if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk
&& contentType == jsonContentType) {
&& contentType == s_jsonContentType) {
storage->serializableData = reply->readAll();
return DoneResult::Success;
}
const auto getError = [&]() -> Error {
if (contentType == jsonContentType) {
if (contentType == s_jsonContentType) {
try {
return DashboardError(reply->url(), statusCode,
reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(),
@@ -404,8 +381,7 @@ static Group fetchDataRecipe(const QUrl &url,
return NetworkError(reply->url(), error, reply->errorString());
};
MessageManager::writeFlashing(
QStringLiteral("Axivion: %1").arg(getError().message()));
MessageManager::writeFlashing(QStringLiteral("Axivion: %1").arg(getError().message()));
return DoneResult::Error;
};
@@ -426,7 +402,7 @@ static Group fetchDataRecipe(const QUrl &url,
const Group recipe {
storage,
Sync(onCredentialSetup),
NetworkQueryTask(onQuerySetup, onQueryDone),
NetworkQueryTask(onNetworkQuerySetup, onNetworkQueryDone),
AsyncTask<SerializableType>(onDeserializeSetup, onDeserializeDone)
};
@@ -495,6 +471,17 @@ Group issueTableRecipe(const IssueListSearch &search, const IssueTableHandler &h
return fetchDataRecipe<Dto::IssueTableDto>(url, handler);
}
Group lineMarkerRecipe(const FilePath &filePath, const LineMarkerHandler &handler)
{
QTC_ASSERT(dd->m_currentProjectInfo, return {}); // TODO: Call handler with unexpected?
QTC_ASSERT(!filePath.isEmpty(), return {}); // TODO: Call handler with unexpected?
const QString fileName = QString::fromUtf8(QUrl::toPercentEncoding(filePath.path()));
const QUrl url = urlForProject(dd->m_currentProjectInfo.value().name + '/')
.resolved(QString("files?filename=" + fileName));
return fetchDataRecipe<Dto::FileViewDto>(url, handler);
}
Group issueHtmlRecipe(const QString &issueId, const HtmlHandler &handler)
{
QTC_ASSERT(dd->m_currentProjectInfo, return {}); // TODO: Call handler with unexpected?
@@ -569,7 +556,7 @@ void AxivionPluginPrivate::fetchIssueInfo(const QString &id)
dd->m_axivionOutputPane.updateAndShowRule(QString::fromUtf8(fixedHtml));
};
m_issueInfoRunner.start(issueHtmlRecipe(QString("SV") + id, ruleHandler));
m_issueInfoRunner.start(issueHtmlRecipe(id, ruleHandler));
}
void AxivionPluginPrivate::handleOpenedDocs()
@@ -591,41 +578,16 @@ void AxivionPluginPrivate::onDocumentOpened(IDocument *doc)
if (!doc || !m_currentProjectInfo || !m_project || !m_project->isKnownFile(doc->filePath()))
return;
IssueListSearch search;
search.kind = "SV";
search.filter_path = doc->filePath().relativeChildPath(m_project->projectDirectory()).path();
search.limit = 0;
const FilePath filePath = doc->filePath().relativeChildPath(m_project->projectDirectory());
QTC_ASSERT(!filePath.isEmpty(), return);
const auto issuesHandler = [this](const Dto::IssueTableDto &dto) {
Issues issues;
const std::vector<std::map<QString, Dto::Any>> &rows = dto.rows;
for (const auto &row : rows) {
Issue issue;
for (auto it = row.cbegin(); it != row.cend(); ++it) {
if (it->first == "id")
issue.id = anyToSimpleString(it->second);
else if (it->first == "state")
issue.state = anyToSimpleString(it->second);
else if (it->first == "errorNumber")
issue.errorNumber = anyToSimpleString(it->second);
else if (it->first == "message")
issue.message = anyToSimpleString(it->second);
else if (it->first == "entity")
issue.entity = anyToSimpleString(it->second);
else if (it->first == "path")
issue.filePath = anyToSimpleString(it->second);
else if (it->first == "severity")
issue.severity = anyToSimpleString(it->second);
else if (it->first == "line")
issue.lineNumber = anyToSimpleString(it->second).toInt();
}
issues << issue;
}
handleIssuesForFile(issues);
const auto handler = [this](const Dto::FileViewDto &data) {
if (data.lineMarkers.empty())
return;
handleIssuesForFile(data);
};
TaskTree *taskTree = new TaskTree;
taskTree->setRecipe(issueTableRecipe(search, issuesHandler));
taskTree->setRecipe(lineMarkerRecipe(filePath, handler));
m_docMarksTrees.insert_or_assign(doc, std::unique_ptr<TaskTree>(taskTree));
connect(taskTree, &TaskTree::done, this, [this, doc] {
const auto it = m_docMarksTrees.find(doc);
@@ -653,24 +615,22 @@ void AxivionPluginPrivate::onDocumentClosed(IDocument *doc)
}
}
void AxivionPluginPrivate::handleIssuesForFile(const Issues &issues)
void AxivionPluginPrivate::handleIssuesForFile(const Dto::FileViewDto &fileView)
{
if (issues.isEmpty())
if (fileView.lineMarkers.empty())
return;
Project *project = ProjectManager::startupProject();
if (!project)
return;
const FilePath filePath = project->projectDirectory()
.pathAppended(issues.first().filePath);
const FilePath filePath = project->projectDirectory().pathAppended(fileView.fileName);
const Id axivionId(AxivionTextMarkId);
for (const Issue &issue : issues) {
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, issue);
new AxivionTextMark(filePath, marker);
}
}

View File

@@ -19,6 +19,8 @@ namespace ProjectExplorer { class Project; }
namespace Tasking { class Group; }
namespace Utils { class FilePath; }
namespace Axivion::Internal {
struct IssueListSearch
@@ -57,6 +59,10 @@ Tasking::Group tableInfoRecipe(const QString &prefix, const TableInfoHandler &ha
using IssueTableHandler = std::function<void(const Dto::IssueTableDto &)>;
Tasking::Group issueTableRecipe(const IssueListSearch &search, const IssueTableHandler &handler);
// TODO: Wrap into expected_str<>?
using LineMarkerHandler = std::function<void(const Dto::FileViewDto &)>;
Tasking::Group lineMarkerRecipe(const Utils::FilePath &filePath, const LineMarkerHandler &handler);
using HtmlHandler = std::function<void(const QByteArray &)>;
Tasking::Group issueHtmlRecipe(const QString &issueId, const HtmlHandler &handler);

View File

@@ -33,7 +33,7 @@ const char PSK_PROJECTNAME[] = "Axivion.ProjectName";
class AxivionProjectSettingsHandler : public QObject
{
public:
AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project)
AxivionProjectSettings *projectSettings(Project *project)
{
auto &settings = m_axivionProjectSettings[project];
if (!settings)
@@ -47,7 +47,7 @@ public:
m_axivionProjectSettings.clear();
}
QHash<ProjectExplorer::Project *, AxivionProjectSettings *> m_axivionProjectSettings;
QHash<Project *, AxivionProjectSettings *> m_axivionProjectSettings;
};
static AxivionProjectSettingsHandler &projectSettingsHandler()
@@ -58,17 +58,15 @@ static AxivionProjectSettingsHandler &projectSettingsHandler()
// AxivionProjectSettings
AxivionProjectSettings::AxivionProjectSettings(ProjectExplorer::Project *project)
AxivionProjectSettings::AxivionProjectSettings(Project *project)
: m_project{project}
{
load();
connect(project, &ProjectExplorer::Project::settingsLoaded,
this, &AxivionProjectSettings::load);
connect(project, &ProjectExplorer::Project::aboutToSaveSettings,
this, &AxivionProjectSettings::save);
connect(project, &Project::settingsLoaded, this, &AxivionProjectSettings::load);
connect(project, &Project::aboutToSaveSettings, this, &AxivionProjectSettings::save);
}
AxivionProjectSettings *AxivionProjectSettings::projectSettings(ProjectExplorer::Project *project)
AxivionProjectSettings *AxivionProjectSettings::projectSettings(Project *project)
{
return projectSettingsHandler().projectSettings(project);
}
@@ -90,10 +88,10 @@ void AxivionProjectSettings::save()
// AxivionProjectSettingsWidget
class AxivionProjectSettingsWidget : public ProjectExplorer::ProjectSettingsWidget
class AxivionProjectSettingsWidget : public ProjectSettingsWidget
{
public:
explicit AxivionProjectSettingsWidget(ProjectExplorer::Project *project);
explicit AxivionProjectSettingsWidget(Project *project);
private:
void fetchProjects();
@@ -109,11 +107,11 @@ private:
QPushButton *m_fetchProjects = nullptr;
QPushButton *m_link = nullptr;
QPushButton *m_unlink = nullptr;
Utils::InfoLabel *m_infoLabel = nullptr;
InfoLabel *m_infoLabel = nullptr;
TaskTreeRunner m_taskTreeRunner;
};
AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(ProjectExplorer::Project *project)
AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(Project *project)
: m_projectSettings(projectSettingsHandler().projectSettings(project))
{
setUseGlobalSettingsCheckBoxVisible(false);
@@ -132,7 +130,7 @@ AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(ProjectExplorer::Proj
verticalLayout->addWidget(new QLabel(Tr::tr("Dashboard projects:")));
verticalLayout->addWidget(m_dashboardProjects);
m_infoLabel = new Utils::InfoLabel(this);
m_infoLabel = new InfoLabel(this);
m_infoLabel->setVisible(false);
verticalLayout->addWidget(m_infoLabel);
@@ -171,7 +169,7 @@ void AxivionProjectSettingsWidget::fetchProjects()
const auto onDashboardInfo = [this](const expected_str<DashboardInfo> &info) {
if (!info) {
m_infoLabel->setText(info.error());
m_infoLabel->setType(Utils::InfoLabel::Error);
m_infoLabel->setType(InfoLabel::Error);
m_infoLabel->setVisible(true);
} else {
for (const QString &project : info->projects)
@@ -234,19 +232,19 @@ void AxivionProjectSettingsWidget::updateEnabledStates()
if (!hasDashboardSettings) {
m_infoLabel->setText(Tr::tr("Incomplete or misconfigured settings."));
m_infoLabel->setType(Utils::InfoLabel::NotOk);
m_infoLabel->setType(InfoLabel::NotOk);
m_infoLabel->setVisible(true);
}
}
class AxivionProjectPanelFactory : public ProjectExplorer::ProjectPanelFactory
class AxivionProjectPanelFactory : public ProjectPanelFactory
{
public:
AxivionProjectPanelFactory()
{
setPriority(250);
setDisplayName(Tr::tr("Axivion"));
setCreateWidgetFunction([](ProjectExplorer::Project *project) {
setCreateWidgetFunction([](Project *project) {
return new AxivionProjectSettingsWidget(project);
});
}

View File

@@ -20,11 +20,12 @@
#include <QUuid>
#include <QVBoxLayout>
using namespace Core;
using namespace Utils;
namespace Axivion::Internal {
AxivionServer::AxivionServer(const Utils::Id &id, const QString &dashboard,
AxivionServer::AxivionServer(const Id &id, const QString &dashboard,
const QString &description, const QString &token)
: id(id)
, dashboard(dashboard)
@@ -68,17 +69,17 @@ AxivionServer AxivionServer::fromJson(const QJsonObject &json)
const QJsonValue token = json.value("token");
if (token == QJsonValue::Undefined)
return invalidServer;
return { Utils::Id::fromString(id.toString()), dashboard.toString(),
return { Id::fromString(id.toString()), dashboard.toString(),
description.toString(), token.toString() };
}
static Utils::FilePath tokensFilePath()
static FilePath tokensFilePath()
{
return Utils::FilePath::fromString(Core::ICore::settings()->fileName()).parentDir()
return FilePath::fromString(ICore::settings()->fileName()).parentDir()
.pathAppended("qtcreator/axivion.json");
}
static void writeTokenFile(const Utils::FilePath &filePath, const AxivionServer &server)
static void writeTokenFile(const FilePath &filePath, const AxivionServer &server)
{
QJsonDocument doc;
doc.setObject(server.toJson());
@@ -87,11 +88,11 @@ static void writeTokenFile(const Utils::FilePath &filePath, const AxivionServer
filePath.setPermissions(QFile::ReadUser | QFile::WriteUser);
}
static AxivionServer readTokenFile(const Utils::FilePath &filePath)
static AxivionServer readTokenFile(const FilePath &filePath)
{
if (!filePath.exists())
return {};
Utils::expected_str<QByteArray> contents = filePath.fileContents();
expected_str<QByteArray> contents = filePath.fileContents();
if (!contents)
return {};
const QJsonDocument doc = QJsonDocument::fromJson(*contents);
@@ -120,7 +121,7 @@ AxivionSettings::AxivionSettings()
void AxivionSettings::toSettings() const
{
writeTokenFile(tokensFilePath(), server);
Utils::AspectContainer::writeSettings();
AspectContainer::writeSettings();
}
// AxivionSettingsPage
@@ -214,7 +215,7 @@ AxivionServer DashboardSettingsWidget::dashboardServer() const
if (m_id.isValid())
result.id = m_id;
else
result.id = m_mode == Edit ? Utils::Id::fromName(QUuid::createUuid().toByteArray()) : m_id;
result.id = m_mode == Edit ? Id::fromName(QUuid::createUuid().toByteArray()) : m_id;
result.dashboard = m_dashboardUrl();
result.description = m_description();
result.token = m_token();
@@ -234,7 +235,7 @@ bool DashboardSettingsWidget::isValid() const
return !m_token().isEmpty() && !m_description().isEmpty() && isUrlValid(m_dashboardUrl());
}
class AxivionSettingsWidget : public Core::IOptionsPageWidget
class AxivionSettingsWidget : public IOptionsPageWidget
{
public:
AxivionSettingsWidget();
@@ -299,7 +300,7 @@ void AxivionSettingsWidget::showEditServerDialog()
// AxivionSettingsPage
class AxivionSettingsPage : public Core::IOptionsPage
class AxivionSettingsPage : public IOptionsPage
{
public:
AxivionSettingsPage()

View File

@@ -34,6 +34,7 @@
#include <vcsbase/vcsbaseconstants.h>
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/vcsbaseplugin.h>
#include <vcsbase/vcsbasetr.h>
#include <vcsbase/vcsbasesubmiteditor.h>
#include <vcsbase/vcscommand.h>
#include <vcsbase/vcsoutputwindow.h>
@@ -62,7 +63,6 @@ namespace Bazaar::Internal {
// Submit editor parameters
const char COMMIT_ID[] = "Bazaar Commit Log Editor";
const char COMMIT_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::VcsBase", "Bazaar Commit Log Editor");
const char COMMITMIMETYPE[] = "text/vnd.qtcreator.bazaar.commit";
// Menu items
@@ -89,27 +89,6 @@ const char COMMIT[] = "Bazaar.Action.Commit";
const char UNCOMMIT[] = "Bazaar.Action.UnCommit";
const char CREATE_REPOSITORY[] = "Bazaar.Action.CreateRepository";
const VcsBaseEditorParameters logEditorParameters {
LogOutput, // type
Constants::FILELOG_ID, // id
Constants::FILELOG_DISPLAY_NAME, // display name
Constants::LOGAPP // mime type
};
const VcsBaseEditorParameters annotateEditorParameters {
AnnotateOutput,
Constants::ANNOTATELOG_ID,
Constants::ANNOTATELOG_DISPLAY_NAME,
Constants::ANNOTATEAPP
};
const VcsBaseEditorParameters diffEditorParameters {
DiffOutput,
Constants::DIFFLOG_ID,
Constants::DIFFLOG_DISPLAY_NAME,
Constants::DIFFAPP
};
class RevertDialog : public QDialog
{
public:
@@ -220,23 +199,32 @@ public:
FilePath m_submitRepository;
VcsEditorFactory logEditorFactory {
&logEditorParameters,
VcsEditorFactory logEditorFactory {{
LogOutput, // type
Constants::FILELOG_ID, // id
VcsBase::Tr::tr("Bazaar File Log Editor"),
Constants::LOGAPP,// mime type
[] { return new BazaarEditorWidget; },
std::bind(&BazaarPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory annotateEditorFactory {
&annotateEditorParameters,
VcsEditorFactory annotateEditorFactory {{
AnnotateOutput,
Constants::ANNOTATELOG_ID,
VcsBase::Tr::tr("Bazaar Annotation Editor"),
Constants::ANNOTATEAPP,
[] { return new BazaarEditorWidget; },
std::bind(&BazaarPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory diffEditorFactory {
&diffEditorParameters,
VcsEditorFactory diffEditorFactory {{
DiffOutput,
Constants::DIFFLOG_ID,
VcsBase::Tr::tr("Bazaar Diff Editor"),
Constants::DIFFAPP,
[] { return new BazaarEditorWidget; },
std::bind(&BazaarPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
};
class UnCommitDialog : public QDialog
@@ -492,7 +480,7 @@ BazaarPluginPrivate::BazaarPluginPrivate()
setupVcsSubmitEditor(this, {
COMMITMIMETYPE,
COMMIT_ID,
COMMIT_DISPLAY_NAME,
VcsBase::Tr::tr("Bazaar Commit Log Editor"),
VcsBaseSubmitEditorParameters::DiffFiles,
[] { return new CommitEditor; }
});
@@ -963,10 +951,10 @@ VcsCommand *BazaarPluginPrivate::createInitialCheckoutCommand(const QString &url
args << m_client.vcsCommandString(BazaarClient::CloneCommand)
<< extraArgs << url << localName;
Environment env = m_client.processEnvironment();
Environment env = m_client.processEnvironment(baseDirectory);
env.set("BZR_PROGRESS_BAR", "text");
auto command = VcsBaseClient::createVcsCommand(baseDirectory, env);
command->addJob({m_client.vcsBinary(), args}, -1);
command->addJob({m_client.vcsBinary(baseDirectory), args}, -1);
return command;
}

View File

@@ -3,8 +3,6 @@
#pragma once
#include <QtGlobal>
namespace Bazaar::Constants {
const char BAZAAR[] = "bazaar";
@@ -23,15 +21,12 @@ const char ANNOTATE_CHANGESET_ID[] = "([.0-9]+)";
// Base editor parameters
const char FILELOG_ID[] = "Bazaar File Log Editor";
const char FILELOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::VcsBase", "Bazaar File Log Editor");
const char LOGAPP[] = "text/vnd.qtcreator.bazaar.log";
const char ANNOTATELOG_ID[] = "Bazaar Annotation Editor";
const char ANNOTATELOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::VcsBase", "Bazaar Annotation Editor");
const char ANNOTATEAPP[] = "text/vnd.qtcreator.bazaar.annotation";
const char DIFFLOG_ID[] = "Bazaar Diff Editor";
const char DIFFLOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::VcsBase", "Bazaar Diff Editor");
const char DIFFAPP[] = "text/x-patch";
// File status hint

View File

@@ -79,7 +79,7 @@ public:
m_widget->highlightSearchResults(QByteArray());
}
int find(const QByteArray &pattern, int pos, FindFlags findFlags, bool *wrapped)
qint64 find(const QByteArray &pattern, qint64 pos, FindFlags findFlags, bool *wrapped)
{
if (wrapped)
*wrapped = false;
@@ -88,7 +88,7 @@ public:
return pos;
}
int res = m_widget->find(pattern, pos, Utils::textDocumentFlagsForFindFlags(findFlags));
qint64 res = m_widget->find(pattern, pos, Utils::textDocumentFlagsForFindFlags(findFlags));
if (res < 0) {
pos = (findFlags & FindBackward) ? -1 : 0;
res = m_widget->find(pattern, pos, Utils::textDocumentFlagsForFindFlags(findFlags));
@@ -111,7 +111,7 @@ public:
if (m_contPos == -1)
m_contPos = m_incrementalStartPos;
bool wrapped;
int found = find(pattern, m_contPos, findFlags, &wrapped);
qint64 found = find(pattern, m_contPos, findFlags, &wrapped);
if (wrapped != m_incrementalWrappedState && (found >= 0)) {
m_incrementalWrappedState = wrapped;
showWrapIndicator(m_widget);
@@ -147,7 +147,7 @@ public:
m_contPos = m_widget->selectionStart()-1;
}
bool wrapped;
int found = find(pattern, m_contPos, findFlags, &wrapped);
qint64 found = find(pattern, m_contPos, findFlags, &wrapped);
if (wrapped)
showWrapIndicator(m_widget);
Result result;

View File

@@ -238,16 +238,16 @@ bool BinEditorWidget::requestOldDataAt(qint64 pos) const
char BinEditorWidget::dataAt(qint64 pos, bool old) const
{
qint64 block = pos / m_blockSize;
int offset = static_cast<int>(pos - block * m_blockSize);
const qint64 block = pos / m_blockSize;
const qint64 offset = pos - block * m_blockSize;
return blockData(block, old).at(offset);
}
void BinEditorWidget::changeDataAt(qint64 pos, char c)
{
qint64 block = pos / m_blockSize;
const qint64 block = pos / m_blockSize;
BlockMap::iterator it = m_modifiedData.find(block);
int offset = static_cast<int>(pos - block * m_blockSize);
const qint64 offset = pos - block * m_blockSize;
if (it != m_modifiedData.end()) {
it.value()[offset] = c;
} else {
@@ -262,7 +262,7 @@ void BinEditorWidget::changeDataAt(qint64 pos, char c)
d->announceChangedData(m_baseAddr + pos, QByteArray(1, c));
}
QByteArray BinEditorWidget::dataMid(qint64 from, int length, bool old) const
QByteArray BinEditorWidget::dataMid(qint64 from, qint64 length, bool old) const
{
qint64 end = from + length;
qint64 block = from / m_blockSize;
@@ -581,20 +581,20 @@ void BinEditorWidget::updateLines()
updateLines(m_cursorPosition, m_cursorPosition);
}
void BinEditorWidget::updateLines(int fromPosition, int toPosition)
void BinEditorWidget::updateLines(qint64 fromPosition, qint64 toPosition)
{
int topLine = verticalScrollBar()->value();
int firstLine = qMin(fromPosition, toPosition) / m_bytesPerLine;
int lastLine = qMax(fromPosition, toPosition) / m_bytesPerLine;
int y = (firstLine - topLine) * m_lineHeight;
int h = (lastLine - firstLine + 1 ) * m_lineHeight;
const qint64 topLine = verticalScrollBar()->value();
const qint64 firstLine = qMin(fromPosition, toPosition) / m_bytesPerLine;
const qint64 lastLine = qMax(fromPosition, toPosition) / m_bytesPerLine;
const int y = (firstLine - topLine) * m_lineHeight;
const int h = (lastLine - firstLine + 1 ) * m_lineHeight;
viewport()->update(0, y, viewport()->width(), h);
}
int BinEditorWidget::dataIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive) const
qint64 BinEditorWidget::dataIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive) const
{
int trailing = pattern.size();
qint64 trailing = pattern.size();
if (trailing > m_blockSize)
return -1;
@@ -603,7 +603,7 @@ int BinEditorWidget::dataIndexOf(const QByteArray &pattern, qint64 from, bool ca
QByteArrayMatcher matcher(pattern);
qint64 block = from / m_blockSize;
const int end = qMin<qint64>(from + SearchStride, m_size);
const qint64 end = qMin<qint64>(from + SearchStride, m_size);
while (from < end) {
if (!requestDataAt(block * m_blockSize))
return -1;
@@ -615,7 +615,7 @@ int BinEditorWidget::dataIndexOf(const QByteArray &pattern, qint64 from, bool ca
if (!caseSensitive)
buffer = buffer.toLower();
int pos = matcher.indexIn(buffer, from - (block * m_blockSize) + trailing);
qint64 pos = matcher.indexIn(buffer, from - (block * m_blockSize) + trailing);
if (pos >= 0)
return pos + block * m_blockSize - trailing;
++block;
@@ -624,9 +624,9 @@ int BinEditorWidget::dataIndexOf(const QByteArray &pattern, qint64 from, bool ca
return end == m_size ? -1 : -2;
}
int BinEditorWidget::dataLastIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive) const
qint64 BinEditorWidget::dataLastIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive) const
{
int trailing = pattern.size();
qint64 trailing = pattern.size();
if (trailing > m_blockSize)
return -1;
@@ -635,10 +635,10 @@ int BinEditorWidget::dataLastIndexOf(const QByteArray &pattern, qint64 from, boo
if (from == -1)
from = m_size;
int block = from / m_blockSize;
const int lowerBound = qMax(qint64(0), from - SearchStride);
qint64 block = from / m_blockSize;
const qint64 lowerBound = qMax<qint64>(0, from - SearchStride);
while (from > lowerBound) {
if (!requestDataAt(qint64(block) * m_blockSize))
if (!requestDataAt(block * m_blockSize))
return -1;
QByteArray data = blockData(block);
char *b = buffer.data();
@@ -648,18 +648,19 @@ int BinEditorWidget::dataLastIndexOf(const QByteArray &pattern, qint64 from, boo
if (!caseSensitive)
buffer = buffer.toLower();
int pos = buffer.lastIndexOf(pattern, from - (block * m_blockSize));
qint64 pos = buffer.lastIndexOf(pattern, from - (block * m_blockSize));
if (pos >= 0)
return pos + block * m_blockSize;
--block;
from = qint64(block) * m_blockSize + (m_blockSize-1) + trailing;
from = block * m_blockSize + (m_blockSize-1) + trailing;
}
return lowerBound == 0 ? -1 : -2;
}
int BinEditorWidget::find(const QByteArray &pattern_arg, qint64 from,
QTextDocument::FindFlags findFlags)
qint64 BinEditorWidget::find(const QByteArray &pattern_arg,
qint64 from,
QTextDocument::FindFlags findFlags)
{
if (pattern_arg.isEmpty())
return 0;
@@ -672,14 +673,14 @@ int BinEditorWidget::find(const QByteArray &pattern_arg, qint64 from,
pattern = pattern.toLower();
bool backwards = (findFlags & QTextDocument::FindBackward);
int found = backwards ? dataLastIndexOf(pattern, from, caseSensitiveSearch)
: dataIndexOf(pattern, from, caseSensitiveSearch);
qint64 found = backwards ? dataLastIndexOf(pattern, from, caseSensitiveSearch)
: dataIndexOf(pattern, from, caseSensitiveSearch);
int foundHex = -1;
qint64 foundHex = -1;
QByteArray hexPattern = calculateHexPattern(pattern_arg);
if (!hexPattern.isEmpty()) {
foundHex = backwards ? dataLastIndexOf(hexPattern, from)
: dataIndexOf(hexPattern, from);
: dataIndexOf(hexPattern, from);
}
qint64 pos = foundHex == -1 || (found >= 0 && (foundHex == -2 || found < foundHex))
@@ -695,15 +696,16 @@ int BinEditorWidget::find(const QByteArray &pattern_arg, qint64 from,
return pos;
}
int BinEditorWidget::findPattern(const QByteArray &data, const QByteArray &dataHex,
int from, int offset, int *match)
qint64 BinEditorWidget::findPattern(const QByteArray &data, const QByteArray &dataHex,
qint64 from, qint64 offset, qint64 *match)
{
if (m_searchPattern.isEmpty())
return -1;
int normal = m_searchPattern.isEmpty()
? -1 : data.indexOf(m_searchPattern, from - offset);
int hex = m_searchPatternHex.isEmpty()
? -1 : dataHex.indexOf(m_searchPatternHex, from - offset);
qint64 normal = m_searchPattern.isEmpty()
? -1 : data.indexOf(m_searchPattern, from - offset);
qint64 hex = m_searchPatternHex.isEmpty()
? -1 : dataHex.indexOf(m_searchPatternHex, from - offset);
if (normal >= 0 && (hex < 0 || normal < hex)) {
if (match)
@@ -787,10 +789,10 @@ void BinEditorWidget::paintEvent(QPaintEvent *e)
painter.fillRect(e->rect() & r, palette().alternateBase());
}
int matchLength = 0;
qint64 matchLength = 0;
QByteArray patternData, patternDataHex;
int patternOffset = qMax(0, topLine*m_bytesPerLine - m_searchPattern.size());
qint64 patternOffset = qMax(0, topLine * m_bytesPerLine - m_searchPattern.size());
if (!m_searchPattern.isEmpty()) {
patternData = dataMid(patternOffset, m_numVisibleLines * m_bytesPerLine + (topLine*m_bytesPerLine - patternOffset));
patternDataHex = patternData;
@@ -798,9 +800,9 @@ void BinEditorWidget::paintEvent(QPaintEvent *e)
patternData = patternData.toLower();
}
int foundPatternAt = findPattern(patternData, patternDataHex, patternOffset, patternOffset, &matchLength);
qint64 foundPatternAt = findPattern(patternData, patternDataHex, patternOffset, patternOffset, &matchLength);
int selStart, selEnd;
qint64 selStart, selEnd;
if (m_cursorPosition >= m_anchorPosition) {
selStart = m_anchorPosition;
selEnd = m_cursorPosition;
@@ -817,13 +819,13 @@ void BinEditorWidget::paintEvent(QPaintEvent *e)
painter.setPen(palette().text().color());
const QFontMetrics &fm = painter.fontMetrics();
for (int i = 0; i <= m_numVisibleLines; ++i) {
for (qint64 i = 0; i <= m_numVisibleLines; ++i) {
qint64 line = topLine + i;
if (line >= m_numLines)
break;
const quint64 lineAddress = m_baseAddr + line * m_bytesPerLine;
int y = i * m_lineHeight + m_ascent;
qint64 y = i * m_lineHeight + m_ascent;
if (y - m_ascent > e->rect().bottom())
break;
if (y + m_descent < e->rect().top())
@@ -1006,7 +1008,7 @@ qint64 BinEditorWidget::cursorPosition() const
void BinEditorWidget::setCursorPosition(qint64 pos, MoveMode moveMode)
{
pos = qMin(m_size - 1, qMax(qint64(0), pos));
int oldCursorPosition = m_cursorPosition;
qint64 oldCursorPosition = m_cursorPosition;
m_lowNibble = false;
m_cursorPosition = pos;
@@ -1377,33 +1379,34 @@ void BinEditorWidget::keyPressEvent(QKeyEvent *e)
break;
case Qt::Key_PageUp:
case Qt::Key_PageDown: {
int line = qMax(qint64(0), m_cursorPosition / m_bytesPerLine - verticalScrollBar()->value());
qint64 line = qMax(qint64(0), m_cursorPosition / m_bytesPerLine - verticalScrollBar()->value());
verticalScrollBar()->triggerAction(e->key() == Qt::Key_PageUp ?
QScrollBar::SliderPageStepSub : QScrollBar::SliderPageStepAdd);
if (!ctrlPressed)
setCursorPosition((verticalScrollBar()->value() + line) * m_bytesPerLine + m_cursorPosition % m_bytesPerLine, moveMode);
} break;
break;
}
case Qt::Key_Home: {
int pos;
qint64 pos;
if (ctrlPressed)
pos = 0;
else
pos = m_cursorPosition/m_bytesPerLine * m_bytesPerLine;
pos = m_cursorPosition / m_bytesPerLine * m_bytesPerLine;
setCursorPosition(pos, moveMode);
} break;
break;
}
case Qt::Key_End: {
int pos;
qint64 pos;
if (ctrlPressed)
pos = m_size;
else
pos = m_cursorPosition/m_bytesPerLine * m_bytesPerLine + 15;
pos = m_cursorPosition / m_bytesPerLine * m_bytesPerLine + 15;
setCursorPosition(pos, moveMode);
} break;
default:
break;
}
default: {
if (m_readOnly)
break;
{
QString text = e->text();
for (int i = 0; i < text.length(); ++i) {
QChar c = text.at(i);
@@ -1461,9 +1464,9 @@ void BinEditorWidget::zoomF(float delta)
void BinEditorWidget::copy(bool raw)
{
int selStart = selectionStart();
int selEnd = selectionEnd();
const int selectionLength = selEnd - selStart + 1;
const qint64 selStart = selectionStart();
const qint64 selEnd = selectionEnd();
const qint64 selectionLength = selEnd - selStart + 1;
if (selectionLength >> 22) {
QMessageBox::warning(this, Tr::tr("Copying Failed"),
Tr::tr("You cannot copy more than 4 MB of binary data."));
@@ -1479,7 +1482,7 @@ void BinEditorWidget::copy(bool raw)
QString hexString;
const char * const hex = "0123456789abcdef";
hexString.reserve(3 * data.size());
for (int i = 0; i < data.size(); ++i) {
for (qint64 i = 0; i < data.size(); ++i) {
const uchar val = static_cast<uchar>(data[i]);
hexString.append(QLatin1Char(hex[val >> 4])).append(QLatin1Char(hex[val & 0xf])).append(QLatin1Char(' '));
}
@@ -1499,7 +1502,7 @@ void BinEditorWidget::highlightSearchResults(const QByteArray &pattern, QTextDoc
viewport()->update();
}
void BinEditorWidget::changeData(int position, uchar character, bool highNibble)
void BinEditorWidget::changeData(qint64 position, uchar character, bool highNibble)
{
if (!requestDataAt(position))
return;
@@ -1572,8 +1575,8 @@ void BinEditorWidget::redo()
void BinEditorWidget::contextMenuEvent(QContextMenuEvent *event)
{
const int selStart = selectionStart();
const int byteCount = selectionEnd() - selStart + 1;
const qint64 selStart = selectionStart();
const qint64 byteCount = selectionEnd() - selStart + 1;
QPointer<QMenu> contextMenu(new QMenu(this));
@@ -1717,12 +1720,12 @@ void BinEditorWidget::asDouble(qint64 offset, double &value, bool old) const
value = *f;
}
void BinEditorWidget::asIntegers(qint64 offset, int count, quint64 &bigEndianValue,
quint64 &littleEndianValue, bool old) const
void BinEditorWidget::asIntegers(qint64 offset, qint64 count, quint64 &bigEndianValue,
quint64 &littleEndianValue, bool old) const
{
bigEndianValue = littleEndianValue = 0;
const QByteArray &data = dataMid(offset, count, old);
for (int pos = 0; pos < data.size(); ++pos) {
for (qint64 pos = 0; pos < data.size(); ++pos) {
const quint64 val = static_cast<quint64>(data.at(pos)) & 0xff;
littleEndianValue += val << (pos * 8);
bigEndianValue += val << ((count - pos - 1) * 8);

View File

@@ -79,7 +79,7 @@ public:
void setReadOnly(bool);
bool isReadOnly() const;
int find(const QByteArray &pattern, qint64 from = 0, QTextDocument::FindFlags findFlags = {});
qint64 find(const QByteArray &pattern, qint64 from = 0, QTextDocument::FindFlags findFlags = {});
void selectAll();
void clear();
@@ -90,8 +90,8 @@ public:
Core::IEditor *editor() const { return m_ieditor; }
void setEditor(Core::IEditor *ieditor) { m_ieditor = ieditor; }
int selectionStart() const { return qMin(m_anchorPosition, m_cursorPosition); }
int selectionEnd() const { return qMax(m_anchorPosition, m_cursorPosition); }
qint64 selectionStart() const { return qMin(m_anchorPosition, m_cursorPosition); }
qint64 selectionEnd() const { return qMax(m_anchorPosition, m_cursorPosition); }
bool event(QEvent*) override;
@@ -146,20 +146,20 @@ private:
QByteArray m_lowerBlock;
qint64 m_size = 0;
int dataIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive = true) const;
int dataLastIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive = true) const;
qint64 dataIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive = true) const;
qint64 dataLastIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive = true) const;
bool requestDataAt(qint64 pos) const;
bool requestOldDataAt(qint64 pos) const;
char dataAt(qint64 pos, bool old = false) const;
char oldDataAt(qint64 pos) const;
void changeDataAt(qint64 pos, char c);
QByteArray dataMid(qint64 from, int length, bool old = false) const;
QByteArray dataMid(qint64 from, qint64 length, bool old = false) const;
QByteArray blockData(qint64 block, bool old = false) const;
QPoint offsetToPos(qint64 offset) const;
void asIntegers(qint64 offset, int count, quint64 &bigEndianValue, quint64 &littleEndianValue,
bool old = false) const;
void asIntegers(qint64 offset, qint64 count, quint64 &bigEndianValue, quint64 &littleEndianValue,
bool old = false) const;
void asFloat(qint64 offset, float &value, bool old) const;
void asDouble(qint64 offset, double &value, bool old) const;
QString toolTip(const QHelpEvent *helpEvent) const;
@@ -198,14 +198,14 @@ private:
bool inTextArea(const QPoint &pos) const;
QRect cursorRect() const;
void updateLines();
void updateLines(int fromPosition, int toPosition);
void updateLines(qint64 fromPosition, qint64 toPosition);
void ensureCursorVisible();
void setBlinkingCursorEnabled(bool enable);
void changeData(int position, uchar character, bool highNibble = false);
void changeData(qint64 position, uchar character, bool highNibble = false);
int findPattern(const QByteArray &data, const QByteArray &dataHex,
int from, int offset, int *match);
qint64 findPattern(const QByteArray &data, const QByteArray &dataHex,
qint64 from, qint64 offset, qint64 *match);
void drawItems(QPainter *painter, int x, int y, const QString &itemString);
void drawChanges(QPainter *painter, int x, int y, const char *changes);

View File

@@ -849,7 +849,7 @@ clang::format::FormatStyle ClangFormatBaseIndenterPrivate::customSettingsStyle(
return currentQtStyle(preferences);
clang::format::FormatStyle currentSettingsStyle;
bool success = parseConfigurationFile(filePath, currentSettingsStyle);
const Utils::expected_str<void> success = parseConfigurationFile(filePath, currentSettingsStyle);
QTC_ASSERT(success, return currentQtStyle(preferences));
return currentSettingsStyle;

View File

@@ -37,6 +37,8 @@
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <utils/infolabel.h>
#include <utils/expected.h>
#include <QComboBox>
#include <QLabel>
@@ -105,8 +107,7 @@ private:
Guard m_ignoreChanges;
QLabel *m_clangVersion;
QLabel *m_clangFileIsCorrectText;
QLabel *m_clangFileIsCorrectIcon;
InfoLabel *m_clangFileIsCorrectText;
ClangFormatIndenter *m_indenter;
};
@@ -137,7 +138,7 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc
Column {
m_clangVersion,
Row { m_editorScrollArea, m_preview },
Row {m_clangFileIsCorrectIcon, m_clangFileIsCorrectText, st}
Row {m_clangFileIsCorrectText, st}
}.attachTo(this);
connect(codeStyle, &TextEditor::ICodeStylePreferences::currentPreferencesChanged,
@@ -194,46 +195,28 @@ void ClangFormatConfigWidget::initEditor(TextEditor::ICodeStylePreferences *code
m_editorScrollArea->setWidget(m_editor->widget());
m_editorScrollArea->setWidgetResizable(true);
m_clangFileIsCorrectText = new QLabel(Tr::tr("Clang-Format is configured correctly."));
QPalette paletteCorrect = m_clangFileIsCorrectText->palette();
paletteCorrect.setColor(QPalette::WindowText, Qt::darkGreen);
m_clangFileIsCorrectText->setPalette(paletteCorrect);
m_clangFileIsCorrectIcon = new QLabel(this);
m_clangFileIsCorrectIcon->setPixmap(Icons::OK.icon().pixmap(16, 16));
m_clangFileIsCorrectText = new InfoLabel("", Utils::InfoLabel::Ok);
m_clangFileIsCorrectText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_clangFileIsCorrectText->hide();
m_clangVersion = new QLabel(Tr::tr("Current ClangFormat version: %1.").arg(LLVM_VERSION_STRING),
this);
connect(m_editor->document(), &TextEditor::TextDocument::contentsChanged, this, [this] {
clang::format::FormatStyle currentSettingsStyle;
const bool success
const Utils::expected_str<void> success
= parseConfigurationContent(m_editor->document()->contents().toStdString(),
currentSettingsStyle);
QString text;
Qt::GlobalColor currentColor;
QPixmap pixmap;
if (success) {
text = Tr::tr("Clang-Format is configured correctly.");
currentColor = Qt::darkGreen;
pixmap = Icons::OK.icon().pixmap(16, 16);
} else {
text = Tr::tr("Clang-Format is not configured correctly.");
currentColor = Qt::red;
pixmap = Icons::WARNING.icon().pixmap(16, 16);
}
m_clangFileIsCorrectText->setText(text);
QPalette paletteCorrect = m_clangFileIsCorrectText->palette();
paletteCorrect.setColor(QPalette::WindowText, currentColor);
m_clangFileIsCorrectText->setPalette(paletteCorrect);
m_clangFileIsCorrectIcon->setPixmap(pixmap);
if (!success)
m_clangFileIsCorrectText->hide();
m_indenter->setOverriddenStyle(currentSettingsStyle);
updatePreview();
return;
m_indenter->setOverriddenStyle(currentSettingsStyle);
updatePreview();
}
m_clangFileIsCorrectText->show();
m_clangFileIsCorrectText->setText(Tr::tr("Warning: ") + success.error());
m_clangFileIsCorrectText->setType(Utils::InfoLabel::Warning);
});
QShortcut *completionSC = new QShortcut(QKeySequence("Ctrl+Space"), this);
@@ -347,8 +330,9 @@ void ClangFormatConfigWidget::apply()
return;
clang::format::FormatStyle currentSettingsStyle;
const bool success = parseConfigurationContent(m_editor->document()->contents().toStdString(),
currentSettingsStyle);
const Utils::expected_str<void> success
= parseConfigurationContent(m_editor->document()->contents().toStdString(),
currentSettingsStyle);
auto saveSettings = [this] {
QString errorString;

View File

@@ -21,6 +21,7 @@
#include <texteditor/texteditorsettings.h>
#include <utils/guard.h>
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <QCheckBox>
@@ -68,7 +69,7 @@ private:
QCheckBox *m_formatOnSave;
QCheckBox *m_useCustomSettingsCheckBox;
QCheckBox *m_useGlobalSettings;
QLabel *m_currentProjectLabel;
InfoLabel *m_currentProjectLabel;
};
ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(ICodeStylePreferences *codeStyle,
@@ -95,10 +96,10 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(ICodeStylePreferenc
m_useGlobalSettings->hide();
m_useCustomSettings = ClangFormatSettings::instance().useCustomSettings();
m_currentProjectLabel = new QLabel(
m_currentProjectLabel = new Utils::InfoLabel(
Tr::tr("Please note that the current project includes a .clang-format file, which will be "
"used for code indenting and formatting."));
m_currentProjectLabel->setStyleSheet("QLabel { color : red; }");
"used for code indenting and formatting."),
Utils::InfoLabel::Warning);
m_currentProjectLabel->setWordWrap(true);
using namespace Layouting;

View File

@@ -19,6 +19,7 @@
#include <projectexplorer/projectmanager.h>
#include <utils/qtcassert.h>
#include <utils/expected.h>
#include <QCryptographicHash>
@@ -417,15 +418,30 @@ Utils::FilePath filePathToCurrentSettings(const TextEditor::ICodeStylePreference
/ QLatin1String(Constants::SETTINGS_FILE_NAME);
}
bool parseConfigurationContent(const std::string &fileContent, clang::format::FormatStyle &style)
static QString s_errorMessage;
Utils::expected_str<void> parseConfigurationContent(const std::string &fileContent,
clang::format::FormatStyle &style)
{
style.Language = clang::format::FormatStyle::LK_Cpp;
const std::error_code error = parseConfiguration(fileContent, &style);
auto diagHandler = [](const llvm::SMDiagnostic &diag, void * /*context*/) {
s_errorMessage = QString::fromStdString(diag.getMessage().str()) + " "
+ QString::number(diag.getLineNo()) + ":"
+ QString::number(diag.getColumnNo());
};
return error.value() == static_cast<int>(ParseError::Success);
style.Language = clang::format::FormatStyle::LK_Cpp;
const std::error_code error = parseConfiguration(llvm::MemoryBufferRef(fileContent, "YAML"),
&style,
false,
diagHandler,
nullptr);
if (error)
return make_unexpected(s_errorMessage);
return {};
}
bool parseConfigurationFile(const Utils::FilePath &filePath, clang::format::FormatStyle &style)
Utils::expected_str<void> parseConfigurationFile(const Utils::FilePath &filePath,
clang::format::FormatStyle &style)
{
return parseConfigurationContent(filePath.fileContents().value_or(QByteArray()).toStdString(),
style);

View File

@@ -48,7 +48,9 @@ clang::format::FormatStyle currentQtStyle(const TextEditor::ICodeStylePreference
Utils::FilePath filePathToCurrentSettings(const TextEditor::ICodeStylePreferences *codeStyle);
bool parseConfigurationContent(const std::string &fileContent, clang::format::FormatStyle &style);
bool parseConfigurationFile(const Utils::FilePath &filePath, clang::format::FormatStyle &style);
Utils::expected_str<void> parseConfigurationContent(const std::string &fileContent,
clang::format::FormatStyle &style);
Utils::expected_str<void> parseConfigurationFile(const Utils::FilePath &filePath,
clang::format::FormatStyle &style);
} // ClangFormat

View File

@@ -11,7 +11,6 @@ namespace Constants {
const char VCS_ID_CLEARCASE[] = "E.ClearCase";
const char CLEARCASE_SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.clearcase.submit";
const char CLEARCASECHECKINEDITOR_ID[] = "ClearCase Check In Editor";
const char CLEARCASECHECKINEDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::VcsBase", "ClearCase Check In Editor");
const char TASK_INDEX[] = "ClearCase.Task.Index";
const char KEEP_ACTIVITY[] = "__KEEP__";
enum { debug = 0 };

View File

@@ -43,6 +43,7 @@
#include <vcsbase/vcsoutputwindow.h>
#include <vcsbase/vcsbasesubmiteditor.h>
#include <vcsbase/vcsbaseplugin.h>
#include <vcsbase/vcsbasetr.h>
#include <vcsbase/vcscommand.h>
#include <QAbstractButton>
@@ -99,26 +100,9 @@ const char CMD_ID_UPDATE_VIEW[] = "ClearCase.UpdateView";
const char CMD_ID_CHECKIN_ALL[] = "ClearCase.CheckInAll";
const char CMD_ID_STATUS[] = "ClearCase.Status";
const VcsBaseEditorParameters logEditorParameters {
LogOutput,
"ClearCase File Log Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "ClearCase File Log Editor"), // display_name
"text/vnd.qtcreator.clearcase.log"
};
const VcsBaseEditorParameters annotateEditorParameters {
AnnotateOutput,
"ClearCase Annotation Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "ClearCase Annotation Editor"), // display_name
"text/vnd.qtcreator.clearcase.annotation"
};
const VcsBaseEditorParameters diffEditorParameters {
DiffOutput,
"ClearCase Diff Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "ClearCase Diff Editor"), // display_name
"text/x-patch"
};
const char LOG_EDITOR_ID[] = "ClearCase File Log Editor";
const char ANNOTATION_EDITOR_ID[] = "ClearCase Annotation Editor";
const char DIFF_EDITOR_ID[] = "ClearCase Diff Editor";
static QString debugCodec(const QTextCodec *c)
{
@@ -311,23 +295,32 @@ public:
ClearCaseSettingsPage m_settingsPage;
VcsEditorFactory logEditorFactory {
&logEditorParameters,
VcsEditorFactory logEditorFactory {{
LogOutput,
LOG_EDITOR_ID,
VcsBase::Tr::tr("ClearCase File Log Editor"), // display_name
"text/vnd.qtcreator.clearcase.log",
[] { return new ClearCaseEditorWidget; },
std::bind(&ClearCasePluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory annotateEditorFactory {
&annotateEditorParameters,
VcsEditorFactory annotateEditorFactory {{
AnnotateOutput,
ANNOTATION_EDITOR_ID,
VcsBase::Tr::tr("ClearCase Annotation Editor"), // display_name
"text/vnd.qtcreator.clearcase.annotation",
[] { return new ClearCaseEditorWidget; },
std::bind(&ClearCasePluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory diffEditorFactory {
&diffEditorParameters,
VcsEditorFactory diffEditorFactory {{
DiffOutput,
DIFF_EDITOR_ID,
VcsBase::Tr::tr("ClearCase Diff Editor"), // display_name
"text/x-patch",
[] { return new ClearCaseEditorWidget; },
std::bind(&ClearCasePluginPrivate::vcsDescribe, this, _1, _2)
};
}};
#ifdef WITH_TESTS
bool m_fakeClearTool = false;
@@ -740,7 +733,7 @@ ClearCasePluginPrivate::ClearCasePluginPrivate()
setupVcsSubmitEditor(this, {
Constants::CLEARCASE_SUBMIT_MIMETYPE,
Constants::CLEARCASECHECKINEDITOR_ID,
Constants::CLEARCASECHECKINEDITOR_DISPLAY_NAME,
VcsBase::Tr::tr("ClearCase Check In Editor"),
VcsBaseSubmitEditorParameters::DiffFiles,
[] { return new ClearCaseSubmitEditor; }
});
@@ -1218,7 +1211,7 @@ void ClearCasePluginPrivate::ccDiffWithPred(const FilePath &workingDir, const QS
diffname = QDir::toNativeSeparators(files.first());
}
const QString title = QString::fromLatin1("cc diff %1").arg(diffname);
IEditor *editor = showOutputInEditor(title, result, diffEditorParameters.id, source, codec);
IEditor *editor = showOutputInEditor(title, result, DIFF_EDITOR_ID, source, codec);
setWorkingDirectory(editor, workingDir);
VcsBaseEditor::tagEditor(editor, tag);
auto diffEditorWidget = qobject_cast<ClearCaseEditorWidget *>(editor->widget());
@@ -1299,7 +1292,7 @@ void ClearCasePluginPrivate::diffActivity()
}
m_diffPrefix.clear();
const QString title = QString::fromLatin1("%1.patch").arg(activity);
IEditor *editor = showOutputInEditor(title, result, diffEditorParameters.id,
IEditor *editor = showOutputInEditor(title, result, DIFF_EDITOR_ID,
FilePath::fromString(activity), nullptr);
setWorkingDirectory(editor, topLevel);
}
@@ -1461,7 +1454,7 @@ void ClearCasePluginPrivate::history(const FilePath &workingDir,
const QString title = QString::fromLatin1("cc history %1").arg(id);
const FilePath source = VcsBaseEditor::getSource(workingDir, files);
IEditor *newEditor = showOutputInEditor(title, result.cleanedStdOut(),
logEditorParameters.id, source, codec);
LOG_EDITOR_ID, source, codec);
VcsBaseEditor::tagEditor(newEditor, tag);
if (enableAnnotationContextMenu)
VcsBaseEditor::getVcsBaseEditor(newEditor)->setFileLogAnnotateEnabled(true);
@@ -1564,7 +1557,7 @@ void ClearCasePluginPrivate::vcsAnnotateHelper(const FilePath &workingDir, const
EditorManager::activateEditor(editor);
} else {
const QString title = QString::fromLatin1("cc annotate %1").arg(id);
IEditor *newEditor = showOutputInEditor(title, res, annotateEditorParameters.id, source, codec);
IEditor *newEditor = showOutputInEditor(title, res, ANNOTATION_EDITOR_ID, source, codec);
VcsBaseEditor::tagEditor(newEditor, tag);
VcsBaseEditor::gotoLineOfEditor(newEditor, lineNumber);
}
@@ -1598,7 +1591,7 @@ void ClearCasePluginPrivate::vcsDescribe(const FilePath &source, const QString &
EditorManager::activateEditor(editor);
} else {
const QString title = QString::fromLatin1("cc describe %1").arg(id);
IEditor *newEditor = showOutputInEditor(title, description, diffEditorParameters.id, source, codec);
IEditor *newEditor = showOutputInEditor(title, description, DIFF_EDITOR_ID, source, codec);
VcsBaseEditor::tagEditor(newEditor, tag);
}
}

View File

@@ -89,6 +89,21 @@ PresetsData CMakeProject::presetsData() const
return m_presetsData;
}
template<typename T>
static QStringList recursiveInheritsList(const T &presetsHash, const QStringList &inheritsList)
{
QStringList result;
for (const QString &inheritFrom : inheritsList) {
result << inheritFrom;
if (presetsHash.contains(inheritFrom)) {
auto item = presetsHash[inheritFrom];
if (item.inherits)
result << recursiveInheritsList(presetsHash, item.inherits.value());
}
}
return result;
}
Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakePresetsData,
Internal::PresetsData &cmakeUserPresetsData)
{
@@ -135,12 +150,14 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP
if (!p.inherits)
continue;
for (const QString &inheritFromName : p.inherits.value()) {
if (presetsHash.contains(inheritFromName)) {
p.inheritFrom(presetsHash[inheritFromName]);
const QStringList inheritsList = recursiveInheritsList(presetsHash,
p.inherits.value());
Utils::reverseForeach(inheritsList, [&presetsHash, &p](const QString &inheritFrom) {
if (presetsHash.contains(inheritFrom)) {
p.inheritFrom(presetsHash[inheritFrom]);
presetsHash[p.name] = p;
}
}
});
}
};

View File

@@ -147,20 +147,31 @@ static QString displayPresetName(const QString &presetName)
FilePaths CMakeProjectImporter::importCandidates()
{
FilePaths candidates;
FilePaths candidates = presetCandidates();
candidates << scanDirectory(projectFilePath().absolutePath(), "build");
if (candidates.isEmpty()) {
candidates << scanDirectory(projectFilePath().absolutePath(), "build");
const QList<Kit *> kits = KitManager::kits();
for (const Kit *k : kits) {
FilePath shadowBuildDirectory
= CMakeBuildConfiguration::shadowBuildDirectory(projectFilePath(),
k,
QString(),
BuildConfiguration::Unknown);
candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString());
const QList<Kit *> kits = KitManager::kits();
for (const Kit *k : kits) {
FilePath shadowBuildDirectory
= CMakeBuildConfiguration::shadowBuildDirectory(projectFilePath(),
k,
QString(),
BuildConfiguration::Unknown);
candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString());
}
}
const FilePaths finalists = Utils::filteredUnique(candidates);
qCInfo(cmInputLog) << "import candidates:" << finalists;
return finalists;
}
FilePaths CMakeProjectImporter::presetCandidates()
{
FilePaths candidates;
for (const auto &configPreset : m_project->presetsData().configurePresets) {
if (configPreset.hidden.value())
continue;
@@ -190,9 +201,9 @@ FilePaths CMakeProjectImporter::importCandidates()
}
}
const FilePaths finalists = Utils::filteredUnique(candidates);
qCInfo(cmInputLog) << "import candidates:" << finalists;
return finalists;
m_hasCMakePresets = !candidates.isEmpty();
return candidates;
}
Target *CMakeProjectImporter::preferredTarget(const QList<Target *> &possibleTargets)
@@ -210,6 +221,22 @@ Target *CMakeProjectImporter::preferredTarget(const QList<Target *> &possibleTar
return ProjectImporter::preferredTarget(possibleTargets);
}
bool CMakeProjectImporter::filter(ProjectExplorer::Kit *k) const
{
if (!m_hasCMakePresets)
return true;
const auto presetConfigItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
if (presetConfigItem.isNull())
return false;
const QString presetName = presetConfigItem.expandedValue(k);
return std::find_if(m_project->presetsData().configurePresets.cbegin(),
m_project->presetsData().configurePresets.cend(),
[&presetName](const auto &preset) { return presetName == preset.name; })
!= m_project->presetsData().configurePresets.cend();
}
static CMakeConfig configurationFromPresetProbe(
const FilePath &importPath,
const FilePath &sourceDirectory,

View File

@@ -24,7 +24,9 @@ public:
Utils::FilePaths importCandidates() final;
ProjectExplorer::Target *preferredTarget(const QList<ProjectExplorer::Target *> &possibleTargets) final;
bool filter(ProjectExplorer::Kit *k) const final;
Utils::FilePaths presetCandidates();
private:
QList<void *> examineDirectory(const Utils::FilePath &importPath,
QString *warningMessage) const final;
@@ -47,6 +49,7 @@ private:
const CMakeProject *m_project;
Utils::TemporaryDirectory m_presetsTempDir;
bool m_hasCMakePresets = false;
};
#ifdef WITH_TESTS

View File

@@ -14,44 +14,40 @@
#include <QRegularExpression>
#include <QStringList>
/*!
\class Core::IVersionControl::TopicCache
\inheaderfile coreplugin/iversioncontrol.h
\inmodule QtCreator
\brief The TopicCache class stores a cache which maps a directory to a topic.
A VCS topic is typically the current active branch name, but it can also have other
values (for example the latest tag) when there is no active branch.
It is displayed:
\list
\li In the project tree, next to each root project - corresponding to the project.
\li In the main window title - corresponding to the current active editor.
\endlist
In order to enable topic display, an IVersionControl subclass needs to create
an instance of the TopicCache subclass with appropriate overrides for its
pure virtual functions, and pass this instance to IVersionControl's constructor.
The cache tracks a file in the repository, which is expected to change when the
topic changes. When the file is modified, the cache is refreshed.
For example: for Git this file is typically <repository>/.git/HEAD
*/
/*!
\fn Utils::FilePath Core::IVersionControl::TopicCache::trackFile(const Utils::FilePath &repository)
Returns the path to the file that invalidates the cache for \a repository when
the file is modified.
\fn QString Core::IVersionControl::TopicCache::refreshTopic(const Utils::FilePath &repository)
Returns the current topic for \a repository.
*/
using namespace Utils;
namespace Core {
namespace Internal {
class TopicData
{
public:
QDateTime timeStamp;
QString topic;
};
class IVersionControlPrivate
{
public:
IVersionControl::FileTracker m_fileTracker;
IVersionControl::TopicRefresher m_topicRefresher;
QHash<FilePath, TopicData> m_topicCache;
};
} // Internal
IVersionControl::IVersionControl()
: d(new Internal::IVersionControlPrivate)
{
Core::VcsManager::addVersionControl(this);
}
IVersionControl::~IVersionControl()
{
delete d;
}
QString IVersionControl::vcsOpenText() const
{
return Tr::tr("Open with VCS (%1)").arg(displayName());
@@ -111,24 +107,83 @@ IVersionControl::RepoUrl IVersionControl::getRepoUrl(const QString &location) co
return RepoUrl(location);
}
void IVersionControl::setTopicCache(TopicCache *topicCache)
FilePath IVersionControl::trackFile(const FilePath &repository)
{
m_topicCache = topicCache;
QTC_ASSERT(d->m_fileTracker, return {});
return d->m_fileTracker(repository);
}
QString IVersionControl::refreshTopic(const FilePath &repository)
{
QTC_ASSERT(d->m_topicRefresher, return {});
return d->m_topicRefresher(repository);
}
/*!
Returns the topic for repository under \a topLevel.
A VCS topic is typically the current active branch name, but it can also have other
values (for example the latest tag) when there is no active branch.
It is displayed:
\list
\li In the project tree, next to each root project - corresponding to the project.
\li In the main window title - corresponding to the current active editor.
\endlist
In order to enable topic display, an IVersionControl subclass needs to create
an instance of the TopicCache subclass with appropriate overrides for its
pure virtual functions, and pass this instance to IVersionControl's constructor.
The cache tracks a file in the repository, which is expected to change when the
topic changes. When the file is modified, the cache is refreshed.
For example: for Git this file is typically <repository>/.git/HEAD
The base implementation features a cache. If the cache for \a topLevel is valid,
it will be used. Otherwise it will be refreshed using the items provided by
\c setTopicFileTracker() and \c setTopicRefresher().
\sa setTopicFileTracker(), setTopicRefresher().
*/
QString IVersionControl::vcsTopic(const FilePath &topLevel)
{
return m_topicCache ? m_topicCache->topic(topLevel) : QString();
QTC_ASSERT(!topLevel.isEmpty(), return QString());
Internal::TopicData &data = d->m_topicCache[topLevel];
const FilePath file = trackFile(topLevel);
if (file.isEmpty())
return QString();
const QDateTime lastModified = file.lastModified();
if (lastModified == data.timeStamp)
return data.topic;
data.timeStamp = lastModified;
return data.topic = refreshTopic(topLevel);
}
IVersionControl::IVersionControl()
/*!
Provides the \a fileTracker function object for use in \c vscTopic() cache handling.
The parameter object takes a repository as input and returns the file
that should trigger topic refresh (e.g. .git/HEAD for Git).
Modification of this file will invalidate the internal topic cache for the repository.
*/
void IVersionControl::setTopicFileTracker(const FileTracker &fileTracker)
{
Core::VcsManager::addVersionControl(this);
d->m_fileTracker = fileTracker;
}
IVersionControl::~IVersionControl()
/*!
Provides the \a topicRefresher function object for use in \c vscTopic() cache handling.
The parameter object takes a repository as input and returns its current topic.
*/
void IVersionControl::setTopicRefresher(const TopicRefresher &topicRefresher)
{
delete m_topicCache;
d->m_topicRefresher = topicRefresher;
}
FilePaths IVersionControl::unmanagedFiles(const FilePaths &filePaths) const
@@ -143,29 +198,6 @@ IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const FilePath
Q_UNUSED(filePath)
return NoOpen;
}
IVersionControl::TopicCache::~TopicCache() = default;
/*!
Returns the topic for repository under \a topLevel.
If the cache for \a topLevel is valid, it will be used. Otherwise it will be refreshed.
*/
QString IVersionControl::TopicCache::topic(const FilePath &topLevel)
{
QTC_ASSERT(!topLevel.isEmpty(), return QString());
TopicData &data = m_cache[topLevel];
const FilePath file = trackFile(topLevel);
if (file.isEmpty())
return QString();
const QDateTime lastModified = file.lastModified();
if (lastModified == data.timeStamp)
return data.topic;
data.timeStamp = lastModified;
return data.topic = refreshTopic(topLevel);
}
void IVersionControl::fillLinkContextMenu(QMenu *, const FilePath &, const QString &)
{
}

View File

@@ -8,16 +8,15 @@
#include <utils/id.h>
#include <utils/filepath.h>
#include <QDateTime>
#include <QFlags>
#include <QHash>
#include <QObject>
#include <QString>
QT_FORWARD_DECLARE_CLASS(QMenu);
namespace Core {
namespace Internal { class IVersionControlPrivate; }
class CORE_EXPORT IVersionControl : public QObject
{
Q_OBJECT
@@ -43,28 +42,6 @@ public:
OpenMandatory /*!< Files must always be opened by the VCS */
};
class CORE_EXPORT TopicCache
{
public:
virtual ~TopicCache();
QString topic(const Utils::FilePath &topLevel);
protected:
virtual Utils::FilePath trackFile(const Utils::FilePath &repository) = 0;
virtual QString refreshTopic(const Utils::FilePath &repository) = 0;
private:
class TopicData
{
public:
QDateTime timeStamp;
QString topic;
};
QHash<Utils::FilePath, TopicData> m_cache;
};
IVersionControl();
~IVersionControl() override;
@@ -218,7 +195,14 @@ public:
};
virtual RepoUrl getRepoUrl(const QString &location) const;
void setTopicCache(TopicCache *topicCache);
// Topic cache
using FileTracker = std::function<Utils::FilePath(const Utils::FilePath &)>;
Utils::FilePath trackFile(const Utils::FilePath &repository);
void setTopicFileTracker(const FileTracker &fileTracker);
using TopicRefresher = std::function<QString(const Utils::FilePath &)>;
QString refreshTopic(const Utils::FilePath &repository);
void setTopicRefresher(const TopicRefresher &topicRefresher);
signals:
void repositoryChanged(const Utils::FilePath &repository);
@@ -226,7 +210,7 @@ signals:
void configurationChanged();
private:
TopicCache *m_topicCache = nullptr;
Internal::IVersionControlPrivate *d;
};
} // namespace Core

View File

@@ -175,7 +175,6 @@ void WelcomePageButton::setSize(Size size)
const int hMargin = size == SizeSmall ? 12 : 26;
const int vMargin = size == SizeSmall ? 2 : 4;
d->m_layout->setContentsMargins(hMargin, vMargin, hMargin, vMargin);
d->m_label->setFont(size == SizeSmall ? font() : StyleHelper::uiFont(StyleHelper::UiElementH2));
}
void WelcomePageButton::setWithAccentColor(bool withAccent)

View File

@@ -87,7 +87,6 @@ SearchBox::SearchBox(QWidget *parent)
m_lineEdit = new FancyLineEdit;
m_lineEdit->setFiltering(true);
m_lineEdit->setFrame(false);
m_lineEdit->setFont(StyleHelper::uiFont(StyleHelper::UiElementH2));
m_lineEdit->setMinimumHeight(33);
m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
@@ -806,7 +805,6 @@ ListModel *SectionedGridView::addSection(const Section &section, const QList<Lis
}.emerge();
m_sectionLabels.append(sectionLabel);
sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
sectionLabel->setFont(StyleHelper::uiFont(StyleHelper::UiElementH2));
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
@@ -877,7 +875,6 @@ void SectionedGridView::zoomInSection(const Section &section)
noMargin
}.emerge();
sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
sectionLabel->setFont(StyleHelper::uiFont(StyleHelper::UiElementH2));
auto gridView = new GridView(zoomedInWidget);
gridView->setItemDelegate(m_itemDelegate);

View File

@@ -34,11 +34,11 @@ public:
QByteArray editorDefines;
QString preferredProjectPartId;
bool operator==(const Configuration &other)
friend bool operator==(const Configuration &left, const Configuration &right)
{
return usePrecompiledHeaders == other.usePrecompiledHeaders
&& editorDefines == other.editorDefines
&& preferredProjectPartId == other.preferredProjectPartId;
return left.usePrecompiledHeaders == right.usePrecompiledHeaders
&& left.editorDefines == right.editorDefines
&& left.preferredProjectPartId == right.preferredProjectPartId;
}
};

View File

@@ -150,12 +150,6 @@ QWidget *CppEditorOutline::widget() const
return m_combo;
}
QSharedPointer<CPlusPlus::Document> getDocument(const Utils::FilePath &filePath)
{
const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot();
return snapshot.document(filePath);
}
void CppEditorOutline::updateNow()
{
m_combo->view()->expandAll();

View File

@@ -5,7 +5,6 @@
#include "cppoutlinemodel.h"
#include <QModelIndex>
#include <QObject>
#include <memory>
@@ -16,7 +15,6 @@ class QSortFilterProxyModel;
class QTimer;
QT_END_NAMESPACE
namespace TextEditor { class TextEditorWidget; }
namespace Utils { class TreeViewComboBox; }
namespace CppEditor {

View File

@@ -323,6 +323,10 @@ void CppEditorPlugin::addPerSymbolActions()
switchDeclDef.setTouchBarText(Tr::tr("Decl/Def", "text on macOS touch bar"));
switchDeclDef.addToContainers(menus, Constants::G_SYMBOL);
switchDeclDef.addToContainer(Core::Constants::TOUCH_BAR, Core::Constants::G_TOUCHBAR_NAVIGATION);
switchDeclDef.addOnTriggered(this, [] {
if (CppEditorWidget *editorWidget = currentCppEditorWidget())
editorWidget->switchDeclarationDefinition(/*inNextSplit*/ false);
});
ActionBuilder openDeclDefSplit(this, Constants::OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT);
openDeclDefSplit.setText(Tr::tr("Open Function Declaration/Definition in Next Split"));

View File

@@ -9,9 +9,6 @@
#include <utils/link.h>
#include <QSharedPointer>
#include <QString>
#include <functional>
namespace Core { class SearchResult; }
@@ -29,9 +26,6 @@ class RefactoringEngineInterface;
class CPPEDITOR_EXPORT ModelManagerSupport
{
public:
using Ptr = QSharedPointer<ModelManagerSupport>;
public:
virtual ~ModelManagerSupport() = 0;

View File

@@ -5008,19 +5008,19 @@ void QuickfixTest::testInsertDefFromDeclTemplateClassAndTemplateFunction()
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
" T fun@c(U u);\n"
"};\n";
QByteArray expected =
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" void fun@c();\n"
" T fun@c(U u);\n"
"};\n"
"\n"
"template<class T>\n"
"template<class U>\n"
"void Foo<T>::func()\n"
"T Foo<T>::func(U u)\n"
"{\n"
"\n"
"}\n";
@@ -5029,6 +5029,39 @@ void QuickfixTest::testInsertDefFromDeclTemplateClassAndTemplateFunction()
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
void QuickfixTest::testInsertDefFromDeclTemplateClassAndFunctionInsideNamespace()
{
QByteArray original =
"namespace N {\n"
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" T fun@c(U u);\n"
"};\n"
"}\n";
QByteArray expected =
"namespace N {\n"
"template<class T>"
"class Foo\n"
"{\n"
" template<class U>\n"
" T fun@c(U u);\n"
"};\n"
"\n"
"template<class T>\n"
"template<class U>\n"
"T Foo<T>::func(U u)\n"
"{\n"
"\n"
"}\n"
"\n"
"}\n";
InsertDefFromDecl factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
void QuickfixTest::testInsertDefFromDeclFunctionWithSignedUnsignedArgument()
{
QByteArray original;

View File

@@ -137,6 +137,7 @@ private slots:
void testInsertDefFromDeclTemplateClassWithValueParam();
void testInsertDefFromDeclTemplateFunction();
void testInsertDefFromDeclTemplateClassAndTemplateFunction();
void testInsertDefFromDeclTemplateClassAndFunctionInsideNamespace();
void testInsertDefFromDeclFunctionWithSignedUnsignedArgument();
void testInsertDefFromDeclNotTriggeredForFriendFunc();
void testInsertDefFromDeclMinimalFunctionParameterType();

View File

@@ -141,7 +141,8 @@ enum DefPos {
inline bool isQtStringLiteral(const QByteArray &id)
{
return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral"
|| id == "QByteArrayLiteral";
}
inline bool isQtStringTranslation(const QByteArray &id)
@@ -1117,21 +1118,20 @@ enum ActionFlags {
EncloseInQLatin1CharAction = 0x1,
EncloseInQLatin1StringAction = 0x2,
EncloseInQStringLiteralAction = 0x4,
EncloseActionMask = EncloseInQLatin1CharAction
| EncloseInQLatin1StringAction | EncloseInQStringLiteralAction,
TranslateTrAction = 0x8,
TranslateQCoreApplicationAction = 0x10,
TranslateNoopAction = 0x20,
TranslationMask = TranslateTrAction
| TranslateQCoreApplicationAction | TranslateNoopAction,
RemoveObjectiveCAction = 0x40,
ConvertEscapeSequencesToCharAction = 0x100,
ConvertEscapeSequencesToStringAction = 0x200,
SingleQuoteAction = 0x400,
DoubleQuoteAction = 0x800
EncloseInQByteArrayLiteralAction = 0x8,
EncloseActionMask = EncloseInQLatin1CharAction | EncloseInQLatin1StringAction
| EncloseInQStringLiteralAction | EncloseInQByteArrayLiteralAction,
TranslateTrAction = 0x10,
TranslateQCoreApplicationAction = 0x20,
TranslateNoopAction = 0x40,
TranslationMask = TranslateTrAction | TranslateQCoreApplicationAction | TranslateNoopAction,
RemoveObjectiveCAction = 0x100,
ConvertEscapeSequencesToCharAction = 0x200,
ConvertEscapeSequencesToStringAction = 0x400,
SingleQuoteAction = 0x800,
DoubleQuoteAction = 0x1000
};
/* Convert single-character string literals into character literals with some
* special cases "a" --> 'a', "'" --> '\'', "\n" --> '\n', "\"" --> '"'. */
static QByteArray stringToCharEscapeSequences(const QByteArray &content)
@@ -1167,6 +1167,8 @@ static QString stringLiteralReplacement(unsigned actions)
return QLatin1String("QLatin1String");
if (actions & EncloseInQStringLiteralAction)
return QLatin1String("QStringLiteral");
if (actions & EncloseInQByteArrayLiteralAction)
return QLatin1String("QByteArrayLiteral");
if (actions & TranslateTrAction)
return QLatin1String("tr");
if (actions & TranslateQCoreApplicationAction)
@@ -1357,6 +1359,9 @@ void WrapStringLiteral::doMatch(const CppQuickFixInterface &interface, QuickFixO
actions = EncloseInQStringLiteralAction | objectiveCActions;
result << new WrapStringLiteralOp(interface, priority, actions,
msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
actions = EncloseInQByteArrayLiteralAction | objectiveCActions;
result << new WrapStringLiteralOp(interface, priority, actions,
msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
}
}

View File

@@ -13,6 +13,7 @@
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/vcsbaseeditorconfig.h>
#include <vcsbase/vcsbaseplugin.h>
#include <vcsbase/vcsbasetr.h>
#include <vcsbase/vcscommand.h>
#include <vcsbase/vcsoutputwindow.h>
@@ -88,35 +89,11 @@ const char CMD_ID_REPOSITORYUPDATE[] = "CVS.RepositoryUpdate";
const char CVS_SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.cvs.submit";
const char CVSCOMMITEDITOR_ID[] = "CVS Commit Editor";
const char CVSCOMMITEDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::VcsBase", "CVS Commit Editor");
const VcsBaseEditorParameters commandLogEditorParameters {
OtherContent,
"CVS Command Log Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "CVS Command Log Editor"), // display name
"text/vnd.qtcreator.cvs.commandlog"
};
const VcsBaseEditorParameters logEditorParameters {
LogOutput,
"CVS File Log Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "CVS File Log Editor"), // display name
"text/vnd.qtcreator.cvs.log"
};
const VcsBaseEditorParameters annotateEditorParameters {
AnnotateOutput,
"CVS Annotation Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "CVS Annotation Editor"), // display name
"text/vnd.qtcreator.cvs.annotation"
};
const VcsBaseEditorParameters diffEditorParameters {
DiffOutput,
"CVS Diff Editor", // id
QT_TRANSLATE_NOOP("QtC::VcsBase", "CVS Diff Editor"), // display name
"text/x-patch"
};
const char CVS_COMMANDLOG_EDITOR_ID[] = "CVS Command Log Editor";
const char CVS_FILELOG_EDITOR_ID[] = "CVS File Log Editor";
const char CVS_ANNOTATION_EDITOR_ID[] = "CVS Annotation Editor";
const char CVS_DIFF_EDITOR_ID[] = "CVS Diff Editor";
static inline bool messageBoxQuestion(const QString &title, const QString &question)
{
@@ -312,29 +289,41 @@ private:
QAction *m_menuAction = nullptr;
public:
VcsEditorFactory commandLogEditorFactory {
&commandLogEditorParameters,
VcsEditorFactory commandLogEditorFactory {{
OtherContent,
CVS_COMMANDLOG_EDITOR_ID,
VcsBase::Tr::tr("CVS Command Log Editor"), // display name
"text/vnd.qtcreator.cvs.commandlog",
[] { return new CvsEditorWidget; },
std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory logEditorFactory {
&logEditorParameters,
VcsEditorFactory logEditorFactory {{
LogOutput,
CVS_FILELOG_EDITOR_ID,
VcsBase::Tr::tr("CVS File Log Editor"), // display name
"text/vnd.qtcreator.cvs.log",
[] { return new CvsEditorWidget; },
std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory annotateEditorFactory {
&annotateEditorParameters,
VcsEditorFactory annotateEditorFactory {{
AnnotateOutput,
CVS_ANNOTATION_EDITOR_ID,
VcsBase::Tr::tr("CVS Annotation Editor"), // display name
"text/vnd.qtcreator.cvs.annotation",
[] { return new CvsEditorWidget; },
std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
VcsEditorFactory diffEditorFactory {
&diffEditorParameters,
VcsEditorFactory diffEditorFactory {{
DiffOutput,
CVS_DIFF_EDITOR_ID,
VcsBase::Tr::tr("CVS Diff Editor"), // display name
"text/x-patch",
[] { return new CvsEditorWidget; },
std::bind(&CvsPluginPrivate::vcsDescribe, this, _1, _2)
};
}};
};
Utils::Id CvsPluginPrivate::id() const
@@ -460,7 +449,7 @@ CvsPluginPrivate::CvsPluginPrivate()
setupVcsSubmitEditor(this, {
CVS_SUBMIT_MIMETYPE,
CVSCOMMITEDITOR_ID,
CVSCOMMITEDITOR_DISPLAY_NAME,
VcsBase::Tr::tr("CVS Commit Editor"),
VcsBaseSubmitEditorParameters::DiffFiles,
[] { return new CvsSubmitEditor; },
});
@@ -965,7 +954,7 @@ void CvsPluginPrivate::filelog(const FilePath &workingDir,
} else {
const QString title = QString::fromLatin1("cvs log %1").arg(id);
IEditor *newEditor = showOutputInEditor(title, response.cleanedStdOut(),
logEditorParameters.id, source, codec);
CVS_FILELOG_EDITOR_ID, source, codec);
VcsBaseEditor::tagEditor(newEditor, tag);
if (enableAnnotationContextMenu)
VcsBaseEditor::getVcsBaseEditor(newEditor)->setFileLogAnnotateEnabled(true);
@@ -1105,7 +1094,7 @@ void CvsPluginPrivate::annotate(const FilePath &workingDir, const QString &file,
} else {
const QString title = QString::fromLatin1("cvs annotate %1").arg(id);
IEditor *newEditor = showOutputInEditor(title, response.cleanedStdOut(),
annotateEditorParameters.id, source, codec);
CVS_ANNOTATION_EDITOR_ID, source, codec);
VcsBaseEditor::tagEditor(newEditor, tag);
VcsBaseEditor::gotoLineOfEditor(newEditor, lineNumber);
}
@@ -1119,7 +1108,7 @@ bool CvsPluginPrivate::status(const FilePath &topLevel, const QString &file, con
const auto response = runCvs(topLevel, args);
const bool ok = response.result() == ProcessResult::FinishedWithSuccess;
if (ok) {
showOutputInEditor(title, response.cleanedStdOut(), commandLogEditorParameters.id,
showOutputInEditor(title, response.cleanedStdOut(), CVS_COMMANDLOG_EDITOR_ID,
topLevel, nullptr);
}
return ok;
@@ -1284,7 +1273,7 @@ bool CvsPluginPrivate::describe(const FilePath &repositoryPath,
setDiffBaseDirectory(editor, repositoryPath);
} else {
const QString title = QString::fromLatin1("cvs describe %1").arg(commitId);
IEditor *newEditor = showOutputInEditor(title, output, diffEditorParameters.id,
IEditor *newEditor = showOutputInEditor(title, output, CVS_DIFF_EDITOR_ID,
FilePath::fromString(entries.front().file), codec);
VcsBaseEditor::tagEditor(newEditor, commitId);
setDiffBaseDirectory(newEditor, repositoryPath);

Some files were not shown because too many files have changed in this diff Show More