53 Commits

Author SHA1 Message Date
defa6a1906 Merge branch 'experimental/template-znode' 2025-09-25 21:19:32 +02:00
Christoph Holzheuer
35ebcd2986 tried PCH 2025-09-24 17:29:10 +02:00
93879db3dd very first step... 2025-09-23 22:24:56 +02:00
5a7e309c24 ... 2025-09-23 11:26:07 +02:00
f509668c47 ... 2025-09-23 10:44:02 +02:00
e063ed7f75 Create README.md, part I 2025-09-23 09:13:03 +02:00
9192c0beb2 Changed Doxyfile 2025-09-23 08:50:31 +02:00
3ee5b1adfc remove word file. 2025-09-21 13:02:51 +02:00
2180b62bf1 added doxygen files 2025-09-21 12:50:15 +02:00
Christoph Holzheuer
bf49d8321f semi fixed missing sections 2025-09-16 16:45:21 +02:00
Christoph Holzheuer
28aa03b31a messups. 2025-09-15 16:37:35 +02:00
fa6499e3da Merge branch 'experimental/add-tree-children' 2025-09-13 22:54:48 +02:00
0f8addbe7f add ignore 2025-09-13 22:54:25 +02:00
b6890257f1 cosmetics 2025-09-13 22:53:32 +02:00
10752908ca try to fix vcx proj 2025-09-13 22:44:33 +02:00
32c5121fcd Merge branch 'experimental/qml-edit' into experimental/add-tree-children
# Conflicts:
#	src/application/xqchildmodel.cpp
2025-09-12 17:21:35 +02:00
Christoph Holzheuer
d07ef3fbf9 First steps. 2025-09-12 15:38:06 +02:00
fd94b2d354 --- fy 2025-09-12 00:39:51 +02:00
cc441d094c Added write-back for combos 2025-09-11 23:09:14 +02:00
Christoph Holzheuer
d5c1f8925c re-merge. 2025-09-11 17:45:44 +02:00
fd41138175 Fix choice model. 2025-09-10 23:32:00 +02:00
Christoph Holzheuer
03b0dafdcc -- fy 2025-09-10 17:57:10 +02:00
a0064b2566 delegate cleanups 2025-09-10 07:27:09 +02:00
Christoph Holzheuer
cbe8b92582 --- fy 2025-09-09 16:22:59 +02:00
809ef10c0d fixed toggle section 2025-09-08 22:58:52 +02:00
Christoph Holzheuer
95b7b026ff --- mess 2025-09-08 15:42:15 +02:00
05bc5ad5d9 -- fy 2025-09-07 23:34:09 +02:00
93ec52933e -- pre fy 2025-09-07 16:22:42 +02:00
3ac129ef26 fixed delete undo. 2025-09-07 15:46:01 +02:00
50703a4c44 fixed cut undo. 2025-09-07 11:29:51 +02:00
1531ec14f1 -- semi-fy 2025-09-06 22:13:46 +02:00
0fe15d6043 fixed selection crashes. 2025-09-06 16:06:47 +02:00
89c671295f finally, fixed hiding of headers. 2025-09-06 14:07:25 +02:00
a9dacca684 Fixed toggleSection 2025-09-06 11:08:07 +02:00
3887748c1a remorked sections. 2025-09-05 21:42:40 +02:00
b8f0893d59 Backup. 2025-09-05 17:12:38 +02:00
9c6f7688d7 Enable section toggle 2025-09-05 11:49:36 +02:00
8d26c32e51 cleanups, added const iterators to xqmaptor 2025-09-05 09:57:43 +02:00
c9b61c1c2b Fixed ::setData, part II 2025-09-04 20:09:21 +02:00
d6ccac1d85 Fixed ::setData, part I 2025-09-04 18:10:14 +02:00
5d2fb1b378 Added new-undo == delete 2025-09-04 17:01:01 +02:00
89c5fd21f1 Completed cmdNew implementation. 2025-09-04 14:56:18 +02:00
f8bd0886d3 Added cmdNew implementation. 2025-09-04 13:52:23 +02:00
Christoph Holzheuer
831daf898c --fy 2025-09-03 17:23:52 +02:00
Christoph Holzheuer
3e7b65dca5 add signal prototypes 2025-09-02 16:58:56 +02:00
147769bf60 added signal handler 'itemChanged' 2025-09-01 23:28:24 +02:00
Christoph Holzheuer
527de65074 -- fy 2025-09-01 17:40:08 +02:00
4d49a495fd -- fy 2025-08-31 23:18:14 +02:00
952409ab1a repaired it (a bit) 2025-08-31 14:38:11 +02:00
c6454f3106 cracket it. 2025-08-27 15:39:33 +02:00
04b0f650d6 -- pre-holiday 2025-08-27 14:06:31 +02:00
6ee677c595 looking better. 2025-08-26 19:41:28 +02:00
5057edb9ad Merge branch 'experimental/qml-widget' 2025-08-26 17:57:16 +02:00
89 changed files with 12486 additions and 1748 deletions

7
.gitignore vendored
View File

@@ -7,9 +7,6 @@ release/
enc_temp_folder/
ui_*
*autosave
doc/~$ree_thoughts.docx
doc/~WRL0004.tmp
UsersC998D~1.HOLAppDataLocalTemptmpj0mbo3rd
xml/fitzefatz.xml
build*
doc/
src/xtree.vcxproj.user
doc/html/

View File

@@ -1,4 +1,36 @@
# XTree
# xtree
Bla blu moo!
...
## Build
```
git clone https://gitea.sourceworx.org/chris/xtree.git
cd xtree/src
qmake xtree.pro
make
```
## Verzeichnisstruktur
| |
|--------|
| `deprecated/` | Reste & Fehlversuche
| `doc/` | Quellcode-Dokumentation
| `qml/` | QML Quellcode
| `src/` | c++ -Quellcode
| `xml/` | XML modeldefinition & Demodaten
## keys
- Widgetset für XML Daten
- experimenteller qml support
- docs erzeugen
-
experimenelle
## Also, noch mal von vorn:
@@ -56,3 +88,55 @@ die Testfiles.
- [ ] ```class XQModelHub : public XQModel, public XQModelReader```: etwas fragwürdig, spart aber leidige Verpointerungen
# libPiGPIO
libPiGPIO: Eine C++ Softwarebibliothek für den Pi4 zur Einbindung elektronischer Steuerelemente
## Getting started
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
```
mkdir existing_repo
git remote add origin http://sourceworx.org:9099/opensource/libpigpio.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools
- [ ] [Set up project integrations](http://sourceworx.org:9099/opensource/libpigpio/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.

2987
doc/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

6
doc/doxygen-awesome-css/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
docs/html
.DS_Store
.idea
node_modules
*.tgz

View File

@@ -0,0 +1,3 @@
*
!doxygen-awesome*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: MIT
.POSIX:
PROJECT = doxygen-awesome-css
# Paths
PREFIX = /usr/local
DATADIR = share
INSTALLDIR = $(DESTDIR)$(PREFIX)/$(DATADIR)/$(PROJECT)
# Utilities
INSTALL = install -m 644
MKDIR = mkdir -p
RM = rm -f
# Files to be installed
FILES = doxygen-awesome-darkmode-toggle.js \
doxygen-awesome-fragment-copy-button.js \
doxygen-awesome-interactive-toc.js \
doxygen-awesome-paragraph-link.js \
doxygen-awesome-sidebar-only-darkmode-toggle.css \
doxygen-awesome-sidebar-only.css \
doxygen-awesome-tabs.js \
doxygen-awesome.css
# Empty targets so that `make` and `make clean` do not cause errors
all:
clean:
install:
$(MKDIR) $(INSTALLDIR)
$(INSTALL) $(FILES) $(INSTALLDIR)/
uninstall:
$(RM) -r $(INSTALLDIR)/
.PHONY: all clean install uninstall

View File

@@ -0,0 +1,207 @@
# Doxygen Awesome
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/releases/latest)
[![GitHub](https://img.shields.io/github/license/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/blob/main/LICENSE)
![GitHub Repo stars](https://img.shields.io/github/stars/jothepro/doxygen-awesome-css)
<div class="title_screenshot">
![Screenshot of Doxygen Awesome CSS](img/screenshot.png)
</div>
**Doxygen Awesome** is a custom CSS theme for Doxygen HTML documentation with lots of customization parameters.
## Motivation
I really like how the Doxygen HTML documentation is structured! But IMHO it looks a bit outdated.
This theme is an attempt to update the visuals of Doxygen without changing its overall layout too much.
## Features
- 🌈 Clean, modern design
- 🚀 Heavily customizable by adjusting CSS variables
- 🧩 No changes to the HTML structure of Doxygen are required
- 📱 Improved mobile usability
- 🌘 Dark mode support!
- 🥇 Works best with **doxygen 1.9.1** - **1.9.4** and **1.9.6** - **1.12.0**
## Examples
Some websites using this theme:
- [Documentation of this repository](https://jothepro.github.io/doxygen-awesome-css/)
- [wxWidgets](https://docs.wxwidgets.org/3.2/)
- [OpenCV 5.x](https://docs.opencv.org/5.x/)
- [Zephyr](https://docs.zephyrproject.org/latest/doxygen/html/index.html)
- [FELTOR](https://mwiesenberger.github.io/feltor/dg/html/modules.html)
- [Spatial Audio Framework (SAF)](https://leomccormack.github.io/Spatial_Audio_Framework/index.html)
- [Randolf Richardson's C++ classes](https://www.randolf.ca/c++/docs/)
- [libCloudSync](https://jothepro.github.io/libCloudSync/)
- [libsl3](https://a4z.github.io/libsl3/)
- [DuMu<sup>x</sup>](https://dumux.org/docs/doxygen/master/)
## Installation
To use the theme when generating your documentation, bring the required CSS and JS files from this repository into your project.
This can be done in several ways:
- manually copying the files
- adding the project as a Git submodule
- downloading the project with CMake FetchContent
- adding the project as a npm/xpm dependency
- installing the theme system-wide
All theme files are located in the root of this repository and start with the prefix `doxygen-awesome-`. You may not need all of them. Follow the install instructions to figure out what files are required for your setup.
### Git submodule
For projects that use git, add the repository as a submodule and check out the desired release:
```sh
git submodule add https://github.com/jothepro/doxygen-awesome-css.git
cd doxygen-awesome-css
git checkout v2.3.4
```
### CMake with FetchContent
For project that build with CMake, the `FetchContent` module can be used to download the repository at configure-time.
Add the following snippet to your `CMakeLists.txt`
```cmake
include(FetchContent)
FetchContent_Declare(
doxygen-awesome-css
URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/heads/main.zip
)
FetchContent_MakeAvailable(doxygen-awesome-css)
# Save the location the files were cloned into
# This allows us to get the path to doxygen-awesome.css
FetchContent_GetProperties(doxygen-awesome-css SOURCE_DIR AWESOME_CSS_DIR)
# Generate the Doxyfile
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in)
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
```
This downloads the latest main (but any other revision could be used) and unpacks in the build folder. The `Doxyfile.in` can reference this location in the `HTML_EXTRA_STYLESHEET` field
```text
HTML_EXTRA_STYLESHEET = @AWESOME_CSS_DIR@/doxygen-awesome.css
```
When the configure stage of CMake is run, the `Doxyfile.in` is rendered to Doxyfile and Doxygen can be run as usual.
### npm/xpm dependency
In the npm ecosystem, this project can be added as a development dependency
to your project:
```sh
cd your-project
npm install https://github.com/jothepro/doxygen-awesome-css#v2.3.4 --save-dev
ls -l node_module/@jothepro/doxygen-awesome-css
```
Similarly, in the [xPack](https://xpack.github.io) ecosystem, this project can be added
as a development dependency to an [`xpm`](https://xpack.github.io/xpm/)
managed project.
### System-wide
You can even install the theme system-wide by running `make install`.
The files will be installed to `/usr/local/share/` by default,
but you can customize the install location with `make PREFIX=/my/custom/path install`.
### Choosing a layout
There are two layout options. Choose one of them and configure Doxygen accordingly:
<div class="tabbed">
- <b class="tab-title">Base Theme</b><div class="darkmode_inverted_image">
![](img/theme-variants-base.drawio.svg)
</div>
Comes with the typical Doxygen titlebar. Optionally the treeview in the sidebar can be enabled.
Required files: `doxygen-awesome.css`
Required `Doxyfile` configuration:
```
GENERATE_TREEVIEW = YES # optional. Also works without treeview
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
```
- <b class="tab-title">Sidebar-Only Theme</b><div class="darkmode_inverted_image">
![](img/theme-variants-sidebar-only.drawio.svg)
</div>
Hides the top titlebar to give more space to the content. The treeview must be enabled in order for this theme to work.
Required files: `doxygen-awesome.css`, `doxygen-awesome-sidebar-only.css`
Required `Doxyfile` configuration:
```
GENERATE_TREEVIEW = YES # required!
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css \
doxygen-awesome-css/doxygen-awesome-sidebar-only.css
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
```
</div>
<br>
@warning
- This theme is not compatible with the `FULL_SIDEBAR = YES` option provided by Doxygen!
- `HTML_COLORSTYLE` must be set to `LIGHT` since Doxygen 1.9.5!
### Further installation instructions
- [Installing extensions](docs/extensions.md)
- [Customizing the theme (colors, spacing, border-radius, ...)](docs/customization.md)
- [Tips and Tricks for further configuration](docs/tricks.md)
## Browser support
Tested with
- Chrome 119, Chrome 119 for Android, Chrome 119 for iOS
- Safari 17, Safari for iOS 16
- Firefox 118, Firefox 120 for Android, Firefox 119 for iOS
- Edge 119
- Opera 108
The theme does not strive to be backward compatible with (significantly) older browser versions.
## Credits
Thanks for all the bug reports and inspiring feedback on GitHub!
Special thanks to all the contributors:
<br><br>
<a href="https://github.com/jothepro/doxygen-awesome-css/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jothepro/doxygen-awesome-css" />
</a>
<div class="section_buttons">
| Read Next |
|---------------------------------:|
| [Extensions](docs/extensions.md) |
</div>

View File

@@ -0,0 +1,121 @@
# Customization
[TOC]
## CSS-Variables
This theme is highly customizable because a lot of things are parameterized with CSS variables.
Just to give you an idea of how flexible the styling is, click this button:
<div class="alter-theme-button" onclick="toggle_alternative_theme()" onkeypress="if (event.keyCode == 13) toggle_alternative_theme()" tabindex=0>Alter theme</div>
<br><hr>
### Setup
It is recommended to add your own `custom.css` and overwrite the variables there:
```
HTML_EXTRA_STYLESHEET = doxygen-awesome.css custom.css
```
Make sure to override the variables in the correct spot. All variables should be customized where they have been defined, in the `html` tag selector:
```css
html {
/* override light-mode variables here */
}
```
For dark-mode overrides, you have to choose where to put them, depending on whether the dark-mode toggle extension is installed or not:
<div class="tabbed">
- <b class="tab-title">dark-mode toggle is installed</b>
```css
html.dark-mode {
/* define dark-mode variable overrides here if you DO use doxygen-awesome-darkmode-toggle.js */
}
```
- <b class="tab-title">dark-mode toggle is **NOT** installed</b>
The dark-mode is enabled automatically depending on the system preference:
```css
@media (prefers-color-scheme: dark) {
html:not(.light-mode) {
/* define dark-mode variable overrides here if you DON'T use doxygen-awesome-darkmode-toggle.js */
}
}
```
</div>
### Available variables
The following list gives an overview of the variables defined in [`doxygen-awesome.css`](https://github.com/jothepro/doxygen-awesome-css/blob/main/doxygen-awesome.css).
The list is not complete. To explore all available variables, have a look at the CSS starting from [here](https://github.com/jothepro/doxygen-awesome-css/blob/main/doxygen-awesome.css#L30).
All variables are defined at the beginning of the stylesheet.
| Parameter | Default (Light) | Default (Dark) |
| :---------------------------------- | :---------------------------------------------------------- | :---------------------------------------------------------- |
| **Color Scheme**:<br>primary theme colors. This will affect the entire websites color scheme: links, arrows, labels, ... |||
| `--primary-color` | <code style="background:#1779c4;color:white">#1779c4</code> | <code style="background:#1982d2;color:white">#1982d2</code> |
| `--primary-dark-color` | <code style="background:#335c80;color:white">#335c80</code> | <code style="background:#5ca8e2;color:black">#5ca8e2</code> |
| `--primary-light-color` | <code style="background:#70b1e9;color:black">#70b1e9</code> | <code style="background:#4779ac;color:white">#4779ac</code> |
| **Page Colors**:<br>background and foreground (text-color) of the documentation. |||
| `--page-background-color` | <code style="background:#ffffff;color:black">#ffffff</code> | <code style="background:#1C1D1F;color:white">#1C1D1F</code> |
| `--page-foreground-color` | <code style="background:#2f4153;color:white">#2f4153</code> | <code style="background:#d2dbde;color:black">#d2dbde</code> |
| `--page-secondary-foreground-color` | <code style="background:#6f7e8e;color:white">#6f7e8e</code> | <code style="background:#859399;color:white">#859399</code> |
| **Spacing:**<br>default spacings. Most ui components reference these values for spacing, to provide uniform spacing on the page. |||
| `--spacing-small` | `5px` | |
| `--spacing-medium` | `10px` | |
| `--spacing-large` | `16px` | |
| **Border Radius**:<br>border radius for all rounded ui components. Will affect many components, like dropdowns, memitems, codeblocks, ... |||
| `--border-radius-small` | `4px` | |
| `--border-radius-medium` | `6px` | |
| `--border-radius-large` | `8px` | |
| **Content Width**:<br>The content is centered and constrained in its width. To make the content fill the whole page, set the following variable to `auto`. |||
| `--content-maxwidth` | `1000px` | |
| **Code Fragment Colors**:<br>Color-Scheme of multiline codeblocks |||
| `--fragment-background` | <code style="background:#F8F9FA;color:black">#F8F9FA</code> | <code style="background:#282c34;color:white">#282c34</code> |
| `--fragment-foreground` | <code style="background:#37474F;color:white">#37474F</code> | <code style="background:#dbe4eb;color:black">#dbe4eb</code> |
| **Arrow Opacity**:<br>By default the arrows in the sidebar are only visible on hover. You can override this behavior so they are visible all the time. |||
| `--side-nav-arrow-opacity` | `0` | |
| `--side-nav-arrow-hover-opacity` | `0.9` | |
| ...and many more |||
If you miss a configuration option or find a bug, please consider [opening an issue](https://github.com/jothepro/doxygen-awesome-css/issues)!
## Doxygen generator
The theme overrides most colors with the `--primary-color-*` variables.
But there are a few small images and graphics that the theme cannot adjust or replace. To make these blend in better with
the rest, it is recommended to adjust the [doxygen color settings](https://www.doxygen.nl/manual/customize.html#minor_tweaks_colors)
to something that matches the chosen color scheme.
For the default color scheme, these values work out quite well:
```
# Doxyfile
HTML_COLORSTYLE_HUE = 209
HTML_COLORSTYLE_SAT = 255
HTML_COLORSTYLE_GAMMA = 113
```
## Share your customizations
If you have customized the theme with custom colors, spacings, font-sizes, etc. and you want to share your creation with others, you can do this [here](https://github.com/jothepro/doxygen-awesome-css/discussions/13).
I am always curious to learn about how you made the theme look even better!
<div class="section_buttons">
| Previous | Next |
|:----------------------------|---------------------------:|
| [Extensions](extensions.md) | [Tips & Tricks](tricks.md) |
</div>

View File

@@ -0,0 +1,284 @@
# Extensions
[TOC]
On top of the base theme provided by `doxygen-awesome.css`, this repository comes with Javascript extensions that require additional setup steps to get them running.
The extensions require customizations in the header HTML template.
This is how you can create the default template with Doxygen:
1. Create default header template:
```sh
doxygen -w html header.html delete_me.html delete_me.css
```
2. Reference the template in your `Doxyfile`:
```
HTML_HEADER = header.html
```
[More details on header customization](https://www.doxygen.nl/manual/customize.html#minor_tweaks_header_css)
## Dark Mode Toggle {#extension-dark-mode-toggle}
Adds a button next to the search bar to enable and disable the dark theme variant manually:
<div class="darkmode_inverted_image bordered_image">
![](img/darkmode_toggle.png){width=250px}
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-darkmode-toggle.js`
- **HTML_EXTRA_STYLESHEET:** `doxygen-awesome-sidebar-only-darkmode-toggle.css`
<em>(ONLY required for the sidebar-only theme variant!)</em>
2. In the `header.html` template, include `doxygen-awesome-darkmode-toggle.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript">
DoxygenAwesomeDarkModeToggle.init()
</script>
</head>
<body>
```
### Customizing
Changing the tooltip of the button:
```js
DoxygenAwesomeDarkModeToggle.title = "Zwischen hellem/dunklem Modus wechseln"
```
Changing Icons. Both Emoji or SVG icons are supported:
```js
DoxygenAwesomeDarkModeToggle.lightModeIcon = '🌞'
// icon from https://fonts.google.com/icons
DoxygenAwesomeDarkModeToggle.darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#009793"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M8.1,14.15C9.77,14.63,11,16.17,11,18c0,0.68-0.19,1.31-0.48,1.87c0.48,0.09,0.97,0.14,1.48,0.14 c1.48,0,2.9-0.41,4.13-1.15c-2.62-0.92-5.23-2.82-6.8-5.86C7.74,9.94,7.78,7.09,8.29,4.9c-2.57,1.33-4.3,4.01-4.3,7.1c0,0,0,0,0,0 c0.01,0,0.01,0,0.02,0C5.66,12,7.18,12.83,8.1,14.15z" opacity=".3"/><path d="M19.78,17.51c-2.47,0-6.57-1.33-8.68-5.43C8.77,7.57,10.6,3.6,11.63,2.01C6.27,2.2,1.98,6.59,1.98,12 c0,0.14,0.02,0.28,0.02,0.42C2.61,12.16,3.28,12,3.98,12c0,0,0,0,0,0c0-3.09,1.73-5.77,4.3-7.1C7.78,7.09,7.74,9.94,9.32,13 c1.57,3.04,4.18,4.95,6.8,5.86c-1.23,0.74-2.65,1.15-4.13,1.15c-0.5,0-1-0.05-1.48-0.14c-0.37,0.7-0.94,1.27-1.64,1.64 c0.98,0.32,2.03,0.5,3.11,0.5c3.5,0,6.58-1.8,8.37-4.52C20.18,17.5,19.98,17.51,19.78,17.51z"/><path d="M7,16l-0.18,0C6.4,14.84,5.3,14,4,14c-1.66,0-3,1.34-3,3s1.34,3,3,3c0.62,0,2.49,0,3,0c1.1,0,2-0.9,2-2 C9,16.9,8.1,16,7,16z"/></g></g></svg>`
```
All customizations must be applied before calling `DoxygenAwesomeDarkModeToggle.init()`!
## Fragment Copy Button {#extension-copy-button}
Shows a copy button when the user hovers over a code fragment:
<div class="darkmode_inverted_image bordered_image">
![](img/fragment_copy_button.png){width=490}
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-fragment-copy-button.js`
- **HTML_COPY_CLIPBOARD:** `NO` required with Doxygen >= 1.10.0
2. In the `header.html` template, include `doxygen-awesome-fragment-copy-button.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
</script>
</head>
<body>
```
### Customizing
The tooltip of the button can be changed:
```js
DoxygenAwesomeFragmentCopyButton.title = "In die Zwischenablage kopieren"
```
The icon can be changed. It must be an SVG:
```js
DoxygenAwesomeFragmentCopyButton.copyIcon = `<svg ...>`
DoxygenAwesomeFragmentCopyButton.successIcon = `<svg ...>`
```
All customizations must be applied before calling `DoxygenAwesomeDarkModeToggle.init()`!
## Paragraph Linking {#extension-para}
Provides a button on hover behind every headline to allow easy creation of a permanent link to the headline:
<div class="darkmode_inverted_image bordered_image">
![](img/paragraph_link.png){width=220}
</div>
Works for all headlines and for many documentation section titles.
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-paragraph-link.js`
2. In the `header.html` template, include `doxygen-awesome-paragraph-link.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript">
DoxygenAwesomeParagraphLink.init()
</script>
</head>
<body>
```
### Customizing
The button tooltip can be changed:
```js
DoxygenAwesomeParagraphLink.title = "Abschnitt verknüpfen"
```
The icon of the button can be changed. Both plain characters or SVG icons are supported:
```js
DoxygenAwesomeParagraphLink.icon = "¶"
```
All customizations must be applied before calling `DoxygenAwesomeParagraphLink.init()`!
## Interactive TOC {#extension-toc}
On large screens, the Table of Contents (TOC) is anchored on the top right of the page. This extension visualizes the reading progress by dynamically highlighting the currently active section.
On small screens, the extension hides the TOC by default. The user can open it manually when needed:
<div class="darkmode_inverted_image bordered_image">
![](img/interactive_toc_mobile.png){width=380}
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-interactive-toc.js`
2. In the `header.html` template, include `doxygen-awesome-interactive-toc.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript">
DoxygenAwesomeInteractiveToc.init()
</script>
</head>
<body>
```
### Customizing
The offset for when a headline is considered active can be changed. A smaller value means that the headline of the section must be closer to the top of the viewport before it is highlighted in the TOC:
```js
DoxygenAwesomeInteractiveToc.topOffset = 45
```
Hiding the TOC on small screens can be disabled. It is still interactive and can be hidden by the user but will now be open by default:
```js
DoxygenAwesomeInteractiveToc.hideMobileMenu = false
```
## Tabs {#extension-tabs}
@warning Experimental feature! Please report bugs [here](https://github.com/jothepro/doxygen-awesome-css/issues).
This extension allows to arrange list content in tabs:
<div class="tabbed">
- <b class="tab-title">Tab 1</b>
This is the content of tab 1
- <b class="tab-title">Tab 2</b>
This is the content of tab 2
1. it has a list
2. with multiple items
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-tabs.js`
2. In the `header.html` template, include `doxygen-awesome-tabs.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-tabs.js"></script>
<script type="text/javascript">
DoxygenAwesomeTabs.init()
</script>
</head>
<body>
```
### Usage
Each list that is supposed to be displayed as tabs has to be wrapped with the `tabbed` CSS class.
Each item in the list must start with an element that has the class `tab-title`. It will then be used as tab title.
```md
<div class="tabbed">
- <b class="tab-title">Tab 1</b> This is the content of tab 1
- <b class="tab-title">Tab 2</b> This is the content of tab 2
</div>
```
## Page Navigation {#extension-page-navigation}
@warning Experimental feature! Please report bugs [here](https://github.com/jothepro/doxygen-awesome-css/issues).
To allow the user to easily navigate from one document to another, "Next" and "Previous" buttons can be added at the end of a Markdown document.
### Installation
The feature is shipped inside the default `doxygen-awesome.css`. No additional stylesheets or scripts need to be added.
### Usage
The following conditions must be met for the feature to work properly:
- The navigation must be inside a Markdown table with 1-2 columns.
- The alignment of the column defines the alignment of the arrow on the navigation button.
- the table must be wrapped inside a `<div>` with the class `section_buttons`.
<div class="tabbed">
- <span class="tab-title">Code</span>
```md
<div class="section_buttons">
| Previous | Next |
|:------------------|----------------------------------:|
| [Home](README.md) | [Customization](customization.md) |
</div>
```
- <span class="tab-title">Result</span>
<div class="section_buttons">
| Previous | Next |
|:------------------|----------------------------------:|
| [Home](README.md) | [Customization](customization.md) |
</div>
</div>
<div class="section_buttons">
| Previous | Next |
|:------------------|----------------------------------:|
| [Home](README.md) | [Customization](customization.md) |
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,127 @@
# Tips & Tricks
[TOC]
## Diagrams with Graphviz {#tricks-graphviz}
To get the best-looking class diagrams for your documentation, generate them with Graphviz as vector graphics with transparent background:
```
# Doxyfile
HAVE_DOT = YES
DOT_IMAGE_FORMAT = svg
DOT_TRANSPARENT = YES
```
In case `INTERACTIVE_SVG = YES` is set in the Doxyfile, all user-defined dotgraphs must be wrapped with the `interactive_dotgraph` CSS class for them to be rendered correctly:
```md
<div class="interactive_dotgraph">
\dotfile graph.dot
</div>
```
@note Both the default overflow scrolling behavior in this theme and the interactive editor enabled by `INTERACTIVE_SVG` are unsatisfying workarounds IMHO. Consider designing your graphs to be narrow enough to fit the page to avoid scrolling.
## Disable Dark Mode {#tricks-darkmode}
If you don't want the theme to automatically switch to dark mode depending on the browser preference,
you can disable dark mode by adding the `light-mode` class to the HTML tag in the header template:
```html
<html xmlns="http://www.w3.org/1999/xhtml" class="light-mode">
```
The same can be done to always enable dark mode:
```html
<html xmlns="http://www.w3.org/1999/xhtml" class="dark-mode">
```
@warning This only works if you don't use the dark-mode toggle extension.
## Choosing Sidebar Width {#tricks-sidebar}
If you have enabled the sidebar-only theme variant, make sure to carefully choose a proper width for your sidebar.
It should be wide enough to hold the icon, project title and version number. If the content is too wide, it will be
cut off.
```css
html {
/* Make sure sidebar is wide enough to contain the page title (logo + title + version) */
--side-nav-fixed-width: 335px;
}
```
The chosen width should also be set in the Doxyfile:
```
# Doxyfile
TREEVIEW_WIDTH = 335
```
## Formatting Tables {#tricks-tables}
By default tables in this theme are left-aligned and as wide as required to fit their content.
Those properties can be changed for individual tables.
### Centering
Tables can be centered by wrapping them in the `<center>` HTML tag.
<div class="tabbed">
- <span class="tab-title">Code</span>
```md
<center>
| This table | is centered |
|------------|----------------------|
| test 1 | test 2 |
</center>
```
- <span class="tab-title">Result</span>
<center>
| This table | is centered |
|------------|----------------------|
| test 1 | test 2 |
</center>
</div>
### Full Width
To make tables span the full width of the page, no matter how wide the content is, wrap the table in the `full_width_table` CSS class.
@warning Apply with caution! This breaks the overflow scrolling of the table. Content might be cut off on small screens!
<div class="tabbed">
- <span class="tab-title">Code</span>
```md
<div class="full_width_table">
| This table | spans the full width |
|------------|----------------------|
| test 1 | test 2 |
</div>
```
- <span class="tab-title">Result</span>
<div class="full_width_table">
| This table | spans the full width |
|------------|----------------------|
| test 1 | test 2 |
</div>
</div>
<div class="section_buttons">
| Previous | Next |
|:----------------------------------|---------------------------------------:|
| [Customization](customization.md) | [Example](https://jothepro.github.io/doxygen-awesome-css/class_my_library_1_1_example.html) |
</div>

View File

@@ -0,0 +1,157 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
// SVG icons from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
static title = "Toggle Light/Dark Mode"
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
static _staticConstructor = function() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
// Update the color scheme when the browsers preference changes
// without user interaction on the website.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
}
});
}()
static init() {
$(function() {
$(document).ready(function() {
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
toggleButton.updateIcon()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
toggleButton.updateIcon()
})
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
toggleButton.updateIcon()
}
});
$(document).ready(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
$(window).resize(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
})
})
}
constructor() {
super();
this.onclick=this.toggleDarkMode
}
/**
* @returns `true` for dark-mode, `false` for light-mode system preference
*/
static get systemPreference() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
/**
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
static set userPreference(userPreference) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
if(!userPreference) {
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
}
} else {
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
}
}
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
}
static enableDarkMode(enable) {
if(enable) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
document.documentElement.classList.add("dark-mode")
document.documentElement.classList.remove("light-mode")
} else {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
document.documentElement.classList.remove("dark-mode")
document.documentElement.classList.add("light-mode")
}
}
static onSystemPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
static onUserPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
toggleDarkMode() {
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
this.updateIcon()
}
updateIcon() {
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
} else {
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
}
}
}
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);

View File

@@ -0,0 +1,85 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)

View File

@@ -0,0 +1,91 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeInteractiveToc {
static topOffset = 38
static hideMobileMenu = true
static headers = []
static init() {
window.addEventListener("load", () => {
let toc = document.querySelector(".contents > .toc")
if(toc) {
toc.classList.add("interactive")
if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) {
toc.classList.add("open")
}
document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => {
if(toc.classList.contains("open")) {
toc.classList.remove("open")
} else {
toc.classList.add("open")
}
})
document.querySelectorAll(".contents > .toc > ul a").forEach((node) => {
let id = node.getAttribute("href").substring(1)
DoxygenAwesomeInteractiveToc.headers.push({
node: node,
headerNode: document.getElementById(id)
})
document.getElementById("doc-content")?.addEventListener("scroll",this.throttle(DoxygenAwesomeInteractiveToc.update, 100))
})
DoxygenAwesomeInteractiveToc.update()
}
})
}
static update() {
let active = DoxygenAwesomeInteractiveToc.headers[0]?.node
DoxygenAwesomeInteractiveToc.headers.forEach((header) => {
let position = header.headerNode.getBoundingClientRect().top
header.node.classList.remove("active")
header.node.classList.remove("aboveActive")
if(position < DoxygenAwesomeInteractiveToc.topOffset) {
active = header.node
active?.classList.add("aboveActive")
}
})
active?.classList.add("active")
active?.classList.remove("aboveActive")
}
static throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCall < delay) {
return;
}
lastCall = now;
return setTimeout(() => {func(...args)}, delay);
};
}
}

View File

@@ -0,0 +1,51 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeParagraphLink {
// Icon from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static icon = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
static title = "Permanent Link"
static init() {
$(function() {
$(document).ready(function() {
document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => {
let anchorlink = document.createElement("a")
anchorlink.setAttribute("href", `#${node.getAttribute("id")}`)
anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title)
anchorlink.classList.add("anchorlink")
node.classList.add("anchor")
anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon
node.parentElement.appendChild(anchorlink)
})
})
})
}
}

