изменено: CMakeLists.txt
новый файл: cmake/Info.plist.in новый файл: default.nix новый файл: desktop.nix новый файл: packaging/linux/idef0-editor.desktop новый файл: packaging/linux/idef0.xml новый файл: packaging/windows/idef0-file-association.reg.in изменено: src/MainWindow.cpp изменено: src/MainWindow.h изменено: src/items/ArrowItem.cpp изменено: src/items/ArrowItem.h изменено: src/items/BlockItem.cpp изменено: src/items/BlockItem.h изменено: src/items/DiagramScene.cpp изменено: src/items/DiagramScene.h новый файл: src/plugins/Manual.md новый файл: src/plugins/PluginApi.h новый файл: src/plugins/PluginManager.cpp новый файл: src/plugins/PluginManager.h новый файл: src/plugins/color/ColorsPlugin.cpp новый файл: src/plugins/color/ColorsPlugin.h новый файл: src/plugins/color/translations/colors_en.ts новый файл: src/plugins/color/translations/colors_fr.ts новый файл: src/plugins/color/translations/colors_ru.ts новый файл: translations/README.txt новый файл: translations/idef0_en.ts новый файл: translations/idef0_fr.ts новый файл: translations/idef0_ru.ts
This commit is contained in:
parent
f6f0598ff2
commit
630c952382
28 changed files with 2720 additions and 90 deletions
|
|
@ -8,14 +8,21 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
|
|
||||||
qt_add_executable(idef0_editor
|
qt_add_library(idef0_core SHARED
|
||||||
src/main.cpp
|
|
||||||
src/MainWindow.h src/MainWindow.cpp
|
src/MainWindow.h src/MainWindow.cpp
|
||||||
src/items/DiagramScene.h src/items/DiagramScene.cpp
|
src/items/DiagramScene.h src/items/DiagramScene.cpp
|
||||||
src/items/BlockItem.h src/items/BlockItem.cpp
|
src/items/BlockItem.h src/items/BlockItem.cpp
|
||||||
src/items/ArrowItem.h src/items/ArrowItem.cpp
|
src/items/ArrowItem.h src/items/ArrowItem.cpp
|
||||||
src/items/JunctionItem.h src/items/JunctionItem.cpp
|
src/items/JunctionItem.h src/items/JunctionItem.cpp
|
||||||
src/items/HeaderFooterItem.h src/items/HeaderFooterItem.cpp
|
src/items/HeaderFooterItem.h src/items/HeaderFooterItem.cpp
|
||||||
|
src/plugins/PluginApi.h
|
||||||
|
src/plugins/PluginManager.h src/plugins/PluginManager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
option(BUILD_COLORS_PLUGIN "Build the Colors plugin" ON)
|
||||||
|
|
||||||
|
qt_add_executable(idef0_editor
|
||||||
|
src/main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
|
@ -36,14 +43,51 @@ endif()
|
||||||
|
|
||||||
target_compile_definitions(idef0_editor PRIVATE $<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG_OUTPUT>)
|
target_compile_definitions(idef0_editor PRIVATE $<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG_OUTPUT>)
|
||||||
|
|
||||||
|
target_include_directories(idef0_core PRIVATE src)
|
||||||
target_include_directories(idef0_editor PRIVATE src)
|
target_include_directories(idef0_editor PRIVATE src)
|
||||||
|
|
||||||
target_link_libraries(idef0_editor PRIVATE Qt6::Widgets)
|
target_link_libraries(idef0_core PRIVATE Qt6::Widgets)
|
||||||
|
target_link_libraries(idef0_editor PRIVATE Qt6::Widgets idef0_core)
|
||||||
|
set_target_properties(idef0_core PROPERTIES
|
||||||
|
WINDOWS_EXPORT_ALL_SYMBOLS ON
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
|
INSTALL_RPATH "\$ORIGIN"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_COLORS_PLUGIN)
|
||||||
|
add_library(colorsplugin MODULE
|
||||||
|
src/plugins/color/ColorsPlugin.h
|
||||||
|
src/plugins/color/ColorsPlugin.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(colorsplugin PRIVATE idef0_core Qt6::Widgets)
|
||||||
|
target_include_directories(colorsplugin PRIVATE src)
|
||||||
|
set_target_properties(colorsplugin PROPERTIES
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/color"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/color"
|
||||||
|
INSTALL_RPATH "\$ORIGIN/../../lib"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(idef0_editor PROPERTIES
|
||||||
|
INSTALL_RPATH "\$ORIGIN/../lib"
|
||||||
|
)
|
||||||
|
|
||||||
install(TARGETS idef0_editor
|
install(TARGETS idef0_editor
|
||||||
BUNDLE DESTINATION .
|
BUNDLE DESTINATION .
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION bin
|
||||||
)
|
)
|
||||||
|
install(TARGETS idef0_core
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
RUNTIME DESTINATION bin
|
||||||
|
ARCHIVE DESTINATION lib
|
||||||
|
)
|
||||||
|
if(BUILD_COLORS_PLUGIN)
|
||||||
|
install(TARGETS colorsplugin
|
||||||
|
LIBRARY DESTINATION plugins/color
|
||||||
|
RUNTIME DESTINATION plugins/color
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/linux/idef0-editor.desktop
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/linux/idef0-editor.desktop
|
||||||
|
|
|
||||||
66
cmake/Info.plist.in
Normal file
66
cmake/Info.plist.in
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>@MACOS_BUNDLE_ID@</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>@MACOS_BUNDLE_NAME@</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>11.0</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<key>UTExportedTypeDeclarations</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>com.gregorybednov.idef0</string>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>IDEF0 Diagram</string>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>idef0</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<string>application/x-idef0+json</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>IDEF0 Diagram</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.gregorybednov.idef0</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
74
default.nix
Normal file
74
default.nix
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {}
|
||||||
|
, buildColorsPlugin ? true
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (pkgs) lib stdenv cmake pkg-config;
|
||||||
|
qt = pkgs.qt6;
|
||||||
|
in
|
||||||
|
stdenv.mkDerivation rec {
|
||||||
|
pname = "idef0_editor";
|
||||||
|
version = "0.1.0";
|
||||||
|
|
||||||
|
src = lib.cleanSourceWith {
|
||||||
|
src = ./.;
|
||||||
|
filter = path: type:
|
||||||
|
(lib.cleanSourceFilter path type)
|
||||||
|
&& (builtins.baseNameOf path != "build");
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
cmake
|
||||||
|
pkg-config
|
||||||
|
qt.wrapQtAppsHook
|
||||||
|
qt.qttools
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
qt.qtbase
|
||||||
|
];
|
||||||
|
|
||||||
|
configurePhase = ''
|
||||||
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=$out \
|
||||||
|
-DBUILD_COLORS_PLUGIN=${if buildColorsPlugin then "ON" else "OFF"}
|
||||||
|
'';
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
cmake --build build
|
||||||
|
find . -path "*/translations/*.ts" -print0 | while IFS= read -r -d "" f; do
|
||||||
|
out_path=$(printf '%s\n' "$f" | sed 's/\.ts$/.qm/')
|
||||||
|
lrelease "$f" -qm "$out_path"
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
cmake --install build --prefix $out
|
||||||
|
# app translations
|
||||||
|
if [ -d translations ]; then
|
||||||
|
mkdir -p $out/share/idef0/translations
|
||||||
|
find translations -maxdepth 1 -type f -name '*.qm' -print0 | xargs -0 -r cp -t $out/share/idef0/translations
|
||||||
|
fi
|
||||||
|
# plugin translations
|
||||||
|
find src/plugins -path "*/translations/*.qm" -print0 | while IFS= read -r -d "" f; do
|
||||||
|
rel="''${f#src/plugins/}" # e.g., color/translations/colors_en.qm
|
||||||
|
plugdir="''${rel%%/translations/*}" # e.g., color
|
||||||
|
dest="$out/plugins/$plugdir/translations"
|
||||||
|
mkdir -p "$dest"
|
||||||
|
cp "$f" "$dest/"
|
||||||
|
done
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
qtWrapperArgs = [
|
||||||
|
"--set" "QT_LOGGING_RULES" "qt.qpa.wayland.textinput=false"
|
||||||
|
];
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "IDEF0 diagram editor built with Qt 6 Widgets";
|
||||||
|
license = licenses.lgpl3Plus;
|
||||||
|
mainProgram = "idef0_editor";
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
}
|
||||||
9
desktop.nix
Normal file
9
desktop.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
# Desktop-focused Nix entrypoint for NixOS distributions.
|
||||||
|
# Currently this reuses default.nix, which already installs:
|
||||||
|
# - binary
|
||||||
|
# - translations
|
||||||
|
# - .desktop launcher
|
||||||
|
# - MIME package (application/x-idef0+json for *.idef0)
|
||||||
|
import ./default.nix { inherit pkgs; }
|
||||||
9
packaging/linux/idef0-editor.desktop
Normal file
9
packaging/linux/idef0-editor.desktop
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=IDEF0 Editor
|
||||||
|
Comment=IDEF0 diagram editor
|
||||||
|
Exec=idef0_editor %f
|
||||||
|
Terminal=false
|
||||||
|
Categories=Office;Graphics;
|
||||||
|
MimeType=application/x-idef0+json;
|
||||||
|
StartupNotify=true
|
||||||
7
packaging/linux/idef0.xml
Normal file
7
packaging/linux/idef0.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="application/x-idef0+json">
|
||||||
|
<comment>IDEF0 diagram</comment>
|
||||||
|
<glob pattern="*.idef0"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
||||||
18
packaging/windows/idef0-file-association.reg.in
Normal file
18
packaging/windows/idef0-file-association.reg.in
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Classes\.idef0]
|
||||||
|
@="IDEF0Editor.Document"
|
||||||
|
"Content Type"="application/x-idef0+json"
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Classes\IDEF0Editor.Document]
|
||||||
|
@="IDEF0 Diagram"
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Classes\IDEF0Editor.Document\DefaultIcon]
|
||||||
|
@="@APP_EXE@,0"
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Classes\IDEF0Editor.Document\shell]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Classes\IDEF0Editor.Document\shell\open]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Classes\IDEF0Editor.Document\shell\open\command]
|
||||||
|
@="\"@APP_EXE@\" \"%1\""
|
||||||
|
|
@ -32,7 +32,6 @@
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QColorDialog>
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
|
|
@ -44,10 +43,13 @@
|
||||||
#include <QPageSize>
|
#include <QPageSize>
|
||||||
#include <QPageLayout>
|
#include <QPageLayout>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtGlobal>
|
||||||
#include "items/BlockItem.h"
|
#include "items/BlockItem.h"
|
||||||
|
#include "plugins/PluginManager.h"
|
||||||
|
|
||||||
static const char* kDiagramFileFilter = "IDEF0 Diagram (*.idef0);;JSON Diagram (*.json)";
|
static const char* kDiagramFileFilter = QT_TR_NOOP("IDEF0 Diagram (*.idef0);;JSON Diagram (*.json)");
|
||||||
static const char* kPdfFileFilter = "PDF (*.pdf)";
|
static const char* kPdfFileFilter = QT_TR_NOOP("PDF (*.pdf)");
|
||||||
|
|
||||||
static QPageSize::PageSizeId pageSizeFromMeta(const QVariantMap& meta) {
|
static QPageSize::PageSizeId pageSizeFromMeta(const QVariantMap& meta) {
|
||||||
const QString size = meta.value("pageSize", "A4").toString();
|
const QString size = meta.value("pageSize", "A4").toString();
|
||||||
|
|
@ -59,7 +61,8 @@ static QPageSize::PageSizeId pageSizeFromMeta(const QVariantMap& meta) {
|
||||||
|
|
||||||
static QPageLayout::Orientation pageOrientationFromMeta(const QVariantMap& meta) {
|
static QPageLayout::Orientation pageOrientationFromMeta(const QVariantMap& meta) {
|
||||||
const QString orient = meta.value("pageOrientation").toString();
|
const QString orient = meta.value("pageOrientation").toString();
|
||||||
if (orient == QObject::tr("Portrait") || orient == "Portrait") {
|
const QString portrait = QCoreApplication::translate("MainWindow", "Portrait");
|
||||||
|
if (orient == portrait || orient == "Portrait") {
|
||||||
return QPageLayout::Portrait;
|
return QPageLayout::Portrait;
|
||||||
}
|
}
|
||||||
return QPageLayout::Landscape;
|
return QPageLayout::Landscape;
|
||||||
|
|
@ -125,7 +128,9 @@ void MainWindow::setupActions() {
|
||||||
|
|
||||||
auto* editMenu = menuBar()->addMenu(tr("&Edit"));
|
auto* editMenu = menuBar()->addMenu(tr("&Edit"));
|
||||||
auto* actSwapNums = new QAction(tr("Swap numbers…"), this);
|
auto* actSwapNums = new QAction(tr("Swap numbers…"), this);
|
||||||
|
auto* actCallMech = new QAction(tr("Call Mechanism add"), this);
|
||||||
editMenu->addAction(actSwapNums);
|
editMenu->addAction(actSwapNums);
|
||||||
|
editMenu->addAction(actCallMech);
|
||||||
|
|
||||||
auto* viewMenu = menuBar()->addMenu(tr("&View"));
|
auto* viewMenu = menuBar()->addMenu(tr("&View"));
|
||||||
auto* actFit = new QAction(tr("Fit"), this);
|
auto* actFit = new QAction(tr("Fit"), this);
|
||||||
|
|
@ -134,6 +139,7 @@ void MainWindow::setupActions() {
|
||||||
m_view->fitInView(m_scene->itemsBoundingRect().adjusted(-50,-50,50,50), Qt::KeepAspectRatio);
|
m_view->fitInView(m_scene->itemsBoundingRect().adjusted(-50,-50,50,50), Qt::KeepAspectRatio);
|
||||||
});
|
});
|
||||||
viewMenu->addAction(actFit);
|
viewMenu->addAction(actFit);
|
||||||
|
auto* pluginsMenu = menuBar()->addMenu(tr("&Plugins"));
|
||||||
|
|
||||||
auto* actZoomIn = new QAction(tr("Scale +"), this);
|
auto* actZoomIn = new QAction(tr("Scale +"), this);
|
||||||
actZoomIn->setShortcut(QKeySequence::ZoomIn);
|
actZoomIn->setShortcut(QKeySequence::ZoomIn);
|
||||||
|
|
@ -149,12 +155,6 @@ void MainWindow::setupActions() {
|
||||||
});
|
});
|
||||||
viewMenu->addAction(actZoomOut);
|
viewMenu->addAction(actZoomOut);
|
||||||
|
|
||||||
m_actColorfulMode = new QAction(tr("Colorful mode"), this);
|
|
||||||
m_actColorfulMode->setCheckable(true);
|
|
||||||
m_actColorfulMode->setChecked(m_welcomeState.value("colorfulMode", false).toBool());
|
|
||||||
viewMenu->addSeparator();
|
|
||||||
viewMenu->addAction(m_actColorfulMode);
|
|
||||||
|
|
||||||
m_actDarkMode = new QAction(tr("Dark mode"), this);
|
m_actDarkMode = new QAction(tr("Dark mode"), this);
|
||||||
m_actDarkMode->setCheckable(true);
|
m_actDarkMode->setCheckable(true);
|
||||||
m_actDarkMode->setChecked(m_welcomeState.value("darkMode", false).toBool());
|
m_actDarkMode->setChecked(m_welcomeState.value("darkMode", false).toBool());
|
||||||
|
|
@ -165,67 +165,22 @@ void MainWindow::setupActions() {
|
||||||
m_actFollowSystemTheme->setChecked(m_welcomeState.value("followSystemTheme", false).toBool());
|
m_actFollowSystemTheme->setChecked(m_welcomeState.value("followSystemTheme", false).toBool());
|
||||||
viewMenu->addAction(m_actFollowSystemTheme);
|
viewMenu->addAction(m_actFollowSystemTheme);
|
||||||
|
|
||||||
m_actArrowColor = new QAction(tr("Arrow color..."), this);
|
|
||||||
m_actBorderColor = new QAction(tr("Block border color..."), this);
|
|
||||||
m_actFontColor = new QAction(tr("Block font color..."), this);
|
|
||||||
viewMenu->addAction(m_actArrowColor);
|
|
||||||
viewMenu->addAction(m_actBorderColor);
|
|
||||||
viewMenu->addAction(m_actFontColor);
|
|
||||||
|
|
||||||
auto ensureDefaultColor = [this](const char* key, const QColor& fallback) {
|
|
||||||
QColor c(m_welcomeState.value(key).toString());
|
|
||||||
if (!c.isValid()) m_welcomeState[key] = fallback.name();
|
|
||||||
};
|
|
||||||
ensureDefaultColor("arrowColor", QColor("#2b6ee6"));
|
|
||||||
ensureDefaultColor("blockBorderColor", QColor("#0f766e"));
|
|
||||||
ensureDefaultColor("blockFontColor", QColor("#991b1b"));
|
|
||||||
|
|
||||||
auto applyModes = [this](bool mark) {
|
auto applyModes = [this](bool mark) {
|
||||||
const bool colorful = m_actColorfulMode->isChecked();
|
|
||||||
const bool followSystem = m_actFollowSystemTheme->isChecked();
|
const bool followSystem = m_actFollowSystemTheme->isChecked();
|
||||||
const bool dark = m_actDarkMode->isChecked();
|
const bool dark = m_actDarkMode->isChecked();
|
||||||
m_welcomeState["colorfulMode"] = colorful;
|
|
||||||
m_welcomeState["followSystemTheme"] = followSystem;
|
m_welcomeState["followSystemTheme"] = followSystem;
|
||||||
m_welcomeState["darkMode"] = dark;
|
m_welcomeState["darkMode"] = dark;
|
||||||
m_welcomeState["resolvedDarkMode"] = effectiveDarkMode();
|
m_welcomeState["resolvedDarkMode"] = effectiveDarkMode();
|
||||||
|
|
||||||
m_actArrowColor->setEnabled(colorful);
|
|
||||||
m_actBorderColor->setEnabled(colorful);
|
|
||||||
m_actFontColor->setEnabled(colorful);
|
|
||||||
m_actDarkMode->setEnabled(!followSystem);
|
m_actDarkMode->setEnabled(!followSystem);
|
||||||
|
|
||||||
applyAppPalette(m_welcomeState.value("resolvedDarkMode").toBool());
|
applyAppPalette(m_welcomeState.value("resolvedDarkMode").toBool());
|
||||||
m_scene->applyMeta(m_welcomeState);
|
m_scene->applyMeta(m_welcomeState);
|
||||||
if (mark) markDirty(true);
|
if (mark) markDirty(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
connect(m_actColorfulMode, &QAction::toggled, this, [applyModes]{ applyModes(true); });
|
|
||||||
connect(m_actDarkMode, &QAction::toggled, this, [applyModes]{ applyModes(true); });
|
connect(m_actDarkMode, &QAction::toggled, this, [applyModes]{ applyModes(true); });
|
||||||
connect(m_actFollowSystemTheme, &QAction::toggled, this, [applyModes]{ applyModes(true); });
|
connect(m_actFollowSystemTheme, &QAction::toggled, this, [applyModes]{ applyModes(true); });
|
||||||
connect(qApp->styleHints(), &QStyleHints::colorSchemeChanged, this, [applyModes](Qt::ColorScheme){
|
connect(qApp->styleHints(), &QStyleHints::colorSchemeChanged, this, [applyModes](Qt::ColorScheme){
|
||||||
applyModes(false);
|
applyModes(false);
|
||||||
});
|
});
|
||||||
connect(m_actArrowColor, &QAction::triggered, this, [this, applyModes]{
|
|
||||||
const QColor cur(m_welcomeState.value("arrowColor").toString());
|
|
||||||
const QColor chosen = QColorDialog::getColor(cur.isValid() ? cur : QColor("#2b6ee6"), this, tr("Arrow color"));
|
|
||||||
if (!chosen.isValid()) return;
|
|
||||||
m_welcomeState["arrowColor"] = chosen.name();
|
|
||||||
applyModes(true);
|
|
||||||
});
|
|
||||||
connect(m_actBorderColor, &QAction::triggered, this, [this, applyModes]{
|
|
||||||
const QColor cur(m_welcomeState.value("blockBorderColor").toString());
|
|
||||||
const QColor chosen = QColorDialog::getColor(cur.isValid() ? cur : QColor("#0f766e"), this, tr("Block border color"));
|
|
||||||
if (!chosen.isValid()) return;
|
|
||||||
m_welcomeState["blockBorderColor"] = chosen.name();
|
|
||||||
applyModes(true);
|
|
||||||
});
|
|
||||||
connect(m_actFontColor, &QAction::triggered, this, [this, applyModes]{
|
|
||||||
const QColor cur(m_welcomeState.value("blockFontColor").toString());
|
|
||||||
const QColor chosen = QColorDialog::getColor(cur.isValid() ? cur : QColor("#991b1b"), this, tr("Block font color"));
|
|
||||||
if (!chosen.isValid()) return;
|
|
||||||
m_welcomeState["blockFontColor"] = chosen.name();
|
|
||||||
applyModes(true);
|
|
||||||
});
|
|
||||||
applyModes(false);
|
applyModes(false);
|
||||||
|
|
||||||
connect(actSwapNums, &QAction::triggered, this, [this]{
|
connect(actSwapNums, &QAction::triggered, this, [this]{
|
||||||
|
|
@ -327,6 +282,59 @@ void MainWindow::setupActions() {
|
||||||
if (dlg.exec() != QDialog::Accepted) return;
|
if (dlg.exec() != QDialog::Accepted) return;
|
||||||
exportPdf(scope->currentIndex() == 1, pageNumbers->isChecked());
|
exportPdf(scope->currentIndex() == 1, pageNumbers->isChecked());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(actCallMech, &QAction::triggered, this, [this]{
|
||||||
|
if (!m_scene) return;
|
||||||
|
const auto sel = m_scene->selectedItems();
|
||||||
|
if (sel.size() != 1) {
|
||||||
|
QMessageBox::information(this, tr("Call Mechanism"), tr("Select exactly one block to add a call mechanism."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto* target = qgraphicsitem_cast<BlockItem*>(sel.first());
|
||||||
|
if (!target) {
|
||||||
|
QMessageBox::information(this, tr("Call Mechanism"), tr("Select a block to add a call mechanism."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_scene->hasCallMechanism(target)) {
|
||||||
|
QMessageBox::information(this, tr("Call Mechanism"), tr("Call mechanism already exists for this block."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct Option { QString display; BlockItem* block = nullptr; };
|
||||||
|
QVector<Option> options;
|
||||||
|
for (QGraphicsItem* it : m_scene->items()) {
|
||||||
|
auto* b = qgraphicsitem_cast<BlockItem*>(it);
|
||||||
|
if (!b) continue;
|
||||||
|
const QString n = b->number();
|
||||||
|
if (n.isEmpty() || n.compare("A0", Qt::CaseInsensitive) == 0) continue;
|
||||||
|
if (b == target) continue;
|
||||||
|
Option opt{QStringLiteral("%1 — %2").arg(n, b->title()), b};
|
||||||
|
options.push_back(opt);
|
||||||
|
}
|
||||||
|
std::sort(options.begin(), options.end(), [](const Option& a, const Option& b){ return a.display.localeAwareCompare(b.display) < 0; });
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
QMessageBox::information(this, tr("Call Mechanism"), tr("No eligible target blocks found."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QStringList displayList;
|
||||||
|
for (const auto& opt : options) displayList << opt.display;
|
||||||
|
bool ok = false;
|
||||||
|
const QString choice = QInputDialog::getItem(this, tr("Call Mechanism"), tr("Choose target block:"), displayList, 0, false, &ok);
|
||||||
|
if (!ok || choice.isEmpty()) return;
|
||||||
|
BlockItem* refBlock = nullptr;
|
||||||
|
for (const auto& opt : options) {
|
||||||
|
if (opt.display == choice) { refBlock = opt.block; break; }
|
||||||
|
}
|
||||||
|
if (!refBlock) return;
|
||||||
|
const QString label = QStringLiteral("%1 %2").arg(refBlock->number(), refBlock->title());
|
||||||
|
if (!m_scene->startCallMechanism(target, refBlock, label)) {
|
||||||
|
QMessageBox::information(this, tr("Call Mechanism"), tr("Call mechanism already exists for this block or selection is invalid."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
statusBar()->showMessage(tr("Click bottom frame to place call mechanism arrow. Esc to cancel."), 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_pluginManager = new PluginManager(this, pluginsMenu, this);
|
||||||
|
m_pluginManager->loadPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString currencySymbolForLocale(const QLocale& loc) {
|
static QString currencySymbolForLocale(const QLocale& loc) {
|
||||||
|
|
@ -358,15 +366,15 @@ bool MainWindow::showWelcome() {
|
||||||
// General tab
|
// General tab
|
||||||
auto* general = new QWidget(dlg);
|
auto* general = new QWidget(dlg);
|
||||||
auto* genForm = new QFormLayout(general);
|
auto* genForm = new QFormLayout(general);
|
||||||
auto* author = new QLineEdit(general);
|
|
||||||
author->setPlaceholderText(tr("John Doe"));
|
|
||||||
author->setText(m_welcomeState.value("author").toString());
|
|
||||||
auto* title = new QLineEdit(general);
|
auto* title = new QLineEdit(general);
|
||||||
title->setPlaceholderText(tr("Main process"));
|
title->setPlaceholderText(tr("Main process"));
|
||||||
title->setText(m_welcomeState.value("title").toString());
|
title->setText(m_welcomeState.value("title").toString());
|
||||||
auto* organization = new QLineEdit(general);
|
auto* organization = new QLineEdit(general);
|
||||||
organization->setPlaceholderText(tr("Example.Org Inc."));
|
organization->setPlaceholderText(tr("Example.Org Inc."));
|
||||||
organization->setText(m_welcomeState.value("organization").toString());
|
organization->setText(m_welcomeState.value("organization").toString());
|
||||||
|
auto* author = new QLineEdit(general);
|
||||||
|
author->setPlaceholderText(tr("John Doe"));
|
||||||
|
author->setText(m_welcomeState.value("author").toString());
|
||||||
auto* initials = new QLineEdit(general);
|
auto* initials = new QLineEdit(general);
|
||||||
initials->setPlaceholderText(tr("JD"));
|
initials->setPlaceholderText(tr("JD"));
|
||||||
initials->setText(m_welcomeState.value("initials").toString());
|
initials->setText(m_welcomeState.value("initials").toString());
|
||||||
|
|
@ -378,6 +386,9 @@ bool MainWindow::showWelcome() {
|
||||||
genForm->addRow(tr("Organization:"), organization);
|
genForm->addRow(tr("Organization:"), organization);
|
||||||
genForm->addRow(tr("Author:"), author);
|
genForm->addRow(tr("Author:"), author);
|
||||||
genForm->addRow(tr("Author's initials:"), initials);
|
genForm->addRow(tr("Author's initials:"), initials);
|
||||||
|
setTabOrder(title, organization);
|
||||||
|
setTabOrder(organization, author);
|
||||||
|
setTabOrder(author, initials);
|
||||||
general->setLayout(genForm);
|
general->setLayout(genForm);
|
||||||
tabs->addTab(general, tr("General"));
|
tabs->addTab(general, tr("General"));
|
||||||
|
|
||||||
|
|
@ -557,21 +568,13 @@ bool MainWindow::loadDiagramFromPath(const QString& path) {
|
||||||
if (root.contains("meta")) {
|
if (root.contains("meta")) {
|
||||||
m_welcomeState = root.value("meta").toMap();
|
m_welcomeState = root.value("meta").toMap();
|
||||||
}
|
}
|
||||||
if (m_actColorfulMode && m_actDarkMode && m_actFollowSystemTheme) {
|
if (m_actDarkMode && m_actFollowSystemTheme) {
|
||||||
const QSignalBlocker b1(m_actColorfulMode);
|
|
||||||
const QSignalBlocker b2(m_actDarkMode);
|
const QSignalBlocker b2(m_actDarkMode);
|
||||||
const QSignalBlocker b3(m_actFollowSystemTheme);
|
const QSignalBlocker b3(m_actFollowSystemTheme);
|
||||||
m_actColorfulMode->setChecked(m_welcomeState.value("colorfulMode", false).toBool());
|
|
||||||
m_actDarkMode->setChecked(m_welcomeState.value("darkMode", false).toBool());
|
m_actDarkMode->setChecked(m_welcomeState.value("darkMode", false).toBool());
|
||||||
m_actFollowSystemTheme->setChecked(m_welcomeState.value("followSystemTheme", false).toBool());
|
m_actFollowSystemTheme->setChecked(m_welcomeState.value("followSystemTheme", false).toBool());
|
||||||
m_actDarkMode->setEnabled(!m_actFollowSystemTheme->isChecked());
|
m_actDarkMode->setEnabled(!m_actFollowSystemTheme->isChecked());
|
||||||
}
|
}
|
||||||
if (m_actArrowColor && m_actBorderColor && m_actFontColor) {
|
|
||||||
const bool colorful = m_welcomeState.value("colorfulMode", false).toBool();
|
|
||||||
m_actArrowColor->setEnabled(colorful);
|
|
||||||
m_actBorderColor->setEnabled(colorful);
|
|
||||||
m_actFontColor->setEnabled(colorful);
|
|
||||||
}
|
|
||||||
m_welcomeState["resolvedDarkMode"] = effectiveDarkMode();
|
m_welcomeState["resolvedDarkMode"] = effectiveDarkMode();
|
||||||
applyAppPalette(m_welcomeState.value("resolvedDarkMode").toBool());
|
applyAppPalette(m_welcomeState.value("resolvedDarkMode").toBool());
|
||||||
m_scene->applyMeta(m_welcomeState);
|
m_scene->applyMeta(m_welcomeState);
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@ class QGraphicsView;
|
||||||
class DiagramScene;
|
class DiagramScene;
|
||||||
class QDialog;
|
class QDialog;
|
||||||
class QAction;
|
class QAction;
|
||||||
|
class PluginManager;
|
||||||
|
|
||||||
class MainWindow final : public QMainWindow {
|
class MainWindow final : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(const QString& startupPath = QString(), QWidget* parent = nullptr);
|
explicit MainWindow(const QString& startupPath = QString(), QWidget* parent = nullptr);
|
||||||
bool openDiagramPath(const QString& path);
|
bool openDiagramPath(const QString& path);
|
||||||
|
DiagramScene* scene() const { return m_scene; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||||
|
|
@ -26,12 +28,9 @@ private:
|
||||||
QString m_currentFile;
|
QString m_currentFile;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
bool m_pinchHandled = false;
|
bool m_pinchHandled = false;
|
||||||
QAction* m_actColorfulMode = nullptr;
|
|
||||||
QAction* m_actFollowSystemTheme = nullptr;
|
QAction* m_actFollowSystemTheme = nullptr;
|
||||||
QAction* m_actDarkMode = nullptr;
|
QAction* m_actDarkMode = nullptr;
|
||||||
QAction* m_actArrowColor = nullptr;
|
PluginManager* m_pluginManager = nullptr;
|
||||||
QAction* m_actBorderColor = nullptr;
|
|
||||||
QAction* m_actFontColor = nullptr;
|
|
||||||
|
|
||||||
void setupUi();
|
void setupUi();
|
||||||
void setupActions();
|
void setupActions();
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include "DiagramScene.h"
|
#include "DiagramScene.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
@ -35,6 +36,8 @@ QColor ArrowItem::s_textColor = QColor(10, 10, 10);
|
||||||
ArrowItem::ArrowItem(QGraphicsItem* parent)
|
ArrowItem::ArrowItem(QGraphicsItem* parent)
|
||||||
: QGraphicsPathItem(parent)
|
: QGraphicsPathItem(parent)
|
||||||
{
|
{
|
||||||
|
static int s_nextId = 1;
|
||||||
|
m_internalId = s_nextId++;
|
||||||
QPen pen(s_lineColor);
|
QPen pen(s_lineColor);
|
||||||
pen.setWidthF(1.4);
|
pen.setWidthF(1.4);
|
||||||
pen.setCapStyle(Qt::RoundCap);
|
pen.setCapStyle(Qt::RoundCap);
|
||||||
|
|
@ -101,6 +104,18 @@ void ArrowItem::setLabelSource(ArrowItem* src) {
|
||||||
m_labelSource = src;
|
m_labelSource = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArrowItem::setCustomColor(const QColor& color) {
|
||||||
|
if (!color.isValid()) return;
|
||||||
|
m_customColor = color;
|
||||||
|
updatePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrowItem::clearCustomColor() {
|
||||||
|
if (!m_customColor) return;
|
||||||
|
m_customColor.reset();
|
||||||
|
updatePath();
|
||||||
|
}
|
||||||
|
|
||||||
ArrowItem* ArrowItem::labelSourceRoot() const {
|
ArrowItem* ArrowItem::labelSourceRoot() const {
|
||||||
const ArrowItem* cur = this;
|
const ArrowItem* cur = this;
|
||||||
QSet<const ArrowItem*> visited;
|
QSet<const ArrowItem*> visited;
|
||||||
|
|
@ -674,10 +689,13 @@ void ArrowItem::updateLabelItem(const QVector<QPointF>& pts) {
|
||||||
// Обновляем маршрут ортогонально, добавляя/схлопывая сегменты при необходимости.
|
// Обновляем маршрут ортогонально, добавляя/схлопывая сегменты при необходимости.
|
||||||
void ArrowItem::updatePath() {
|
void ArrowItem::updatePath() {
|
||||||
QPen themedPen = pen();
|
QPen themedPen = pen();
|
||||||
themedPen.setColor(s_lineColor);
|
themedPen.setColor(m_customColor.value_or(s_lineColor));
|
||||||
|
if (m_isCallMechanism) {
|
||||||
|
themedPen.setStyle(Qt::DashLine);
|
||||||
|
}
|
||||||
setPen(themedPen);
|
setPen(themedPen);
|
||||||
if (m_labelItem) {
|
if (m_labelItem) {
|
||||||
m_labelItem->setBrush(s_textColor);
|
m_labelItem->setBrush(m_customColor.value_or(s_textColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRectF oldSceneRect = mapRectToScene(boundingRect());
|
const QRectF oldSceneRect = mapRectToScene(boundingRect());
|
||||||
|
|
@ -912,7 +930,13 @@ void ArrowItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* e) {
|
||||||
void ArrowItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* e) {
|
void ArrowItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* e) {
|
||||||
if (e->button() == Qt::LeftButton && !m_labelLocked) {
|
if (e->button() == Qt::LeftButton && !m_labelLocked) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
const QString text = QInputDialog::getText(nullptr, QObject::tr("Arrow label"), QObject::tr("Label:"), QLineEdit::Normal, m_label, &ok);
|
const QString text = QInputDialog::getText(
|
||||||
|
nullptr,
|
||||||
|
QCoreApplication::translate("ArrowItem", "Arrow label"),
|
||||||
|
QCoreApplication::translate("ArrowItem", "Label:"),
|
||||||
|
QLineEdit::Normal,
|
||||||
|
m_label,
|
||||||
|
&ok);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
setLabel(text);
|
setLabel(text);
|
||||||
setLabelHidden(false);
|
setLabelHidden(false);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ public:
|
||||||
qreal topOffset() const { return m_topOffset; }
|
qreal topOffset() const { return m_topOffset; }
|
||||||
qreal bottomOffset() const { return m_bottomOffset; }
|
qreal bottomOffset() const { return m_bottomOffset; }
|
||||||
int type() const override { return Type; }
|
int type() const override { return Type; }
|
||||||
|
int internalId() const { return m_internalId; }
|
||||||
bool isInterface() const { return m_isInterface; }
|
bool isInterface() const { return m_isInterface; }
|
||||||
bool isInterfaceStub() const { return m_isInterface && m_interfaceStubOnly; }
|
bool isInterfaceStub() const { return m_isInterface && m_interfaceStubOnly; }
|
||||||
bool isLabelLocked() const { return m_labelLocked; }
|
bool isLabelLocked() const { return m_labelLocked; }
|
||||||
|
|
@ -58,6 +59,13 @@ public:
|
||||||
void resetInterfaceStub();
|
void resetInterfaceStub();
|
||||||
void setLabelLocked(bool locked);
|
void setLabelLocked(bool locked);
|
||||||
static void setVisualTheme(const QColor& lineColor, const QColor& textColor);
|
static void setVisualTheme(const QColor& lineColor, const QColor& textColor);
|
||||||
|
void setCustomColor(const QColor& color);
|
||||||
|
void clearCustomColor();
|
||||||
|
std::optional<QColor> customColor() const { return m_customColor; }
|
||||||
|
void setCallMechanism(bool v) { m_isCallMechanism = v; }
|
||||||
|
bool isCallMechanism() const { return m_isCallMechanism; }
|
||||||
|
void setCallRefId(int id) { m_callRefId = id; }
|
||||||
|
int callRefId() const { return m_callRefId; }
|
||||||
|
|
||||||
void updatePath();
|
void updatePath();
|
||||||
std::optional<QPointF> hitTest(const QPointF& scenePos, qreal radius) const;
|
std::optional<QPointF> hitTest(const QPointF& scenePos, qreal radius) const;
|
||||||
|
|
@ -87,6 +95,10 @@ private:
|
||||||
ArrowItem* m_labelSource = nullptr;
|
ArrowItem* m_labelSource = nullptr;
|
||||||
static QColor s_lineColor;
|
static QColor s_lineColor;
|
||||||
static QColor s_textColor;
|
static QColor s_textColor;
|
||||||
|
std::optional<QColor> m_customColor;
|
||||||
|
int m_internalId = -1;
|
||||||
|
bool m_isCallMechanism = false;
|
||||||
|
int m_callRefId = -1;
|
||||||
|
|
||||||
DragPart m_dragPart = DragPart::None;
|
DragPart m_dragPart = DragPart::None;
|
||||||
QPointF m_lastDragScenePos;
|
QPointF m_lastDragScenePos;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,9 @@ BlockItem::BlockItem(QString title, QGraphicsItem* parent, int id)
|
||||||
m_rect(0, 0, 200, 100),
|
m_rect(0, 0, 200, 100),
|
||||||
m_id(id)
|
m_id(id)
|
||||||
{
|
{
|
||||||
|
if (m_title.isEmpty()) {
|
||||||
|
m_title = tr("Function");
|
||||||
|
}
|
||||||
setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
|
setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,11 +48,15 @@ void BlockItem::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) {
|
||||||
|
|
||||||
// фон
|
// фон
|
||||||
p->setPen(Qt::NoPen);
|
p->setPen(Qt::NoPen);
|
||||||
p->setBrush(isSelected() ? s_selectedBackgroundColor : s_backgroundColor);
|
const QColor borderColor = m_customColor.value_or(s_borderColor);
|
||||||
|
const QColor fontColor = m_customColor.value_or(s_fontColor);
|
||||||
|
const QColor foregroundColor = m_customColor.value_or(s_foregroundColor);
|
||||||
|
const QColor selectedBg = m_customColor ? m_customColor->lighter(160) : s_selectedBackgroundColor;
|
||||||
|
p->setBrush(isSelected() ? selectedBg : s_backgroundColor);
|
||||||
p->drawRoundedRect(m_rect, 6, 6);
|
p->drawRoundedRect(m_rect, 6, 6);
|
||||||
|
|
||||||
// рамка
|
// рамка
|
||||||
QPen pen(s_borderColor);
|
QPen pen(borderColor);
|
||||||
pen.setWidthF(1.5);
|
pen.setWidthF(1.5);
|
||||||
p->setPen(pen);
|
p->setPen(pen);
|
||||||
p->setBrush(Qt::NoBrush);
|
p->setBrush(Qt::NoBrush);
|
||||||
|
|
@ -65,22 +72,22 @@ void BlockItem::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) {
|
||||||
fold.closeSubpath();
|
fold.closeSubpath();
|
||||||
p->setBrush(s_backgroundColor.darker(108));
|
p->setBrush(s_backgroundColor.darker(108));
|
||||||
p->drawPath(fold);
|
p->drawPath(fold);
|
||||||
p->setPen(QPen(s_borderColor.lighter(130), 1.0));
|
p->setPen(QPen(borderColor.lighter(130), 1.0));
|
||||||
p->drawLine(m_rect.topLeft() + QPointF(ear, 0), m_rect.topLeft() + QPointF(0, ear));
|
p->drawLine(m_rect.topLeft() + QPointF(ear, 0), m_rect.topLeft() + QPointF(0, ear));
|
||||||
}
|
}
|
||||||
|
|
||||||
// заголовок
|
// заголовок
|
||||||
p->setPen(s_fontColor);
|
p->setPen(fontColor);
|
||||||
p->drawText(m_rect.adjusted(10, 8, -10, -8), Qt::AlignLeft | Qt::AlignTop, m_title);
|
p->drawText(m_rect.adjusted(10, 8, -10, -8), Qt::AlignLeft | Qt::AlignTop, m_title);
|
||||||
if (!m_number.isEmpty()) {
|
if (!m_number.isEmpty()) {
|
||||||
QFont f = p->font();
|
QFont f = p->font();
|
||||||
f.setBold(true);
|
f.setBold(true);
|
||||||
p->setFont(f);
|
p->setFont(f);
|
||||||
p->setPen(s_foregroundColor);
|
p->setPen(foregroundColor);
|
||||||
p->drawText(m_rect.adjusted(8, 4, -8, -8), Qt::AlignRight | Qt::AlignBottom, m_number);
|
p->drawText(m_rect.adjusted(8, 4, -8, -8), Qt::AlignRight | Qt::AlignBottom, m_number);
|
||||||
}
|
}
|
||||||
if (m_price.has_value()) {
|
if (m_price.has_value()) {
|
||||||
p->setPen(s_foregroundColor);
|
p->setPen(foregroundColor);
|
||||||
p->drawText(m_rect.adjusted(8, 4, -8, -8), Qt::AlignLeft | Qt::AlignBottom, formattedPrice());
|
p->drawText(m_rect.adjusted(8, 4, -8, -8), Qt::AlignLeft | Qt::AlignBottom, formattedPrice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,12 +96,14 @@ void BlockItem::setTitle(const QString& t) {
|
||||||
if (t == m_title) return;
|
if (t == m_title) return;
|
||||||
m_title = t;
|
m_title = t;
|
||||||
update();
|
update();
|
||||||
|
emit titleChanged(m_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockItem::setNumber(const QString& n) {
|
void BlockItem::setNumber(const QString& n) {
|
||||||
if (n == m_number) return;
|
if (n == m_number) return;
|
||||||
m_number = n;
|
m_number = n;
|
||||||
update();
|
update();
|
||||||
|
emit numberChanged(m_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockItem::setPrice(std::optional<qreal> price) {
|
void BlockItem::setPrice(std::optional<qreal> price) {
|
||||||
|
|
@ -103,6 +112,18 @@ void BlockItem::setPrice(std::optional<qreal> price) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlockItem::setCustomColor(const QColor& color) {
|
||||||
|
if (!color.isValid()) return;
|
||||||
|
m_customColor = color;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockItem::clearCustomColor() {
|
||||||
|
if (!m_customColor) return;
|
||||||
|
m_customColor.reset();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void BlockItem::setCurrencyFormat(const QString& symbol, const QString& placement) {
|
void BlockItem::setCurrencyFormat(const QString& symbol, const QString& placement) {
|
||||||
QString effectiveSymbol = symbol;
|
QString effectiveSymbol = symbol;
|
||||||
if (effectiveSymbol.isEmpty()) {
|
if (effectiveSymbol.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public:
|
||||||
enum class Port { Input, Control, Output, Mechanism };
|
enum class Port { Input, Control, Output, Mechanism };
|
||||||
enum { Type = UserType + 1 };
|
enum { Type = UserType + 1 };
|
||||||
|
|
||||||
explicit BlockItem(QString title = "Function", QGraphicsItem* parent = nullptr, int id = -1);
|
explicit BlockItem(QString title = QString(), QGraphicsItem* parent = nullptr, int id = -1);
|
||||||
|
|
||||||
QRectF boundingRect() const override;
|
QRectF boundingRect() const override;
|
||||||
void paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) override;
|
void paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) override;
|
||||||
|
|
@ -43,9 +43,14 @@ public:
|
||||||
const QColor& border,
|
const QColor& border,
|
||||||
const QColor& font,
|
const QColor& font,
|
||||||
const QColor& selectedBackground);
|
const QColor& selectedBackground);
|
||||||
|
void setCustomColor(const QColor& color);
|
||||||
|
void clearCustomColor();
|
||||||
|
std::optional<QColor> customColor() const { return m_customColor; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void geometryChanged();
|
void geometryChanged();
|
||||||
|
void titleChanged(const QString& title);
|
||||||
|
void numberChanged(const QString& number);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariant itemChange(GraphicsItemChange change, const QVariant& value) override;
|
QVariant itemChange(GraphicsItemChange change, const QVariant& value) override;
|
||||||
|
|
@ -62,6 +67,7 @@ private:
|
||||||
bool m_hasDecomposition = false;
|
bool m_hasDecomposition = false;
|
||||||
QSet<ArrowItem*> m_arrows;
|
QSet<ArrowItem*> m_arrows;
|
||||||
std::optional<qreal> m_price;
|
std::optional<qreal> m_price;
|
||||||
|
std::optional<QColor> m_customColor;
|
||||||
|
|
||||||
static QString s_currencySymbol;
|
static QString s_currencySymbol;
|
||||||
static QString s_currencyPlacement;
|
static QString s_currencyPlacement;
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,10 @@ DiagramScene::DiagramScene(QObject* parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockItem* DiagramScene::createBlockAt(const QPointF& scenePos) {
|
BlockItem* DiagramScene::createBlockAt(const QPointF& scenePos) {
|
||||||
auto* b = new BlockItem("Function", nullptr, m_nextBlockId++);
|
auto* b = new BlockItem(QString(), nullptr, m_nextBlockId++);
|
||||||
addItem(b);
|
addItem(b);
|
||||||
b->setNumber(assignNumber(b));
|
b->setNumber(assignNumber(b));
|
||||||
|
connectBlockSignals(b);
|
||||||
const QRectF r = m_contentRect.isNull() ? sceneRect() : m_contentRect;
|
const QRectF r = m_contentRect.isNull() ? sceneRect() : m_contentRect;
|
||||||
const QPointF desired = scenePos - QPointF(100, 50); // центрируем
|
const QPointF desired = scenePos - QPointF(100, 50); // центрируем
|
||||||
const QRectF br = b->boundingRect().translated(desired);
|
const QRectF br = b->boundingRect().translated(desired);
|
||||||
|
|
@ -59,6 +60,7 @@ BlockItem* DiagramScene::createBlockWithId(const QPointF& scenePos, int id, cons
|
||||||
addItem(b);
|
addItem(b);
|
||||||
b->setPos(scenePos);
|
b->setPos(scenePos);
|
||||||
if (b->number().isEmpty()) b->setNumber(assignNumber(b));
|
if (b->number().isEmpty()) b->setNumber(assignNumber(b));
|
||||||
|
connectBlockSignals(b);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,9 +157,9 @@ void DiagramScene::updateMeta(const QVariantMap& patch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DiagramScene::currentNodeLabel() const {
|
QString DiagramScene::currentNodeLabel() const {
|
||||||
if (m_currentBlockId < 0) return QStringLiteral("TOP");
|
if (m_currentBlockId < 0) return tr("TOP");
|
||||||
if (!m_currentPrefix.isEmpty()) return m_currentPrefix;
|
if (!m_currentPrefix.isEmpty()) return m_currentPrefix;
|
||||||
return QStringLiteral("A0");
|
return tr("A0");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DiagramScene::currentDiagramTitle() const {
|
QString DiagramScene::currentDiagramTitle() const {
|
||||||
|
|
@ -594,6 +596,9 @@ DiagramScene::Snapshot DiagramScene::captureSnapshot() const {
|
||||||
blk.hasDecomp = hasDecomp;
|
blk.hasDecomp = hasDecomp;
|
||||||
blk.number = b->number();
|
blk.number = b->number();
|
||||||
blk.price = b->price();
|
blk.price = b->price();
|
||||||
|
if (auto col = b->customColor()) {
|
||||||
|
blk.color = col->name();
|
||||||
|
}
|
||||||
s.blocks.push_back(blk);
|
s.blocks.push_back(blk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -621,9 +626,14 @@ DiagramScene::Snapshot DiagramScene::captureSnapshot() const {
|
||||||
ar.isInterface = a->isInterface();
|
ar.isInterface = a->isInterface();
|
||||||
ar.isInterfaceStub = a->isInterfaceStub();
|
ar.isInterfaceStub = a->isInterfaceStub();
|
||||||
ar.labelLocked = a->isLabelLocked();
|
ar.labelLocked = a->isLabelLocked();
|
||||||
|
ar.callMechanism = a->isCallMechanism();
|
||||||
|
ar.callRefId = a->callRefId();
|
||||||
if (a->interfaceEdge()) {
|
if (a->interfaceEdge()) {
|
||||||
ar.interfaceEdge = encodeEp(*a->interfaceEdge());
|
ar.interfaceEdge = encodeEp(*a->interfaceEdge());
|
||||||
}
|
}
|
||||||
|
if (auto col = a->customColor()) {
|
||||||
|
ar.color = col->name();
|
||||||
|
}
|
||||||
s.arrows.push_back(std::move(ar));
|
s.arrows.push_back(std::move(ar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -655,6 +665,10 @@ void DiagramScene::restoreSnapshot(const Snapshot& snap, bool resetHistoryState)
|
||||||
if (b.price.has_value()) {
|
if (b.price.has_value()) {
|
||||||
blk->setPrice(b.price);
|
blk->setPrice(b.price);
|
||||||
}
|
}
|
||||||
|
if (b.color.has_value()) {
|
||||||
|
const QColor col(*b.color);
|
||||||
|
if (col.isValid()) blk->setCustomColor(col);
|
||||||
|
}
|
||||||
blockMap.insert(b.id, blk);
|
blockMap.insert(b.id, blk);
|
||||||
m_nextBlockId = std::max(m_nextBlockId, b.id + 1);
|
m_nextBlockId = std::max(m_nextBlockId, b.id + 1);
|
||||||
}
|
}
|
||||||
|
|
@ -710,10 +724,17 @@ for (const auto& j : snap.junctions) {
|
||||||
ar->setLabelHidden(a.labelHidden);
|
ar->setLabelHidden(a.labelHidden);
|
||||||
ar->setLabelInherited(a.labelInherited);
|
ar->setLabelInherited(a.labelInherited);
|
||||||
ar->setLabelLocked(a.labelLocked);
|
ar->setLabelLocked(a.labelLocked);
|
||||||
|
ar->setCallMechanism(a.callMechanism);
|
||||||
|
ar->setCallRefId(a.callRefId);
|
||||||
|
if (a.color.has_value()) {
|
||||||
|
const QColor col(*a.color);
|
||||||
|
if (col.isValid()) ar->setCustomColor(col);
|
||||||
|
}
|
||||||
ar->finalize();
|
ar->finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_restoringSnapshot = false;
|
m_restoringSnapshot = false;
|
||||||
|
updateCallMechanismLabels();
|
||||||
if (resetHistoryState) {
|
if (resetHistoryState) {
|
||||||
resetHistory(captureSnapshot());
|
resetHistory(captureSnapshot());
|
||||||
}
|
}
|
||||||
|
|
@ -742,9 +763,59 @@ void DiagramScene::scheduleSnapshot() {
|
||||||
QTimer::singleShot(0, this, [this]{
|
QTimer::singleShot(0, this, [this]{
|
||||||
m_snapshotScheduled = false;
|
m_snapshotScheduled = false;
|
||||||
pushSnapshot();
|
pushSnapshot();
|
||||||
|
updateCallMechanismLabels();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiagramScene::connectBlockSignals(BlockItem* b) {
|
||||||
|
if (!b) return;
|
||||||
|
connect(b, &BlockItem::titleChanged, this, [this]{ updateCallMechanismLabels(); });
|
||||||
|
connect(b, &BlockItem::numberChanged, this, [this]{ updateCallMechanismLabels(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagramScene::updateCallMechanismLabels() {
|
||||||
|
QHash<int, BlockItem*> blockById;
|
||||||
|
for (QGraphicsItem* it : items()) {
|
||||||
|
if (auto* b = qgraphicsitem_cast<BlockItem*>(it)) {
|
||||||
|
blockById.insert(b->id(), b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (QGraphicsItem* it : items()) {
|
||||||
|
auto* a = qgraphicsitem_cast<ArrowItem*>(it);
|
||||||
|
if (!a || !a->isCallMechanism()) continue;
|
||||||
|
BlockItem* ref = blockById.value(a->callRefId(), nullptr);
|
||||||
|
if (!ref) continue;
|
||||||
|
const QString num = ref->number();
|
||||||
|
const QString title = ref->title();
|
||||||
|
const QString label = num.isEmpty() ? title : QStringLiteral("%1 %2").arg(num, title);
|
||||||
|
a->setLabel(label);
|
||||||
|
a->setLabelHidden(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagramScene::purgeBrokenCallMechanisms() {
|
||||||
|
QSet<BlockItem*> blocks;
|
||||||
|
for (QGraphicsItem* it : items()) {
|
||||||
|
if (auto* b = qgraphicsitem_cast<BlockItem*>(it)) blocks.insert(b);
|
||||||
|
}
|
||||||
|
QVector<ArrowItem*> toRemove;
|
||||||
|
for (QGraphicsItem* it : items()) {
|
||||||
|
if (auto* a = qgraphicsitem_cast<ArrowItem*>(it)) {
|
||||||
|
if (!a->isCallMechanism()) continue;
|
||||||
|
auto from = a->from();
|
||||||
|
auto to = a->to();
|
||||||
|
bool ok = true;
|
||||||
|
if (from.block && !blocks.contains(from.block)) ok = false;
|
||||||
|
if (to.block && !blocks.contains(to.block)) ok = false;
|
||||||
|
if (!ok) toRemove.push_back(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ArrowItem* a : toRemove) {
|
||||||
|
removeItem(a);
|
||||||
|
delete a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DiagramScene::undo() {
|
void DiagramScene::undo() {
|
||||||
if (m_historyIndex <= 0) return;
|
if (m_historyIndex <= 0) return;
|
||||||
m_historyIndex -= 1;
|
m_historyIndex -= 1;
|
||||||
|
|
@ -1022,6 +1093,7 @@ void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* e) {
|
||||||
maybeSnapshotMovedItems();
|
maybeSnapshotMovedItems();
|
||||||
m_itemDragActive = false;
|
m_itemDragActive = false;
|
||||||
}
|
}
|
||||||
|
purgeBrokenCallMechanisms();
|
||||||
QGraphicsScene::mouseReleaseEvent(e);
|
QGraphicsScene::mouseReleaseEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1056,6 +1128,47 @@ void DiagramScene::keyPressEvent(QKeyEvent* e) {
|
||||||
QGraphicsScene::keyPressEvent(e);
|
QGraphicsScene::keyPressEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DiagramScene::hasCallMechanism(const BlockItem* target) const {
|
||||||
|
if (!target) return false;
|
||||||
|
const auto all = items();
|
||||||
|
for (QGraphicsItem* it : all) {
|
||||||
|
if (auto* a = qgraphicsitem_cast<ArrowItem*>(it)) {
|
||||||
|
if (!a->isCallMechanism()) continue;
|
||||||
|
const auto from = a->from();
|
||||||
|
if (from.block && from.block == target) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiagramScene::startCallMechanism(BlockItem* origin, BlockItem* refBlock, const QString& label) {
|
||||||
|
if (!origin || !refBlock) return false;
|
||||||
|
if (origin == refBlock) return false;
|
||||||
|
if (hasCallMechanism(origin)) return false;
|
||||||
|
cancelCurrentDrag();
|
||||||
|
auto* a = new ArrowItem();
|
||||||
|
addItem(a);
|
||||||
|
ArrowItem::Endpoint from;
|
||||||
|
from.scenePos = origin->portScenePos(BlockItem::Port::Mechanism) + QPointF(0, 40);
|
||||||
|
from.port = BlockItem::Port::Mechanism;
|
||||||
|
ArrowItem::Endpoint to;
|
||||||
|
to.block = origin;
|
||||||
|
to.port = BlockItem::Port::Mechanism;
|
||||||
|
a->setFrom(from);
|
||||||
|
a->setTo(to);
|
||||||
|
a->setCallMechanism(true);
|
||||||
|
a->setCallRefId(refBlock->id());
|
||||||
|
a->setLabel(label);
|
||||||
|
a->setLabelHidden(false);
|
||||||
|
a->setLabelLocked(true);
|
||||||
|
a->setLabelInherited(false);
|
||||||
|
a->finalize();
|
||||||
|
pushSnapshot();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DiagramScene::cancelCurrentDrag() {
|
void DiagramScene::cancelCurrentDrag() {
|
||||||
if (m_dragArrow) {
|
if (m_dragArrow) {
|
||||||
if (m_dragArrow->isInterface()) {
|
if (m_dragArrow->isInterface()) {
|
||||||
|
|
@ -1119,7 +1232,10 @@ void DiagramScene::deleteSelection() {
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!toDelete.isEmpty()) pushSnapshot();
|
if (!toDelete.isEmpty()) {
|
||||||
|
purgeBrokenCallMechanisms();
|
||||||
|
pushSnapshot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiagramScene::requestSnapshot() {
|
void DiagramScene::requestSnapshot() {
|
||||||
|
|
@ -1278,6 +1394,7 @@ QVariantMap DiagramScene::exportToVariant() {
|
||||||
o["hasDecomp"] = b.hasDecomp;
|
o["hasDecomp"] = b.hasDecomp;
|
||||||
o["number"] = b.number;
|
o["number"] = b.number;
|
||||||
if (b.price.has_value()) o["price"] = *b.price;
|
if (b.price.has_value()) o["price"] = *b.price;
|
||||||
|
if (b.color.has_value()) o["color"] = *b.color;
|
||||||
blocks.append(o);
|
blocks.append(o);
|
||||||
}
|
}
|
||||||
root["blocks"] = blocks;
|
root["blocks"] = blocks;
|
||||||
|
|
@ -1307,6 +1424,9 @@ QVariantMap DiagramScene::exportToVariant() {
|
||||||
o["isInterfaceStub"] = a.isInterfaceStub;
|
o["isInterfaceStub"] = a.isInterfaceStub;
|
||||||
o["labelLocked"] = a.labelLocked;
|
o["labelLocked"] = a.labelLocked;
|
||||||
o["interfaceEdge"] = endpointToJson(a.interfaceEdge);
|
o["interfaceEdge"] = endpointToJson(a.interfaceEdge);
|
||||||
|
o["callMechanism"] = a.callMechanism;
|
||||||
|
o["callRefId"] = a.callRefId;
|
||||||
|
if (a.color.has_value()) o["color"] = *a.color;
|
||||||
arrows.append(o);
|
arrows.append(o);
|
||||||
}
|
}
|
||||||
root["arrows"] = arrows;
|
root["arrows"] = arrows;
|
||||||
|
|
@ -1340,7 +1460,7 @@ bool DiagramScene::importFromVariant(const QVariantMap& map) {
|
||||||
Snapshot snap;
|
Snapshot snap;
|
||||||
for (const auto& vb : root.value("blocks").toArray()) {
|
for (const auto& vb : root.value("blocks").toArray()) {
|
||||||
const auto o = vb.toObject();
|
const auto o = vb.toObject();
|
||||||
const auto posArr = o.value("pos").toArray();
|
const auto posArr = o.value("pos").toArray();
|
||||||
if (posArr.size() != 2) continue;
|
if (posArr.size() != 2) continue;
|
||||||
DiagramScene::Snapshot::Block blk;
|
DiagramScene::Snapshot::Block blk;
|
||||||
blk.id = o.value("id").toInt();
|
blk.id = o.value("id").toInt();
|
||||||
|
|
@ -1351,6 +1471,8 @@ bool DiagramScene::importFromVariant(const QVariantMap& map) {
|
||||||
if (o.contains("price") && !o.value("price").isNull()) {
|
if (o.contains("price") && !o.value("price").isNull()) {
|
||||||
blk.price = o.value("price").toDouble();
|
blk.price = o.value("price").toDouble();
|
||||||
}
|
}
|
||||||
|
const QString blkColor = o.value("color").toString();
|
||||||
|
if (!blkColor.isEmpty()) blk.color = blkColor;
|
||||||
snap.blocks.push_back(blk);
|
snap.blocks.push_back(blk);
|
||||||
}
|
}
|
||||||
for (const auto& vj : root.value("junctions").toArray()) {
|
for (const auto& vj : root.value("junctions").toArray()) {
|
||||||
|
|
@ -1376,6 +1498,10 @@ bool DiagramScene::importFromVariant(const QVariantMap& map) {
|
||||||
ar.isInterfaceStub = o.value("isInterfaceStub").toBool(false);
|
ar.isInterfaceStub = o.value("isInterfaceStub").toBool(false);
|
||||||
ar.labelLocked = o.value("labelLocked").toBool(false);
|
ar.labelLocked = o.value("labelLocked").toBool(false);
|
||||||
ar.interfaceEdge = endpointFromJson(o.value("interfaceEdge").toObject());
|
ar.interfaceEdge = endpointFromJson(o.value("interfaceEdge").toObject());
|
||||||
|
ar.callMechanism = o.value("callMechanism").toBool(false);
|
||||||
|
ar.callRefId = o.value("callRefId").toInt(-1);
|
||||||
|
const QString arrowColor = o.value("color").toString();
|
||||||
|
if (!arrowColor.isEmpty()) ar.color = arrowColor;
|
||||||
snap.arrows.push_back(ar);
|
snap.arrows.push_back(ar);
|
||||||
}
|
}
|
||||||
return snap;
|
return snap;
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,16 @@ public:
|
||||||
QRectF contentRect() const { return m_contentRect; }
|
QRectF contentRect() const { return m_contentRect; }
|
||||||
QString currentNodeLabel() const;
|
QString currentNodeLabel() const;
|
||||||
QString currentDiagramTitle() const;
|
QString currentDiagramTitle() const;
|
||||||
|
int currentBlockId() const { return m_currentBlockId; }
|
||||||
bool goDownIntoSelected();
|
bool goDownIntoSelected();
|
||||||
bool goDownIntoBlock(BlockItem* b);
|
bool goDownIntoBlock(BlockItem* b);
|
||||||
bool goUp();
|
bool goUp();
|
||||||
void propagateLabelFrom(ArrowItem* root);
|
void propagateLabelFrom(ArrowItem* root);
|
||||||
QVariantMap exportToVariant();
|
QVariantMap exportToVariant();
|
||||||
bool importFromVariant(const QVariantMap& map);
|
bool importFromVariant(const QVariantMap& map);
|
||||||
|
bool startCallMechanism(BlockItem* origin, BlockItem* refBlock, const QString& label);
|
||||||
|
bool hasCallMechanism(const BlockItem* origin) const;
|
||||||
|
void updateCallMechanismLabels();
|
||||||
signals:
|
signals:
|
||||||
void changed();
|
void changed();
|
||||||
void metaChanged(const QVariantMap& meta);
|
void metaChanged(const QVariantMap& meta);
|
||||||
|
|
@ -48,7 +52,7 @@ private:
|
||||||
std::optional<QPointF> localPos;
|
std::optional<QPointF> localPos;
|
||||||
std::optional<QPointF> scenePos;
|
std::optional<QPointF> scenePos;
|
||||||
};
|
};
|
||||||
struct Block { int id; QString title; QPointF pos; bool hasDecomp = false; QString number; std::optional<qreal> price; };
|
struct Block { int id; QString title; QPointF pos; bool hasDecomp = false; QString number; std::optional<qreal> price; std::optional<QString> color; };
|
||||||
struct Junction { int id; QPointF pos; };
|
struct Junction { int id; QPointF pos; };
|
||||||
struct Arrow {
|
struct Arrow {
|
||||||
Endpoint from;
|
Endpoint from;
|
||||||
|
|
@ -64,6 +68,9 @@ private:
|
||||||
bool isInterfaceStub = false;
|
bool isInterfaceStub = false;
|
||||||
bool labelLocked = false;
|
bool labelLocked = false;
|
||||||
Endpoint interfaceEdge;
|
Endpoint interfaceEdge;
|
||||||
|
std::optional<QString> color;
|
||||||
|
bool callMechanism = false;
|
||||||
|
int callRefId = -1;
|
||||||
};
|
};
|
||||||
QVector<Block> blocks;
|
QVector<Block> blocks;
|
||||||
QVector<Junction> junctions;
|
QVector<Junction> junctions;
|
||||||
|
|
@ -113,6 +120,8 @@ private:
|
||||||
QString currentPathKey() const;
|
QString currentPathKey() const;
|
||||||
QString childKeyFor(int blockId) const;
|
QString childKeyFor(int blockId) const;
|
||||||
QString assignNumber(BlockItem* b);
|
QString assignNumber(BlockItem* b);
|
||||||
|
void connectBlockSignals(BlockItem* b);
|
||||||
|
void purgeBrokenCallMechanisms();
|
||||||
|
|
||||||
enum class Edge { None, Left, Right, Top, Bottom };
|
enum class Edge { None, Left, Right, Top, Bottom };
|
||||||
Edge hitTestEdge(const QPointF& scenePos, QPointF* outScenePoint = nullptr) const;
|
Edge hitTestEdge(const QPointF& scenePos, QPointF* outScenePoint = nullptr) const;
|
||||||
|
|
|
||||||
79
src/plugins/Manual.md
Normal file
79
src/plugins/Manual.md
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
# Plugin Development Manual
|
||||||
|
|
||||||
|
This application loads runtime plugins as plain shared libraries located in `plugins/`. Plugins are isolated from internal types and talk to the host via the C ABI defined in `PluginApi.h`.
|
||||||
|
|
||||||
|
## Plugin API
|
||||||
|
|
||||||
|
- Header: `src/plugins/PluginApi.h`
|
||||||
|
- Entry point: a single exported C function
|
||||||
|
```c
|
||||||
|
extern "C" bool idef0_plugin_init_v1(Idef0Host* host);
|
||||||
|
```
|
||||||
|
- You **must not** include or rely on any other app headers.
|
||||||
|
|
||||||
|
`Idef0Host` exposes function pointers:
|
||||||
|
|
||||||
|
| Function | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `size_t selected_items(void* opaque, Idef0SelectedItem* out, size_t max)` | Returns count of selected items, fills kind (`IDEF0_ITEM_BLOCK` / `IDEF0_ITEM_ARROW`) and `id`. |
|
||||||
|
| `bool set_item_color(void* opaque, int kind, int id, const char* color_hex)` | Set per-item color override as `#rrggbb`. |
|
||||||
|
| `bool clear_item_color(void* opaque, int kind, int id)` | Clear color override. |
|
||||||
|
| `void add_menu_action(void* opaque, const char* text, Idef0ActionCallback cb, void* user_data)` | Add an action under the Plugins menu; `cb` gets `(user_data, host)`. |
|
||||||
|
| `const char* plugin_dir(void* opaque)` | Absolute directory of this plugin library (use to load plugin-local resources/translations). |
|
||||||
|
|
||||||
|
## Minimal Example (C++)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "plugins/PluginApi.h"
|
||||||
|
#include <QObject> // for tr(), optional
|
||||||
|
#include <QColorDialog> // optional UI
|
||||||
|
|
||||||
|
static void onSetRed(void*, Idef0Host* host) {
|
||||||
|
Idef0SelectedItem items[32];
|
||||||
|
size_t n = host->selected_items(host->opaque, items, 32);
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
host->set_item_color(host->opaque, items[i].kind, items[i].id, "#ff0000");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool idef0_plugin_init_v1(Idef0Host* host) {
|
||||||
|
if (!host || !host->add_menu_action) return false;
|
||||||
|
host->add_menu_action(host->opaque, "Set red", &onSetRed, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build with CMake
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(myplugin LANGUAGES CXX)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Widgets) # only if you use Qt UI
|
||||||
|
|
||||||
|
add_library(myplugin MODULE myplugin.cpp)
|
||||||
|
target_include_directories(myplugin PRIVATE /path/to/app/src) # for plugins/PluginApi.h
|
||||||
|
target_link_libraries(myplugin PRIVATE Qt6::Widgets) # optional
|
||||||
|
set_target_properties(myplugin PROPERTIES
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
|
||||||
|
INSTALL_RPATH "\$ORIGIN/../lib"
|
||||||
|
)
|
||||||
|
install(TARGETS myplugin LIBRARY DESTINATION plugins RUNTIME DESTINATION plugins)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don’t need Qt widgets, omit `find_package(Qt6...)` and link against nothing extra.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
- Place the built library next to the app in `plugins/`:
|
||||||
|
- Linux: `plugins/libmyplugin.so`
|
||||||
|
- Windows: `plugins/myplugin.dll`
|
||||||
|
- macOS: `plugins/libmyplugin.dylib`
|
||||||
|
- Ensure it is built against the same `PluginApi.h` version as the app.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Keep plugin state in your own globals; the host provides only opaque pointers.
|
||||||
|
- Validate inputs (e.g., color strings) and return early on null host functions.
|
||||||
|
- Use `selected_items` first, then act via `set_item_color`/`clear_item_color`.
|
||||||
|
- Add menu actions via `add_menu_action` for user-visible triggers. The host calls your callbacks on activation.
|
||||||
37
src/plugins/PluginApi.h
Normal file
37
src/plugins/PluginApi.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
enum Idef0ItemKind {
|
||||||
|
IDEF0_ITEM_BLOCK = 1,
|
||||||
|
IDEF0_ITEM_ARROW = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Idef0SelectedItem {
|
||||||
|
int kind; // Idef0ItemKind
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Idef0Host;
|
||||||
|
typedef void (*Idef0ActionCallback)(void* user_data, Idef0Host* host);
|
||||||
|
|
||||||
|
struct Idef0Host {
|
||||||
|
void* opaque; // host-specific pointer
|
||||||
|
// Fills up to max entries, returns actual count.
|
||||||
|
size_t (*selected_items)(void* opaque, Idef0SelectedItem* out, size_t max);
|
||||||
|
// Set hex color (#rrggbb) for item id/kind. Returns true on success.
|
||||||
|
bool (*set_item_color)(void* opaque, int kind, int id, const char* color_hex);
|
||||||
|
// Clear override color.
|
||||||
|
bool (*clear_item_color)(void* opaque, int kind, int id);
|
||||||
|
// Add menu action under Plugins; callback invoked on trigger.
|
||||||
|
void (*add_menu_action)(void* opaque, const char* text, Idef0ActionCallback cb, void* user_data);
|
||||||
|
// Absolute directory where this plugin library resides.
|
||||||
|
const char* (*plugin_dir)(void* opaque);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Plugin entry point must be exported with this name.
|
||||||
|
typedef bool (*Idef0PluginInit)(Idef0Host* host);
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
175
src/plugins/PluginManager.cpp
Normal file
175
src/plugins/PluginManager.cpp
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
#include "plugins/PluginManager.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
#include "MainWindow.h"
|
||||||
|
#include "items/DiagramScene.h"
|
||||||
|
#include "items/BlockItem.h"
|
||||||
|
#include "items/ArrowItem.h"
|
||||||
|
|
||||||
|
PluginManager::PluginManager(MainWindow* window, QMenu* pluginsMenu, QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_window(window),
|
||||||
|
m_pluginsMenu(pluginsMenu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu* PluginManager::pluginsMenu() const {
|
||||||
|
return m_pluginsMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagramScene* PluginManager::currentScene() const {
|
||||||
|
return m_window ? m_window->scene() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* PluginManager::window() const {
|
||||||
|
return m_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PluginManager::pluginSearchPaths() const {
|
||||||
|
QStringList paths;
|
||||||
|
const QString appDir = QCoreApplication::applicationDirPath();
|
||||||
|
paths << (appDir + "/plugins");
|
||||||
|
paths << (appDir + "/../plugins");
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::loadPlugins() {
|
||||||
|
const QStringList roots = pluginSearchPaths();
|
||||||
|
qInfo() << "PluginManager scanning paths:" << roots;
|
||||||
|
QStringList queue = roots;
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
const QString dirPath = queue.takeFirst();
|
||||||
|
QDir dir(dirPath);
|
||||||
|
if (!dir.exists()) continue;
|
||||||
|
// Enqueue subdirectories for recursive scan (one level deep is enough here).
|
||||||
|
const auto subdirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (const QFileInfo& di : subdirs) {
|
||||||
|
queue << di.absoluteFilePath();
|
||||||
|
}
|
||||||
|
// Load plugins from this directory.
|
||||||
|
const auto entries = dir.entryInfoList(QDir::Files);
|
||||||
|
for (const QFileInfo& fi : entries) {
|
||||||
|
if (!QLibrary::isLibrary(fi.absoluteFilePath())) continue;
|
||||||
|
auto lib = std::make_unique<QLibrary>(fi.absoluteFilePath());
|
||||||
|
if (!lib->load()) {
|
||||||
|
qWarning() << "Plugin load failed:" << fi.absoluteFilePath() << lib->errorString();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto init = reinterpret_cast<Idef0PluginInit>(lib->resolve("idef0_plugin_init_v1"));
|
||||||
|
if (!init) {
|
||||||
|
qWarning() << "Plugin missing entrypoint idef0_plugin_init_v1:" << fi.absoluteFilePath();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto ctx = makeContext(fi.absoluteFilePath());
|
||||||
|
if (!ctx) continue;
|
||||||
|
if (!init(&ctx->host)) {
|
||||||
|
qWarning() << "Plugin init returned false:" << fi.absoluteFilePath();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
qInfo() << "Plugin loaded:" << fi.absoluteFilePath();
|
||||||
|
m_plugins.emplace_back(std::move(lib), std::move(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PluginManager::HostContext> PluginManager::makeContext(const QString& pluginPath) {
|
||||||
|
auto ctx = std::make_unique<HostContext>();
|
||||||
|
ctx->manager = this;
|
||||||
|
ctx->pluginPath = pluginPath;
|
||||||
|
ctx->pluginDirUtf8 = QFileInfo(pluginPath).absolutePath().toUtf8();
|
||||||
|
ctx->host.opaque = ctx.get();
|
||||||
|
ctx->host.selected_items = &PluginManager::apiSelectedItems;
|
||||||
|
ctx->host.set_item_color = &PluginManager::apiSetItemColor;
|
||||||
|
ctx->host.clear_item_color = &PluginManager::apiClearItemColor;
|
||||||
|
ctx->host.add_menu_action = &PluginManager::apiAddMenuAction;
|
||||||
|
ctx->host.plugin_dir = &PluginManager::apiPluginDir;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::addAction(const QString& text, Idef0ActionCallback cb, void* userData, HostContext* ctx) {
|
||||||
|
if (!m_pluginsMenu || !cb || !ctx) return;
|
||||||
|
QAction* act = m_pluginsMenu->addAction(text);
|
||||||
|
connect(act, &QAction::triggered, this, [cb, userData, ctx]{
|
||||||
|
cb(userData, &ctx->host);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PluginManager::apiSelectedItems(void* opaque, Idef0SelectedItem* out, size_t max) {
|
||||||
|
auto* ctx = static_cast<HostContext*>(opaque);
|
||||||
|
if (!ctx || !ctx->manager || !ctx->manager->m_window || !out || max == 0) return 0;
|
||||||
|
DiagramScene* scene = ctx->manager->currentScene();
|
||||||
|
if (!scene) return 0;
|
||||||
|
const auto sel = scene->selectedItems();
|
||||||
|
const size_t selCount = static_cast<size_t>(sel.size());
|
||||||
|
const size_t count = std::min(selCount, max);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
Idef0SelectedItem item{0, -1};
|
||||||
|
if (auto* b = qgraphicsitem_cast<BlockItem*>(sel[int(i)])) {
|
||||||
|
item.kind = IDEF0_ITEM_BLOCK;
|
||||||
|
item.id = b->id();
|
||||||
|
} else if (auto* a = qgraphicsitem_cast<ArrowItem*>(sel[int(i)])) {
|
||||||
|
item.kind = IDEF0_ITEM_ARROW;
|
||||||
|
item.id = a->internalId();
|
||||||
|
}
|
||||||
|
out[i] = item;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool applyColorToScene(DiagramScene* scene, int kind, int id, const QColor& col, bool clear) {
|
||||||
|
if (!scene) return false;
|
||||||
|
bool changed = false;
|
||||||
|
const auto items = scene->items();
|
||||||
|
for (QGraphicsItem* it : items) {
|
||||||
|
if (kind == IDEF0_ITEM_BLOCK) {
|
||||||
|
if (auto* b = qgraphicsitem_cast<BlockItem*>(it)) {
|
||||||
|
if (b->id() == id) {
|
||||||
|
clear ? b->clearCustomColor() : b->setCustomColor(col);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (kind == IDEF0_ITEM_ARROW) {
|
||||||
|
if (auto* a = qgraphicsitem_cast<ArrowItem*>(it)) {
|
||||||
|
if (a->internalId() == id) {
|
||||||
|
clear ? a->clearCustomColor() : a->setCustomColor(col);
|
||||||
|
a->updatePath();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) scene->requestSnapshot();
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginManager::apiSetItemColor(void* opaque, int kind, int id, const char* color_hex) {
|
||||||
|
auto* ctx = static_cast<HostContext*>(opaque);
|
||||||
|
if (!ctx || !ctx->manager || !ctx->manager->m_window || !color_hex) return false;
|
||||||
|
QColor c(color_hex);
|
||||||
|
if (!c.isValid()) return false;
|
||||||
|
return applyColorToScene(ctx->manager->currentScene(), kind, id, c, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginManager::apiClearItemColor(void* opaque, int kind, int id) {
|
||||||
|
auto* ctx = static_cast<HostContext*>(opaque);
|
||||||
|
if (!ctx || !ctx->manager || !ctx->manager->m_window) return false;
|
||||||
|
return applyColorToScene(ctx->manager->currentScene(), kind, id, QColor(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::apiAddMenuAction(void* opaque, const char* text, Idef0ActionCallback cb, void* user_data) {
|
||||||
|
auto* ctx = static_cast<HostContext*>(opaque);
|
||||||
|
if (!ctx || !ctx->manager || !text || !cb) return;
|
||||||
|
ctx->manager->addAction(QString::fromUtf8(text), cb, user_data, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* PluginManager::apiPluginDir(void* opaque) {
|
||||||
|
auto* ctx = static_cast<HostContext*>(opaque);
|
||||||
|
if (!ctx) return nullptr;
|
||||||
|
return ctx->pluginDirUtf8.constData();
|
||||||
|
}
|
||||||
55
src/plugins/PluginManager.h
Normal file
55
src/plugins/PluginManager.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QLibrary>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "plugins/PluginApi.h"
|
||||||
|
|
||||||
|
class QMenu;
|
||||||
|
class MainWindow;
|
||||||
|
class DiagramScene;
|
||||||
|
|
||||||
|
class PluginManager final : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PluginManager(MainWindow* window, QMenu* pluginsMenu, QObject* parent = nullptr);
|
||||||
|
void loadPlugins();
|
||||||
|
|
||||||
|
QMenu* pluginsMenu() const;
|
||||||
|
DiagramScene* currentScene() const;
|
||||||
|
QWidget* window() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct HostContext {
|
||||||
|
PluginManager* manager = nullptr;
|
||||||
|
QString pluginPath;
|
||||||
|
QByteArray pluginDirUtf8;
|
||||||
|
Idef0Host host;
|
||||||
|
};
|
||||||
|
struct LoadedPlugin {
|
||||||
|
std::unique_ptr<QLibrary> lib;
|
||||||
|
std::unique_ptr<HostContext> ctx;
|
||||||
|
LoadedPlugin() = default;
|
||||||
|
LoadedPlugin(std::unique_ptr<QLibrary>&& l, std::unique_ptr<HostContext>&& c)
|
||||||
|
: lib(std::move(l)), ctx(std::move(c)) {}
|
||||||
|
LoadedPlugin(LoadedPlugin&&) noexcept = default;
|
||||||
|
LoadedPlugin& operator=(LoadedPlugin&&) noexcept = default;
|
||||||
|
LoadedPlugin(const LoadedPlugin&) = delete;
|
||||||
|
LoadedPlugin& operator=(const LoadedPlugin&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
QStringList pluginSearchPaths() const;
|
||||||
|
std::unique_ptr<HostContext> makeContext(const QString& pluginPath);
|
||||||
|
void addAction(const QString& text, Idef0ActionCallback cb, void* userData, HostContext* ctx);
|
||||||
|
static size_t apiSelectedItems(void* opaque, Idef0SelectedItem* out, size_t max);
|
||||||
|
static bool apiSetItemColor(void* opaque, int kind, int id, const char* color_hex);
|
||||||
|
static bool apiClearItemColor(void* opaque, int kind, int id);
|
||||||
|
static void apiAddMenuAction(void* opaque, const char* text, Idef0ActionCallback cb, void* user_data);
|
||||||
|
static const char* apiPluginDir(void* opaque);
|
||||||
|
|
||||||
|
MainWindow* m_window = nullptr;
|
||||||
|
QMenu* m_pluginsMenu = nullptr;
|
||||||
|
std::vector<LoadedPlugin> m_plugins;
|
||||||
|
};
|
||||||
92
src/plugins/color/ColorsPlugin.cpp
Normal file
92
src/plugins/color/ColorsPlugin.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include "plugins/color/ColorsPlugin.h"
|
||||||
|
|
||||||
|
#include <QColorDialog>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
static void ensureTranslator(Idef0Host* host) {
|
||||||
|
static bool loaded = false;
|
||||||
|
static QTranslator* translator = nullptr;
|
||||||
|
if (loaded || !host || !host->plugin_dir) return;
|
||||||
|
const char* dirC = host->plugin_dir(host->opaque);
|
||||||
|
if (!dirC) return;
|
||||||
|
const QString dir = QString::fromUtf8(dirC);
|
||||||
|
const QString baseLocale = QLocale().name().replace('-', '_');
|
||||||
|
const QString shortLocale = baseLocale.section('_', 0, 0);
|
||||||
|
|
||||||
|
QStringList candidates;
|
||||||
|
auto addCandidate = [&](const QString& c) {
|
||||||
|
if (!c.isEmpty() && !c.contains("C")) candidates << c;
|
||||||
|
};
|
||||||
|
addCandidate(baseLocale);
|
||||||
|
addCandidate(shortLocale);
|
||||||
|
const QString sysName = QLocale::system().name().replace('-', '_');
|
||||||
|
addCandidate(sysName);
|
||||||
|
addCandidate(sysName.section('_', 0, 0));
|
||||||
|
for (const QString& lang : QLocale::system().uiLanguages()) {
|
||||||
|
const QString norm = QString(lang).replace('-', '_');
|
||||||
|
addCandidate(norm);
|
||||||
|
addCandidate(norm.section('_', 0, 0));
|
||||||
|
}
|
||||||
|
const QString envLang = QString::fromLocal8Bit(qgetenv("LANG")).section('.', 0, 0).replace('-', '_');
|
||||||
|
addCandidate(envLang);
|
||||||
|
addCandidate(envLang.section('_', 0, 0));
|
||||||
|
|
||||||
|
candidates.removeDuplicates();
|
||||||
|
if (candidates.isEmpty()) candidates << QStringLiteral("en");
|
||||||
|
|
||||||
|
translator = new QTranslator(qApp);
|
||||||
|
qInfo() << "[colors plugin] translator search in" << dir << "candidates" << candidates;
|
||||||
|
for (const QString& loc : candidates) {
|
||||||
|
if (loc.isEmpty()) continue;
|
||||||
|
const QString baseName = QStringLiteral("colors_%1").arg(loc);
|
||||||
|
if (translator->load(baseName, dir + "/translations")) {
|
||||||
|
qApp->installTranslator(translator);
|
||||||
|
qInfo() << "[colors plugin] translator loaded" << baseName;
|
||||||
|
loaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!loaded) {
|
||||||
|
qWarning() << "[colors plugin] translator not found, falling back to default language";
|
||||||
|
delete translator;
|
||||||
|
translator = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onSetColor(void*, Idef0Host* host) {
|
||||||
|
if (!host || !host->selected_items || !host->set_item_color) return;
|
||||||
|
ensureTranslator(host);
|
||||||
|
Idef0SelectedItem items[64];
|
||||||
|
const size_t count = host->selected_items(host->opaque, items, 64);
|
||||||
|
if (count == 0) return;
|
||||||
|
QColor initial("#2b6ee6");
|
||||||
|
const QColor chosen = QColorDialog::getColor(initial, nullptr, QObject::tr("Select item color"));
|
||||||
|
if (!chosen.isValid()) return;
|
||||||
|
const QByteArray hex = chosen.name(QColor::HexRgb).toUtf8();
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
host->set_item_color(host->opaque, items[i].kind, items[i].id, hex.constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onClearColor(void*, Idef0Host* host) {
|
||||||
|
if (!host || !host->selected_items || !host->clear_item_color) return;
|
||||||
|
ensureTranslator(host);
|
||||||
|
Idef0SelectedItem items[64];
|
||||||
|
const size_t count = host->selected_items(host->opaque, items, 64);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
host->clear_item_color(host->opaque, items[i].kind, items[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool idef0_plugin_init_v1(Idef0Host* host) {
|
||||||
|
if (!host || !host->add_menu_action) return false;
|
||||||
|
ensureTranslator(host);
|
||||||
|
qInfo() << "[colors plugin] init, plugin dir:" << (host->plugin_dir ? host->plugin_dir(host->opaque) : "(none)");
|
||||||
|
host->add_menu_action(host->opaque, QObject::tr("Set item color…").toUtf8().constData(), &onSetColor, nullptr);
|
||||||
|
host->add_menu_action(host->opaque, QObject::tr("Clear item colors").toUtf8().constData(), &onClearColor, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
2
src/plugins/color/ColorsPlugin.h
Normal file
2
src/plugins/color/ColorsPlugin.h
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#pragma once
|
||||||
|
#include "plugins/PluginApi.h"
|
||||||
19
src/plugins/color/translations/colors_en.ts
Normal file
19
src/plugins/color/translations/colors_en.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US">
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<source>Select item color</source>
|
||||||
|
<translation>Select item color</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set item color…</source>
|
||||||
|
<translation>Set item color…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear item colors</source>
|
||||||
|
<translation>Clear item colors</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
19
src/plugins/color/translations/colors_fr.ts
Normal file
19
src/plugins/color/translations/colors_fr.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="fr_FR">
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<source>Select item color</source>
|
||||||
|
<translation>Choisir la couleur de l'élément</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set item color…</source>
|
||||||
|
<translation>Définir la couleur de l'élément…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear item colors</source>
|
||||||
|
<translation>Réinitialiser les couleurs des éléments</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
19
src/plugins/color/translations/colors_ru.ts
Normal file
19
src/plugins/color/translations/colors_ru.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="ru_RU">
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<source>Select item color</source>
|
||||||
|
<translation>Выберите цвет элемента</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set item color…</source>
|
||||||
|
<translation>Задать цвет элемента…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear item colors</source>
|
||||||
|
<translation>Сбросить цвета элементов</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
1
translations/README.txt
Normal file
1
translations/README.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Translation sources here (use lupdate/lrelease)
|
||||||
567
translations/idef0_en.ts
Normal file
567
translations/idef0_en.ts
Normal file
|
|
@ -0,0 +1,567 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US">
|
||||||
|
<context>
|
||||||
|
<name>ArrowItem</name>
|
||||||
|
<message>
|
||||||
|
<source>Arrow label</source>
|
||||||
|
<translation>Arrow label</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Label:</source>
|
||||||
|
<translation>Label:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>BlockItem</name>
|
||||||
|
<message>
|
||||||
|
<source>Function</source>
|
||||||
|
<translation>Function</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit block</source>
|
||||||
|
<translation>Edit block</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Title:</source>
|
||||||
|
<translation>Title:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set price</source>
|
||||||
|
<translation>Set price</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Price:</source>
|
||||||
|
<translation>Price:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DiagramScene</name>
|
||||||
|
<message>
|
||||||
|
<source>TOP</source>
|
||||||
|
<translation>TOP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>A0</source>
|
||||||
|
<translation>A0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Landscape</source>
|
||||||
|
<translation>Landscape</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Portrait</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>HeaderFooterItem</name>
|
||||||
|
<message>
|
||||||
|
<source>USED AT:</source>
|
||||||
|
<translation>USED AT:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>ORGANIZATION:</source>
|
||||||
|
<translation>ORGANIZATION:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>AUTHOR:</source>
|
||||||
|
<translation>AUTHOR:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PROJECT:</source>
|
||||||
|
<translation>PROJECT:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>TITLE:</source>
|
||||||
|
<translation>TITLE:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NOTES:</source>
|
||||||
|
<translation>NOTES:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>DATE: </source>
|
||||||
|
<translation>DATE: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>REV: </source>
|
||||||
|
<translation>REV: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>WORKING</source>
|
||||||
|
<translation>WORKING</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>DRAFT</source>
|
||||||
|
<translation>DRAFT</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>RECOMMENDED</source>
|
||||||
|
<translation>RECOMMENDED</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PUBLICATION</source>
|
||||||
|
<translation>PUBLICATION</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>READER: </source>
|
||||||
|
<translation>READER: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>CONTEXT: </source>
|
||||||
|
<translation>CONTEXT: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NODE: </source>
|
||||||
|
<translation>NODE: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NUMBER: </source>
|
||||||
|
<translation>NUMBER: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit header/footer</source>
|
||||||
|
<translation>Edit header/footer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Working</source>
|
||||||
|
<translation>Working</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Draft</source>
|
||||||
|
<translation>Draft</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recommended</source>
|
||||||
|
<translation>Recommended</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Publication</source>
|
||||||
|
<translation>Publication</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Used at:</source>
|
||||||
|
<translation>Used at:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author:</source>
|
||||||
|
<translation>Author:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Project:</source>
|
||||||
|
<translation>Project:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notes:</source>
|
||||||
|
<translation>Notes:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Date:</source>
|
||||||
|
<translation>Date:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rev:</source>
|
||||||
|
<translation>Rev:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reader:</source>
|
||||||
|
<translation>Reader:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reader date:</source>
|
||||||
|
<translation>Reader date:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Context:</source>
|
||||||
|
<translation>Context:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Node:</source>
|
||||||
|
<translation>Node:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number:</source>
|
||||||
|
<translation>Number:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>State:</source>
|
||||||
|
<translation>State:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Portrait</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>RMB: add block. LMB on port: drag arrow. Double click block: rename.</source>
|
||||||
|
<translation>RMB: add block. LMB on port: drag arrow. Double click block: rename.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call Mechanism add</source>
|
||||||
|
<translation>Call Mechanism add</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call Mechanism</source>
|
||||||
|
<translation>Call Mechanism</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select exactly one block to add a call mechanism.</source>
|
||||||
|
<translation>Select exactly one block to add a call mechanism.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a block to add a call mechanism.</source>
|
||||||
|
<translation>Select a block to add a call mechanism.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call mechanism already exists for this block.</source>
|
||||||
|
<translation>Call mechanism already exists for this block.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No eligible target blocks found.</source>
|
||||||
|
<translation>No eligible target blocks found.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Choose target block:</source>
|
||||||
|
<translation>Choose target block:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Click bottom frame to place call mechanism arrow. Esc to cancel.</source>
|
||||||
|
<translation>Click bottom frame to place call mechanism arrow. Esc to cancel.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&File</source>
|
||||||
|
<translation>&File</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New</source>
|
||||||
|
<translation>New</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open…</source>
|
||||||
|
<translation>Open…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation>Save</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save As…</source>
|
||||||
|
<translation>Save As…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export to PDF…</source>
|
||||||
|
<translation>Export to PDF…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Edit</source>
|
||||||
|
<translation>&Edit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap numbers…</source>
|
||||||
|
<translation>Swap numbers…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&View</source>
|
||||||
|
<translation>&View</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fit</source>
|
||||||
|
<translation>Fit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Scale +</source>
|
||||||
|
<translation>Scale +</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Scale -</source>
|
||||||
|
<translation>Scale -</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Colorful mode</source>
|
||||||
|
<translation>Colorful mode</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Dark mode</source>
|
||||||
|
<translation>Dark mode</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Follow system theme</source>
|
||||||
|
<translation>Follow system theme</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Arrow color...</source>
|
||||||
|
<translation>Arrow color...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block border color...</source>
|
||||||
|
<translation>Block border color...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block font color...</source>
|
||||||
|
<translation>Block font color...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Arrow color</source>
|
||||||
|
<translation>Arrow color</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block border color</source>
|
||||||
|
<translation>Block border color</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block font color</source>
|
||||||
|
<translation>Block font color</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap numbers</source>
|
||||||
|
<translation>Swap numbers</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a single block to swap its number.</source>
|
||||||
|
<translation>Select a single block to swap its number.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a block.</source>
|
||||||
|
<translation>Select a block.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No other blocks to swap with.</source>
|
||||||
|
<translation>No other blocks to swap with.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap with:</source>
|
||||||
|
<translation>Swap with:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open diagram</source>
|
||||||
|
<translation>Open diagram</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>IDEF0 Diagram (*.idef0);;JSON Diagram (*.json)</source>
|
||||||
|
<translation>IDEF0 Diagram (*.idef0);;JSON Diagram (*.json)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save failed</source>
|
||||||
|
<translation>Save failed</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not open file for writing.</source>
|
||||||
|
<translation>Could not open file for writing.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save diagram</source>
|
||||||
|
<translation>Save diagram</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save diagram as</source>
|
||||||
|
<translation>Save diagram as</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export to PDF</source>
|
||||||
|
<translation>Export to PDF</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Current diagram</source>
|
||||||
|
<translation>Current diagram</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All diagrams</source>
|
||||||
|
<translation>All diagrams</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number pages in footer (NUMBER field)</source>
|
||||||
|
<translation>Number pages in footer (NUMBER field)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export scope:</source>
|
||||||
|
<translation>Export scope:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Welcome</source>
|
||||||
|
<translation>Welcome</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Main process</source>
|
||||||
|
<translation>Main process</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Example.Org Inc.</source>
|
||||||
|
<translation>Example.Org Inc.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>John Doe</source>
|
||||||
|
<translation>John Doe</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>JD</source>
|
||||||
|
<translation>JD</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Title:</source>
|
||||||
|
<translation>Title:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Organization:</source>
|
||||||
|
<translation>Organization:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author:</source>
|
||||||
|
<translation>Author:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author's initials:</source>
|
||||||
|
<translation>Author's initials:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>General</source>
|
||||||
|
<translation>General</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Numbering</source>
|
||||||
|
<translation>Numbering</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Display</source>
|
||||||
|
<translation>Display</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seconds</source>
|
||||||
|
<translation>Seconds</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minutes</source>
|
||||||
|
<translation>Minutes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hours</source>
|
||||||
|
<translation>Hours</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Days</source>
|
||||||
|
<translation>Days</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Weeks</source>
|
||||||
|
<translation>Weeks</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Months</source>
|
||||||
|
<translation>Months</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Years</source>
|
||||||
|
<translation>Years</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Currency:</source>
|
||||||
|
<translation>Currency:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Symbol placement:</source>
|
||||||
|
<translation>Symbol placement:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Time Unit:</source>
|
||||||
|
<translation>Time Unit:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>ABC Units</source>
|
||||||
|
<translation>ABC Units</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Landscape</source>
|
||||||
|
<translation>Landscape</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Portrait</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>With header</source>
|
||||||
|
<translation>With header</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>With footer</source>
|
||||||
|
<translation>With footer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Size:</source>
|
||||||
|
<translation>Size:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Type:</source>
|
||||||
|
<translation>Type:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Page Setup</source>
|
||||||
|
<translation>Page Setup</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Start</source>
|
||||||
|
<translation>Start</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Start a new diagram or open an existing one?</source>
|
||||||
|
<translation>Start a new diagram or open an existing one?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open failed</source>
|
||||||
|
<translation>Open failed</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not open file for reading.</source>
|
||||||
|
<translation>Could not open file for reading.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File format is invalid.</source>
|
||||||
|
<translation>File format is invalid.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Diagram data missing or corrupted.</source>
|
||||||
|
<translation>Diagram data missing or corrupted.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PDF (*.pdf)</source>
|
||||||
|
<translation>PDF (*.pdf)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>IDEF0 Diagram Export</source>
|
||||||
|
<translation>IDEF0 Diagram Export</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export failed</source>
|
||||||
|
<translation>Export failed</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not initialize PDF painter.</source>
|
||||||
|
<translation>Could not initialize PDF painter.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsaved changes</source>
|
||||||
|
<translation>Unsaved changes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You have unsaved changes. Quit anyway?</source>
|
||||||
|
<translation>You have unsaved changes. Quit anyway?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Yes</source>
|
||||||
|
<translation>Yes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No</source>
|
||||||
|
<translation>No</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Untitled</source>
|
||||||
|
<translation>Untitled</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> — IDEF0 editor</source>
|
||||||
|
<translation> — IDEF0 editor</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
567
translations/idef0_fr.ts
Normal file
567
translations/idef0_fr.ts
Normal file
|
|
@ -0,0 +1,567 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="fr_FR">
|
||||||
|
<context>
|
||||||
|
<name>ArrowItem</name>
|
||||||
|
<message>
|
||||||
|
<source>Arrow label</source>
|
||||||
|
<translation>Libellé de la flèche</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Label:</source>
|
||||||
|
<translation>Libellé :</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>BlockItem</name>
|
||||||
|
<message>
|
||||||
|
<source>Function</source>
|
||||||
|
<translation>Fonction</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit block</source>
|
||||||
|
<translation>Modifier le bloc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Title:</source>
|
||||||
|
<translation>Titre :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set price</source>
|
||||||
|
<translation>Définir le prix</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Price:</source>
|
||||||
|
<translation>Prix :</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DiagramScene</name>
|
||||||
|
<message>
|
||||||
|
<source>TOP</source>
|
||||||
|
<translation>HAUT</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>A0</source>
|
||||||
|
<translation>A0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Landscape</source>
|
||||||
|
<translation>Paysage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Portrait</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>HeaderFooterItem</name>
|
||||||
|
<message>
|
||||||
|
<source>USED AT:</source>
|
||||||
|
<translation>UTILISÉ À :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>ORGANIZATION:</source>
|
||||||
|
<translation>ORGANISATION :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>AUTHOR:</source>
|
||||||
|
<translation>AUTEUR :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PROJECT:</source>
|
||||||
|
<translation>PROJET :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>TITLE:</source>
|
||||||
|
<translation>TITRE :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NOTES:</source>
|
||||||
|
<translation>NOTES :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>DATE: </source>
|
||||||
|
<translation>DATE : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>REV: </source>
|
||||||
|
<translation>RÉV. : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>WORKING</source>
|
||||||
|
<translation>EN COURS</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>DRAFT</source>
|
||||||
|
<translation>BROUILLON</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>RECOMMENDED</source>
|
||||||
|
<translation>RECOMMANDÉ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PUBLICATION</source>
|
||||||
|
<translation>PUBLICATION</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>READER: </source>
|
||||||
|
<translation>LECTEUR : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>CONTEXT: </source>
|
||||||
|
<translation>CONTEXTE : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NODE: </source>
|
||||||
|
<translation>NŒUD : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NUMBER: </source>
|
||||||
|
<translation>NUMÉRO : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit header/footer</source>
|
||||||
|
<translation>Modifier l'en-tête/pied</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Working</source>
|
||||||
|
<translation>En cours</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Draft</source>
|
||||||
|
<translation>Brouillon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recommended</source>
|
||||||
|
<translation>Recommandé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Publication</source>
|
||||||
|
<translation>Publication</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Used at:</source>
|
||||||
|
<translation>Utilisé à :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author:</source>
|
||||||
|
<translation>Auteur :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Project:</source>
|
||||||
|
<translation>Projet :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notes:</source>
|
||||||
|
<translation>Notes :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Date:</source>
|
||||||
|
<translation>Date :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rev:</source>
|
||||||
|
<translation>Rév. :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reader:</source>
|
||||||
|
<translation>Lecteur :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reader date:</source>
|
||||||
|
<translation>Date de lecture :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Context:</source>
|
||||||
|
<translation>Contexte :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Node:</source>
|
||||||
|
<translation>Nœud :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number:</source>
|
||||||
|
<translation>Numéro :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>State:</source>
|
||||||
|
<translation>État :</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Portrait</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>RMB: add block. LMB on port: drag arrow. Double click block: rename.</source>
|
||||||
|
<translation>Clic droit : ajouter un bloc. Clic gauche sur un port : tirer une flèche. Double-clic sur un bloc : renommer.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call Mechanism add</source>
|
||||||
|
<translation>Ajouter un Call Mechanism</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call Mechanism</source>
|
||||||
|
<translation>Call Mechanism</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select exactly one block to add a call mechanism.</source>
|
||||||
|
<translation>Sélectionnez exactement un bloc pour ajouter un Call Mechanism.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a block to add a call mechanism.</source>
|
||||||
|
<translation>Sélectionnez un bloc pour ajouter un Call Mechanism.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call mechanism already exists for this block.</source>
|
||||||
|
<translation>Un Call Mechanism existe déjà pour ce bloc.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No eligible target blocks found.</source>
|
||||||
|
<translation>Aucun bloc cible éligible trouvé.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Choose target block:</source>
|
||||||
|
<translation>Choisissez le bloc cible :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Click bottom frame to place call mechanism arrow. Esc to cancel.</source>
|
||||||
|
<translation>Cliquez sur le cadre inférieur pour placer la flèche Call Mechanism. Échap pour annuler.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&File</source>
|
||||||
|
<translation>&Fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New</source>
|
||||||
|
<translation>Nouveau</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open…</source>
|
||||||
|
<translation>Ouvrir…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation>Enregistrer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save As…</source>
|
||||||
|
<translation>Enregistrer sous…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export to PDF…</source>
|
||||||
|
<translation>Exporter en PDF…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Edit</source>
|
||||||
|
<translation>&Édition</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap numbers…</source>
|
||||||
|
<translation>Échanger les numéros…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&View</source>
|
||||||
|
<translation>&Affichage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fit</source>
|
||||||
|
<translation>Adapter</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Scale +</source>
|
||||||
|
<translation>Zoom +</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Scale -</source>
|
||||||
|
<translation>Zoom -</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Colorful mode</source>
|
||||||
|
<translation>Mode coloré</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Dark mode</source>
|
||||||
|
<translation>Mode sombre</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Follow system theme</source>
|
||||||
|
<translation>Suivre le thème système</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Arrow color...</source>
|
||||||
|
<translation>Couleur des flèches...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block border color...</source>
|
||||||
|
<translation>Couleur du contour du bloc...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block font color...</source>
|
||||||
|
<translation>Couleur du texte du bloc...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Arrow color</source>
|
||||||
|
<translation>Couleur des flèches</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block border color</source>
|
||||||
|
<translation>Couleur du contour du bloc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block font color</source>
|
||||||
|
<translation>Couleur du texte du bloc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap numbers</source>
|
||||||
|
<translation>Échanger les numéros</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a single block to swap its number.</source>
|
||||||
|
<translation>Sélectionnez un seul bloc pour échanger son numéro.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a block.</source>
|
||||||
|
<translation>Sélectionnez un bloc.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No other blocks to swap with.</source>
|
||||||
|
<translation>Aucun autre bloc pour l'échange.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap with:</source>
|
||||||
|
<translation>Échanger avec :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open diagram</source>
|
||||||
|
<translation>Ouvrir un diagramme</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>IDEF0 Diagram (*.idef0);;JSON Diagram (*.json)</source>
|
||||||
|
<translation>Diagramme IDEF0 (*.idef0);;Diagramme JSON (*.json)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save failed</source>
|
||||||
|
<translation>Échec de l'enregistrement</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not open file for writing.</source>
|
||||||
|
<translation>Impossible d'ouvrir le fichier en écriture.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save diagram</source>
|
||||||
|
<translation>Enregistrer le diagramme</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save diagram as</source>
|
||||||
|
<translation>Enregistrer le diagramme sous</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export to PDF</source>
|
||||||
|
<translation>Exporter en PDF</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Current diagram</source>
|
||||||
|
<translation>Diagramme actuel</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All diagrams</source>
|
||||||
|
<translation>Tous les diagrammes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number pages in footer (NUMBER field)</source>
|
||||||
|
<translation>Numéroter les pages dans le pied (champ NUMBER)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export scope:</source>
|
||||||
|
<translation>Périmètre d'export :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Welcome</source>
|
||||||
|
<translation>Bienvenue</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Main process</source>
|
||||||
|
<translation>Processus principal</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Example.Org Inc.</source>
|
||||||
|
<translation>Example.Org Inc.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>John Doe</source>
|
||||||
|
<translation>Jean Dupont</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>JD</source>
|
||||||
|
<translation>JD</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Title:</source>
|
||||||
|
<translation>Titre :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Organization:</source>
|
||||||
|
<translation>Organisation :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author:</source>
|
||||||
|
<translation>Auteur :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author's initials:</source>
|
||||||
|
<translation>Initiales de l'auteur :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>General</source>
|
||||||
|
<translation>Général</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Numbering</source>
|
||||||
|
<translation>Numérotation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Display</source>
|
||||||
|
<translation>Affichage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seconds</source>
|
||||||
|
<translation>Secondes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minutes</source>
|
||||||
|
<translation>Minutes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hours</source>
|
||||||
|
<translation>Heures</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Days</source>
|
||||||
|
<translation>Jours</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Weeks</source>
|
||||||
|
<translation>Semaines</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Months</source>
|
||||||
|
<translation>Mois</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Years</source>
|
||||||
|
<translation>Années</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Currency:</source>
|
||||||
|
<translation>Devise :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Symbol placement:</source>
|
||||||
|
<translation>Position du symbole :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Time Unit:</source>
|
||||||
|
<translation>Unité de temps :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>ABC Units</source>
|
||||||
|
<translation>Unités ABC</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Landscape</source>
|
||||||
|
<translation>Paysage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Portrait</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>With header</source>
|
||||||
|
<translation>Avec en-tête</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>With footer</source>
|
||||||
|
<translation>Avec pied de page</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Size:</source>
|
||||||
|
<translation>Taille :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Type:</source>
|
||||||
|
<translation>Orientation :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Page Setup</source>
|
||||||
|
<translation>Mise en page</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Start</source>
|
||||||
|
<translation>Démarrer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Start a new diagram or open an existing one?</source>
|
||||||
|
<translation>Créer un nouveau diagramme ou ouvrir un existant ?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open failed</source>
|
||||||
|
<translation>Échec de l'ouverture</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not open file for reading.</source>
|
||||||
|
<translation>Impossible d'ouvrir le fichier en lecture.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File format is invalid.</source>
|
||||||
|
<translation>Format de fichier invalide.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Diagram data missing or corrupted.</source>
|
||||||
|
<translation>Données de diagramme manquantes ou corrompues.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PDF (*.pdf)</source>
|
||||||
|
<translation>PDF (*.pdf)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>IDEF0 Diagram Export</source>
|
||||||
|
<translation>Export diagramme IDEF0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export failed</source>
|
||||||
|
<translation>Échec de l'export</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not initialize PDF painter.</source>
|
||||||
|
<translation>Impossible d'initialiser l'export PDF.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsaved changes</source>
|
||||||
|
<translation>Modifications non enregistrées</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You have unsaved changes. Quit anyway?</source>
|
||||||
|
<translation>Des modifications ne sont pas enregistrées. Quitter quand même ?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Yes</source>
|
||||||
|
<translation>Oui</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No</source>
|
||||||
|
<translation>Non</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Untitled</source>
|
||||||
|
<translation>Sans titre</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> — IDEF0 editor</source>
|
||||||
|
<translation> — éditeur IDEF0</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
571
translations/idef0_ru.ts
Normal file
571
translations/idef0_ru.ts
Normal file
|
|
@ -0,0 +1,571 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="ru_RU">
|
||||||
|
<context>
|
||||||
|
<name>ArrowItem</name>
|
||||||
|
<message>
|
||||||
|
<source>Arrow label</source>
|
||||||
|
<translation>Подпись стрелки</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Label:</source>
|
||||||
|
<translation>Подпись:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>BlockItem</name>
|
||||||
|
<message>
|
||||||
|
<source>Function</source>
|
||||||
|
<translation>Функция</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit block</source>
|
||||||
|
<translation>Редактировать блок</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Title:</source>
|
||||||
|
<translation>Название:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set price</source>
|
||||||
|
<translation>Указать стоимость</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Price:</source>
|
||||||
|
<translation>Стоимость:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DiagramScene</name>
|
||||||
|
<message>
|
||||||
|
<source>TOP</source>
|
||||||
|
<translation>ВЕРХ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>A0</source>
|
||||||
|
<translation>A0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Landscape</source>
|
||||||
|
<translation>Альбомная</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Портретная</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>HeaderFooterItem</name>
|
||||||
|
<message>
|
||||||
|
<source>USED AT:</source>
|
||||||
|
<translation>МЕСТО ПРИМЕНЕНИЯ:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>ORGANIZATION:</source>
|
||||||
|
<translation>ОРГАНИЗАЦИЯ:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>AUTHOR:</source>
|
||||||
|
<translation>АВТОР:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PROJECT:</source>
|
||||||
|
<translation>ПРОЕКТ:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>TITLE:</source>
|
||||||
|
<translation>НАЗВАНИЕ:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NOTES:</source>
|
||||||
|
<translation>ПРИМЕЧАНИЯ:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>DATE: </source>
|
||||||
|
<translation>ДАТА: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>REV: </source>
|
||||||
|
<translation>РЕВ.: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>WORKING</source>
|
||||||
|
<translation>В РАБОТЕ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>DRAFT</source>
|
||||||
|
<translation>ЧЕРНОВИК</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>RECOMMENDED</source>
|
||||||
|
<translation>РЕКОМЕНДОВАНО</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PUBLICATION</source>
|
||||||
|
<translation>ПУБЛИКАЦИЯ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>READER: </source>
|
||||||
|
<translation>ПРОВЕРЯЮЩИЙ: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>CONTEXT: </source>
|
||||||
|
<translation>КОНТЕКСТ: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NODE: </source>
|
||||||
|
<translation>УРОВЕНЬ: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>NUMBER: </source>
|
||||||
|
<translation>НОМЕР: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit header/footer</source>
|
||||||
|
<translation>Редактировать шапку/подвал</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Working</source>
|
||||||
|
<translation>В работе</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Draft</source>
|
||||||
|
<translation>Черновик</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recommended</source>
|
||||||
|
<translation>Рекомендовано</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Publication</source>
|
||||||
|
<translation>Публикация</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Used at:</source>
|
||||||
|
<translation>Место применения:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author:</source>
|
||||||
|
<translation>Автор:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Project:</source>
|
||||||
|
<translation>Проект:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notes:</source>
|
||||||
|
<translation>Примечания:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Date:</source>
|
||||||
|
<translation>Дата:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rev:</source>
|
||||||
|
<translation>Рев.:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reader:</source>
|
||||||
|
<translation>Проверяющий:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reader date:</source>
|
||||||
|
<translation>Дата проверки:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Context:</source>
|
||||||
|
<translation>Контекст:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Node:</source>
|
||||||
|
<translation>Уровень:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number:</source>
|
||||||
|
<translation>Номер:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>State:</source>
|
||||||
|
<translation>Состояние:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Портретная</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>RMB: add block. LMB on port: drag arrow. Double click block: rename.</source>
|
||||||
|
<translation>ПКМ: добавить блок. ЛКМ на порту: тянуть стрелку. Двойной щелчок по блоку: переименовать.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call Mechanism add</source>
|
||||||
|
<translation>Добавить Call Mechanism</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call Mechanism</source>
|
||||||
|
<translation>Call Mechanism</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select exactly one block to add a call mechanism.</source>
|
||||||
|
<translation>Выберите ровно один блок для добавления Call Mechanism.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a block to add a call mechanism.</source>
|
||||||
|
<translation>Выберите блок для добавления Call Mechanism.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Call mechanism already exists for this block.</source>
|
||||||
|
<translation>Call Mechanism уже есть у этого блока.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No eligible target blocks found.</source>
|
||||||
|
<translation>Нет подходящих целевых блоков.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Choose target block:</source>
|
||||||
|
<translation>Выберите целевой блок:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Click bottom frame to place call mechanism arrow. Esc to cancel.</source>
|
||||||
|
<translation>Нажмите на нижнюю рамку для размещения стрелки Call Mechanism. Esc — отмена.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&File</source>
|
||||||
|
<translation>&Файл</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New</source>
|
||||||
|
<translation>Новый</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open…</source>
|
||||||
|
<translation>Открыть…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation>Сохранить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save As…</source>
|
||||||
|
<translation>Сохранить как…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export to PDF…</source>
|
||||||
|
<translation>Экспорт в PDF…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Edit</source>
|
||||||
|
<translation>&Правка</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap numbers…</source>
|
||||||
|
<translation>Поменять номера…</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&View</source>
|
||||||
|
<translation>&Вид</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Plugins</source>
|
||||||
|
<translation>&Плагины</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fit</source>
|
||||||
|
<translation>Вписать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Scale +</source>
|
||||||
|
<translation>Масштаб +</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Scale -</source>
|
||||||
|
<translation>Масштаб -</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Colorful mode</source>
|
||||||
|
<translation>Цветной режим</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Dark mode</source>
|
||||||
|
<translation>Тёмный режим</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Follow system theme</source>
|
||||||
|
<translation>Следовать теме системы</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Arrow color...</source>
|
||||||
|
<translation>Цвет стрелок...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block border color...</source>
|
||||||
|
<translation>Цвет рамки блока...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block font color...</source>
|
||||||
|
<translation>Цвет текста блока...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Arrow color</source>
|
||||||
|
<translation>Цвет стрелок</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block border color</source>
|
||||||
|
<translation>Цвет рамки блока</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Block font color</source>
|
||||||
|
<translation>Цвет текста блока</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap numbers</source>
|
||||||
|
<translation>Поменять номера</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a single block to swap its number.</source>
|
||||||
|
<translation>Выберите один блок, чтобы поменять его номер.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select a block.</source>
|
||||||
|
<translation>Выберите блок.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No other blocks to swap with.</source>
|
||||||
|
<translation>Нет других блоков для обмена.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Swap with:</source>
|
||||||
|
<translation>Поменять с:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open diagram</source>
|
||||||
|
<translation>Открыть диаграмму</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>IDEF0 Diagram (*.idef0);;JSON Diagram (*.json)</source>
|
||||||
|
<translation>Диаграмма IDEF0 (*.idef0);;Диаграмма JSON (*.json)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save failed</source>
|
||||||
|
<translation>Ошибка сохранения</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not open file for writing.</source>
|
||||||
|
<translation>Не удалось открыть файл для записи.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save diagram</source>
|
||||||
|
<translation>Сохранить диаграмму</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save diagram as</source>
|
||||||
|
<translation>Сохранить диаграмму как</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export to PDF</source>
|
||||||
|
<translation>Экспорт в PDF</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Current diagram</source>
|
||||||
|
<translation>Текущая диаграмма</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All diagrams</source>
|
||||||
|
<translation>Все диаграммы</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number pages in footer (NUMBER field)</source>
|
||||||
|
<translation>Нумеровать страницы в подвале (поле NUMBER)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export scope:</source>
|
||||||
|
<translation>Область экспорта:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Welcome</source>
|
||||||
|
<translation>Добро пожаловать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Main process</source>
|
||||||
|
<translation>Основной процесс</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Example.Org Inc.</source>
|
||||||
|
<translation>Example.Org Inc.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>John Doe</source>
|
||||||
|
<translation>Иван Иванов</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>JD</source>
|
||||||
|
<translation>ИИ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Title:</source>
|
||||||
|
<translation>Название:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Organization:</source>
|
||||||
|
<translation>Организация:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author:</source>
|
||||||
|
<translation>Автор:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Author's initials:</source>
|
||||||
|
<translation>Инициалы автора:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>General</source>
|
||||||
|
<translation>Общее</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Numbering</source>
|
||||||
|
<translation>Нумерация</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Display</source>
|
||||||
|
<translation>Отображение</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seconds</source>
|
||||||
|
<translation>Секунды</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minutes</source>
|
||||||
|
<translation>Минуты</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hours</source>
|
||||||
|
<translation>Часы</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Days</source>
|
||||||
|
<translation>Дни</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Weeks</source>
|
||||||
|
<translation>Недели</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Months</source>
|
||||||
|
<translation>Месяцы</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Years</source>
|
||||||
|
<translation>Годы</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Currency:</source>
|
||||||
|
<translation>Валюта:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Symbol placement:</source>
|
||||||
|
<translation>Расположение символа:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Time Unit:</source>
|
||||||
|
<translation>Единица времени:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>ABC Units</source>
|
||||||
|
<translation>Единицы ABC</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Landscape</source>
|
||||||
|
<translation>Альбомная</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Portrait</source>
|
||||||
|
<translation>Портретная</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>With header</source>
|
||||||
|
<translation>С заголовком</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>With footer</source>
|
||||||
|
<translation>С подвалом</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Size:</source>
|
||||||
|
<translation>Размер:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Type:</source>
|
||||||
|
<translation>Ориентация:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Page Setup</source>
|
||||||
|
<translation>Параметры страницы</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Start</source>
|
||||||
|
<translation>Начать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Start a new diagram or open an existing one?</source>
|
||||||
|
<translation>Начать новую диаграмму или открыть существующую?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open failed</source>
|
||||||
|
<translation>Ошибка открытия</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not open file for reading.</source>
|
||||||
|
<translation>Не удалось открыть файл для чтения.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File format is invalid.</source>
|
||||||
|
<translation>Недопустимый формат файла.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Diagram data missing or corrupted.</source>
|
||||||
|
<translation>Данные диаграммы отсутствуют или повреждены.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>PDF (*.pdf)</source>
|
||||||
|
<translation>PDF (*.pdf)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>IDEF0 Diagram Export</source>
|
||||||
|
<translation>Экспорт диаграммы IDEF0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export failed</source>
|
||||||
|
<translation>Ошибка экспорта</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not initialize PDF painter.</source>
|
||||||
|
<translation>Не удалось инициализировать вывод в PDF.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsaved changes</source>
|
||||||
|
<translation>Несохраненные изменения</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You have unsaved changes. Quit anyway?</source>
|
||||||
|
<translation>Есть несохраненные изменения. Выйти?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Yes</source>
|
||||||
|
<translation>Да</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No</source>
|
||||||
|
<translation>Нет</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Untitled</source>
|
||||||
|
<translation>Без имени</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> — IDEF0 editor</source>
|
||||||
|
<translation> — редактор IDEF0</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue