Merge "Merge remote-tracking branch 'origin/13.0'"
97
dist/changelog/changes-13.0.0.md
vendored
@@ -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
|
||||
|
||||
BIN
doc/qtcreator/images/qtcreator-code-style-built-in-indenter.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
BIN
doc/qtcreator/images/qtcreator-new-file-ts.webp
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 13 KiB |
BIN
doc/qtcreator/images/qtcreator-projects-vanished-targets.webp
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
@@ -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}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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}.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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 """
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
86
src/libs/3rdparty/qtkeychain/qtkeychain.qbs
vendored
Normal 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",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 ¤t,
|
||||
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
|
||||
|
||||
@@ -188,6 +188,8 @@ protected:
|
||||
QStringList *found,
|
||||
const QString &start) const;
|
||||
|
||||
virtual bool disconnected() const;
|
||||
|
||||
private:
|
||||
bool iterateWithFind(const FilePath &filePath,
|
||||
const FileFilter &filter,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ¤t) 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
|
||||
|
||||
@@ -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 ¤t) 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ¤t) 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
|
||||
@@ -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 ¤t) 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {});
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -198,8 +198,6 @@ QtcLibrary {
|
||||
"namevaluedictionary.h",
|
||||
"namevalueitem.cpp",
|
||||
"namevalueitem.h",
|
||||
"namevaluemodel.cpp",
|
||||
"namevaluemodel.h",
|
||||
"namevaluesdialog.cpp",
|
||||
"namevaluesdialog.h",
|
||||
"namevaluevalidator.cpp",
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 &)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 §ion, 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 §ion)
|
||||
noMargin
|
||||
}.emerge();
|
||||
sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
|
||||
sectionLabel->setFont(StyleHelper::uiFont(StyleHelper::UiElementH2));
|
||||
|
||||
auto gridView = new GridView(zoomedInWidget);
|
||||
gridView->setItemDelegate(m_itemDelegate);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -137,6 +137,7 @@ private slots:
|
||||
void testInsertDefFromDeclTemplateClassWithValueParam();
|
||||
void testInsertDefFromDeclTemplateFunction();
|
||||
void testInsertDefFromDeclTemplateClassAndTemplateFunction();
|
||||
void testInsertDefFromDeclTemplateClassAndFunctionInsideNamespace();
|
||||
void testInsertDefFromDeclFunctionWithSignedUnsignedArgument();
|
||||
void testInsertDefFromDeclNotTriggeredForFriendFunc();
|
||||
void testInsertDefFromDeclMinimalFunctionParameterType();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||