View File

@@ -0,0 +1,40 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@media screen and (min-width: 768px) {
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
}
}

View File

@@ -0,0 +1,116 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
html {
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
* Make sure it is wide enough to contain the page title (logo + title + version)
*/
--side-nav-fixed-width: 335px;
--menu-display: none;
--top-height: 120px;
--toc-sticky-top: -25px;
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
}
#projectname {
white-space: nowrap;
}
@media screen and (min-width: 768px) {
html {
--searchbar-background: var(--page-background-color);
}
#side-nav {
min-width: var(--side-nav-fixed-width);
max-width: var(--side-nav-fixed-width);
top: var(--top-height);
overflow: visible;
}
#nav-tree, #side-nav {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
height: var(--top-height);
margin-bottom: calc(0px - var(--top-height));
max-width: var(--side-nav-fixed-width);
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
cursor: default;
width: 1px !important;
background: var(--separator-color);
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
#doc-content {
height: calc(100vh - 31px) !important;
padding-bottom: calc(3 * var(--spacing-large));
padding-top: calc(var(--top-height) - 80px);
box-sizing: border-box;
margin-left: var(--side-nav-fixed-width) !important;
}
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
}
#MSearchResultsWindow {
left: var(--spacing-medium) !important;
right: auto;
}
}

View File

@@ -0,0 +1,90 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeTabs {
static init() {
window.addEventListener("load", () => {
document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => {
let tabLinkList = []
tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => {
tab.id = "tab_" + tabbedIndex + "_" + tabIndex
let header = tab.querySelector(".tab-title")
let tabLink = document.createElement("button")
tabLink.classList.add("tab-button")
tabLink.appendChild(header)
header.title = header.textContent
tabLink.addEventListener("click", () => {
tabbed.querySelectorAll(":scope > ul > li").forEach((tab) => {
tab.classList.remove("selected")
})
tabLinkList.forEach((tabLink) => {
tabLink.classList.remove("active")
})
tab.classList.add("selected")
tabLink.classList.add("active")
})
tabLinkList.push(tabLink)
if(tabIndex == 0) {
tab.classList.add("selected")
tabLink.classList.add("active")
}
})
let tabsOverview = document.createElement("div")
tabsOverview.classList.add("tabs-overview")
let tabsOverviewContainer = document.createElement("div")
tabsOverviewContainer.classList.add("tabs-overview-container")
tabLinkList.forEach((tabLink) => {
tabsOverview.appendChild(tabLink)
})
tabsOverviewContainer.appendChild(tabsOverview)
tabbed.before(tabsOverviewContainer)
function resize() {
let maxTabHeight = 0
tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => {
let visibility = tab.style.display
tab.style.display = "block"
maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight)
tab.style.display = visibility
})
tabbed.style.height = `${maxTabHeight + 10}px`
}
resize()
new ResizeObserver(resize).observe(tabbed)
})
})
}
static resize(tabbed) {
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
html.alternative {
/* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #7aabd6;
--primary-lighter-color: #cae1f1;
--primary-lightest-color: #e9f1f8;
/* page base colors */
--page-background-color: white;
--page-foreground-color: #2c3e50;
--page-secondary-foreground-color: #67727e;
--border-radius-large: 22px;
--border-radius-small: 9px;
--border-radius-medium: 14px;
--spacing-small: 8px;
--spacing-medium: 14px;
--spacing-large: 19px;
--top-height: 125px;
--side-nav-background: #324067;
--side-nav-foreground: #F1FDFF;
--header-foreground: var(--side-nav-foreground);
--searchbar-background: var(--side-nav-foreground);
--searchbar-border-radius: var(--border-radius-medium);
--header-background: var(--side-nav-background);
--header-foreground: var(--side-nav-foreground);
--toc-background: rgb(243, 240, 252);
--toc-foreground: var(--page-foreground-color);
}
html.alternative.dark-mode {
color-scheme: dark;
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #4779ac;
--primary-lighter-color: #191e21;
--primary-lightest-color: #191a1c;
--page-background-color: #1C1D1F;
--page-foreground-color: #d2dbde;
--page-secondary-foreground-color: #859399;
--separator-color: #3a3246;
--side-nav-background: #171D32;
--side-nav-foreground: #F1FDFF;
--toc-background: #20142C;
--searchbar-background: var(--page-background-color);
}

View File

@@ -0,0 +1,57 @@
.github-corner svg {
fill: var(--primary-light-color);
color: var(--page-background-color);
width: 72px;
height: 72px;
}
@media screen and (max-width: 767px) {
.github-corner svg {
width: 50px;
height: 50px;
}
#projectnumber {
margin-right: 22px;
}
}
.alter-theme-button {
display: inline-block;
cursor: pointer;
background: var(--primary-color);
color: var(--page-background-color) !important;
border-radius: var(--border-radius-medium);
padding: var(--spacing-small) var(--spacing-medium);
text-decoration: none;
}
.alter-theme-button:hover {
background: var(--primary-dark-color);
}
html.dark-mode .darkmode_inverted_image img, /* < doxygen 1.9.3 */
html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
filter: brightness(89%) hue-rotate(180deg) invert();
}
.bordered_image {
border-radius: var(--border-radius-small);
border: 1px solid var(--separator-color);
display: inline-block;
overflow: hidden;
}
html.dark-mode .bordered_image img, /* < doxygen 1.9.3 */
html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
border-radius: var(--border-radius-small);
}
.title_screenshot {
filter: drop-shadow(0px 3px 10px rgba(0,0,0,0.22));
max-width: 500px;
margin: var(--spacing-large) 0;
}
.title_screenshot .caption {
display: none;
}

View File

