Compare commits

...

52 Commits

Author SHA1 Message Date
Laurent Trinques 87f9f40e91 Update MacQetDeploy_arm64.sh
Windows Build / build-windows (push) Has been cancelled
Test Windows VS2026 migration / Build on windows-2025-vs2026 (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-14 13:12:43 +02:00
Laurent Trinques 43fb34f852 Update MacQetDeploy_arm64.sh 2026-05-14 12:44:45 +02:00
Laurent Trinques d6251c901e Update MacQetDeploy_arm64.sh 2026-05-14 12:11:21 +02:00
Laurent Trinques c0ba961fb3 Update MacQetDeploy_arm64.sh 2026-05-14 11:45:11 +02:00
Laurent Trinques 56f5a09737 Update MacQetDeploy_arm64.sh 2026-05-14 11:20:36 +02:00
Laurent Trinques b5eebb2123 Update MacQetDeploy_arm64.sh 2026-05-14 11:06:56 +02:00
Laurent Trinques 7bf395afab Every Monday at 2 am (UTC) (cron) or manually
↓
  windows-build.yml
  ├── build-windows  →  generates an exe file + zip + portable artefact
  └── deploy-pages   →  clears old files, uploads the exe file + zip to the ‘release nightly’ repository
        ↓ (workflow_run: completed + successful)
  windows-msi.yml
  ├── uploads the portable artefact
  ├── builds the MSI with WiX v7
  ├── deletes the old .msi, uploads the MSI to the nightly version
  └── generates and deploys GitHub Pages  ← the 3 URLs are known here
The GitHub Pages page is no longer generated by windows-build.yml but by windows-msi.yml once the MSI is in the release
2026-05-14 09:53:29 +02:00
Laurent Trinques 93baa00d00 Update windows-msi.yml 2026-05-14 09:26:01 +02:00
Laurent Trinques 6a7ae44ce5 git submodule update --remote elements
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-14 03:21:35 +02:00
Laurent Trinques 89131a21a3 Merge pull request #459 from Kellermorph/makro-fix
Fix and Improve Multi-selection for Diagram Operations
2026-05-14 03:18:20 +02:00
Kellermorph 0b79dfd149 Fix and Improve Multi-selection for Diagram Operations 2026-05-13 19:42:11 +02:00
Laurent Trinques 72178714fd git submodule update --remote elements
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-13 17:25:39 +02:00
Laurent Trinques c7e236cd48 Update test-vs2026.yml 2026-05-13 13:43:55 +02:00
Laurent Trinques fb769b884c Try to fix https://github.com/qelectrotech/qelectrotech-source-mirror/issues/307
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-13 13:16:43 +02:00
Laurent Trinques 2ae9ec87bb Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-13 01:40:55 +02:00
Laurent Trinques 76d311cb35 Move build-aux/windows/generate-page.py -> build-aux/generate-page.py 2026-05-13 01:23:18 +02:00
Laurent Trinques b016cc9f54 Update windows-build.yml 2026-05-13 01:06:17 +02:00
Laurent Trinques 526e39e909 Add files via upload 2026-05-13 01:05:31 +02:00
Laurent Trinques d781105dfd Update windows-build.yml 2026-05-13 01:02:32 +02:00
Laurent Trinques f6b93c6b71 Update windows-build.yml 2026-05-13 00:40:17 +02:00
Laurent Trinques 61319bbbd6 Update windows-build.yml 2026-05-13 00:08:18 +02:00
Laurent Trinques 1b522c251b Update windows-build.yml 2026-05-12 23:34:10 +02:00
Laurent Trinques acfdab77fa Update windows-msi.yml 2026-05-12 23:33:27 +02:00
Laurent Trinques 8e327448cc Update QElectroTech.wxs 2026-05-12 23:01:54 +02:00
Laurent Trinques 7a8cee0ce6 Update QElectroTech.wxs 2026-05-12 22:58:00 +02:00
Laurent Trinques 82b8e7947e Update windows-msi.yml 2026-05-12 22:54:20 +02:00
Laurent Trinques e542a05d3f Update QElectroTech.wxs 2026-05-12 22:53:33 +02:00
Laurent Trinques 0b337a1514 Update windows-msi.yml 2026-05-12 22:44:12 +02:00
Laurent Trinques 48fec1db98 Update QElectroTech.wxs 2026-05-12 22:30:23 +02:00
Laurent Trinques 31f946426b Update QElectroTech.wxs 2026-05-12 22:18:58 +02:00
Laurent Trinques efa74dd0f5 Update QElectroTech.wxs 2026-05-12 22:12:42 +02:00
Laurent Trinques 703797bb97 Update QElectroTech.wxs 2026-05-12 22:08:24 +02:00
Laurent Trinques ef75ee736a Update windows-msi.yml 2026-05-12 22:02:53 +02:00
Laurent Trinques 15e623ac5f Update QElectroTech.wxs 2026-05-12 21:58:07 +02:00
Laurent Trinques df82a1125d Update windows-msi.yml 2026-05-12 21:56:59 +02:00
Laurent Trinques 7edc2e0241 Update windows-msi.yml 2026-05-12 21:50:32 +02:00
Laurent Trinques 55ae3fc3c6 Update QElectroTech.wxs 2026-05-12 21:46:11 +02:00
Laurent Trinques 2f72e6164c Update windows-msi.yml
Removal of all envs: WIX_ACCEPT_EULA: true (does not work)
Addition of a dedicated ‘Accept WiX EULA’ step with wix eula accept wix7 before any other WiX command — this is the official CI/CD method, which writes a sentinel file to the user profile, thereby authorising all subsequent WiX commands in the same job.
2026-05-12 21:39:36 +02:00
Laurent Trinques e40f9c6b72 Update windows-msi.yml 2026-05-12 21:33:05 +02:00
Laurent Trinques ef261a7afd Update windows-msi.yml
Remove --accept-eula on dotnet tool install
2026-05-12 21:25:57 +02:00
Laurent Trinques 1550944011 Update windows-msi.yml
dotnet tool install --global wix --version 7.0.0 --accept-eula
wix extension add WixToolset.UI.wixext/7.0.0 --accept-eula
wix build ... --accept-eula
2026-05-12 20:55:22 +02:00
Laurent Trinques 32733187b8 Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-12 20:12:02 +02:00
Laurent Trinques 79542edd3b Try to build Windows msi files 2026-05-12 19:50:20 +02:00
Laurent Trinques 9e0ec69c61 Add windows spec files for msi test 2026-05-12 19:49:34 +02:00
Laurent Trinques 2e0a1a55e3 Test Windows VS2026 migration on GitHUB action 2026-05-12 19:43:06 +02:00
Laurent Trinques 308f2ea838 Update windows-build.yml 2026-05-12 15:01:21 +02:00
Laurent Trinques 9a9a5446cf Update windows-build.yml 2026-05-12 14:53:03 +02:00
Laurent Trinques 0f8d835a1b git submodule update --remote elements 2026-05-12 14:22:35 +02:00
Laurent Trinques 663336a7bc Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-11 15:00:59 +02:00
Laurent Trinques 4fab90d5b9 Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-10 21:27:10 +02:00
Laurent Trinques f67df92f0e Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
Use 7-Zip is already installed on all GitHub Windows computers (C:\Program Files\7-Zip\7z.exe), and it is much faster than Compress-Archive when dealing with 9,931 files.
2026-05-10 13:04:31 +02:00
Laurent Trinques 7e2c2cccf8 Update windows-build.yml
Add Ready_to_use packages
2026-05-10 12:42:30 +02:00
15 changed files with 1520 additions and 570 deletions
+112
View File
@@ -0,0 +1,112 @@
name: Test Windows VS2026 migration
# Ce workflow vérifie que le build QElectroTech fonctionne sur l'image
# windows-2025-vs2026, avant la migration forcée du 8 juin 2026.
# Il peut être supprimé une fois la migration confirmée OK.
on:
workflow_dispatch: # déclenchement manuel uniquement
schedule:
- cron: '0 4 * * 1' # chaque lundi à 4h00 UTC (optionnel)
jobs:
test-vs2026:
name: Build on windows-2025-vs2026
runs-on: windows-2025-vs2026 # <-- image avec VS 2026
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Install MSYS2 + dependencies
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: false
install: >-
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-ninja
mingw-w64-ucrt-x86_64-qt5-base
mingw-w64-ucrt-x86_64-qt5-svg
mingw-w64-ucrt-x86_64-qt5-tools
mingw-w64-ucrt-x86_64-qt5-translations
mingw-w64-ucrt-x86_64-sqlite3
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-extra-cmake-modules
mingw-w64-ucrt-x86_64-kwidgetsaddons-qt5
mingw-w64-ucrt-x86_64-kcoreaddons-qt5
mingw-w64-ucrt-x86_64-nsis
mingw-w64-ucrt-x86_64-ccache
mingw-w64-ucrt-x86_64-7zip
git
- name: Force Qt5 (remove Qt6 interference)
shell: msys2 {0}
run: |
rm -rf /ucrt64/lib/cmake/Qt6 || true
pacman -R --noconfirm mingw-w64-ucrt-x86_64-qt6-tools 2>/dev/null || true
- name: Cache ccache
uses: actions/cache@v4
with:
path: C:\Users\runneradmin\AppData\Local\ccache
key: ccache-vs2026-${{ runner.os }}-${{ github.sha }}
restore-keys: |
ccache-vs2026-${{ runner.os }}-
- name: Configure (CMake)
shell: msys2 {0}
run: |
set -euo pipefail
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DQt5_DIR=/ucrt64/lib/cmake/Qt5 \
-DQT_VERSION_MAJOR=5 \
-DCMAKE_DISABLE_FIND_PACKAGE_Qt6=ON \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_FLAGS="-DQET_EXPORT_PROJECT_DB" \
-DBUILD_TESTING=OFF \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
-DSQLite3_INCLUDE_DIR=/ucrt64/include \
-DSQLite3_LIBRARY=/ucrt64/lib/libsqlite3.dll.a \
- name: Build
shell: msys2 {0}
run: |
set -euo pipefail
cmake --build build --parallel $(nproc)
- name: Verify executable
shell: msys2 {0}
run: |
set -euo pipefail
EXE=$(find build -name "qelectrotech.exe" | head -1)
if [ -z "$EXE" ]; then
echo "ERROR: qelectrotech.exe introuvable après le build"
exit 1
fi
SIZE=$(stat -c%s "$EXE")
echo "Executable trouvé : $EXE ($SIZE octets)"
if [ "$SIZE" -lt 100000 ]; then
echo "ERROR: exe trop petit ($SIZE octets), build probablement incomplet"
exit 1
fi
echo "BUILD VS2026 : OK ✓"
- name: Summary
if: always()
shell: msys2 {0}
run: |
echo "=== Résumé de compatibilité VS2026 ==="
gcc --version
cmake --version
ninja --version
echo "Image runner : windows-2025-vs2026"
echo "Date du test : $(date -u)"
+62 -191
View File
@@ -1,11 +1,9 @@
name: Windows Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
schedule:
- cron: '0 2 * * 1' # Every Monday at 2:00 UTC
workflow_dispatch: # Manual trigger available at any time
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -316,11 +314,30 @@ jobs:
nsis_root/files/bin/
if-no-files-found: warn
- name: Zip portable (readytouse)
id: zip_portable
shell: pwsh
run: |
$version = "${{ steps.qet_version.outputs.base_version }}"
$head = "${{ steps.qet_version.outputs.head }}"
$zipName = "qelectrotech-${version}+git${head}-x86-win64-readytouse.zip"
$src = "$env:GITHUB_WORKSPACE\nsis_root\files"
$dst = "$env:GITHUB_WORKSPACE\dist\$zipName"
$7z = "C:\Program Files\7-Zip\7z.exe"
New-Item -ItemType Directory -Force -Path "$env:GITHUB_WORKSPACE\dist" | Out-Null
& $7z a -tzip -mx=5 -mmt=on $dst "$src\*"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$sizeMB = [math]::Round((Get-Item $dst).Length / 1MB, 1)
Write-Output "ZIP created: $zipName ($sizeMB MB)"
"zip_name=$zipName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Upload portable (files/ without installer)
uses: actions/upload-artifact@v4
with:
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
path: nsis_root/files/
path: dist/${{ steps.zip_portable.outputs.zip_name }}
retention-days: 14
- name: Upload NSIS installer
@@ -330,6 +347,13 @@ jobs:
path: dist/Installer_*.exe
retention-days: 14
- name: Upload portable (nom fixe pour le workflow MSI)
uses: actions/upload-artifact@v4
with:
name: qelectrotech-windows-portable
path: nsis_root/files/
retention-days: 14
# ---------------------------------------------------------------------------
# Job 2 : Publie une release nightly + page GitHub Pages
# Ne tourne que sur push master (pas sur les PRs)
@@ -342,11 +366,13 @@ jobs:
contents: write
steps:
- name: Checkout gh-pages branch
- name: Checkout master (for build-aux scripts)
uses: actions/checkout@v4
with:
ref: gh-pages
path: gh-pages
ref: master
path: source
sparse-checkout: build-aux/generate-page.py
sparse-checkout-cone-mode: false
- name: Download installer artifact
uses: actions/download-artifact@v4
@@ -354,6 +380,27 @@ jobs:
name: qelectrotech-windows-installer
path: downloaded/installer/
- name: Download portable artifact
uses: actions/download-artifact@v4
with:
pattern: qelectrotech-*-readytouse
path: downloaded/portable/
merge-multiple: true
- name: Delete old nightly assets (.exe and .zip)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
# Fetch existing .exe and .zip asset names and delete them
gh release view nightly --repo "$REPO" --json assets \
--jq '.assets[] | select(.name | test("\\.(exe|zip)$")) | .name' \
| while read -r name; do
echo "Deleting old asset: $name"
gh release delete-asset nightly "$name" --repo "$REPO" --yes
done
echo "Old .exe and .zip assets deleted."
- name: Update nightly release
uses: softprops/action-gh-release@v2
with:
@@ -368,190 +415,14 @@ jobs:
| **Run** | https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
| **Date** | ${{ github.event.head_commit.timestamp }} |
> ⚠️ This release is overwritten on every push to `master`.
> ⚠️ This is a development version; it introduces new features you want, but may cause bugs that have not yet been identified yet.
> For stable releases, see the [Releases page](https://github.com/${{ github.repository }}/releases).
prerelease: true
make_latest: false
files: downloaded/installer/*.exe
files: |
downloaded/installer/*.exe
downloaded/portable/*.zip
token: ${{ secrets.GITHUB_TOKEN }}
- name: Generate download page (index.html)
run: |
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
SHORT="${{ github.sha }}"
SHORT="${SHORT:0:7}"
REPO="${{ github.repository }}"
RUN_URL="https://github.com/$REPO/actions/runs/${{ github.run_id }}"
EXE_NAME=$(ls downloaded/installer/*.exe | xargs -I{} basename {})
INSTALLER_URL="https://github.com/$REPO/releases/download/nightly/$EXE_NAME"
mkdir -p gh-pages
cat > gh-pages/index.html << HTMLEOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QElectroTech Nightly Builds</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f0f4f8;
color: #2d3748;
min-height: 100vh;
}
header {
background: linear-gradient(135deg, #1a365d 0%, #2b6cb0 100%);
color: white;
padding: 48px 24px 40px;
text-align: center;
}
header h1 { font-size: 2.2em; letter-spacing: -0.5px; margin-bottom: 8px; }
header p { opacity: 0.8; font-size: 1.05em; }
main {
max-width: 680px;
margin: 40px auto;
padding: 0 20px 60px;
}
.card {
background: white;
border-radius: 12px;
padding: 28px;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
}
.card h2 {
font-size: 1em;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #718096;
margin-bottom: 16px;
}
.meta {
font-size: 0.875em;
color: #4a5568;
line-height: 1.8;
margin-bottom: 20px;
}
.meta a { color: #2b6cb0; text-decoration: none; }
.meta a:hover { text-decoration: underline; }
.badge {
display: inline-block;
background: #ebf8ff;
color: #2b6cb0;
border-radius: 4px;
font-size: 0.8em;
font-weight: 600;
padding: 2px 8px;
margin-left: 6px;
vertical-align: middle;
}
.warning {
background: #fffbeb;
border-left: 4px solid #f6ad55;
border-radius: 4px;
padding: 12px 16px;
font-size: 0.875em;
color: #744210;
margin-bottom: 24px;
line-height: 1.5;
}
.warning a { color: #c05621; }
.downloads { display: flex; flex-direction: column; gap: 12px; }
.btn {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 20px;
border-radius: 8px;
font-size: 0.95em;
font-weight: 600;
text-decoration: none;
transition: transform 0.1s, box-shadow 0.1s;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.btn-primary { background: #2b6cb0; color: white; }
.btn-secondary { background: #edf2f7; color: #2d3748; }
.btn-icon { font-size: 1.3em; }
.btn-text small {
display: block;
font-weight: 400;
font-size: 0.8em;
opacity: 0.75;
margin-top: 1px;
}
footer {
text-align: center;
font-size: 0.8em;
color: #a0aec0;
padding: 32px 0 0;
}
footer a { color: #718096; text-decoration: none; }
</style>
</head>
<body>
<header>
<h1>⚡ QElectroTech</h1>
<p>Nightly Windows Builds</p>
</header>
<main>
<div class="card">
<h2>Build info</h2>
<div class="meta">
📅 &nbsp;<strong>$DATE</strong><br>
🔀 &nbsp;Commit <a href="https://github.com/$REPO/commit/${{ github.sha }}"><code>$SHORT</code></a><br>
🔧 &nbsp;<a href="$RUN_URL">CI Run #${{ github.run_number }}</a>
<span class="badge">nightly</span>
</div>
<div class="warning">
⚠️ These builds are generated automatically on every commit to <code>master</code>
and may be unstable or incomplete.
For production use, download a
<a href="https://github.com/$REPO/releases">stable release</a>.
</div>
</div>
<div class="card">
<h2>🪟 Windows — x86_64</h2>
<div class="downloads">
<a class="btn btn-primary" href="$INSTALLER_URL">
<span class="btn-icon">⬇</span>
<span class="btn-text">
Windows Installer
<small>.exe — recommended, includes all dependencies</small>
</span>
</a>
<a class="btn btn-secondary" href="https://github.com/$REPO/releases/tag/nightly">
<span class="btn-icon">📦</span>
<span class="btn-text">
All nightly files on GitHub
<small>Release page with checksums</small>
</span>
</a>
</div>
</div>
</main>
<footer>
Auto-generated by GitHub Actions &nbsp;·&nbsp;
<a href="https://github.com/$REPO">Source on GitHub</a> &nbsp;·&nbsp;
<a href="https://qelectrotech.org">qelectrotech.org</a>
</footer>
</body>
</html>
HTMLEOF
- name: Commit and push to gh-pages
run: |
cd gh-pages
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add index.html
git diff --staged --quiet \
|| git commit -m "nightly: build #${{ github.run_number }} (${{ github.sha }})"
git push origin gh-pages
# GitHub Pages is generated and deployed by windows-msi.yml
# after the MSI upload, so that all 3 URLs (exe, zip, msi) are known.
+363
View File
@@ -0,0 +1,363 @@
name: Windows MSI (WiX v7)
on:
# Triggered automatically after a successful Windows build on master
workflow_run:
workflows: ["Windows Build"]
types: [completed]
branches: [master]
# Manual trigger still available (e.g. for a specific run_id)
workflow_dispatch:
inputs:
run_id:
description: "Run ID of 'Windows Build' (leave empty for automatic)"
required: false
default: ""
jobs:
build-msi:
name: Build MSI with WiX v7
runs-on: windows-latest
# Only runs if Windows Build succeeded (or triggered manually)
if: >
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
permissions:
contents: write
pages: write
id-token: write
steps:
# ----------------------------------------------------------------
# 1. Checkout (to retrieve QElectroTech.wxs and sources)
# ----------------------------------------------------------------
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# ----------------------------------------------------------------
# 2. Download the portable artifact from the main build
# Requires windows-build.yml to upload an artifact named
# "qelectrotech-windows-portable" (fixed name)
# ----------------------------------------------------------------
- name: Download portable artifact
uses: actions/download-artifact@v4
with:
name: qelectrotech-windows-portable
path: artifact\files
# workflow_run => use the triggering run's ID
# workflow_dispatch => use input run_id if provided, otherwise current run
run-id: ${{ github.event.workflow_run.id || github.event.inputs.run_id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
# ----------------------------------------------------------------
# 3. Extract version from sources
# ----------------------------------------------------------------
- name: Extract version
id: version
shell: pwsh
run: |
# Version from qetversion.cpp (same logic as windows-build.yml)
$src = Get-Content "sources\qetversion.cpp" -Raw -ErrorAction SilentlyContinue
if ($src -match 'return QVersionNumber\{([^}]+)\}') {
$ver = $Matches[1] -replace '\s','' -replace ',','.'
} else {
# Fallback: CMakeLists.txt
$cmake = Get-Content "CMakeLists.txt" -Raw
if ($cmake -match 'project\s*\([^)]*VERSION\s+([\d]+\.[\d]+\.[\d]+)') {
$ver = $Matches[1]
} else {
$ver = "0.0.0"
}
}
# Numeric MSI version: 4 digits required (e.g. 0.100.1.0)
$verMsi = "$ver.0"
# Short SHA for the display version
$sha = git rev-parse --short HEAD 2>$null
if (-not $sha) { $sha = "unknown" }
# Cumulative revision number (same calculation as windows-build.yml)
$count = git rev-list HEAD --count 2>$null
$rev = [int]$count + 473
$verDisplay = "${ver}-r${rev}-${sha}_x86_64-win64"
echo "VERSION_MSI=$verMsi" >> $env:GITHUB_OUTPUT
echo "VERSION_DISPLAY=$verDisplay" >> $env:GITHUB_OUTPUT
Write-Host "Version MSI : $verMsi"
Write-Host "Version display : $verDisplay"
# ----------------------------------------------------------------
# 4. Install WiX v7, accept EULA and install WixUI extension
# All done in one step: PATH is updated within the same step
# so wix is immediately available for eula and extension commands
# ----------------------------------------------------------------
- name: Install WiX v7
shell: pwsh
run: |
dotnet tool install --global wix --version 7.0.0
# Update PATH immediately for the rest of this step
$toolsPath = [System.IO.Path]::Combine($env:USERPROFILE, '.dotnet', 'tools')
$env:PATH = "$toolsPath;$env:PATH"
# Also export for subsequent steps
echo $toolsPath >> $env:GITHUB_PATH
# Accept OSMF EULA (official CI/CD method: writes a sentinel file)
wix eula accept wix7
# Install WixUI extension
wix extension add WixToolset.UI.wixext/7.0.0
Write-Host "WiX v7 installed, EULA accepted, UI extension added."
# ----------------------------------------------------------------
# 5. Check that the WXS file exists in the repository
# ----------------------------------------------------------------
- name: Check WXS file
shell: pwsh
run: |
$wxs = "build-aux\windows\QElectroTech.wxs"
if (-not (Test-Path $wxs)) {
Write-Error "WXS file not found: $wxs"
Write-Host "Contents of build-aux\windows\ :"
Get-ChildItem "build-aux\windows\" -ErrorAction SilentlyContinue
exit 1
}
Write-Host "WXS found: $wxs"
# ----------------------------------------------------------------
# 6. Check the artifact structure and locate files/
# ----------------------------------------------------------------
- name: Check artifact structure
shell: pwsh
run: |
Write-Host "=== Contents of artifact\files (2 levels) ==="
Get-ChildItem -Path "artifact\files" -Depth 2 |
Select-Object FullName | Format-Table -AutoSize
# Search for qelectrotech.exe in the artifact
$exe = Get-ChildItem -Path "artifact\files" -Filter "qelectrotech.exe" -Recurse | Select-Object -First 1
if (-not $exe) {
# Also try QElectroTech.exe (capital Q)
$exe = Get-ChildItem -Path "artifact\files" -Filter "QElectroTech.exe" -Recurse | Select-Object -First 1
}
if (-not $exe) {
Write-Error "qelectrotech.exe not found in artifact"
exit 1
}
Write-Host "Executable: $($exe.FullName) ($([math]::Round($exe.Length/1MB,1)) MB)"
# FilesDir = folder containing bin\
$binDir = $exe.Directory.FullName
$filesDir = Split-Path $binDir -Parent
echo "FILES_DIR=$filesDir" >> $env:GITHUB_ENV
Write-Host "FILES_DIR: $filesDir"
# ----------------------------------------------------------------
# 7. Convert LICENSE (GPL-2) to RTF for the WixUI licence screen
# RTF is the only format accepted by Windows Installer.
# The conversion wraps plain text lines in basic RTF markup.
# ----------------------------------------------------------------
- name: Convert LICENSE to RTF
shell: pwsh
run: |
$licSrc = "LICENSE"
$licRtf = "$env:TEMP\License.rtf"
if (-not (Test-Path $licSrc)) {
Write-Error "LICENSE file not found in repository root"
exit 1
}
$lines = Get-Content $licSrc -Encoding UTF8
# RTF header — Courier New, 9pt, black
$rtf = New-Object System.Text.StringBuilder
[void]$rtf.AppendLine('{\rtf1\ansi\ansicpg1252\deff0')
[void]$rtf.AppendLine('{\fonttbl{\f0\fmodern\fprq1\fcharset0 Courier New;}}')
[void]$rtf.AppendLine('{\colortbl;\red0\green0\blue0;}')
[void]$rtf.AppendLine('\f0\fs18\cf1')
foreach ($line in $lines) {
# Escape RTF special characters
$escaped = $line `
-replace '\\', '\\\\' `
-replace '\{', '\{' `
-replace '\}', '\}'
[void]$rtf.AppendLine("$escaped\par")
}
[void]$rtf.AppendLine('}')
[System.IO.File]::WriteAllText($licRtf, $rtf.ToString(), [System.Text.Encoding]::ASCII)
echo "LICENSE_RTF=$licRtf" >> $env:GITHUB_ENV
Write-Host "License.rtf generated: $licRtf ($([math]::Round((Get-Item $licRtf).Length/1KB,1)) KB)"
# ----------------------------------------------------------------
# 8. Replace Lancer QET.bat with the MSI-specific version
# The portable version uses relative paths suited for the zip.
# The MSI version uses %~dp0 to resolve paths relative to
# the installation directory in Program Files.
# ----------------------------------------------------------------
- name: Replace Lancer QET.bat for MSI
shell: pwsh
run: |
$bat = "$env:FILES_DIR\Lancer QET.bat"
$content = "@echo off`r`nstart `"`" `"%~dp0bin\qelectrotech.exe`" --common-elements-dir=`"%~dp0elements/`" --common-tbt-dir=`"%~dp0titleblocks/`" --lang-dir=`"%~dp0lang/`" -style windowsvista`r`n"
[System.IO.File]::WriteAllText($bat, $content, [System.Text.Encoding]::ASCII)
Write-Host "Lancer QET.bat replaced for MSI installation."
Write-Host "=== Content of new Lancer QET.bat ==="
Get-Content $bat
# ----------------------------------------------------------------
# 9. Build the MSI
# ----------------------------------------------------------------
- name: Build MSI
shell: pwsh
run: |
$version = "${{ steps.version.outputs.VERSION_MSI }}"
$verDisplay = "${{ steps.version.outputs.VERSION_DISPLAY }}"
$filesDir = $env:FILES_DIR
$licRtf = $env:LICENSE_RTF
$wxs = "build-aux\windows\QElectroTech.wxs"
$outputName = "QElectroTech-${verDisplay}.msi"
New-Item -ItemType Directory -Force -Path "dist" | Out-Null
Write-Host "=== wix build ==="
Write-Host " WXS : $wxs"
Write-Host " Version : $version"
Write-Host " FilesDir : $filesDir"
Write-Host " LicenseRtf : $licRtf"
Write-Host " Output : dist\$outputName"
wix build $wxs `
-arch x64 `
-d "Version=$version" `
-d "ProductVersion=$verDisplay" `
-d "FilesDir=$filesDir" `
-d "LicenseRtf=$licRtf" `
-ext WixToolset.UI.wixext `
-o "dist\$outputName"
if (-not (Test-Path "dist\$outputName")) {
Write-Error "MSI not generated: dist\$outputName"
exit 1
}
$size = [math]::Round((Get-Item "dist\$outputName").Length / 1MB, 1)
Write-Host "MSI generated: dist\$outputName ($size MB) ✓"
echo "MSI_NAME=$outputName" >> $env:GITHUB_ENV
# ----------------------------------------------------------------
# 10. Upload the MSI artifact
# ----------------------------------------------------------------
- name: Upload MSI artifact
uses: actions/upload-artifact@v4
with:
name: qelectrotech-windows-msi
path: dist\*.msi
retention-days: 14
if-no-files-found: error
# ----------------------------------------------------------------
# 11. Delete old .msi asset then upload new MSI to nightly release
# ----------------------------------------------------------------
- name: Delete old nightly .msi asset
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
gh release view nightly --repo "$REPO" --json assets \
--jq '.assets[] | select(.name | test("\\.msi$")) | .name' \
| while read -r name; do
echo "Deleting old asset: $name"
gh release delete-asset nightly "$name" --repo "$REPO" --yes
done
echo "Old .msi assets deleted."
shell: bash
- name: Upload MSI to nightly release
uses: softprops/action-gh-release@v2
with:
tag_name: nightly
files: dist/*.msi
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ----------------------------------------------------------------
# 12. Summary
# ----------------------------------------------------------------
- name: Summary
if: always()
shell: pwsh
run: |
Write-Host "=== MSI build summary ==="
Write-Host "Version : ${{ steps.version.outputs.VERSION_DISPLAY }}"
Write-Host "WiX : v7.0.0"
Write-Host "Runner image : ${{ runner.os }} / ${{ runner.arch }}"
if (Test-Path "dist\$env:MSI_NAME") {
$size = [math]::Round((Get-Item "dist\$env:MSI_NAME").Length / 1MB, 1)
Write-Host "MSI : $env:MSI_NAME ($size MB) ✓"
} else {
Write-Host "MSI : FAILED ✗"
}
# ----------------------------------------------------------------
# 13. Generate and deploy the GitHub Pages download page
# Toutes les URLs sont connues ici (exe, zip, msi).
# ----------------------------------------------------------------
- name: Checkout (for generate-page.py)
uses: actions/checkout@v4
with:
ref: master
path: source
sparse-checkout: build-aux/generate-page.py
sparse-checkout-cone-mode: false
- name: Generate download page (index.html)
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
REPO="${{ github.repository }}"
# Fetch asset names from the nightly release (source of truth)
ASSETS=$(gh release view nightly --repo "$REPO" --json assets --jq '.assets[].name')
EXE_NAME=$(echo "$ASSETS" | grep '\.exe$' | head -1)
ZIP_NAME=$(echo "$ASSETS" | grep '\.zip$' | head -1)
MSI_NAME=$(echo "$ASSETS" | grep '\.msi$' | head -1 || echo "")
BASE="https://github.com/$REPO/releases/download/nightly"
INSTALLER_URL="$BASE/$EXE_NAME"
PORTABLE_URL="$BASE/$ZIP_NAME"
MSI_URL=""
[ -n "$MSI_NAME" ] && MSI_URL="$BASE/$MSI_NAME"
SHA="${{ github.event.workflow_run.head_sha || github.sha }}"
SHORT="${SHA:0:7}"
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
RUN_URL="https://github.com/$REPO/actions/runs/${{ github.run_id }}"
RUN_NUMBER="${{ github.run_number }}"
export DATE SHORT REPO SHA RUN_URL RUN_NUMBER
export INSTALLER_URL PORTABLE_URL MSI_URL
python3 source/build-aux/generate-page.py
- name: Add .nojekyll
shell: bash
run: touch gh-pages/.nojekyll
- name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: gh-pages/
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
generate-page.py — Generates gh-pages/index.html for QElectroTech nightly builds.
Called from windows-build.yml deploy-pages job.
Environment variables required:
DATE, SHORT, REPO, SHA, RUN_URL, RUN_NUMBER,
INSTALLER_URL, PORTABLE_URL, MSI_URL (optional)
"""
import os
date = os.environ.get("DATE", "")
short = os.environ.get("SHORT", "")
repo = os.environ.get("REPO", "")
sha = os.environ.get("SHA", "")
run_url = os.environ.get("RUN_URL", "")
run_number = os.environ.get("RUN_NUMBER", "")
installer_url = os.environ.get("INSTALLER_URL", "")
portable_url = os.environ.get("PORTABLE_URL", "")
msi_url = os.environ.get("MSI_URL", "")
msi_block = ""
if msi_url:
msi_block = f"""
<a class="btn btn-msi" href="{msi_url}">
<span class="btn-icon">&#11015;</span>
<span class="btn-text">Windows Installer .msi<small>.msi &mdash; for enterprise / GPO deployment</small></span>
</a>"""
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QElectroTech &ndash; Nightly Builds</title>
<style>
*,*::before,*::after{{box-sizing:border-box;margin:0;padding:0}}
body{{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f4f8;color:#2d3748;min-height:100vh}}
header{{background:linear-gradient(135deg,#1a365d 0%,#2b6cb0 100%);color:white;padding:48px 24px 40px;text-align:center}}
header h1{{font-size:2.2em;letter-spacing:-0.5px;margin-bottom:8px}}
header p{{opacity:.8;font-size:1.05em}}
main{{max-width:680px;margin:40px auto;padding:0 20px 60px}}
.card{{background:white;border-radius:12px;padding:28px;margin-bottom:24px;box-shadow:0 2px 12px rgba(0,0,0,.08)}}
.card h2{{font-size:1em;text-transform:uppercase;letter-spacing:.06em;color:#718096;margin-bottom:16px}}
.meta{{font-size:.875em;color:#4a5568;line-height:1.8;margin-bottom:20px}}
.meta a{{color:#2b6cb0;text-decoration:none}}
.meta a:hover{{text-decoration:underline}}
.badge{{display:inline-block;background:#ebf8ff;color:#2b6cb0;border-radius:4px;font-size:.8em;font-weight:600;padding:2px 8px;margin-left:6px;vertical-align:middle}}
.warning{{background:#fffbeb;border-left:4px solid #f6ad55;border-radius:4px;padding:12px 16px;font-size:.875em;color:#744210;margin-bottom:24px;line-height:1.5}}
.warning a{{color:#c05621}}
.downloads{{display:flex;flex-direction:column;gap:12px}}
.btn{{display:flex;align-items:center;gap:12px;padding:14px 20px;border-radius:8px;font-size:.95em;font-weight:600;text-decoration:none;transition:transform .1s,box-shadow .1s}}
.btn:hover{{transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.15)}}
.btn-primary{{background:#2b6cb0;color:white}}
.btn-msi{{background:#6b46c1;color:white}}
.btn-secondary{{background:#edf2f7;color:#2d3748}}
.btn-icon{{font-size:1.3em}}
.btn-text small{{display:block;font-weight:400;font-size:.8em;opacity:.75;margin-top:1px}}
footer{{text-align:center;font-size:.8em;color:#a0aec0;padding:32px 0 0}}
footer a{{color:#718096;text-decoration:none}}
</style>
</head>
<body>
<header>
<h1>&#9889; QElectroTech</h1>
<p>Nightly Windows Builds</p>
</header>
<main>
<div class="card">
<h2>Build info</h2>
<div class="meta">
&#128197; &nbsp;<strong>{date}</strong><br>
&#128256; &nbsp;Commit <a href="https://github.com/{repo}/commit/{sha}"><code>{short}</code></a><br>
&#128295; &nbsp;<a href="{run_url}">CI Run #{run_number}</a>
<span class="badge">nightly</span>
</div>
<div class="warning">
&#9888;&#65039; This is a development version; it introduces new features you want,
but may cause bugs that have not yet been identified yet in <code>master</code>.
For production use, download a <a href="https://github.com/{repo}/releases">stable release</a>.
</div>
</div>
<div class="card">
<h2>&#127993; Windows &mdash; x86_64</h2>
<div class="downloads">
<a class="btn btn-primary" href="{installer_url}">
<span class="btn-icon">&#11015;</span>
<span class="btn-text">Windows Installer<small>.exe &mdash; recommended, includes all dependencies</small></span>
</a>
{msi_block}
<a class="btn btn-secondary" href="{portable_url}">
<span class="btn-icon">&#128230;</span>
<span class="btn-text">Windows Portable<small>.zip &mdash; no installation required, extract and run &quot;Lancer QET.bat&quot;</small></span>
</a>
<a class="btn btn-secondary" href="https://github.com/{repo}/releases/tag/nightly">
<span class="btn-icon">&#128230;</span>
<span class="btn-text">All nightly files on GitHub<small>Release page with checksums</small></span>
</a>
</div>
</div>
</main>
<footer>
Auto-generated by GitHub Actions &nbsp;&middot;&nbsp;
<a href="https://github.com/{repo}">Source on GitHub</a> &nbsp;&middot;&nbsp;
<a href="https://qelectrotech.org">qelectrotech.org</a>
</footer>
</body>
</html>"""
os.makedirs("gh-pages", exist_ok=True)
with open("gh-pages/index.html", "w", encoding="utf-8") as f:
f.write(html)
print("index.html written OK")
+118
View File
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
QElectroTech.wxs - WiX v7 installer definition
Generates a self-contained x64 MSI for QElectroTech (MSYS2/UCRT64 build).
Variables passed from the wix build command line:
-d FilesDir=<absolute path to artifact\files>
-d Version=<numeric version e.g. 0.100.1.0>
-d ProductVersion=<display version e.g. 0.100.1-r8770-abc1234_x86_64-win64>
-d LicenseRtf=<absolute path to License.rtf>
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package
Name="QElectroTech"
Manufacturer="QElectroTech Team"
Version="$(var.Version)"
UpgradeCode="A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
Language="1033"
Codepage="1252"
InstallerVersion="500"
Scope="perMachine">
<!-- Embed all cabinet files inside the MSI (self-contained installer) -->
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<!-- In-place upgrade: automatically uninstalls the previous version -->
<MajorUpgrade
DowngradeErrorMessage="A newer version of QElectroTech is already installed."
Schedule="afterInstallInitialize" />
<!-- Installation directory -->
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="INSTALLDIR" Name="QElectroTech" />
</StandardDirectory>
<!-- ============================================================
All application files harvested in one pass from files\**
============================================================ -->
<ComponentGroup Id="CG_AllFiles" Directory="INSTALLDIR">
<Files Include="$(var.FilesDir)\**" />
</ComponentGroup>
<!-- ============================================================
Desktop + Start Menu shortcuts
============================================================ -->
<ComponentGroup Id="CG_Shortcuts" Directory="INSTALLDIR">
<Component Id="C_ShortcutDesktop" Guid="F1A2B3C4-D5E6-7890-5678-012345678901">
<Shortcut Id="DesktopShortcut"
Directory="DesktopFolder"
Name="QElectroTech"
Target="[INSTALLDIR]Lancer QET.bat"
Icon="qet.ico"
WorkingDirectory="INSTALLDIR" />
<RegistryValue Root="HKCU"
Key="Software\QElectroTech"
Name="DesktopShortcut"
Type="integer" Value="1"
KeyPath="yes" />
</Component>
<Component Id="C_ShortcutStartMenu" Guid="A2B3C4D5-E6F7-8901-6789-123456789012">
<Shortcut Id="StartMenuShortcut"
Directory="ProgramMenuFolder"
Name="QElectroTech"
Target="[INSTALLDIR]Lancer QET.bat"
Icon="qet.ico"
WorkingDirectory="INSTALLDIR" />
<RegistryValue Root="HKCU"
Key="Software\QElectroTech"
Name="StartMenuShortcut"
Type="integer" Value="1"
KeyPath="yes" />
</Component>
</ComponentGroup>
<!-- ============================================================
.qet file association
============================================================ -->
<ComponentGroup Id="CG_FileAssoc" Directory="INSTALLDIR">
<Component Id="C_FileAssoc" Guid="B3C4D5E6-F7A8-9012-7890-234567890123">
<RegistryValue Root="HKCR" Key=".qet" Type="string" Value="QElectroTech.Document" KeyPath="yes" />
<RegistryValue Root="HKCR" Key="QElectroTech.Document" Type="string" Value="QElectroTech Project" />
<RegistryValue Root="HKCR" Key="QElectroTech.Document\DefaultIcon" Type="string"
Value="[INSTALLDIR]ico\qelectrotech.ico" />
<RegistryValue Root="HKCR" Key="QElectroTech.Document\shell\open\command" Type="string"
Value="&quot;[INSTALLDIR]bin\qelectrotech.exe&quot; &quot;%1&quot;" />
</Component>
</ComponentGroup>
<!-- ============================================================
Icon for shortcuts
============================================================ -->
<Icon Id="qet.ico" SourceFile="$(var.FilesDir)\ico\qelectrotech.ico" />
<!-- ============================================================
Main feature (everything included)
============================================================ -->
<Feature Id="ProductFeature" Title="QElectroTech" Level="1">
<ComponentGroupRef Id="CG_AllFiles" />
<ComponentGroupRef Id="CG_Shortcuts" />
<ComponentGroupRef Id="CG_FileAssoc" />
</Feature>
<!-- WixUI_Minimal with QElectroTech GPL-2 license -->
<ui:WixUI Id="WixUI_Minimal" />
<!-- WixVariable without ui: namespace, at Package level -->
<WixVariable Id="WixUILicenseRtf" Value="$(var.LicenseRtf)" />
<!-- Properties shown in Programs and Features -->
<Property Id="ARPPRODUCTICON" Value="qet.ico" />
<Property Id="ARPHELPLINK" Value="https://qelectrotech.org" />
<Property Id="ARPURLINFOABOUT" Value="https://qelectrotech.org" />
<Property Id="ARPCOMMENTS" Value="Free electrical schematic editor" />
</Package>
</Wix>
+293 -121
View File
@@ -16,7 +16,6 @@
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
# Need homebrew and coreutils installed see <http://brew.sh>.
#Force MacOSX12.3.sdk
#see: https://www.downtowndougbrown.com/2023/08/how-to-create-a-qt-5-arm-intel-universal-binary-for-mac/
@@ -26,14 +25,20 @@ export DEVELOPER_DIR=/Applications/Xcode_14.01.app/Contents/Developer
APPNAME='qelectrotech'
BUNDLE=$APPNAME.app
APPBIN="$BUNDLE/Contents/MacOS/$APPNAME"
IDENTITY="Developer ID Application: Laurent TRINQUES (Y73WZ6WZ5X)"
# Emplacement du script
# Temp paths
RW_DMG="/tmp/qet_rw.dmg"
MOUNT_POINT="/tmp/qet_dmg_mount"
STAGING="/tmp/qet_dmg_staging"
# Script location
current_dir=$(dirname "$0")
# On se remet au depart
# Go back to repo root
cd "${current_dir}/../"
# Emplacement courant
# Current directory
current_dir=$(PWD)
@@ -46,11 +51,11 @@ echo "Please see the \"Deploying an Application on Qt/Mac\""
echo "page in the Qt documentation for more information."
echo
echo "This script :"
echo "\t - up date the git depot"
echo "\t - built the application bundle,"
echo "\t - update the git depot"
echo "\t - build the application bundle,"
echo "\t - copy over required Qt frameworks,"
echo "\t - copy additional files: translations, titleblocks and elements,"
echo "\t - create image disk."
echo "\t - notarize the .app, then create a signed DMG."
echo
echo "Enjoy ;-)"
echo
@@ -70,25 +75,23 @@ echo
echo "______________________________________________________________"
echo "Run GIT:"
# Fait une mise à jour
git submodule init
git submodule update
git pull --recurse-submodules
git pull
#git checkout foliolist_position
# recupere le numero de la nouvelle revision
# Get revision number and version
GITCOMMIT=$(git rev-parse --short HEAD)
A=$(git rev-list HEAD --count)
HEAD=$(($A+473))
VERSION=$(cat sources/qetversion.cpp | grep "return QVersionNumber{"| head -n 1| awk -F "{" '{ print $2 }' | awk -F "}" '{ print $1 }' | sed -e 's/,/./g' -e 's/ //g')
#VERSION=$(cat sources/qetversion.cpp | grep "return QVersionNumber{ 0, "| head -n 1| cut -c32-40| sed -e 's/,/./g' -e 's/ //g') #Find major, minor, and micro version numbers in sources/qetversion.cp
# Tarball de la dernière revision déjà créé
if [ -e "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip" ] ; then
DMG_NAME="${APPNAME}-$VERSION-r$HEAD-arm64.dmg"
DMG_PATH="build-aux/mac-osx/$DMG_NAME"
# Check if already built
if [ -e "$DMG_PATH" ] ; then
echo "There are not new updates, make disk image can"
echo "take a lot of time (5 min). Can you continu?"
echo "[y/n]"
@@ -108,28 +111,27 @@ echo
echo "______________________________________________________________"
echo "Run make install:"
# pour effacer lancienne compilation
# Remove old bundle
if [ -d $BUNDLE ] ; then
echo "Removing hold bundle..."
echo "Removing old bundle..."
rm -rf $BUNDLE
fi
if [ -e Makefile ] ; then
echo "Removing hold Makefile..."
echo "Removing old Makefile..."
rm .qmake.stash
make clean
fi
# genere le Makefile
# Generate Makefile
echo "Generating new makefile..."
qmake -spec macx-clang
qmake -spec macx-clang
# compilation
# Compile
if [ -e Makefile.Release ] ; then
START_TIME=$SECONDS
# arret du script si erreur de compilation
START_TIME=$SECONDS
testSuccessBuild () {
if [ $? -ne 0 ]; then
if [ $? -ne 0 ]; then
cleanVerionTag
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo
@@ -138,19 +140,18 @@ if [ -e Makefile.Release ] ; then
fi
}
# utilise tout les coeurs pour une compilation plus rapide
coeur=$(sysctl hw.ncpu | awk '{print $2}')
if [ $? -ne 0 ]; then
if [ $? -ne 0 ]; then
make -f Makefile.Release
testSuccessBuild
else
make -j$(($coeur + 1)) -f Makefile.Release
testSuccessBuild
fi
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo
echo "The time of compilation is $(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec"
echo
echo "The time of compilation is $(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec"
else
echo "ERROR: Makefile not found. This script requires the macx-clang makespec"
exit
@@ -158,8 +159,7 @@ fi
cp -R ${current_dir}/misc/Info.plist qelectrotech.app/Contents/
cp -R ${current_dir}/ico/mac_icon/*.icns qelectrotech.app/Contents/Resources/
# On rajoute le numero de version pour "cmd + i"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION r$HEAD" "qelectrotech.app/Contents/Info.plist" # Version number
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION r$HEAD" "qelectrotech.app/Contents/Info.plist"
### copy over frameworks ############################################
@@ -168,70 +168,58 @@ echo
echo "______________________________________________________________"
echo "Copy Qt libraries and private frameworks:"
echo "Processing Mac deployment tool..."
echo "Processing Mac deployment tool..."
if [ ! -d $BUNDLE ] ; then
echo "ERROR: cannot find application bundle \"$BUNDLE\" in current directory"
exit
fi
#~/Qt/5.5/clang_64/bin/macdeployqt $BUNDLE
macdeployqt $BUNDLE
### add file missing #######################################
### add missing files ###############################################
echo
echo "______________________________________________________________"
echo "Copy file missing:"
echo "Copy missing files:"
# Dossier à ajouter
QET_ELMT_DIR="${current_dir}/elements/"
QET_TBT_DIR="${current_dir}/titleblocks/"
QET_LANG_DIR="${current_dir}/lang/"
QET_EXAMPLES_DIR="${current_dir}/examples/"
QET_FONTS_DIR="${current_dir}/fonts/"
QET_LICENSES_DIR="${current_dir}/licenses/"
# Add new folder for Qt dialog translation see
## see <https://download.tuxfamily.org/qet/Qt_lang/>.
LANG_DIR="${current_dir}/lang1/"
if [ -d "${QET_ELMT_DIR}" ]; then
echo "Copying add elements in the bundle..."
#mkdir $BUNDLE/Contents/Resources/elements
echo "Copying elements in the bundle..."
cp -R ${QET_ELMT_DIR} $BUNDLE/Contents/Resources/elements
fi
if [ -d "${QET_TBT_DIR}" ]; then
echo "Copying titleblocks in the bundle..."
#mkdir $BUNDLE/Contents/Resources/titleblocks
cp -R ${QET_TBT_DIR} $BUNDLE/Contents/Resources/titleblocks
fi
if [ -d "${QET_LANG_DIR}" ]; then
echo "Copying translations in the bundle... "
echo "Copying translations in the bundle..."
mkdir $BUNDLE/Contents/Resources/lang
cp ${current_dir}/lang/*.qm $BUNDLE/Contents/Resources/lang
fi
if [ -d "${LANG_DIR}" ]; then
echo "Copying translations in the bundle... "
cp ${current_dir}/lang1/*.qm $BUNDLE/Contents/Resources/lang
echo "Copying extra translations in the bundle..."
cp ${current_dir}/lang1/*.qm $BUNDLE/Contents/Resources/lang
fi
if [ -d "${QET_EXAMPLES_DIR}" ]; then
echo "Copying examples in the bundle... "
mkdir $BUNDLE/Contents/Resources/examples
cp ${current_dir}/examples/*.qet $BUNDLE/Contents/Resources/examples
echo "Copying examples in the bundle..."
mkdir $BUNDLE/Contents/Resources/examples
cp ${current_dir}/examples/*.qet $BUNDLE/Contents/Resources/examples
fi
if [ -d "${QET_FONTS_DIR}" ]; then
echo "Copying fonts in the bundle... "
mkdir $BUNDLE/Contents/Resources/fonts
cp ${current_dir}/fonts/*.ttf $BUNDLE/Contents/Resources/fonts
echo "Copying fonts in the bundle..."
mkdir $BUNDLE/Contents/Resources/fonts
cp ${current_dir}/fonts/*.ttf $BUNDLE/Contents/Resources/fonts
fi
if [ -d "${QET_LICENSES_DIR}" ]; then
@@ -240,98 +228,282 @@ if [ -d "${QET_LICENSES_DIR}" ]; then
cp -R -L ${QET_LICENSES_DIR} $BUNDLE/Contents/Resources/licenses
fi
codesign --force --deep --sign --timestamp -s "Developer ID Application: Laurent TRINQUES (Y73WZ6WZ5X)" --options=runtime $BUNDLE
### create zip tarball ###############################################
### Sign the bundle #################################################
# Sign in correct order: all dylibs first (including flat libs copied
# by macdeployqt from Homebrew), then frameworks, plugins, bundle last.
echo
echo "______________________________________________________________"
echo "Create zip tarball:"
echo "Code signing (dylibs -> frameworks -> plugins -> bundle):"
/usr/bin/ditto -c -k --keepParent $BUNDLE "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip"
# 1. Sign all flat .dylib files in Frameworks/
echo "-- Signing dylibs in Frameworks/..."
find "$BUNDLE/Contents/Frameworks" -name "*.dylib" | while read lib; do
echo " $(basename $lib)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
### notarize zip tarball ###############################################
echo -e "\033[1;31mWould you like to upload for Notarize packages "${APPNAME}"-"$VERSION"-"r$HEAD-arm64.zip", n/Y?.\033[m"
# 2. Sign .framework bundles
echo "-- Signing .framework bundles..."
find "$BUNDLE/Contents/Frameworks" -maxdepth 1 -name "*.framework" | while read fw; do
echo " $(basename $fw)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$fw"
done
# 3. Sign plugins
echo "-- Signing plugins..."
find "$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so" \) | while read lib; do
echo " $(basename $lib)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
# 4. Sign any dylibs in MacOS/
echo "-- Signing dylibs in MacOS/..."
find "$BUNDLE/Contents/MacOS" -name "*.dylib" | while read lib; do
echo " $(basename $lib)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
# 5. Sign the main executable explicitly
echo "-- Signing main executable..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
"$BUNDLE/Contents/MacOS/$APPNAME"
# 6. Sign the bundle itself last
echo "-- Signing bundle..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$BUNDLE"
# 7. Verify
echo
echo "Verifying bundle signature..."
codesign --verify --deep --strict --verbose=2 "$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: bundle signature verification failed, aborting."
exit 1
fi
spctl -a -vv "$BUNDLE"
echo "Bundle signature OK."
### Notarize the .app (via temporary ZIP) ###########################
echo
echo "______________________________________________________________"
echo "Create temporary ZIP for notarization:"
NOTARIZE_ZIP="/tmp/${APPNAME}-$VERSION-r$HEAD-arm64-notarize.zip"
/usr/bin/ditto -c -k --keepParent "$BUNDLE" "$NOTARIZE_ZIP"
echo -e "\033[1;31mWould you like to notarize the .app \"${APPNAME}-${VERSION}-r${HEAD}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
echo
echo "______________________________________________________________"
echo "Notarize zip tarball:"
xcrun notarytool submit build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip --keychain-profile "org.qelectrotech" --wait
echo
echo "______________________________________________________________"
echo "Notarizing .app:"
xcrun notarytool submit "$NOTARIZE_ZIP" --keychain-profile "org.qelectrotech" --wait
if [ $? -ne 0 ]; then
echo "ERROR: notarization failed. Check the log with:"
echo " xcrun notarytool log <submission-id> --keychain-profile org.qelectrotech"
rm -f "$NOTARIZE_ZIP"
exit 1
fi
else
echo -e "\033[1;33mExit.\033[m"
echo -e "\033[1;33mExit.\033[m"
fi
### The end, process is done ##########################################
echo "Cleaning up temporary notarization ZIP..."
rm -f "$NOTARIZE_ZIP"
echo
echo "______________________________________________________________"
echo "The process of creating deployable application zip is done."
echo The disque image is in the folder \'build-aux/mac-osx\'.
# Affiche les mise à jour depuis l'ancienne revision
#if [ ! $(($HEAD - $revAv)) -eq 0 ] ; then
# echo
# echo "There are new updates. This numero of revision is $HEAD."
# svn log -l $(($HEAD - $revAv))
#else
# echo
# echo "There are not new updates. This numero of revision is $HEAD."
# fi
# echo
### Staple the .app #################################################
# La version en local n'est pas conforme à la dernière version svn
# svnversion | grep -q '[MS:]' ; if [ $? -eq 0 ] ; then
# echo Please note that the latest \local version is $(svnversion).
# echo This is not the same version as the deposit.
# echo You can use \'svn diff\' to see the differences.
# echo And use \'svn revert \<fichier\>\' to delete the difference.
# echo To go back, you can use svn update -r 360
# echo to go to revision number 360.
# echo
#fi
# Clean up disk folder
echo 'Cleaning up... '
rm "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip"
# staple the app
echo -e "\033[1;31mWould you like to staple the app MacOS packages "${APPNAME}"-"$VERSION"-"r$HEAD", n/Y?.\033[m"
echo -e "\033[1;31mWould you like to staple the .app \"${APPNAME}-${VERSION}-r${HEAD}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
xcrun stapler staple -v $BUNDLE
xcrun stapler staple -v "$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: stapling .app failed."
exit 1
fi
echo "Verifying staple on .app..."
xcrun stapler validate -v "$BUNDLE"
spctl -a -vv "$BUNDLE"
echo ".app stapled OK."
else
echo -e "\033[1;33mExit.\033[m"
echo -e "\033[1;33mExit.\033[m"
fi
### Create staging folder with Applications symlink #################
# The staging folder contains the .app and a symlink to /Applications
# so the user can drag-and-drop to install directly from the DMG.
echo
echo "______________________________________________________________"
echo "Re Create zip tarball:"
echo "Preparing DMG staging folder:"
/usr/bin/ditto -c -k --sequesterRsrc --keepParent $BUNDLE "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip"
rm -rf "$STAGING"
mkdir -p "$STAGING"
cp -R "$BUNDLE" "$STAGING/"
ln -s /Applications "$STAGING/Applications"
echo "Staging folder ready: $STAGING"
### Create writable DMG (UDRW) ######################################
# We use a writable DMG first so we can re-sign the .app inside
# after hdiutil copies it (hdiutil can invalidate Sealed Resources
# during the copy, so we must re-sign inside the mounted volume).
# Clean up disk folder
echo 'Cleaning up... '
rm -rf $BUNDLE
echo
echo "______________________________________________________________"
echo "Create writable DMG (UDRW) and re-sign .app inside:"
rm -f "$RW_DMG"
hdiutil create \
-volname "QElectroTech $VERSION" \
-srcfolder "$STAGING" \
-ov \
-format UDRW \
-fs HFS+ \
"$RW_DMG"
#rsync to TF DMG builds
echo -e "\033[1;31mWould you like to upload MacOS packages "${APPNAME}"-"$VERSION"-"r$HEAD-arm64.zip", n/Y?.\033[m"
if [ $? -ne 0 ]; then
echo "ERROR: hdiutil failed to create writable DMG."
rm -rf "$STAGING"
exit 1
fi
# Mount the writable DMG
rm -rf "$MOUNT_POINT"
mkdir -p "$MOUNT_POINT"
hdiutil attach "$RW_DMG" -mountpoint "$MOUNT_POINT" -nobrowse -noverify
if [ $? -ne 0 ]; then
echo "ERROR: failed to mount writable DMG."
rm -f "$RW_DMG"
rm -rf "$STAGING"
exit 1
fi
# Re-sign all binaries inside the mounted DMG
echo "-- Re-signing dylibs inside DMG..."
find "$MOUNT_POINT/$BUNDLE/Contents/Frameworks" -name "*.dylib" | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
find "$MOUNT_POINT/$BUNDLE/Contents/Frameworks" -maxdepth 1 -name "*.framework" | while read fw; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$fw"
done
find "$MOUNT_POINT/$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so" \) | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
echo "-- Re-signing main executable inside DMG..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
"$MOUNT_POINT/$BUNDLE/Contents/MacOS/$APPNAME"
echo "-- Re-signing bundle inside DMG..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
"$MOUNT_POINT/$BUNDLE"
# Verify signature inside the mounted DMG
echo "Verifying bundle signature inside DMG..."
codesign --verify --deep --strict --verbose=2 "$MOUNT_POINT/$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: bundle signature invalid inside DMG, aborting."
hdiutil detach "$MOUNT_POINT"
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
exit 1
fi
echo "Bundle signature inside DMG OK."
# Detach the writable DMG
hdiutil detach "$MOUNT_POINT"
### Convert UDRW to final compressed UDZO ###########################
echo
echo "______________________________________________________________"
echo "Convert to final compressed DMG (UDZO):"
mkdir -p "build-aux/mac-osx"
rm -f "$DMG_PATH"
hdiutil convert "$RW_DMG" \
-format UDZO \
-o "$DMG_PATH"
if [ $? -ne 0 ]; then
echo "ERROR: hdiutil convert failed."
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
exit 1
fi
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
### Sign the final DMG ##############################################
echo "Signing final DMG..."
codesign --sign "$IDENTITY" --timestamp "$DMG_PATH"
### Notarize and staple the final DMG ###############################
echo -e "\033[1;31mWould you like to notarize the DMG \"${DMG_NAME}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
cp -Rf "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip" /Users/laurent/MAC_OS_X/
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w --progress --exclude='.DS_Store' /Users/laurent/MAC_OS_X/ server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
if [ $? != 0 ]; then
{
echo "RSYNC ERROR: problem syncing ${APPNAME}-$VERSION-r$HEAD-arm64.zip"
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w --progress --exclude='.DS_Store' /Users/laurent/MAC_OS_X/ server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
echo
echo "______________________________________________________________"
echo "Notarizing DMG:"
xcrun notarytool submit "$DMG_PATH" --keychain-profile "org.qelectrotech" --wait
if [ $? -ne 0 ]; then
echo "ERROR: DMG notarization failed. Check the log with:"
echo " xcrun notarytool log <submission-id> --keychain-profile org.qelectrotech"
exit 1
fi
} fi
echo "Stapling DMG..."
xcrun stapler staple "$DMG_PATH"
if [ $? -ne 0 ]; then
echo "ERROR: stapling DMG failed."
exit 1
fi
echo "DMG notarized and stapled OK."
echo "Verifying final DMG..."
spctl -a -vv "$DMG_PATH"
else
echo -e "\033[1;33mExit.\033[m"
echo -e "\033[1;33mExit.\033[m"
fi
### Clean up bundle #################################################
echo "Cleaning up bundle..."
rm -rf "$BUNDLE"
### The end #########################################################
echo
echo "______________________________________________________________"
echo "The process is done."
echo "DMG is in the folder 'build-aux/mac-osx'."
### Upload via rsync ################################################
echo -e "\033[1;31mWould you like to upload MacOS package \"${DMG_NAME}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
cp -Rf "$DMG_PATH" /Users/laurent/MAC_OS_X/
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w \
--progress --exclude='.DS_Store' \
/Users/laurent/MAC_OS_X/ \
server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
if [ $? != 0 ]; then
echo "RSYNC ERROR: problem syncing ${DMG_NAME}, retrying..."
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w \
--progress --exclude='.DS_Store' \
/Users/laurent/MAC_OS_X/ \
server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
fi
else
echo -e "\033[1;33mExit.\033[m"
fi
+26 -5
View File
@@ -22,6 +22,7 @@
#include "qeticons.h"
#include "qetproject.h"
#include "titleblock/templatescollection.h"
#include <QApplication>
/*
Lorsque le flag ENABLE_PANEL_DND_CHECKS est defini, le panel d'elements
@@ -42,7 +43,7 @@ ElementsPanel::ElementsPanel(QWidget *parent) :
first_reload_(true)
{
// selection unique
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setColumnCount(1);
setExpandsOnDoubleClick(true);
setMouseTracking(true);
@@ -299,11 +300,14 @@ void ElementsPanel::reload()
}
/**
@brief ElementsPanel::slot_clicked
handle click on qtwi
@param qtwi item that was clickerd on
*/
* @brief ElementsPanel::slot_clicked
* handle click on qtwi
* @param qtwi item that was clickerd on
*/
void ElementsPanel::slot_clicked(QTreeWidgetItem *clickedItem, int) {
if (QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) {
return;
}
requestForItem(clickedItem);
}
@@ -553,3 +557,20 @@ void ElementsPanel::keyPressEvent(QKeyEvent *event)
QTreeView::keyPressEvent(event);
}
}
/**
* @brief ElementsPanel::selectedDiagrams
* @return A list of all currently selected diagrams in the panel.
*/
QList<Diagram *> ElementsPanel::selectedDiagrams() const
{
QList<Diagram *> diagrams;
foreach (QTreeWidgetItem *item, selectedItems()) {
if (item->type() == QET::Diagram) {
if (Diagram *diagram = valueForItem<Diagram *>(item)) {
diagrams.append(diagram);
}
}
}
return diagrams;
}
+1
View File
@@ -49,6 +49,7 @@ class ElementsPanel : public GenericPanel {
// methods used to get what is represented by a particular visual item
QString dirPathForItem(QTreeWidgetItem *);
QString filePathForItem(QTreeWidgetItem *);
QList<Diagram *> selectedDiagrams() const;
signals:
void requestForProject(QETProject *);
+181 -122
View File
@@ -25,6 +25,7 @@
#include "qetproject.h"
#include "titleblock/templatedeleter.h"
#include <QFileInfo>
#include <QMessageBox>
/*
When the ENABLE_PANEL_WIDGET_DND_CHECKS flag is set, the panel
@@ -242,85 +243,134 @@ void ElementsPanelWidget::newDiagram()
}
/**
Emet le signal requestForDiagramDeletion avec le schema selectionne
*/
* Emet le signal requestForDiagramsDeletion avec les schemas selectionnes
*/
void ElementsPanelWidget::deleteDiagram()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramDeletion(selected_diagram));
elements_panel->reload();
QList<Diagram *> diagrams_to_delete = elements_panel->selectedDiagrams();
if (diagrams_to_delete.isEmpty()) return;
emit(requestForDiagramsDeletion(diagrams_to_delete));
elements_panel->reload();
}
/**
* Emits the requestForDiagramMoveUpTop signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramUpTop() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveUpTop(diagrams_to_move);
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
/**
Emet le signal requestForDiagramMoveUpTop avec le schema selectionne
+*/
void ElementsPanelWidget::moveDiagramUpTop()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveUpTop(selected_diagram));
}
}
* Emits the requestForDiagramMoveUp signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramUp() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveUp(diagrams_to_move);
/**
Emet le signal requestForDiagramMoveUp avec le schema selectionne
*/
void ElementsPanelWidget::moveDiagramUp()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveUp(selected_diagram));
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
/**
Emet le signal requestForDiagramMoveDown avec le schema selectionne
*/
void ElementsPanelWidget::moveDiagramDown()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveDown(selected_diagram));
* Emits the requestForDiagramMoveDown signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramDown() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveDown(diagrams_to_move);
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
/**
Emet le signal requestForDiagramMoveUpx10 avec le schema selectionne
*/
void ElementsPanelWidget::moveDiagramUpx10()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveUpx10(selected_diagram));
* Emits the requestForDiagramMoveUpx10 signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramUpx10() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveUpx10(diagrams_to_move);
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
/**
Emet le signal requestForDiagramMoveUpx100 avec le schema selectionne
*/
void ElementsPanelWidget::moveDiagramUpx100()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveUpx100(selected_diagram));
* Emits the requestForDiagramMoveUpx100 signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramUpx100() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveUpx100(diagrams_to_move);
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
/**
Emet le signal requestForDiagramMoveDownx10 avec le schema selectionne
*/
void ElementsPanelWidget::moveDiagramDownx10()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveDownx10(selected_diagram));
* Emits the requestForDiagramMoveDownx10 signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramDownx10() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveDownx10(diagrams_to_move);
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
/**
Emet le signal requestForDiagramMoveDownx100 avec le schema selectionne
*/
void ElementsPanelWidget::moveDiagramDownx100()
{
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramMoveDownx100(selected_diagram));
* Emits the requestForDiagramMoveDownx100 signal with all selected diagrams.
*/
void ElementsPanelWidget::moveDiagramDownx100() {
QList<Diagram *> diagrams_to_move = elements_panel->selectedDiagrams();
if (diagrams_to_move.isEmpty()) return;
// Emit the entire list at once
emit requestForDiagramMoveDownx100(diagrams_to_move);
// Clear messy tree selection caused by moving items, then restore clean selection
elements_panel->clearSelection();
for (Diagram *d : diagrams_to_move) {
if (auto item = elements_panel->getItemForDiagram(d)) item->setSelected(true);
}
}
@@ -378,21 +428,35 @@ void ElementsPanelWidget::updateButtons()
bool is_writable = !(elements_panel -> selectedProject() -> isReadOnly());
prj_add_diagram -> setEnabled(is_writable);
} else if (current_type == QET::Diagram) {
Diagram *selected_diagram = elements_panel -> selectedDiagram();
QETProject *selected_diagram_project = selected_diagram -> project();
// Fetch ALL selected diagrams instead of just one
QList<Diagram *> selected_diagrams = elements_panel -> selectedDiagrams();
bool is_writable = !(selected_diagram_project -> isReadOnly());
int project_diagrams_count = selected_diagram_project -> diagrams().count();
int diagram_position = selected_diagram_project -> diagrams().indexOf(selected_diagram);
if (!selected_diagrams.isEmpty()) {
QETProject *selected_diagram_project = selected_diagrams.first() -> project();
bool is_writable = !(selected_diagram_project -> isReadOnly());
int project_diagrams_count = selected_diagram_project -> diagrams().count();
prj_del_diagram -> setEnabled(is_writable);
prj_move_diagram_up -> setEnabled(is_writable && diagram_position > 0);
prj_move_diagram_down -> setEnabled(is_writable && diagram_position < project_diagrams_count - 1);
prj_move_diagram_top -> setEnabled(is_writable && diagram_position > 0);
prj_move_diagram_upx10 -> setEnabled(is_writable && diagram_position > 10);
prj_move_diagram_upx100 -> setEnabled(is_writable && diagram_position > 100);
prj_move_diagram_downx10 -> setEnabled(is_writable && diagram_position < project_diagrams_count - 10);
prj_move_diagram_downx100 -> setEnabled(is_writable && diagram_position < project_diagrams_count - 100);
// Find the highest (min) and lowest (max) index among the selection
int min_position = project_diagrams_count;
int max_position = -1;
for (Diagram *diagram : selected_diagrams) {
int pos = selected_diagram_project -> diagrams().indexOf(diagram);
if (pos < min_position) min_position = pos;
if (pos > max_position) max_position = pos;
}
prj_del_diagram -> setEnabled(is_writable);
prj_move_diagram_up -> setEnabled(is_writable && min_position > 0);
prj_move_diagram_down -> setEnabled(is_writable && max_position < project_diagrams_count - 1);
prj_move_diagram_top -> setEnabled(is_writable && min_position > 0);
// Adjusted to >= to allow exactly 10 or 100 steps if space permits
prj_move_diagram_upx10 -> setEnabled(is_writable && min_position > 10);
prj_move_diagram_upx100 -> setEnabled(is_writable && min_position > 100);
prj_move_diagram_downx10 -> setEnabled(is_writable && max_position < project_diagrams_count - 10);
prj_move_diagram_downx100 -> setEnabled(is_writable && max_position < project_diagrams_count - 100);
}
} else if (current_type == QET::TitleBlockTemplatesCollection) {
TitleBlockTemplateLocation location = elements_panel -> templateLocationForItem(current_item);
tbt_add -> setEnabled(!location.isReadOnly());
@@ -475,62 +539,57 @@ void ElementsPanelWidget::filterEdited(const QString &next_text) {
}
/**
Treat key press event inside elements panel widget
*/
void ElementsPanelWidget::keyPressEvent (QKeyEvent *e) {
switch(e -> key()) {
case Qt::Key_Delete: //delete diagram through elements panel widget
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
emit(requestForDiagramDeletion(selected_diagram));
}
break;
case Qt::Key_F3:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveUp(selected_diagram));
}
break;
case Qt::Key_F4:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveDown(selected_diagram));
}
break;
case Qt::Key_F5:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveUpTop(selected_diagram));
}
break;
case Qt::Key_F6:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveDownx10(selected_diagram));
}
break;
case Qt::Key_F7:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveDownx100(selected_diagram));
}
break;
case Qt::Key_F8:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveUpx10(selected_diagram));
}
break;
case Qt::Key_F9:
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
elements_panel->setSelectedItem(elements_panel->getItemForDiagram(selected_diagram));
emit(requestForDiagramMoveUpx100(selected_diagram));
}
break;
}
return;
* Treat key press event inside elements panel widget
*/
/**
* Treat key press event inside elements panel widget.
* Respects the enabled/disabled state of the corresponding QActions.
*/
void ElementsPanelWidget::keyPressEvent(QKeyEvent *e) {
switch(e->key()) {
case Qt::Key_Delete:
if (prj_del_diagram && prj_del_diagram->isEnabled()) {
deleteDiagram();
}
break;
case Qt::Key_F3:
if (prj_move_diagram_up && prj_move_diagram_up->isEnabled()) {
moveDiagramUp();
}
break;
case Qt::Key_F4:
if (prj_move_diagram_down && prj_move_diagram_down->isEnabled()) {
moveDiagramDown();
}
break;
case Qt::Key_F5:
if (prj_move_diagram_top && prj_move_diagram_top->isEnabled()) {
moveDiagramUpTop();
}
break;
case Qt::Key_F6:
if (prj_move_diagram_downx10 && prj_move_diagram_downx10->isEnabled()) {
moveDiagramDownx10();
}
break;
case Qt::Key_F7:
if (prj_move_diagram_downx100 && prj_move_diagram_downx100->isEnabled()) {
moveDiagramDownx100();
}
break;
case Qt::Key_F8:
if (prj_move_diagram_upx10 && prj_move_diagram_upx10->isEnabled()) {
moveDiagramUpx10();
}
break;
case Qt::Key_F9:
if (prj_move_diagram_upx100 && prj_move_diagram_upx100->isEnabled()) {
moveDiagramUpx100();
}
break;
default:
// Pass unhandled key events to the base class
QWidget::keyPressEvent(e);
break;
}
}
+8 -7
View File
@@ -69,13 +69,14 @@ class ElementsPanelWidget : public QWidget {
void requestForProjectPropertiesEdition(QETProject *);
void requestForDiagramPropertiesEdition(Diagram *);
void requestForDiagramDeletion(Diagram *);
void requestForDiagramMoveUp(Diagram *);
void requestForDiagramMoveDown(Diagram *);
void requestForDiagramMoveUpTop(Diagram *);
void requestForDiagramMoveUpx10(Diagram *);
void requestForDiagramMoveUpx100(Diagram *);
void requestForDiagramMoveDownx10(Diagram *);
void requestForDiagramMoveDownx100(Diagram *);
void requestForDiagramsDeletion(const QList<Diagram *> &diagrams);
void requestForDiagramMoveUp(const QList<Diagram *> &diagrams);
void requestForDiagramMoveDown(const QList<Diagram *> &diagrams);
void requestForDiagramMoveUpTop(const QList<Diagram *> &diagrams);
void requestForDiagramMoveUpx10(const QList<Diagram *> &diagrams);
void requestForDiagramMoveUpx100(const QList<Diagram *> &diagrams);
void requestForDiagramMoveDownx10(const QList<Diagram *> &diagrams);
void requestForDiagramMoveDownx100(const QList<Diagram *> &diagrams);
public slots:
void openDirectoryForSelectedItem();
+74 -21
View File
@@ -364,11 +364,12 @@ QETResult ProjectView::noProjectResult() const
}
/**
@brief ProjectView::removeDiagram
Remove a diagram (folio) of the project
@param diagram_view : diagram view to remove
*/
void ProjectView::removeDiagram(DiagramView *diagram_view)
* @brief ProjectView::removeDiagram
* Remove a diagram (folio) of the project
* @param diagram_view : diagram view to remove
* @param silent : if true, bypasses the confirmation message box
*/
void ProjectView::removeDiagram(DiagramView *diagram_view, bool silent)
{
if (!diagram_view)
return;
@@ -377,17 +378,18 @@ void ProjectView::removeDiagram(DiagramView *diagram_view)
if (!m_diagram_ids.values().contains(diagram_view))
return;
//Ask confirmation to user.
int answer = QET::QetMessageBox::question(
this,
tr("Supprimer le folio ?", "message box title"),
tr("Êtes-vous sûr de vouloir supprimer ce folio du projet ? Ce changement est irréversible.", "message box content"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::No
);
if (answer != QMessageBox::Yes) {
return;
if (!silent) {
//Ask confirmation to user.
int answer = QET::QetMessageBox::question(
this,
tr("Supprimer le folio ?", "message box title"),
tr("Êtes-vous sûr de vouloir supprimer ce folio du projet ? Ce changement est irréversible.", "message box content"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::No
);
if (answer != QMessageBox::Yes) {
return;
}
}
//Remove the diagram view of the tabs widget
@@ -405,14 +407,15 @@ void ProjectView::removeDiagram(DiagramView *diagram_view)
}
/**
Enleve un schema du ProjectView
@param diagram Schema a enlever
*/
void ProjectView::removeDiagram(Diagram *diagram) {
* Enleve un schema du ProjectView
* @param diagram Schema a enlever
* @param silent Si vrai, supprime sans demander confirmation
*/
void ProjectView::removeDiagram(Diagram *diagram, bool silent) {
if (!diagram) return;
if (DiagramView *diagram_view = findDiagram(diagram)) {
removeDiagram(diagram_view);
removeDiagram(diagram_view, silent);
}
}
@@ -557,6 +560,56 @@ void ProjectView::moveDiagramUpx10(Diagram *diagram) {
moveDiagramUpx10(findDiagram(diagram));
}
/**
* @brief ProjectView::moveDiagramUpx100
* Moves the diagram_view up / left x100
* @param diagram_view View to move
*/
void ProjectView::moveDiagramUpx100(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (!diagram_view_position) {
// The diagram is the first of the project
return;
}
m_tab->tabBar()->moveTab(diagram_view_position, diagram_view_position - 100);
}
/**
* @brief ProjectView::moveDiagramUpx100
* Moves the diagram up / left x100
* @param diagram Diagram to move
*/
void ProjectView::moveDiagramUpx100(Diagram *diagram) {
moveDiagramUpx100(findDiagram(diagram));
}
/**
* @brief ProjectView::moveDiagramDownx100
* Moves the diagram_view down / right x100
* @param diagram_view View to move
*/
void ProjectView::moveDiagramDownx100(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (diagram_view_position + 1 == m_diagram_ids.count()) {
// The diagram is the last of the project
return;
}
m_tab->tabBar()->moveTab(diagram_view_position, diagram_view_position + 100);
}
/**
* @brief ProjectView::moveDiagramDownx100
* Moves the diagram down / right x100
* @param diagram Diagram to move
*/
void ProjectView::moveDiagramDownx100(Diagram *diagram) {
moveDiagramDownx100(findDiagram(diagram));
}
/**
Deplace le schema diagram_view vers le bas / la droite x10
*/
+6 -2
View File
@@ -104,8 +104,8 @@ class ProjectView : public QWidget
void changeLastTab();
public slots:
void removeDiagram(DiagramView *);
void removeDiagram(Diagram *);
void removeDiagram(DiagramView *diagram_view, bool silent = false);
void removeDiagram(Diagram *diagram, bool silent = false);
void showDiagram(DiagramView *);
void showDiagram(Diagram *);
void editProjectProperties();
@@ -122,6 +122,10 @@ class ProjectView : public QWidget
void moveDiagramUpx10(Diagram *);
void moveDiagramDownx10(DiagramView *);
void moveDiagramDownx10(Diagram *);
void moveDiagramUpx100(DiagramView *);
void moveDiagramUpx100(Diagram *);
void moveDiagramDownx100(DiagramView *);
void moveDiagramDownx100(Diagram *);
void exportProject();
QETResult save();
QETResult saveAs();
+153 -95
View File
@@ -16,7 +16,7 @@
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qetdiagrameditor.h"
#include <QCoreApplication>
#include "ElementsCollection/elementscollectionwidget.h"
#include "QWidgetAnimation/qwidgetanimation.h"
#include "autoNum/ui/autonumberingdockwidget.h"
@@ -47,7 +47,7 @@
#include "TerminalStrip/ui/addterminalstripitemdialog.h"
#include "wiringlistexport.h"
#include "ui/terminalnumberingdialog.h"
#include <QDebug>
#ifdef BUILD_WITHOUT_KF5
#else
# include <KAutoSaveFile>
@@ -176,12 +176,14 @@ void QETDiagramEditor::setUpElementsPanel()
connect(pa, SIGNAL(requestForProjectPropertiesEdition (QETProject *)), this, SLOT(editProjectProperties(QETProject *)));
connect(pa, SIGNAL(requestForNewDiagram (QETProject *)), this, SLOT(addDiagramToProject(QETProject *)));
connect(pa, SIGNAL(requestForDiagramPropertiesEdition (Diagram *)), this, SLOT(editDiagramProperties(Diagram *)));
connect(pa, SIGNAL(requestForDiagramDeletion (Diagram *)), this, SLOT(removeDiagram(Diagram *)));
connect(pa, SIGNAL(requestForDiagramMoveUp (Diagram *)), this, SLOT(moveDiagramUp(Diagram *)));
connect(pa, SIGNAL(requestForDiagramMoveDown (Diagram *)), this, SLOT(moveDiagramDown(Diagram *)));
connect(pa, SIGNAL(requestForDiagramMoveUpTop (Diagram *)), this, SLOT(moveDiagramUpTop(Diagram *)));
connect(pa, SIGNAL(requestForDiagramMoveUpx10 (Diagram *)), this, SLOT(moveDiagramUpx10(Diagram *)));
connect(pa, SIGNAL(requestForDiagramMoveDownx10 (Diagram *)), this, SLOT(moveDiagramDownx10(Diagram *)));
connect(pa, SIGNAL(requestForDiagramsDeletion (const QList<Diagram *> &)), this, SLOT(removeDiagrams(const QList<Diagram *> &)));
connect(pa, SIGNAL(requestForDiagramMoveUp (const QList<Diagram *> &)), this, SLOT(moveDiagramUp(const QList<Diagram *>&)));
connect(pa, SIGNAL(requestForDiagramMoveDown (const QList<Diagram *> &)), this, SLOT(moveDiagramDown(const QList<Diagram *>&)));
connect(pa, SIGNAL(requestForDiagramMoveUpTop (const QList<Diagram *> &)), this, SLOT(moveDiagramUpTop(const QList<Diagram *>&)));
connect(pa, SIGNAL(requestForDiagramMoveUpx10 (const QList<Diagram *> &)), this, SLOT(moveDiagramUpx10(const QList<Diagram *>&)));
connect(pa, SIGNAL(requestForDiagramMoveDownx10 (const QList<Diagram *> &)), this, SLOT(moveDiagramDownx10(const QList<Diagram *>&)));
connect(pa, SIGNAL(requestForDiagramMoveUpx100 (const QList<Diagram *> &)), this, SLOT(moveDiagramUpx100(const QList<Diagram *>&)));
connect(pa, SIGNAL(requestForDiagramMoveDownx100 (const QList<Diagram *> &)), this, SLOT(moveDiagramDownx100(const QList<Diagram *>&)));
}
/**
@@ -2183,126 +2185,182 @@ void QETDiagramEditor::addDiagramToProject(QETProject *project)
project_view->project()->addNewDiagram();
}
}
/**
* @brief QETDiagramEditor::removeDiagram
* Wrapper für einzelne Diagramme, um Abwärtskompatibilität zu erhalten.
*/
void QETDiagramEditor::removeDiagram(Diagram *diagram)
{
if (!diagram) return;
QList<Diagram *> list;
list << diagram;
removeDiagrams(list);
}
/**
* @brief QETDiagramEditor::removeDiagrams
* Deletes a list of folios with a single query.
*/
void QETDiagramEditor::removeDiagrams(const QList<Diagram *> &diagrams)
{
if (diagrams.isEmpty()) return;
if (diagrams.count() == 1) {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, tr("Supprimer le folio"),
tr("Êtes-vous sûr de vouloir supprimer ce folio ?"),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::No) return;
} else {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, tr("Supprimer les folios"),
tr("Êtes-vous sûr de vouloir supprimer les %1 folios sélectionnés ?").arg(diagrams.count()),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::No) return;
}
ProjectView *project_view = nullptr;
if (QETProject *diagram_project = diagrams.first()->project()) {
project_view = findProject(diagram_project);
}
if (project_view) project_view->setUpdatesEnabled(false);
if (pa) pa->setUpdatesEnabled(false);
foreach (Diagram *diagram, diagrams) {
removeDiagramSilent(diagram);
}
if (pa) pa->setUpdatesEnabled(true);
if (project_view) project_view->setUpdatesEnabled(true);
emit syncElementsPanel();
}
/**
Supprime un schema de son projet
@param diagram Schema a supprimer
*/
void QETDiagramEditor::removeDiagram(Diagram *diagram)
void QETDiagramEditor::removeDiagramSilent(Diagram *diagram)
{
if (!diagram) return;
// recupere le projet contenant le schema
if (QETProject *diagram_project = diagram -> project()) {
// recupere la vue sur ce projet
if (ProjectView *project_view = findProject(diagram_project)) {
// affiche le schema en question
project_view -> showDiagram(diagram);
// supprime le schema
project_view -> removeDiagram(diagram);
project_view -> removeDiagram(diagram, true);
}
}
}
void QETDiagramEditor::moveDiagramUp(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up
for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUp(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
/**
Change l'ordre des schemas d'un projet, en decalant le schema vers le haut /
la gauche
@param diagram Schema a decaler vers le haut / la gauche
*/
void QETDiagramEditor::moveDiagramUp(Diagram *diagram)
{
if (!diagram) return;
// recupere le projet contenant le schema
if (QETProject *diagram_project = diagram -> project()) {
if (diagram_project -> isReadOnly()) return;
// recupere la vue sur ce projet
if (ProjectView *project_view = findProject(diagram_project)) {
project_view -> moveDiagramUp(diagram);
void QETDiagramEditor::moveDiagramDown(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDown(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
/**
Change l'ordre des schemas d'un projet, en decalant le schema vers le bas /
la droite
@param diagram Schema a decaler vers le bas / la droite
*/
void QETDiagramEditor::moveDiagramDown(Diagram *diagram)
{
if (!diagram) return;
// recupere le projet contenant le schema
if (QETProject *diagram_project = diagram -> project()) {
if (diagram_project -> isReadOnly()) return;
// recupere la vue sur ce projet
if (ProjectView *project_view = findProject(diagram_project)) {
project_view -> moveDiagramDown(diagram);
void QETDiagramEditor::moveDiagramUpTop(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop to preserve relative order of the selected items when moving to top
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramUpTop(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
/**
Change l'ordre des schemas d'un projet, en decalant le schema vers le haut /
la gauche en position 0
@param diagram Schema a decaler vers le haut / la gauche en position 0
*/
void QETDiagramEditor::moveDiagramUpTop(Diagram *diagram)
{
if (!diagram) return;
// recupere le projet contenant le schema
if (QETProject *diagram_project = diagram -> project()) {
if (diagram_project -> isReadOnly()) return;
// recupere la vue sur ce projet
if (ProjectView *project_view = findProject(diagram_project)) {
project_view -> moveDiagramUpTop(diagram);
void QETDiagramEditor::moveDiagramUpx10(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up
for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUpx10(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
/**
Change l'ordre des schemas d'un projet, en decalant le schema vers le haut /
la gauche x10
@param diagram Schema a decaler vers le haut / la gauche x10
*/
void QETDiagramEditor::moveDiagramUpx10(Diagram *diagram)
{
if (!diagram) return;
// recupere le projet contenant le schema
if (QETProject *diagram_project = diagram -> project()) {
if (diagram_project -> isReadOnly()) return;
// recupere la vue sur ce projet
if (ProjectView *project_view = findProject(diagram_project)) {
project_view -> moveDiagramUpx10(diagram);
void QETDiagramEditor::moveDiagramDownx10(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDownx10(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
/**
Change l'ordre des schemas d'un projet, en decalant le schema vers le bas /
la droite x10
@param diagram Schema a decaler vers le bas / la droite x10
*/
void QETDiagramEditor::moveDiagramDownx10(Diagram *diagram)
{
if (!diagram) return;
void QETDiagramEditor::moveDiagramUpx100(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up
for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUpx100(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
// recupere le projet contenant le schema
if (QETProject *diagram_project = diagram -> project()) {
if (diagram_project -> isReadOnly()) return;
// recupere la vue sur ce projet
if (ProjectView *project_view = findProject(diagram_project)) {
project_view -> moveDiagramDownx10(diagram);
void QETDiagramEditor::moveDiagramDownx100(const QList<Diagram *> &diagrams) {
if (diagrams.isEmpty()) return;
QList<Diagram *> safeDiagrams = diagrams;
if (QETProject *diagram_project = safeDiagrams.first()->project()) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDownx100(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
}
}
}
+10 -5
View File
@@ -138,12 +138,15 @@ class QETDiagramEditor : public QETMainWindow
void editDiagramProperties(Diagram *);
void addDiagramToProject(QETProject *);
void removeDiagram(Diagram *);
void removeDiagrams(const QList<Diagram *> &diagrams);
void removeDiagramFromProject();
void moveDiagramUp(Diagram *);
void moveDiagramDown(Diagram *);
void moveDiagramUpTop(Diagram *);
void moveDiagramUpx10(Diagram *);
void moveDiagramDownx10(Diagram *);
void moveDiagramUp(const QList<Diagram *> &diagrams);
void moveDiagramDown(const QList<Diagram *> &diagrams);
void moveDiagramUpTop(const QList<Diagram *> &diagrams);
void moveDiagramUpx10(const QList<Diagram *> &diagrams);
void moveDiagramDownx10(const QList<Diagram *> &diagrams);
void moveDiagramUpx100(const QList<Diagram *> &diagrams);
void moveDiagramDownx100(const QList<Diagram *> &diagrams);
void reloadOldElementPanel();
void diagramWasAdded(DiagramView *);
void findElementInPanel(const ElementsLocation &);
@@ -222,6 +225,8 @@ class QETDiagramEditor : public QETMainWindow
QList <QAction *> m_zoom_action_toolBar; ///Only zoom action must displayed in the toolbar
void removeDiagramSilent(Diagram *diagram);
QMdiArea m_workspace;
QSignalMapper windowMapper;
QDir open_dialog_dir; /// Directory to use for file dialogs such as File > save