@@ -0,0 +1,90 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!-- BEGIN opengraph metadata -->
<meta property="og:title" content="Doxygen Awesome" />
<meta property="og:image" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta property="og:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
<!-- END opengraph metadata -->
<!-- BEGIN twitter metadata -->
<meta name="twitter:image:src" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta name="twitter:title" content="Doxygen Awesome" />
<meta name="twitter:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<!-- END twitter metadata -->
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<link rel="icon" type="image/svg+xml" href="logo.drawio.svg"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-tabs.js"></script>
<script type="text/javascript" src="$relpath^toggle-alternative-theme.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
DoxygenAwesomeDarkModeToggle.init()
DoxygenAwesomeParagraphLink.init()
DoxygenAwesomeInteractiveToc.init()
DoxygenAwesomeTabs.init()
</script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<!-- https://tholman.com/github-corners/ -->
<a href="https://github.com/jothepro/doxygen-awesome-css" class="github-corner" title="View source on GitHub" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 250 250" width="40" height="40" style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@@ -0,0 +1,12 @@
let original_theme_active = true;
function toggle_alternative_theme() {
if(original_theme_active) {
document.documentElement.classList.add("alternative")
original_theme_active = false;
} else {
document.documentElement.classList.remove("alternative")
original_theme_active = true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,117 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="491px" height="261px" viewBox="-0.5 -0.5 491 261" content="&lt;mxfile&gt;&lt;diagram id=&quot;6E4AiNPWWr3a8GvC3Ypl&quot; name=&quot;Page-1&quot;&gt;xZfLrpswEIafBqndIIzBwLK5tZt2k0pd+wQHrBqcOs6tT98xmABxmrYiUUEK+Pd1vhkPjofn1fmjorvys8yZ8MIgP3t44YVhihL4NcKlFZIkbYVC8byVUC+s+U9mxcCqB56z/aihllJovhuLG1nXbKNHGlVKnsbNtlKMZ93RgjnCekOFq37juS6tWXHQ658YL8puZhTYmop2ja2wL2kuTwMJLz08V1Lq9q06z5kw7Doubb/Vb2qvC1Os1n/ToVvHkYqDNc4uTF86awslDzsPz+BR58x0DKBE1cY6JoWSO69dypEpzc73vELfugl6wyFgmKyYVhdod+rRRpldZznAGhIrUuvO4tq3txherNH3AZA/2z+2+1RyzdY7ujG1Jwhv0EpdwfgLBK97reR3NpdCqqY3JktzQ00h6H5vBwEEmvKaKVt28V0dc8vvP3GKHE5zsMEs97m4ls09FUlXm6V+jNteNtVEkZ/a7DPgBi19Qlx0KI19hKfTCx16X7kW7I0qUN99oUdeUM1l7RkbwfRgzWB/le+nwd1yIQZoF8vlYrV6GKMvjcMGPUqyqPudzhU7XNfwZXCwPpfjKjH36zg+jFQUEj/Br4vU2CG6go8Ra4DOFKP5Rh2q3X4i0deSg4wHu3lwZQ7GuwGKsY9HF5nOM3EjtNnaD/ihKfy2kJUHekjM/aR0ihPkJ2GEcJJhFOAoHmVWdLu9Qzd4A+R3naM0TOLsTiDf+I6k052Q3vnIE6EtruZI0hEjPw7m7DXr2Q0kUphnk7q7AWDqdoy2znEroNWPfFfLmt2kGCtRwYsaioJtzQjGTRyOoB+sXPE8N5PcDZXxZjQLtGe1cPJp43x1U5qROEZdQIT/Ggyw24ahkJL4JcEAxf443dQN/pPg5S8=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="490" height="260" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<rect x="198.53" y="44.87" width="219.66" height="185.13" fill="rgb(255, 255, 255)" stroke="#e3e3e3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 137px; margin-left: 200px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Content
</div>
</div>
</div>
</foreignObject>
<text x="308" y="141" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Content
</text>
</switch>
</g>
<rect x="0" y="0" width="490" height="44.87" fill="#deedff" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 22px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Titlebar (Navigation + Search)
</div>
</div>
</div>
</foreignObject>
<text x="245" y="26" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Titlebar (Navigation + Search)
</text>
</switch>
</g>
<rect x="0" y="44.87" width="126.73" height="185.13" fill="#f7f7f7" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 137px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Sidebar (Navigation)
</div>
</div>
</div>
</foreignObject>
<text x="63" y="141" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Sidebar (Navigation)
</text>
</switch>
</g>
<rect x="0" y="226.67" width="490" height="33.33" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 243px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Footer (Breadcrumbs)
</div>
</div>
</div>
</foreignObject>
<text x="245" y="247" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Footer (Breadcrumbs)
</text>
</switch>
</g>
<rect x="371.72" y="14.87" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 23px; margin-left: 373px;">
<div data-drawio-colors="color: #262626; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(38, 38, 38); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Search
</div>
</div>
</div>
</foreignObject>
<text x="422" y="27" fill="#262626" font-family="Helvetica" font-size="12px" text-anchor="middle">
Search
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 23px; margin-left: 19px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
<font color="#262626">
Title
</font>
</div>
</div>
</div>
</foreignObject>
<text x="19" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="20px">
Tit...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,102 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="491px" height="261px" viewBox="-0.5 -0.5 491 261" content="&lt;mxfile&gt;&lt;diagram id=&quot;6E4AiNPWWr3a8GvC3Ypl&quot; name=&quot;Page-1&quot;&gt;xZZNj5swEIZ/DdL2gjAGA8cmm7SX9pJKPTvBAasGU8f56q/vOJjFLOxqpbC7WIrMO/5gnhlP7OFldfmmaFP+kDkTXhjkFw8/emGYogR+jXBthSRJW6FQPG8l1Asb/o9ZMbDqkefsMBiopRSaN0NxJ+ua7fRAo0rJ83DYXorhrg0t2EjY7KgYq795rkvrVhz0+nfGi7LbGQXWUtFusBUOJc3l2ZHwysNLJaVue9VlyYRh13Fp561fsD59mGK1ftOErJ1xouJonbMfpq+dt4WSx2a8sN3rxJRmlynsdNut0HsGGcFkxbS6wrhzzy7KLJDS4RYSK1Ibr+Jpbu8SdKxX0x6+wUHwr86ZGR94eHEuuWabhu6M9Qz5C1qpK1j/EUH3oJX8w5ZSSHWbjcnKNLBMIMpeYPRJLLrz48BYylqb752XyerW7mXSWbPUj8Osf2z1sMUDEZ9YxYEIs3xCJjhi7GM8A0o0QrmBorSlyjPpTgTst9jCCylM7+EX14DWmIBJ8JOeeEE1l/WX+8jvuRAO93Vi2odkKQqJn4TvlajhiO4aaiIzcB8WitF8p45Vc7iT3jtRuriEIoSTDKMARzEe5C2Qggx1n1ESY4L9MIlTEgYkzUgcj3Hf0tl9yAz08Ti3GVW78hXa6B7aeyhCjh4S02aLApQHJwrpsHpEaBiEcSVBAfK7yVEK4cjGUUDPQ5nOEIVo4p+rrSuG1+1/tkNG/h7NjWHRw3OktgLZAtQuAHu3a7S2UVyBrX4teLWs2bPqYyUqeFHDq2B7s4KJE4eL01crVzzPzSaTuTI8u+YD7dUvDD4mGRCaDqN7JLHv5kJKJs7kDNkAr/0l8GZzbtJ49R8=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="490" height="260" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<rect x="198.53" y="16.67" width="219.66" height="233.33" fill="rgb(255, 255, 255)" stroke="#e3e3e3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 133px; margin-left: 200px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Content
</div>
</div>
</div>
</foreignObject>
<text x="308" y="137" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Content
</text>
</switch>
</g>
<rect x="0" y="0" width="126.72" height="260" fill="#f7f7f7" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 130px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Sidebar
<br/>
(Title + Navigation)
</div>
</div>
</div>
</foreignObject>
<text x="63" y="134" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Sidebar...
</text>
</switch>
</g>
<rect x="126.72" y="226.67" width="363.28" height="33.33" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 361px; height: 1px; padding-top: 243px; margin-left: 128px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Footer (Breadcrumbs)
</div>
</div>
</div>
</foreignObject>
<text x="308" y="247" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Footer (Breadcrumbs)
</text>
</switch>
</g>
<rect x="12.67" y="41.67" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 50px; margin-left: 14px;">
<div data-drawio-colors="color: #262626; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(38, 38, 38); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Search
</div>
</div>
</div>
</foreignObject>
<text x="63" y="54" fill="#262626" font-family="Helvetica" font-size="12px" text-anchor="middle">
Search
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 20px; margin-left: 15px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
<font color="#262626">
Title
</font>
</div>
</div>
</div>
</foreignObject>
<text x="15" y="26" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="20px">
Tit...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,169 @@
#pragma once
#include <string>
namespace MyLibrary {
enum Color { red = 1, green = 2, blue = 3 };
/**
* @brief Example class to demonstrate the features of the custom CSS.
*
* @author jothepro
*
*/
class Example {
public:
/**
* @brief brief summary
*
* doxygen test documentation
*
* @param test this is the only parameter of this test function. It does nothing!
*
* # Supported elements
*
* These elements have been tested with the custom CSS.
*
* ## Tables
*
* <div class="tabbed">
*
* - <b class="tab-title">Basic</b>
* This theme supports normal markdown tables:<br>
* | Item | Title | Description | More |
* |-----:|-------|-----------------------|--------------------------------------------|
* | 1 | Foo | A placeholder | Some lorem ipsum to make this table wider. |
* | 2 | Bar | Also a placeholder | More lorem ipsum. |
* | 3 | Baz | The third placeholder | More lorem ipsum. |
* - <b class="tab-title">Centered</b>
* <center>
* A table can be centered with the `<center>` html tag:<br>
* | Item | Title | Description | More |
* |-----:|-------|-----------------------|--------------------------------------------|
* | 1 | Foo | A placeholder | Some lorem ipsum to make this table wider. |
* | 2 | Bar | Also a placeholder | More lorem ipsum. |
* | 3 | Baz | The third placeholder | More lorem ipsum. |
* </center>
* - <b class="tab-title">Stretched</b>
* A table wrapped in `<div class="full_width_table">` fills the full page width.
* <div class="full_width_table">
* | Item | Title | Description | More |
* |-----:|-------|-----------------------|--------------------------------------------|
* | 1 | Foo | A placeholder | Some lorem ipsum to make this table wider. |
* | 2 | Bar | Also a placeholder | More lorem ipsum. |
* | 3 | Baz | The third placeholder | More lorem ipsum. |
* </div>
* **Caution**: This will break the overflow scrolling support!
* - <b class="tab-title">Complex</b>
* Complex [Doxygen tables](https://www.doxygen.nl/manual/tables.html) are also supported as seen in @ref multi_row "this example":<br>
* <table>
* <caption id="multi_row">Complex table</caption>
* <tr><th>Column 1 <th>Column 2 <th>Column 3
* <tr><td rowspan="2">cell row=1+2,col=1<td>cell row=1,col=2<td>cell row=1,col=3
* <tr><td rowspan="2">cell row=2+3,col=2 <td>cell row=2,col=3
* <tr><td>cell row=3,col=1 <td>cell row=3,col=3
* </table>
* - <b class="tab-title">Overflow Scrolling</b> The table content is scrollable if the table gets too wide.<br>
* | first_column | second_column | third_column | fourth_column | fifth_column | sixth_column | seventh_column | eighth_column | ninth_column |
* |--------------|---------------|--------------|---------------|--------------|--------------|----------------|---------------|--------------|
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* - <b class="tab-title">Images</b>A table can contain images:<br>
* | Column 1 | Column 2 |
* |---------------------------|-------------------------------------------------|
* | ![doxygen](testimage.png) | ← the image should not be inverted in dark-mode |
*
*
* </div>
*
* ## Diagrams
*
* Graphviz diagrams support dark mode and can be scrolled once they get too wide:
*
* \dot Graphviz with a caption
* digraph example {
* node [fontsize="12"];
* rankdir="LR"
* a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k;
* }
* \enddot
*
* ## Lists
*
* - element 1
* - element 2
*
* 1. element 1
* ```
* code in lists
* ```
* 2. element 2
*
* ## Quotes
*
* > Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
* > ut labore et dolore magna aliqua. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra.
* > Velit sed ullamcorper morbi tincidunt ornare.
* >
* > Lorem ipsum dolor sit amet consectetur adipiscing elit duis.
* *- jothepro*
*
* ## Code block
*
* ```cpp
* auto x = "code within md fences";
* ```
*
* @code{.cpp}
* // code within @code block
* while(true) {
* auto example = std::make_shared<Example>(5);
* example->test("test");
* }
* @endcode
*
* // code within indented code block
* auto test = std::shared_ptr<Example(5);
*
*
* Inline `code` elements in a text. *Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.* This also works within multiline text and does not break the `layout`.
*
*
* ## Special hints
*
* @warning this is a warning only for demonstration purposes
*
* @note this is a note to show that notes work. They can also include `code`:
* @code{.c}
* void this_looks_awesome();
* @endcode
*
* @bug example bug
*
* @deprecated None of this will be deprecated, because it's beautiful!
*
* @invariant This is an invariant
*
* @pre This is a precondition
*
* @post This is a postcondition
*
* @todo This theme is never finished!
*
* @remark This is awesome!
*
*/
std::string test(const std::string& test);
virtual int virtualfunc() = 0;
static bool staticfunc();
};
class SecondExample {
std::string foo();
}
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <string>
#include "example.hpp"
#include <iostream>
namespace MyLibrary {
/**
* @brief some subclass
*/
template<typename TemplatedClass>
class SubclassExample : public Example {
public:
/**
* @bug second bug
* @return
*/
int virtualfunc() override;
/**
* @brief Template function function
*/
template <typename T>
std::shared_ptr<std::string> function_template_test(std::shared_ptr<T>& param);
/**
* @brief Extra long function with lots of parameters and many template types.
*
* Also has a long return type.
*
* @param param1 first parameter
* @param param2 second parameter
* @param parameter3 third parameter
*/
template <typename T, typename Foo, typename Bar, typename Alice, typename Bob, typename Charlie, typename Hello, typename World>
std::pair<std::string, std::string> long_function_with_many_parameters(std::shared_ptr<T>& param1, std::shared_ptr<std::string>& param2, bool parameter3, Alice paramater4 Bob parameter 5) {
if(true) {
std::cout << "this even has some code." << std::endl;
}
}
};
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="61px" height="74px" viewBox="-0.5 -0.5 61 74" content="&lt;mxfile host=&quot;drawio-plugin&quot; modified=&quot;2021-03-16T23:58:23.462Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36&quot; version=&quot;13.7.9&quot; etag=&quot;JoeaGLJ54FcERO7YrWLQ&quot; type=&quot;embed&quot;&gt;&lt;diagram id=&quot;JMB9aH8b_oZ7EWDuqJgx&quot; name=&quot;Page-1&quot;&gt;7VdNc5swEP01HDsjkGPDsSVJe+lMZnzoWYENaAwsI8ux6a+vCCtA4KSu62kmSS+M9LT7tB9P0uDxuDx8VaLOv2MKhRew9ODxay8Igigy3xZoCOC8AzIl0w7yB2AtfwKBjNCdTGHrGGrEQsvaBROsKki0gwmlcO+aPWDh7lqLDGbAOhHFHP0hU513aHjFBvwbyCy3O/uMVkphjQnY5iLF/QjiNx6PFaLuRuUhhqKtna1L53f7zGofmIJKn+RAcTyKYkfJUWC6sdlmCnc1mYHScDhWY3Fvzdk8Br/PzCgCsAStGmNCRJy2JDH4pIV8VMG+edS4rCcZcjMDSu+ZVP3fpwpV+rnVh5ndF5hsPP4l16VhvPbN8AErTWI0re7mMRaonpw5Y8tlHBvcsNzKwnpttVDaslZYgcXIhj3NFW56LS1bbrM44l6m4Wq5MLhxzEDfgZKmAKDWtUhklRFNgqVM7LYb0Enu8I9j9dkVC80KtgS6Lb3fGnYVgXSm/1Ez2fFu7oeTYA/CuIUWU1AILR9d/mN9pR3uUJqde7F88leOWhYLl2GLO5UAOY2FP+GxMm3c6CwNlXlKY9oompFZ3Rps59EOkuw8BoH2BTtNs8EfaZbUdYZkXQGuXhDgR9DYRBycXURj00D+UmMT2ktJLnr9B8HG0IzFcPkHYfUe3oPZqfOjMEiDs1+KEw5n9P/+/1f3f/gq1394lt7erqQ+0HVvpsPPRWc+/KHxm18=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 13 57 L 13.01 57.01 L 15.87 50.14 L 18.37 43.14 L 20.91 36.15 L 23.67 29.25 L 26.4 22.33 Q 30 13 33.71 22.28 L 33.55 22.22 L 35.48 26.91 L 37.49 31.64 L 39.48 36.36 L 41.2 40.97 L 43.05 45.63" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 47.51 56.77 L 47.65 56.93 L 45.43 54.91 L 43.41 53.11 L 41.43 51.35 L 39.63 49.8 L 37.48 47.86 L 37.39 47.64 L 39.79 47.17 L 41.9 45.98 L 44.24 45.37 L 46.48 44.52 L 48.62 43.4 L 48.54 43.39 L 48.58 46.09 L 48.04 48.74 L 48.04 51.43 L 47.8 54.1 L 47.51 56.77 Z Z" fill-opacity="0.1" fill="#010508" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 43 L 9.94 42.88 L 12.16 41.98 L 14.31 40.96 L 16.51 40.01 L 18.62 38.89 L 20.88 38.1 Q 30 34 40 34 L 40 33.75 L 42 33.83 L 44 33.8 L 46 33.79 L 48 34.05 L 50 34" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="7" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 54 L 9.97 53.99 L 12.69 47.07 L 15.43 40.16 L 18.07 33.21 L 20.65 26.24 L 23.4 19.33 Q 27 10 30.71 19.28 L 30.66 19.26 L 32.46 23.91 L 34.55 28.66 L 36.26 33.27 L 38.35 38.03 L 40.05 42.63" fill="none" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 44.51 53.77 L 44.56 53.83 L 42.48 51.97 L 40.5 50.21 L 38.48 48.41 L 36.41 46.56 L 34.48 44.86 L 34.55 45.02 L 36.72 44 L 39 43.24 L 41.21 42.28 L 43.48 41.51 L 45.62 40.4 L 45.78 40.42 L 45.51 43.09 L 45.01 45.74 L 44.87 48.42 L 44.94 51.12 L 44.51 53.77 Z Z" fill="#1982d2" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 7 40 L 7.02 40.05 L 9.28 39.25 L 11.33 38 L 13.48 36.96 L 15.73 36.14 L 17.88 35.1 Q 27 31 37 31 L 37 30.79 L 39 31.11 L 41 30.85 L 43 30.78 L 45 30.89 L 47 31" fill="none" stroke="#1982d2" stroke-width="8" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,34 @@
{
"name": "@jothepro/doxygen-awesome-css",
"version": "2.3.4",
"description": "Custom CSS theme for doxygen html-documentation with lots of customization parameters.",
"main": "",
"scripts": {
"npm-pack": "npm pack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/jothepro/doxygen-awesome-css.git"
},
"homepage": "https://jothepro.github.io/doxygen-awesome-css/",
"bugs": {
"url": "https://github.com/jothepro/doxygen-awesome-css/issues"
},
"keywords": [
"doxygen",
"css",
"theme",
"awesome"
],
"author": {
"name": "jothepro",
"url": "https://github.com/jothepro",
"git": "https://github.com/jothepro/doxygen-awesome-css"
},
"license": "MIT",
"config": {},
"dependencies": {},
"devDependencies": {},
"xpack": {}
}

View File

@@ -1,44 +0,0 @@
// SPDX-FileCopyrightText: 2025 Martin Leutelt <martin.leutelt@basyskom.com>
// SPDX-FileCopyrightText: 2025 basysKom GmbH
//
// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick
import QtQuick.Controls
Control {
id: container
required property int column
required property var model
property int sortOrder: Qt.AscendingOrder
signal clicked(int sortOrder)
padding: 8
background: Rectangle {
color: tapHandler.pressed ? "gray" : "lightgray"
TapHandler {
id: tapHandler
onTapped: {
if (container.sortOrder === Qt.AscendingOrder) {
container.sortOrder = Qt.DescendingOrder
} else {
container.sortOrder = Qt.AscendingOrder
}
container.clicked(container.sortOrder)
}
}
}
contentItem: Label {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
text: container.model.display
}
}

View File

@@ -1,26 +0,0 @@
import QtQuick
import QtQuick.Controls
Control {
id: container
required property int row
required property var model
padding: 8
background: Rectangle {
color: tapHandler.pressed ? "gray" : "lightgray"
TapHandler {
id: tapHandler
}
}
contentItem: Label {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
text: container.model.display
}
}

View File

@@ -1,161 +0,0 @@
// SPDX-FileCopyrightText: 2025 Martin Leutelt <martin.leutelt@basyskom.com>
// SPDX-FileCopyrightText: 2025 basysKom GmbH
//
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
width: 640
height: 480
visible: true
title: Application.displayName
header: ToolBar {
ColumnLayout {
RowLayout {
Label {
text: "Rows"
}
SpinBox {
id: rowSettings
from: 1
to: 20
}
ToolSeparator {}
Label {
text: "Columns"
}
SpinBox {
id: columnSettings
from: 1
to: 20
}
ToolSeparator {}
CheckBox {
id: movableColumnsSetting
text: "Movable columns"
}
ToolSeparator {}
CheckBox {
id: resizableColumnsSetting
text: "Resizable columns"
}
}
RowLayout {
Label {
text: "Selection"
}
ComboBox {
id: selectionSetting
textRole: "text"
valueRole: "value"
model: [
{ text: "disabled", value: TableView.SelectionDisabled },
{ text: "by cells", value: TableView.SelectCells },
{ text: "by rows", value: TableView.SelectRows },
{ text: "by columns", value: TableView.SelectColumns }
]
onCurrentIndexChanged: tableView.selectionModel.clear()
}
Label {
text: "Longpress to start selection, modify selection with CTRL/SHIFT of by mouse"
visible: selectionSetting.currentIndex > 0
}
}
}
}
Rectangle {
id: tableBackground
anchors.fill: parent
color: Application.styleHints.colorScheme === Qt.Light ? palette.mid : palette.midlight
HorizontalHeaderView {
id: horizontalHeader
anchors.left: tableView.left
anchors.top: parent.top
syncView: tableView
movableColumns: movableColumnsSetting.checked
resizableColumns: resizableColumnsSetting.checked
clip: true
boundsBehavior: tableView.boundsBehavior
delegate: HorizontalHeaderViewDelegate {
onClicked: (sortOrder) => tableView.model.sort(column, sortOrder)
}
}
VerticalHeaderView {
id: verticalHeader
anchors.top: tableView.top
anchors.left: parent.left
syncView: tableView
clip: true
boundsBehavior: tableView.boundsBehavior
delegate: VerticalHeaderViewDelegate {}
}
TableView {
id: tableView
anchors.left: verticalHeader.right
anchors.top: horizontalHeader.bottom
anchors.right: parent.right
anchors.bottom: parent.bottom
clip: true
columnSpacing: 1
rowSpacing: 1
rowHeightProvider: (row) => 40
boundsBehavior: TableView.StopAtBounds
selectionModel: ItemSelectionModel {}
selectionBehavior: selectionSetting.currentValue
model: SortFilterModel {
sourceModel: TableModel {
columns: columnSettings.value
rows: rowSettings.value
// when adding a new column its width isn't properly applied to the header, so we do that manually
onColumnsInserted: {
if (columns > 1) {
horizontalHeader.setColumnWidth(columns - 1, tableView.implicitColumnWidth(columns - 1))
}
}
}
}
delegate: TableViewDelegate {
implicitWidth: tableView.width / columnSettings.to
}
ScrollBar.horizontal: ScrollBar {}
ScrollBar.vertical: ScrollBar {}
}
SelectionRectangle {
target: tableView
}
}
}

View File

@@ -1,45 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
ApplicationWindow
{
visible: true
width: 600
height: 400
title: "TableView mit myChildModel"
TreeView
{
anchors.fill: parent
clip: true
columnSpacing: 1
rowSpacing: 1
model: myChildModel
delegate: Rectangle
{
implicitWidth: 150
implicitHeight: 40
border.color: "#cccccc"
//color: index % 2 === 0 ? "#f9f9f9" : "#e0e0e0"
Text {
anchors.centerIn: parent
text: display
font.pixelSize: 14
}
}
ScrollBar.horizontal: ScrollBar {}
ScrollBar.vertical: ScrollBar {}
}
}

View File

@@ -1,65 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window
{
width: 640
height: 480
visible: true
title: qsTr("StringListModel")
TableView
{
id: childTableView
anchors.fill: parent
model: myChildModel // z.B. QStandardItemModel mit 9 Spalten
delegate: Rectangle
{
required property string display
//height: 5
//width: childTableView.width
color : "blue"
border.color: "#ccc"
width: childTableView.width;
RowLayout
{
anchors.fill: parent
anchors.margins: 2
TextField
{
text : display
font.pixelSize: 10
Layout.fillWidth: true
background: Rectangle
{
color : "white"
}
onEditingFinished:
{
console.log("Editing finished, new text is :"+ text + " at index :" + index)
model.names = text //The roles here are defined in model class
}
}
}
}
ScrollBar.horizontal: ScrollBar {}
ScrollBar.vertical: ScrollBar {}
// // Optional: Spaltenbreiten setzen
// onModelChanged: {
// for (let i = 0; i < model.columns; ++i)
// table.setColumnWidth(i, 100)
// }
}
}

83
qml/xqtreeview.qml Normal file
View File

@@ -0,0 +1,83 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
TreeView
{
anchors.fill: parent
clip: true
columnSpacing: 1
rowSpacing: 1
model: xtrChildModel
columnWidthProvider: function(column)
{
var z= 1.7*(width / columns);
return z;
}
delegate: Rectangle
{
required property int row
required property int column
required property var model
border.width: 0
implicitWidth: 30
implicitHeight: 20
border.color: "#cccccc"
//color: index % 2 === 0 ? "#f9f9f9" : "#e0e0e0"
//color: TreeView.isSelected ? "#d0eaff" : (row % 2 === 0 ? "#f9f9f9" : "#ffffff")
//color: TreeView.isSelected ? "#d0eaff" : (row % 2 === 0 ? "#f9f9f9" : "#ffffff")
TextField
{
id: currentEntry
anchors.centerIn: parent
text: display
font.pixelSize: 12
// Ändere die Border-Farbe je nachdem, ob das Feld den Fokus hat
property color borderColor: currentEntry.activeFocus ? "dodgerblue" : "gray"
property int borderWidth: currentEntry.activeFocus ? 2 : 1
background: Rectangle {
// Die Farbe des Hintergrunds im Normalzustand
color: "#ffffff"
// Hier werden Rahmenfarbe und -breite definiert
border.color: currentEntry.borderColor
border.width: currentEntry.borderWidth
// Abgerundete Ecken für ein modernes Aussehen
radius: 4
// Sanfter Übergang der Rahmenfarbe und -breite
Behavior on border.color {
ColorAnimation { duration: 150 }
}
Behavior on border.width {
NumberAnimation { duration: 150 }
}
}
onEditingFinished:
{
console.log("Editing finished, new text is :"+ text + " at index :" + index)
model.display = text //The roles here are defined in model class
}
}
}
ScrollBar.horizontal: ScrollBar {}
ScrollBar.vertical: ScrollBar {}
}

View File

@@ -38,6 +38,7 @@ const QString c_ProjectID = "ProjectID";
const QString c_ModelSheetFileName = "xml/modelsheets.xml";
const QString c_ModelDummyFileName = "xml/saved_testfile.xtr";
const QString c_DocumentDirectory = "xml/";
const QString c_DocumentFileName1 = "xml/modeldata1.xtr";
const QString c_DocumentFileName2 = "xml/modeldata2.xtr";
const QString c_DocumentFileName3 = "xml/modeldata3.xtr";

View File

@@ -36,10 +36,6 @@ XQChildModel::XQChildModel( QObject *parent )
void XQChildModel::addModelData( const XQNodePtr& contentRoot )
{
// __fix: set object name ??
qDebug() << " --- create Model Data: " << contentRoot->to_string();
// Die Datenbasis als shared_ptr sichern
_contentRoot = contentRoot;
@@ -50,70 +46,81 @@ void XQChildModel::addModelData( const XQNodePtr& contentRoot )
// Das ist hier der Typ des Eintrags: Panel, Battery ...
QString key = contentEntry->tag_name();
// 'silent failure' hier der Datenbaum kann auch Knoten enthalten
// die nicht für uns gedacht sind.
if (!_sections.hasValidSection(key))
continue;
XQModelSection& section = _sections.at( key );
// wir speichern das parent des datenknoten auch in der
// section.
// contentEntry->parent == _contentRoot, aber halt nur weil das model flach ist
//qDebug() << " --- add section ENTRY: " << key << " TagName: " << contentEntry->attribute("TagName");
const XQModelSection& section = _sections.sectionByKey( key );
section.setContentRootNode( contentEntry->parent() );
int newRow = _sections.lastRow(section);
XQNodePtr sheetNode = section.sheetRootNode();
XQItemList list = _itemFactory.makeRow( XQItemFactory::mData, sheetNode, contentEntry );
XQItemList list = _itemFactory.makeRow( sheetNode, contentEntry );
// als Baum?
//section.headerItem().appendRow( list );
insertRow( newRow, list);
// _hinter_ der letzen zeile einfügen
insertRow( newRow+1, list);
if( contentEntry->has_children())
{
qDebug() << " --- AddModelData: CHILD Found for: :" << contentEntry->tag_name() << " sheet parent: " << sheetNode->tag_name();
if( !sheetNode->has_children() )
qDebug() << " --- AUA";
//else
}
} // for
}
//! Erzeugt eine model-section und fügt den zugehörigen header ein.
void XQChildModel::addSectionEntry( const QString& key, const XQNodePtr& contentEntry )
{
XQModelSection& section = _sections.at( key );
const XQModelSection& section = _sections.sectionByKey( key );
if(section.isValid() )
{
section.setContentRootNode( contentEntry->parent() );
int newRow = _sections.lastRow(section);
int newRow =_sections.lastRow(section);
XQNodePtr sheetNode = section.sheetRootNode();
XQItemList list = _itemFactory.makeRow( XQItemFactory::mHeader, sheetNode, contentEntry );
XQItemList list = _itemFactory.makeRow( sheetNode, nullptr );
insertRow( newRow, list);
}
}
//! erzeugt ein adhoc-contextmenu, je nachdem welche aktionen gerade möflich sind.
//! erzeugt ein adhoc-contextmenu, je nachdem welche aktionen gerade möglich sind.
void XQChildModel::initContextMenu()
{
// __fixme! add a menu title
_contextMenu->clear();
const QModelIndex& curIdx = _treeTable->currentIndex();
bool hasSel = curIdx.isValid() && _treeTable->selectionModel()->hasSelection();
bool canPaste = _clipBoard.canPaste( curIdx );
_contextMenu->addAction( "icn11Dummy", "Undo", XQCommand::cmdUndo, _undoStack->canUndo() );
_contextMenu->addAction( "icn17Dummy", "Redo", XQCommand::cmdRedo, _undoStack->canRedo() );
_contextMenu->addAction( "icn58Dummy", "Cut", XQCommand::cmdCut, hasSel );
_contextMenu->addAction( "icn61Dummy", "Paste", XQCommand::cmdPaste, canPaste );
_contextMenu->addAction( "icn55Dummy", "Copy", XQCommand::cmdCopy, hasSel );
//_contextMenu->addAction( "icn35Dummy", "Move", XQCommand::cmdMove, hasSel );
_contextMenu->addAction( "icn70Dummy", "New", XQCommand::cmdNew, hasSel );
_contextMenu->addAction( "icn50Dummy", "Delete", XQCommand::cmdDelete, hasSel );
// editieren nur wenns kein header ist.
if ( !xqItemFromIndex(curIdx).isHeaderStyle() )
{
bool hasSel = curIdx.isValid() && _treeTable->selectionModel()->hasSelection();
bool canPaste = _clipBoard.canPaste( curIdx );
_contextMenu->addAction( "icn58Dummy", "Cut", XQCommand::cmdCut, hasSel );
_contextMenu->addAction( "icn61Dummy", "Paste", XQCommand::cmdPaste, canPaste );
_contextMenu->addAction( "icn55Dummy", "Copy", XQCommand::cmdCopy, hasSel );
//_contextMenu->addAction( "icn35Dummy", "Move", XQCommand::cmdMove, hasSel );
_contextMenu->addAction( "icn70Dummy", "New", XQCommand::cmdNew, hasSel );
_contextMenu->addAction( "icn50Dummy", "Delete", XQCommand::cmdDelete, hasSel );
}
// __fixme! set 'toggle section <name>' entry
//contextMenu.actions().first()->setText("<name>");
_contextMenu->addAction( "icn29Dummy", "Toggle Section", XQCommand::cmdToggleSection, hasSel);
_contextMenu->addAction( "icn29Dummy", "Hide Section", XQCommand::cmdToggleSection, true );
}

View File

@@ -18,8 +18,8 @@
//! erzeugt ein docukument
XQDocument::XQDocument(const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aModelView )
: fileName{ aFileName }, friendlyName{ aFriendlyName }, treeItem{ aTreeItem }, modelView{ aModelView }
XQDocument::XQDocument(const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aViewModel )
: fileName{ aFileName }, friendlyName{ aFriendlyName }, treeItem{ aTreeItem }, viewModel{ aViewModel }
{
}
@@ -44,9 +44,9 @@ XQDocumentStore::~XQDocumentStore()
//! erzeugt ein document eintrag
void XQDocumentStore::addDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aModelView )
void XQDocumentStore::addDocument(const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aViewModel )
{
XQDocument newDocument( aFileName, aFriendlyName, aTreeItem, aModelView );
XQDocument newDocument( aFileName, aFriendlyName, aTreeItem, aViewModel );
addAtKey( aFileName, newDocument );
// attention: this assumes the presence of the 'ProjectID' value
//addAlias( aFileName, aTreeItem->attribute(c_ProjectID) );

View File

@@ -28,12 +28,10 @@ struct XQDocument
XQDocument() = default;
XQDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aModelView );
virtual ~XQDocument() = default;
QString fileName; // also used as key
QString friendlyName;
XQItem* treeItem{};
XQViewModel* modelView{};
XQViewModel* viewModel{};
};
@@ -46,11 +44,7 @@ public:
XQDocumentStore() = default;
virtual ~ XQDocumentStore();
void addDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aModelView );
protected:
XQNode _treeRootNode{ "treeRootNode" };
void addDocument( const QString& aFileName, const QString& aFriendlyName, XQItem* aTreeItem, XQViewModel* aViewModel );
};

View File

@@ -45,57 +45,36 @@ void XQMainModel::initContextMenu()
XQItem* XQMainModel::addProjectItem( XQNodePtr contentNode )
{
// wir durchsuchen alle unsere section nach dem passenden content-type,
// hier: content-type beschreibt die
// wir durchsuchen alle unsere sections nach dem passenden content-type,
// hier: content-type beschreibt den projekt-status
for(const auto& section : _sections )
const QString& sectionKey = contentNode->attribute(c_ContentType);
if( _sections.hasValidSection( sectionKey ) )
{
const XQModelSection& section = _sections.sectionByKey( sectionKey );
// __fixme! das ist mist!
const XQNodePtr sheetNode = section.sheetRootNode()->first_child();
XQItem* newItem = _itemFactory.makeSingleItem( sheetNode, contentNode->attribute( "ProjectName") );
if( contentNode->attribute( c_ContentType) == section.contentType() )
{
qDebug() << " --- add PROJECT: contentNode: " << contentNode->to_string();
// __fixme! das ist mist!
const XQNodePtr sheetNode = section.sheetRootNode()->first_child();
XQItemList list = _itemFactory.makeRow( XQItemFactory::mSingle, sheetNode, contentNode, "ProjectName");
// den neuen eintrag in die passende section der übersicht eintragen ...
section.headerItem().appendRow( list );
// ... ausklappen...
const QModelIndex index = section.headerItem().index();
_treeTable->expand( index );
// ... und markieren
_treeTable->setCurrentIndex( index );
// quellknoten auch speichern
//newItem->setContentNode( contentNode );
//emit itemCreated( newItem );
XQItem* newItem = dynamic_cast<XQItem*>(list[0]);
// erzeuger sheet node speichern
newItem->setSheetNode( sheetNode );
return newItem;
}
// den neuen eintrag in die passende section der übersicht eintragen ...
section.headerItem().appendRow( newItem );
// erzeuger sheet node speichern
newItem->setSheetNode( sheetNode );
expandNewItem(section.headerItem().index() );
return newItem;
}
throw XQException( "addProjectItem: main model should not be empty!" );
}
//! erzeugt einen einzelen baum-eintrag mit hilfe der section und den projekt-daten
void XQMainModel::addSectionItem( const XQModelSection& section, XQItem* projectItem )
{
return;
// ich brauche _meine_ section für den sheetNode!
XQNodePtr sheetNode = projectItem->sheetNode()->find_child_by_tag_name("CurrentSection");
XQItemList list = _itemFactory.makeRow( XQItemFactory::mSingle, sheetNode, nullptr, c_ContentType );
projectItem->appendRow( list );
_treeTable->expand( projectItem->index() );
XQItem* newItem = _itemFactory.makeSingleItem( sheetNode, section.contentType() );
projectItem->appendRow( newItem );
expandNewItem(projectItem->index() );
}

View File

@@ -35,6 +35,7 @@ public:
XQItem* addProjectItem( XQNodePtr contentNode );
void addSectionItem( const XQModelSection& section, XQItem* projectItem );
protected:
void initContextMenu() override;

View File

@@ -88,36 +88,39 @@ void XQMainWindow::initMainWindow()
connect( _actionExit, &QAction::triggered, this, &XQMainWindow::onExit );
connect( _actionAbout, &QAction::triggered, this, &XQMainWindow::onAbout );
connect( _tabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(onChildViewTabClicked(int)) );
//connect(&_mainModel, &QStandardItemModel::itemChanged, this, &XQMainWindow::onTreeItemChanged );
//connect( _mainTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex)) );
connect( _mainTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onTreeItemClicked(QModelIndex)) );
connect( _tabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(onTabClicked(int)) );
//connect( _mainTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onTreeItemClicked(QModelIndex)) );
connect(&_mainModel, &XQViewModel::xqItemChanged, this, &XQMainWindow::onTreeViewItemChanged );
/*
connect( &_mainModelView, &XQViewModel::itemCreated, this, [=, this](XQItem* item)
connect( _mainTreeView, &QTreeView::clicked, this, [&,this](const QModelIndex& index)
{
// when a new main tree item has been created ...
QString pID = item->contentNode()->attribute(c_ProjectID);
_mainTreeView->setCurrentIndex( item->index() );
// ... we set the current view to this node
if( _documentStore.contains( pID ) )
_tabWidget->setCurrentWidget( _documentStore[pID].modelView->treeTable() );
} );
*/
onTreeViewItemClicked( XQItem::xqItemFromIndex(index) );
});
try
{
// hand over undostack
_mainModelView.setUndoStack(&_undoStack);
_mainModel.setUndoStack(&_undoStack);
// hand over left side navigation tree
_mainModelView.setTreeTable(_mainTreeView);
_mainModel.setTreeTable(_mainTreeView);
// #1. init the left side main tree view
_mainModelView.initModel( c_MainModelName );
_mainModel.initModel( c_MainModelName );
// 1: Wiebelbach
// 2: Gerbrunn
// 3: TBB
// #2. load demo data
loadDocument( c_DocumentFileName1 );
loadDocumentQML( c_DocumentFileName2 );
loadDocument( c_DocumentFileName2, true );
//loadDocument( c_DocumentFileName2 );
//loadDocument( c_DocumentFileName3 );
qDebug() << " --- all here: " << XQNode::s_Count;
@@ -131,9 +134,6 @@ void XQMainWindow::initMainWindow()
}
//! slot für zentrales undo
void XQMainWindow::onUndo()
@@ -235,7 +235,7 @@ void XQMainWindow::onAbout()
QMessageBox msgBox(QMessageBox::NoIcon, "About", "", QMessageBox::Ok);
QString text = "<b>xtree concept</b><br>";
text += "2024 c.holzheuer<br><br>";
text += "2024-2025 c.holzheuer<br><br>";
text += "<a href=\"https://sourceworx.org/xtree\">sourceworx.org/xtree</a>";
msgBox.setTextFormat(Qt::RichText); // This allows you to click the link
@@ -245,66 +245,190 @@ void XQMainWindow::onAbout()
}
//! wenn ein item im navigations-baum geklickt wird, soll die document
//! view rechts angepasst werden.
void XQMainWindow::onTreeItemClicked(const QModelIndex& index )
void XQMainWindow::onTreeViewItemClicked( const XQItem& item )
{
XQItem& entry = XQItem::xqItemFromIndex(index);
qDebug() << " --- XXX mainWindow onTreeItemClicked:" << entry.text();
_mainTreeView->selectionModel()->select(index, QItemSelectionModel::Select);
if( XQNodePtr contentNode = entry.contentNode() )
//qDebug() << " --- Tree item CLICK:" << item.text() << " : " << item.itemType().text();
if( item.itemType().text() == "TreeChildType" )
{
QString key = contentNode->attribute(c_ProjectID);
qDebug() << " --- FIRZ: key: " << key;
bool isThere = _documentStore.contains(key);
if( isThere)
_tabWidget->setCurrentWidget( _documentStore[key].modelView->treeTable() );
setChildTabByName( item.text() );
}
}
void XQMainWindow::onTreeViewItemChanged(const XQItem& item )
{
qDebug() << " --- TREE VIEW itemChanged: text" << item.text() << " parent: " << item.parent()->text() << " type: " << item.itemType().text() << " : " << (void*)&_mainModel << " : " << (void*) sender();
// hier müssen wir erst das projekt aktivieren
XQItem* xqItem = static_cast<XQItem*>(item.parent());
onTreeViewItemClicked( *xqItem );
//if( item.itemType().text() == "TreeSectionType" )
{
int idx = _tabWidget->currentIndex();
if(_documentStore.contains(idx) )
{
qDebug() << " --- Has Document and might toggle: " << item.text();
XQViewModel& childModel = *_documentStore[idx].viewModel;
childModel.onToggleSection(item.text());
}
}
}
//! beim click auf ein tab im linken fenster wird der navigationsbaum angepasst.
void XQMainWindow::onTabClicked( int index )
void XQMainWindow::onChildViewTabClicked( int idx )
{
//const QString& key = _documentStore[index].treeItem->attribute( c_ProjectID );
//qDebug() << " ---- tab clicked: " << index << " : " << _documentStore[index].friendlyName;// << ": " << key;
//_mainTreeView->setCurrentIndex( _documentStore[index].treeItem->index() );
}
void XQMainWindow::onSectionCreated( const XQModelSection& section )
{
qDebug() << " --- XXX section created: " << section.contentType() << ":" << section.sheetRootNode()->to_string();
if( _currentProjectItem )
if(_documentStore.contains(idx) )
{
_mainModelView.addSectionItem( section, _currentProjectItem );
QModelIndex treeIndex =_documentStore[idx].treeItem->index();
_mainModel.expandNewItem( treeIndex );
}
}
void XQMainWindow::onSectionToggled( const XQModelSection& section )
//! SLOT, der aufgerufen wird, sobald eine section erzeugt worden ist.
void XQMainWindow::onSectionCreated( const XQModelSection& section )
{
//qDebug() << " --- XXX section toggled: " << section.contentType() << ":" << section.sheetRootNode()->to_string();
if( _currentProjectItem )
{
_mainModel.addSectionItem( section, _currentProjectItem );
}
}
QStandardItemModel* createModel() {
auto* model = new QStandardItemModel;
model->setHorizontalHeaderLabels({ "Name" });
QStandardItem* parent = new QStandardItem("Tiere");
parent->appendRow(new QStandardItem("Hund"));
parent->appendRow(new QStandardItem("Katze"));
model->appendRow(parent);
//! SLOT, der aufgerufen wird, wenn eine section getoggelt wurde.
return model;
void XQMainWindow::onSectionToggled( const XQModelSection& section )
{
for (int row = 0; row < _currentProjectItem->rowCount(); ++row)
{
QStandardItem* child = _currentProjectItem->child(row);
if (child->text() == section.contentType() )
{
bool checked = (child->checkState() == Qt::Checked);
child->setCheckState( checked ? Qt::Unchecked :Qt::Checked );
break;
}
}
}
//! aktiviert das tab, das zum dokument mit dem schlüssel 'key' gehört.
void XQMainWindow::setChildTabByName( const QString& key )
{
for( int i=0; i<_documentStore.size(); ++i )
{
if( key == _documentStore[i].friendlyName)
{
_tabWidget->setCurrentIndex(i);
return;
}
}
}
XQNodePtr XQMainWindow::createDataTree( const QString& fileName )
{
// gibts die Datei?
if( !QFile::exists( fileName) )
throw XQException( "no such file", fileName );
XQNodeFactory treeLoader;
// xml daten laden
XQNodePtr rawTree = treeLoader.load_tree( qPrintable(fileName) );
// versteckten root node ignorieren
return rawTree->first_child();
}
XQChildModel* XQMainWindow::createChildModel( const XQNodePtr& contentRoot )
{
// Ein neues Child-Model erzeugen
XQChildModel* childModel = new XQChildModel(this);
connect( childModel, SIGNAL(sectionCreated(XQModelSection)), this, SLOT(onSectionCreated(XQModelSection)) );
connect( childModel, SIGNAL(sectionToggled(XQModelSection)), this, SLOT(onSectionToggled(XQModelSection)) );
// Den globalen undo-stack ...
childModel->setUndoStack(&_undoStack);
// die Modelstruktur anlegen
childModel->initModel( c_ChildModelName );
// model inhalte laden
childModel->addModelData( contentRoot->first_child() );
childModel->setObjectName( contentRoot->friendly_name() );
return childModel;
}
//! liest eine XML datei namens 'fileName'
void XQMainWindow::loadDocument( const QString& fileName, bool useQML )
{
// Datenbaum laden
XQNodePtr contentRoot = createDataTree( fileName );
// Falls schon vorhanden ...
const QString& pID = contentRoot->attribute(c_ProjectID);
int idx = _documentStore.indexOf( pID );
if( idx > -1 )
{
const XQDocument& document = _documentStore.at(idx);
QMessageBox::warning( this, "Load Document", QString("File: %1 already loaded.").arg( fileName ) );
_mainTreeView->setCurrentIndex( document.treeItem->index() );
_tabWidget->setCurrentIndex( idx );
// ... wird nichts wieter unternommen
return;
}
// 'friendly Name' ist ein Link auf ein anderes Attribute
// das als Namen verwendet wird.
const QString& fName = contentRoot->friendly_name();
QString pTabTitle = QString("Project %1: %2").arg( pID, fName );
// neuen eintrag im übsichts-baum erzeugen
_currentProjectItem = _mainModel.addProjectItem( contentRoot );
// Kindmodel für den Datenbaum erzeugen.
XQChildModel* childModel = createChildModel(contentRoot);
_documentStore.addDocument( fileName, fName, _currentProjectItem, childModel );
QWidget* childView{};
if(useQML)
{
XQQuickWidget* quickView = new XQQuickWidget(_tabWidget);
//quickChild->setResizeMode(QQuickWidget::SizeViewToRootObject);
quickView->rootContext()->setContextProperty("xtrChildModel", childModel);
quickView->setSource(QUrl("qrc:/xqtreeview.qml"));
childView = quickView;
}
else
{
// Eine neue TreeView erzeugn und im TabWidget parken.
XQTreeTable* treeTable = new XQTreeTable(_tabWidget);
// und die TreeView übergeben
childModel->setTreeTable(treeTable);
childView = treeTable;
}
_tabWidget->addTab( childView, pTabTitle );
_tabWidget->setCurrentWidget( childView );
setWindowTitle( pTabTitle );
}
void XQMainWindow::loadDocumentQML( const QString& fileName )
{
// gibts die Datei?
@@ -332,75 +456,14 @@ void XQMainWindow::loadDocumentQML( const QString& fileName )
XQQuickWidget* quickChild = new XQQuickWidget(_tabWidget);
//quickChild->setResizeMode(QQuickWidget::SizeViewToRootObject);
quickChild->rootContext()->setContextProperty("myChildModel", childModel);
quickChild->setSource(QUrl("qrc:/xqtableview.qml"));
quickChild->rootContext()->setContextProperty("xtrChildModel", childModel);
quickChild->setSource(QUrl("qrc:/xqtreeview.qml"));
_tabWidget->addTab( quickChild, "QML:"+fName );
_tabWidget->setCurrentWidget( quickChild );
quickChild->setResizeMode(QQuickWidget::SizeRootObjectToView);
}
//! liest eine XML datei namens 'fileName'
void XQMainWindow::loadDocument( const QString& fileName )
{
// gibts die Datei?
if( !QFile::exists( fileName) )
throw XQException( "no such file", fileName );
XQNodeFactory treeLoader;
// xml daten laden
XQNodePtr rawTree = treeLoader.load_tree( qPrintable(fileName) );
// versteckten root node ignorieren
XQNodePtr contentRoot = rawTree->first_child();
// Project-ID behandeln
const QString& pID = contentRoot->attribute(c_ProjectID);
int idx = _documentStore.indexOf( pID );
if( idx > -1 )
{
const XQDocument& document = _documentStore.at(idx);
QMessageBox::warning( this, "Load Document", QString("File: %1 already loaded.").arg( fileName ) );
_mainTreeView->setCurrentIndex( document.treeItem->index() );
_tabWidget->setCurrentIndex( idx );
return;
}
// 'friendly Name' ist ein Link auf ein anderes Attribute
// das als Namen verwendet wird.
const QString& fName = contentRoot->friendly_name();
QString pTitle = QString("Project %1: %2").arg( pID, fName );
// Eine neue TreeView erzeugn und im TabWidget parken.
XQTreeTable* childTreeView = new XQTreeTable(_tabWidget);
_tabWidget->addTab( childTreeView, pTitle );
_tabWidget->setCurrentWidget( childTreeView );
setWindowTitle( pTitle );
// Ein neues Child-Model erzeugen
XQChildModel* childModel = new XQChildModel(this);
connect( childModel, SIGNAL(sectionCreated(XQModelSection)), this, SLOT(onSectionCreated(XQModelSection)) );
connect( childModel, SIGNAL(sectionToggled(XQModelSection)), this, SLOT(onSectionToggled(XQModelSection)) );
// Den globalen undo-stack ...
childModel->setUndoStack(&_undoStack);
// und die TreeView übergeben
childModel->setTreeTable(childTreeView);
// neuen eintrag im übsichts-baum erzeugen
_currentProjectItem = _mainModelView.addProjectItem( contentRoot );
_documentStore.addDocument( fileName, pTitle, _currentProjectItem, childModel );
// die Modelstruktur anlegen
childModel->initModel( c_ChildModelName );
// model inhalte laden
childModel->addModelData( contentRoot->first_child() );
}
//! speichert ein XML unter dem 'filename'
@@ -409,7 +472,9 @@ void XQMainWindow::saveDocument( const QString& fileName )
{
XQNodeWriter nodeWriter;
int curIdx = _tabWidget->currentIndex();
XQNodePtr rootNode = _documentStore[curIdx].treeItem->contentNode();
//XQNodePtr rootNode = _documentStore[curIdx].treeItem->contentNode();
XQNodePtr rootNode = _documentStore[curIdx].viewModel->contentRootNode();
Q_ASSERT(rootNode);
nodeWriter.dumpTree( rootNode, fileName );
}

View File

@@ -35,6 +35,11 @@ public:
public slots:
virtual void onMyFirz(XQItem& item)
{
qDebug() << " --- myFirz: " << item.text();
}
void onUndo();
void onRedo();
@@ -46,31 +51,38 @@ public slots:
void onAbout();
void onExit();
void onTreeItemClicked(const QModelIndex& index );
void onTabClicked( int index );
void onTreeViewItemClicked( const XQItem& item );
void onTreeViewItemChanged( const XQItem& item );
void onChildViewTabClicked( int index );
//void onItemCreated( XQItem* item );
void onSectionCreated( const XQModelSection& section);
void onSectionToggled( const XQModelSection& section );
void setChildTabByName( const QString& key );
// fixme implement
//void showDocument( const QString& key ){}
void loadDocument( const QString& fileName, bool useQML=false );
void loadDocumentQML( const QString& fileName );
void saveDocument( const QString& fileName );
static void setupWorkingDir();
protected:
XQNodePtr createDataTree( const QString& fileName );
XQChildModel* createChildModel( const XQNodePtr& contentRoot );
// fixme implement
void showDocumnet( const QString& key ){}
void loadDocument( const QString& fileName );
void loadDocumentQML( const QString& fileName );
void saveDocument( const QString& fileName );
QUndoStack _undoStack;
XQDocumentStore _documentStore;
XQMainModel _mainModelView;
XQMainModel _mainModel;
XQItem* _currentProjectItem{};
//XQChildModel* _currentChildModel{};
};

View File

@@ -64,7 +64,7 @@ XQItem::XQRenderStyleMap XQItem::s_RenderStyleMap
{ "CustomRenderStyle", CustomRenderStyle },
{ "PickerStyle", PickerStyle },
{ "SpinBoxStyle", SpinBoxStyle },
{ "ProgressBarStyle", ProgressBarStyle},
{ "ColorBarStyle", ColorBarStyle},
{ "FormattedStyle", FormattedStyle},
};
@@ -74,7 +74,7 @@ XQItem::XQEditorTypeMap XQItem::s_EditorTypeMap
{ "LineEditType", LineEditType },
{ "ComboBoxType", ComboBoxType },
{ "PickerType", PickerType },
{ "ProgressBarType", ProgressBarType },
{ "ColorBarType", ColorBarType },
{ "SpinBoxType", SpinBoxType},
{ "CustomEditorType", CustomEditorType}
};
@@ -116,6 +116,8 @@ XQItem::XQPrefixExponentMap XQItem::s_PrefixExponentMap
};
//! Default konstruktor, setzt einen ungültigen (dummy)
//! itemType.
XQItem::XQItem()
: XQItem{XQItemType::staticItemType()}
@@ -123,6 +125,9 @@ XQItem::XQItem()
}
//! Default konstruktor mit einem vorhandenen itemType.
XQItem::XQItem( XQItemType* itemType )
: QStandardItem{}
{
@@ -130,12 +135,21 @@ XQItem::XQItem( XQItemType* itemType )
}
XQItem::XQItem(XQItemType* itemType, const QString *content )
//! konstruiert ein daten-item mit zeiger auf 'unser' attribut
//! im übergeordneten content-node.
XQItem::XQItem(XQItemType* itemType, const QString* content )
: XQItem{ itemType }
{
setContent(content);
// hier setzen wir direkt ohne umwege den string pointer
QStandardItem::setData( QVariant::fromValue<const QString*>(content), XQItem::ContentRole );
}
XQItem::XQItem( XQItemType* itemType, const QString& content )
: XQItem{ itemType }
{
setText(content);
}
//! ruft den copy-konstruktor auf.
XQItem* XQItem::clone() const
@@ -147,21 +161,36 @@ XQItem* XQItem::clone() const
//! false für ein ungültiges item. 'ungültig' heisst hier, dass nur ein
//! mockup-itemtype gesetzt ist.
bool XQItem::isValid() const
bool XQItem::isValidX() const
{
XQItemType* dummyType = XQItemType::staticItemType();
return QStandardItem::data( XQItem::ItemTypeRole ).value<XQItemType*>() != dummyType;
}
bool XQItem::hasContentNode() const
{
if( column() == 0)
{
QVariant value = QStandardItem::data( XQItem::ContentNodeRole );
return !value.isNull();
}
// sonst: delegieren an den node-Besitzer
QModelIndex pIndex = model()->index( row(), 0 );
if( pIndex.isValid() )
{
XQItem& firstItem = xqItemFromIndex( pIndex );
return firstItem.hasContentNode();
}
return false;
}
//! gibt den content-node zurück.
XQNodePtr XQItem::contentNode() const
{
XQNodePtr node = data( ContentNodeRole ).value<XQNodePtr>();
if( node )
return node;
throw XQException("XQItem::contentNode() nullptr");
return data( ContentNodeRole ).value<XQNodePtr>();
}
@@ -319,37 +348,23 @@ QString XQItem::rawText() const
//! Gibt den string-zeiger auf das attribut aus unseren XQNodePtr zurück.
/*
QString* XQItem::content() const
{
// macht jetzt das, ws draufsteht: gibt einen string* zurück
// macht jetzt das, was draufsteht: gibt einen string* zurück
return data( XQItem::ContentRole ).value<QString*>();
}
*/
//! set den content()-string pointer. (als leihgabe)
void XQItem::setContent( const QString* content )
{
setData( QVariant::fromValue<const QString*>(content), XQItem::ContentRole );
}
//! holt den schlüssel bzw. bezeicher des content() string aus 'unserem' content knoten.
QString XQItem::contentKey() const
{
return contentNode()->attributes().key_of( rawText() );
}
//! gibt den content-format string zurück
//! Gibt den content-format string zurück
QString XQItem::contentFormat() const
{
return data( XQItem::ContentFormatRole ).toString();
}
//! setz den den content format-string. wird im itemType gespeichert.
//! Setzt den den content format-string. wird im itemType gespeichert.
void XQItem::setContentFormat(const QString& contentFormat)
{
@@ -357,7 +372,7 @@ void XQItem::setContentFormat(const QString& contentFormat)
}
//! gibt das read-only auswahl-model zurück (wenn dieses item als
//! Gibt das read-only auswahl-model zurück (wenn dieses item als
//! combobox gerendert wird). wird im itemType gespeichert.
QStandardItemModel* XQItem::fixedChoices() const
@@ -413,6 +428,30 @@ QString XQItem::dataRoleName(int role) const
return XQItem::fetchItemDataRoleName(role);
}
bool XQItem::hasContentPtr() const
{
return !QStandardItem::data( XQItem::ContentRole ).isNull();
}
//! Gibt den content()-String zurück, sofern vorhanden.
//! sonst: gibt der ihnalt der Qt::DisplayRole als fallback
//! zurück.
QString XQItem::contentFallBackText() const
{
if( hasContentPtr() )
{
const QString* contentPtr = QStandardItem::data( XQItem::ContentRole ).value<const QString*>();
if(contentPtr)
return *contentPtr;
}
// wenn wir keinen contentPtr haben, benutzen wir als fallback
// die basis-text() role
return QStandardItem::data( Qt::DisplayRole ).toString();
}
//! angespasste variante von qstandarditem::setData. geteilte attribute
//! werden vom xqitemtype geholt
@@ -438,33 +477,35 @@ QVariant XQItem::data(int role ) const
return itemType().data(role);
}
// Zugriffe auf den sichtbaren inhalt geben den inhalt des string pointer
// auf ein feld in content node wieder.
// DisplayRole gibt den formatieren inhalt wieder. die formatierung übernimmt
// der item type
// auf den original inhalt im content node zurückgeben.
case Qt::DisplayRole :
case XQItem::ContentRole:
{
if( itemType().renderStyle() == XQItem::FormattedStyle)//return "display:"+content();
return itemType().formatText( *this );
[[fallthrough]];
qDebug() << " --- data(XQItem::ContentRole) should NOT be called!";
return *QStandardItem::data( XQItem::ContentRole ).value<QString*>();
}
// EditRole & ContentRole sollen den 'rohen' inhalt unseres string-pointers
// auf den original inhalt im content node zurückgeben.
case Qt::EditRole :
case XQItem::ContentRole:
case Qt::EditRole :
{
// Zugriffe auf den text-inhalt geben den inhalt des string pointer
// auf ein feld in content-node wieder. Wenn kein content-node vorhanden
// ist (single-items), wird Qt::DisplayRole zurückgeliefert.
const QString* contentPtr = QStandardItem::data( XQItem::ContentRole ).value<const QString*>();
if(contentPtr)
return *contentPtr;
return contentFallBackText();
//[[fallthrough]];
}
static const QString s_dummyContent("-");
return s_dummyContent;
// DisplayRole gibt den formatierten inhalt wieder. die formatierung übernimmt
// der item type
case Qt::DisplayRole :
{
QString plainText = contentFallBackText();
//if( renderStyle() == XQItem::FormattedStyle)
if( unitType() != XQItem::NoUnitType)
return XQItemType::formatToSI( plainText, unitType() );
return plainText;
}
case Qt::ToolTipRole:
@@ -479,16 +520,30 @@ QVariant XQItem::data(int role ) const
case ContentNodeRole:
{
// Das Node-Besitzer-Item wohnt in der ersten Spalte,
// wenn wir also der Node-Besitzer item sind ...
if( column() == 0)
return QStandardItem::data( XQItem::ContentNodeRole );
// Das Node-Besitzer-Item wohnt in der ersten Spalte,
// wenn wir also der Node-Besitzer item sind ...
if( column() == 0)
{
QVariant value = QStandardItem::data( XQItem::ContentNodeRole );
if( !value.isNull() )
return value;
// sonst: delegieren an den node-Besitzer
QModelIndex pIndex = model()->index( row(), 0 );
// das gibt immerhin was zurück, was auf nullptr getestet werden kann,
return QVariant::fromValue<XQNodePtr>(nullptr);
// diese variante erzieht uns zur verwendung von 'hasContentNode()'
// was ist besser ?
throw XQException( "ContentNode is nullptr!");
}
// sonst: delegieren an den node-Besitzer
QModelIndex pIndex = model()->index( row(), 0 );
if( pIndex.isValid())
{
XQItem& firstItem = xqItemFromIndex( pIndex );
return firstItem.data( XQItem::ContentNodeRole );
}
throw XQException( "Item has no valid index (yet)!");
}
case Qt::StatusTipRole:
@@ -537,7 +592,6 @@ void XQItem::setData(const QVariant& value, int role )
{
switch(role)
{
case RenderStyleRole :
case EditorTypeRole :
case UnitTypeRole:
@@ -557,30 +611,33 @@ void XQItem::setData(const QVariant& value, int role )
return;
}
// set the raw, unformatted data
case ContentRole:
case Qt::DisplayRole:
case Qt::EditRole:
case XQItem::ContentRole:
{
// string ptr setzen kann die basis.
break;
}
QVariant newValue;
case Qt::EditRole :
{
qDebug() << " --- setting EDITrole: " << value.toString();
break;
}
//if( itemType().renderStyle() == XQItem::FormattedStyle)
if( unitType() != XQItem::NoUnitType)
newValue = XQItemType::unFormatFromSI( value.toString() );
else
newValue = value;
case Qt::DisplayRole :
{
// what will happen? value is a string ptr ?!
qDebug() << " --- setting DISPLAYrole: " << value.toString();
break;
// fallback: wenns keinen content node gibt, dann nehmen wir
// das standardverfahren.
if( !hasContentPtr() )
return QStandardItem::setData( newValue, Qt::DisplayRole );
// wir nehmen den string pointer
const QString* constContentPtr = QStandardItem::data( XQItem::ContentRole ).value<const QString*>();
// aua, aua, muss aber sein, weil sonst alle anderen consts nicht durchgehalten werden könnten
*const_cast<QString*>(constContentPtr) = newValue.toString();
return;
}
// alles andere wie gehabt
case ContentNodeRole:
case SheetNodeRole:
//case TypeKeyRole: not used
default:
break;
@@ -591,6 +648,133 @@ void XQItem::setData(const QVariant& value, int role )
}
//! erzeugt eine QVariant zur gegebenen rolle aus dem gegebenen string.
//! Hack: Das Item wird hier als zusätzliche datenquelle übergeben,
//! um _vorher_ erzeugte eigenschaften des items als parameter für _jetzt_
//! erzeugte eigenschaft verwenden zu können.
//! Beispiel
QVariant XQItem::makeVariant( XQItem* item, int dataRole, const QString& source )
{
QVariant value;
//qDebug() << " ----- makeVariant: " << XQItem::fetchItemDataRoleName( dataRole );
switch(dataRole)
{
// das ist ein pointer auf den original-string aus dem XML
case XQItem::ContentRole:
{
// content() -> QString*
value = QVariant::fromValue(&source);
break;
}
/*
case XQItem::ItemTypeRole:
{
// itemType() -> XQItemType*
XQItemType* itemType = findItemTypeTemplate( source );
value = QVariant::fromValue(itemType);
break;
}
*/
case XQItem::RenderStyleRole:
{
XQItem::RenderStyle renderStyle = XQItem::fetchRenderStyle( source );
value = QVariant::fromValue(renderStyle);
break;
}
case XQItem::EditorTypeRole:
{
XQItem::EditorType editorType = XQItem::fetchEditorType( source );
value = QVariant::fromValue(editorType);
break;
}
case XQItem::UnitTypeRole:
{
//qDebug() << " --- make unit type: " << source;
XQItem::UnitType unitType = XQItem::fetchUnitType( source );
value = QVariant::fromValue(unitType);
break;
}
case XQItem::ContentFormatRole:
{
// contentFormat() -> QString
value = QVariant::fromValue(source);
break;
}
case XQItem::FlagsRole:
{
QFlags itemFlags = Qt::NoItemFlags;
const QStringList flagKeys = source.split( '|' );
for( const QString& flagKey : flagKeys )
{
Qt::ItemFlag flag = XQItem::fetchItemFlag( flagKey );
itemFlags.setFlag( flag );
}
value = QVariant::fromValue(itemFlags);
break;
}
case XQItem::IconRole:
{
QIcon typeIcon = XQAppData::typeIcon(source);
value = QVariant::fromValue(typeIcon);
break;
}
case XQItem::FixedChoicesRole:
{
const QStringList choices = source.split( '|' );
QStandardItemModel* fixedChoices = new QStandardItemModel();
for( const QString& entry : choices )
{
QString result = entry;
if( item->unitType() != XQItem::NoUnitType )
result = XQItemType::formatToSI( entry, item->unitType() );
fixedChoices->appendRow( new QStandardItem( result ) );
}
value = QVariant::fromValue(fixedChoices);
break;
}
/*
case XQItem::ContentNodeRole:
{
value = QVariant::fromValue(&source);
break;
}
case XQItem::XQItem::SheetNodeRole:
{
value = QVariant::fromValue(&source);
break;
}
*/
default:
case XQItem::XQItem::NoRole:
{
break;
}
};
//if( !value.toString().isEmpty())
// setData( value, dataRole);
return value;
}
//! gibt ein statisches invalid-item zurück, anstelle von nullptr
@@ -694,13 +878,10 @@ XQItem::UnitType XQItem::fetchUnitType(const QString& unitTypeKey)
return s_UnitTypeMap.key(unitTypeKey);
}
//! gibt die bezeichung für den gegebenen unitType aus.
QString XQItem::fetchUnitTypeToString( UnitType unitType)
{
return s_UnitTypeMap[unitType];
}
/// ---
/// ---
/// ---

View File

@@ -15,6 +15,7 @@
#ifndef XQITEM_H
#define XQITEM_H
#include <QtQmlIntegration>
#include <QVariant>
#include <QVector>
#include <QStandardItem>
@@ -35,6 +36,8 @@ class XQItemType;
class XQItem : public QStandardItem
{
QML_ELEMENT
friend class XQItemFactory;
public:
@@ -81,7 +84,7 @@ public:
ComboBoxStyle,
PickerStyle,
SpinBoxStyle,
ProgressBarStyle,
ColorBarStyle,
FormattedStyle,
TreeHeaderStyle,
CustomRenderStyle,
@@ -96,7 +99,7 @@ public:
LineEditType,
ComboBoxType,
PickerType,
ProgressBarType,
ColorBarType,
SpinBoxType,
CustomEditorType,
EditorTypeEnd
@@ -130,6 +133,7 @@ public:
XQItem();
XQItem( XQItemType* itemType );
XQItem( XQItemType* itemType, const QString* content );
XQItem( XQItemType* itemType, const QString& content );
virtual ~XQItem() = default;
@@ -137,14 +141,16 @@ public:
//! -- not used at the moment --
XQItem* clone() const override;
//!
bool isValid() const;
//! __fix Tested, ob ein itemtype vorhanden ist.
bool isValidX() const;
//! gibt den zu diesem item gehörigen datenknoten zurück
virtual XQNodePtr contentNode() const;
bool hasContentNode() const;
//! gibt den zu diesem item gehörigen datenknoten zurück
XQNodePtr contentNode() const;
virtual XQNodePtr sheetNode() const;
virtual void setSheetNode( const XQNodePtr& sheetNode );
XQNodePtr sheetNode() const;
void setSheetNode( const XQNodePtr& sheetNode );
XQItemType& itemType() const;
void setItemType( XQItemType* itemTypePtr );
@@ -159,9 +165,8 @@ public:
QString rawText() const;
// changed: gibt jetzt den pointer zurück.
QString* content() const;
QString contentKey() const;
void setContent( const QString* content );
//QString* content() const;
//void setContent( const QString* content );
//
// Convenience-Funktionen zum Memberzugriff, die Implementierung
@@ -219,6 +224,8 @@ public:
/// Static convenience methods
///
static QVariant makeVariant( XQItem* item, int dataRole, const QString& source );
static XQItem& xqItemFromIndex( const QModelIndex& index );
static XQItem& fallBackDummyItem();
@@ -238,6 +245,10 @@ protected:
XQItem(const XQItem& other) = default;
XQItem& operator=(const XQItem& other) = default;
bool hasContentPtr() const;
QString contentFallBackText() const;
// das ist protected, weil damit der content()-zugriff demoliert werden kann
void setContentNode(const XQNodePtr& contentNode );
using XQItemFlagMap = QMap<QString,int>;
@@ -259,9 +270,7 @@ protected:
};
Q_DECLARE_METATYPE(XQItem);
Q_DECLARE_METATYPE(XQItem::RenderStyle);
Q_DECLARE_METATYPE(XQItem::EditorType);
Q_DECLARE_METATYPE(XQItem::UnitType);

View File

@@ -17,6 +17,7 @@
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QProgressBar>
#include <QSlider>
#include <QPainter>
#include <QHeaderView>
@@ -28,153 +29,167 @@
#include <xqviewmodel.h>
//! erzeugt eine editorfactory mit den hauseigenen editortypen.
class XQItemEditorFactory : public QItemEditorFactory
{
public:
XQItemEditorFactory()
{
registerEditor(XQItem::LineEditType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::ComboBoxType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::ComboBoxType, new QStandardItemEditorCreator<QComboBox>());
registerEditor(XQItem::PickerType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::ProgressBarType, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItem::SpinBoxType, new QStandardItemEditorCreator<QLineEdit>());
//registerEditor(XQItem::ColorBarType, new QStandardItemEditorCreator<QProgressBar>());
registerEditor(XQItem::ColorBarType, new QStandardItemEditorCreator<QSlider>());
registerEditor(XQItem::SpinBoxType, new QStandardItemEditorCreator<QSpinBox>());
registerEditor(XQItem::CustomEditorType, new QStandardItemEditorCreator<QLineEdit>());
/*
registerEditor(XQItem::LineEditStyle, new QStandardItemEditorCreator<QLineEdit>());
registerEditor(XQItemType::ComboBoxStyle, new QStandardItemEditorCreator<QComboBox>());
//registerEditor(XQItemType::ProgressBarStyle, new QStandardItemEditorCreator<QProgressBar>());
registerEditor(XQItemType::SpinBoxStyle, new QStandardItemEditorCreator<QSpinBox>());
*/
/*
registerEditor(XQItem::etDoubleSpinType, new QStandardItemEditorCreator<QDoubleSpinBox>());
registerEditor(XQItemItemTypes::etDoubleSpinType, new QStandardItemEditorCreator<QDoubleSpinBox>());
registerEditor(XQItemItemTypes::etIPAddressType, new QStandardItemEditorCreator<NTIpAddressEdit>());
registerEditor(XQItemItemTypes::etLineEditBrowser, new QStandardItemEditorCreator<NTFileSelectLine>());
*/
}
};
//! kontruktor mit dem zusändigen viewModel
XQItemDelegate::XQItemDelegate( XQViewModel& modelView)
: _modelView{modelView}
XQItemDelegate::XQItemDelegate( XQViewModel& viewModel)
: QStyledItemDelegate(), _modelView{viewModel}
{
static XQItemEditorFactory s_EditorFactory;
setItemEditorFactory(&s_EditorFactory);
}
//! gibt die interne tree table zurück
XQTreeTable* XQItemDelegate::treeTable() const
{
return _modelView.treeTable();
}
//! shortcut: gibt das XQItem für den gegebenen index zurück.
XQItem& XQItemDelegate::xqItemFromIndex( const QModelIndex& index ) const
{
return _modelView.xqItemFromIndex( index );
}
//! überladene paint-methode: zeichnet das item je nach render-style.
void XQItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if( !index.isValid() )
qDebug() << " index DEAD!";
XQItem& item = xqItemFromIndex( index );
if( item.isValid() )
if( index.isValid() )
{
XQItem& item = xqItemFromIndex( index );
switch( item.renderStyle() )
{
case XQItem::HeaderStyle :
return drawHeaderStyle( painter, option, index );
return drawHeaderStyle( painter, option, item );
//return drawHeaderStyleX( painter, option, index );
case XQItem::ComboBoxStyle :
return drawComboBoxStyle( painter, option, index );
return drawComboBoxStyle( painter, option, item );
case XQItem::ColorBarStyle :
return drawColorBarStyle( painter, option, item );
// das funktioniert nicht unter windows11
//case XQItem::SpinBoxStyle :
// return drawSpinBoxStyle( painter, option, item );
case XQItem::HiddenStyle :
return;
//case XQItem::ProgressBarStyle :
// return drawProgressBarStyle( painter, option, index );
default:
break;
} // switch
}
else
{
qDebug() << " ---- paint: INDEX DEAD!" ;
}
QStyledItemDelegate::paint(painter, option, index);
}
//! einen section header im header-style zeichnen
void XQItemDelegate::drawHeaderStyle(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
void XQItemDelegate::drawHeaderStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const
{
QStyleOptionHeader headerOption;
XQItem& item = xqItemFromIndex( index );
// use the header as "parent" for style init
QWidget* srcWidget = treeTable();//->header();
QWidget* srcWidget = treeTable();//->header();
headerOption.initFrom(srcWidget);
headerOption.text = index.data(Qt::DisplayRole).toString();
headerOption.rect = option.rect.adjusted(0,0,0,3);
headerOption.text = item.text();//index.data(Qt::DisplayRole).toString();
headerOption.rect = option.rect;//.adjusted(0,0,0,3);
headerOption.styleObject = option.styleObject;
// __ch: reduce inner offset when painting
headerOption.textAlignment |= Qt::AlignVCenter;
headerOption.icon = item.icon();
if (srcWidget != nullptr)
{
// save painter
painter->save();
//value = index.data(Qt::ForegroundRole);
//if (value.canConvert<QBrush>())
//headerOption.palette.setBrush(QPalette::Text, Qt::red );
//headerOption.palette.setBrush(QPalette::Window, Qt::red );
QCommonStyle itemStyle;
//headerOption.backgroundBrush()
//srcWidget->style()->drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
itemStyle.drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
// restore painter
painter->restore();
}
}
// save painter
painter->save();
//value = index.data(Qt::ForegroundRole);
//if (value.canConvert<QBrush>())
//headerOption.palette.setBrush(QPalette::Text, Qt::red );
//headerOption.palette.setBrush(QPalette::Window, Qt::red );
//QCommonStyle itemStyle;
void XQItemDelegate::drawProgressBarStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//qApp->style()->drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
//srcWidget->style()->drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
// warum das nur mit dem commonstyle, ist mir echt unklar.
_commonStyle.drawControl(QStyle::CE_Header, &headerOption, painter, srcWidget);
// restore painter
painter->restore();
int progress = index.data(XQItem::ContentRole ).toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = option.rect;
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textAlignment = Qt::AlignCenter;
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,&progressBarOption, painter);
}
void XQItemDelegate::drawComboBoxStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
//! Zeichnet prozent-werte als balken
void XQItemDelegate::drawColorBarStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const
{
//QStyledItemDelegate::paint(painter, option, item);
// Wert aus dem Modell holen
bool ok;
int value = item.data(Qt::EditRole).toInt(&ok);
if (!ok || value < 0 || value > 100)
return;
// Balkenbereich berechnen
QRect rect = option.rect.adjusted(2, 2, -2, -2); // etwas Padding
int barWidth = static_cast<int>(rect.width() * (value / 100.0));
// Balken zeichnen
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QRect barRect(rect.left(), rect.top(), barWidth, rect.height());
QColor barColor = QColor(100, 180, 255);
painter->setBrush(barColor);
painter->setPen(Qt::NoPen);
painter->drawRect(barRect);
painter->setPen(Qt::black);
painter->drawText(rect, Qt::AlignCenter, item.text() );
painter->restore();
}
//! Zeichnet das Item als combo box.
void XQItemDelegate::drawComboBoxStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const
{
QStyleOptionComboBox comboOption;
QWidget* srcWidget = qobject_cast<QWidget*>(option.styleObject);
QStyleOptionComboBox comboOption;
QStyle* comboStyle = srcWidget->style();
comboOption.initFrom(srcWidget);
// set options
@@ -182,68 +197,102 @@ void XQItemDelegate::drawComboBoxStyle(QPainter *painter, const QStyleOptionView
comboOption.state = option.state | QStyle::State_Selected | QStyle::State_Enabled;
// not editable => only visual, but painter needs to know it
comboOption.editable = false;
comboOption.currentText = index.data(Qt::DisplayRole).toString();
comboOption.currentText = item.text();
// decoration (if any)
comboOption.currentIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
comboOption.currentIcon = qvariant_cast<QIcon>(item.data(Qt::DecorationRole));
comboOption.iconSize = comboOption.currentIcon.actualSize(QSize(option.rect.height() - 3, option.rect.height() - 3));
// save painter
painter->save();
// hier wiederum funktioniert der '_commonStyle' nicht
QStyle* widgetStyle = srcWidget->style();
// draw combo
comboStyle->drawComplexControl(QStyle::CC_ComboBox, &comboOption, painter, srcWidget);
widgetStyle->drawComplexControl(QStyle::CC_ComboBox, &comboOption, painter, srcWidget);
// and combobox label
comboStyle->drawControl(QStyle::CE_ComboBoxLabel, &comboOption, painter, srcWidget);
widgetStyle->drawControl(QStyle::CE_ComboBoxLabel, &comboOption, painter, srcWidget);
// restore painter
painter->restore();
}
void XQItemDelegate::drawSpinBoxStyle(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
//! Zeichnet das Item als spin box.
void XQItemDelegate::drawSpinBoxStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const
{
// xx_fix!
//int value = index.data(XQItem::ContentRole ).toInt();
QStyleOptionSpinBox spinBoxOption;
spinBoxOption.rect = option.rect;
/*
spinBoxOption.text = QString::number(value);
spinBoxOption.textAlignment = Qt::AlignCenter;
spinBoxOption.textVisible = true;
*/
qDebug() << " --- jawas +++? SPINBOX!";
QWidget* srcWidget = qobject_cast<QWidget*>(option.styleObject);
QStyleOptionViewItem viewOption(option);
QStyleOptionSpinBox spinBoxOption;
spinBoxOption.initFrom(srcWidget);
initStyleOption(&viewOption, item.index());
if (option.state & QStyle::State_HasFocus)
{
viewOption.state = viewOption.state ^ QStyle::State_HasFocus; // Fokus nicht auf dem Hintergrund malen
}
QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewOption, painter);
spinBoxOption.rect = option.rect;//.adjusted( 0,0,-40,0);
spinBoxOption.state = option.state | QStyle::State_Sunken; // Sunken-State für den "LineEdit"-Look
spinBoxOption.buttonSymbols = QAbstractSpinBox::UpDownArrows;
spinBoxOption.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled;
spinBoxOption.frame = true;
_commonStyle.drawComplexControl(QStyle::CC_SpinBox, &spinBoxOption, painter, nullptr);
QRect editRect =_commonStyle.subControlRect(QStyle::CC_SpinBox, &spinBoxOption, QStyle::SC_SpinBoxEditField, nullptr);
painter->drawText(spinBoxOption.rect, Qt::AlignCenter, item.text());
QApplication::style()->drawComplexControl(QStyle::CC_SpinBox,&spinBoxOption, painter);
}
QSize XQItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
//! Überschreibt QStyledItemDelegate::sizeHint(option, index);
QSize XQItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return QStyledItemDelegate::sizeHint(option, index);
}
//! Erzeugt ein editor-widget, sofern ein gültiger content-Ptr vorhanden und ein editor-Type gesetzt ist.
QWidget* XQItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
Q_UNUSED(option);
return QStyledItemDelegate::createEditor( parent, option, index );
int editorType = XQItem::xqItemFromIndex(index).editorType();
QWidget* editor = itemEditorFactory()->createEditor(editorType, parent);
if( editor )
XQItem& item = xqItemFromIndex(index);
XQItem::EditorType edType = item.editorType();
if( edType == XQItem::NoEditorType )
{
return editor;
qDebug() << "---- NO Content or NO EditorType";
return nullptr;
}
qDebug() << "---- ed type:" << XQItem::fetchEditorTypeToString( edType ) << ": " << edType;
QWidget* editor = itemEditorFactory()->createEditor(edType, parent);;
//return QStyledItemDelegate::createEditor( parent, option, index );
return editor;
}
//! Füttert einen editor mit den model-daten
void XQItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
XQItem& item = xqItemFromIndex( index );
switch( item.editorType() )
XQItem::EditorType edType = item.editorType();
if( edType == XQItem::NoEditorType )
return;
switch( edType )
{
case XQItemType::ComboBoxType :
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
// wir erwarten hier ein gültiges model?
comboBox->setModel( item.fixedChoices());
comboBox->setCurrentText( item.data().toString() );
comboBox->showPopup();
@@ -251,24 +300,37 @@ void XQItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) co
}
default:
break;
// wir benutzen hier die DisplayRole wenn der Inhalt schon formatiert ist.
int role = item.renderStyle() == XQItem::FormattedStyle ? Qt::DisplayRole : Qt::EditRole;
QVariant value = index.data(role);
QByteArray userProp = editor->metaObject()->userProperty().name();
if (!userProp.isEmpty())
{
if (!value.isValid())
value = QVariant(editor->property(userProp).metaType());
editor->setProperty(userProp, value);
}
}
QStyledItemDelegate::setEditorData(editor, index);
}
void XQItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
//! Schreibt die daten aus dem editor ins model zurück
void XQItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
XQItem& item = xqItemFromIndex( index );
switch( item.editorType() )
{
case XQItem::ComboBoxType :
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
item.setData( comboBox->currentText(), Qt::DisplayRole );
return;
}
@@ -280,8 +342,12 @@ void XQItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, co
QStyledItemDelegate::setModelData(editor, model, index);
}
//! Überschreibt QItemDelegate::updateEditorGeometry. Nicht implementiert.
void XQItemDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
//qDebug() << " --- update Editor Geometry";
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}

View File

@@ -16,6 +16,7 @@
#define XQITEMDELEGATE_H
#include <QStyledItemDelegate>
#include <QCommonStyle>
#include <xqappdata.h>
class XQItem;
@@ -32,26 +33,27 @@ class XQItemDelegate : public QStyledItemDelegate
public:
explicit XQItemDelegate(XQViewModel& modelView);
explicit XQItemDelegate(XQViewModel& viewModel);
XQTreeTable* treeTable() const;
XQItem& xqItemFromIndex( const QModelIndex& index ) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel *model, const QModelIndex& index) const override;
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
protected:
void drawHeaderStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawProgressBarStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawComboBoxStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawSpinBoxStyle(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawHeaderStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item ) const;
void drawColorBarStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const;
void drawComboBoxStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const;
void drawSpinBoxStyle(QPainter* painter, const QStyleOptionViewItem& option, const XQItem& item) const;
XQViewModel& _modelView;
QCommonStyle _commonStyle;
};

View File

@@ -29,7 +29,7 @@ void XQItemFactory::initItemFactory( const QString& modelSheetFileName )
for( const auto& [key,value] : sheetNode->attributes() )
{
//qDebug() << " --- conf item Type: " << key << " : " << value;
setItemDataFromString( *itemType, key, value );
setItemTypeDataFromString( *itemType, key, value );
}
};
@@ -91,7 +91,7 @@ XQItemType* XQItemFactory::makeItemType(const XQNodePtr& sheetEntry )
// wenn ja, überschreiben
if( role != XQItem::NoRole )
{
QVariant newValue = makeVariant(role, attrEntry.second );
QVariant newValue = XQItem::makeVariant( itemType, role, attrEntry.second );
itemType = itemType->replaceAttribute( newValue, role );
}
@@ -99,12 +99,13 @@ XQItemType* XQItemFactory::makeItemType(const XQNodePtr& sheetEntry )
return itemType;
}
//! sucht einen item typ aus der map mit 'vorgefertigen' itemtypen.
XQItemType* XQItemFactory::findItemTypeTemplate(const QString& key ) const
{
if( !key.isEmpty() && s_ItemTypeTemplates.contains(key))
return s_ItemTypeTemplates[key];
return s_ItemTypeTemplates[key];
throw XQException( "itemfactory: findItemTypeTemplate: not found:", key );
}
@@ -123,188 +124,21 @@ XQNodePtr XQItemFactory::findModelSheet( const QString& modelName ) const
//! erzeugt eine QVariant aus dem gegebenen string und setzt diese dann via role im item.
void XQItemFactory::setItemDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const
void XQItemFactory::setItemTypeDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const
{
int dataRole = XQItem::fetchItemDataRole( roleKey );
if( dataRole != XQItem::NoRole)
{
QVariant variant = makeVariant( dataRole, source );
QVariant variant = XQItem::makeVariant( &item, dataRole, source );
if( !variant.isNull() && variant.isValid() )
item.setData( variant, dataRole );
}
}
//! erzeugt eine QVariant aus dem gegebenen string
QVariant XQItemFactory::makeVariant( int dataRole, const QString& source ) const
{
QVariant value;
switch(dataRole)
{
// das ist ein pointer auf den original-string aus dem XML
case XQItem::ContentRole:
{
// content() -> QString*
value = QVariant::fromValue(&source);
break;
}
case XQItem::ItemTypeRole:
{
// itemType() -> XQItemType*
//qDebug() << " --- makeVariant: make ItemType: " << source;
XQItemType* itemType = findItemTypeTemplate( source );
value = QVariant::fromValue(itemType);
break;
}
case XQItem::RenderStyleRole:
{
XQItem::RenderStyle renderStyle = XQItem::fetchRenderStyle( source );
value = QVariant::fromValue(renderStyle);
break;
}
case XQItem::EditorTypeRole:
{
XQItem::EditorType editorType = XQItem::fetchEditorType( source );
value = QVariant::fromValue(editorType);
break;
}
case XQItem::UnitTypeRole:
{
//qDebug() << " --- make unit type: " << source;
XQItem::UnitType unitType = XQItem::fetchUnitType( source );
value = QVariant::fromValue(unitType);
break;
}
case XQItem::ContentFormatRole:
{
// contentFormat() -> QString
value = QVariant::fromValue(source);
break;
}
case XQItem::FlagsRole:
{
QFlags itemFlags = Qt::NoItemFlags;
const QStringList flagKeys = source.split( '|' );
for( const QString& flagKey : flagKeys )
{
Qt::ItemFlag flag = XQItem::fetchItemFlag( flagKey );
itemFlags.setFlag( flag );
}
value = QVariant::fromValue(itemFlags);
break;
}
case XQItem::IconRole:
{
QIcon typeIcon = XQAppData::typeIcon(source);
value = QVariant::fromValue(typeIcon);
break;
}
case XQItem::FixedChoicesRole:
{
const QStringList choices = source.split( '|' );
QStandardItemModel* fixedChoices = new QStandardItemModel();
for( const QString& entry : choices )
fixedChoices->appendRow( new QStandardItem( entry ) );
value = QVariant::fromValue(fixedChoices);
break;
}
/*
case XQItem::ContentNodeRole:
{
value = QVariant::fromValue(&source);
break;
}
case XQItem::XQItem::SheetNodeRole:
{
value = QVariant::fromValue(&source);
break;
}
*/
default:
case XQItem::XQItem::NoRole:
{
break;
}
};
//if( !value.toString().isEmpty())
// setData( value, dataRole);
return value;
}
///
/// ------------------------------------------------
///
/*
XQItemList XQItemFactory::makeEmptyRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode )
{
Q_UNUSED(contentNode)
XQItemList list;
// create a data node for each sheet entry
size_t max = sheetNode->children().size();
for( size_t i=0; i<max; ++i )
{
// __fix
//list.append( new XQItem( "", XQItemType::EmptyStyle ) );
}
return list;
}
*/
/*
XQItemList XQItemFactory::createGenericRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode )
{
// we have a new empty contentNode, so we add attributes first.
for( const auto& sheetEntry : sheetNode->children() )
{
QString value = "[" + sheetEntry->tag_name() + "]";
if( sheetEntry->has_attribute("Unit") )
value = "0";
contentNode->set_attribute( sheetEntry->tag_name(), value );
}
if( sheetNode->has_attribute( c_FriendlyName ) )
contentNode->set_attribute( c_FriendlyName, sheetNode->friendly_name() );
// now, we can create a normal entry row
return makeContentRow(contentNode, sheetNode );
}
*/
//! erzeugt eine item-row.
XQItemList XQItemFactory::makeRow(CreationMode mode, const XQNodePtr& sheetNode, const XQNodePtr& contentNode, const QString& captionKey )
XQItemList XQItemFactory::makeRow(const XQNodePtr& sheetNode, const XQNodePtr& contentNode )
{
XQItemList list;
@@ -320,24 +154,31 @@ XQItemList XQItemFactory::makeRow(CreationMode mode, const XQNodePtr& sheetNode,
//
for( const auto& sheetEntry : sheetNode->children() )
list.append( makeItem( mode, sheetEntry, contentNode, captionKey ) );
list.append( makeItem( sheetEntry, contentNode ) );
Q_ASSERT(!list.empty());
// wir merken uns den original content node auch, aber
// im ersten Item.
// im ersten Item. Kann null sein, macht aber erstmal nix.
dynamic_cast<XQItem*>(list[0])->setContentNode(contentNode);
return list;
}
XQItemList XQItemFactory::makeChildRow( XQItem* parent, const XQNodePtr& sheetNode, const XQNodePtr& contentNode )
{
Q_UNUSED(parent);
Q_UNUSED(sheetNode);
Q_UNUSED(contentNode);
//! fixme! unsinn!
//! erzeugt ein XQItem aus einer typ-beschreibung ('sheetNode') und einem daten-knoten ('contentNode').
//! wenn der content node nicht gesetzt ist, wird stattdess das attribut 'Caption' aus der typ-beschreibung
//! verwendet: es handelt sich dann um ein header item, das erzeugt wurde.
return XQItemList();
}
XQItem* XQItemFactory::makeItem(CreationMode mode, const XQNodePtr& sheetNode, const XQNodePtr& contentNode, const QString& captionKey )
//! Erzeugt ein XQItem aus einer typ-beschreibung ('sheetNode') und einem daten-knoten ('contentNode').
//! Wenn der content node nicht gesetzt ist, wird stattdess das attribut 'Caption' aus der typ-beschreibung
//! verwendet: es handelt sich dann um ein header item
XQItem* XQItemFactory::makeItem(const XQNodePtr& sheetNode, const XQNodePtr& contentNode )
{
// den itemtype des neuen items rausfinden
XQItemType* itemType = makeItemType(sheetNode); // throws
@@ -347,19 +188,10 @@ XQItem* XQItemFactory::makeItem(CreationMode mode, const XQNodePtr& sheetNode, c
// das ist Unterschied vom HeaderItem zum normalen Item: Der Titel kommt aus der Modelbeschreibung,
// sonst wird der content indirekt über den tag-name des sheetnode geholt
switch( mode )
{
case mHeader:
contentPtr = sheetNode->attribute_ptr(captionKey);
break;
case mData:
contentPtr = contentNode->attribute_ptr( sheetNode->tag_name() );
break;
case mSingle:
contentPtr = contentNode->attribute_ptr( captionKey );
}
if( !contentNode )
contentPtr = sheetNode->attribute_ptr(c_Caption);
else
contentPtr = contentNode->attribute_ptr( sheetNode->tag_name() );
XQItem* newItem = new XQItem( itemType, contentPtr);
@@ -371,3 +203,18 @@ XQItem* XQItemFactory::makeItem(CreationMode mode, const XQNodePtr& sheetNode, c
return newItem;
}
//! Erzeugt ein Item _ohne_ internen content node, sondern
XQItem* XQItemFactory::makeSingleItem( const XQNodePtr& sheetNode, const QString& caption )
{
// den itemtype des neuen items rausfinden
XQItemType* itemType = makeItemType(sheetNode); // throws
XQItem* newItem = new XQItem( itemType, caption);
// __fixme!
if( newItem->isCheckable() )
{
newItem->setCheckState( Qt::Checked );
}
return newItem;
}

View File

@@ -28,35 +28,25 @@ class XQItemFactory : public xsingleton<XQItemFactory>
public:
enum CreationMode
{
mHeader,
mData,
mSingle
};
void initItemFactory(const QString& modelSheetFileName );
XQNodePtr findModelSheet( const QString& modelName ) const;
//XQItemList makeEmptyRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode );
XQItemList makeRow( const XQNodePtr& sheetNode, const XQNodePtr& contentNode );
XQItemList makeChildRow( XQItem* parent, const XQNodePtr& sheetNode, const XQNodePtr& contentNode );
XQItem* makeSingleItem( const XQNodePtr& sheetNode, const QString& caption );
XQItemList makeRow( CreationMode mode, const XQNodePtr& sheetNode, const XQNodePtr& contentNode, const QString& captionKey=c_Caption );
// wozu ist das gut?
//XQItemList createGenericRow( const XQNodePtr& contentNode, const XQNodePtr& sheetNode );
void setItemDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const;
void setItemTypeDataFromString( XQItem& item, const QString& roleKey, const QString& source ) const;
XQItemType* makeItemType(const XQNodePtr& sheetEntry );
XQItemType* findItemTypeTemplate(const QString& key ) const;
QVariant makeVariant(int dataRole, const QString &value ) const;
protected:
bool isValid();
XQItem* makeItem( CreationMode mode, const XQNodePtr& sheetNode, const XQNodePtr& contentNode, const QString& captionKey );
XQItem* makeItem(const XQNodePtr& sheetNode, const XQNodePtr& contentNode );
// shortcuts
using ItemConfigFunc = std::function<void( XQItem* item, const QString& attrValue, XQNodePtr contentNode, XQNodePtr sheetNode )>;

View File

@@ -113,6 +113,7 @@ XQItemType* XQItemType::replaceAttribute( const QVariant& newValue, int role )
// Gibt es den geänderten ItemType schon?
QString newKey = myClone->makeItemTypeKey();
// jawoll
if( s_ItemTypeMap.contains( newKey ) )
{
// abräumen ...
@@ -134,22 +135,9 @@ XQItemType* XQItemType::replaceAttribute( const QVariant& newValue, int role )
}
//! formatiert den content() string eines items.
QVariant XQItemType::formatText( const XQItem& item ) const
{
XQItem::UnitType uType = unitType();
//qDebug() << " --- formatText: " << XQItem::fetchUnitTypeToString( uType);
const QString& cont = item.rawText();
if( uType != XQItem::NoUnitType )
return formatToSI( cont, uType );
return cont;
}
//! formatiert einen zahlenwert als string mit einheit.
QString XQItemType::formatToSI( const QString& valueTxt, XQItem::UnitType unitType ) const
QString XQItemType::formatToSI( const QString& valueTxt, XQItem::UnitType unitType )
{
if( valueTxt.isEmpty() )
@@ -180,18 +168,18 @@ QString XQItemType::formatToSI( const QString& valueTxt, XQItem::UnitType unitTy
strVal = sysLocale.toString(nVal, 'f', 2);
strPrefix = s_PrefixExponentMap.key(exp);
//qDebug() << " convert: " << dVal << " : " << valueTxt << ": " << strVal << ":" << exp << " : " << strPrefix << ": " << nVal;
return QString("%1 %2%3").arg( strVal, strPrefix, unitTypeToString() );
QString unitStr = XQItem::fetchUnitTypeToString( unitType);
return QString("%1 %2%3").arg( strVal, strPrefix, unitStr );
}
//! entfernt die einheit aus einem formatierten string
QString XQItemType::unFormatFromSI(const QString& formText ) const
QString XQItemType::unFormatFromSI(const QString& formText )
{
QString input = formText.simplified();
const QString input = formText.simplified();
// #1: strip numeric part
if( input.isEmpty() )
return input;

View File

@@ -40,17 +40,14 @@ public:
QVariant data( int role ) const override;
void setData(const QVariant& value, int role ) override;
QVariant formatText( const XQItem& item ) const;
QString formatToSI(const QString& rawText, XQItem::UnitType unitType ) const;
QString unFormatFromSI(const QString& valueText ) const;
int roleForAttributeKey( const QString& attrKey );
XQItemType* replaceAttribute(const QVariant& newValue, int role );
QString makeItemTypeKey();
static XQItemType* staticItemType();
static QString formatToSI(const QString& rawText, XQItem::UnitType unitType );
static QString unFormatFromSI(const QString& valueText );
protected:

View File

@@ -19,6 +19,7 @@
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QQmlContext>
#include <QStyleFactory>
#include <xqchildmodel.h>
#include <xqquickwidget.h>
@@ -73,60 +74,67 @@ XQChildModel* createChildModel()
}
/*
class DummyModel : public XQChildModel
{
public:
CONFIG += precompile_header
PRECOMPILED_HEADER = pch.h
DummyModel()
{
// pch.h Precompiled Header für Qt-Projekt
initModel( c_ChildModelName );
XQNodeFactory treeLoader;
// xml daten laden
XQNodePtr rawTree = treeLoader.load_tree( qPrintable(c_DocumentFileName1) );
// versteckten root node ignorieren
XQNodePtr contentRoot = rawTree->first_child();
addModelData( contentRoot->first_child() );
Stelle sicher, dass pch.h im Projektordner liegt und in den .cpp-Dateien eingebunden wird:
//XQTreeTable* treeTable = new XQTreeTable;
//treeTable->setModel(this);
//setTreeTable( treeTable );
C++
#include "pch.h"
//treeTable->show();
}
};
#ifndef PCH_H
#define PCH_H
// Qt Core
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QDateTime>
// Qt GUI
#include <QtGui/QGuiApplication>
#include <QtGui/QIcon>
#include <QtGui/QPixmap>
// Qt Widgets (falls verwendet)
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QLabel>
// STL
#include <vector>
#include <string>
#include <map>
#include <memory>
#endif // PCH_H
*/
using namespace Qt::Literals::StringLiterals;
int main(int argc, char *argv[])
{
/*
// Signal für einzelne QStandardItem-Änderungen
connect(model, &QStandardItemModel::itemChanged,
this, [](QStandardItem *changedItem){
QVariant state = changedItem->data(Qt::CheckStateRole);
qDebug() << "Neuer Check-State:" << state.toInt();
});
*/
/*
QApplication app(argc, argv);
//app.setStyle("fusion");
//qDebug() << QStyleFactory::keys();
//QApplication::setStyle("fusion");
//QApplication::setStyle("windowsvista");
//QApplication::setStyle("windows");
XQMainWindow window;
window.show();
*/
/*
QApplication app(argc, argv);
XQMainWindow::setupWorkingDir();
XQItemFactory::instance().initItemFactory( c_ModelSheetFileName );
@@ -152,6 +160,7 @@ connect(model, &QStandardItemModel::itemChanged,
qDebug() << " hhakl!";
*/
return app.exec();
}

View File

@@ -28,10 +28,10 @@ void XQNodeStore::dumpList( const QString& title ) const
}
//! kostruktor. übergibt command-type und die aufrufende modelView.
//! kostruktor. übergibt command-type und die aufrufende viewModel.
XQCommand::XQCommand(CmdType cmdType, XQViewModel* modelView )
: _cmdType{ cmdType }, _viewModel(modelView)
XQCommand::XQCommand(CmdType cmdType, XQViewModel* viewModel )
: _cmdType{ cmdType }, _viewModel(viewModel)
{
}
@@ -108,20 +108,22 @@ void XQCommand::setOriginIndex( const QModelIndex& origin )
void XQCommand::saveNodes( const QModelIndexList& list )
{
clear();
// über jede zeil
// über jede zeile
for( auto entry : list )
{
// knoten holen
const XQNodePtr& contentNode = XQItem::xqItemFromIndex( entry ).contentNode();
// hier speichern wir den original knoten, nicht einen clone, wie im clipboard.
push_back( {entry.row(), contentNode->own_pos(), contentNode } );
// obacht: bei einem Header is der content node null
if(contentNode)
push_back( {entry.row(), contentNode->own_pos(), contentNode } );
}
}
//! erzeugt einen string aus dem command-type, fürs debuggen.
QString XQCommand::toString()
QString XQCommand::toString() const
{
static QMap<CmdType,QString> s_CmdTypeMap

View File

@@ -66,7 +66,7 @@ public:
cmdExtern //??
};
XQCommand(CmdType cmdType, XQViewModel* modelView );
XQCommand(CmdType cmdType, XQViewModel* viewModel );
virtual ~XQCommand();
CmdType commandType() const;
@@ -80,7 +80,7 @@ public:
void redo() override;
void undo() override;
QString toString();
QString toString() const;
protected:

View File

@@ -1,206 +0,0 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqmodelsectionlist.h>
//! kontstruktor. übergibt den start-index und einen model-knoten mit der beschreibung
//! der datenknoten.
XQModelSection::XQModelSection(const QModelIndex& modelIndex, XQNodePtr sheetNode)
: _modelIndex{ modelIndex }, _sectionRootNode{ sheetNode }
{
}
//! elementvergleich.
bool XQModelSection::operator==(const XQModelSection& other) const
{
return _modelIndex == other._modelIndex && _sectionRootNode == other._sectionRootNode;
}
//! true wenn der start-index valide und ein model-knoten vorhanden.
bool XQModelSection::isValid() const
{
return _modelIndex.isValid() && _sectionRootNode;
}
QModelIndex XQModelSection::persistentModelIndex() const
{
return _modelIndex.operator QModelIndex();
}
XQNodePtr XQModelSection::sectionRootNode() const
{
return _sectionRootNode;
}
//! Gibt den sheet-node zurück, das ist die model-beschreibung,
//! siehe modelsheet.xml:
//! <section>
//! <header>
//! <data> <- dort
//! __fix! das versteht doch kein mensch!
XQNodePtr XQModelSection::sheetRootNode() const
{
return _sectionRootNode->find_child_by_tag_name( c_ModelSheet );
}
//! Gibt den content root node zurück, das ist der
//! zeiger auf die realen inhalte.
XQNodePtr XQModelSection::contentRootNode() const
{
return _contentRootNode;
}
void XQModelSection::setContentRootNode( const XQNodePtr contentRootNode )
{
_contentRootNode = contentRootNode;
}
//! gibt die zeile des start-index zurück.
int XQModelSection::XQModelSection::row() const
{
return _modelIndex.row();
}
//! gibt den 'content type' zurück.
const QString& XQModelSection::contentType() const
{
return _sectionRootNode->attribute( c_ContentType );
}
//! gibt das dieser section entsprechende header-item zurück.
XQItem& XQModelSection::XQModelSection::headerItem() const
{
return XQItem::xqItemFromIndex( _modelIndex );
}
//! testet, ob die unter 'sectionKey' eine gültige section vorhanden ist.
bool XQModelSectionList::hasValidSection(const QString& sectionKey) const
{
if (!contains(sectionKey) )
return false;
return at(sectionKey).isValid();
}
//! gibt für einen model index die 'zuständige' section zurück.
const XQModelSection& XQModelSectionList::sectionFromIndex( const QModelIndex& index ) const
{
return sectionFromRow( index.row() );
}
//! gibt für eine zeile die 'zuständige' section zurück: der bestand an section wird
//! nach der passenden section durchsucht.
const XQModelSection& XQModelSectionList::sectionFromRow(int itemRow ) const
{
int i = size() - 1;
for (; i >= 0; --i)
{
if ( at(i).persistentModelIndex().row() < itemRow )
return at(i);
}
static XQModelSection s_DummySection;
return s_DummySection;
}
//! ermittelt die erste zeile einer section.
int XQModelSectionList::firstRow(const QModelIndex& idx) const
{
return sectionFromRow(idx.row() ).row();
}
//! ermittelt die zeile unterhalb des gegebenen modelindex,
//! zum einfügen neuer items ebendort.
int XQModelSectionList::lastRow(const QModelIndex& idx) const
{
return lastRow(sectionFromRow(idx.row()));
}
//! ermittelt die zeile unterhalb der gegebenen section,
//! zum einfügen neuer items ebendort.
int XQModelSectionList::lastRow(const XQModelSection& section ) const
{
//qDebug() << " -- last row in section: " << section.modelIndex.data().toString() << " --> " << section.modelIndex.row();
// row() der section unterhalb dieser
// __fix? index mit speichern?
int index = indexOf(section);
if (index > -1)
{
// last section? return last row of model
if (index == size() - 1)
return section.persistentModelIndex().model()->rowCount();// - 1;
// return row above the row of the next section -> last row of given section
return at(index+1).row();
}
return -1;
}
//! gibt alle sections aus, zum ankucken.
void XQModelSectionList::dump() const
{
qDebug() << " --- sections dump(): " <<size() << " entries.";
for( int i = 0; i<size(); ++i )
{
QModelIndex idx = at(i).persistentModelIndex();
qDebug() << " --- sections:" << i << "row: " << idx.row() << " keyOf(i): " << keyOf(i) << " indexData: "<< idx.data().toString() << " itemData: " << XQItem::xqItemFromIndex(idx).data(Qt::DisplayRole).toString();
}
}

View File

@@ -0,0 +1,207 @@
/***************************************************************************
source::worx xtree
Copyright © 2024-2025 c.holzheuer
christoph.holzheuer@gmail.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
***************************************************************************/
#include <xqsectionmanager.h>
//! kontstruktor. übergibt den start-index und einen model-knoten mit der beschreibung
//! der datenknoten.
XQModelSection::XQModelSection(const QModelIndex& modelIndex, XQNodePtr sheetNode)
: _modelIndex{ modelIndex }, _sectionSheetRootNode{ sheetNode }
{
}
//! elementvergleich.
bool XQModelSection::operator==(const XQModelSection& other) const
{
return _modelIndex == other._modelIndex && _sectionSheetRootNode == other._sectionSheetRootNode;
}
//! true wenn der start-index valide und ein model-knoten vorhanden.
bool XQModelSection::isValid() const
{
return _modelIndex.isValid() && _sectionSheetRootNode;
}
QModelIndex XQModelSection::startIndex() const
{
return _modelIndex.operator QModelIndex();
}
XQNodePtr XQModelSection::sectionRootNode() const
{
return _sectionSheetRootNode;
}
//! Gibt den sheet-node zurück, das ist die model-beschreibung,
//! siehe modelsheet.xml:
//! <section>
//! <header>
//! <data> <- dort
//! __fix! das versteht doch kein mensch!
XQNodePtr XQModelSection::sheetRootNode() const
{
return _sectionSheetRootNode->find_child_by_tag_name( c_ModelSheet );
}
//! Gibt den content root node zurück, das ist der
//! zeiger auf die realen inhalte.
XQNodePtr XQModelSection::contentRootNode() const
{
return _contentRootNode;
}
void XQModelSection::setContentRootNode( const XQNodePtr contentRootNode ) const
{
_contentRootNode = contentRootNode;
}
//! gibt die zeile des start-index zurück.
int XQModelSection::XQModelSection::firstRow() const
{
return _modelIndex.row();
}
//! gibt den 'content type' zurück.
const QString& XQModelSection::contentType() const
{
//qDebug() << " ---AUA & AUS!";
return _sectionSheetRootNode->attribute( c_ContentType );
}
//! gibt das dieser section entsprechende header-item zurück.
XQItem& XQModelSection::XQModelSection::headerItem() const
{
return XQItem::xqItemFromIndex( _modelIndex );
}
//! testet, ob die unter 'sectionKey' eine gültige section vorhanden ist.
bool XQSectionManager::hasValidSection(const QString& sectionKey) const
{
if (!_sections.contains(sectionKey) )
return false;
return _sections.at(sectionKey).isValid();
}
const XQModelSection& XQSectionManager::sectionByKey( const QString& sectionKey ) const
{
if( hasValidSection( sectionKey ) )
return _sections.at(sectionKey);
throw XQException( "No section for key", sectionKey);
}
//! gibt für eine zeile die 'zuständige' section zurück: der bestand an section wird
//! nach der passenden section durchsucht.
const XQModelSection& XQSectionManager::sectionByRow(int itemRow ) const
{
for (const auto& section : _sections)
{
qDebug() << " ---- SEC: " << itemRow << " -> " << section.firstRow() << " : " << lastRow( section );
XQSectionRange range = sectionRange(section);
if( itemRow >= range.firstRow && itemRow <= range.lastRow)
return section;
}
throw XQException( "No section for item row", QString::number(itemRow));
}
const XQModelSection& XQSectionManager::createSection(const QModelIndex& modelIndex, XQNodePtr sheetNode)
{
const QString& sectionKey = sheetNode->attribute(c_ContentType);
//qDebug() << " --- create Section: " << sectionKey << ": " << modelIndex.data().toString();
XQModelSection section(modelIndex, sheetNode );
_sections.addAtKey( sectionKey, section);
return sectionByKey(sectionKey);
}
//! ermittelt die zeile unterhalb des gegebenen modelindex,
//! zum einfügen neuer items ebendort.
int XQSectionManager::lastRow(const XQModelSection& section ) const
{
//qDebug() << " -- last row in section: " << section.startIndex().data().toString() << " --> " << section.startIndex().row();
// row() der section unterhalb dieser
// __fix? index mit speichern?
int index = _sections.indexOf(section);
if (index > -1)
{
// last section? return last row of model
if (index == _sections.size() - 1)
return section.startIndex().model()->rowCount() - 1;
// return row above the row of the next section -> last row of given section
return _sections.at(index+1).firstRow() - 1;
}
return -1;
}
XQSectionRange XQSectionManager::sectionRange(const XQModelSection& section ) const
{
return XQSectionRange{ section.startIndex().row(), lastRow(section) };
}
//! gibt alle sections aus, zum ankucken.
void XQSectionManager::dump() const
{
qDebug() << " --- sections dump(): " <<_sections.size() << " entries.";
for( int i = 0; i<_sections.size(); ++i )
{
QModelIndex idx = _sections.at(i).startIndex();
qDebug() << " --- sections:" << i << "row: " << idx.row() << " keyOf(i): " << _sections.keyOf(i) << " indexData: "<< idx.data().toString() << " itemData: " << XQItem::xqItemFromIndex(idx).data(Qt::DisplayRole).toString();
}
}

View File

@@ -12,17 +12,18 @@
***************************************************************************/
#ifndef XQMODELSECTIONLIST_H
#define XQMODELSECTIONLIST_H
#ifndef XQSECTIONMANAGER_H
#define XQSECTIONMANAGER_H
#include <QPersistentModelIndex>
#include <xqmaptor.h>
#include <xqitem.h>
/**
* @brief Struct containing data for a header section
*/
//! Daten zur beschreibung einer 'sektion' des models.
class XQModelSection
{
@@ -35,13 +36,13 @@ public:
bool operator==(const XQModelSection& other) const;
bool isValid() const;
int row() const;
int firstRow() const;
QModelIndex persistentModelIndex() const;
QModelIndex startIndex() const;
XQNodePtr sectionRootNode() const;
XQNodePtr sheetRootNode() const;
XQNodePtr contentRootNode() const;
void setContentRootNode( const XQNodePtr dataRootNode );
void setContentRootNode( const XQNodePtr dataRootNode ) const;
const QString& contentType() const;
XQItem& headerItem() const;
@@ -50,32 +51,42 @@ protected:
QPersistentModelIndex _modelIndex;
XQNodePtr _sectionRootNode{};
XQNodePtr _contentRootNode{};
mutable XQNodePtr _sectionSheetRootNode{};
mutable XQNodePtr _contentRootNode{};
};
Q_DECLARE_METATYPE(XQModelSection)
/**
* @brief Maptor containing all header sections.
*/
//! Erste und letzte ziele einer XQModelSection
struct XQSectionRange
{
int firstRow{-1};
int lastRow{-1};
};
class XQModelSectionList : public XQMaptor<XQModelSection>
//! struktur, die alle sections enthält
class XQSectionManager
{
public:
bool hasValidSection(const QString& sectionKey) const;
const XQModelSection& sectionFromRow( int row ) const;
const XQModelSection& sectionFromIndex( const QModelIndex& index ) const;
const XQModelSection& sectionByKey( const QString& sectionKey ) const;
const XQModelSection& sectionByRow( int row ) const;
int firstRow(const QModelIndex& idx) const;
int lastRow(const QModelIndex& idx) const;
int lastRow(const XQModelSection& section) const;
const XQModelSection& createSection(const QModelIndex& modelIndex, XQNodePtr sheetNode);
int lastRow(const XQModelSection& section ) const;
XQSectionRange sectionRange(const XQModelSection &section) const;
void dump()const override;
void dump()const;
protected:
XQMaptor<XQModelSection> _sections;
};
#endif // XQMODELSECTIONLIST_H
#endif // XQSECTIONMANAGER_H

View File

@@ -35,28 +35,34 @@ XQSelectionModel::XQSelectionModel(QAbstractItemModel* model, QObject* parent)
}
//! firz
//! jetzt die selektierten indices, wie die basisklasse, aber nur die innerhalt einer section.
void XQSelectionModel::select(const QItemSelection& selection, QItemSelectionModel::SelectionFlags command)
{
// step #0: fetch selected indices.
// step #0: die ursprüngliche selection bestimmen
const QModelIndexList list = selection.indexes();
if (list.isEmpty() || selectedRows().isEmpty() )
return QItemSelectionModel::select(selection, command);
// fetch first index
// step 01: den ersten index bestimmen
QModelIndex firstValid = list.first();
if (hasSelection() )
firstValid = selectedRows().first();
//XQItem& firstItem = XQItem::xqItemFromIndex(firstValid);
//if( firstItem.isValid() )
// step 02: finde das erste item gültigem content node.
XQNodePtr firstNode = XQItem::xqItemFromIndex(firstValid).contentNode();
while( !firstNode)
{
firstValid = firstValid.siblingAtRow( firstValid.row()+1);
firstNode = XQItem::xqItemFromIndex(firstValid).contentNode();
}
XQNodePtr firstNode = XQItem::xqItemFromIndex(firstValid).contentNode();
// step 03: selektiere nur knoten, die den gleichen tag_name haben, sich also
// in der selben section befinden
if( firstNode )
{
QItemSelection newSelection;
// __fixme! das crasht!
for (const QModelIndex& idx : list)
{
XQNodePtr nextNode = XQItem::xqItemFromIndex(idx).contentNode();
@@ -66,5 +72,7 @@ void XQSelectionModel::select(const QItemSelection& selection, QItemSelectionMod
}
return QItemSelectionModel::select(newSelection, command);
}
// fallback
QItemSelectionModel::select(selection, command);
}

View File

@@ -35,18 +35,34 @@
void showItemList( const XQItemList& list)
{
for(const auto& entry : list )
qDebug() << " --- itemList: " << ((XQItem*)entry)->content();
qDebug() << " --- itemList: " << entry->text();
qDebug();
}
void showSelectionList( const QModelIndexList& list)
{
for(const auto& entry : list )
qDebug() << " --- SelectionList: " << entry.data().toString();
qDebug();
}
//! Konstruktur mit parent.
//! Konstruktor mit parent.
XQViewModel::XQViewModel( QObject* parent )
: QStandardItemModel{ parent }, _itemFactory{ XQItemFactory::instance() }
{
invisibleRootItem()->setData( "[rootItem]", Qt::DisplayRole );
setItemPrototype( new XQItem );
// auf änderungen kann in den unterklassen reagiert werden
connect(this, &QStandardItemModel::itemChanged, this, [this](QStandardItem *item)
{
XQItem* xqItem = static_cast<XQItem*>(item);
emit xqItemChanged( *xqItem );
});
// not needed
//qRegisterMetaType<XQItem>("XQItem");
}
@@ -59,7 +75,14 @@ const XQItem& XQViewModel::xqRootItem()
// dynamisch über den ItemData Mechanismus wie in QStandardItem
return *static_cast<XQItem*>(invisibleRootItem());
}
//! Gibt den daten root node des models zurück.
XQNodePtr XQViewModel::contentRootNode()
{
return _contentRoot;
}
@@ -83,6 +106,16 @@ XQItem& XQViewModel::xqFirstItem(int row) const
return *static_cast<XQItem*>( QStandardItemModel::item(row) );
}
void XQViewModel::expandNewItem(const QModelIndex& index)
{
if( _treeTable )
{
// ... ausklappen...
_treeTable->expand( index );
// ... und markieren
_treeTable->setCurrentIndex( index );
}
}
//! initialisiert dieses model über den namen. Es wird hier
//! nur die strukur erzeugt, keine inhalte.
@@ -99,9 +132,7 @@ void XQViewModel::initModel(const QString& modelName)
*/
setObjectName( modelName );
qDebug() << " --- initModel: " << objectName();
// model rootnode finden -> <DocumentTreeModel>
// model rootnode finden -> <DocumentTreeModel>
XQNodePtr modelSheet = _itemFactory.findModelSheet( modelName ); // throws
// #1: über alle sections
@@ -111,7 +142,7 @@ void XQViewModel::initModel(const QString& modelName)
const XQNodePtr header = sectionNode->find_child_by_tag_name( c_Header );
if( header )
{
XQItemList list = _itemFactory.makeRow( XQItemFactory::mHeader, header, nullptr );
XQItemList list = _itemFactory.makeRow( header, nullptr );
addSection(list, sectionNode );
}
}
@@ -122,25 +153,24 @@ void XQViewModel::initModel(const QString& modelName)
//! die section kann erst gültig sein, wenn die items im model gelandet sind,
//! deswegen ist das hier zusammengefasst.
//! Wrzeugt dann eine section aus einer frisch erzeugten itemlist. Der erste modelindex
//! Erzeugt dann eine section aus einer frisch erzeugten itemlist. Der erste modelindex
//! der liste und der root knoten der model-beschreibung werden gespeichert.
void XQViewModel::addSection(const XQItemList& list, const XQNodePtr& sectionNode )
void XQViewModel::addSection(const XQItemList& list, const XQNodePtr& sheetNode )
{
// 1. die liste darf nicht leer sein
Q_ASSERT(!list.isEmpty());
// 2. sectionNode muss da sein
Q_ASSERT(sectionNode);
// 2. sheetNode muss da sein
Q_ASSERT(sheetNode);
// 3. 'ContenType' muss vorhanden sein
if( !sectionNode->has_attribute( c_ContentType) )
if( !sheetNode->has_attribute( c_ContentType) )
throw XQException( "section list: Section node needs attribute 'ContentType'!");
// 5. das erzeugt dann auch valide indices
appendRow(list);
// 6. jetzt können wir auch die sction erzeugen
XQModelSection section(list[0]->index(), sectionNode );
_sections.addAtKey(sectionNode->attribute( c_ContentType), section);
// 6. jetzt können wir auch die section erzeugen
const XQModelSection& section = _sections.createSection( list[0]->index(), sheetNode );
// ... und es der welt mitteilen.
emit sectionCreated( section );
@@ -148,11 +178,46 @@ void XQViewModel::addSection(const XQItemList& list, const XQNodePtr& sectionNod
}
//! SLOT, toggled die section mit dem 'sectionKey' (hier: contentType)
void XQViewModel::onToggleSection(const QString& sectionKey )
{
qDebug() << " --- Model: " << this->objectName() << " should toggle: " << sectionKey << ": " << _sections.hasValidSection( sectionKey );
_sections.dump();
if(_sections.hasValidSection( sectionKey ) )
toggleSection( _sections.sectionByKey(sectionKey) );
}
//! toggled die gegebene model section.
void XQViewModel::toggleSection( const XQModelSection& section )
{
if( section.isValid() && _treeTable )
{
XQSectionRange pos = _sections.sectionRange(section);
qDebug() << " --- Section RANGE: " << pos.firstRow << " -> " << pos.lastRow;
_treeTable->toggleRowsHidden(pos.firstRow, pos.lastRow );
}
}
/*
//! SLOT als weiterleitung vom SIGNAL itemchanged
void XQViewModel::onItemChanged(XQItem* item )
{
qDebug() << " --- BASE item changed: " << item->text();
}
*/
//! SLOT, der aufgerufen wird, wenn eine edit-action getriggert wurde.
void XQViewModel::onActionTriggered(QAction* action)
{
qDebug() << " --- onActionTriggered: count:" << XQNode::s_Count;
qDebug() << " --- onActionTriggered: count:" << action->text() <<": " << XQNode::s_Count;
// all selected indices
QModelIndexList selectionList = treeTable()->selectionModel()->selectedRows();
@@ -161,6 +226,7 @@ void XQViewModel::onActionTriggered(QAction* action)
switch( cmdType )
{
// just handle undo ...
case XQCommand::cmdUndo :
return _undoStack->undo();
@@ -185,45 +251,22 @@ void XQViewModel::onActionTriggered(QAction* action)
// we create a command
XQCommand* command = new XQCommand( cmdType, this );
QModelIndex currentIndex = treeTable()->currentIndex();
command->setOriginIndex(currentIndex);
// store the row positions of the selected indices
showSelectionList(selectionList);
command->saveNodes( selectionList );
command->setOriginIndex( treeTable()->currentIndex() );
// execute command
_undoStack->push( command );
}
/*
switch (command.commandType())
{
case XQCommand::cmdToggleSection:
return cmdToggleSection( command.originIndex() );
case XQCommand::cmdCut:
return cmdCut( command );
case XQCommand::cmdPaste:
return cmdPaste( command );
case XQCommand::cmdNew:
return cmdNew( command );
case XQCommand::cmdDelete:
return cmdDelete( command );
case XQCommand::cmdMove:
break;
default:
qDebug() << " --- onCommandRedo: default: not handled: " << command.toString();
}
*/
//! führt die 'redo' action des gegebenen commnds aus.
void XQViewModel::onCommandRedo( XQCommand& command )
void XQViewModel::onCommandRedo( const XQCommand& command )
{
static MemCallMap redoCalls
{
@@ -249,45 +292,10 @@ void XQViewModel::onCommandRedo( XQCommand& command )
}
}
/*
try
{
switch (command.commandType())
{
case XQCommand::cmdToggleSection:
return cmdToggleSection( command.originIndex() );
break;
// undo Cut -> perform undoCut
case XQCommand::cmdCut:
return cmdCutUndo( command );
// undo Paste -> perform Cut
case XQCommand::cmdPaste:
return cmdPasteUndo( command );
// undo Move -> perform move back
case XQCommand::cmdMove:
// not yet implemented
break;
// undo New -> perform Delete
case XQCommand::cmdNew:
cmdNewUndo( command );
break;
// undo Delete -> perform New
case XQCommand::cmdDelete:
qDebug() << " --- onCommandUndo: delete: " << command.toString();
return cmdDeleteUndo( command );
default:
qDebug() << " --- onCommandUndo: default: not handled: " << command.toString();
}
*/
//! führt die 'undo' action des gegebenen commnds aus.
void XQViewModel::onCommandUndo( XQCommand& command )
void XQViewModel::onCommandUndo( const XQCommand& command )
{
qDebug() << " --- onCommandUndo: count: " << XQNode::s_Count;
@@ -319,18 +327,26 @@ void XQViewModel::onCommandUndo( XQCommand& command )
//! markierte knoten entfernen, 'command' enthält die liste
void XQViewModel::cmdCut( XQCommand& command )
void XQViewModel::cmdCut( const XQCommand& command )
{
int itmPos = command.first().itemPos;
const XQModelSection& section = _sections.sectionByRow( itmPos );
qDebug() << " --- HEADSHOT I: " << itmPos << "->" << section.contentType();
// wir gehen rückwärts über alle gemerkten knoten ...
for (auto it = command.rbegin(); it != command.rend(); ++it)
{
// ... holen das erste item, das auch den content node enthält
//const XQNodeBackup& entry = *it;
// jetzt löschen, dabei wird die parent-verbindung entfernt
const XQNodeBackup& entry = *it;
XQItem& firstItem = xqFirstItem( (*it).itemPos );
qDebug() << " --- Cut: " << firstItem.text() << " " << firstItem.row() << " id#" << entry.contentNode->_id;
//qDebug() << " --- Cut: " << firstItem.text() << " " << firstItem.row() << " id#" << entry.contentNode->_id;
qDebug() << " ---- command CUT: itemPos: " << entry.itemPos << " nodePos: "<< entry.nodePos << " is " << entry.contentNode->friendly_name();
entry.contentNode->unlink_self();
removeRow(entry.itemPos );
@@ -340,31 +356,37 @@ void XQViewModel::cmdCut( XQCommand& command )
//! entfernte knoten wieder einfügen , 'command' enthält die liste
void XQViewModel::cmdCutUndo( XQCommand& command )
void XQViewModel::cmdCutUndo( const XQCommand& command )
{
// die anfangsposition
int itmPos = command.first().itemPos;
// die 'zuständige' section rausfinden
const XQModelSection& section = _sections.sectionFromRow( itmPos );
const XQModelSection& section = _sections.sectionByRow( itmPos );
qDebug() << " --- HEADSHOT II: " << itmPos << "->" << section.contentType();
// über alle einträge ...
for (auto& entry : command )
{
const XQNodePtr& savedNode = entry.contentNode;
// __fix! should not be _contentRoot!
savedNode->add_me_at( entry.nodePos, _contentRoot );
XQItemList list = _itemFactory.makeRow( XQItemFactory::mData, section.sheetRootNode(), savedNode );
XQItemList list = _itemFactory.makeRow( section.sheetRootNode(), savedNode );
insertRow( entry.itemPos, list );
XQItem& firstItem = *((XQItem*)list[0]);
qDebug() << " ---- command cut UNDO2: itemPos: " << entry.itemPos << " nodePos: "<< entry.nodePos << " is " << entry.contentNode->friendly_name();
qDebug() << " --- Cut Undo: " << firstItem.text() << " " << firstItem.row() << " id#" << entry.contentNode->_id << " count: " << entry.contentNode.use_count();
insertRow( entry.itemPos, list );
}
}
//! clipboard inhalte einfügen
void XQViewModel::cmdPaste( XQCommand& command )
void XQViewModel::cmdPaste( const XQCommand& command )
{
// selection holen ...
QItemSelectionModel* selectionModel = treeTable()->selectionModel();
@@ -379,7 +401,7 @@ void XQViewModel::cmdPaste( XQCommand& command )
int nodePos = item.contentNode()->own_pos()+1;
// die zugehörige section finden
const XQModelSection& section = _sections.sectionFromRow( insRow-1 );
const XQModelSection& section = _sections.sectionByRow( insRow-1 );
// wir pasten das clipboard
for (auto& entry : _clipBoard )
{
@@ -388,7 +410,7 @@ void XQViewModel::cmdPaste( XQCommand& command )
// ... diesen einfügen ...
newNode->add_me_at( nodePos );
// ... und damit eine frische item-row erzeugen
XQItemList list = _itemFactory.makeRow( XQItemFactory::mData, section.sheetRootNode(), newNode );
XQItemList list = _itemFactory.makeRow( section.sheetRootNode(), newNode );
insertRow( insRow, list );
// die neue item-row selektieren
const QModelIndex& selIdx = list[0]->index();
@@ -399,14 +421,16 @@ void XQViewModel::cmdPaste( XQCommand& command )
}
// unsere änderungen merken fürs 'undo'
command.saveNodes( selectionModel->selectedRows() );
/// fix_xx
const_cast<XQCommand&>(command).saveNodes( selectionModel->selectedRows() );
}
//! einfügen aus dem clipboard wieder rückgängig machen
void XQViewModel::cmdPasteUndo( XQCommand& command )
void XQViewModel::cmdPasteUndo( const XQCommand& command )
{
command.dumpList("Paste UNDO");
// wir gehen rückwärts über alle markieren knoten ...
@@ -427,7 +451,7 @@ void XQViewModel::cmdPasteUndo( XQCommand& command )
//! entfernen der selection ohne copy in clipboard.
void XQViewModel::cmdDelete( XQCommand& command )
void XQViewModel::cmdDelete( const XQCommand& command )
{
// wir gehen rückwärts über alle markieren knoten ...
for (auto it = command.rbegin(); it != command.rend(); ++it)
@@ -435,83 +459,85 @@ void XQViewModel::cmdDelete( XQCommand& command )
// ... holen das erste item, das auch den content node enthält
const XQNodeBackup& entry = *it;
XQItem& firstItem = xqFirstItem( (*it).itemPos );
qDebug() << " --- Cut: " << firstItem.text() << " " << firstItem.row();
qDebug() << " --- delete: " << firstItem.text() << " " << firstItem.row();
// jetzt löschen
entry.contentNode->unlink_self();
removeRow(entry.itemPos );
}
}
//! macht 'delete' wirder rückgängig.
//! macht 'delete' wieder rückgängig.
void XQViewModel::cmdDeleteUndo( XQCommand& command )
void XQViewModel::cmdDeleteUndo( const XQCommand& command )
{
for (const auto& entry : command)
{
qDebug() << " --- delete UNDo: " << entry.contentNode->friendly_name();
}
cmdCutUndo(command);
}
//! legt eine neue, leere zeile an.
void XQViewModel::cmdNew( XQCommand& command )
void XQViewModel::cmdNew( const XQCommand& command )
{
// __fix
/*
const QModelIndex& origin = command.originIndex();
if( !origin.isValid() )
throw XQException("cmdNewRow failed: index not valid ");
XQItem* target = xqItemFromIndex( origin );
XQItem& target = xqItemFromIndex( origin );
// current data node
XQNodePtr node = target->contentNode();
// we create a new data node
//XQNodePtr newNode = new XQNodePtr( node->tag_name(), node->parent() );
XQNodePtr newNode = XQNode::make_node( node->tag_name(), node->tag_value(), node->parent() );
XQNodePtr node = target.contentNode();
// we create a new data node
XQNodePtr newNode = XQNode::make_node( node->tag_name(), node->tag_value() );
// store node in node->parent()
//node->add_before_me( newNode );
// store node also in 'command' to enable undo
const XQModelSection& section = _sections.sectionFromIndex( origin );
newNode->add_me_at( node->own_pos(), node->parent() );
// create new item row
XQItemList list = _itemFactory.createGenericRow( newNode, section.sheetRootNode );
// add it to the treeview ...
//...
const XQModelSection& section = _sections.sectionByRow( origin.row() );
// neue, leere zeile erzeugen ...
XQItemList list =_itemFactory.makeRow( section.sheetRootNode(), newNode );
// ... zur treeview hinzufügen ...
insertRow( origin.row(), list );
// ... editierbar machen ...
QModelIndex newIndex = list[0]->index();
treeTable()->setCurrentIndex( newIndex );
treeTable()->edit( newIndex );
// ,,, und fürs undo speichern
const_cast<XQCommand&>(command).saveNodes( {newIndex} );
// ... and make it ...
treeTable()->setCurrentIndex( list[0]->index() );
// ... editable
treeTable()->edit( list[0]->index() );
*/
}
//! entfernt die neu angelegte zeile.
void XQViewModel::cmdNewUndo( XQCommand& command )
void XQViewModel::cmdNewUndo( const XQCommand& command )
{
cmdDelete( command );
}
//! schaltet eine section sichtbar oder unsichtbar.
void XQViewModel::cmdToggleSection( XQCommand& command )
void XQViewModel::cmdToggleSection( const XQCommand& command )
{
const QModelIndex& index = command.originIndex();
Q_ASSERT(index.isValid());
const XQModelSection& section = _sections.sectionByRow(index.row());
int fstRow = _sections.firstRow( index );
int lstRow = _sections.lastRow( index );
// Obacht! Das ist hier etwas unsauber, 'toogleSection'' ändert den check-State
// im document-tree, was wiederum die 'toggleSection' auslöst, das gibt also
// einen doppelten Aufruf und wir sind dann wieder im Anfangszustand.
bool hidden =_treeTable->isRowHidden( fstRow, _treeTable->rootIndex() );
for (int row = fstRow; row < lstRow; ++row )
_treeTable->setRowHidden( row, _treeTable->rootIndex(), !hidden );
//toggleSection( section );
emit sectionToggled(section);
emit sectionToggled( _sections.sectionFromIndex(index) );
}
//! git die treetable zurück
//! gibt die treetable zurück
XQTreeTable* XQViewModel::treeTable()
{
@@ -524,7 +550,7 @@ void XQViewModel::setTreeTable(XQTreeTable* mainView )
{
// store view for direct access: the maintree
_treeTable = mainView;
// connect myself as model to the mainview
// set myself as model to the mainview
_treeTable->setModel(this);
XQItemDelegate* delegate = new XQItemDelegate( *this );
_treeTable->setItemDelegate( delegate );

View File

@@ -22,7 +22,7 @@
#include <QtQmlIntegration>
#include <xqsimpleclipboard.h>
#include <xqmodelsectionlist.h>
#include <xqsectionmanager.h>
#include <xqitemfactory.h>
#include <xqcontextmenu.h>
@@ -37,7 +37,7 @@ class XQCommand;
class XQViewModel : public QStandardItemModel
{
Q_OBJECT
//QML_ELEMENT
QML_ELEMENT
public:
@@ -54,78 +54,65 @@ public:
virtual void initModel( const QString& modelName);
void expandNewItem(const QModelIndex& index);
void toggleSection( const XQModelSection& section );
//little helpers
const XQItem& xqRootItem();
XQNodePtr contentRootNode();
XQItem& xqItemFromIndex(const QModelIndex& index) const;
XQItem& xqFirstItem(int row) const;
// undo-/redo-able stuff
virtual void cmdToggleSection( XQCommand& command );
virtual void cmdCut( XQCommand& command );
virtual void cmdCutUndo( XQCommand& command );
virtual void cmdPaste( XQCommand& command );
virtual void cmdPasteUndo( XQCommand& command );
virtual void cmdDelete( XQCommand& command );
virtual void cmdDeleteUndo( XQCommand& command );
virtual void cmdNew( XQCommand& command );
virtual void cmdNewUndo( XQCommand& command );
virtual void cmdToggleSection( const XQCommand& command );
virtual void cmdCut( const XQCommand& command );
virtual void cmdCutUndo( const XQCommand& command );
virtual void cmdPaste( const XQCommand& command );
virtual void cmdPasteUndo( const XQCommand& command );
virtual void cmdDelete( const XQCommand& command );
virtual void cmdDeleteUndo( const XQCommand& command );
virtual void cmdNew( const XQCommand& command );
virtual void cmdNewUndo( const XQCommand& command );
// Derzeit wird die default-implementierung von data/setData genutzt. hier wäre dann die
// Stelle um setData & data an externe 'handler' umzubiegen, siehe giovannies 'model-injection'
signals:
/*!
Derzeit wird die default-implementierung von data/setData genutzt. hier wäre dann die
Stelle um setData & data an externe 'handler' umzubiegen, siehe giovannies 'model-injection'
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
return QStandardItemModel::data( index, role );
}
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override
{
qDebug() << " --- setData: " << value.toString();
return QStandardItemModel::setData( index, value, role );
}
*/
void xqItemChanged( const XQItem& item );
void itemCreated( XQItem* newItem );
void sectionCreated( const XQModelSection& section );
void sectionToggled( const XQModelSection& section );
public slots:
virtual void onShowContextMenu(const QPoint& point);
virtual void onActionTriggered(QAction* action);
virtual void onToggleSection(const QString& sectionKey );
// handle XQCommands ( == UndoCommand )
virtual void onCommandRedo( XQCommand& command );
virtual void onCommandUndo( XQCommand& command );
signals:
void itemCreated( XQItem* newItem );
void sectionCreated( const XQModelSection& section );
void sectionToggled( const XQModelSection& section );
virtual void onCommandRedo( const XQCommand& command );
virtual void onCommandUndo( const XQCommand& command );
protected:
void addSection(const XQItemList& list, const XQNodePtr& sheetNode );
virtual void initContextMenu(){}
virtual void initContextMenu() = 0;
// __fixme: should be created from xml
virtual void setupViewProperties();
protected:
using MemCall = void (XQViewModel::*)(XQCommand&);
using MemCall = void (XQViewModel::*)(const XQCommand&);
using MemCallMap = QMap<XQCommand::CmdType,MemCall>;
// das eine reference auf ein globales singleton
XQItemFactory& _itemFactory;
XQSimpleClipBoard _clipBoard;
XQModelSectionList _sections;
XQItemFactory& _itemFactory;
XQSimpleClipBoard _clipBoard;
XQSectionManager _sections;
XQTreeTable* _treeTable{};
XQTreeTable* _treeTable{};
//QAbstractItemView* _treeTable{};
QUndoStack* _undoStack{};
XQContextMenu* _contextMenu{};

View File

@@ -53,6 +53,7 @@ namespace znode
//! einfache tree-klasse, besonderheit: der nutzlast-string-type ist templated.
template<typename str_t>
//class zbasic_node : public zid, public zpayload<str_t>, public std::enable_shared_from_this<zbasic_node<str_t>>
class zbasic_node : public zid, public zpayload<str_t>, public std::enable_shared_from_this<zbasic_node<str_t>>
{
@@ -78,6 +79,7 @@ namespace znode
zweak_node _parent;
znode_list _children;
// functor, der auf pointer gleichheit prüft.
struct match_node
{
match_node( zbasic_node* match )
@@ -95,9 +97,11 @@ namespace znode
public:
//! shortcut auf std::make_shared...
static zshared_node make_node( str_cref arg1, str_cref arg2 = "" , zshared_cref parent = nullptr )
//! beachte: der eltern-knoten wird hier nicht gesetzt, der neue knoten
//! wird nirgends eingefügt.
static zshared_node make_node( str_cref arg1, str_cref arg2 = "" )
{
return std::make_shared<zbasic_node>( arg1, arg2, parent );
return std::make_shared<zbasic_node>( arg1, arg2 );
}
//! leerer konstruktor
@@ -136,7 +140,7 @@ namespace znode
zbasic_node(const zbasic_node&) = delete;
zbasic_node& operator=(const zbasic_node&) = delete;
// 'move' geht (shared_from_this bleibt gültig)
//! 'move' geht (shared_from_this bleibt gültig)
zbasic_node(zbasic_node&&) noexcept = default;
zbasic_node& operator=(zbasic_node&&) noexcept = default;
@@ -178,31 +182,31 @@ namespace znode
return _parent.lock();
}
//! gibt den nachfolge-knoten oder 'end()' zurück.
zshared_node sibling()
{
if( !parent() )
//return zshared_node( make_node("WTF1") );
return zshared_node();
znode_list& childs = _parent->_children;
auto it = std::find( childs.begin(), childs.end(), this->shared_from_this() );
if( ++it != childs.end())
if( parent() )
{
znode_list& childs = _parent->_children;
auto it = std::find( childs.begin(), childs.end(), this->shared_from_this() );
return *(it);
//return zshared_node( make_node("WTF?") );
return zshared_node();
}
throw std::runtime_error("sibling(): no parent node");
}
//! gibt den vector mit kind-knoten zurück
inline const znode_list& children() const
{
return _children;
}
//! testet, ob kinder vorhanden sind.
bool has_children() const
{
return !children().empty();
}
//! gibt das erste kind zurück
zshared_node first_child()
{
if(!children().empty())
@@ -210,6 +214,7 @@ namespace znode
return nullptr;
}
//! gibt das letzte kind oder nullptr zurück
zshared_node last_child()
{
if(!children().empty())
@@ -237,7 +242,6 @@ namespace znode
int add_child_at( int idx, const zshared_node& node )
{
// _fixme! was ist, wenn da schon ein elternknoten ist?
_children.insert(children().begin() + idx, node );
node->_parent = this->shared_from_this();
return int(children().size() - 1);
@@ -250,10 +254,9 @@ namespace znode
parent()->add_child_at( offset, this->shared_from_this() );
else
throw std::runtime_error("add_me_at(offset): no parent node");
}
//! fügt einen shard_ptr von 'mir' in die kinderliste des übergebenen knotens ein
//! fügt einen shared_ptr von 'mir' in die kinderliste des übergebenen knotens ein
//! und macht diesen zu meinem elternknoten.
void add_me_at( int offset, const zshared_node& parent_node )
{
@@ -266,18 +269,17 @@ namespace znode
{
throw std::runtime_error("add_me_at(offset,parent): no parent node");
}
}
//! findet die eigene position im eltern-knoten
int own_pos()
{
if( parent())
return parent()->child_pos( this->shared_from_this() );
return -1;
}
//int child_pos(zbasic_node* child)
//! findet die postion eines kind-knotens
int child_pos(const zshared_node& child)
{
//auto pos = std::find_if(children().begin(), children().end(), match_node(child) );
@@ -287,7 +289,7 @@ namespace znode
return -1;
}
//zshared_node unlink_child( zbasic_node* node )
//! findet die postion eines kind-knotens
zshared_node unlink_child( const zshared_node& node )
{
auto it = std::find(_children.begin(), _children.end(), node);
@@ -315,11 +317,10 @@ namespace znode
{
for( auto child : _children )
{
qDebug() << " --#- " << child->name() << " : " << child->has_attribute( attrkey, attrvalue );
if( child->has_attribute( attrkey, attrvalue ))
return child;
}
return zshared_node();
return nullptr;
}
//
@@ -330,7 +331,7 @@ namespace znode
if( child->tag_name() == tagname )
return child;
}
return zshared_node();
return nullptr;
}
zshared_node find_child_by_id( int id )
@@ -340,46 +341,7 @@ namespace znode
if (child->_id == id)
return child;
}
return zshared_node();
}
void dump(int indent = 0) const
{
// fix_me!
qDebug() << std::string(indent * 2, ' ').c_str() << this->to_string();
//qDebug() << to_string();
//qDebug() << '\n';// std::endl;
if (!children().empty())
{
for (auto child : _children)
{
//qDebug() << " --- type: " << typeid(child).name();
child.get()->dump( indent + 1 );
}
}
}
template<typename T>
bool for_each_x( T func, int depth = 0 )
//bool for_each( auto func ) const
{
if( !apply( func, depth ) )
return false;
if( !children().empty() )
{
for( auto child : _children )
{
if( !child->for_each( func, depth+1 ) )
return false;
}
}
return true;
return nullptr;
}
// find ...
@@ -392,22 +354,6 @@ namespace znode
};
class zbasic_node_walker
{
public:
virtual void begin()
{}
template<typename str_t>
void for_each_node( zbasic_node<str_t>* node );
virtual void end()
{}
};
} //namespace znode

View File

@@ -100,7 +100,7 @@ namespace znode
//parent->add_child( new_node );
//zbasic_node<str_t>* new_node = new zbasic_node<str_t>( node.name(), node.child_value(), parent );
zshared_node new_node = zbasic_node<str_t>::make_node( xml_node.name(), xml_node.child_value(), parent );
zshared_node new_node = zbasic_node<str_t>::make_node( xml_node.name(), xml_node.child_value() );
parent->add_child( new_node );
if( !xml_node.attributes().empty() )

View File

@@ -4,9 +4,6 @@
#include <vector>
#include <map>
//#include <znode_stringmap.h>
//#include <znode_attributes.h>
namespace znode
{
template<typename str_t>

View File

@@ -73,12 +73,23 @@ public:
return _data.end();
}
auto begin() const
{
return _data.begin();
}
auto end() const
{
return _data.end();
}
inline int size() const
{
return (int) _data.size();
}
inline bool isEmpty() const
{
return (_data.size()==0);
@@ -94,13 +105,11 @@ public:
return mapIndex().contains(key);
}
inline const XQMapIndex& mapIndex() const
{
return _index;
}
int indexOf( const QString& key ) const
{
return mapIndex().indexOf(key);
@@ -121,7 +130,6 @@ public:
return mapIndex().key( index );
}
T& operator[]( int index )
{
if( contains(index) )
@@ -129,7 +137,6 @@ public:
throw XQException("XQMaptor operator[ int index ]: out of range");
}
const T& operator[]( int index ) const
{
if ( contains(index) )
@@ -150,19 +157,17 @@ public:
T& operator[]( const QString& key )
{
if( key.isEmpty() || !contains(key) )
throw XQException("maprow operator[]: key empty || not found: " + key);
throw XQException("XQMaptor operator[]: key empty || not found: " + key);
return _data[ _index[key] ];
}
const T& operator[]( const QString& key ) const
{
if (key.isEmpty() || !contains(key))
throw XQException("maprow operator[]: key empty || not found: " + key);
throw XQException("XQMaptor operator[]: key empty || not found: " + key);
return _data[_index[key]];
}
T& at( const QString& key )
{
return (*this)[key];
@@ -173,28 +178,54 @@ public:
return (*this)[key];
}
const T& last() const
{
if(_data.isEmpty())
throw XQException( "XQMaptor last: is empty!" );
return _data.last();
}
const T& first() const
{
if(_data.isEmpty())
throw XQException( "XQMaptor first: is empty!" );
return _data.first();
}
T& last()
{
if(_data.isEmpty())
throw XQException( "XQMaptor last: is empty!" );
return _data.last();
}
T& first()
{
if(_data.isEmpty())
throw XQException( "XQMaptor first: is empty!" );
return _data.first();
}
virtual int add( const T& item )
{
_data.push_back( item );
return _data.size()-1;
}
virtual void addAtIndex( int index, const T& item )
{
if(contains(index))
throw XQException( "QStringrow::add: index out of range!" );
throw XQException( "XQMaptor add at index: index out of range!" );
_data[index] = item;
}
// convenience method to mimic QMap<T,QString>
virtual void insert( const T& item, const QString& key )
{
addAtKey(key, item );
}
virtual void addAtKey( const QString& key, const T& item )
{
XQMapIndex::iterator pos = _index.find( key );
@@ -209,7 +240,6 @@ public:
}
}
bool addAlias( const QString& key, const QString& alias )
{
// look for 'original' key
@@ -227,20 +257,17 @@ public:
return true;
}
void addKey( const QString& key, int index )
{
_index.addKey( key, index );
}
virtual void clear()
{
_data.clear();
_index.clear();
}
virtual bool killEntry( const QString& key )
{
int idx = indexOf( key );
@@ -249,7 +276,6 @@ public:
return killEntry( (int) idx );
}
virtual bool killEntry( int index )
{
if( index >= this->_data.size() )
@@ -261,19 +287,16 @@ public:
return true;
}
virtual QString toString() const
{
return join( ";" );
}
virtual void dump() const
{
throw XQException("XQMaptor: dump not implemented!" );
}
virtual QString join( const QString& sep, int from=0, int to=-1) const
{
Q_UNUSED(sep)
@@ -283,7 +306,6 @@ public:
return "--";
}
int replaceKey( const QString& oldkey, const QString& newkey )
{
int idx = indexOf( oldkey );

View File

@@ -31,7 +31,7 @@ void XQContextMenu::addAction(const QString& text, XQCommand::CmdType commandTyp
QAction* newAction = new QAction(text, this);
newAction->setData(commandType);
_actionMap[commandType] = newAction;
QWidget::addAction(newAction);
QMenu::addAction(newAction);
setActionEnabled( commandType, enabled );
}
@@ -53,7 +53,7 @@ void XQContextMenu::addAction(const QIcon& icon, const QString& text, XQCommand:
QAction* newAction = new QAction(icon, text, this);
newAction->setData(commandType);
_actionMap[commandType] = newAction;
QWidget::addAction(newAction);
QMenu::addAction(newAction);
setActionEnabled( commandType, enabled );
}

View File

@@ -14,7 +14,7 @@
#include <xqquickwidget.h>
XQQuickWidget::XQQuickWidget(QWidget *parent)
XQQuickWidget::XQQuickWidget(QWidget* parent)
: QQuickWidget(parent)
{

View File

@@ -23,7 +23,7 @@ class XQQuickWidget : public QQuickWidget
public:
XQQuickWidget(QWidget *parent = nullptr);
XQQuickWidget(QWidget* parent = nullptr);
};
#endif // XQQUICKWIDGET_H

View File

@@ -38,7 +38,7 @@ XQTreeTable::XQTreeTable(QWidget* parent)
//! gibt die verbundene modelview zurück, cast auf 'model()'
XQViewModel* XQTreeTable::modelView()
XQViewModel* XQTreeTable::viewModel()
{
return static_cast<XQViewModel*>(model());
}
@@ -48,7 +48,18 @@ XQViewModel* XQTreeTable::modelView()
XQItem& XQTreeTable::xqItemFromIndex(const QModelIndex& index )
{
return modelView()->xqItemFromIndex( index );
return viewModel()->xqItemFromIndex( index );
}
//! rows sichtbar/unsichtbar schalten, von 'fstRow' bis _einschliesslich_
//! 'lstRow'
void XQTreeTable::toggleRowsHidden( int fstRow, int lstRow )
{
bool hidden = isRowHidden( fstRow, rootIndex() );
for (int row = fstRow; row <= lstRow; ++row )
setRowHidden( row, rootIndex(), !hidden );
}
@@ -71,7 +82,7 @@ void XQTreeTable::currentChanged(const QModelIndex& current, const QModelIndex&
}
//! firz
//! ändert die breite eines header-feldes anhand der maus-position
void XQTreeTable::mouseResizeHeaderEntry(int xpos)
{
@@ -96,7 +107,7 @@ void XQTreeTable::mouseResizeHeaderEntry(int xpos)
}
//! firz
//! behandelt den mouse-drag zur grössenänderung der header-felder.
void XQTreeTable::mouseMoveEvent(QMouseEvent* event)
{
@@ -106,10 +117,9 @@ void XQTreeTable::mouseMoveEvent(QMouseEvent* event)
bool leftBtn = (event->buttons() & Qt::LeftButton);
QPoint eventPos = event->pos();
// splitcursor ist active
// splitcursor ist gesetzt
bool splitCursor = (cursor().shape() == Qt::SplitHCursor);
// sind wir schon am 'draggen'?
if (_indexToResize.isValid() && splitCursor && leftBtn)
{

View File

@@ -38,9 +38,11 @@ public:
XQTreeTable(QWidget* parent = nullptr );
virtual ~XQTreeTable() = default;
XQViewModel* modelView();
XQViewModel* viewModel();
XQItem& xqItemFromIndex(const QModelIndex& index );
void toggleRowsHidden(int fstRow, int lstRow );
protected:
void currentChanged(const QModelIndex& current, const QModelIndex& previous) override;

View File

@@ -2,6 +2,7 @@ QT += core gui widgets quick quickwidgets
# widgets-private
CONFIG += c++20 qmltypes
CONFIG -= qml_debug
QML_IMPORT_NAME = org.sourceworx.qmlcomponents
QML_IMPORT_MAJOR_VERSION = 1
@@ -23,9 +24,9 @@ HEADERS += \
items/xqitemtype.h \
items/xqitemdelegate.h \
model/xqcommand.h \
model/xqmodelsectionlist.h \
model/xqnode.h \
model/xqnodewriter.h \
model/xqsectionmanager.h \
model/xqselectionmodel.h \
model/xqsimpleclipboard.h \
model/xqviewmodel.h \
@@ -61,9 +62,9 @@ SOURCES += \
items/xqitemdelegate.cpp \
main.cpp \
model/xqcommand.cpp \
model/xqmodelsectionlist.cpp \
model/xqnode.cpp \
model/xqnodewriter.cpp \
model/xqsectionmanager.cpp \
model/xqselectionmodel.cpp \
model/xqsimpleclipboard.cpp \
model/xqviewmodel.cpp \

View File

@@ -1,13 +1,9 @@
<RCC>
<qresource prefix="/">
<file alias="modeldata1.xtr">../xml/modeldata1.xtr</file>
<file alias="modeldata2.xtr">../xml/modeldata2.xtr</file>
<file alias="modeldata3.xtr">../xml/modeldata3.xtr</file>
<file alias="modelsheet.xml">../xml/modelsheets.xml</file>
<file alias="xqtableview.qml">../qml/xqtableview.qml</file>
<file alias="HorizontalHeaderViewDelegate.qml">../qml/HorizontalHeaderViewDelegate.qml</file>
<file alias="XMain.qml">../qml/XMain.qml</file>
<file alias="VerticalHeaderViewDelegate.qml">../qml/VerticalHeaderViewDelegate.qml</file>
<file alias="dummyview.qml">../qml/dummyview.qml</file>
</qresource>
</RCC>
<qresource prefix="/">
<file alias="modeldata1.xtr">../xml/modeldata1.xtr</file>
<file alias="modeldata2.xtr">../xml/modeldata2.xtr</file>
<file alias="modeldata3.xtr">../xml/modeldata3.xtr</file>
<file alias="modelsheet.xml">../xml/modelsheets.xml</file>
<file alias="xqtreeview.qml">../qml/xqtreeview.qml</file>
</qresource>
</RCC>

View File

@@ -7,14 +7,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xtree", "xtree.vcxproj", "{
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Debug|x86.ActiveCfg = Debug|Win32
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Debug|x86.Build.0 = Debug|Win32
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Release|x86.ActiveCfg = Release|Win32
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Release|x86.Build.0 = Release|Win32
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Debug|x64.ActiveCfg = Debug|x64
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Debug|x64.Build.0 = Debug|x64
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Release|x64.ActiveCfg = Release|x64
{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -5,16 +5,26 @@
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D9E56CB4-F99F-4F88-B721-1443A0AFD5D0}</ProjectGuid>
<Keyword>QtVS_v304</Keyword>
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)'=='Release|x64'">10.0</WindowsTargetPlatformVersion>
<QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
@@ -24,6 +34,12 @@
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
@@ -31,13 +47,25 @@
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
<Import Project="$(QtMsBuild)\qt_defaults.props" />
</ImportGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'" Label="QtSettings">
<QtInstall>qt691</QtInstall>
<QtModules>core;gui;widgets</QtModules>
<QtInstall>qt692</QtInstall>
<QtModules>core;gui;quick;quickcontrols2;quickdialogs2;quicklayouts;widgets;quickwidgets</QtModules>
<QtBuildConfig>debug</QtBuildConfig>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="QtSettings">
<QtInstall>qt692</QtInstall>
<QtModules>core;gui;quick;widgets;quickwidgets</QtModules>
<QtBuildConfig>debug</QtBuildConfig>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'" Label="QtSettings">
@@ -45,6 +73,11 @@
<QtModules>core;gui;widgets</QtModules>
<QtBuildConfig>release</QtBuildConfig>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="QtSettings">
<QtInstall>VS2017x86Default</QtInstall>
<QtModules>core;gui;widgets</QtModules>
<QtBuildConfig>release</QtBuildConfig>
</PropertyGroup>
<Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
<Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
</Target>
@@ -54,13 +87,22 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'">
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@@ -69,6 +111,12 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<AdditionalIncludeDirectories>items;model;application;widgets;util;nodes;pugixml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'" Label="Configuration">
<ClCompile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -81,6 +129,18 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ClCompile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'" Label="Configuration">
<ClCompile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -97,62 +157,70 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ClCompile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<QtRcc Include="xtree.qrc" />
<QtUic Include="application\xqmainwindow.ui" />
<ClCompile Include="application\xqappdata.cpp" />
<ClCompile Include="application\xqchildmodelview.cpp" />
<ClCompile Include="application\xqchildmodel.cpp" />
<ClCompile Include="application\xqdocumentstore.cpp" />
<ClCompile Include="application\xqmainmodelview.cpp" />
<ClCompile Include="application\xqmainmodel.cpp" />
<ClCompile Include="application\xqmainwindow.cpp" />
<ClCompile Include="items\xqgenericitem.cpp" />
<ClCompile Include="items\xqitem.cpp" />
<ClCompile Include="items\xqitemdelegate.cpp" />
<ClCompile Include="items\xqitemfactory.cpp" />
<ClCompile Include="items\xqitemtype.cpp" />
<ClCompile Include="model\xqcommand.cpp" />
<ClCompile Include="model\xqitemtype.cpp" />
<ClCompile Include="model\xqitemtypefactory.cpp" />
<ClCompile Include="model\xqnodewriter.cpp" />
<ClCompile Include="model\xqsectionmanager.cpp" />
<ClCompile Include="model\xqselectionmodel.cpp" />
<ClCompile Include="model\xqitem.cpp" />
<ClCompile Include="model\xqmodelview.cpp" />
<ClCompile Include="model\xqmodelsections.cpp" />
<ClCompile Include="model\xqsimpleclipboard.cpp" />
<ClCompile Include="model\xqitemfactory.cpp" />
<ClCompile Include="model\xqnode.cpp" />
<ClCompile Include="model\xqviewmodel.cpp" />
<ClCompile Include="nodes\znode.cpp" />
<ClCompile Include="pugixml\pugixml.cpp" />
<ClCompile Include="util\xqexception.cpp" />
<ClCompile Include="widgets\xqitemdelegate.cpp" />
<ClCompile Include="widgets\xqcontextmenu.cpp" />
<ClCompile Include="widgets\xqtreeview.cpp" />
<ClCompile Include="widgets\xqquickwidget.cpp" />
<ClCompile Include="widgets\xqtreetable.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="application\xqchildmodelview.h" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="application\xqmainmodelview.h" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="application\xqmainwindow.h" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="widgets\xqtreetable.h" />
<QtMoc Include="widgets\xqquickwidget.h" />
<QtMoc Include="application\xqdocumentstore.h" />
<ClInclude Include="application\xqappdata.h" />
<ClInclude Include="items\xqgenericitem.h" />
<QtMoc Include="application\xqchildmodel.h" />
<QtMoc Include="application\xqmainmodel.h" />
<ClInclude Include="items\xqitem.h" />
<ClInclude Include="items\xqitemdelegate.h" />
<ClInclude Include="items\xqitemfactory.h" />
<ClInclude Include="items\xqitemtype.h" />
<ClInclude Include="model\xqcommand.h" />
<ClInclude Include="model\xqmodelsections.h" />
<ClInclude Include="model\xqnodewriter.h" />
<ClInclude Include="model\xqsectionmanager.h" />
<ClInclude Include="model\xqsimpleclipboard.h" />
<ClInclude Include="model\xqnode.h" />
<QtMoc Include="model\xqviewmodel.h" />
<ClInclude Include="nodes\znode.h" />
<ClInclude Include="nodes\znode_attributes.h" />
<ClInclude Include="nodes\znode_factory.h" />
<ClInclude Include="nodes\znode_id.h" />
<ClInclude Include="nodes\znode_iterator.h" />
@@ -166,10 +234,7 @@
<ClInclude Include="util\xqptrmaptor.h" />
<ClInclude Include="util\xsingleton.h" />
<ClInclude Include="util\xtreewalker.h" />
<QtMoc Include="widgets\xqtreeview.h" />
<QtMoc Include="widgets\xqcontextmenu.h" />
<QtMoc Include="widgets\xqitemdelegate.h" />
<QtMoc Include="model\xqmodelview.h" />
<QtMoc Include="model\xqselectionmodel.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@@ -76,42 +76,21 @@
<ClCompile Include="model\xqcommand.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqitem.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqmodelview.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqnode.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="application\xqmainmodelview.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
<ClCompile Include="application\xqmainwindow.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
<ClCompile Include="nodes\znode.cpp">
<Filter>Source Files\nodes</Filter>
</ClCompile>
<ClCompile Include="widgets\xqitemdelegate.cpp">
<Filter>Source Files\widgets</Filter>
</ClCompile>
<ClCompile Include="widgets\xqtreeview.cpp">
<Filter>Source Files\widgets</Filter>
</ClCompile>
<ClCompile Include="pugixml\pugixml.cpp">
<Filter>Source Files\pugixml</Filter>
</ClCompile>
<ClCompile Include="model\xqmodelsections.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqselectionmodel.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="application\xqchildmodelview.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
<ClCompile Include="model\xqsimpleclipboard.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
@@ -127,21 +106,9 @@
<ClCompile Include="model\xqnodewriter.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqitemfactory.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="application\xqappdata.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
<ClCompile Include="model\xqitemtypefactory.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqitemtype.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="items\xqgenericitem.cpp">
<Filter>Source Files\Items</Filter>
</ClCompile>
<ClCompile Include="items\xqitem.cpp">
<Filter>Source Files\Items</Filter>
</ClCompile>
@@ -154,6 +121,24 @@
<ClCompile Include="items\xqitemtype.cpp">
<Filter>Source Files\Items</Filter>
</ClCompile>
<ClCompile Include="model\xqsectionmanager.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="model\xqviewmodel.cpp">
<Filter>Source Files\model</Filter>
</ClCompile>
<ClCompile Include="widgets\xqquickwidget.cpp">
<Filter>Source Files\widgets</Filter>
</ClCompile>
<ClCompile Include="widgets\xqtreetable.cpp">
<Filter>Source Files\widgets</Filter>
</ClCompile>
<ClCompile Include="application\xqchildmodel.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
<ClCompile Include="application\xqmainmodel.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="model\xqcommand.h">
@@ -183,9 +168,6 @@
<ClInclude Include="pugixml\pugixml.hpp">
<Filter>Header Files\pugixml</Filter>
</ClInclude>
<ClInclude Include="model\xqmodelsections.h">
<Filter>Header Files\model</Filter>
</ClInclude>
<ClInclude Include="util\xqmapindex.h">
<Filter>Header Files\util</Filter>
</ClInclude>
@@ -216,12 +198,6 @@
<ClInclude Include="nodes\znode_payload.h">
<Filter>Header Files\nodes</Filter>
</ClInclude>
<ClInclude Include="nodes\znode_attributes.h">
<Filter>Header Files\nodes</Filter>
</ClInclude>
<ClInclude Include="items\xqgenericitem.h">
<Filter>Header Files\items</Filter>
</ClInclude>
<ClInclude Include="items\xqitem.h">
<Filter>Header Files\items</Filter>
</ClInclude>
@@ -234,33 +210,36 @@
<ClInclude Include="items\xqitemtype.h">
<Filter>Header Files\items</Filter>
</ClInclude>
<ClInclude Include="model\xqsectionmanager.h">
<Filter>Header Files\model</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtMoc Include="model\xqmodelview.h">
<Filter>Header Files\model</Filter>
</QtMoc>
<QtMoc Include="application\xqmainmodelview.h">
<Filter>Header Files\application</Filter>
</QtMoc>
<QtMoc Include="application\xqmainwindow.h">
<Filter>Header Files\application</Filter>
</QtMoc>
<QtMoc Include="widgets\xqitemdelegate.h">
<Filter>Header Files\widgets</Filter>
</QtMoc>
<QtMoc Include="widgets\xqtreeview.h">
<Filter>Header Files\widgets</Filter>
</QtMoc>
<QtMoc Include="widgets\xqcontextmenu.h">
<Filter>Header Files\widgets</Filter>
</QtMoc>
<QtMoc Include="model\xqselectionmodel.h">
<Filter>Header Files\model</Filter>
</QtMoc>
<QtMoc Include="application\xqchildmodelview.h">
<QtMoc Include="application\xqdocumentstore.h">
<Filter>Header Files\application</Filter>
</QtMoc>
<QtMoc Include="application\xqdocumentstore.h">
<QtMoc Include="model\xqviewmodel.h">
<Filter>Header Files\model</Filter>
</QtMoc>
<QtMoc Include="widgets\xqquickwidget.h">
<Filter>Header Files\widgets</Filter>
</QtMoc>
<QtMoc Include="widgets\xqtreetable.h">
<Filter>Header Files\widgets</Filter>
</QtMoc>
<QtMoc Include="application\xqchildmodel.h">
<Filter>Header Files\application</Filter>
</QtMoc>
<QtMoc Include="application\xqmainmodel.h">
<Filter>Header Files\application</Filter>
</QtMoc>
</ItemGroup>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<QtTouchProperty>
</QtTouchProperty>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<QtTouchProperty>
</QtTouchProperty>
</PropertyGroup>
</Project>

View File

@@ -10,21 +10,20 @@
<Panel PanelID="#4 JA 04" FriendlyName="@PanelName" PanelName="JA 04 Solar X58C" Manufacturer="JA Solar 4" WattPeak="440" Height="1,70" Width="1,10" Weight="12" MaxVolt="42" MaxAmpere="11"/>
<Panel PanelID="#5 JA 05" FriendlyName="@PanelName" PanelName="JA 05 Solar X58C" Manufacturer="JA Solar 5" WattPeak="440" Height="1,70" Width="1,10" Weight="12" MaxVolt="42" MaxAmpere="11"/>
<Panel PanelID="#6 JA 06" FriendlyName="@PanelName" PanelName="JA 06 Solar X58C" Manufacturer="JA Solar 6" WattPeak="440" Height="1,70" Width="1,10" Weight="12" MaxVolt="42" MaxAmpere="11"/>
<Inverter InverterID="#1 HM600 01" FriendlyName="@InverterName" InverterName="01 HM600 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="2000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Inverter InverterID="#2 HM800 02" FriendlyName="@InverterName" InverterName="02 HM800 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="4000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="800" NumStrings="2" Weight="29"/>
<Inverter InverterID="#3 HM1600 03" FriendlyName="@InverterName" InverterName="03 HM1600 S4 TMax" Manufacturer="HoyMiles" MaxPowerInput="6000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="1600" NumStrings="4" Weight="32"/>
<Inverter InverterID="#4 D12K 04" FriendlyName="@InverterName" InverterName="04 HM600 S2 TMax" Manufacturer="Deye" MaxPowerInput="12000,33" MaxPowerInputChoice="6000;8000;12000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Inverter InverterID="#1 HM600 01" FriendlyName="@InverterName" InverterName="01 HM600 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="3000,00" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Inverter InverterID="#2 HM800 02" FriendlyName="@InverterName" InverterName="02 HM800 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="4000" MaxPowerInputChoice="4000;6000;8000" MaxPowerOutput="800" NumStrings="2" Weight="29"/>
<Inverter InverterID="#3 HM1600 03" FriendlyName="@InverterName" InverterName="03 HM1600 S4 TMax" Manufacturer="HoyMiles" MaxPowerInput="9000,00" MaxPowerInputChoice="6000;8000;10000" MaxPowerOutput="1600" NumStrings="4" Weight="32"/>
<Inverter InverterID="#4 D12K 04" FriendlyName="@InverterName" InverterName="04 HM600 S2 TMax" Manufacturer="Deye" MaxPowerInput="8000" MaxPowerInputChoice="6000;8000;12000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Battery BatteryID="#1 BYD 01" FriendlyName="@BatteryName" BatteryName="01 BYD T01 Stackable" Manufacturer="BYD" Capacity="4500" Yield="90" MaxCurrent="120" MaxVolt="48">
<AdditionalData DataItem="Image" DataValue="image.png"/>
<AdditionalData DataItem="Manual" DataValue="manual.docx"/>
<AdditionalData DataItem="Certificate" DataValue="certificate.pdf"/>
<Images FrontView="image.png" PackageView="package.png" InstalledView="installed.png"/>
<Documents Manual="manual.docx" Certificate="certificate.pdf"/>
</Battery>
<Battery BatteryID="#2 BYD 02" FriendlyName="@BatteryName" BatteryName="02 BYD T02 Stackable" Manufacturer="BYD" Capacity="9000" Yield="94" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#3 BYD 03" FriendlyName="@BatteryName" BatteryName="03 BYD T01 Stackable" Manufacturer="BYD" Capacity="4500" Yield="86" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#4 BYD 04" FriendlyName="@BatteryName" BatteryName="04 BYD T02 Stackable" Manufacturer="BYD" Capacity="9000" Yield="98" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#4 BYD 04" FriendlyName="@BatteryName" BatteryName="04 BYD T02 Stackable" Manufacturer="BYD" Capacity="9000" Yield="91" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#5 GroWatt 05 G2K" FriendlyName="@BatteryName" BatteryName="05 BYD T01 Stackable" Manufacturer="GroWatt" Capacity="4500" Yield="94" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#6 GroWatt 06 G4K" FriendlyName="@BatteryName" BatteryName="06 BYD T02 Stackable" Manufacturer="GroWatt" Capacity="9000" Yield="49" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#7 Pyne 07 G4K" FriendlyName="@BatteryName" BatteryName="07 Pyne K7 Stackable" Manufacturer="PyNe" Capacity="9000" Yield="49" MaxCurrent="120" MaxVolt="48"/>
<Battery BatteryID="#7 Pyne 07 G4K" FriendlyName="@BatteryName" BatteryName="07 Pyne K7 Stackable" Manufacturer="PyNe" Capacity="9000" Yield="68" MaxCurrent="120" MaxVolt="48"/>
</Components>
<IrgendWasAnderes>

View File

@@ -4,9 +4,9 @@
<Project ProjectID="HA02" FriendlyName="@ProjectName" ProjectName="Gerbrunn Ost" Established="2006" WattPeak="9840" ContentType="planned">
<Components>
<Panel PanelID="Jingli 01" FriendlyName="@PanelName" PanelName="Jingli 01 Solar T62B" Manufacturer="Jingli Solar" WattPeak="620" Height="2,70" Width="1,10" Weight="12" MaxVolt="67" MaxAmpere="11">
<AdditionalData DataItem="Image" DataValue="image,png"/>
<AdditionalData DataItem="Manual" DataValue="manual,docx"/>
<AdditionalData DataItem="Certificate" DataValue="certificate,pdf"/>
<AdditionalData DataItem="Image" DataValue="image.png"/>
<AdditionalData DataItem="Manual" DataValue="manual.docx"/>
<AdditionalData DataItem="Certificate" DataValue="certificate.pdf"/>
</Panel>
<Panel PanelID="Jingli 02" FriendlyName="@PanelName" PanelName="Jingli 02 Solar X58C" Manufacturer="Jingli Solar" WattPeak="440" Height="1,70" Width="1,10" Weight="12" MaxVolt="42" MaxAmpere="11"/>
<Panel PanelID="Jingli 03" FriendlyName="@PanelName" PanelName="Jingli 03 Solar T62B" Manufacturer="Jingli Solar" WattPeak="620" Height="2,70" Width="1,10" Weight="12" MaxVolt="67" MaxAmpere="11"/>

View File

@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<Project ProjectID="HA03" FriendlyName="@ProjectName" ProjectName="Neubrunn Süd" Established="2006" WattPeak="9840" ContentType="runnning">
<Project ProjectID="HA03" FriendlyName="@ProjectName" ProjectName="Tauberbischoffsheim SÜD" Established="2006" WattPeak="9840" ContentType="runnning">
<Components>
<Panel PanelID="AIKO 01" FriendlyName="@PanelName" PanelName="AIKO 01 Solar T62B" Manufacturer="AIKO Solar" WattPeak="620" Height="2,70" Width="1,10" Weight="12" MaxVolt="67" MaxAmpere="11">
<AdditionalData DataItem="Image" DataValue="image.png"/>

View File

@@ -6,28 +6,29 @@
-->
<ItemTypes>
<TreeParentType RenderStyle="PlainStyle" ItemFlags="IsEnabled|IsDropEnabled" Icon="DirIcon" />
<TreeSectionType RenderStyle="PlainStyle" ItemFlags="IsEnabled" Icon="DesktopIcon"/>
<TreeChildType RenderStyle="PlainStyle" ItemFlags="IsUserCheckable|IsEnabled" Icon="MediaPlay"/>
<HeaderType RenderStyle="HeaderStyle" ItemFlags="IsEnabled"/>
<TreeParentType RenderStyle="PlainStyle" EditorType="LineEditType" ItemFlags="IsEnabled|IsDropEnabled" Icon="DirIcon" />
<TreeChildType RenderStyle="PlainStyle" EditorType="LineEditType" ItemFlags="IsEnabled" Icon="MediaPlay"/>
<TreeSectionType RenderStyle="PlainStyle" EditorType="LineEditType" ItemFlags="IsUserCheckable|IsEnabled" Icon="DesktopIcon"/>
<HeaderType RenderStyle="HeaderStyle" EditorType="LineEditType" ItemFlags="IsEnabled"/>
<HiddenType RenderStyle="HiddenStyle"/>
<StaticType RenderStyle="PlainStyle"/>
<PlainType RenderStyle="PlainStyle" ItemFlags="IsEnabled|IsEditable|IsSelectable"/>
<ValueType RenderStyle="FormattedStyle" ItemFlags="IsEnabled|IsEditable|IsSelectable" UnitType="Coulomb"/>
<CheckableType RenderStyle="FormattedStyle" ItemFlags="IsEnabled|IsEditable|IsSelectable" UnitType="###"/>
<PercentageType RenderStyle="ProgressBarStyle" ItemFlags="IsEnabled|IsSelectable"/>
<ChoiceType RenderStyle="ComboBoxStyle" ItemFlags="IsEnabled|IsSelectable|IsEditable" FixedChoices="la|le|lo|lu"/>
<IntValueType RenderStyle="SpinBoxStyle" ItemFlags="IsEnabled|IsSelectable"/>
<PlainType RenderStyle="PlainStyle" EditorType="LineEditType" ItemFlags="IsEnabled|IsEditable|IsSelectable"/>
<ValueType RenderStyle="FormattedStyle" EditorType="LineEditType" ItemFlags="IsEnabled|IsEditable|IsSelectable" UnitType="Coulomb"/>
<CheckableType RenderStyle="FormattedStyle" EditorType="LineEditType" ItemFlags="IsEnabled|IsEditable|IsSelectable" />
<PercentageType RenderStyle="ColorBarStyle" EditorType="ColorBarType" ItemFlags="IsEnabled|IsEditable|IsSelectable" UnitType="%"/>
<ChoiceType RenderStyle="ComboBoxStyle" EditorType="ComboBoxType" ItemFlags="IsEnabled|IsEditable|IsSelectable" UnitType="W" FixedChoices="2000|4000|6000|8000" />
<IntValueType RenderStyle="SpinBoxStyle" EditorType="SpinBoxType" ItemFlags="IsEnabled|IsEditable|IsSelectable"/>
</ItemTypes>
<DocumentTreeModel>
<Section ContentType="runnning">
<Header>
<Entry Caption="Active Projects" ItemType="TreeParentType"/>
</Header>
<ModelSheet firz="running">
<ModelSheet>
<Project Caption="@ProjectName" ItemType="TreeChildType">
<CurrentSection ItemType="TreeSectionType"/>
</Project>
@@ -37,7 +38,7 @@
<Header>
<Entry Caption="Planned Projects" ItemType="TreeParentType"/>
</Header>
<ModelSheet firz="planned">
<ModelSheet>
<Project Caption="@ProjectName" ItemType="TreeChildType">
<CurrentSection ItemType="TreeSectionType"/>
</Project>
@@ -47,7 +48,7 @@
<Header>
<Entry Caption="Finished Projects" ItemType="TreeParentType"/>
</Header>
<ModelSheet firz="finished">
<ModelSheet>
<Project Caption="@ProjectName" ItemType="TreeChildType">
<CurrentSection ItemType="TreeSectionType"/>
</Project>
@@ -75,10 +76,10 @@
<!-- 'Icon' überschreibt den default wert im ItemType und erzeugt damit einen neuen ItemType-->
<PanelID ItemType="PlainType" Icon="DesktopIcon"/>
<PanelName ItemType="PlainType" Icon="BrowserStop"/>
<Manufacturer ItemType="ValueType"/>
<Manufacturer ItemType="PlainType"/>
<!-- 'UnitType' überschreibt den default wert im ItemType und erzeugt damit einen neuen ItemType-->
<WattPeak ItemType="ValueType" UnitType="Wp"/>
<Width ItemType="CheckableType" Icon="VistaShield" UnitType="m"/>
<Width ItemType="ValueType" Icon="VistaShield" UnitType="m"/>
<Height ItemType="ValueType" UnitType="m"/>
<Weight ItemType="ValueType" UnitType="kg"/>
<MaxVolt ItemType="ValueType" UnitType="V"/>
@@ -86,8 +87,7 @@
</ModelSheet>
</Section>
<Section ContentType="Inverter" >
<Section ContentType="Inverter" >
<Header >
<InverterID Caption="Inverter" ItemType="HeaderType" />
<InverterName Caption="Name" ItemType="HeaderType" />
@@ -98,20 +98,20 @@
<Weight Caption="Weight" ItemType="HeaderType" />
</Header>
<ModelSheet>
<InverterID Caption="Inverter" ItemType="ValueType" />
<InverterName Caption="Name" ItemType="ValueType" />
<Manufacturer Caption="Manufacturer" ItemType="ValueType" />
<MaxPowerInput Caption="max. Input" ItemType="ValueType" ItemType="ChoiceType" ChoiceModelSheetSource="MaxPowerInputChoice" UnitType="W"/>
<MaxPowerOutput Caption="max Output" ItemType="ValueType" UnitType="W"/>
<NumStrings Caption="Strings" ItemType="ValueType" />
<Weight Caption="Weight" ItemType="ValueType" UnitType="kg"/>
<InverterID ItemType="PlainType" />
<InverterName ItemType="PlainType" />
<Manufacturer ItemType="PlainType" />
<MaxPowerInput ItemType="ChoiceType" FixedChoices="2000|4000|6000|8000" UnitType="W"/>
<MaxPowerOutput ItemType="ValueType" UnitType="W"/>
<NumStrings ItemType="IntValueType" />
<Weight ItemType="ValueType" UnitType="kg"/>
</ModelSheet>
</Section>
<Section ContentType="Battery" >
<Header>
<BatteryID Caption="Name" ItemType="HeaderType" />
<BatteryName Caption="Battery" ItemType="HeaderType" />
<BatteryID Caption="Battery" ItemType="HeaderType" />
<BatteryName Caption="Name" ItemType="HeaderType" />
<Manufacturer Caption="Manufacturer" ItemType="HeaderType" />
<Capacity Caption="Capacity" ItemType="HeaderType"/>
<Yield Caption="Yield" ItemType="HeaderType" />
@@ -119,14 +119,24 @@
<MaxVolt Caption="max. Volt" ItemType="HeaderType" />
</Header>
<ModelSheet>
<BatteryID Caption="Battery" ItemType="ValueType" />
<BatteryName Caption="Name" ItemType="ValueType" />
<Manufacturer Caption="Manufacturer" ItemType="ValueType" />
<Capacity Caption="Capacity" ItemType="ValueType" UnitType="Wh"/>
<Yield Caption="Yield" ItemType="ValueType" ItemType="PercentageType" UnitType="%"/>
<MaxCurrent Caption="max. Current" ItemType="ValueType" UnitType="A"/>
<MaxVolt Caption="max. Volt" ItemType="ValueType" UnitType="V"/>
<BatteryID ItemType="PlainType" />
<BatteryName ItemType="PlainType" />
<Manufacturer ItemType="PlainType" />
<Capacity ItemType="ValueType" UnitType="Wh"/>
<Yield ItemType="PercentageType" UnitType="%"/>
<MaxCurrent ItemType="ValueType" UnitType="A">
<SubType ItemType="PlainType"/>
</MaxCurrent>
<MaxVolt ItemType="ValueType" UnitType="V"/>
<firz ItemType="PlainType"/>
<Image DataValue="image.png" ItemType="PlainType"/>
<Manual DataValue="manual.docx" ItemType="PlainType"/>
<Certificate DataValue="certificate.pdf" ItemType="PlainType"/>
</ModelSheet>
</Section>
</DocumentDetailsModel>
</DocumentDetailsModel>

View File

@@ -1,27 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Project Established="2006" FriendlyName="@ProjectName" ProjectID="HA01" ProjectName="Wiebelbach West" State="runnning" WattPeak="84000">
<Components>
<Panel FriendlyName="@PanelName" Height="2,70" Manufacturer="JA Solar 1 XX" MaxAmpere="11" MaxVolt="67" PanelID="#1 JA 01" PanelName="JA 01 Solar T62B" WattPeak="620" Weight="12" Width="1,10"/>
<Panel FriendlyName="@PanelName" Height="1,70" Manufacturer="JA Solar 2" MaxAmpere="11" MaxVolt="42" PanelID="#2 JA 02" PanelName="JA 02 Solar X58C" WattPeak="440" Weight="12" Width="1,10"/>
<Panel FriendlyName="@PanelName" Height="2,70" Manufacturer="JA Solar 3" MaxAmpere="11" MaxVolt="67" PanelID="#3 JA 03" PanelName="JA 03 Solar T62B" WattPeak="620" Weight="12" Width="1,10"/>
<Panel FriendlyName="@PanelName" Height="1,70" Manufacturer="JA Solar 4" MaxAmpere="11" MaxVolt="42" PanelID="#4 JA 04" PanelName="JA 04 Solar X58C" WattPeak="440" Weight="12" Width="1,10"/>
<Panel FriendlyName="@PanelName" Height="1,70" Manufacturer="JA Solar 5" MaxAmpere="11" MaxVolt="42" PanelID="#5 JA 05" PanelName="JA 05 Solar X58C" WattPeak="440" Weight="12" Width="1,10"/>
<Panel FriendlyName="@PanelName" Height="1,70" Manufacturer="JA Solar 6" MaxAmpere="11" MaxVolt="42" PanelID="#6 JA 06" PanelName="JA 06 Solar X58C" WattPeak="440" Weight="12" Width="1,10"/>
<Inverter FriendlyName="@InverterName" InverterID="#1 HM600 01" InverterName="01 HM600 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="2000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Inverter FriendlyName="@InverterName" InverterID="#2 HM800 02" InverterName="02 HM800 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="4000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="800" NumStrings="2" Weight="29"/>
<Inverter FriendlyName="@InverterName" InverterID="#3 HM1600 03" InverterName="03 HM1600 S4 TMax" Manufacturer="HoyMiles" MaxPowerInput="6000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="1600" NumStrings="4" Weight="32"/>
<Inverter FriendlyName="@InverterName" InverterID="#4 D12K 04" InverterName="04 HM600 S2 TMax" Manufacturer="Deye" MaxPowerInput="12000,33" MaxPowerInputChoice="6000;8000;12000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Battery BatteryID="#1 BYD 01" BatteryName="01 BYD T01 Stackable" Capacity="4500" FriendlyName="@BatteryName" Manufacturer="BYD" MaxCurrent="120" MaxVolt="48" Yield="90">
<AdditionalData DataItem="Image" DataValue="image.png"/>
<AdditionalData DataItem="Manual" DataValue="manual.docx"/>
<AdditionalData DataItem="Certificate" DataValue="certificate.pdf"/>
</Battery>
<Battery BatteryID="#2 BYD 02" BatteryName="02 BYD T02 Stackable" Capacity="9000" FriendlyName="@BatteryName" Manufacturer="BYD" MaxCurrent="120" MaxVolt="48" Yield="94"/>
<Battery BatteryID="#3 BYD 03" BatteryName="03 BYD T01 Stackable" Capacity="4500" FriendlyName="@BatteryName" Manufacturer="BYD" MaxCurrent="120" MaxVolt="48" Yield="86"/>
<Battery BatteryID="#4 BYD 04" BatteryName="04 BYD T02 Stackable" Capacity="9000" FriendlyName="@BatteryName" Manufacturer="BYD" MaxCurrent="120" MaxVolt="48" Yield="98"/>
<Battery BatteryID="#5 GroWatt 05 G2K" BatteryName="05 BYD T01 Stackable" Capacity="4500" FriendlyName="@BatteryName" Manufacturer="GroWatt" MaxCurrent="120" MaxVolt="48" Yield="94"/>
<Battery BatteryID="#6 GroWatt 06 G4K" BatteryName="06 BYD T02 Stackable" Capacity="9000" FriendlyName="@BatteryName" Manufacturer="GroWatt" MaxCurrent="120" MaxVolt="48" Yield="49"/>
<Battery BatteryID="#7 Pyne 07 G4K" BatteryName="07 Pyne K7 Stackable" Capacity="9000" FriendlyName="@BatteryName" Manufacturer="PyNe" MaxCurrent="120" MaxVolt="48" Yield="49"/>
</Components>
<IrgendWasAnderes/>
</Project>
<?xml version="1.0" encoding="UTF-8"?>
<Components>
<Panel FriendlyName="@PanelName" Height="2,70" Manufacturer="Jingli Solar" MaxAmpere="11" MaxVolt="67" PanelID="Jingli 01" PanelName="Jingli 01 Solar T62B" WattPeak="620" Weight="12" Width="1,10">
<AdditionalData DataItem="Image" DataValue="image.png"/>
<AdditionalData DataItem="Manual" DataValue="manual.docx"/>
<AdditionalData DataItem="Certificate" DataValue="certificate.pdf"/>
</Panel>
<Panel FriendlyName="@PanelName" Height="1,70" Manufacturer="Jingli Solar" MaxAmpere="11" MaxVolt="42" PanelID="Jingli 02" PanelName="Jingli 02 Solar X58C" WattPeak="440" Weight="12" Width="1,10"/>
<Panel FriendlyName="@PanelName" Height="2,70" Manufacturer="Jingli Solar" MaxAmpere="11" MaxVolt="67" PanelID="Jingli 03" PanelName="Jingli 03 Solar T62B" WattPeak="620" Weight="12" Width="1,10"/>
<Inverter FriendlyName="@InverterName" InverterID="HM600 01" InverterName="01 HM600 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="4000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Inverter FriendlyName="@InverterName" InverterID="HM800 02" InverterName="02 HM800 S2 TMax" Manufacturer="HoyMiles" MaxPowerInput="4000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="800" NumStrings="2" Weight="29"/>
<Inverter FriendlyName="@InverterName" InverterID="HM1600 03" InverterName="03 HM1600 S4 TMax" Manufacturer="HoyMiles" MaxPowerInput="4000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="1600" NumStrings="4" Weight="32"/>
<Inverter FriendlyName="@InverterName" InverterID="D12K 04" InverterName="04 HM600 S2 TMax" Manufacturer="Deye" MaxPowerInput="4000" MaxPowerInputChoice="2000;4000;6000" MaxPowerOutput="600" NumStrings="2" Weight="28"/>
<Battery BatteryID="BYD 01" BatteryName="FIRZ!" Capacity="4500" FriendlyName="@BatteryName" Manufacturer="BYD" MaxCurrent="120" MaxVolt="48" Yield="88">
<AdditionalData DataItem="Image" DataValue="image.png"/>
<AdditionalData DataItem="Manual" DataValue="manual.docx"/>
<AdditionalData DataItem="Certificate" DataValue="certificate.pdf"/>
</Battery>
<Battery BatteryID="BYD 04" BatteryName="04 BYD T02 Stackable" Capacity="9000" FriendlyName="@BatteryName" Manufacturer="BYD" MaxCurrent="120" MaxVolt="48" Yield="32"/>
<Battery BatteryID="GroWatt 05 G2K" BatteryName="05 BYD T01 Stackable" Capacity="4500" FriendlyName="@BatteryName" Manufacturer="PylonTech" MaxCurrent="120" MaxVolt="48" Yield="46"/>
<Battery BatteryID="GroWatt 06 G4K" BatteryName="06 BYD T02 Stackable" Capacity="9000" FriendlyName="@BatteryName" Manufacturer="PylonTech" MaxCurrent="120" MaxVolt="48" Yield="94"/>
</Components>