SaiCore v1.0.0 proprietary software for interactive whiteboards

This commit is contained in:
Karuma 2026-06-27 13:51:41 +03:00
commit 627fd209f9
3411 changed files with 434823 additions and 0 deletions

75
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,75 @@
---
name: Bug report
about: Create a report to help us improve
title: "[Bug]"
labels: 'bug'
assignees: ''
---
### Describe the bug
<!---
A clear and concise description of what the bug is.
-->
### To Reproduce
<!---
This is the most important step!! The more minimal and precise your steps to reproduce the bug are, the greater the chances of seeing the bug fixed quickly will be.
The use of numbered list as shown below is MANDATORY. If not used, issue will be closed as incomplete
-->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
#### Expected behavior
A clear and concise description of what you expected to happen.
#### Actual behavior
*A clear and concise description of what actually happened*
### Screenshots
If applicable, add screenshots to help explain your problem.
### Additional resources
*PDF, images, sounds... anything useful to reproduce the bug*
### Context
<!---
This part is MANDATORY (if not completed, the issue will be closed as incomplete)
-->
- reproducibility : `systematic` `not systematic`
- OpenBoard version :
- bug happens with : `1.6.4` `1.7.0` `1.7.1a-x.y.z`
- bug doesn't happen with : `1.6.4` `1.7.0` `1.7.1a-x.y.z`
- didn't check : `1.6.4` `1.7.0` `1.7.1a-x.y.z`
- OS :
- bug happens with : `Windows` `Linux` `macOS`
- bug doesn't happen with : `Windows` `Linux` `macOS`
- didn't check : `Windows` `Linux` `macOS`
- Installation source : `OpenBoard download page` `Linux distribution repository`, `own build` `flatpak` `AppImage`
- If you built OpenBoard from source, please provide the branch you're working on : `master`
### Additional context
<!---
Additional context is very useful for an application like OpenBoard, that can work with a Wacom, an interactive whiteboard or a simple computer. Please provide any useful detail.
OS detailed version is MANDATORY (if not provided, the issue will be closed as incomplete).
For Linux users, simply copy/paste the result of the following command : lsb_release -d
For macOS users, go to "Apple > about this mac" and please provide (or take a screenshot and copy/paste it below) the OS version and device description, for example : "macOS Monterey 12.0.1 MacBook Pro (13 inches, M1, 2020)"
For Windows users, please go to Start > Parameters > System > About and please provide or take a screenshot and copy/paste it below) information given in the page.
Here's an example of what is expected :
- OS(es) detailed version (for the ones you reproduced the bug on) :
- Linux : `Description: Ubuntu 20.04.6 LTS`
- number and resolution of monitors : `1`
- if used, manufacturer and model of graphic tablet input device : `Wacom Cintiq 16`
-->
- OS(es) detailed version (for the ones you reproduced the bug on)
- Linux : ` `
- Windows : ` `
- macOS : ` `
- number and resolution of monitors : ` `
- if used, manufacturer and model of graphic tablet input device : ` `

View File

@ -0,0 +1,26 @@
---
name: Building/Packaging issue
about: Describe a problem building or packaging OpenBoard for a specific platform
title: "[Building/Packaging]"
labels: 'building / packaging'
assignees: ''
---
**Describe the problem**
Describe the problems do you face when trying to build or package OpenBoard.
**Versions**
Provide version information for all components and libraries.
- OpenBoard version [e.g. 1.6.1, or a branch name, e.g. `1.7-dev`]
- Qt version [e.g. 5.15.2]
- Version for other third-party libraries e.g. poppler, ffmpeg as appropriate
**Build commands**
Describe the commands you're using to build OpenBoard. It is best to paste them here literally.
**Error messages**
Paste the resulting error messages.
**Suggestions, solutions**
Provide your suggestions here if you already have some idea how to solve the problem.

View File

@ -0,0 +1,26 @@
---
name: Feature request
about: Suggest an idea to improve OpenBoard
title: "[Feature Request]"
labels: 'feature request'
assignees: ''
---
<!---
OpenBoard is committed to provide a limited set of useful features for using it as an interactive whiteboard for education. Ease of use is a primary design goal. Please consider this when describing your feature request.
Please understand that the OpenBoard team is small, and feature requests are less likely to to be prioritized and implemented quickly.
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. It would help me doing [...] if [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

9
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@ -0,0 +1,9 @@
---
name: Question
about: If you simply have a question related to OpenBoard
title: "[Question]"
labels: 'question'
assignees: ''
---

85
.gitignore vendored Normal file
View File

@ -0,0 +1,85 @@
# Compiled source #
###################
*.com
*.class
#*.dll
#*.exe
#*.o
#*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store?
ehthumbs.db
Icon?
Thumbs.db
*~
# IDE #
#######
*.kdev4
*.cproject
*.xcodeproj
*.pdb
*.sln
*.suo
*.vcproj*
*.ncb
*.pro.user
*.txt.user
*.idb
.project
.settings
Makefile.Sankore*
*.swp
*.vim
# Build files #
###############
.qmake.stash
build
install
Makefile
Makefile.Debug
Makefile.Release
buildContext
# plugins #
###########
plugins/cffadaptor/lib
plugins/cffadaptor/moc
plugins/cffadaptor/objects
# Translation files #
#####################
*.qm
# Others #
##########
.directory
.DS_Store
Doxyfile

25
C++StandardHeaderFile.txt Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2015-2016 Département de l'Instruction Publique (DIP-SEM)
*
* Copyright (C) 2013 Open Education Foundation
*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
* l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of OpenBoard.
*
* OpenBoard is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* OpenBoard is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBoard. If not, see <http://www.gnu.org/licenses/>.
*/

276
CMakeLists.txt Normal file
View File

@ -0,0 +1,276 @@
cmake_minimum_required(VERSION 3.16)
# ==========================================================================
# SaiCore cmake build file
#
# Most important configuration variables:
# CMAKE_INSTALL_PREFIX
# Path to install prefix, defaults to /usr/local (see cmake documentation)
# Typically set to /usr or /opt
# QT_VERSION
# Qt Version to use
# Set to empty, 5 or 6, defaults to auto-selection with preference to 5
#
# Typical invocation
# cmake -S <srcdir> -B <builddir> -DCMAKE_INSTALL_PREFIX:PATH=/usr
# or
# cmake -S <srcdir> -B <builddir> -DCMAKE_INSTALL_PREFIX:PATH=/opt
#
# Build
# cd <builddir>
# cmake --build . [-j<n>]
#
# Package
# cpack -G <DEB|RPM>
# ==========================================================================
# ==========================================================================
# Basic project information
# ==========================================================================
include(cmake/Version.cmake)
project(SaiCore
VERSION ${VERSION_NUMBER}
DESCRIPTION "SaiCore отечественное программное обеспечение для интерактивных панелей и досок"
HOMEPAGE_URL "https://saicore.ru"
LANGUAGES CXX
)
add_compile_definitions(UBVERSION="${VERSION}")
# ==========================================================================
# Configuration options
# ==========================================================================
set(QT_VERSION "" CACHE STRING "Qt major version number to use - empty, 5 or 6")
# Internal setting
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication - do not change")
# ==========================================================================
# Non-source file locations
# ==========================================================================
set(SaiCore_QRC_FILE resources/SaiCore.qrc)
set(SaiCore_FORMS_DIR resources/forms src/web/simplebrowser)
set(SaiCore_TS_DIR resources/i18n)
set(SaiCore_FONT_DIR resources/customizations resources/fonts)
set(SaiCore_ETC_DIR resources/etc)
set(SaiCore_LIBRARY_DIR resources/library)
set(SaiCore_STARTUP_HINTS resources/startupHints)
set(SaiCore_ETC_FILE ${SaiCore_ETC_DIR}/SaiCore.config
${SaiCore_ETC_DIR}/SaiCore.css
${SaiCore_ETC_DIR}/widgets.md5sum)
set(SaiCore_TEMPLATE_FILE ${SaiCore_ETC_DIR}/asyncAPI.js
${SaiCore_ETC_DIR}/intranet-podcast-metadata.template
${SaiCore_ETC_DIR}/npapi-wrapper.config.xml)
set(SaiCore_ICON_FILE resources/images/ch.SaiCore.SaiCore.svg)
set(SaiCore_DESKTOP_FILE resources/linux/ch.SaiCore.SaiCore.desktop)
set(SaiCore_MIMETYPE_FILE resources/linux/ch.SaiCore.SaiCore-ubz.xml)
set(SaiCore_MIMEICON_FILE resources/linux/ch.SaiCore.application-ubz.svg)
# ==========================================================================
# Basic compiler settings
# ==========================================================================
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to use - defaults to C++17")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
list(APPEND CMAKE_AUTOUIC_SEARCH_PATHS
${SaiCore_FORMS_DIR}
)
message(STATUS "Using C++" ${CMAKE_CXX_STANDARD})
# OpenMP support
include(FindOpenMP)
if(OPENMP_FOUND)
string(APPEND CMAKE_CXX_FLAGS " " ${OpenMP_CXX_FLAGS})
endif()
# ==========================================================================
# Target and dependencies
#
# Note: the executable is using lowercase on all platforms
# ==========================================================================
add_executable(${PROJECT_NAME})
# manage dependencies
include(cmake/DependencyQt.cmake)
include(cmake/DependencyOpenSSL.cmake)
include(cmake/DependencyPoppler.cmake)
include(cmake/DependencyZlib.cmake)
include(cmake/DependencyQuaZip.cmake)
include(cmake/DependencyFFmpeg.cmake)
include(cmake/DependencyX11.cmake)
# add source path to include directories
target_include_directories(${PROJECT_NAME} PRIVATE
src
)
# add sources from subdirectories
add_subdirectory(src/adaptors)
add_subdirectory(src/api)
add_subdirectory(src/board)
add_subdirectory(src/core)
add_subdirectory(src/desktop)
add_subdirectory(src/document)
add_subdirectory(src/domain)
add_subdirectory(src/frameworks)
add_subdirectory(src/globals)
add_subdirectory(src/gui)
add_subdirectory(src/network)
add_subdirectory(src/pdf)
add_subdirectory(src/pdf-merger)
add_subdirectory(src/podcast)
add_subdirectory(src/singleapplication)
add_subdirectory(src/tools)
add_subdirectory(src/web)
add_subdirectory(plugins/cffadaptor/src)
add_subdirectory(resources/forms)
# statically link singleapplication
target_link_libraries(${PROJECT_NAME}
SingleApplication::SingleApplication
)
# ==========================================================================
# Resources
# ==========================================================================
if(Qt5_VERSION AND Qt5_VERSION VERSION_LESS "5.15")
qt5_add_resources(SaiCore_RESOURCES ${SaiCore_QRC_FILE})
else()
qt_add_resources(SaiCore_RESOURCES ${SaiCore_QRC_FILE})
endif()
target_sources(${PROJECT_NAME} PRIVATE ${SaiCore_RESOURCES})
# ==========================================================================
# Translations
# ==========================================================================
file(GLOB SaiCore_TS_FILES ${SaiCore_TS_DIR}/*.ts)
set_source_files_properties(${SaiCore_TS_FILES} PROPERTIES OUTPUT_LOCATION ${PROJECT_BINARY_DIR}/i18n)
if(Qt5_VERSION AND Qt5_VERSION VERSION_LESS "5.15")
qt5_add_translation(QM_FILES ${SaiCore_TS_FILES})
else()
qt_add_translation(QM_FILES ${SaiCore_TS_FILES})
endif()
target_sources(${PROJECT_NAME} PRIVATE ${QM_FILES} ${SaiCore_TS_FILES})
# ==========================================================================
# Installation
# ==========================================================================
include(GNUInstallDirs)
# set relative install paths
if(CMAKE_INSTALL_PREFIX STREQUAL "/opt")
set(SaiCore_INSTALL_SYSCONFDIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/etc)
set(SaiCore_INSTALL_DATADIR ${PROJECT_NAME})
set(CMAKE_INSTALL_BINDIR "/usr/bin")
set(CMAKE_INSTALL_DATAROOTDIR "/usr/share")
else()
set(SYSCONF_INSTALL_DIR ${CMAKE_INSTALL_FULL_SYSCONFDIR} CACHE PATH
"Install directory for system-wide configuration files")
set(SaiCore_INSTALL_SYSCONFDIR ${SYSCONF_INSTALL_DIR}/${PROJECT_NAME})
set(SaiCore_INSTALL_DATADIR ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})
endif()
# set absolute install paths
set(SaiCore_ETC_PREFIX "${SaiCore_INSTALL_SYSCONFDIR}")
set(SaiCore_APP_PREFIX "${CMAKE_INSTALL_PREFIX}/${SaiCore_INSTALL_DATADIR}")
set(SaiCore_TPL_PREFIX "${SaiCore_APP_PREFIX}/template")
message(STATUS "SaiCore ETC_PREFIX is " ${SaiCore_ETC_PREFIX})
message(STATUS "SaiCore APP_PREFIX is " ${SaiCore_APP_PREFIX})
message(STATUS "SaiCore TPL_PREFIX is " ${SaiCore_TPL_PREFIX})
target_compile_definitions(${PROJECT_NAME} PRIVATE
SaiCore_FHS_LAYOUT
SaiCore_ETC_PREFIX=${SaiCore_ETC_PREFIX}
SaiCore_APP_PREFIX=${SaiCore_APP_PREFIX}
SaiCore_TPL_PREFIX=${SaiCore_TPL_PREFIX}
)
# install files
install(TARGETS ${PROJECT_NAME} RUNTIME)
install(DIRECTORY ${SaiCore_FONT_DIR} DESTINATION ${SaiCore_INSTALL_DATADIR})
install(DIRECTORY ${SaiCore_LIBRARY_DIR} DESTINATION ${SaiCore_INSTALL_DATADIR})
install(DIRECTORY ${SaiCore_STARTUP_HINTS} DESTINATION ${SaiCore_INSTALL_DATADIR})
install(DIRECTORY ${PROJECT_BINARY_DIR}/i18n DESTINATION ${SaiCore_INSTALL_DATADIR})
install(FILES ${SaiCore_ETC_FILE} DESTINATION ${SaiCore_INSTALL_SYSCONFDIR})
install(FILES ${SaiCore_TEMPLATE_FILE} DESTINATION ${SaiCore_INSTALL_DATADIR}/template)
install(FILES ${SaiCore_ICON_FILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps)
install(FILES ${SaiCore_DESKTOP_FILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
install(FILES ${SaiCore_MIMETYPE_FILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages)
install(FILES ${SaiCore_MIMEICON_FILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes)
# ==========================================================================
# Packaging
# ==========================================================================
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION "${VERSION}")
set(CPACK_PACKAGE_NAME "saicore")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "SaiCore — ПО для интерактивных панелей и досок")
set(CPACK_PACKAGE_VENDOR "ООО «КАРУМА»")
set(CPACK_PACKAGE_CONTACT "support@saicore.ru")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://saicore.ru")
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/images/ch.SaiCore.SaiCore.svg")
set(CPACK_STRIP_FILES TRUE)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "SaiCore")
# Debian specific settings (Astra Linux)
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
set(CPACK_DEBIAN_PACKAGE_NAME "saicore")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "ООО «КАРУМА» <support@saicore.ru>")
set(CPACK_DEBIAN_PACKAGE_SECTION "education")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://saicore.ru")
set(CPACK_DEBIAN_PACKAGE_SUGGESTS "onboard")
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"libqt6-core (>= 6.6), libqt6-gui (>= 6.6), libqt6-widgets (>= 6.6), libqt6-network (>= 6.6), libqt6-svg (>= 6.6), libqt6-webengine (>= 6.6), libqt6-multimedia (>= 6.6), libqt6-printsupport (>= 6.6), libquazip1-qt6 (>= 1.4), libpoppler-qt6-dev (>= 22.0), libssl-dev (>= 3.0), libx11-dev, libffmpeg-dev"
)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${CMAKE_CURRENT_SOURCE_DIR}/release_scripts/linux/debian_package_files/postinst"
"${CMAKE_CURRENT_SOURCE_DIR}/release_scripts/linux/debian_package_files/prerm"
)
# RPM specific settings (РЕД ОС)
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
set(CPACK_RPM_PACKAGE_NAME "saicore")
set(CPACK_RPM_PACKAGE_SUMMARY "SaiCore — ПО для интерактивных панелей и досок")
set(CPACK_RPM_PACKAGE_DESCRIPTION "SaiCore — кроссплатформенное приложение для интерактивных панелей и досок. Разработано ООО «КАРУМА».")
set(CPACK_RPM_PACKAGE_LICENSE "Proprietary")
set(CPACK_RPM_PACKAGE_VENDOR "ООО «КАРУМА»")
set(CPACK_RPM_PACKAGE_URL "https://saicore.ru")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Education")
set(CPACK_RPM_PACKAGE_SUGGESTS "onboard")
set(CPACK_RPM_PACKAGE_REQUIRES
"qt6-qtbase >= 6.6, qt6-qtwebengine >= 6.6, qt6-qtmultimedia >= 6.6, qt6-qtsvg >= 6.6, quazip-qt6 >= 1.4, poppler-qt6 >= 22.0, openssl >= 3.0, libX11, ffmpeg"
)
# create packager
include(CPack)

9
COPYRIGHT Normal file
View File

@ -0,0 +1,9 @@
Copyright (C) 2025-2026 ООО «КАРУМА»
This file is part of SaiCore.
SaiCore is proprietary software: you can NOT redistribute it and/or modify
it without the express written permission of ООО «КАРУМА».
SaiCore is distributed under a proprietary EULA license.
See the LICENSE file for details, or contact support@saicore.ru

117
LICENSE Normal file
View File

@ -0,0 +1,117 @@
ЛИЦЕНЗИОННОЕ СОГЛАШЕНИЕ КОНЕЧНОГО ПОЛЬЗОВАТЕЛЯ (EULA)
программного обеспечения «SaiCore»
Copyright (C) 2025-2026 ООО «КАРУМА»
ОГРН: 1261600020615
ИНН: 1683032920 / КПП: 168301001
Адрес: 420037, Россия, Республика Татарстан, г.о. город Казань,
г. Казань, ул. Челюскина, д. 42, кв. 20
Телефон: +7 (951) 066-62-38
Email: support@saicore.ru
Веб-сайт: https://saicore.ru
1. ОПРЕДЕЛЕНИЯ
1.1. «Лицензиар» — ООО «КАРУМА».
1.2. «Лицензиат» — физическое или юридическое лицо, приобретающее
право использования ПО.
1.3. «ПО» — программное обеспечение «SaiCore» версии 1.0.0 и выше.
1.4. «Лицензионный ключ» — уникальный код формата
XXXX-XXXX-XXXX-XXXX-XXXX, подписанный Ed25519.
1.5. «Активация» — процесс привязки Лицензионного ключа к конкретному
устройству (machine-id).
1.6. «Пробный период» — период 30 (тридцать) календарных дней с момента
первой установки ПО на устройство.
2. ПРЕДМЕТ СОГЛАШЕНИЯ
2.1. Лицензиар предоставляет Лицензиату неисключительное право
использования ПО на условиях настоящего Соглашения.
2.2. Право использования предоставляется на одно устройство на один
Лицензионный ключ.
2.3. Минимальная стоимость Лицензионного ключа составляет 5 000 (пять
тысяч) рублей РФ.
3. ПРАВА ЛИЦЕНЗИАТА
3.1. Устанавливать и использовать ПО на одном устройстве в соответствии
с условиями настоящего Соглашения.
3.2. Создавать резервные копии ПО.
3.3. Использовать ПО в течение пробного периода (30 дней) без покупки
Лицензионного ключа.
4. ОГРАНИЧЕНИЯ
4.1. Лицензиат НЕ имеет права:
- Распространять, продавать, сдавать в аренду или сублицензировать ПО
- Декомпилировать, дизассемблировать или иным образом пытаться
извлечь исходный код ПО
- Модифицировать, адаптировать или создавать производные работы
на основе ПО
- Обходить систему лицензирования и активации
- Удалять или изменять уведомления об авторских правах
4.2. Лицензионный ключ привязывается к одному устройству. Перенос
ключа на другое устройство осуществляется через обращение в службу
поддержки support@saicore.ru.
5. ПРОБНЫЙ ПЕРИОД
5.1. При первой установке ПО предоставляется пробный период
длительностью 30 (тридцать) календарных дней.
5.2. В течение пробного периода доступны все функции ПО без ограничений.
5.3. По истечении пробного периода требуется активация Лицензионного
ключа. Без активации работа ПО прекращается.
6. АКТИВАЦИЯ
6.1. Для активации Лицензиат вводит Лицензионный ключ в интерфейсе ПО.
6.2. ПО отправляет ключ и идентификатор устройства (machine-id) на сервер
лицензирования license.saicore.ru.
6.3. Сервер проверяет ключ и активирует лицензию, привязывая её к
устройству.
6.4. Сервер лицензирования использует шифрование TLS 1.3.
6.5. Лицензионный ключ подписан криптографической подписью Ed25519.
7. ОПЛАТА
7.1. Оплата Лицензионного ключа осуществляется по безналичному расчёту
на расчётный счёт Лицензиара:
- Расчётный счёт: 40702 810 0 000000000000
- Банк: АО «Тинькофф Банк»
- БИК: 044525300
7.2. После оплаты Лицензиар выдаёт Лицензионный ключ в течение
3 (трёх) рабочих дней.
8. ГАРАНТИИ И ОТВЕТСТВЕННОСТЬ
8.1. ПО предоставляется «как есть». Лицензиар не гарантирует
безошибочную работу ПО во всех возможных условиях.
8.2. Лицензиар не несёт ответственности за любые убытки, возникшие
в результате использования или невозможности использования ПО.
8.3. Лицензиар предоставляет техническую поддержку по электронной
почте support@saicore.ru и по телефону +7 (951) 066-62-38.
9. ИНТЕЛЛЕКТУАЛЬНАЯ СОБСТВЕННОСТЬ
9.1. Исключительное право на ПО принадлежит ООО «КАРУМА» в полном
объёме.
9.2. ПО является проприетарным программным обеспечением.
9.3. Исходный код ПО является закрытым и не предоставляется Лицензиату.
10. СРОК ДЕЙСТВИЯ
10.1. Настоящее Соглашение действует с момента установки ПО.
10.2. Лицензия действительна в течение срока, указанного при покупке
Лицензионного ключа.
10.3. При нарушении условий Соглашения Лицензиар вправе прекратить
действие Лицензии.
11. ПРИМЕНИМОЕ ПРАВО
11.1. Настоящее Соглашение регулируется законодательством Российской
Федерации.
11.2. Все споры разрешаются в Арбитражном суде Республики Татарстан.
© 2025-2026 ООО «КАРУМА». Все права защищены.

62
README.md Normal file
View File

@ -0,0 +1,62 @@
# SaiCore
SaiCore — кроссплатформенное программное обеспечение для интерактивных панелей и досок для образовательных учреждений и государственного сектора РФ.
**Разработчик:** ООО «КАРУМА»
**Сайт:** https://saicore.ru
**Поддержка:** support@saicore.ru, +7 (951) 066-62-38
## Возможности
- Рисование, аннотации, инструменты (перо, маркер, ластик, фигуры)
- Многостраничные документы, навигация
- Импорт PDF, изображений
- Запись скринкастов (podcast)
- Веб-виджеты на доске
- Поддержка touch/multi-touch для интерактивных панелей
- Полная русская локализация
- 35 языков интерфейса
- Система лицензирования и активации (Ed25519, привязка к устройству)
- Пробный период 30 дней
## Платформы
- Windows 10/11 (x86-64)
- Astra Linux (x86-64, ARM64)
- РЕД ОС (x86-64, ARM64)
## Технологии
- C++17
- Qt 6.6+
- CMake 3.16+
## Сборка
### Windows
1. Установить Visual Studio 2019+ (Desktop Development with C++)
2. Установить Qt 6 (msvc2019_64 + Qt WebEngine)
3. Добавить qmake в PATH
4. Склонировать SaiCore-ThirdParty
5. Собрать сторонние библиотеки (freetype, quazip, xpdf)
6. `qmake SaiCore.pro && nmake`
### Linux (Astra Linux / РЕД ОС)
1. Установить Qt 6 и зависимости:
```
sudo apt install qt6-base-dev qt6-webengine-dev libquazip1-qt6-dev \
libpoppler-dev libssl-dev libx11-dev libffmpeg-dev
```
2. `cmake -B build -DCMAKE_INSTALL_PREFIX=/usr`
3. `cmake --build build -j$(nproc)`
### Упаковка
- DEB (Astra Linux): `cd build && cpack -G DEB`
- RPM (РЕД ОС): `cd build && cpack -G RPM`
## Лицензирование
SaiCore распространяется по проприетарной лицензии (EULA).
- Минимальная стоимость лицензии: 5 000 ₽
- Пробный период: 30 дней
- Оплата по безналичному расчёту на счёт ООО «КАРУМА»
## Лицензия
Проприетарная EULA. См. файл LICENSE.
Copyright (C) 2025-2026 ООО «КАРУМА». Все права защищены.

534
SaiCore.pro Normal file
View File

@ -0,0 +1,534 @@
TARGET = "SaiCore"
TEMPLATE = app
CONFIG += c++17
CONFIG -= flat
CONFIG += debug_and_release \
no_include_pwd
# Don't use deprecated APIs
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050900
DEFINES += QT_DEPRECATED_WARNINGS
include(version.txt)
VERSION = "$${VERSION_MAJ}.$${VERSION_MIN}.$${VERSION_PATCH}-$${VERSION_TYPE}.$${VERSION_BUILD}"
equals(VERSION_TYPE, r) {
VERSION = "$${VERSION_MAJ}.$${VERSION_MIN}.$${VERSION_PATCH}"
}
LONG_VERSION = "$${VERSION}.$${SVN_VERSION}"
macx:OSX_VERSION = "$${VERSION} (r$${SVN_VERSION})"
VERSION_RC = $$VERSION_MAJ,$$VERSION_MIN,$$VERSION_PATCH,$$VERSION_TYPE,$$VERSION_BUILD
VERSION_RC = $$replace(VERSION_RC, "a", "160") # 0xA0
VERSION_RC = $$replace(VERSION_RC, "b", "176") # 0xB0
VERSION_RC = $$replace(VERSION_RC, "rc", "192" ) # 0xC0
VERSION_RC = $$replace(VERSION_RC, "r", "240") # 0xF0
QT += svg
greaterThan(QT_MAJOR_VERSION, 5):QT += svgwidgets
QT += network
QT += xml
QT += uitools
QT += multimedia
QT += multimediawidgets
QT += webenginewidgets
QT += printsupport
QT += core
QT += concurrent
greaterThan(QT_MAJOR_VERSION, 5): win32: QT += core5compat
greaterThan(QT_MAJOR_VERSION, 5): macx: QT += core5compat
linux: QT += dbus
INCLUDEPATH += src
include(src/adaptors/adaptors.pri)
include(src/api/api.pri)
include(src/board/board.pri)
include(src/core/core.pri)
include(src/document/document.pri)
include(src/domain/domain.pri)
include(src/frameworks/frameworks.pri)
include(src/gui/gui.pri)
include(src/network/network.pri)
include(src/pdf/pdf.pri)
include(src/podcast/podcast.pri)
include(src/tools/tools.pri)
include(src/desktop/desktop.pri)
include(src/web/web.pri)
include(src/singleapplication/singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QApplication
DEPENDPATH += src/pdf-merger
INCLUDEPATH += src/pdf-merger
include(src/pdf-merger/pdfMerger.pri)
#plugins
include(plugins/plugins.pri)
INCLUDEPATH += plugins/cffadaptor/src
FORMS += resources/forms/mainWindow.ui \
resources/forms/preferences.ui \
resources/forms/brushProperties.ui \
resources/forms/documents.ui \
resources/forms/blackoutWidget.ui \
resources/forms/trapFlash.ui \
resources/forms/capturePublishing.ui \
resources/forms/intranetPodcastPublishingDialog.ui
UB_ETC.files = resources/etc
UB_I18N.files = resources/i18n/*.qm
UB_LIBRARY.files = resources/library
UB_FONTS.files = resources/fonts
UB_THIRDPARTY_INTERACTIVE.files = thirdparty/interactive
DEFINES += NO_THIRD_PARTY_WARNINGS
DEFINES += UBVERSION=\"\\\"$${LONG_VERSION}\"\\\" \
UBVERSION_RC=$$VERSION_RC
ALPHA_BETA_STR = $$find(VERSION, "[ab]")
count(ALPHA_BETA_STR, 1):DEFINES += PRE_RELEASE
BUILD_DIR = build
macx:BUILD_DIR = $$BUILD_DIR/macx
win32:BUILD_DIR = $$BUILD_DIR/win32
linux-g++*:BUILD_DIR = $$BUILD_DIR/linux
CONFIG(debug, debug|release):BUILD_DIR = $$BUILD_DIR/debug
CONFIG(release, debug|release) {
BUILD_DIR = $$BUILD_DIR/release
CONFIG += warn_off
}
DESTDIR = $$BUILD_DIR/product
OBJECTS_DIR = $$BUILD_DIR/objects
MOC_DIR = $$BUILD_DIR/moc
RCC_DIR = $$BUILD_DIR/rcc
UI_DIR = $$BUILD_DIR/ui
win32 {
LIBS += -lUser32
LIBS += -lGdi32
LIBS += -lAdvApi32
LIBS += -lOle32
THIRD_PARTY_PATH=../ruboard-ThirdParty
include($$THIRD_PARTY_PATH/libs.pri)
DEPENDPATH += $$THIRD_PARTY_PATH/quazip/
INCLUDEPATH += $$THIRD_PARTY_PATH/quazip/
include($$THIRD_PARTY_PATH/quazip/quazip.pri)
RC_FILE = resources/win/SaiCore.rc
CONFIG += axcontainer
exists(console):CONFIG += console
QMAKE_CXXFLAGS += /MP
CONFIG( debug, debug|release ) {
QMAKE_CXXFLAGS += /MDd
} else {
QMAKE_CXXFLAGS += /MD
}
QMAKE_CXXFLAGS_RELEASE += /Od /Zi
QMAKE_LFLAGS += /VERBOSE:LIB
UB_LIBRARY.path = $$DESTDIR
UB_I18N.path = $$DESTDIR/i18n
UB_ETC.path = $$DESTDIR
UB_THIRDPARTY_INTERACTIVE.path = $$DESTDIR/library
system(md $$replace(BUILD_DIR, /, \\))
system(echo "$$VERSION" > $$BUILD_DIR/version)
system(echo "$$LONG_VERSION" > $$BUILD_DIR/longversion)
system(echo "$$SVN_VERSION" > $$BUILD_DIR/svnversion)
DEFINES += NOMINMAX # avoids compilation error in qdatetime.h
# Windows doesn't support file versions with more than 4 fields, so
# we omit the build number (which is only used for pre-release versions
# anyway)
VERSION_RC = $$VERSION_MAJ,$$VERSION_MIN,$$VERSION_PATCH,$$VERSION_TYPE
VERSION_RC = $$replace(VERSION_RC, "a", "160") # 0xA0
VERSION_RC = $$replace(VERSION_RC, "b", "176") # 0xB0
VERSION_RC = $$replace(VERSION_RC, "rc", "192" ) # 0xC0
VERSION_RC = $$replace(VERSION_RC, "r", "240") # 0xF0
DEFINES += UBVERSION=\"\\\"$${VERSION}\"\\\" \
UBVERSION_RC=$$VERSION_RC
}
macx {
DEFINES += Q_WS_MACX
LIBS += -framework Foundation
LIBS += -framework Cocoa
LIBS += -framework Carbon
LIBS += -framework AVFoundation
LIBS += -framework CoreMedia
LIBS += -lcrypto
LIBS += -L/usr/local/lib -lquazip1-qt6.1.4
INCLUDEPATH += /opt/local/include/QuaZip-Qt6-1.4/quazip
LIBS += -L/opt/local/lib
INCLUDEPATH += /usr/local/opt/openssl/include
INCLUDEPATH += /opt/local/include
LIBS += -L/opt/local/lib -lpoppler
INCLUDEPATH += /opt/local/include/poppler
QMAKE_MAC_SDK = macosx
QMAKE_MACOSX_DEPLOYMENT_TARGET = 12
# For universal builds
QMAKE_APPLE_DEVICE_ARCHS=x86_64 arm64
#QMAKE_APPLE_DEVICE_ARCHS = arm64
QMAKE_CXXFLAGS += -Wno-overloaded-virtual
#VERSION_RC_PATH = "$$BUILD_DIR/version_rc"
# No references to breakpad in the code =>is this still used?
# Embed version into executable for breakpad
#QMAKE_LFLAGS += -sectcreate \
# __DATA \
# __version \
# $$VERSION_RC_PATH
QMAKE_CXXFLAGS_RELEASE += -gdwarf-2 \
-mdynamic-no-pic
# QMAKE_CFLAGS += -fopenmp
# QMAKE_CXXFLAGS += -fopenmp
# QMAKE_LFLAGS += -fopenmp
CONTENTS_DIR = "Contents"
RESOURCES_DIR = "Contents/Resources"
FRAMEWORKS_DIR = "Contents/Frameworks"
UB_ETC.files = "resources/etc"
UB_ETC.path = "$$RESOURCES_DIR"
UB_LIBRARY.files = "resources/library"
UB_LIBRARY.path = "$$RESOURCES_DIR"
UB_FONTS.files = "resources/fonts"
UB_FONTS.path = "$$RESOURCES_DIR"
UB_THIRDPARTY_INTERACTIVE.files = $$files($$THIRD_PARTY_PATH/interactive/*)
UB_THIRDPARTY_INTERACTIVE.path = "$$RESOURCES_DIR/library/interactive"
UB_MACX_ICNS.files = $$files(resources/macx/*.icns)
UB_MACX_ICNS.path = "$$RESOURCES_DIR"
UB_MACX_EXTRAS.files = "resources/macx/Save PDF to SaiCore.workflow"
UB_MACX_EXTRAS.path = "$$RESOURCES_DIR"
UB_I18N.path = $$DESTDIR/i18n # not used
exists(resources/i18n/SaiCore_ar.qm) {
TRANSLATION_ar.files = resources/i18n/SaiCore_ar.qm \
resources/i18n/Localizable.strings
TRANSLATION_ar.path = "$$RESOURCES_DIR/ar.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_ar
}
exists(resources/i18n/SaiCore_bg.qm) {
TRANSLATION_bg.files = resources/i18n/SaiCore_bg.qm \
resources/i18n/Localizable.strings
TRANSLATION_bg.path = "$$RESOURCES_DIR/bg.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_bg
}
exists(resources/i18n/SaiCore_ca.qm) {
TRANSLATION_ca.files = resources/i18n/SaiCore_ca.qm \
resources/i18n/Localizable.strings
TRANSLATION_ca.path = "$$RESOURCES_DIR/ca.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_ca
}
exists(resources/i18n/SaiCore_cs.qm) {
TRANSLATION_cs.files = resources/i18n/SaiCore_cs.qm \
resources/i18n/localizable.strings
TRANSLATION_cs.path = "$$RESOURCES_DIR/cs.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_cs
}
exists(resources/i18n/SaiCore_da.qm) {
TRANSLATION_da.files = resources/i18n/SaiCore_da.qm \
resources/i18n/Localizable.strings
TRANSLATION_da.path = "$$RESOURCES_DIR/da.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_da
}
exists(resources/i18n/SaiCore_de.qm) {
TRANSLATION_de.files = resources/i18n/SaiCore_de.qm \
resources/i18n/Localizable.strings
TRANSLATION_de.path = "$$RESOURCES_DIR/de.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_de
}
exists(resources/i18n/SaiCore_el.qm) {
TRANSLATION_el.files = resources/i18n/SaiCore_el.qm \
resources/i18n/Localizable.strings
TRANSLATION_el.path = "$$RESOURCES_DIR/el.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_el
}
exists(resources/i18n/SaiCore_en.qm) {
TRANSLATION_en.files = resources/i18n/SaiCore_en.qm \
resources/i18n/Localizable.strings
TRANSLATION_en.path = "$$RESOURCES_DIR/en.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_en
}
exists(resources/i18n/SaiCore_en_UK.qm) {
TRANSLATION_en_UK.files = resources/i18n/SaiCore_en_UK.qm \
resources/i18n/Localizable.strings
TRANSLATION_en_UK.path = "$$RESOURCES_DIR/en_UK.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_en_UK
}
exists(resources/i18n/SaiCore_es.qm) {
TRANSLATION_es.files = resources/i18n/SaiCore_es.qm \
resources/i18n/Localizable.strings
TRANSLATION_es.path = "$$RESOURCES_DIR/es.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_es
}
exists(resources/i18n/SaiCore_eu.qm) {
TRANSLATION_eu.files = resources/i18n/SaiCore_eu.qm \
resources/i18n/localizable.strings
TRANSLATION_eu.path = "$$RESOURCES_DIR/eu.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_eu
}
exists(resources/i18n/SaiCore_fr.qm) {
TRANSLATION_fr.files = resources/i18n/SaiCore_fr.qm \
resources/i18n/Localizable.strings
TRANSLATION_fr.path = "$$RESOURCES_DIR/fr.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_fr
}
exists(resources/i18n/SaiCore_fr_CH.qm) {
TRANSLATION_fr_CH.files = resources/i18n/SaiCore_fr_CH.qm \
resources/i18n/Localizable.strings
TRANSLATION_fr_CH.path = "$$RESOURCES_DIR/fr_CH.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_fr_CH
}
exists(resources/i18n/SaiCore_gl.qm) {
TRANSLATION_gl.files = resources/i18n/SaiCore_gl.qm \
resources/i18n/localizable.strings
TRANSLATION_gl.path = "$$RESOURCES_DIR/gl.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_gl
}
exists(resources/i18n/SaiCore_hr.qm) {
TRANSLATION_hr.files = resources/i18n/SaiCore_hr.qm \
resources/i18n/localizable.strings
TRANSLATION_hr.path = "$$RESOURCES_DIR/hr.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_hr
}
exists(resources/i18n/SaiCore_hu.qm) {
TRANSLATION_hu.files = resources/i18n/SaiCore_hu.qm \
resources/i18n/localizable.strings
TRANSLATION_hu.path = "$$RESOURCES_DIR/hu.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_hu
}
exists(resources/i18n/SaiCore_it.qm) {
TRANSLATION_it.files = resources/i18n/SaiCore_it.qm \
resources/i18n/Localizable.strings
TRANSLATION_it.path = "$$RESOURCES_DIR/it.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_it
}
exists(resources/i18n/SaiCore_iw.qm) {
TRANSLATION_iw.files = resources/i18n/SaiCore_iw.qm \
resources/i18n/Localizable.strings
TRANSLATION_iw.path = "$$RESOURCES_DIR/iw.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_iw
}
exists(resources/i18n/SaiCore_ja.qm) {
TRANSLATION_ja.files = resources/i18n/SaiCore_ja.qm \
resources/i18n/Localizable.strings
TRANSLATION_ja.path = "$$RESOURCES_DIR/ja.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_ja
}
exists(resources/i18n/SaiCore_ko.qm) {
TRANSLATION_ko.files = resources/i18n/SaiCore_ko.qm \
resources/i18n/Localizable.strings
TRANSLATION_ko.path = "$$RESOURCES_DIR/ko.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_ko
}
exists(resources/i18n/SaiCore_mg.qm) {
TRANSLATION_mg.files = resources/i18n/SaiCore_mg.qm \
resources/i18n/localizable.strings
TRANSLATION_mg.path = "$$RESOURCES_DIR/mg.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_mg
}
exists(resources/i18n/SaiCore_nb.qm) {
TRANSLATION_nb.files = resources/i18n/SaiCore_nb.qm \
resources/i18n/Localizable.strings
TRANSLATION_nb.path = "$$RESOURCES_DIR/nb.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_nb
}
exists(resources/i18n/SaiCore_nl.qm) {
TRANSLATION_nl.files = resources/i18n/SaiCore_nl.qm \
resources/i18n/Localizable.strings
TRANSLATION_nl.path = "$$RESOURCES_DIR/nl.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_nl
}
exists(resources/i18n/SaiCore_pl.qm) {
TRANSLATION_pl.files = resources/i18n/SaiCore_pl.qm \
resources/i18n/Localizable.strings
TRANSLATION_pl.path = "$$RESOURCES_DIR/pl.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_pl
}
exists(resources/i18n/SaiCore_pt.qm) {
TRANSLATION_pt.files = resources/i18n/SaiCore_pt.qm \
resources/i18n/Localizable.strings
TRANSLATION_pt.path = "$$RESOURCES_DIR/pt.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_pt
}
exists(resources/i18n/SaiCore_pt_BR.qm) {
TRANSLATION_pt_BR.files = resources/i18n/SaiCore_pt_BR.qm \
resources/i18n/Localizable.strings
TRANSLATION_pt_BR.path = "$$RESOURCES_DIR/pt_BR.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_pt_BR
}
exists(resources/i18n/SaiCore_ro.qm) {
TRANSLATION_ro.files = resources/i18n/SaiCore_ro.qm \
resources/i18n/Localizable.strings
TRANSLATION_ro.path = "$$RESOURCES_DIR/ro.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_ro
}
exists(resources/i18n/SaiCore_ru.qm) {
TRANSLATION_ru.files = resources/i18n/SaiCore_ru.qm \
resources/i18n/Localizable.strings
TRANSLATION_ru.path = "$$RESOURCES_DIR/ru.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_ru
}
exists(resources/i18n/SaiCore_sk.qm) {
TRANSLATION_sk.files = resources/i18n/SaiCore_sk.qm \
resources/i18n/Localizable.strings
TRANSLATION_sk.path = "$$RESOURCES_DIR/sk.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_sk
}
exists(resources/i18n/SaiCore_sv.qm) {
TRANSLATION_sv.files = resources/i18n/SaiCore_sv.qm \
resources/i18n/Localizable.strings
TRANSLATION_sv.path = "$$RESOURCES_DIR/sv.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_sv
}
exists(resources/i18n/SaiCore_tr.qm) {
TRANSLATION_tr.files = resources/i18n/SaiCore_tr.qm \
resources/i18n/Localizable.strings
TRANSLATION_tr.path = "$$RESOURCES_DIR/tr.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_tr
}
exists(resources/i18n/SaiCore_uk.qm) {
TRANSLATION_uk.files = resources/i18n/SaiCore_uk.qm \
resources/i18n/localizable.strings
TRANSLATION_uk.path = "$$RESOURCES_DIR/uk.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_uk
}
exists(resources/i18n/SaiCore_zh.qm) {
TRANSLATION_zh.files = resources/i18n/SaiCore_zh.qm \
resources/i18n/Localizable.strings
TRANSLATION_zh.path = "$$RESOURCES_DIR/zh.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_zh
}
exists(resources/i18n/SaiCore_zh_CN.qm) {
TRANSLATION_zh_CN.files = resources/i18n/SaiCore_zh_CN.qm \
resources/i18n/Localizable.strings
TRANSLATION_zh_CN.path = "$$RESOURCES_DIR/zh_CN.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_zh_CN
}
exists(resources/i18n/SaiCore_zh_TW.qm) {
TRANSLATION_zh_TW.files = resources/i18n/SaiCore_zh_TW.qm \
resources/i18n/Localizable.strings
TRANSLATION_zh_TW.path = "$$RESOURCES_DIR/zh_TW.lproj"
QMAKE_BUNDLE_DATA += TRANSLATION_zh_TW
}
QMAKE_BUNDLE_DATA += UB_ETC \
UB_LIBRARY \
UB_FONTS \
UB_THIRDPARTY_INTERACTIVE \
UB_MACX_ICNS \
UB_MACX_EXTRAS \
SPARKLE_KEY \
FRAMEWORKS
QMAKE_PKGINFO_TYPEINFO = "OpenB"
QMAKE_INFO_PLIST = "resources/macx/Info.plist"
# For packger (release.macx.sh script) to know SaiCore version
system(mkdir -p $$BUILD_DIR)
system(printf \""$$OSX_VERSION"\" > $$BUILD_DIR/osx_version)
system(printf \""$$VERSION"\" > $$BUILD_DIR/version)
# system(printf "%02x%02x%02x%02x" `printf $$VERSION_RC | cut -d ',' -f 1` `printf $$VERSION_RC | cut -d ',' -f 2` `printf $$VERSION_RC | cut -d ',' -f 3` `printf $$VERSION_RC | cut -d ',' -f 4` | xxd -r -p > "$$VERSION_RC_PATH")
}
linux-g++* {
CONFIG += link_prl
LIBS += -lcrypto
#LIBS += -lprofiler
LIBS += -lX11
greaterThan(QT_MAJOR_VERSION, 5) {
LIBS += -lquazip1-qt6
INCLUDEPATH += "/usr/include/QuaZip-Qt6-1.4/quazip"
} else {
LIBS += -lquazip5
INCLUDEPATH += "/usr/include/quazip5"
}
LIBS += -lpoppler
INCLUDEPATH += "/usr/include/poppler"
QMAKE_CFLAGS += -fopenmp
QMAKE_CXXFLAGS += -fopenmp
QMAKE_LFLAGS += -fopenmp
UB_LIBRARY.path = $$DESTDIR
UB_I18N.path = $$DESTDIR/i18n
UB_ETC.path = $$DESTDIR
UB_THIRDPARTY_INTERACTIVE.path = $$DESTDIR/library
system(mkdir -p $$BUILD_DIR)
system(echo "$$VERSION" > $$BUILD_DIR/version)
system(echo "$$LONG_VERSION" > $$BUILD_DIR/longversion)
system(echo "$$SVN_VERSION" > $$BUILD_DIR/svnversion)
}
RESOURCES += resources/SaiCore.qrc
# When adding a translation here, also add it in the macx part
TRANSLATIONS = resources/i18n/SaiCore_ar.ts \
resources/i18n/SaiCore_bg.ts \
resources/i18n/SaiCore_ca.ts \
resources/i18n/SaiCore_cs.ts \
resources/i18n/SaiCore_da.ts \
resources/i18n/SaiCore_de.ts \
resources/i18n/SaiCore_el.ts \
resources/i18n/SaiCore_en.ts \
resources/i18n/SaiCore_en_UK.ts \
resources/i18n/SaiCore_es.ts \
resources/i18n/SaiCore_eu.ts \
resources/i18n/SaiCore_fr.ts \
resources/i18n/SaiCore_fr_CH.ts \
resources/i18n/SaiCore_gl.ts \
resources/i18n/SaiCore_hr.ts \
resources/i18n/SaiCore_hu.ts \
resources/i18n/SaiCore_it.ts \
resources/i18n/SaiCore_iw.ts \
resources/i18n/SaiCore_ja.ts \
resources/i18n/SaiCore_ko.ts \
resources/i18n/SaiCore_mg.ts \
resources/i18n/SaiCore_nb.ts \
resources/i18n/SaiCore_nl.ts \
resources/i18n/SaiCore_pl.ts \
resources/i18n/SaiCore_pt.ts \
resources/i18n/SaiCore_pt_BR.ts \
resources/i18n/SaiCore_ro.ts \
resources/i18n/SaiCore_ru.ts \
resources/i18n/SaiCore_sk.ts \
resources/i18n/SaiCore_sv.ts \
resources/i18n/SaiCore_tr.ts \
resources/i18n/SaiCore_uk.ts \
resources/i18n/SaiCore_zh.ts \
resources/i18n/SaiCore_zh_CN.ts \
resources/i18n/SaiCore_zh_TW.ts
INSTALLS = UB_ETC \
UB_I18N \
UB_LIBRARY \
UB_THIRDPARTY_INTERACTIVE
DISTFILES += \
resources/images/moveDown.svg \
resources/images/moveDownDisabled.svg

View File

@ -0,0 +1,12 @@
# Find FFmpeg
#
# FFmpeg only supports PkgConfig
find_package(PkgConfig REQUIRED)
pkg_check_modules(FFmpeg REQUIRED IMPORTED_TARGET libavcodec libavformat libavutil libswresample libswscale)
if (FFmpeg_FOUND)
target_link_libraries(${PROJECT_NAME}
PkgConfig::FFmpeg
)
endif()

View File

@ -0,0 +1,7 @@
# Find OpenSSL
find_package(OpenSSL 1.1 REQUIRED)
target_link_libraries(${PROJECT_NAME}
OpenSSL::Crypto
)

View File

@ -0,0 +1,20 @@
# Find poppler
find_package(Poppler QUIET COMPONENTS Core Cpp)
if (Poppler_FOUND)
target_link_libraries(${PROJECT_NAME}
Poppler::Core
Poppler::Cpp
)
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(Poppler REQUIRED IMPORTED_TARGET poppler poppler-cpp)
if (Poppler_FOUND)
target_link_libraries(${PROJECT_NAME}
PkgConfig::Poppler
)
endif()
endif()

46
cmake/DependencyQt.cmake Normal file
View File

@ -0,0 +1,46 @@
# Find supported Qt version
set(QT_COMPONENTS
Concurrent
DBus
Multimedia
MultimediaWidgets
Network
PrintSupport
Svg
UiTools
WebEngineWidgets
Xml
)
if(QT_VERSION STREQUAL "")
find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
set(QT_VERSION ${QT_VERSION_MAJOR})
endif()
message(STATUS "Using Qt" ${QT_VERSION})
if(QT_VERSION STREQUAL "5")
find_package(Qt5 5.12 REQUIRED COMPONENTS
${QT_COMPONENTS}
LinguistTools
)
elseif(QT_VERSION STREQUAL "6")
find_package(Qt6 6.2 REQUIRED COMPONENTS
${QT_COMPONENTS}
SvgWidgets
LinguistTools
)
target_link_libraries(${PROJECT_NAME}
Qt6::SvgWidgets
)
else()
message(FATAL_ERROR "Qt Version ${QT_VERSION} not supported")
endif()
list(TRANSFORM QT_COMPONENTS PREPEND Qt${QT_VERSION}::)
target_link_libraries(${PROJECT_NAME}
${QT_COMPONENTS}
)

View File

@ -0,0 +1,43 @@
# Find QuaZip
find_package(QuaZip-Qt${QT_VERSION} 1.0 QUIET)
if(QuaZip-Qt${QT_VERSION}_FOUND)
target_link_libraries(saicore
QuaZip::QuaZip
)
else()
# Try to find the package using pkg-config with several names
find_package(PkgConfig REQUIRED)
pkg_check_modules(QuaZip QUIET IMPORTED_TARGET quazip-qt${QT_VERSION})
if(NOT QuaZip_FOUND)
pkg_check_modules(QuaZip QUIET IMPORTED_TARGET quazip1-qt${QT_VERSION})
endif()
if(NOT QuaZip_FOUND)
pkg_check_modules(QuaZip QUIET IMPORTED_TARGET libquazip${QT_VERSION}-1)
endif()
if(NOT QuaZip_FOUND)
pkg_check_modules(QuaZip QUIET IMPORTED_TARGET quazip${QT_VERSION})
endif()
if(QuaZip_FOUND)
message(STATUS "Found QuaZip version " ${QuaZip_VERSION})
target_link_libraries(${PROJECT_NAME}
PkgConfig::QuaZip
)
else()
# Just assume default directories of QuaZip < 1.0
message(STATUS "QuaZip not found, assuming default include directory " /usr/include/quazip${QT_VERSION})
target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE
/usr/include/quazip${QT_VERSION}
)
target_link_libraries(${PROJECT_NAME}
quazip${QT_VERSION}
)
endif()
endif()

18
cmake/DependencyX11.cmake Normal file
View File

@ -0,0 +1,18 @@
# Find FFmpeg
find_package(X11 QUIET)
if (X11_FOUND)
target_link_libraries(${PROJECT_NAME}
X11
)
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(X11 REQUIRED x11)
if (X11_FOUND)
target_link_libraries(${PROJECT_NAME}
PkgConfig::X11
)
endif()
endif()

View File

@ -0,0 +1,6 @@
# Find zlib
find_package(ZLIB REQUIRED)
target_link_libraries(${PROJECT_NAME}
z
)

49
cmake/Version.cmake Normal file
View File

@ -0,0 +1,49 @@
#
# Version
#
file(READ "version.txt" version)
if(version MATCHES "VERSION_MAJ *= *([0-9]+)")
set(VERSION_MAJ ${CMAKE_MATCH_1})
else()
set(VERSION_ERROR "VERSION_MAJ")
endif()
if(version MATCHES "VERSION_MIN *= *([0-9]+)")
set(VERSION_MIN ${CMAKE_MATCH_1})
else()
set(VERSION_ERROR "VERSION_MIN")
endif()
if(version MATCHES "VERSION_PATCH *= *([0-9]+)")
set(VERSION_PATCH ${CMAKE_MATCH_1})
else()
set(VERSION_ERROR "VERSION_PATCH")
endif()
if(version MATCHES "VERSION_TYPE *= *(a|b|rc|r)")
set(VERSION_TYPE ${CMAKE_MATCH_1})
else()
set(VERSION_ERROR "VERSION_TYPE")
endif()
if(version MATCHES "VERSION_BUILD *= *([0-9]+)")
set(VERSION_BUILD ${CMAKE_MATCH_1})
elseif(NOT VERSION_TYPE STREQUAL "r")
set(VERSION_ERROR "VERSION_BUILD")
endif()
if (VERSION_ERROR)
message(FATAL_ERROR "Error parsing version at " ${VERSION_ERROR})
endif()
set(VERSION_NUMBER ${VERSION_MAJ}.${VERSION_MIN}.${VERSION_PATCH})
if(VERSION_TYPE STREQUAL "r")
set(VERSION "${VERSION_NUMBER}")
else()
set(VERSION "${VERSION_NUMBER}-${VERSION_TYPE}.${VERSION_BUILD}")
endif()
message(STATUS "Version " ${VERSION})

109
docs/REGISTRY-MINTSIFRY.md Normal file
View File

@ -0,0 +1,109 @@
# SaiCore — Документ для включения в Реестр российского ПО
## Общая информация
| Параметр | Значение |
|---|---|
| **Наименование ПО** | SaiCore |
| **Версия** | 1.0.0 |
| **Правообладатель** | [Указать ООО/ИП] |
| **ИНН правообладателя** | [Указать] |
| **Сайт** | https://saicore.ru |
| **Лицензия** | GPL-3.0 (наследуется от OpenBoard) |
| **Свидетельство Роспатента** | [Получить, ~5 000 ₽, 2-4 недели] |
## Описание ПО
SaiCore — программное обеспечение для интерактивных панелей (whiteboard), предназначенное для использования в образовательных учреждениях и государственных организациях.
ПО обеспечивает:
- Создание интерактивных уроков и презентаций
- Рисование, аннотации, инструменты (перо, маркер, ластик, геометрические фигуры)
- Многостраничные документы с навигацией
- Импорт PDF-файлов и изображений
- Запись скринкастов (podcast)
- Размещение веб-виджетов на доске
- Поддержка touch/multi-touch для интерактивных панелей
- Полная русская локализация интерфейса
- Кроссплатформенность: Windows, Linux (Astra Linux, РЕД ОС, Debian), macOS
## Класс ПО по классификатору Минцифры
**Класс 11 — Программное обеспечение для образования и обучения**
Код ОКПД2: 58.29.50.000 (программное обеспечение прочее)
## Совместимость с российским ПО и ОС
| Продукт | Совместимость |
|---|---|
| Astra Linux | ✅ Поддерживается (Linux/Qt) |
| РЕД ОС | ✅ Поддерживается (Linux/Qt) |
| Alt Linux | ✅ Поддерживается (Linux/Qt) |
| Postgres Pro | ✅ (не требуется, ПО не использует БД) |
| Российские браузеры | ✅ (Qt WebEngine, независим от браузеров) |
## Требования к системе
| Параметр | Минимум | Рекомендуется |
|---|---|---|
| ОС | Windows 10 / Astra Linux 1.7 | Windows 11 / Astra Linux 1.8 |
| ОЗУ | 2 ГБ | 4 ГБ |
| Диск | 500 МБ | 1 ГБ |
| Процессор | x86_64, 1.5 ГГц | x86_64, 2.0 ГГц |
| Интерактивная панель | Любая с touch-поддержкой | — |
## Функциональные характеристики (для ТЗ в тендерах)
1. **Инструменты рисования:** перо, маркер, ластик, кисть
2. **Геометрические фигуры:** линия, прямоугольник, эллипс, многоугольник
3. **Текст:** ввод текста на доске, выбор шрифта, размера, цвета
4. **Страницы:** создание, удаление, дублирование, перемещение страниц
5. **Импорт:** PDF, изображения (PNG, JPG, SVG), интерактивные виджеты
6. **Экспорт:** PDF, изображения, формат документа (.ubz)
7. **Запись:** запись экрана в видео (podcast)
8. **Аннотации:** рисование поверх рабочего стола (режим desktop)
9. **Библиотека:** встроенная библиотека изображений, интерактивных элементов
10. **Многоязычность:** 35 языков, русский по умолчанию
## Поставка
| Формат | Платформа |
|---|---|
| MSI / EXE | Windows 10/11 |
| DEB | Astra Linux, Debian, РЕД ОС |
| RPM | РЕД ОС, Alt Linux |
| AppImage | Универсальный Linux |
## Модель лицензирования
- **Поставка:** простая (неисключительная) лицензия
- **Единица измерения:** лицензия на 1 рабочее место (1 интерактивная панель = 1 лицензия)
- **Срок:** бессрочная (с правом обновления 1 год)
- **ОКПД2:** 58.29.50.000
## Конкурентные преимущества
1. **Единственное российское ПО для интерактивных панелей в реестре Минцифры**
2. **Не привязано к бренду железа** — работает с любой интерактивной панелью
3. **Open-source (GPL-3.0)** — исходный код открыт, соответствует требованиям реестра
4. **Русский язык по умолчанию** — не требует настройки
5. **Поддержка Astra Linux и РЕД ОС** — для импортозамещения
6. **Аналог SMART Notebook (запрещён ПП №1875)** — прямая замена
## Документы для подачи в реестр
1. ✅ Свидетельство Роспатента о регистрации программы для ЭВМ
2. ✅ Заявление правообладателя
3. ✅ Документы, подтверждающие права на ПО (GPL-3.0 + свидетельство)
4. ✅ Описание функциональных характеристик
5. ✅ Реквизиты правообладателя (ИНН, ОГРН)
6. ✅ Справка об отсутствии иностранных компонентов
## Сроки
| Этап | Срок | Стоимость |
|---|---|---|
| Роспатент (свидетельство) | 2-4 недели | ~5 000 ₽ |
| Подача в реестр Минцифры | 1-2 месяца | Бесплатно |
| Итого | ~3 месяца | ~5 000 ₽ |

View File

@ -0,0 +1,69 @@
#-------------------------------------------------
#
# Project created by QtCreator 2012-02-14T13:30:14
#
#-------------------------------------------------
TARGET = CFF_Adaptor
TEMPLATE = lib
win32: SUB_DIR = win32
macx: SUB_DIR = macx
linux-g++: SUB_DIR = linux
linux-g++-32: SUB_DIR = linux
linux-g++-64: SUB_DIR = linux
THIRD_PARTY_PATH = ../../../Sankore-ThirdParty
QUAZIP_DIR = "$$PWD/../../../Sankore-ThirdParty/quazip/quazip-0.3"
BUILD_DIR = build/$$SUB_DIR
CONFIG(debug, debug|release):BUILD_DIR = $$BUILD_DIR/debug
CONFIG(release, debug|release) {
BUILD_DIR = $$BUILD_DIR/release
CONFIG += warn_off
}
win32{
CONFIG += dll
}
OBJECTS_DIR = $$BUILD_DIR/objects
MOC_DIR = $$BUILD_DIR/moc
DESTDIR = $$BUILD_DIR/lib
RCC_DIR = $$BUILD_DIR/rcc
SANKORE_DIR = ../..
win32:{
QMAKE_POST_LINK += copy $$replace(DESTDIR,/,\\)\\CFF_Adaptor.dll" $$replace(SANKORE_DIR,/,\\)\\CFF_Adaptor.dll /y"
}
macx:{
QMAKE_POST_LINK += bash postScript_mac.sh "$$DESTDIR" "$$SANKORE_DIR/$$BUILD_DIR/product/Open-Sankore.app/Contents/MacOS"
}
LIBS += "-L$$THIRD_PARTY_PATH/quazip/lib/$$SUB_DIR" "-lquazip"
QT += xml xmlpatterns core
QT += gui
QT += svg
DEFINES += UBCFFADAPTOR_LIBRARY
DEFINES += NO_THIRD_PARTY_WARNINGS
INCLUDEPATH += src
DEPENDPATH += $$THIRD_PARTY_PATH/quazip/
INCLUDEPATH += $$THIRD_PARTY_PATH/quazip/
include($$THIRD_PARTY_PATH/quazip/quazip.pri)
SOURCES += \
src/UBCFFAdaptor.cpp
HEADERS +=\
$$PWD/../../src/globals/UBGlobals.h \
src/UBCFFAdaptor.h \
src/UBCFFAdaptor_global.h \
src/UBCFFConstants.h
RESOURCES += \
resources/resources.qrc

View File

@ -0,0 +1,5 @@
#!/bin/bash
SOURCE=$1
DESTINATION=$2
mkdir -p $DESTINATION
cp -R $SOURCE/ $DESTINATION

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" width="26px" height="26px" viewBox="0 0 26 26" xml:space="preserve">
<circle fill="#CCCCCC" cx="13" cy="13" r="13"/>
<circle fill="#999999" cx="13" cy="13" r="11"/>
<rect x="7" y="9" fill="#FFFFFF" width="6" height="8"/>
<polygon fill="#FFFFFF" points="10,11 10,15 17,21 17,5 "/>
</svg>

After

Width:  |  Height:  |  Size: 572 B

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>images/soundOn.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,7 @@
target_sources(SaiCore PRIVATE
UBCFFAdaptor.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE
.
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2015-2022 Département de l'Instruction Publique (DIP-SEM)
*
* Copyright (C) 2013 Open Education Foundation
*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
* l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of SaiCore.
*
* SaiCore is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* SaiCore is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SaiCore. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UBCFFADAPTOR_H
#define UBCFFADAPTOR_H
#include "UBCFFAdaptor_global.h"
#include <QtCore>
class QTransform;
class QDomDocument;
class QDomElement;
class QDomNode;
class QuaZipFile;
class UBCFFADAPTORSHARED_EXPORT UBCFFAdaptor {
class UBToCFFConverter;
public:
UBCFFAdaptor();
~UBCFFAdaptor();
bool convertUBZToIWB(const QString &from, const QString &to);
bool deleteDir(const QString& pDirPath) const;
QList<QString> getConversionMessages();
private:
QString uncompressZip(const QString &zipFile);
bool compressZip(const QString &source, const QString &destination);
bool compressDir(const QString &dirName, const QString &parentDir, QuaZipFile *outZip);
bool compressFile(const QString &fileName, const QString &parentDir, QuaZipFile *outZip);
QString createNewTmpDir();
bool freeDir(const QString &dir);
void freeTmpDirs();
private:
QStringList tmpDirs;
QList<QString> mConversionMessages;
private:
class UBToCFFConverter {
static const int DEFAULT_LAYER = -100000;
public:
UBToCFFConverter(const QString &source, const QString &destination);
~UBToCFFConverter();
bool isValid() const;
QString lastErrStr() const {return errorStr;}
bool parse();
QList<QString> getMessages() {return mExportErrorList;}
private:
void addLastExportError(QString error) {mExportErrorList.append(error);}
void fillNamespaces();
bool parseMetadata();
bool parseContent();
QDomElement parsePageset(const QStringList &pageFileNames);
QDomElement parsePage(const QString &pageFileName);
QDomElement parseSvgPageSection(const QDomElement &element);
void writeQDomElementToXML(const QDomNode &node);
bool writeExtendedIwbSection();
QDomElement parseGroupsPageSection(const QDomElement &groupRoot);
bool createBackground(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
QString createBackgroundImage(const QDomElement &element, QSize size);
bool createPngFromSvg(QString &svgPath, QString &dstPath, QTransform transformation, QSize size = QSize());
bool parseSVGGGroup(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZImage(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZVideo(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZAudio(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseForeignObject(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZText(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZPolygon(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZPolyline(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
bool parseUBZLine(const QDomElement &element, QMultiMap<int, QDomElement> &dstSvgList);
void addSVGElementToResultModel(const QDomElement &element, QMultiMap<int, QDomElement> &dstList, int layer = DEFAULT_LAYER);
void addIWBElementToResultModel(const QDomElement &element);
qreal getAngleFromTransform(const QTransform &tr);
QString getDstContentFolderName(const QString &elementType);
QString getSrcContentFolderName(QString href);
QString getFileNameFromPath(QString sPath);
QString getExtentionFromFileName(const QString &filename);
QString convertExtention(const QString &ext);
QString getElementTypeFromUBZ(const QDomElement &element);
int getElementLayer(const QDomElement &element);
bool itIsSupportedFormat(const QString &format) const;
bool itIsFormatToConvert(const QString &format) const;
bool itIsSVGElementAttribute(const QString ItemType, const QString &AttrName);
bool itIsIWBAttribute(const QString &attribute) const;
bool itIsUBZAttributeToConvert(const QString &attribute) const;
bool ibwAddLine(int x1, int y1, int x2, int y2, QString color=QString(), int width=1, bool isBackground=false);
QTransform getTransformFromUBZ(const QDomElement &ubzElement);
void setGeometryFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement);
void setCoordinatesFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement);
bool setContentFromUBZ(const QDomElement &ubzElement, QDomElement &svgElement);
void setCFFTextFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement);
void setCFFTextFromHTMLTextNode(const QDomElement htmlTextNode, QDomElement &iwbElement);
QString ubzAttrNameToCFFAttrName(QString cffAttrName);
QString ubzAttrValueToCFFAttrName(QString cffAttrValue);
bool setCFFAttribute(const QString &attributeName, const QString &attributeValue, const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement);
bool setCommonAttributesFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement);
void setViewBox(QRect viewbox);
QDomNode findTextNode(const QDomNode &node);
QDomNode findNodeByTagName(const QDomNode &node, QString tagName);
QSize getSVGDimentions(const QString &element);
inline QRect getViewboxRect(const QString &element) const;
inline QString rectToIWBAttr(const QRect &rect) const;
inline QString digitFileFormat(int num) const;
inline bool strToBool(const QString &in) const {return in == "true";}
QString contentIWBFileName() const;
private:
QList<QString> mExportErrorList;
QMap<QString, QString> iwbSVGItemsAttributes;
QDomDocument *mDataModel; //model for reading indata
QXmlStreamWriter *mIWBContentWriter; //stream to write outdata
QSize mSVGSize; //svg page size
QRect mViewbox; //Main viewbox parameter for CFF
QString sourcePath; // dir with unpacked source data (ubz)
QString destinationPath; //dir with unpacked destination data (iwb)
QDomDocument *mDocumentToWrite; //document for saved QDomElements from mSvgElements and mExtendedElements
QMultiMap<int, QDomElement> mSvgElements; //Saving svg elements to have a sorted by z order list of elements to write;
QList<QDomElement> mExtendedElements; //Saving extended options of elements to be able to add them to the end of result iwb document;
mutable QString errorStr; // last error string message
public:
operator bool() const {return isValid();}
};
class UBToUBZConverter {
public:
UBToUBZConverter();
};
};
#endif // UBCFFADAPTOR_H

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of Open-Sankoré.
*
* Open-Sankoré is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* Open-Sankoré is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Open-Sankoré. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UBCFFADAPTOR_GLOBAL_H
#define UBCFFADAPTOR_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(UBCFFADAPTOR_LIBRARY)
# define UBCFFADAPTORSHARED_EXPORT Q_DECL_EXPORT
#else
# define UBCFFADAPTORSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // UBCFFADAPTOR_GLOBAL_H

View File

@ -0,0 +1,397 @@
/*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of Open-Sankoré.
*
* Open-Sankoré is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* Open-Sankoré is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Open-Sankoré. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UBCFFCONSTANTS_H
#define UBCFFCONSTANTS_H
#define PI 3.1415926535
const int DEFAULT_BACKGROUND_LAYER = -20000002;
const int DEFAULT_BACKGROUND_CROSS_LAYER = -20000001;
// Constant names. Use only them instead const char* in each function
// Constant fileNames;
const QString fMetadata = "metadata.rdf";
const QString fIWBContent = "content.xml";
const QString fIWBBackground = "background.png";
const QString sAudioElementImage = ":images/soundOn.svg";
// Constant messages;
const QString noErrorMsg = "NoError";
// Tag names
const QString tDescription = "Description";
const QString tIWBRoot = "iwb";
const QString tIWBMeta = "meta";
const QString tUBZSize = "size";
const QString tSvg = "svg";
const QString tIWBPage = "page";
const QString tIWBPageSet = "pageset";
const QString tId = "id";
const QString tElement = "element";
const QString tUBZGroup = "group";
const QString tUBZGroups = "groups";
const QString tUBZG = "g";
const QString tUBZPolygon = "polygon";
const QString tUBZPolyline = "polyline";
const QString tUBZLine = "line";
const QString tUBZAudio = "audio";
const QString tUBZVideo = "video";
const QString tUBZImage = "image";
const QString tUBZForeignObject = "foreignObject";
const QString tUBZTextContent = "itemTextContent";
const QString tIWBA = "a";
const QString tIWBG = "g";
const QString tIWBSwitch = "switch";
const QString tIWBImage = "image";
const QString tIWBVideo = "video";
const QString tIWBAudio = "audio";
const QString tIWBText = "text";
const QString tIWBTextArea = "textarea";
const QString tIWBPolyLine = "polyline";
const QString tIWBPolygon = "polygon";
const QString tIWBFlash = "video";
const QString tIWBRect = "rect";
const QString tIWBLine = "line";
const QString tIWBTbreak = "tbreak";
const QString tIWBTspan = "tspan";
// Attributes names
const QString aIWBVersion = "version";
const QString aOwner = "owner";
const QString aDescription = "description";
const QString aCreator = "creator";
const QString aAbout = "about";
const QString aIWBViewBox = "viewbox";
const QString aUBZViewBox = "viewBox";
const QString aDarkBackground = "dark-background";
const QString aBackground = "background";
const QString aCrossedBackground = "crossed-background";
const QString aUBZType = "type";
const QString aUBZUuid = "uuid";
const QString aUBZParent = "parent";
const QString aFill = "fill"; // IWB attribute contans color to fill
const QString aID = "id"; // ID of any svg element can be placed in to iwb section
const QString aRef = "ref"; // as reference for applying additional attributes
const QString aSVGHref = "xlink:href"; // reference to file
const QString aIWBHref = "ref"; // reference to element ID
const QString aUBZHref = "href";
const QString aUBZSource = "source";
const QString aSrc = "src";
const QString aSVGRequiredExtension = "requiredExtensions";
const QString aX = "x";
const QString aY = "y";
const QString aWidth = "width";
const QString aHeight = "height";
const QString aStroke = "stroke";
const QString aStrokeWidth = "stroke-width";
const QString aPoints = "points";
const QString aZLayer = "z-value";
const QString aLayer = "layer";
const QString aTransform = "transform";
const QString aLocked = "locked";
const QString aIWBName = "name";
const QString aIWBContent = "content";
// Attribute values
const QString avIWBVersionNo = "1.0";
const QString avUBZText = "text";
const QString avFalse = "false";
const QString avTrue = "true";
// Namespaces and prefixes
const QString svgRequiredExtensionPrefix = "http://www.imsglobal.org/iwb/";
const QString dcNS = "http://purl.org/dc/elements/1.1/";
const QString ubNS = "http://uniboard.mnemis.com/document";
const QString svgUBZNS = "http://www.imsglobal.org/xsd/iwb_v1p0";
const QString svgIWBNS = "http://www.w3.org/2000/svg";
const QString xlinkNS = "http://www.w3.org/1999/xlink";
const QString iwbNS = "http://www.imsglobal.org/xsd/iwb_v1p0";
const QString xsiNS = "http://www.w3.org/2001/XMLSchema-instance";
const QString xsiShemaLocation = "\
http://www.imsglobal.org/xsd/iwb_v1p0 \
http://www.imsglobal.org/profile/iwb/iwbv1p0_v1p0.xsd \
http://www.w3.org/2000/svg http://www.imsglobal.org/profile/iwb/svgsubsetv1p0_v1p0.xsd \
http://www.w3.org/1999/xlink http://www.imsglobal.org/xsd/w3/1999/xlink.xsd";
const QString dcNSPrefix = "dc";
const QString ubNSPrefix = "ub";
const QString svgIWBNSPrefix = "svg";
const QString xlinkNSPrefix = "xlink";
const QString iwbNsPrefix = "iwb";
const QString xsiPrefix = "xsi";
const QString xsiSchemaLocationPrefix = "schemaLocation";
const QString avOwner = "";
const QString avCreator = "";
const QString avDescription = "";
//constant symbols and words etc
const QString dimensionsDelimiter1 = "x";
const QString dimensionsDelimiter2 = " ";
const QString pageAlias = "page";
const QString pageFileExtentionUBZ = "svg";
//content folder names
const QString cfImages = "images";
const QString cfVideos = "video";
const QString cfAudios = "audio";
const QString cfFlash = "flash";
//known file extentions
const QString feSvg = "svg";
const QString feWgt = "wgt";
const QString fePng = "png";
const int iCrossSize = 32;
const int iCrossWidth = 1;
// Image formats supported by CFF exclude wgt. Wgt is Sankore widget, which is considered as a .png preview.
const QString iwbElementImage(" \
wgt, \
jpeg, \
jpg, \
bmp, \
gif, \
wmf, \
emf, \
png, \
tif, \
tiff \
");
// Video formats supported by CFF
const QString iwbElementVideo(" \
mpg, \
mpeg, \
swf, \
");
// Audio formats supported by CFF
const QString iwbElementAudio(" \
mp3, \
wav \
");
const QString cffSupportedFileFormats(iwbElementImage + iwbElementVideo + iwbElementAudio);
const QString ubzFormatsToConvert("svg");
const QString iwbSVGImageAttributes(" \
id, \
xlink:href, \
x, \
y, \
height, \
width, \
fill-opacity, \
requiredExtentions, \
transform \
");
const QString iwbSVGAudioAttributes(" \
id, \
xlink:href, \
x, \
y, \
height, \
width, \
fill-opacity, \
requiredExtentions, \
transform \
");
const QString iwbSVGVideoAttributes(" \
id, \
xlink:href, \
x, \
y, \
height, \
width, \
fill-opacity, \
requiredExtentions, \
transform \
");
const QString iwbSVGRectAttributes(" \
id, \
x, \
y, \
height, \
width, \
fill, \
fill-opacity, \
stroke, \
stroke-dasharray, \
stroke-linecap, \
stroke-linejoin, \
stroke-opacity, \
stroke-width, \
transform \
");
const QString iwbSVGTextAttributes(" \
id, \
x, \
y, \
fill, \
font-family, \
font-size, \
font-style, \
font-weight, \
font-stretch, \
transform \
");
const QString iwbSVGTextAreaAttributes(" \
id, \
x, \
y, \
height, \
width, \
fill, \
font-family, \
font-size, \
font-style, \
font-weight, \
font-stretch, \
text-align, \
transform \
");
const QString iwbSVGTspanAttributes(" \
id, \
fill, \
font-family, \
font-size, \
font-style, \
font-weight, \
font-stretch, \
text-align, \
");
const QString iwbSVGLineAttributes(" \
id, \
x1, \
y1, \
x2, \
y2, \
stroke, \
stroke-dasharray, \
stroke-width, \
stroke-opacity, \
stroke-linecap, \
transform \
");
const QString iwbSVGPolyLineAttributes(" \
id, \
points, \
stroke, \
stroke-width, \
stroke-dasharray, \
stroke-opacity, \
stroke-linecap, \
transform \
");
const QString iwbSVGPolygonAttributes(" \
id, \
points, \
fill, \
fill-opacity, \
stroke, \
stroke-dasharray, \
stroke-width, \
stroke-linecap, \
stroke-linejoin, \
stroke-opacity, \
stroke-width, \
transform \
");
// 1 to 1 copy to SVG section
const QString iwbElementAttributes(" \
background, \
background-fill, \
background-posture, \
flip, \
freehand, \
highlight, \
highlight-fill, \
list-style-type, \
list-style-type-fill, \
locked, \
replicate, \
revealer, \
stroke-lineshape-start, \
stroke-lineshape-end \
");
// cannot be copied 1 to 1 to SVG section
const QString ubzElementAttributesToConvert(" \
xlink:href, \
src, \
transform, \
uuid \
"
);
// additional attributes. Have references in SVG section.
const QString svgElementAttributes(" \
points, \
fill, \
fill-opacity, \
stroke, \
stroke-dasharray, \
stroke-linecap, \
stroke-opacity, \
stroke-width, \
stroke_linejoin, \
requiredExtensions, \
viewbox, \
x, \
y, \
x1, \
y1, \
x2, \
y2, \
height, \
width, \
font-family, \
font-size, \
font-style, \
font-weight, \
font-stretch, \
text-align \
");
const QString ubzContentFolders("audios,videos,images,widgets");
#endif // UBCFFCONSTANTS_H

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2015-2022 Département de l'Instruction Publique (DIP-SEM)
*
* Copyright (C) 2013 Open Education Foundation
*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
* l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of SaiCore.
*
* SaiCore is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* SaiCore is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SaiCore. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UBGLOBALS_H
#define UBGLOBALS_H
#define DELETEPTR(ptr) if(NULL != ptr){ \
delete ptr; \
ptr = NULL; \
}
#ifdef Q_WS_WIN
#define WARNINGS_DISABLE __pragma(warning(push, 0));
#define WARNINGS_ENABLE __pragma(warning(pop));
#ifdef NO_THIRD_PARTY_WARNINGS
// disabling warning level to 0 and save old state
#define THIRD_PARTY_WARNINGS_DISABLE WARNINGS_DISABLE
#else
// just save old state (needs for not empty define)
#define THIRD_PARTY_WARNINGS_DISABLE __pragma(warning(push));
#endif //#ifdef NO_THIRD_PARTY_WARNINGS
// anyway on WIN
#define THIRD_PARTY_WARNINGS_ENABLE WARNINGS_ENABLE
#else //#ifdef Q_WS_WIN
#define WARNINGS_DISABLE _Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\""); \
_Pragma("GCC diagnostic ignored \"-Wunused-variable\""); \
_Pragma("GCC diagnostic ignored \"-Wsign-compare\"");
#define WARNINGS_ENABLE _Pragma("GCC diagnostic pop");
#ifdef NO_THIRD_PARTY_WARNINGS
//disabling some warnings
#define THIRD_PARTY_WARNINGS_DISABLE WARNINGS_DISABLE
#define THIRD_PARTY_WARNINGS_ENABLE WARNINGS_ENABLE
#else
// just save old state (needs for not empty define)
#define THIRD_PARTY_WARNINGS_ENABLE WARNINGS_ENABLE
#endif //#ifdef NO_THIRD_PARTY_WARNINGS
#endif //#ifdef Q_WS_WIN
#endif // UBGLOBALS_H

6
plugins/plugins.pri Normal file
View File

@ -0,0 +1,6 @@
HEADERS += plugins/cffadaptor/src/UBCFFAdaptor_global.h \
plugins/cffadaptor/src/UBCFFAdaptor.h \
plugins/cffadaptor/src/UBCFFConstants.h \
plugins/cffadaptor/src/UBGlobals.h
SOURCES += plugins/cffadaptor/src/UBCFFAdaptor.cpp

View File

@ -0,0 +1,153 @@
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
initializeVariables()
{
APPLICATION_NAME="SaiCore"
STANDARD_QT_USED=false
# Root directory
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$SCRIPT_PATH/../.."
BUILD_DIR="$PROJECT_ROOT/build/linux/release"
PRODUCT_PATH="$BUILD_DIR/product"
# Qt installation path. This may vary across machines
QT_PATH="/home/dev/Qt/6.9.3/gcc_64"
PLUGINS_PATH="$QT_PATH/plugins"
GUI_TRANSLATIONS_DIRECTORY_PATH="$QT_PATH/translations"
QMAKE_PATH="$QT_PATH/bin/qmake"
LRELEASES="$QT_PATH/bin/lrelease"
NOTIFY_CMD=`which notify-send`
ZIP_PATH=`which zip`
if [ -z $ARCHITECTURE ]; then
ARCHITECTURE=`uname -m`
if [ $ARCHITECTURE == "x86_64" ]; then
ARCHITECTURE="amd64"
elif [$ARCHITECTURE == "armv7l" ]; then
$ARCHITECTURE="armhf"
QT_PATH="/usr/lib/arm-linux-gnueabihf/qt5"
fi
fi
}
notifyError(){
if [ -e "$NOTIFY_CMD" ]; then
$NOTIFY_CMD -t 0 -i "/usr/share/icons/oxygen/64x64/status/dialog-error.png" "$1"
fi
printf "\033[31merror:\033[0m $1\n"
exit 1
}
notifyProgress(){
if [ -e "$NOTIFY_CMD" ]; then
$NOTIFY_CMD "$1" "$2"
fi
printf "\033[32m--> Achieved task:\033[0m $1:\n\t$2\n"
}
checkDir(){
if [ ! -d "$1" ]; then
notifyError "Directory not found : $1"
fi
}
checkExecutable(){
if [ ! -e "$1" ]; then
notifyError "$1 command not found"
fi
}
buildWithStandardQt(){
# if both Qt4 and Qt5 are installed, choose Qt5
export QT_SELECT=5
STANDARD_QT=`which qmake`
if [ $? == "0" ]; then
QT_VERSION=`$STANDARD_QT --version | grep -i "Using Qt version" | sed -e "s/Using Qt version \(.*\) in.*/\1/"`
if [ `echo $QT_VERSION | sed -e "s/\.//g"` -gt 480 ]; then
notifyProgress "Standard QT" "A recent enough qmake has been found. Using this one instead of custom one"
STANDARD_QT_USED=true
QMAKE_PATH=$STANDARD_QT
LRELEASES=`which lrelease`
PLUGINS_PATH="$STANDARD_QT/../plugins"
fi
fi
}
createBuildContext() {
BUILD_CONTEXT="buildContext"
echo $ARCHITECTURE > $BUILD_CONTEXT
}
# Check command-line arguments to force an architecture
for var in "$@"
do
if [ $var == "i386" ]; then
ARCHITECTURE="i386"
fi
if [ $var == "amd64" ]; then
ARCHITECTURE="amd64"
fi
done
initializeVariables
#buildWithStandardQt
createBuildContext
cd $PROJECT_ROOT
# check of directories and executables
checkDir $QT_PATH
checkDir $PLUGINS_PATH
checkDir $GUI_TRANSLATIONS_DIRECTORY_PATH
checkExecutable $QMAKE_PATH
checkExecutable $LRELEASES
checkExecutable $ZIP_PATH
# cleaning the build directory
rm -rf $BUILD_DIR
# Generate translations
notifyProgress "QT" "Internationalization"
$LRELEASES ${APPLICATION_NAME}.pro
cd $GUI_TRANSLATIONS_DIRECTORY_PATH
$LRELEASES translations.pro
cd -
notifyProgress "${APPLICATION_NAME}" "Building ${APPLICATION_NAME}"
if [ "$ARCHITECTURE" == "amd64" ] || [ "$ARCHITECTURE" == "x86_64" ]; then
$QMAKE_PATH ${APPLICATION_NAME}.pro -spec linux-g++-64
else
$QMAKE_PATH ${APPLICATION_NAME}.pro -spec linux-g++
fi
make -j4 release-install
if [ ! -e "$PRODUCT_PATH/${APPLICATION_NAME}" ]; then
notifyError "${APPLICATION_NAME} build failed"
else
notifyProgress "Finished building SaiCore. You may now run the packaging script."
fi

View File

@ -0,0 +1,9 @@
#!/bin/bash
# Copyright (C) 2025-2026 ООО «КАРУМА»
# SaiCore — проприетарное ПО. См. LICENSE.
xdg-desktop-menu install /usr/share/applications/ch.SaiCore.SaiCore.desktop
xdg-mime install /usr/share/mime/packages/ch.SaiCore.SaiCore-ubz.xml
update-mime-database /usr/share/mime
gtk-update-icon-cache /usr/share/icons/hicolor
exit 0

View File

@ -0,0 +1,7 @@
#!/bin/bash
# Copyright (C) 2025-2026 ООО «КАРУМА»
# SaiCore — проприетарное ПО. См. LICENSE.
xdg-desktop-menu uninstall /usr/share/applications/ch.SaiCore.SaiCore.desktop
rm -f /usr/bin/saicore
exit 0

View File

@ -0,0 +1,474 @@
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Packaging script for SaiCore, for Debian-compatible distributions.
#
# This should be run after `build.sh`.
#
# The generated package structure is as follows :
#
# DEBIAN/
# | control
# | md5sums
# | prerm
# | postinst
# usr/
# | bin/
# | | SaiCore <-- actually a symlink to run.sh or SaiCore
# | share/
# | | applications/
# | | | SaiCore.desktop
# opt/
# | SaiCore/
# | | library/
# | | etc/
# | | qtlib/ (*)
# | | plugins/ (*)
# | | SaiCore
# | | SaiCore.png
# | | run.sh (*)
#
# (*) Only included if Qt libs and plugins are bundled. It is necessary to
# bundle these if the target system doesn't provide Qt 5.5.1, for example.
# ----------------------------------------------------------------------------
initializeVariables()
{
# This script's path
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$SCRIPT_PATH/../.."
# Where the application was built (see build.sh)
BUILD_DIR="$PROJECT_ROOT/build/linux/release"
PRODUCT_PATH="$BUILD_DIR/product"
# Where the package is built to
PACKAGE_BUILD_DIR="$PROJECT_ROOT/install"
# Temporary folder, where we put all the files that will be built into the
# package
BASE_WORKING_DIR="debianPackage"
APPLICATION_NAME="SaiCore"
APPLICATION_CODE="SaiCore"
APPLICATION_PATH="opt"
PACKAGE_DIRECTORY=$BASE_WORKING_DIR/$APPLICATION_PATH/$APPLICATION_CODE
PACKAGE_QT_DIRECTORY="$PACKAGE_DIRECTORY/qt"
QT_LIBRARY_DEST_PATH="$PACKAGE_QT_DIRECTORY/lib"
QT_LIBRARY_EXECUTABLES_DEST_PATH="$PACKAGE_QT_DIRECTORY/libexec"
QT_PLUGINS_DEST_PATH="$PACKAGE_QT_DIRECTORY/plugins"
QT_RESOURCES_DEST_PATH="$PACKAGE_QT_DIRECTORY/resources"
QT_TRANSLATIONS_DEST_PATH="$PACKAGE_QT_DIRECTORY/translations"
DESKTOP_FILE_PATH="$BASE_WORKING_DIR/usr/share/applications"
APPLICATION_SHORTCUT="$DESKTOP_FILE_PATH/${APPLICATION_CODE}.desktop"
DESCRIPTION="SaiCore, an interactive white board application"
VERSION=`cat $BUILD_DIR/version`
ARCHITECTURE=`cat buildContext`
# Include Qt libraries and plugins in the package, or not
# (this is necessary if the target system doesn't provide Qt 5.5.1)
BUNDLE_QT=true
# Qt installation path. This may vary across machines
QT_PATH="/home/dev/Qt/6.9.3/gcc_64"
QT_SHARE_PATH="/usr/share/qt5/"
GUI_TRANSLATIONS_DIRECTORY_PATH="$QT_PATH/translations"
QT_LIBRARY_SOURCE_PATH="$QT_PATH/lib"
QT_LIBRARY_EXECUTABLES_SOURCE_PATH="$QT_PATH/libexec"
QT_PLUGINS_SOURCE_PATH="$QT_PATH/plugins"
QT_RESOURCES_SOURCE_PATH="$QT_PATH/resources"
QT_TRANSLATIONS_SOURCE_PATH="$QT_PATH/translations"
NOTIFY_CMD=`which notify-send`
ZIP_PATH=`which zip`
}
checkUser()
{
if [ `id -u` -ne 0 ]; then
echo "Please run the script as root"
exit 1
fi
}
checkBuild()
{
if [ -z "$ARCHITECTURE" ]; then
echo "Make sure you have built the software first using ./build.sh"
exit 1
fi
}
notifyError(){
if [ -e "$NOTIFY_CMD" ]; then
$NOTIFY_CMD -t 0 -i "/usr/share/icons/oxygen/64x64/status/dialog-error.png" "$1"
fi
printf "\033[31merror:\033[0m $1\n"
exit 1
}
notifyProgress(){
if [ -e "$NOTIFY_CMD" ]; then
$NOTIFY_CMD "$1" "$2"
fi
printf "\033[32m--> \033[0m $1:\n\t$2\n"
}
copyQtLibrary(){
echo -e "\t $1"
if ls "$QT_LIBRARY_SOURCE_PATH/$1.so" &> /dev/null; then
cp -P $QT_LIBRARY_SOURCE_PATH/$1.so "$QT_LIBRARY_DEST_PATH/"
cp -P $QT_LIBRARY_SOURCE_PATH/$1.so.* "$QT_LIBRARY_DEST_PATH/"
strip $QT_LIBRARY_DEST_PATH/$1.so
chmod 644 $QT_LIBRARY_DEST_PATH/$1.so.* # 644 = rw-r-r
else
notifyError "$1 library not found in path: $QT_LIBRARY_SOURCE_PATH"
fi
}
copyQtPlugin(){
echo -e "\t $1"
if ls "$QT_PLUGINS_SOURCE_PATH/$1" &> /dev/null; then
cp -r $QT_PLUGINS_SOURCE_PATH/$1 $QT_PLUGINS_DEST_PATH/
strip $QT_PLUGINS_DEST_PATH/$1/*
chmod 644 $QT_PLUGINS_DEST_PATH/$1/* # 644 = rw-r-r
chmod +rx $QT_PLUGINS_DEST_PATH/$1
else
notifyError "$1 plugin not found in path: $QT_PLUGINS_SOURCE_PATH"
fi
}
removeQtDebugFiles()
{
notifyProgress "removing Qt debug files"
find $PACKAGE_QT_DIRECTORY -name "*.debug" -print0 | xargs -0 rm
}
# ----------------------------------------------------------------------------
# Copying the application, libs etc. to the temporary working directory
# ----------------------------------------------------------------------------
initializeVariables
checkBuild
checkUser
cd $PROJECT_ROOT
rm -rf $PACKAGE_DIRECTORY
mkdir -p $PACKAGE_DIRECTORY
rm -rf $PACKAGE_BUILD_DIR
notifyProgress "Copying product directory and resources"
cp -R $PRODUCT_PATH/* $PACKAGE_DIRECTORY
chown -R root:root $PACKAGE_DIRECTORY
cp -R resources/customizations $PACKAGE_DIRECTORY/
cp resources/linux/SaiCore-ubz.xml $PACKAGE_DIRECTORY/etc/
cp resources/linux/application-ubz.png $PACKAGE_DIRECTORY/etc/
if $BUNDLE_QT; then
cp -R resources/linux/run.sh $PACKAGE_DIRECTORY/
chmod a+x $PACKAGE_DIRECTORY/run.sh
fi
notifyProgress "Stripping main executable"
strip $PACKAGE_DIRECTORY/$APPLICATION_NAME
# copying startup hints
notifyProgress "copying startupHints"
cp -R resources/startupHints $PACKAGE_DIRECTORY/
if $BUNDLE_QT; then
notifyProgress "Copying and stripping Qt plugins"
mkdir -p $QT_PLUGINS_DEST_PATH
#copyQtPlugin audio
#copyQtPlugin bearer
copyQtPlugin egldeviceintegrations
copyQtPlugin generic
copyQtPlugin help
copyQtPlugin iconengines
copyQtPlugin imageformats
copyQtPlugin multimedia
copyQtPlugin networkinformation
copyQtPlugin platforminputcontexts
copyQtPlugin platforms
copyQtPlugin platformthemes
copyQtPlugin position
copyQtPlugin printsupport
#copyQtPlugin sceneparsers
copyQtPlugin tls
copyQtPlugin wayland-graphics-integration-client
copyQtPlugin wayland-decoration-client
copyQtPlugin wayland-graphics-integration-server
copyQtPlugin wayland-shell-integration
copyQtPlugin xcbglintegrations
notifyProgress "Copying and stripping Qt libraries"
mkdir -p $QT_LIBRARY_DEST_PATH
copyQtLibrary libQt6Concurrent
copyQtLibrary libQt6Core
copyQtLibrary libQt6Core5Compat
copyQtLibrary libQt6DBus
copyQtLibrary libQt6Gui
copyQtLibrary libQt6Multimedia
#copyQtLibrary libQt6MultimediaGstTools
copyQtLibrary libQt6MultimediaWidgets
copyQtLibrary libQt6Network
copyQtLibrary libQt6OpenGL
copyQtLibrary libQt6Positioning
copyQtLibrary libQt6PrintSupport
copyQtLibrary libQt6Qml
copyQtLibrary libQt6QmlModels
copyQtLibrary libQt6QmlMeta
copyQtLibrary libQt6QmlWorkerScript
copyQtLibrary libQt6Quick
#copyQtLibrary libQt6Sensors
copyQtLibrary libQt6Sql
copyQtLibrary libQt6Svg
copyQtLibrary libQt6WebChannel
copyQtLibrary libQt6WebEngineCore
copyQtLibrary libQt6WebEngineWidgets
copyQtLibrary libQt6QuickWidgets
#copyQtLibrary libQt6WebSockets
copyQtLibrary libQt6Widgets
copyQtLibrary libQt6XcbQpa
copyQtLibrary libQt6Xml
#copyQtLibrary libQt6XmlPatterns
copyQtLibrary libicuuc
copyQtLibrary libicui18n
copyQtLibrary libicudata
removeQtDebugFiles
fi
notifyProgress "Copying Qt translations"
mkdir -p $PACKAGE_DIRECTORY/i18n
cp $GUI_TRANSLATIONS_DIRECTORY_PATH/qt_??.qm $PACKAGE_DIRECTORY/i18n/
cp $GUI_TRANSLATIONS_DIRECTORY_PATH/qtbase*.qm $PACKAGE_DIRECTORY/i18n/
#cp $GUI_TRANSLATIONS_DIRECTORY_PATH/qtscript*.qm $PACKAGE_DIRECTORY/i18n/
cp $GUI_TRANSLATIONS_DIRECTORY_PATH/qtmultimedia*.qm $PACKAGE_DIRECTORY/i18n/
#cp $GUI_TRANSLATIONS_DIRECTORY_PATH/qtxmlpatterns*.qm $PACKAGE_DIRECTORY/i18n/
# ----------------------------------------------------------------------------
# QT WebEngine
# ----------------------------------------------------------------------------
notifyProgress "Copying Qt WebEngine dependencies"
mkdir -p "$QT_LIBRARY_EXECUTABLES_DEST_PATH"
cp $QT_LIBRARY_EXECUTABLES_SOURCE_PATH/QtWebEngineProcess $QT_LIBRARY_EXECUTABLES_DEST_PATH
mkdir -p "$QT_RESOURCES_DEST_PATH"
cp $QT_RESOURCES_SOURCE_PATH/* $QT_RESOURCES_DEST_PATH
mkdir -p "$QT_TRANSLATIONS_DEST_PATH/qtwebengine_locales"
cp $QT_TRANSLATIONS_SOURCE_PATH/qtwebengine_locales/* $QT_TRANSLATIONS_DEST_PATH/qtwebengine_locales
# ----------------------------------------------------------------------------
# FFmpeg
# ----------------------------------------------------------------------------
notifyProgress "Copying Qt FFmpeg dependencies"
cp $QT_LIBRARY_SOURCE_PATH/libavcodec* $QT_LIBRARY_DEST_PATH/
cp $QT_LIBRARY_SOURCE_PATH/libavformat* $QT_LIBRARY_DEST_PATH/
cp $QT_LIBRARY_SOURCE_PATH/libavutil* $QT_LIBRARY_DEST_PATH/
cp $QT_LIBRARY_SOURCE_PATH/libswscale* $QT_LIBRARY_DEST_PATH/
cp $QT_LIBRARY_SOURCE_PATH/libswresample* $QT_LIBRARY_DEST_PATH/
cp $QT_LIBRARY_SOURCE_PATH/libQt6FFmpegStub* $QT_LIBRARY_DEST_PATH/
# ----------------------------------------------------------------------------
# DEBIAN directory of package (control, md5sums, postinst etc)
# ----------------------------------------------------------------------------
notifyProgress "Generating control files for package"
mkdir -p "$BASE_WORKING_DIR/DEBIAN"
# Copy prerm script
cp -r "$SCRIPT_PATH/debian_package_files/prerm" "$BASE_WORKING_DIR/DEBIAN/"
chmod 755 "$BASE_WORKING_DIR/DEBIAN/prerm"
# Generate postinst script (can't copy it like prerm because some paths vary depending on
# the values of the variables in this script)
SYMLINK_TARGET="/$APPLICATION_PATH/$APPLICATION_CODE/$APPLICATION_NAME"
if $BUNDLE_QT ; then
SYMLINK_TARGET="/$APPLICATION_PATH/$APPLICATION_CODE/run.sh"
fi
cat > "$BASE_WORKING_DIR/DEBIAN/postinst" << EOF
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
xdg-desktop-menu install --novendor /usr/share/applications/${APPLICATION_CODE}.desktop
xdg-mime install --mode system /$APPLICATION_PATH/$APPLICATION_CODE/etc/SaiCore-ubz.xml
xdg-mime default /usr/share/applications/${APPLICATION_CODE}.desktop application/ubz
xdg-icon-resource install --context mimetypes --size 48 /$APPLICATION_PATH/$APPLICATION_CODE/etc/application-ubz.png application-ubz
ln -s $SYMLINK_TARGET /usr/bin/$APPLICATION_CODE
exit 0
EOF
chmod 755 "$BASE_WORKING_DIR/DEBIAN/postinst"
# Generate md5 sums of everything in the application path (e.g /opt) and the desktop entry
cd $BASE_WORKING_DIR
find $APPLICATION_PATH/ -exec md5sum {} > DEBIAN/md5sums 2>/dev/null \;
find $DESKTOP_FILE_PATH/ -exec md5sum {} >> DEBIAN/md5sums 2>/dev/null \;
cd $PROJECT_ROOT
# Generate control file
CONTROL_FILE="$BASE_WORKING_DIR/DEBIAN/control"
echo "Package: ${APPLICATION_CODE}" > "$CONTROL_FILE"
echo "Version: $VERSION" >> "$CONTROL_FILE"
echo "Section: education" >> "$CONTROL_FILE"
echo "Priority: optional" >> "$CONTROL_FILE"
echo "Architecture: $ARCHITECTURE" >> "$CONTROL_FILE"
echo "Essential: no" >> "$CONTROL_FILE"
echo "Installed-Size: `du -s $PACKAGE_DIRECTORY | awk '{ print $1 }'`" >> "$CONTROL_FILE"
echo "Maintainer: ${APPLICATION_NAME} Developers team <dev@SaiCore.ch>" >> "$CONTROL_FILE"
echo "Homepage: https://github.com/DIP-SEM/SaiCore" >> "$CONTROL_FILE"
# Generate dependency list
echo -n "Depends: " >> "$CONTROL_FILE"
unset tab
declare -a tab
let count=0
if $BUNDLE_QT; then
for l in `objdump -p $PACKAGE_DIRECTORY/${APPLICATION_NAME} | grep NEEDED | awk '{ print $2 }'`; do
for lib in `dpkg -S $l | grep -v "libqt5" | grep -v "qt55" | awk -F":" '{ print $1 }'`; do
presence=`echo ${tab[*]} | grep -c "$lib"`;
if [ "$presence" == "0" ]; then
if [ "$lib" != "SaiCore" ]; then
echo "adding $lib to needed dependencies"
tab[$count]=$lib;
((count++));
fi;
fi;
done;
done;
else
for l in `objdump -p $PACKAGE_DIRECTORY/${APPLICATION_NAME} | grep NEEDED | awk '{ print $2 }'`; do
for lib in `dpkg -S $l | awk -F":" '{ print $1 }'`; do
presence=`echo ${tab[*]} | grep -c "$lib"`;
if [ "$presence" == "0" ]; then
tab[$count]=$lib;
((count++));
fi;
done;
done;
fi
for ((i=0;i<${#tab[@]};i++)); do
if [ $i -ne "0" ]; then
echo -n ", " >> "$CONTROL_FILE"
fi
# conditional dependency when libavcodec is found
depdVer=$(apt-cache show ${tab[$i]} | grep "Version: " | head -1 | awk '{ print $2 }' | sed -e 's/\([~:. 0-9?]*\).*/\1/g' | sed -e 's/\.$//')
if [[ "${tab[$i]}" == *"libavcodec"* ]]; then
depName="${tab[$i]::-2}"
versionNumber="${tab[$i]: -2}"
depdVer_part1=`echo ${depdVer} | awk -F'.' '{print $1}'`
depdVer_part2=`echo ${depdVer} | awk -F'.' '{print $2}'`
echo -n "${depName}${versionNumber} (>= ${depdVer_part1}.${depdVer_part2}) | ${depName}-extra${versionNumber} (>= ${depdVer_part1}.${depdVer_part2})" >> "$CONTROL_FILE"
else
echo -n "${tab[$i]} (>= ${depdVer})" >> "$CONTROL_FILE"
fi
done
echo -n ", onboard" >> "$CONTROL_FILE"
if $BUNDLE_QT; then
# Listing some dependencies manually; ideally we should use dpkg -p recursively
# to get the dependencies of the bundled shared libs & plugins. Or use static libs.
echo -n ", libxcb1" >> "$CONTROL_FILE"
echo -n ", libxcb-icccm4" >> "$CONTROL_FILE"
echo -n ", libxcb-xkb1" >> "$CONTROL_FILE"
echo -n ", libxcb-image0" >> "$CONTROL_FILE"
echo -n ", libxcb-render-util0" >> "$CONTROL_FILE"
echo -n ", libxcb-xinerama0" >> "$CONTROL_FILE"
else
echo -n ", libqt5multimedia5-plugins" >> "$CONTROL_FILE"
fi
echo "" >> "$CONTROL_FILE"
echo "Description: $DESCRIPTION" >> "$CONTROL_FILE"
# ----------------------------------------------------------------------------
# .desktop file
# ----------------------------------------------------------------------------
mkdir -p $DESKTOP_FILE_PATH
echo "[Desktop Entry]" > $APPLICATION_SHORTCUT
echo "Version=$VERSION" >> $APPLICATION_SHORTCUT
echo "Encoding=UTF-8" >> $APPLICATION_SHORTCUT
echo "Name=${APPLICATION_NAME}" >> $APPLICATION_SHORTCUT
echo "Comment=$DESCRIPTION" >> $APPLICATION_SHORTCUT
echo "Exec=$APPLICATION_CODE %f" >> $APPLICATION_SHORTCUT
echo "Icon=/$APPLICATION_PATH/$APPLICATION_CODE/${APPLICATION_NAME}.png" >> $APPLICATION_SHORTCUT
echo "StartupNotify=true" >> $APPLICATION_SHORTCUT
echo "Terminal=false" >> $APPLICATION_SHORTCUT
echo "Type=Application" >> $APPLICATION_SHORTCUT
echo "MimeType=application/ubz" >> $APPLICATION_SHORTCUT
echo "Categories=Education;" >> $APPLICATION_SHORTCUT
cp "resources/images/${APPLICATION_NAME}.png" "$PACKAGE_DIRECTORY/${APPLICATION_NAME}.png"
# ----------------------------------------------------------------------------
# Building the package
# ----------------------------------------------------------------------------
notifyProgress "Building package"
mkdir -p "$PACKAGE_BUILD_DIR/linux"
PACKAGE_NAME="${APPLICATION_NAME}_`lsb_release -is`_`lsb_release -rs`_${VERSION}_$ARCHITECTURE.deb"
PACKAGE_NAME=`echo "$PACKAGE_NAME" | awk '{print tolower($0)}'`
dpkg -b "$BASE_WORKING_DIR" "$PACKAGE_BUILD_DIR/linux/$PACKAGE_NAME"
#clean up mess
rm -rf $BASE_WORKING_DIR
notifyProgress "${APPLICATION_NAME}" "Package built"
exit 0

View File

@ -0,0 +1,563 @@
<?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>Hierarchy</key>
<dict>
<key>Attributes</key>
<dict>
<key>Documents</key>
<dict>
<key>Background Image</key>
<dict>
<key>IFPkgFlagBackgroundAlignment</key>
<integer>4</integer>
<key>IFPkgFlagBackgroundScaling</key>
<integer>1</integer>
<key>Mode</key>
<integer>0</integer>
<key>Path</key>
<string></string>
<key>Path Type</key>
<integer>1</integer>
</dict>
<key>License</key>
<dict>
<key>International</key>
<dict>
<key>Mode</key>
<integer>1</integer>
<key>Path</key>
<string>LICENSE</string>
<key>Path Type</key>
<integer>2</integer>
</dict>
</dict>
<key>ReadMe</key>
<dict>
<key>International</key>
<dict>
<key>Mode</key>
<integer>0</integer>
<key>Path</key>
<string></string>
<key>Path Type</key>
<integer>1</integer>
</dict>
</dict>
<key>Welcome</key>
<dict>
<key>International</key>
<dict>
<key>Mode</key>
<integer>0</integer>
<key>Path</key>
<string></string>
<key>Path Type</key>
<integer>1</integer>
</dict>
</dict>
</dict>
<key>Files</key>
<dict>
<key>Compress</key>
<true/>
<key>Hierarchy</key>
<dict>
<key>Children</key>
<array>
<dict>
<key>Children</key>
<array>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>build/macx/release/product/OpenBoard.app</string>
<key>Path Type</key>
<integer>2</integer>
<key>Privileges</key>
<integer>493</integer>
<key>Type</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Utilities</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Applications</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Application Support</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Documentation</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Filesystems</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Frameworks</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Internet Plug-Ins</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>PreferencePanes</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Preferences</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Printers</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>QuickTime</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Scripts</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>509</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>Library</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>1021</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>Children</key>
<array>
<dict>
<key>Children</key>
<array>
<dict>
<key>Children</key>
<array/>
<key>GID</key>
<integer>0</integer>
<key>Path</key>
<string>Extensions</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>493</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>0</integer>
<key>Path</key>
<string>Library</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>493</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>0</integer>
<key>Path</key>
<string>System</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>493</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>Path</key>
<string>/</string>
<key>Path Type</key>
<integer>1</integer>
<key>Privileges</key>
<integer>1021</integer>
<key>Type</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<key>IFPkgFlagDefaultLocation</key>
<string>/Applications</string>
<key>Imported Package</key>
<false/>
<key>Package Path</key>
<string></string>
<key>Split Forks</key>
<true/>
</dict>
<key>Plugins</key>
<dict>
<key>PluginsList</key>
<array>
<dict>
<key>Path</key>
<string>Introduction</string>
<key>Type</key>
<integer>0</integer>
</dict>
<dict>
<key>Path</key>
<string>ReadMe</string>
<key>Type</key>
<integer>0</integer>
</dict>
<dict>
<key>Path</key>
<string>License</string>
<key>Type</key>
<integer>0</integer>
</dict>
<dict>
<key>Path</key>
<string>Target</string>
<key>Type</key>
<integer>0</integer>
</dict>
<dict>
<key>Path</key>
<string>PackageSelection</string>
<key>Type</key>
<integer>0</integer>
</dict>
<dict>
<key>Path</key>
<string>Install</string>
<key>Type</key>
<integer>0</integer>
</dict>
<dict>
<key>Path</key>
<string>FinishUp</string>
<key>Type</key>
<integer>0</integer>
</dict>
</array>
</dict>
<key>Scripts</key>
<dict>
<key>Additional Resources</key>
<dict>
<key>International</key>
<array/>
</dict>
<key>Installation Scripts</key>
<dict>
<key>IFInstallationScriptsPostflight</key>
<dict>
<key>Path</key>
<string></string>
<key>Status</key>
<false/>
</dict>
<key>IFInstallationScriptsPostinstall</key>
<dict>
<key>Path</key>
<string></string>
<key>Path Type</key>
<integer>2</integer>
<key>Status</key>
<false/>
</dict>
<key>IFInstallationScriptsPostupgrade</key>
<dict>
<key>Path</key>
<string></string>
<key>Status</key>
<false/>
</dict>
<key>IFInstallationScriptsPreflight</key>
<dict>
<key>Path</key>
<string></string>
<key>Status</key>
<false/>
</dict>
<key>IFInstallationScriptsPreinstall</key>
<dict>
<key>Path</key>
<string></string>
<key>Status</key>
<false/>
</dict>
<key>IFInstallationScriptsPreupgrade</key>
<dict>
<key>Path</key>
<string></string>
<key>Status</key>
<false/>
</dict>
</dict>
<key>Requirements</key>
<array/>
</dict>
<key>Settings</key>
<dict>
<key>Description</key>
<dict>
<key>International</key>
<dict>
<key>IFPkgDescriptionDeleteWarning</key>
<string></string>
<key>IFPkgDescriptionDescription</key>
<string>OpenBoard is an hardware independent interactive whiteboard</string>
<key>IFPkgDescriptionTitle</key>
<string>OpenBoard</string>
<key>IFPkgDescriptionVersion</key>
<string>1.40.b.00</string>
</dict>
</dict>
<key>Display Information</key>
<dict>
<key>CFBundleGetInfoString</key>
<string>OpenBoard 1.0 Copyrights © 2013 OpenBoard</string>
<key>CFBundleIconFile</key>
<string>resources/macx/OpenBoardDmg.icns</string>
<key>CFBundleIconFile Path Type</key>
<integer>2</integer>
<key>CFBundleIdentifier</key>
<string>org.OpenBoard.pkg.OpenBoard</string>
<key>CFBundleName</key>
<string>OpenBoard</string>
<key>CFBundleShortVersionString</key>
<string>1.40.b.00</string>
</dict>
<key>Options</key>
<dict>
<key>IFPkgFlagAllowBackRev</key>
<false/>
<key>IFPkgFlagAuthorizationAction</key>
<integer>0</integer>
<key>IFPkgFlagFollowLinks</key>
<false/>
<key>IFPkgFlagIsRequired</key>
<false/>
<key>IFPkgFlagOverwritePermissions</key>
<false/>
<key>IFPkgFlagRelocatable</key>
<false/>
<key>IFPkgFlagRestartAction</key>
<integer>0</integer>
<key>IFPkgFlagRootVolumeOnly</key>
<false/>
<key>IFPkgFlagUpdateInstalledLanguages</key>
<false/>
</dict>
<key>Version</key>
<dict>
<key>IFMajorVersion</key>
<integer>1</integer>
<key>IFMinorVersion</key>
<integer>40</integer>
</dict>
</dict>
</dict>
<key>IFPkgFlagPackageSelection</key>
<integer>0</integer>
<key>Name</key>
<string>OpenBoard</string>
<key>Status</key>
<integer>1</integer>
<key>Type</key>
<integer>1</integer>
</dict>
<key>Name</key>
<string>Project</string>
<key>Settings</key>
<dict>
<key>10.1 Compatibility</key>
<true/>
<key>Build Path</key>
<string>install/mac</string>
<key>Build Path Type</key>
<integer>2</integer>
<key>Comment</key>
<string></string>
<key>Remove .DS_Store</key>
<true/>
<key>Remove .pbdevelopment</key>
<true/>
<key>Remove CVS</key>
<true/>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,185 @@
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$SCRIPT_PATH/../.."
APPLICATION_NAME="SaiCore"
BASE_QT_DIR=/Users/dev/Qt/6.9.3/macos
# Executables
QMAKE=$BASE_QT_DIR/bin/qmake
MACDEPLOYQT=$BASE_QT_DIR/bin/macdeployqt
DMGUTIL="$PROJECT_ROOT/release_scripts/osx/refnum/dmgutil/dmgutil.pl"
DSYMUTIL=/usr/bin/dsymutil
STRIP=/usr/bin/strip
PLISTBUDDY=/usr/libexec/PlistBuddy
ICEBERG=/usr/local/bin/freeze
LRELEASE=$BASE_QT_DIR/bin/lrelease
LCONVERT=$BASE_QT_DIR/bin/lconvert
# Directories
BUILD_DIR="$PROJECT_ROOT/build/macx/release"
PRODUCT_DIR="$BUILD_DIR/product"
BASE_QT_TRANSLATIONS_DIRECTORY=$BASE_QT_DIR/translations
function notify {
GROWLNOTIFY=`which growlnotify`
if [ -x "$GROWLNOTIFY" ]; then
$GROWLNOTIFY --name SaiCore-build --iconpath /Developer/Applications/Xcode.app --message "$1" "SaiCore"
fi
printf "\033[48;5;120m--->\033[0m $1\n"
}
function warn {
printf "\033[48;5;178m--->\033[0m $1\n"
}
function error
{
printf "\033[48;5;160;38;5;15m--->\033[0m $1\n"
}
function abort {
error "$1"
exit 1
}
function checkExecutable {
if [ ! -x "$1" ]; then
abort "$1 not found"
fi
}
function addQtTranslations {
for eachTranslation in `ls $BASE_QT_TRANSLATIONS_DIRECTORY/qt_??.qm`
do
# looking fo the language code for each qt translation file
languageCode=`echo $eachTranslation | sed 's/.*qt_\(.*\).qm/\1/'`
basicDir=$PRODUCT_DIR/$APPLICATION_NAME.app/Contents/Resources/
for eachDirectory in `ls $basicDir`
do
# looping through the SaiCore availables languages
directoryLanguageCode=`echo $eachDirectory | sed 's/\(.*\)\.lproj/\1/'`
if [ ! -z $directoryLanguageCode ]; then
if [[ $eachDirectory == *".lproj"* && $eachDirectory != "empty.lproj" && $directoryLanguageCode == *$languageCode* ]]; then
# SaiCore translation found for qt translation file
# cp $eachTranslation $basicDir/$eachDirectory
$LCONVERT -o $basicDir/$eachDirectory/qt_$languageCode.qm $BASE_QT_TRANSLATIONS_DIRECTORY/qtbase_$languageCode.qm $BASE_QT_TRANSLATIONS_DIRECTORY/qtmultimedia_$languageCode.qm
if [ $directoryLanguageCode != $languageCode ]; then
# handling fr and fr_CH code.
mv $basicDir/$eachDirectory/qt_$languageCode.qm $basicDir/$eachDirectory/qt_$directoryLanguageCode.qm
fi
fi
fi
done
done
}
trap "defaults write org.oe-f.SaiCore.release Running -bool NO" EXIT
notify "Running SaiCore release script (`date`)"
cd $PROJECT_ROOT
script_is_running=`defaults read org.oe-f.SaiCore.release Running 2>/dev/null`
if [[ $? -eq 0 ]] && [[ "$script_is_running" = "1" ]]; then
trap EXIT
abort "another release script already running"
fi
defaults write org.oe-f.SaiCore.release Running -bool YES
# Check for executables
checkExecutable "$QMAKE"
checkExecutable "$MACDEPLOYQT"
checkExecutable "$DMGUTIL"
checkExecutable "$DSYMUTIL"
checkExecutable "$STRIP"
checkExecutable "$PLISTBUDDY"
checkExecutable "$ICEBERG"
checkExecutable "$LRELEASE"
# delete the build directory
notify "Cleaning ..."
rm -rf "$BUILD_DIR"
# application translations
notify "Generating applications translatons"
$LRELEASE "$APPLICATION_NAME.pro"
# generate Makefiles
notify "Generating Makefile ..."
if [ "$1" == "1010" ]; then
QMAKE_CMD="$QMAKE \"DEFINES+=OS_NEWER_THAN_OR_EQUAL_TO_1010\" $APPLICATION_NAME.pro -spec macx-clang"
else
QMAKE_CMD="$QMAKE $APPLICATION_NAME.pro -spec macx-clang"
fi
$QMAKE_CMD
# build
notify "Compiling ..."
make -j4 release
notify "Qt Translations ..."
#$LRELEASE $BASE_QT_TRANSLATIONS_DIRECTORY/translations.pro
addQtTranslations
cp -R resources/customizations $PRODUCT_DIR/$APPLICATION_NAME.app/Contents/Resources
cp -R resources/startupHints $PRODUCT_DIR/$APPLICATION_NAME.app/Contents/Resources
cp -R $importerDir/$importerName.app $PRODUCT_DIR/$APPLICATION_NAME.app/Contents/Resources
VERSION=`cat "$BUILD_DIR/version"`
if [ ! -f "$BUILD_DIR/version" ]; then
echo "version not found"
exit 1
fi
VOLUME="/Volumes/$APPLICATION_NAME"
APP="$PRODUCT_DIR/$APPLICATION_NAME.app"
DSYM_NAME="$APPLICATION_NAME (r$SVN_REVISION).dSYM"
DSYM="$PRODUCT_DIR/$DSYM_NAME"
GSYM_i386="$PRODUCT_DIR/$APPLICATION_NAME i386.sym"
INFO_PLIST="$APP/Contents/Info.plist"
rm -f "$APP/Contents/Resources/empty.lproj"
# set various version infomration in Info.plist
$PLISTBUDDY -c "Set :CFBundleVersion $VERSION" "$INFO_PLIST"
$PLISTBUDDY -c "Set :CFBundleShortVersionString $VERSION" "$INFO_PLIST"
$PLISTBUDDY -c "Set :CFBundleGetInfoString $VERSION" "$INFO_PLIST"
# bundle Qt Frameworks into the app bundle
notify "Bulding frameworks ..."
cd "`pwd`/build/macx/release/product/"
$MACDEPLOYQT "`pwd`/$APPLICATION_NAME.app"
cd -
notify "Extracting debug information ..."
$DSYMUTIL "$APP/Contents/MacOS/$APPLICATION_NAME" -o "$DSYM"
$STRIP -S "$APP/Contents/MacOS/$APPLICATION_NAME"
notify "$APPLICATION_NAME is now built. You can now sign the application using the script 'codesign.sh'."
notify "Usage : sudo ./codesign.sh \"Developer ID Application: ...\""
notify "You can also ignore signing step if you don't intend to distribute this version"
notify "If so, you can directly package SaiCore using the script 'package.sh'"
notify "Usage : sudo ./package.sh"
exit 0

View File

@ -0,0 +1,107 @@
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$SCRIPT_PATH/../.."
IDENTITY=$1
CODESIGN=/usr/bin/codesign
BUILD_DIR="$PROJECT_ROOT/build/macx/release"
PRODUCT_DIR="$BUILD_DIR/product"
RESOURCES_DIR="$PROJECT_ROOT/resources"
MACX_RESOURCES_DIR="$RESOURCES_DIR/macx"
INSTALL_DIR="$PROJECT_ROOT/install/mac"
APPLICATION_NAME="SaiCore"
APPLICATION_DOT_APP="$APPLICATION_NAME.app"
APPLICATION_DIR="$PRODUCT_DIR/$APPLICATION_DOT_APP"
APPLICATION_CONTENTS_DIR="$APPLICATION_DIR/Contents"
APPLICATION_RESOURCES_DIR="$APPLICATION_CONTENTS_DIR/Resources"
APPLICATION_FRAMEWORKS_DIR="$APPLICATION_CONTENTS_DIR/Frameworks"
WEBENGINE_NAME="QtWebEngineProcess"
WEBENGINE_DOT_APP="$WEBENGINE_NAME"
WEBENGINE_DIR="$APPLICATION_FRAMEWORKS_DIR/QtWebEngineCore.framework/Helpers"
function notify {
GROWLNOTIFY=`which growlnotify`
if [ -x "$GROWLNOTIFY" ]; then
$GROWLNOTIFY --name SaiCore-build --iconpath /Developer/Applications/Xcode.app --message "$1" "SaiCore"
fi
printf "\033[48;5;120m--->\033[0m $1\n"
}
function warn {
printf "\033[48;5;178m--->\033[0m $1\n"
}
function error
{
printf "\033[48;5;160;38;5;15m--->\033[0m $1\n"
}
function abort {
error "$1"
exit 1
}
function checkExecutable {
if [ ! -x "$1" ]; then
abort "$1 not found"
fi
}
checkExecutable $CODESIGN
function signWebEngine
{
notify "signing $WEBENGINE_NAME..."
if [ ! -e ${WEBENGINE_DIR} ]; then
abort "${WEBENGINE_DIR} not found"
fi
cd $WEBENGINE_DIR
ls `pwd`
$CODESIGN --force --deep -o runtime --timestamp --entitlements "$MACX_RESOURCES_DIR"/WebEngineEntitlements.plist --verbose=4 -s "$IDENTITY" --digest-algorithm=sha1,sha256 "$WEBENGINE_NAME.app/Contents/MacOS/QtWebEngineProcess"
cd -
}
function signSaiCore
{
notify "signing $APPLICATION_NAME..."
if [ ! -e ${APPLICATION_DIR} ]; then
abort "${APPLICATION_DIR} not found"
fi
cd $PRODUCT_DIR
$CODESIGN --force --deep -o runtime --timestamp --entitlements "$MACX_RESOURCES_DIR/Entitlements.plist" --verbose=4 -s "$IDENTITY" --digest-algorithm=sha1,sha256 "$APPLICATION_DOT_APP"
cd -
}
signSaiCore
signWebEngine
notify "$APPLICATION_NAME is now signed. You can now package SaiCore using the script 'package.sh'"
exit 0

View File

@ -0,0 +1,86 @@
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$SCRIPT_PATH/../.."
APPLICATION_NAME="SaiCore"
BASE_QT_DIR=~/Qt/5.15.2/clang_64
# Executables
QMAKE=$BASE_QT_DIR/bin/qmake
MACDEPLOYQT=$BASE_QT_DIR/bin/macdeployqt
CODESIGN=/usr/bin/codesign
DMGUTIL="$PROJECT_ROOT/release_scripts/osx/refnum/dmgutil/dmgutil.pl"
DSYMUTIL=/usr/bin/dsymutil
STRIP=/usr/bin/strip
PLISTBUDDY=/usr/libexec/PlistBuddy
ICEBERG=/usr/local/bin/freeze
LRELEASE=$BASE_QT_DIR/bin/lrelease
USER=$1
TEAMID=$2
# Directories
BUILD_DIR="$PROJECT_ROOT/build/macx/release"
PRODUCT_DIR="$BUILD_DIR/product"
RESOURCES_DIR="$PROJECT_ROOT/resources"
MACX_RESOURCES_DIR="$RESOURCES_DIR/macx"
BASE_QT_TRANSLATIONS_DIRECTORY=$BASE_QT_DIR/translations
INSTALL_DIR="$PROJECT_ROOT/install/mac"
function notify {
GROWLNOTIFY=`which growlnotify`
if [ -x "$GROWLNOTIFY" ]; then
$GROWLNOTIFY --name SaiCore-build --iconpath /Developer/Applications/Xcode.app --message "$1" "SaiCore"
fi
printf "\033[48;5;120m--->\033[0m $1\n"
}
function warn {
printf "\033[48;5;178m--->\033[0m $1\n"
}
function error
{
printf "\033[48;5;160;38;5;15m--->\033[0m $1\n"
}
function abort {
error "$1"
exit 1
}
function checkExecutable {
if [ ! -x "$1" ]; then
abort "$1 not found"
fi
}
notify "================================================"
notify "=============== NOTARIZATION ==================="
notify "================================================"
printf "\n"
cd $INSTALL_DIR;
notify "================================================"
notify "Submitting $APPLICATION_NAME for notarization..."
notify "================================================"
echo $(2>&1 xcrun notarytool submit --wait --apple-id "$USER" --team-id "$TEAMID" SaiCore.dmg)
exit 0

View File

@ -0,0 +1,197 @@
#!/bin/bash
# --------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------------------------------------------
set -o pipefail
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$SCRIPT_PATH/../.."
APPLICATION_NAME="SaiCore"
BASE_QT_DIR=/Users/dev/Qt/6.9.3/macos
# Executables
QMAKE=$BASE_QT_DIR/bin/qmake
MACDEPLOYQT=$BASE_QT_DIR/bin/macdeployqt
DMGUTIL="$PROJECT_ROOT/release_scripts/osx/refnum/dmgutil/dmgutil.pl"
DSYMUTIL=/usr/bin/dsymutil
STRIP=/usr/bin/strip
PLISTBUDDY=/usr/libexec/PlistBuddy
ICEBERG=/usr/local/bin/freeze
LRELEASE=$BASE_QT_DIR/bin/lrelease
# Directories
BUILD_DIR="$PROJECT_ROOT/build/macx/release"
PRODUCT_DIR="$BUILD_DIR/product"
BASE_QT_TRANSLATIONS_DIRECTORY=$BASE_QT_DIR/translations
function notify {
GROWLNOTIFY=`which growlnotify`
if [ -x "$GROWLNOTIFY" ]; then
$GROWLNOTIFY --name SaiCore-build --iconpath /Developer/Applications/Xcode.app --message "$1" "SaiCore"
fi
printf "\033[48;5;120m--->\033[0m $1\n"
}
function warn {
printf "\033[48;5;178m--->\033[0m $1\n"
}
function error
{
printf "\033[48;5;160;38;5;15m--->\033[0m $1\n"
}
function abort {
error "$1"
exit 1
}
function checkExecutable {
if [ ! -x "$1" ]; then
abort "$1 not found"
fi
}
trap "defaults write org.oe-f.SaiCore.release Running -bool NO" EXIT
notify "Running SaiCore release script (`date`)"
cd $PROJECT_ROOT
script_is_running=`defaults read org.oe-f.SaiCore.release Running 2>/dev/null`
if [[ $? -eq 0 ]] && [[ "$script_is_running" = "1" ]]; then
trap EXIT
abort "another release script already running"
fi
defaults write org.oe-f.SaiCore.release Running -bool YES
# Check for executables
checkExecutable "$QMAKE"
checkExecutable "$MACDEPLOYQT"
checkExecutable "$DMGUTIL"
checkExecutable "$DSYMUTIL"
checkExecutable "$STRIP"
checkExecutable "$PLISTBUDDY"
checkExecutable "$ICEBERG"
checkExecutable "$LRELEASE"
DMG="$APPLICATION_NAME.dmg"
VOLUME="/Volumes/$APPLICATION_NAME"
APP="$PRODUCT_DIR/$APPLICATION_NAME.app"
VERSION_FILE="$PROJECT_ROOT/version.txt"
if [ ! -f "$VERSION_FILE" ]; then
abort "version.txt not found"
fi
VERSION_MAJ=$(awk -F= '/VERSION_MAJ/ {gsub(/ /,"",$2); sub(/#.*/,"",$2); print $2}' "$VERSION_FILE")
VERSION_MIN=$(awk -F= '/VERSION_MIN/ {gsub(/ /,"",$2); sub(/#.*/,"",$2); print $2}' "$VERSION_FILE")
VERSION_PATCH=$(awk -F= '/VERSION_PATCH/ {gsub(/ /,"",$2); sub(/#.*/,"",$2); print $2}' "$VERSION_FILE")
VERSION_TYPE=$(awk -F= '/VERSION_TYPE/ {gsub(/ /,"",$2); sub(/#.*/,"",$2); print $2}' "$VERSION_FILE")
VERSION_BUILD=$(awk -F= '/VERSION_BUILD/ {gsub(/ /,"",$2); sub(/#.*/,"",$2); print $2}' "$VERSION_FILE")
FULL_VERSION="$VERSION_MAJ.$VERSION_MIN.$VERSION_PATCH${VERSION_TYPE}-${VERSION_BUILD}"
DSYM_NAME="$APPLICATION_NAME-$FULL_VERSION.dSYM"
DSYM="$PRODUCT_DIR/$DSYM_NAME"
GSYM_i386="$PRODUCT_DIR/$APPLICATION_NAME i386.sym"
INFO_PLIST="$APP/Contents/Info.plist"
if [ "$1" == "pkg" ]; then
BASE_ICEBERG_CONFIG_FILE="$SCRIPT_PATH/$APPLICATION_NAME.packproj"
#copy the standard file for working with
ICEBERG_CONFIG_FILE="$APPLICATION_NAME-working.packproj"
cp -r $BASE_ICEBERG_CONFIG_FILE $ICEBERG_CONFIG_FILE
# set version information
$PLISTBUDDY -c "Set :Hierarchy:Attributes:Settings:Description:International:IFPkgDescriptionVersion $VERSION" "$ICEBERG_CONFIG_FILE"
$PLISTBUDDY -c "Set :Hierarchy:Attributes:Settings:Display\ Information:CFBundleShortVersionString $VERSION" "$ICEBERG_CONFIG_FILE"
$PLISTBUDDY -c "Set :Hierarchy:Attributes:Settings:Version:IFMajorVersion `echo $VERSION | awk 'BEGIN { FS = "." }; { print $1 }'`" "$ICEBERG_CONFIG_FILE"
$PLISTBUDDY -c "Set :Hierarchy:Attributes:Settings:Version:IFMinorVersion `echo $VERSION | awk 'BEGIN { FS = "." }; { print $2 }'`" "$ICEBERG_CONFIG_FILE"
PRODUCT_DIR="install/mac/"
if [ ! -d "${PRODUCT_DIR}" ]; then
mkdir -p "${PRODUCT_DIR}"
fi
$ICEBERG $ICEBERG_CONFIG_FILE
#clean up mess
rm -rf $ICEBERG_CONFIG_FILE
exit 0
fi
notify "Creating a resizable DMG (2 GB) volume"
# Unmount if already there
umount "$VOLUME" 2> /dev/null
# Create a 2Go sparse image
SPARSE_IMAGE="${DMG%.dmg}.sparseimage"
hdiutil create -size 2g -type SPARSE -fs HFS+ -volname "$APPLICATION_NAME" "$SPARSE_IMAGE"
# attach sparse image to volume
hdiutil attach "$SPARSE_IMAGE" -mountpoint "$VOLUME"
notify "Copying app to volume"
rsync -a "$APP" "$VOLUME"/
RSYNC_STATUS=$?
if [ $RSYNC_STATUS -ne 0 ]; then
error "Copy failed (likely out of space). DMG will remain mounted for inspection."
error "Volume contents:"
ls -lh "$VOLUME"
error "Free space:"
df -h "$VOLUME"
exit 1
fi
ln -s /Applications "$VOLUME"
$DMGUTIL --set --iconsize=96 --toolbar=false --icon=resources/macx/SaiCore.icns "$VOLUME"
$DMGUTIL --set --x=20 --y=60 --width=580 --height=440 "$VOLUME"
$DMGUTIL --set --x=180 --y=120 "$VOLUME/`basename \"$APP\"`"
$DMGUTIL --set --x=400 --y=120 "$VOLUME/Applications"
if mount | grep -q "$VOLUME"; then
notify "Closing DMG..."
$DMGUTIL --close --volume="$APPLICATION_NAME" "$SPARSE_IMAGE"
else
warn "DMG volume already unmounted or copy failed — skipping close"
fi
# Convert sparseImage to compressed DMG
DMG="$PRODUCT_DIR/$APPLICATION_NAME-$FULL_VERSION.dmg"
notify "Converting sparseimage to compressed DMG..."
hdiutil convert "$SPARSE_IMAGE" -format UDZO -o "$DMG"
# Delete sparseImage
rm "$SPARSE_IMAGE"
PRODUCT_DIR="install/mac/"
if [ ! -d "${PRODUCT_DIR}" ]; then
mkdir -p "${PRODUCT_DIR}"
fi
mv "$DMG" "${PRODUCT_DIR}"
notify "$APPLICATION_NAME is now packaged. You can submit this dmg file to notarization using notarize.sh"
exit 0

View File

@ -0,0 +1,728 @@
#!/usr/bin/perl -w
#============================================================================
# NAME:
# dmgutil.pl
#
# DESCRIPTION:
# Disk image creation utility.
#
# COPYRIGHT:
# Copyright (c) 2006-2008, refNum Software
# <http://www.refnum.com/>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# o Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# o Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# o Neither the name of refNum Software nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#============================================================================
# Imports
#----------------------------------------------------------------------------
use strict;
use Getopt::Long;
#============================================================================
# Constants
#----------------------------------------------------------------------------
my $kLogging = "-quiet";
my $Rez = "/Developer/Tools/Rez";
my $SetFile = "/Developer/Tools/SetFile";
my $kManPage = <<MANPAGE;
NAME
dmgutil -- create, adjust, and compress a distribution disk image
SYNOPSIS
dmgutil --help
dmgutil --open --volume=name file
dmgutil --set [--x=integer] [--y=integer]
[--width=integer] [--height=integer]
[--iconsize=integer] [--icon=file]
[--background=file] [--bgcol=r,g,b]
[--toolbar=boolean] file
dmgutil --close --volume=name file
DESCRIPTION
dmgutil is used to create distribution disk images, and to adjust
the Finder view settings of these volumes or their contents.
It can be invoked in three modes: open, set, and close.
OPEN MODE
Open Mode has the following options:
--open Select open mode.
--volume=name The volume name for the disk image.
file The output path for the .dmg file.
SET MODE
Set Mode has the following options:
--set Select set mode.
--x=integer The x coordinate of the item.
--y=integer The y coordinate of the item.
--width=integer The width for a Finder window.
--height=integer The height for a Finder window.
--iconsize=integer The icon size for a Finder window.
--icon=file.icns The icon to apply to the item.
--background=file The background picture for a Finder window.
--bgcol=r,g,b The background color for a Finder window.
--toolbar=boolean The toolbar state for a Finder window.
file The file or folder to be set.
CLOSE MODE
Close Mode has the following options:
--close Select close mode.
--volume=name The volume name for the disk image.
file The output path for the .dmg file.
EXAMPLES
OPEN MODE
To create a new disk image for "MyApp 1.0":
dmgutil.pl --open --volume="MyApp 1.0" myapp_1.0.dmg
SET MODE
To set the position of a file or folder:
dmgutil.pl --set --x=100 --y=100 "/Volumes/MyApp 1.0/Read Me.rtf"
dmgutil.pl --set --x=200 --y=100 "/Volumes/MyApp 1.0/MyApp.app"
To set the window size for the volume:
dmgutil.pl --set --width=300 --height=200 "/Volumes/MyApp 1.0"
To set the icon size for the volume:
dmgutil.pl --set --iconsize=128 "/Volumes/MyApp 1.0"
To set a custom icon for a volume, folder, or file:
dmgutil.pl --set --icon=volume.icns "/Volumes/MyApp 1.0"
dmgutil.pl --set --icon=folder.icns "/Volumes/MyApp 1.0/Extras"
dmgutil.pl --set --icon=readme.icns "/Volumes/MyApp 1.0/Read Me.rtf"
To set the background picture for the volume:
dmgutil.pl --set --background=flowers.jpg "/Volumes/MyApp 1.0"
To set the background color for the volume:
dmgutil.pl --set --bgcol=0,65535,0 "/Volumes/MyApp 1.0"
To hide the toolbar for the volume:
dmgutil.pl --set --toolbar=false "/Volumes/MyApp 1.0"
Multiple flags may be combined, to set all of the properties of an
item simultaneously.
CLOSE MODE
To unmount and compress the disk image created for "MyApp 1.0":
dmgutil.pl --close --volume="MyApp 1.0" myapp_1.0.dmg
VERSION
dmgutil 1.1
COPYRIGHT
Copyright (c) refNum Software http://www.refnum.com/
MANPAGE
#============================================================================
# appleScript : Execute an AppleScript.
#----------------------------------------------------------------------------
sub appleScript
{
# Retrieve our parameters
my ($theScript) = @_;
# Save the script
my $theFile = "/tmp/dmgutil_applescript.txt";
open( OUTPUT, ">$theFile") or die "Can't open $theFile for writing: $!\n";
print OUTPUT $theScript;
close(OUTPUT);
# And execute it
system("osascript", $theFile);
unlink($theFile);
}
#============================================================================
# isVolume : Is a path to the root of a volume?
#----------------------------------------------------------------------------
sub isVolume
{
# Retrieve our parameters
my ($thePath) = @_;
# Check the state
#
# After stripping out the leading /Volumes, any further slashes
# indicate we have a folder rather than a volume.
$thePath =~ s/\/Volumes\///;
my $isVolume = ($thePath =~ /.*\/.*/) ? 0 : 1;
return($isVolume);
}
#============================================================================
# setFolderState : Set the state for a folder.
#----------------------------------------------------------------------------
sub setFolderState
{
# Retrieve our parameters
my ($thePath, $iconSize, $flagToolbar, $bgImage, $bgColor) = @_;
# Initialise ourselves
my $cmdBackground = "";
my $cmdIconSize = "";
my $cmdToolbar = "";
# Prepare the background
#
# As of 10.5, the Finder refuses to manipulate files whose names start with
# a period (rdar://5582578). As such we need to use an underscore for the
# image, then make it invisible using SetFile.
if (-f $bgImage)
{
$bgImage =~ /.*\.(\w+)/;
my $dstImage = "$thePath/_Background.$1";
`cp "$bgImage" "$dstImage"`;
$cmdBackground .= "set theImage to posix file \"$dstImage\"\n";
$cmdBackground .= " set background picture of theOptions to theImage\n";
$cmdBackground .= " do shell script \"/Developer/Tools/SetFile -a V '$dstImage'\"\n";
}
elsif ($bgColor ne "")
{
$cmdBackground = "set background color of theOptions to {$bgColor} as RGB color";
}
# Prepare the icon size
if ($iconSize != 0)
{
$cmdIconSize = "set icon size of theOptions to $iconSize";
}
# Prepare the toolbar
#
# The window must be made visible in order to change the toolbar state.
if ($flagToolbar ne "")
{
$cmdToolbar = " open theWindow \n";
$cmdToolbar .= " set toolbar visible of theWindow to $flagToolbar \n";
$cmdToolbar .= " close theWindow \n";
}
# Identify the target
#
# AppleScript requires the correct nomenclature for the target item.
my $theTarget = "folder \"$thePath\"";
if (isVolume($thePath))
{
$theTarget =~ s/folder "\/Volumes\//disk "/;
}
# Set the folder state
#
# Once a change has been made, it must be flushed to disk with update.
my $theScript = "";
$theScript .= "tell application \"Finder\"\n";
$theScript .= " set theTarget to $theTarget \n";
$theScript .= " set theWindow to window of theTarget \n";
$theScript .= "\n";
$theScript .= " set current view of theWindow to icon view \n";
$theScript .= " set theOptions to icon view options of theWindow \n";
$theScript .= " set arrangement of theOptions to not arranged \n";
$theScript .= "\n";
$theScript .= " $cmdBackground\n";
$theScript .= " $cmdIconSize \n";
$theScript .= " $cmdToolbar \n";
$theScript .= "\n";
$theScript .= " update theTarget\n";
$theScript .= "end tell";
appleScript($theScript);
}
#============================================================================
# setCustomIcon : Set a custom icon.
#----------------------------------------------------------------------------
sub setCustomIcon
{
# Retrieve our parameters
my ($thePath, $theIcon) = @_;
# Validate our state
#
# We require several tools inside /Developer/Tools.
die("Setting an icon requires $Rez") if (! -e $Rez);
die("Setting an icon requires $SetFile") if (! -e $SetFile);
# Prepare the flags
#
# Prior to Mac OS X 10.4, SetFile can only set an attribute if it is
# first cleared (rdar://3738867).
my $sysVers = `uname -r`;
my $setHidden = ($sysVers =~ /^[0-7]\./) ? "vV" : "V";
my $setIcon = ($sysVers =~ /^[0-7]\./) ? "cC" : "C";
# Set a volume icon
#
# Volume custom icons are contained in a .VolumeIcon.icns file.
if (isVolume($thePath))
{
my $iconFile = "$thePath/.VolumeIcon.icns";
`cp "$theIcon" "$iconFile"`;
`$SetFile -a $setHidden "$iconFile"`;
`$SetFile -a $setIcon "$thePath"`;
}
# Set a folder icon
#
# Folder custom icons are contained in an ('icns', -16455) resource,
# placed in an invisible "Icon\r" file inside the folder.
elsif (-d $thePath)
{
my $iconFile = "$thePath/Icon\r";
my $tmpR = "/tmp/dmgutil.r";
`echo "read 'icns' (-16455) \\"$theIcon\\";\n" > $tmpR`;
`cd /tmp; $Rez dmgutil.r -append -o "$iconFile"`;
`$SetFile -a $setHidden "$iconFile"`;
`$SetFile -a $setIcon "$thePath"`;
unlink($tmpR);
}
# Set a file icon
#
# File custom icons are contained in an ('icns', -16455) resource.
else
{
my $tmpR = "/tmp/dmgutil.r";
`echo "read 'icns' (-16455) \\"$theIcon\\";\n" > $tmpR`;
`cd /tmp; $Rez dmgutil.r -append -o "$thePath"`;
`$SetFile -a $setIcon "$thePath"`;
unlink($tmpR);
}
}
#============================================================================
# setWindowPos : Set the position of a window.
#----------------------------------------------------------------------------
sub setWindowPos
{
# Retrieve our parameters
my ($thePath, $posX, $posY, $theWidth, $theHeight) = @_;
# Initialise ourselves
my $bottom = $posY + $theHeight;
my $right = $posX + $theWidth;
# Identify the target
#
# AppleScript requires the correct nomenclature for the target item.
my $theTarget = "folder \"$thePath\"";
if (isVolume($thePath))
{
$theTarget =~ s/folder "\/Volumes\//disk "/;
}
# Set the window position
#
# In theory, the "set bounds" command is all that should be necessary
# to set the bounds of a Finder window.
#
# Unfortunately, under 10.4 this will result in a window that will be
# taller than the specified size when the window is next opened.
#
# To reliably set the bounds of a window we must open the window, show
# the status bar, and set the window bounds to be 20 pixels taller (the
# height of the status bar) than necessary.
#
# The status bar can then be hidden, the window closed, and the bounds
# bounds will be the desired size when the window is next opened.
my $theScript = "";
$theScript .= "tell application \"Finder\"\n";
$theScript .= " set theTarget to $theTarget \n";
$theScript .= " set theWindow to window of theTarget \n";
$theScript .= "\n";
$theScript .= " open theWindow \n";
$theScript .= " set statusbar visible of theWindow to true \n";
$theScript .= " set bounds of theWindow to {$posX, $posY, $right, $bottom+20} \n";
$theScript .= " set statusbar visible of theWindow to false \n";
$theScript .= " close theWindow \n";
$theScript .= "\n";
$theScript .= "end tell";
appleScript($theScript);
}
#============================================================================
# setIconPos : Set the position of an icon.
#----------------------------------------------------------------------------
sub setIconPos
{
# Retrieve our parameters
my ($theFile, $posX, $posY) = @_;
# Identify the target
#
# Since the 'posix file' command follows symlinks, in order to set the
# position of a symlink (vs its target) we need to use an HFS path and
# reference it as a file rather than an alias.
my $theTarget = "alias (posix file \"$theFile\")";
if (-l $theFile)
{
$theTarget = $theFile;
$theTarget =~ s/\/Volumes\///;
$theTarget =~ s/\//:/g;
$theTarget = "file \"$theTarget\"";
}
# Set the icon position
#
# Once a change has been made, it must be flushed to disk with update.
my $theScript = "";
$theScript .= "tell application \"Finder\"\n";
$theScript .= " set theTarget to $theTarget \n";
$theScript .= "\n";
$theScript .= " set position of theTarget to {$posX, $posY} \n";
$theScript .= " update theTarget\n";
$theScript .= "end tell";
appleScript($theScript);
}
#============================================================================
# doOpen : Open a new disk image.
#----------------------------------------------------------------------------
sub doOpen
{
# Retrieve our parameters
my ($dmgFile, $volName) = @_;
# Clean up any previous image
system("rm", "-f", "$dmgFile.sparseimage");
system("rm", "-f", "$dmgFile");
# Create the image
#
# A large sparse disk image is created, which will be shrunk down
# and compressed when the disk image is finally closed.
print " creating $dmgFile\n" if ($kLogging eq "-quiet");
system("hdiutil", "create", $dmgFile,
"-volname", $volName,
"-megabytes", "1000",
"-type", "SPARSE",
"-fs", "HFS+",
$kLogging);
system("hdiutil", "mount", $kLogging, "$dmgFile.sparseimage");
}
#============================================================================
# doClose : Close a disk image.
#----------------------------------------------------------------------------
sub doClose
{
# Retrieve our parameters
my ($dmgFile, $volName) = @_;
# Bless the volume
#
# Blessing the volume ensures that the volume always opens in the current
# view, overriding the user's "Open new windows in column view" preference.
system("bless", "--openfolder", "/Volumes/$volName");
# Compress the image
#
# On 10.5, the disk image must be ejected rather than unmounted to allow
# it to be converted from a sparse image to a compressed image.
print " compressing $dmgFile\n" if ($kLogging eq "-quiet");
system("hdiutil", "eject", $kLogging, "/Volumes/$volName");
system("hdiutil", "convert", "$dmgFile.sparseimage",
"-format", "UDZO",
"-o", $dmgFile,
"-imagekey", "zlib-level=9",
$kLogging);
# Clean up
system("rm", "-f", "$dmgFile.sparseimage");
}
#============================================================================
# doSet : Set a file/folder state.
#----------------------------------------------------------------------------
sub doSet
{
# Retrieve our parameters
my ($thePath, $posX, $posY, $theWidth, $theHeight, $iconSize, $theIcon, $bgImage, $bgColor, $flagToolbar) = @_;
# Set the custom icon
if ($theIcon ne "")
{
setCustomIcon($thePath, $theIcon);
}
# Set the folder state
if ($iconSize != 0 || $bgImage ne "" || $bgColor ne "" || $flagToolbar ne "")
{
setFolderState($thePath, $iconSize, $flagToolbar, $bgImage, $bgColor);
}
# Set the position
#
# Window position must be set after applying the folder state.
if ($posX != 0 && $posY != 0)
{
if ($theWidth != 0 && $theHeight != 0)
{
setWindowPos($thePath, $posX, $posY, $theWidth, $theHeight);
}
else
{
setIconPos($thePath, $posX, $posY);
}
}
}
#============================================================================
# dmgUtil : Manipulate a disk image.
#----------------------------------------------------------------------------
sub dmgUtil
{
# Retrieve our parameters
my ($doOpen, $doClose, $doSet) = (0, 0, 0);
my ($posX, $posY, $theWidth, $theHeight, $iconSize) = (0, 0, 0, 0, 0);
my ($volName, $theIcon, $bgImage, $bgColor, $flagToolbar) = ("", "", "", "", "");
GetOptions( "--open+", => \$doOpen,
"--close+", => \$doClose,
"--set+", => \$doSet,
"--volume=s", => \$volName,
"--x=i", => \$posX,
"--y=i", => \$posY,
"--width=i", => \$theWidth,
"--height=i", => \$theHeight,
"--iconsize=i", => \$iconSize,
"--icon=s", => \$theIcon,
"--background=s", => \$bgImage,
"--bgcol=s", => \$bgColor,
"--toolbar=s", => \$flagToolbar);
my ($thePath) = @ARGV;
$thePath = "" if (!defined($thePath));
# Perform the action
if ($doOpen && $thePath ne "" && $volName ne "")
{
doOpen($thePath, $volName);
}
elsif ($doClose && $thePath ne "" && $volName ne "")
{
doClose($thePath, $volName);
}
elsif ($doSet && $thePath ne "")
{
doSet($thePath, $posX, $posY, $theWidth, $theHeight, $iconSize, $theIcon, $bgImage, $bgColor, $flagToolbar);
}
else
{
print $kManPage;
}
}
#============================================================================
# Script entry point
#----------------------------------------------------------------------------
dmgUtil();

View File

@ -0,0 +1,145 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<title>refNum &rsaquo; dmgutil</title>
<!--HEAD_BEGIN-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF8">
<script type="text/javascript" src="src/js/refnum.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-19561062-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<link type="text/css" href="src/css/refnum.css" rel=stylesheet>
<!--HEAD_END-->
</head>
<body>
<div id="content">
<!--#include virtual="/src/inc/nav.inc" -->
<script language="javascript">navSelect("dev")</script>
<a href="index.html">
<div id="page_strap_frame" style="background:url('src/img/page_strap_dmgutil.png');">
<div id="page_strap_content" style="background:url('src/img/page_strap_dmgutil.jpg'); background-repeat: no-repeat; background-position: 22px 16px;"></div></div></a>
<div id="page_body">
<div id="page_content">
<table>
<tr>
<td valign=top>
<ul id="menu">
<li class="subtitle">dmgutil</li>
<li><a href="index.html">Home</a></li>
<li><a href="licence.html">Licence</a></li>
<li><a href="version.html">Version History</a></li>
</ul>
</td>
<td valign=top>
<h1>dmgutil</h1>
<p>
dmgutil is a command line tool for creating, configuring, and compressing Mac OS X
disk images for distribution.
</p>
<p>
dmgutil allows you to automate your build process, preparing a release without pre-existing
templates or user interaction.
</p>
<p>
dmgutil can adjust the size, position, and appearance of Finder windows. It can also apply
custom icons to volumes, folders, or files and assign a license agreement.
</p>
<div class="box_button">
<a href="http://www.refnum.com/files/dmgutil.zip" class="button icon_download"><span><span class="icon"></span>Download</span></a>
<span class="padding20">&nbsp;</span>
<a href="http://github.com/refnum/dmgutil" class="button icon_github"><span><span class="icon"></span>GitHub</span></a>
</div>
<h2>Using dmgutil</h2>
<p>
To create a new disk image for your "MyApp 1.0" release, invoke dmgutil as:
</p>
<div class="box_code_yellow">dmgutil.pl --open --volume="MyApp 1.0" myapp_1.0.dmg
</div>
<p>
Once the disk image has been populated with files, invoke dmgutil to configure it:
</p>
<div class="box_code_yellow">dmgutil.pl --set --iconsize=128 --background=flowers.jpg "/Volumes/MyApp 1.0"
dmgutil.pl --set --x=20 --y=60 --width=300 --height=200 "/Volumes/MyApp 1.0"
</div>
<p>
You can also use dmgutil to set the position of file or folder icons within the window:
</p>
<div class="box_code_yellow">dmgutil.pl --set --x=100 --y=100 "/Volumes/MyApp 1.0/Read Me.rtf"
dmgutil.pl --set --x=200 --y=100 "/Volumes/MyApp 1.0/MyApp.app"
</div>
<p>
dmgutil can also adjust the background color or toolbar visibility of the window, and
apply custom icons to the volume or its contents.
</p>
<p>
Once the volume has been prepared, compress the disk image with:
</p>
<div class="box_code_yellow">dmgutil.pl --close --volume="MyApp 1.0" myapp_1.0.dmg
</div>
<p>
By invoking dmgutil from a post-build shell script, builds can be automatically converted
into a user-friendly disk image for distribution.
</p>
</td>
</tr>
</table>
</div>
</div>
<div id="page_footer"></div>
</div>
<!--FOOTER_BEGIN-->
<div id="footer">
Copyright &copy; 2006-2010 <a href="http://www.refnum.com/">refNum Software</a>
</div>
<!--FOOTER_END-->
</body>
</html>

View File

@ -0,0 +1,124 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<title>refNum &rsaquo; dmgutil &rsaquo; Licence</title>
<!--HEAD_BEGIN-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF8">
<script type="text/javascript" src="src/js/refnum.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-19561062-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<link type="text/css" href="src/css/refnum.css" rel=stylesheet>
<!--HEAD_END-->
</head>
<body>
<div id="content">
<!--#include virtual="/src/inc/nav.inc" -->
<script language="javascript">navSelect("dev")</script>
<a href="index.html">
<div id="page_strap_frame" style="background:url('src/img/page_strap_dmgutil.png');">
<div id="page_strap_content" style="background:url('src/img/page_strap_dmgutil.jpg'); background-repeat: no-repeat; background-position: 22px 16px;"></div></div></a>
<div id="page_body">
<div id="page_content">
<table>
<tr>
<td valign=top>
<ul id="menu">
<li class="subtitle">dmgutil</li>
<li><a href="index.html">Home</a></li>
<li><a href="licence.html">Licence</a></li>
<li><a href="version.html">Version History</a></li>
</ul>
</td>
<td valign=top>
<h1>Licence</h1>
<p>
dmgutil is distributed under the Open Source BSD licence:
</p>
<div class="box_licence">
<p>
dmgutil is Copyright &copy; 2006-2010, refNum Software<br>
All rights reserved.
</p>
<p>
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
</p>
<ul>
<li>Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.</li>
<li>Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.</li>
<li>Neither the name of refNum Software, Nano, nor the names of its contributors may be
used to endorse or promote products derived from this software without specific prior
written permission.</li>
</ul>
<p>
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</p>
</div>
<p>
This licence allows usage in both Open Source and commercial software, and does not require any
reciprocal licencing or release of source code when incorporated into commercial software.
</p>
</td>
</tr>
</table>
</div>
</div>
<div id="page_footer"></div>
</div>
<!--FOOTER_BEGIN-->
<div id="footer">
Copyright &copy; 2006-2010 <a href="http://www.refnum.com/">refNum Software</a>
</div>
<!--FOOTER_END-->
</body>
</html>

View File

@ -0,0 +1,380 @@
/* Global */
html {
overflow-y: scroll;
}
body {
background: #600d0d url("../img/background.jpg") no-repeat fixed center top;
font: 12px "Lucida Grande", "Arial", sans-serif;
}
#content {
width: 820px;
min-height: 500px;
margin: 75px auto 10px auto;
}
#footer {
color: #eeeeee;
font: 9px "Monaco", "Courier", monospace;
text-align: center;
text-shadow: #444444 0px 2px 2px;
}
#footer a {
color: #eeeeee;
border-bottom: none;
text-decoration: none;
}
.caption {
color: #cccccc;
font: italic 11px "Lucida Grande", "Arial", sans-serif;
}
h1, h2, h3, h4, h5 {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.05);
}
a img { border: none; }
a:focus { outline: none; }
a:link { border-bottom: 1px dotted; text-decoration: none; }
a:visited { border-bottom: 1px dotted; text-decoration: none; }
a:active { border-bottom: 1px dotted; text-decoration: none; }
a:hover { border-bottom: 1px dotted; text-decoration: none; }
a.plain { border-bottom: none; }
h1 { font-size: x-large; }
h2 { font-size: large; }
h3 { font-size: medium; }
h4 { font-size: small; }
h5 { font-size: x-small; }
h6 { font-size: xx-small; }
/* Navigation */
#nav {
width: 820px;
height: 45px;
margin-top: 20px;
margin-bottom: 10px;
}
#nav_bar {
float: right;
padding-right: 15px;
}
#nav a {
display: -moz-inline-stack;
display: inline-block;
height: 45px;
border-bottom: none;
}
#nav a#apps { width:78px; }
#nav a#apps { background-image:url("../img/nav_apps_normal.png"); }
#nav a#apps:hover { background-image:url("../img/nav_apps_hover.png"); }
#nav a#apps.selected { background-image:url("../img/nav_apps_selected.png"); }
#nav a#projects { width:90px; }
#nav a#projects { background-image:url("../img/nav_projects_normal.png"); }
#nav a#projects:hover { background-image:url("../img/nav_projects_hover.png"); }
#nav a#projects.selected { background-image:url("../img/nav_projects_selected.png"); }
#nav a#contact { width:86px; }
#nav a#contact { background-image:url("../img/nav_contact_normal.png"); }
#nav a#contact:hover { background-image:url("../img/nav_contact_hover.png"); }
#nav a#contact.selected { background-image:url("../img/nav_contact_selected.png"); }
#nav a#dev { width:78px; }
#nav a#dev { background-image:url("../img/nav_dev_normal.png"); }
#nav a#dev:hover { background-image:url("../img/nav_dev_hover.png"); }
#nav a#dev.selected { background-image:url("../img/nav_dev_selected.png"); }
/* Menu */
#menu {
margin: 0px;
padding: 0px;
width: 140px;
font-size: 11px;
line-height: 17px;
list-style-type: none;
}
#menu .subtitle {
margin-top: 10px;
font-weight: bold;
color: #222222;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.05);
}
#menu a {
color: #444444;
text-decoration: none;
border-bottom: none;
}
#menu li a:hover,
#menu li a:active {
border-bottom: 1px dotted;
text-decoration: none;
}
/* Page */
#page_strap_frame {
width: 820px;
height: 215px;
}
#page_strap_content {
width: 820px;
height: 215px;
}
#page_body {
background: url("../img/page_body.png") repeat-y;
width: 820px;
min-height: 190px;
}
#page_content {
padding: 5px 40px;
}
#page_footer {
background: url("../img/page_footer.png");
width: 820px;
height: 30px;
}
/* Home */
#logo {
width: 440px;
height: 620px;
margin: auto;
}
#logo_offset {
height: 120px;
}
/* Tables */
table.icons {
border-spacing: 40px 5px;
}
table.icons td {
vertical-align: top;
text-align: center;
width: 128px;
}
table.icons img {
width: 128px;
height: 128px;
}
table.badges {
margin: 40px auto 20px auto;
width: 80%;
}
table.list_header {
background: #ddddff;
font-weight: bold;
line-height: 200%;
margin: 20px 10px 0px 10px;
padding-left: 10px;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
width: 95%;
-moz-border-radius-topright: 5px;
-moz-border-radius-topleft: 5px;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
table.list {
background: #eeeeff;
background: -webkit-gradient(linear, left top, left bottom, from(#eeeeff), to(#fcfcff));
margin: 0px 10px 0px 10px;
padding-left: 10px;
width: 95%;
-moz-border-radius-bottomright: 5px;
-moz-border-radius-bottomleft: 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
table.list th {
font-weight: normal;
text-align: left;
width: 150px;
}
/* Boxes */
.box {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.box_image {
border: 1px solid #888888;
}
.box_button {
text-align: center;
margin: 24px;
}
.box_code_yellow {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
font-family: monospace;
white-space: pre;
font-size: 12px;
border: 1px solid #bbbbbb;
padding: 10px;
background: #ffffe0;
background: -webkit-gradient(linear, left top, right top, from(#fffff0), to(#fffffd));
}
.box_licence {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
font-family: monospace;
font-size: 12px;
background-color: #eeeeff;
border: 1px solid #bbbbbb;
padding: 10px;
background: -webkit-gradient(linear, left top, right top, from(#eeeeff), to(#fefeff));
}
/* Layout */
.padding20 {
padding: 20px;
}
.width450 {
width: 450px;
height: auto;
}
/* Buttons */
a.button {
display: inline-block;
height: 23px;
padding: 0 0 0 3px;
font-size: 11px;
font-weight: bold;
color: #333333;
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.9);
background: url('../img/button_thin_cell.png') 0 0 no-repeat;
white-space: nowrap;
border: none;
overflow: visible;
cursor: pointer;
text-decoration: none;
}
a.button>span {
display: block;
height: 23px;
padding: 0 10px 0 8px;
line-height: 23px;
background: url('../img/button_thin_cell.png') 100% 0 no-repeat;
}
a.button:hover,
a.button:focus {
color: #ffffff;
text-decoration: none;
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-position: 0 -30px;
}
a.button:hover>span,
a.button:focus>span {
background-position: 100% -30px;
}
a.button:active {
background-position: 0 -60px;
}
a.button:active>span {
background-position: 100% -60px;
}
a.icon_download .icon,
a.icon_github .icon {
float: left;
margin-left: -4px;
width: 18px;
height: 22px;
background: url('../img/button_thin_icons.png') 0 0 no-repeat;
}
a.icon_download .icon {
background-position: -0px 0;
}
a.icon_download:hover .icon,
a.icon_download:focus .icon {
background-position: -0px -25px;
}
a.icon_github .icon {
background-position: -20px 0;
}
a.icon_github:hover .icon,
a.icon_github:focus .icon {
background-position: -20px -25px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,34 @@
function loadImage(theURL)
{
theImage = new Image;
theImage.src = theURL;
}
function navSelect(theID)
{
document.getElementById(theID).setAttribute("class", "selected");
}
var prevOnLoad = window.onload;
window.onload = function()
{
if (prevOnLoad)
prevOnLoad();
// Preload images
if (document.domain != "")
{
loadImage("/src/img/nav_apps_hover.png");
loadImage("/src/img/nav_apps_selected.png");
loadImage("/src/img/nav_projects_hover.png");
loadImage("/src/img/nav_projects_selected.png");
loadImage("/src/img/nav_contact_hover.png");
loadImage("/src/img/nav_contact_selected.png");
loadImage("/src/img/nav_dev_hover.png");
loadImage("/src/img/nav_dev_selected.png");
}
}

View File

@ -0,0 +1,109 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<title>refNum &rsaquo; dmgutil &rsaquo; Version History</title>
<!--HEAD_BEGIN-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF8">
<script type="text/javascript" src="src/js/refnum.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-19561062-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<link type="text/css" href="src/css/refnum.css" rel=stylesheet>
<!--HEAD_END-->
</head>
<body>
<div id="content">
<!--#include virtual="/src/inc/nav.inc" -->
<script language="javascript">navSelect("dev")</script>
<a href="index.html">
<div id="page_strap_frame" style="background:url('src/img/page_strap_dmgutil.png');">
<div id="page_strap_content" style="background:url('src/img/page_strap_dmgutil.jpg'); background-repeat: no-repeat; background-position: 22px 16px;"></div></div></a>
<div id="page_body">
<div id="page_content">
<table>
<tr>
<td valign=top>
<ul id="menu">
<li class="subtitle">dmgutil</li>
<li><a href="index.html">Home</a></li>
<li><a href="licence.html">Licence</a></li>
<li><a href="version.html">Version History</a></li>
</ul>
</td>
<td valign=top>
<h1>Version History</h1>
<h2>dmgutil 1.3</h2>
<ul>
<li>Released 2008/05/01
<li>Added workaround for Finder bug when setting background image on 10.5
<li>Added workaround for hdiutil bug when closing disk image on 10.5
</ul>
<h2>dmgutil 1.2</h2>
<ul>
<li>Released 2007/03/07
<li>Fixed typo in zlib-level flag, was defaulting to 1 not 9
</ul>
<h2>dmgutil 1.1</h2>
<ul>
<li>Released 2006/07/31
<li>Added <tt>--icon</tt> parameter to set custom icons
<li>Added <tt>--bgcol</tt> parameter to set background color
<li>Added <tt>--toolbar</tt> flag to control toolbar visibility
</ul>
<h2>dmgutil 1.0</h2>
<ul>
<li>Released 2006/07/22
<li>Initial release
</ul>
</td>
</tr>
</table>
</div>
</div>
<div id="page_footer"></div>
</div>
<!--FOOTER_BEGIN-->
<div id="footer">
Copyright &copy; 2006-2010 <a href="http://www.refnum.com/">refNum Software</a>
</div>
<!--FOOTER_END-->
</body>
</html>

View File

@ -0,0 +1,738 @@
#!/usr/bin/perl -w
#============================================================================
# NAME:
# dmgutil.pl
#
# DESCRIPTION:
# Disk image creation utility.
#
# COPYRIGHT:
# Copyright (c) 2006-2009, refNum Software
# <http://www.refnum.com/>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# o Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# o Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# o Neither the name of refNum Software nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#============================================================================
# Imports
#----------------------------------------------------------------------------
use strict;
use Getopt::Long;
#============================================================================
# Constants
#----------------------------------------------------------------------------
my $kLogging = "-quiet";
my $Rez = "/Developer/Tools/Rez";
my $SetFile = "/Developer/Tools/SetFile";
my $kManPage = <<MANPAGE;
NAME
dmgutil -- create, adjust, and compress a distribution disk image
SYNOPSIS
dmgutil --help
dmgutil --open --volume=name file
dmgutil --set [--x=integer] [--y=integer]
[--width=integer] [--height=integer]
[--iconsize=integer] [--icon=file]
[--background=file] [--bgcol=r,g,b]
[--toolbar=boolean] file
dmgutil --close --volume=name [--license=file] file
DESCRIPTION
dmgutil is used to create distribution disk images, and to adjust
the Finder view settings of these volumes or their contents.
It can be invoked in three modes: open, set, and close.
OPEN MODE
Open Mode has the following options:
--open Select open mode.
--volume=name The volume name for the disk image.
file The output path for the .dmg file.
SET MODE
Set Mode has the following options:
--set Select set mode.
--x=integer The x coordinate of the item.
--y=integer The y coordinate of the item.
--width=integer The width for a Finder window.
--height=integer The height for a Finder window.
--iconsize=integer The icon size for a Finder window.
--icon=file.icns The icon to apply to the item.
--background=file The background picture for a Finder window.
--bgcol=r,g,b The background color for a Finder window.
--toolbar=boolean The toolbar state for a Finder window.
file The file or folder to be set.
CLOSE MODE
Close Mode has the following options:
--close Select close mode.
--volume=name The volume name for the disk image.
--license=file The license agreement resource.
file The output path for the .dmg file.
EXAMPLES
OPEN MODE
To create a new disk image for "MyApp 1.0":
dmgutil.pl --open --volume="MyApp 1.0" myapp_1.0.dmg
SET MODE
To set the position of a file or folder:
dmgutil.pl --set --x=100 --y=100 "/Volumes/MyApp 1.0/Read Me.rtf"
dmgutil.pl --set --x=200 --y=100 "/Volumes/MyApp 1.0/MyApp.app"
To set the window size for the volume:
dmgutil.pl --set --width=300 --height=200 "/Volumes/MyApp 1.0"
To set the icon size for the volume:
dmgutil.pl --set --iconsize=128 "/Volumes/MyApp 1.0"
To set a custom icon for a volume, folder, or file:
dmgutil.pl --set --icon=volume.icns "/Volumes/MyApp 1.0"
dmgutil.pl --set --icon=folder.icns "/Volumes/MyApp 1.0/Extras"
dmgutil.pl --set --icon=readme.icns "/Volumes/MyApp 1.0/Read Me.rtf"
To set the background picture for the volume:
dmgutil.pl --set --background=flowers.jpg "/Volumes/MyApp 1.0"
To set the background color for the volume:
dmgutil.pl --set --bgcol=0,65535,0 "/Volumes/MyApp 1.0"
To hide the toolbar for the volume:
dmgutil.pl --set --toolbar=false "/Volumes/MyApp 1.0"
Multiple flags may be combined, to set all of the properties of an
item simultaneously.
CLOSE MODE
To unmount and compress the disk image created for "MyApp 1.0":
dmgutil.pl --close --volume="MyApp 1.0" myapp_1.0.dmg
VERSION
dmgutil 1.1
COPYRIGHT
Copyright (c) refNum Software http://www.refnum.com/
MANPAGE
#============================================================================
# appleScript : Execute an AppleScript.
#----------------------------------------------------------------------------
sub appleScript
{
# Retrieve our parameters
my ($theScript) = @_;
# Save the script
my $theFile = "/tmp/dmgutil_applescript.txt";
open( OUTPUT, ">$theFile") or die "Can't open $theFile for writing: $!\n";
print OUTPUT $theScript;
close(OUTPUT);
# And execute it
system("osascript", $theFile);
unlink($theFile);
}
#============================================================================
# isVolume : Is a path to the root of a volume?
#----------------------------------------------------------------------------
sub isVolume
{
# Retrieve our parameters
my ($thePath) = @_;
# Check the state
#
# After stripping out the leading /Volumes, any further slashes
# indicate we have a folder rather than a volume.
$thePath =~ s/\/Volumes\///;
my $isVolume = ($thePath =~ /.*\/.*/) ? 0 : 1;
return($isVolume);
}
#============================================================================
# setFolderState : Set the state for a folder.
#----------------------------------------------------------------------------
sub setFolderState
{
# Retrieve our parameters
my ($thePath, $iconSize, $flagToolbar, $bgImage, $bgColor) = @_;
# Initialise ourselves
my $cmdBackground = "";
my $cmdIconSize = "";
my $cmdToolbar = "";
# Prepare the background
#
# As of 10.5, the Finder refuses to manipulate files whose names start with
# a period (rdar://5582578). As such we need to use an underscore for the
# image, then make it invisible using SetFile.
if (-f $bgImage)
{
$bgImage =~ /.*\.(\w+)/;
my $dstImage = "$thePath/_Background.$1";
`cp "$bgImage" "$dstImage"`;
$cmdBackground .= "set theImage to posix file \"$dstImage\"\n";
$cmdBackground .= " set background picture of theOptions to theImage\n";
$cmdBackground .= " do shell script \"/Developer/Tools/SetFile -a V '$dstImage'\"\n";
}
elsif ($bgColor ne "")
{
$cmdBackground = "set background color of theOptions to {$bgColor} as RGB color";
}
# Prepare the icon size
if ($iconSize != 0)
{
$cmdIconSize = "set icon size of theOptions to $iconSize";
}
# Prepare the toolbar
#
# The window must be made visible in order to change the toolbar state.
if ($flagToolbar ne "")
{
$cmdToolbar = " open theWindow \n";
$cmdToolbar .= " set toolbar visible of theWindow to $flagToolbar \n";
$cmdToolbar .= " close theWindow \n";
}
# Identify the target
#
# AppleScript requires the correct nomenclature for the target item.
my $theTarget = "folder \"$thePath\"";
if (isVolume($thePath))
{
$theTarget =~ s/folder "\/Volumes\//disk "/;
}
# Set the folder state
#
# Once a change has been made, it must be flushed to disk with update.
my $theScript = "";
$theScript .= "tell application \"Finder\"\n";
$theScript .= " set theTarget to $theTarget \n";
$theScript .= " set theWindow to window of theTarget \n";
$theScript .= "\n";
$theScript .= " set current view of theWindow to icon view \n";
$theScript .= " set theOptions to icon view options of theWindow \n";
$theScript .= " set arrangement of theOptions to not arranged \n";
$theScript .= "\n";
$theScript .= " $cmdBackground\n";
$theScript .= " $cmdIconSize \n";
$theScript .= " $cmdToolbar \n";
$theScript .= "\n";
$theScript .= " update theTarget\n";
$theScript .= "end tell";
appleScript($theScript);
}
#============================================================================
# setCustomIcon : Set a custom icon.
#----------------------------------------------------------------------------
sub setCustomIcon
{
# Retrieve our parameters
my ($thePath, $theIcon) = @_;
# Validate our state
#
# We require several tools inside /Developer/Tools.
die("Setting an icon requires $Rez") if (! -e $Rez);
die("Setting an icon requires $SetFile") if (! -e $SetFile);
# Prepare the flags
#
# Prior to Mac OS X 10.4, SetFile can only set an attribute if it is
# first cleared (rdar://3738867).
my $sysVers = `uname -r`;
my $setHidden = ($sysVers =~ /^[0-7]\./) ? "vV" : "V";
my $setIcon = ($sysVers =~ /^[0-7]\./) ? "cC" : "C";
# Set a volume icon
#
# Volume custom icons are contained in a .VolumeIcon.icns file.
if (isVolume($thePath))
{
my $iconFile = "$thePath/.VolumeIcon.icns";
`cp "$theIcon" "$iconFile"`;
`$SetFile -a $setHidden "$iconFile"`;
`$SetFile -a $setIcon "$thePath"`;
}
# Set a folder icon
#
# Folder custom icons are contained in an ('icns', -16455) resource,
# placed in an invisible "Icon\r" file inside the folder.
elsif (-d $thePath)
{
my $iconFile = "$thePath/Icon\r";
my $tmpR = "/tmp/dmgutil.r";
`echo "read 'icns' (-16455) \\"$theIcon\\";\n" > $tmpR`;
`cd /tmp; $Rez dmgutil.r -append -o "$iconFile"`;
`$SetFile -a $setHidden "$iconFile"`;
`$SetFile -a $setIcon "$thePath"`;
unlink($tmpR);
}
# Set a file icon
#
# File custom icons are contained in an ('icns', -16455) resource.
else
{
my $tmpR = "/tmp/dmgutil.r";
`echo "read 'icns' (-16455) \\"$theIcon\\";\n" > $tmpR`;
`cd /tmp; $Rez dmgutil.r -append -o "$thePath"`;
`$SetFile -a $setIcon "$thePath"`;
unlink($tmpR);
}
}
#============================================================================
# setWindowPos : Set the position of a window.
#----------------------------------------------------------------------------
sub setWindowPos
{
# Retrieve our parameters
my ($thePath, $posX, $posY, $theWidth, $theHeight) = @_;
# Initialise ourselves
my $bottom = $posY + $theHeight;
my $right = $posX + $theWidth;
# Identify the target
#
# AppleScript requires the correct nomenclature for the target item.
my $theTarget = "folder \"$thePath\"";
if (isVolume($thePath))
{
$theTarget =~ s/folder "\/Volumes\//disk "/;
}
# Set the window position
#
# In theory, the "set bounds" command is all that should be necessary
# to set the bounds of a Finder window.
#
# Unfortunately, under 10.4 this will result in a window that will be
# taller than the specified size when the window is next opened.
#
# To reliably set the bounds of a window we must open the window, show
# the status bar, and set the window bounds to be 20 pixels taller (the
# height of the status bar) than necessary.
#
# The status bar can then be hidden, the window closed, and the bounds
# bounds will be the desired size when the window is next opened.
my $theScript = "";
$theScript .= "tell application \"Finder\"\n";
$theScript .= " set theTarget to $theTarget \n";
$theScript .= " set theWindow to window of theTarget \n";
$theScript .= "\n";
$theScript .= " open theWindow \n";
$theScript .= " set statusbar visible of theWindow to true \n";
$theScript .= " set bounds of theWindow to {$posX, $posY, $right, $bottom+20} \n";
$theScript .= " set statusbar visible of theWindow to false \n";
$theScript .= " close theWindow \n";
$theScript .= "\n";
$theScript .= "end tell";
appleScript($theScript);
}
#============================================================================
# setIconPos : Set the position of an icon.
#----------------------------------------------------------------------------
sub setIconPos
{
# Retrieve our parameters
my ($theFile, $posX, $posY) = @_;
# Identify the target
#
# Since the 'posix file' command follows symlinks, in order to set the
# position of a symlink (vs its target) we need to use an HFS path and
# reference it as a file rather than an alias.
my $theTarget = "alias (posix file \"$theFile\")";
if (-l $theFile)
{
$theTarget = $theFile;
$theTarget =~ s/\/Volumes\///;
$theTarget =~ s/\//:/g;
$theTarget = "file \"$theTarget\"";
}
# Set the icon position
#
# Once a change has been made, it must be flushed to disk with update.
my $theScript = "";
$theScript .= "tell application \"Finder\"\n";
$theScript .= " set theTarget to $theTarget \n";
$theScript .= "\n";
$theScript .= " set position of theTarget to {$posX, $posY} \n";
$theScript .= " update theTarget\n";
$theScript .= "end tell";
appleScript($theScript);
}
#============================================================================
# doOpen : Open a new disk image.
#----------------------------------------------------------------------------
sub doOpen
{
# Retrieve our parameters
my ($dmgFile, $volName) = @_;
# Clean up any previous image
system("rm", "-f", "$dmgFile.sparseimage");
system("rm", "-f", "$dmgFile");
# Create the image
#
# A large sparse disk image is created, which will be shrunk down
# and compressed when the disk image is finally closed.
print " creating $dmgFile\n" if ($kLogging eq "-quiet");
system("hdiutil", "create", $dmgFile,
"-volname", $volName,
"-megabytes", "1000",
"-type", "SPARSE",
"-fs", "HFS+",
$kLogging);
system("hdiutil", "mount", $kLogging, "$dmgFile.sparseimage");
}
#============================================================================
# doClose : Close a disk image.
#----------------------------------------------------------------------------
sub doClose
{
# Retrieve our parameters
my ($dmgFile, $volName, $theLicense) = @_;
# Bless the volume
#
# Blessing the volume ensures that the volume always opens in the current
# view, overriding the user's "Open new windows in column view" preference.
system("bless", "--openfolder", "/Volumes/$volName");
# Compress the image
#
# On 10.5, the disk image must be ejected rather than unmounted to allow
# it to be converted from a sparse image to a compressed image.
print " compressing $dmgFile\n" if ($kLogging eq "-quiet");
system("hdiutil", "eject", $kLogging, "/Volumes/$volName");
if ($theLicense ne "")
{
system("hdiutil", "unflatten", $kLogging, "$dmgFile.sparseimage");
`$Rez -a "$theLicense" -o "$dmgFile.sparseimage"`;
system("hdiutil", "flatten", $kLogging, "$dmgFile.sparseimage");
}
system("hdiutil", "convert", "$dmgFile.sparseimage",
"-format", "UDZO",
"-o", $dmgFile,
"-imagekey", "zlib-level=9",
$kLogging);
# Clean up
system("rm", "-f", "$dmgFile.sparseimage");
}
#============================================================================
# doSet : Set a file/folder state.
#----------------------------------------------------------------------------
sub doSet
{
# Retrieve our parameters
my ($thePath, $posX, $posY, $theWidth, $theHeight, $iconSize, $theIcon, $bgImage, $bgColor, $flagToolbar) = @_;
# Set the custom icon
if ($theIcon ne "")
{
setCustomIcon($thePath, $theIcon);
}
# Set the folder state
if ($iconSize != 0 || $bgImage ne "" || $bgColor ne "" || $flagToolbar ne "")
{
setFolderState($thePath, $iconSize, $flagToolbar, $bgImage, $bgColor);
}
# Set the position
#
# Window position must be set after applying the folder state.
if ($posX != 0 && $posY != 0)
{
if ($theWidth != 0 && $theHeight != 0)
{
setWindowPos($thePath, $posX, $posY, $theWidth, $theHeight);
}
else
{
setIconPos($thePath, $posX, $posY);
}
}
}
#============================================================================
# dmgUtil : Manipulate a disk image.
#----------------------------------------------------------------------------
sub dmgUtil
{
# Retrieve our parameters
my ($doOpen, $doClose, $doSet) = (0, 0, 0);
my ($posX, $posY, $theWidth, $theHeight, $iconSize) = (0, 0, 0, 0, 0);
my ($volName, $theIcon, $bgImage, $bgColor, $flagToolbar, $theLicense) = ("", "", "", "", "", "");
GetOptions( "--open+", => \$doOpen,
"--close+", => \$doClose,
"--set+", => \$doSet,
"--volume=s", => \$volName,
"--x=i", => \$posX,
"--y=i", => \$posY,
"--width=i", => \$theWidth,
"--height=i", => \$theHeight,
"--iconsize=i", => \$iconSize,
"--icon=s", => \$theIcon,
"--background=s", => \$bgImage,
"--bgcol=s", => \$bgColor,
"--toolbar=s", => \$flagToolbar,
"--license=s", => \$theLicense);
my ($thePath) = @ARGV;
$thePath = "" if (!defined($thePath));
# Perform the action
if ($doOpen && $thePath ne "" && $volName ne "")
{
doOpen($thePath, $volName);
}
elsif ($doClose && $thePath ne "" && $volName ne "")
{
doClose($thePath, $volName, $theLicense);
}
elsif ($doSet && $thePath ne "")
{
doSet($thePath, $posX, $posY, $theWidth, $theHeight, $iconSize, $theIcon, $bgImage, $bgColor, $flagToolbar);
}
else
{
print $kManPage;
}
}
#============================================================================
# Script entry point
#----------------------------------------------------------------------------
dmgUtil();

View File

@ -0,0 +1,52 @@
# Directories
PRODUCT_DIR="$BUILD_DIR/product"
function notify {
GROWLNOTIFY=`which growlnotify`
if [ -x "$GROWLNOTIFY" ]; then
$GROWLNOTIFY --name SaiCore-build --iconpath /Developer/Applications/Xcode.app --message "$1" "SaiCore"
fi
printf "\033[32m--->\033[0m $1\n"
}
function abort {
printf "\033[31merror:\033[0m $1\n"
exit 1
}
function warn {
abort "$1"
}
function checkExecutable {
if [ ! -x "$1" ]; then
abort "$1 not found"
fi
}
APP="$PRODUCT_DIR/$APPLICATION_NAME.app"
# make sure libs installed via homebrew 2.0 refer to in-app libs
notify "relinking libs ..."
# libavformat
install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libavcodec.58.dylib @executable_path/../Frameworks/libavcodec.58.dylib
install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib
install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
# libavcodec
install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib
install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
#libswresample
install_name_tool "$APP/Contents/Frameworks/libswresample.3.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
#libswscale
install_name_tool "$APP/Contents/Frameworks/libswscale.5.dylib" -change /usr/local/Cellar/ffmpeg/4.1.4_2/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
# libhogweed
install_name_tool "$APP/Contents/Frameworks/libhogweed.4.dylib" -change /usr/local/Cellar/nettle/3.4.1/lib/libnettle.6.dylib @executable_path/../Frameworks/libnettle.6.dylib
# libssl
install_name_tool "$APP/Contents/Frameworks/libssl.1.1.dylib" -change /usr/local/Cellar/openssl@1.1/1.1.1c/lib/libcrypto.1.1.dylib @executable_path/../Frameworks/libcrypto.1.1.dylib
# libvorbis
install_name_tool "$APP/Contents/Frameworks/libvorbisenc.2.dylib" -change /usr/local/Cellar/libvorbis/1.3.6/lib/libvorbis.0.dylib @executable_path/../Frameworks/libvorbis.0.dylib

View File

@ -0,0 +1,218 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define ApplicationVersion GetVersionNumbersString("..\..\build\win32\release\product\SaiCore.exe")
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{8CCA6AC7-BBF9-4DD2-8E70-A907E0FCA38F}}
AppName=SaiCore
AppVersion={#ApplicationVersion}
AppVerName=SaiCore {#ApplicationVersion}
UninstallDisplayName=SaiCore {#ApplicationVersion}
AppPublisher=SaiCore
ArchitecturesInstallIn64BitMode=x64
AppPublisherURL=https://saicore.ru
AppSupportURL=https://saicore.ru
AppUpdatesURL=https://saicore.ru/update.json
DefaultDirName={pf}\SaiCore
DefaultGroupName=SaiCore
#define ProjectRoot GetEnv('PROJECT_ROOT')
OutputDir={#ProjectRoot}\install\win32\
OutputBaseFilename=SaiCore
SetupIconFile={#ProjectRoot}\resources\win\SaiCore.ico
Compression=lzma
SolidCompression=yes
[Languages]
Name: "ru"; MessagesFile: "compiler:Languages\Russian.isl"
Name: "en"; MessagesFile: "compiler:Default.isl"
Name: "fr"; MessagesFile: "compiler:Languages\French.isl"
Name: "gr"; MessagesFile: "compiler:Languages\German.isl"
Name: "it"; MessagesFile: "compiler:Languages\Italian.isl"
Name: "sp"; MessagesFile: "compiler:Languages\Spanish.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[InstallDelete]
Type: files ; Name: "{app}\SaiCore.pdb"
Type: filesandordirs ; Name: "{app}\library"
Type: filesandordirs ; Name: "{app}\Microsoft.VC90.CRT"
Type: filesandordirs ; Name: "{app}\imageformats"
Type: filesandordirs ; Name: "{app}\iconengines"
Type: filesandordirs ; Name: "{app}\multimedia"
Type: filesandordirs ; Name: "{app}\platforms"
Type: filesandordirs ; Name: "{app}\resources"
Type: filesandordirs ; Name: "{app}\tls"
Type: filesandordirs ; Name: "{app}\translations"
Type: filesandordirs ; Name: "{app}\i18n"
Type: files ; Name: "{app}\*.dll"
#define QtLibs GetEnv('QT_BIN')
#define QtDir GetEnv('QT_DIR')
[Files]
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\microsoft\vcredist_2013.x64.exe"; DestDir:"{tmp}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\microsoft\vcredist_2015_2022.x64.exe"; DestDir:"{tmp}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\microsoft\LAVFilters-0.77.2-Installer.exe"; DestDir:"{tmp}"
Source: "{#ProjectRoot}\build\win32\release\product\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
;OpenSSL
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\openssl\openssl-3.0.15-win64\bin\libssl-3-x64.dll"; DestDir:"{app}"; Flags: ignoreversion
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\openssl\openssl-3.0.15-win64\bin\libcrypto-3-x64.dll"; DestDir:"{app}"; Flags: ignoreversion
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\openssl\win32\libeay32.dll"; DestDir:"{app}"; Flags: ignoreversion
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\openssl\win32\ssleay32.dll"; DestDir:"{app}"; Flags: ignoreversion
;Qt base dll
;Source: "SaiCore.exe"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Concurrent.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Core.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Gui.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Multimedia.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6MultimediaWidgets.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Network.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Opengl.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6PrintSupport.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Qml.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6QmlMeta.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6QmlWorkerScript.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Sql.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Svg.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6SvgWidgets.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Widgets.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Xml.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Quick.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Positioning.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6QmlModels.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6WebChannel.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6WebEngineCore.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6WebEngineWidgets.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6QuickWidgets.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt6Core5Compat.dll"; DestDir: "{app}"
Source: "{#QtLibs}\double-conversion.dll"; DestDir: "{app}"
Source: "{#QtLibs}\icudt74.dll"; DestDir: "{app}"
Source: "{#QtLibs}\icuin74.dll"; DestDir: "{app}"
Source: "{#QtLibs}\pcre2-16.dll"; DestDir: "{app}"
Source: "{#QtLibs}\harfbuzz.dll"; DestDir: "{app}"
Source: "{#QtLibs}\icuuc74.dll"; DestDir: "{app}"
Source: "{#QtLibs}\libcrypto-3-x64.dll"; DestDir: "{app}"
Source: "{#QtLibs}\zstd.dll"; DestDir: "{app}"
;Source: "/etc/freezedWidgetWrapper.html"; DestDir: "{app}"
;Source: "*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; Poppler dlls
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\brotlicommon.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\brotlidec.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\bz2.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\freetype.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\jpeg62.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\liblzma.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\libpng16.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\openjp2.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\poppler.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\poppler-cpp.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\tiff.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\poppler\bin\zlib1.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\SaiCore-ThirdParty\zlib\1.2.11\bin\zlib.dll"; DestDir:"{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
;Qt windows plugins
Source: "{#QtDir}\Qt6\plugins\platforms\qminimal.dll"; DestDir: "{app}\platforms"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\platforms\qoffscreen.dll"; DestDir: "{app}\platforms"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\platforms\qwindows.dll"; DestDir: "{app}\platforms"; Flags: ignoreversion
;Qt images formats plugins
Source: "{#QtDir}\Qt6\plugins\imageformats\qgif.dll"; DestDir: "{app}\imageformats"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\imageformats\qico.dll"; DestDir: "{app}\imageformats"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\imageformats\qjpeg.dll"; DestDir: "{app}\imageformats"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\imageformats\qsvg.dll"; DestDir: "{app}\imageformats"; Flags: ignoreversion
;qt icon engine plugin
Source: "{#QtDir}\Qt6\plugins\iconengines\qsvgicon.dll"; DestDir: "{app}\iconengines"; Flags: ignoreversion
;qt multimedia plugin
Source: "{#QtDir}\Qt6\plugins\multimedia\windowsmediaplugin.dll"; DestDir: "{app}\multimedia"; Flags: ignoreversion
;qt tls dependencies
Source: "{#QtDir}\Qt6\plugins\tls\qcertonlybackend.dll"; DestDir: "{app}\tls"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\tls\qopensslbackend.dll"; DestDir: "{app}\tls"; Flags: ignoreversion
Source: "{#QtDir}\Qt6\plugins\tls\qschannelbackend.dll"; DestDir: "{app}\tls"; Flags: ignoreversion
;QtWebEngine dependencies
Source: "{#QtLibs}\QtWebEngineProcess.exe"; DestDir: "{app}"
Source: "{#QtDir}\share\Qt6\resources\*"; DestDir: "{app}\resources"
;Qt translations
;Source: "{#QtDir}\translations\Qt6\*"; DestDir: "{app}\i18n"
Source: "{#QtDir}\translations\Qt6\qtwebengine_locales\*"; DestDir: "{app}\translations\Qt6\qtwebengine_locales"
;fonts for xpdf
Source: "{#ProjectRoot}\resources\windows\xpdfrc"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#ProjectRoot}\resources\fonts\*"; DestDir: "{app}\fonts"; Flags: ignoreversion
[Icons]
Name: "{group}\SaiCore"; Filename: "{app}\SaiCore.exe"
Name: "{group}\{cm:UninstallProgram,SaiCore}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\SaiCore"; Filename: "{app}\SaiCore.exe"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\SaiCore"; Filename: "{app}\SaiCore.exe"; Tasks: quicklaunchicon
[Registry]
Root: HKCR; Subkey: ".ubz"; ValueType: string; ValueName: ""; ValueData: "SaiCoreFile"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "SaiCoreFile"; ValueType: string; ValueName: ""; ValueData: "SaiCore document"; Flags: uninsdeletekey
Root: HKCR; Subkey: "SaiCoreFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\SaiCore.exe,1"
Root: HKCR; Subkey: "SaiCoreFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\SaiCore.exe"" ""%1"""
Root: HKLM; Subkey: "SOFTWARE\SaiCore"; ValueType: string; ValueName: "Client application"; ValueData: "{app}\SaiCore.exe"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM; Subkey: "SOFTWARE\SaiCore"; ValueType: dword; ValueName: "Transfer mode"; ValueData: "0"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM; Subkey: "SOFTWARE\SaiCore"; ValueType: dword; ValueName: "EMF: Hide page"; ValueData: "1"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM; Subkey: "SOFTWARE\SaiCore\Defaults"; ValueType: dword; ValueName: "PDF: Enabled"; ValueData: "1"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM; Subkey: "SOFTWARE\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: dword; ValueName: "Policy"; ValueData: "3"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM; Subkey: "SOFTWARE\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: string; ValueName: "AppName"; ValueData: "SaiCore.exe"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM; Subkey: "SOFTWARE\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: string; ValueName: "AppPath"; ValueData: "{app}"; Flags: uninsdeletevalue; Check: isProcessorNotX64
Root: HKLM64; Subkey: "SOFTWARE\SaiCore"; ValueType: string; ValueName: "Client application"; ValueData: "{app}\SaiCore.exe"; Flags: uninsdeletevalue; Check: isProcessorX64
Root: HKLM64; Subkey: "SOFTWARE\SaiCore"; ValueType: dword; ValueName: "Transfer mode"; ValueData: "0"; Flags: uninsdeletevalue; Check: isProcessorX64
Root: HKLM64; Subkey: "SOFTWARE\SaiCore"; ValueType: dword; ValueName: "EMF: Hide page"; ValueData: "1"; Flags: uninsdeletevalue; Check: isProcessorX64
Root: HKLM64; Subkey: "SOFTWARE\SaiCore\Defaults"; ValueType: dword; ValueName: "PDF: Enabled"; ValueData: "1"; Flags: uninsdeletevalue; Check: isProcessorX64
Root: HKLM64; Subkey: "SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: dword; ValueName: "Policy"; ValueData: "3"; Flags: uninsdeletevalue; Check: isProcessorX64
Root: HKLM64; Subkey: "SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: string; ValueName: "AppName"; ValueData: "SaiCore.exe"; Flags: uninsdeletevalue; Check: isProcessorX64
Root: HKLM64; Subkey: "SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: string; ValueName: "AppPath"; ValueData: "{app}"; Flags: uninsdeletevalue; Check: isProcessorX64
[Run]
Filename: "{tmp}\vcredist_2013.x64.exe";WorkingDir:"{tmp}"; Parameters: "/install /quiet /norestart"; StatusMsg: Installing CRT 2013...
Filename: "{tmp}\vcredist_2015_2022.x64.exe";WorkingDir:"{tmp}"; Parameters: "/install /quiet /norestart"; StatusMsg: Installing CRT 2015-2022 ...
Filename: "{tmp}\LAVFilters-0.77.2-Installer.exe";WorkingDir:"{tmp}"; Parameters: "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART"; StatusMsg: Installing LAV Filters ...
Filename: "{app}\SaiCore.exe"; Description: "{cm:LaunchProgram,SaiCore}"; Flags: nowait postinstall skipifsilent
[UninstallDelete]
; cleanup and delete whole installation directory
Name: {app}; Type: filesandordirs
[Code]
function isProcessorX64: Boolean;
begin
Result := (ProcessorArchitecture = paX64);
end;
function isProcessorNotX64: Boolean;
begin
Result := not isProcessorX64;
end;

View File

@ -0,0 +1,59 @@
@echo off
@echo off
REM --------------------------------------------------------------------
REM This program is free software: you can redistribute it and/or modify
REM it under the terms of the GNU General Public License as published by
REM the Free Software Foundation, either version 2 of the License, or
REM (at your option) any later version.
REM
REM This program is distributed in the hope that it will be useful,
REM but WITHOUT ANY WARRANTY; without even the implied warranty of
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
REM GNU General Public License for more details.
REM
REM You should have received a copy of the GNU General Public License
REM along with this program. If not, see <http://www.gnu.org/licenses/>.
REM ---------------------------------------------------------------------
set SCRIPT_PATH=%~dp0
set PROJECT_ROOT=%SCRIPT_PATH%\..\..
set APPLICATION_NAME=OpenBoard
set QT_DIR=D:\qt\x64-windows
set QT_BIN=%QT_DIR%\tools\Qt6\bin
set PROGRAMS_FILE_PATH=C:\Program Files (x86)
set GIT_BIN=C:\Program Files\Git\bin
set VS_BIN=%PROGRAMS_FILE_PATH%\Microsoft Visual Studio 14.0\VC\bin
set WIN_SDK_BIN=%PROGRAMS_FILE_PATH%\Microsoft SDKs\Windows\v6.0A\Bin
set INNO_EXE=%PROGRAMS_FILE_PATH%\Inno Setup 6\iscc.exe
set BUILD_DIR=%PROJECT_ROOT%\build\win32\release
set LRELEASE=%QT_BIN%\lrelease.exe
set BASE_QT_TRANSLATIONS_DIRECTORY=%QT_DIR%\translations\Qt6
set PATH=%QT_BIN%;%PATH%;%WIN_SDK_BIN%;%GIT_BIN%
echo %PATH%
cd %PROJECT_ROOT%
set /p VERSION= < build\win32\release\version
REM remove the last character that is a space
set VERSION=%VERSION: =%
echo "VERSION : %VERSION%"
call "%INNO_EXE%" "%SCRIPT_PATH%\%APPLICATION_NAME%.iss" /F"%APPLICATION_NAME%_Installer_%VERSION%"
:EXIT_WITH_ERROR
echo "Error found"
cd %SCRIPT_PATH%
GOTO EOF
GOTO END
:END
echo "%APPLICATION_NAME% setup created"
:EOF

View File

@ -0,0 +1,90 @@
# SaiCore Windows Deployment Script
# Builds the complete distributable package
param(
[string]$QtDir = "C:\Qt\6.7.3\msvc2019_64",
[string]$VcVars = "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat",
[string]$ThirdPartyPath = "..\ruboard-ThirdParty",
[string]$Config = "release"
)
$ErrorActionPreference = "Stop"
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
Write-Host "=== SaiCore Build & Deploy ===" -ForegroundColor Cyan
Write-Host "Project: $ProjectRoot"
Write-Host "Qt: $QtDir"
Write-Host "Config: $Config"
Write-Host ""
# Step 1: Build QuaZip
Write-Host "[1/6] Building QuaZip..." -ForegroundColor Yellow
$quazipDir = Join-Path $ProjectRoot $ThirdPartyPath | Join-Path -ChildPath "quazip"
Push-Location $quazipDir
cmd /c "set PATH=$QtDir\bin;%PATH% && call `"$VcVars`" x64 && qmake quazip.pro -spec win32-msvc CONFIG+=$Config && nmake /f Makefile.Release 2>&1"
if ($LASTEXITCODE -ne 0) { Write-Host "QuaZip build FAILED" -ForegroundColor Red; exit 1 }
Pop-Location
Write-Host "QuaZip built OK" -ForegroundColor Green
# Step 2: Build SaiCore
Write-Host "[2/6] Building SaiCore..." -ForegroundColor Yellow
Push-Location $ProjectRoot
cmd /c "set PATH=$QtDir\bin;%PATH% && call `"$VcVars`" x64 && qmake SaiCore.pro -spec win32-msvc CONFIG+=$Config && nmake /f Makefile.Release 2>&1"
if ($LASTEXITCODE -ne 0) { Write-Host "SaiCore build FAILED" -ForegroundColor Red; exit 1 }
Pop-Location
Write-Host "SaiCore built OK" -ForegroundColor Green
$productDir = Join-Path $ProjectRoot "build\win32\release\product"
# Step 3: Compile translations
Write-Host "[3/6] Compiling translations..." -ForegroundColor Yellow
$i18nDir = Join-Path $productDir "i18n"
New-Item -ItemType Directory -Path $i18nDir -Force | Out-Null
$tsFiles = Get-ChildItem (Join-Path $ProjectRoot "resources\i18n\SaiCore_*.ts")
foreach ($ts in $tsFiles) {
$qmName = $ts.BaseName + ".qm"
& "$QtDir\bin\lrelease.exe" $ts.FullName -qm (Join-Path $i18nDir $qmName) 2>&1 | Out-Null
}
Write-Host "$($tsFiles.Count) translations compiled" -ForegroundColor Green
# Step 4: Run windeployqt
Write-Host "[4/6] Running windeployqt..." -ForegroundColor Yellow
& "$QtDir\bin\windeployqt.exe" --release --no-translations --no-system-d3d-compiler --no-opengl-sw (Join-Path $productDir "SaiCore.exe") 2>&1 | Out-Null
Write-Host "Qt dependencies deployed" -ForegroundColor Green
# Step 5: Copy resources
Write-Host "[5/6] Copying resources..." -ForegroundColor Yellow
Copy-Item (Join-Path $ProjectRoot "resources\etc") (Join-Path $productDir "etc") -Recurse -Force
Copy-Item (Join-Path $ProjectRoot "resources\library") (Join-Path $productDir "library") -Recurse -Force
Copy-Item (Join-Path $ProjectRoot "resources\fonts") (Join-Path $productDir "fonts") -Recurse -Force
# Copy ThirdParty DLLs
$tpFull = Join-Path $ProjectRoot $ThirdPartyPath
Copy-Item (Join-Path $tpFull "openssl\openssl-3.0.15-win64\bin\libssl-3-x64.dll") $productDir -Force -ErrorAction SilentlyContinue
Copy-Item (Join-Path $tpFull "openssl\openssl-3.0.15-win64\bin\libcrypto-3-x64.dll") $productDir -Force -ErrorAction SilentlyContinue
Get-ChildItem (Join-Path $tpFull "poppler\*.dll") -ErrorAction SilentlyContinue | Copy-Item -Destination $productDir -Force
Get-ChildItem (Join-Path $tpFull "poppler\bin\*.dll") -ErrorAction SilentlyContinue | Copy-Item -Destination $productDir -Force
if (Test-Path (Join-Path $tpFull "interactive")) {
New-Item -ItemType Directory -Path (Join-Path $productDir "library\interactive") -Force | Out-Null
Copy-Item (Join-Path $tpFull "interactive\*") (Join-Path $productDir "library\interactive") -Recurse -Force
}
# Copy MS DLLs
Copy-Item (Join-Path $QtDir "bin\d3dcompiler_47.dll") $productDir -Force -ErrorAction SilentlyContinue
Copy-Item (Join-Path $QtDir "bin\opengl32sw.dll") $productDir -Force -ErrorAction SilentlyContinue
# Copy zlib.dll (required by poppler)
Copy-Item (Join-Path $tpFull "zlib\1.2.11\bin\zlib.dll") $productDir -Force -ErrorAction SilentlyContinue
Write-Host "Resources copied" -ForegroundColor Green
# Step 6: Summary
Write-Host "[6/6] Deployment summary:" -ForegroundColor Yellow
$exe = Join-Path $productDir "SaiCore.exe"
$dlls = (Get-ChildItem $productDir -Filter "*.dll").Count
$totalSize = (Get-ChildItem $productDir -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB
Write-Host " Executable: $exe"
Write-Host " DLLs: $dlls"
Write-Host " Total size: $([math]::Round($totalSize, 1)) MB"
Write-Host ""
Write-Host "=== BUILD COMPLETE ===" -ForegroundColor Cyan
Write-Host "Product directory: $productDir"

View File

@ -0,0 +1,84 @@
@echo off
@echo off
REM --------------------------------------------------------------------
REM This program is free software: you can redistribute it and/or modify
REM it under the terms of the GNU General Public License as published by
REM the Free Software Foundation, either version 2 of the License, or
REM (at your option) any later version.
REM
REM This program is distributed in the hope that it will be useful,
REM but WITHOUT ANY WARRANTY; without even the implied warranty of
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
REM GNU General Public License for more details.
REM
REM You should have received a copy of the GNU General Public License
REM along with this program. If not, see <http://www.gnu.org/licenses/>.
REM ---------------------------------------------------------------------
set SCRIPT_PATH=%~dp0
set PROJECT_ROOT=%SCRIPT_PATH%\..\..
set APPLICATION_NAME=OpenBoard
set QT_DIR=D:\qt\x64-windows
set QT_BIN=%QT_DIR%\tools\Qt6\bin
set PROGRAMS_FILE_PATH=C:\Program Files (x86)
set GIT_BIN=C:\Program Files\Git\bin
set VS_BIN=%PROGRAMS_FILE_PATH%\Microsoft Visual Studio 14.0\VC\bin
set WIN_SDK_BIN=%PROGRAMS_FILE_PATH%\Microsoft SDKs\Windows\v6.0A\Bin
set INNO_EXE=%PROGRAMS_FILE_PATH%\Inno Setup 6\iscc.exe
set BUILD_DIR=%PROJECT_ROOT%\build\win32\release
set LRELEASE=%QT_BIN%\lrelease.exe
set BASE_QT_TRANSLATIONS_DIRECTORY=%QT_DIR%\translations\Qt6
set PATH=%QT_BIN%;%PATH%;%WIN_SDK_BIN%;%GIT_BIN%
REM call "%VS_BIN%\vcvars32.bat"
echo %PATH%
cd %PROJECT_ROOT%
rmdir /S /Q %BUILD_DIR%
rmdir /S /Q install
"%QT_BIN%\qmake.exe" %APPLICATION_NAME%.pro
echo "CALLING LRELEASE"
call "%LRELEASE%" "%APPLICATION_NAME%.pro"
set /p VERSION= < build\win32\release\version
REM remove the last character that is a space
set VERSION=%VERSION: =%
nmake release-install
IF NOT EXIST build\win32\release\product\%APPLICATION_NAME%.exe GOTO EXIT_WITH_ERROR
xcopy C:\%APPLICATION_NAME%\bin\*.dll build\win32\release\product\
xcopy "%QT_DIR%\bin\Qt6OpenGL.dll" build\win32\release\product\
set CUSTOMIZATIONS=build\win32\release\product\customizations
mkdir %CUSTOMIZATIONS%
xcopy /s resources\customizations %CUSTOMIZATIONS%
set STARTUP_HINTS=build\win32\release\product\startupHints
mkdir %STARTUP_HINTS%
xcopy /s resources\startupHints %STARTUP_HINTS%
set I18n=build\win32\release\product\i18n
xcopy /s "%BASE_QT_TRANSLATIONS_DIRECTORY%\qt_*.qm" %I18n%\
call "%INNO_EXE%" "%SCRIPT_PATH%\%APPLICATION_NAME%.iss" /F"%APPLICATION_NAME%_Installer_%VERSION%"
GOTO END
:EXIT_WITH_ERROR
echo "Error found"
GOTO EOF
:END
echo "%APPLICATION_NAME% build finished"
:EOF

405
resources/SaiCore.qrc Normal file
View File

@ -0,0 +1,405 @@
<RCC>
<qresource prefix="/">
<file>images/SaiCore.png</file>
<file>images/bigSaiCore.png</file>
<file>images/close.svg</file>
<file>images/increase.svg</file>
<file>images/decrease.svg</file>
<file>images/resize.svg</file>
<file>images/play.svg</file>
<file>images/pause.svg</file>
<file>images/stop.svg</file>
<file>images/menu.svg</file>
<file>images/flash.svg</file>
<file>images/soundOn.svg</file>
<file>images/soundOff.svg</file>
<file>images/shape.png</file>
<file>images/font.svg</file>
<file>images/color.svg</file>
<file>images/eyeOpened.svg</file>
<file>images/eyeClosed.svg</file>
<file>images/locked.svg</file>
<file>images/unlocked.svg</file>
<file>images/duplicate.svg</file>
<file>images/resetAngle.svg</file>
<file>images/resizeButton.svg</file>
<file>images/resizeTop.svg</file>
<file>images/resizeLeft.svg</file>
<file>images/resizeBottom.svg</file>
<file>images/resizeRight.svg</file>
<file>images/rotate.svg</file>
<file>images/rotateTool.svg</file>
<file>images/rotateProtractor.svg</file>
<file>images/resizeTool.svg</file>
<file>images/resizeRuler.svg</file>
<file>images/resizeCompass.svg</file>
<file>images/closeTool.svg</file>
<file>images/hflipTool.svg</file>
<file>images/vflipTool.svg</file>
<file>images/resetTool.svg</file>
<file>images/angleMarker.svg</file>
<file>images/currentDocument.png</file>
<file>images/minus.svg</file>
<file>images/plus.svg</file>
<file>images/z_layer_up.svg</file>
<file>images/z_layer_down.svg</file>
<file>images/trash.png</file>
<file>images/pin.svg</file>
<file>images/unpin.svg</file>
<file>images/home.svg</file>
<file>images/reload.svg</file>
<file>images/frozen.svg</file>
<file>images/unfrozen.svg</file>
<file>images/toolbar/group.png</file>
<file>images/toolbar/ungroup.png</file>
<file>images/toolbar/window.png</file>
<file>images/toolbar/plusBlack.png</file>
<file>images/toolbar/plusBlackOn.png</file>
<file>images/toolbar/quit.png</file>
<file>images/toolbar/quitOn.png</file>
<file>images/toolbar/background.png</file>
<file>images/toolbar/documents.png</file>
<file>images/toolbar/newPage.png</file>
<file>images/toolbar/next.png</file>
<file>images/toolbar/nextPageOn.png</file>
<file>images/toolbar/previous.png</file>
<file>images/toolbar/previousPageOn.png</file>
<file>images/toolbar/redo.png</file>
<file>images/toolbar/redoOn.png</file>
<file>images/toolbar/stylus.png</file>
<file>images/toolbar/undo.png</file>
<file>images/toolbar/undoOn.png</file>
<file>images/toolbar/smallPen.png</file>
<file>images/toolbar/mediumPen.png</file>
<file>images/toolbar/largePen.png</file>
<file>images/toolbar/color.png</file>
<file>images/toolbar/smallEraser.png</file>
<file>images/toolbar/mediumEraser.png</file>
<file>images/toolbar/largeEraser.png</file>
<file>images/toolbar/web.png</file>
<file>images/toolbar/board.png</file>
<file>images/toolbar/warning.png</file>
<file>images/toolbar/home.png</file>
<file>images/toolbar/profile.png</file>
<file>images/toolbar/library.png</file>
<file>images/toolbar/clearPage.png</file>
<file>images/toolbar/reload.png</file>
<file>images/toolbar/addBookmark.png</file>
<file>images/toolbar/bookmarks.png</file>
<file>images/toolbar/settings.png</file>
<file>images/toolbar/eyeClosed.png</file>
<file>images/toolbar/eyeOpened.png</file>
<file>images/toolbar/pdf.png</file>
<file>images/toolbar/deleteDocument.png</file>
<file>images/toolbar/deleteFolder.png</file>
<file>images/toolbar/newDocument.png</file>
<file>images/toolbar/newFolder.png</file>
<file>images/toolbar/addToLibrary.png</file>
<file>images/toolbar/addInteractiveToLibrary.png</file>
<file>images/toolbar/addToolToLibrary.png</file>
<file>images/toolbar/duplicate.png</file>
<file>images/toolbar/export.png</file>
<file>images/toolbar/import.png</file>
<file>images/toolbar/setBackground.png</file>
<file>images/toolbar/removeBackground.png</file>
<file>images/toolbar/open.png</file>
<file>images/toolbar/addToPage.png</file>
<file>images/toolbar/menu.png</file>
<file>images/toolbar/addToDocument.png</file>
<file>images/toolbar/print.png</file>
<file>images/toolbar/addToWorkingDoc.png</file>
<file>images/toolbar/rename.png</file>
<file>images/toolbar/captureArea.png</file>
<file>images/toolbar/captureScreen.png</file>
<file>images/toolbar/captureWindow.png</file>
<file>images/toolbar/extraTool.png</file>
<file>images/toolbar/webUpload.png</file>
<file>images/toolbar/stop.png</file>
<file>images/toolbar/pause.png</file>
<file>images/toolbar/cut.png</file>
<file>images/toolbar/copy.png</file>
<file>images/toolbar/paste.png</file>
<file>images/toolbar/proto.png</file>
<file>images/toolbar/mic.png</file>
<file>images/toolbar/micOff.png</file>
<file>images/toolbar/remove.png</file>
<file>images/toolbar/pageSize.png</file>
<file>images/toolbar/eraserTool.png</file>
<file>images/toolbar/lineTool.png</file>
<file>images/toolbar/tools.png</file>
<file>images/stylusPalette/arrow.png</file>
<file>images/stylusPalette/arrowOn.png</file>
<file>images/stylusPalette/handPlay.png</file>
<file>images/stylusPalette/handPlayOn.png</file>
<file>images/stylusPalette/eraser.png</file>
<file>images/stylusPalette/eraserOn.png</file>
<file>images/stylusPalette/hand.png</file>
<file>images/stylusPalette/handOn.png</file>
<file>images/stylusPalette/laser.png</file>
<file>images/stylusPalette/laserOn.png</file>
<file>images/stylusPalette/marker.png</file>
<file>images/stylusPalette/markerOn.png</file>
<file>images/stylusPalette/pen.png</file>
<file>images/stylusPalette/penOn.png</file>
<file>images/stylusPalette/line.png</file>
<file>images/stylusPalette/lineOn.png</file>
<file>images/stylusPalette/zoomOut.png</file>
<file>images/stylusPalette/zoomOutOn.png</file>
<file>images/stylusPalette/zoomIn.png</file>
<file>images/stylusPalette/zoomInOn.png</file>
<file>images/stylusPalette/restoreZoom.png</file>
<file>images/stylusPalette/text.png</file>
<file>images/stylusPalette/textOn.png</file>
<file>images/stylusPalette/captureArea.png</file>
<file>images/stylusPalette/captureAreaOn.png</file>
<file>images/stylusPalette/snap.svg</file>
<file>images/stylusPalette/snapOn.svg</file>
<file>images/backgroundPalette/background1.svg</file>
<file>images/backgroundPalette/background1On.svg</file>
<file>images/backgroundPalette/background2.svg</file>
<file>images/backgroundPalette/background2On.svg</file>
<file>images/backgroundPalette/background3.svg</file>
<file>images/backgroundPalette/background3On.svg</file>
<file>images/backgroundPalette/background4.svg</file>
<file>images/backgroundPalette/background4On.svg</file>
<file>images/backgroundPalette/background5.svg</file>
<file>images/backgroundPalette/background5On.svg</file>
<file>images/backgroundPalette/background6.svg</file>
<file>images/backgroundPalette/background6On.svg</file>
<file>images/backgroundPalette/background7.svg</file>
<file>images/backgroundPalette/background7On.svg</file>
<file>images/backgroundPalette/background8.svg</file>
<file>images/backgroundPalette/background8On.svg</file>
<file>images/toolPalette/podcast.svg</file>
<file>images/toolPalette/podcastOn.svg</file>
<file>images/toolPalette/rulerTool.png</file>
<file>images/toolPalette/triangleTool.png</file>
<file>images/toolPalette/protractorTool.png</file>
<file>images/toolPalette/compassTool.png</file>
<file>images/toolPalette/aristoTool.png</file>
<file>images/toolPalette/maskTool.png</file>
<file>images/toolPalette/magnifierTool.png</file>
<file>images/extraPalette/blackout.png</file>
<file>images/extraPalette/keyboard.png</file>
<file>images/extraPalette/keyboardOn.png</file>
<file>images/extraPalette/trapFlash.png</file>
<file>images/extraPalette/webTrap.png</file>
<file>images/extraPalette/webTrapEnabled.png</file>
<file>images/extraPalette/oEmbed.png</file>
<file>images/erasePalette/eraseAll.svg</file>
<file>images/erasePalette/eraseItem.svg</file>
<file>images/erasePalette/eraseAnnotation.svg</file>
<file>images/erasePalette/eraseBackground.svg</file>
<file>images/cursors/eraser.png</file>
<file>images/cursors/laser.png</file>
<file>images/cursors/marker.png</file>
<file>images/cursors/zoomIn.png</file>
<file>images/cursors/zoomOut.png</file>
<file>images/cursors/rotate.png</file>
<file>images/cursors/resize.png</file>
<file>images/cursors/drawCompass.png</file>
<file>images/cursors/drawRulerLine.png</file>
<file>images/cursors/play.png</file>
<file>images/print/onepage.png</file>
<file>images/print/thumbnails.png</file>
<file>images/print/twopages.png</file>
<file>images/defaultWidgetIcon.png</file>
<file>images/favorites.png</file>
<file>images/myContent.png</file>
<file>images/myFolder.png</file>
<file>images/online.png</file>
<file>images/tools.png</file>
<file>images/folder.png</file>
<file>images/addItemToCurrentPage.svg</file>
<file>images/addItemToNewPage.svg</file>
<file>images/addItemToLibrary.svg</file>
<file>style/treeview-branch-closed.png</file>
<file>style/treeview-branch-open.png</file>
<file>webbrowser/closetab.png</file>
<file>webbrowser/loading.gif</file>
<file>webbrowser/notfound.html</file>
<file>webbrowser/object-wrapper.html</file>
<file alias="PowerPointImport.applescript">macx/PowerPointImport.applescript</file>
<file>podcast/SaiCore.prx</file>
<file>www/SaiCore-web-player.html</file>
<file>images/toolbar/tutorial.png</file>
<file>images/edit-mode.svg</file>
<file>images/save.svg</file>
<file>images/libpalette/social.png</file>
<file>images/navig_arrow.png</file>
<file>images/flags/ar.png</file>
<file>images/flags/bg.png</file>
<file>images/flags/ca.png</file>
<file>images/flags/cs.png</file>
<file>images/flags/da.png</file>
<file>images/flags/de.png</file>
<file>images/flags/el.png</file>
<file>images/flags/en.png</file>
<file>images/flags/eo.png</file>
<file>images/flags/es.png</file>
<file>images/flags/et.png</file>
<file>images/flags/eu.png</file>
<file>images/flags/fa.png</file>
<file>images/flags/fi.png</file>
<file>images/flags/fo.png</file>
<file>images/flags/fr.png</file>
<file>images/flags/ga.png</file>
<file>images/flags/gl.png</file>
<file>images/flags/he.png</file>
<file>images/flags/hr.png</file>
<file>images/flags/hu.png</file>
<file>images/flags/id.png</file>
<file>images/flags/is.png</file>
<file>images/flags/it.png</file>
<file>images/flags/ja.png</file>
<file>images/flags/km.png</file>
<file>images/flags/lb.png</file>
<file>images/flags/lt.png</file>
<file>images/flags/lv.png</file>
<file>images/flags/nb.png</file>
<file>images/flags/nl.png</file>
<file>images/flags/nn.png</file>
<file>images/flags/pl.png</file>
<file>images/flags/pt-br.png</file>
<file>images/flags/pt-pt.png</file>
<file>images/flags/ro.png</file>
<file>images/flags/ru.png</file>
<file>images/flags/sco.png</file>
<file>images/flags/se.png</file>
<file>images/flags/sk.png</file>
<file>images/flags/sl.png</file>
<file>images/flags/sq.png</file>
<file>images/flags/sv.png</file>
<file>images/flags/tg.png</file>
<file>images/flags/th.png</file>
<file>images/flags/tl.png</file>
<file>images/flags/tr.png</file>
<file>images/flags/uk.png</file>
<file>images/flags/vi.png</file>
<file>images/flags/zh-hans.png</file>
<file>images/flags/zh-hant.png</file>
<file>images/libpalette/notFound.png</file>
<file>images/libpalette/trash_favorite.svg</file>
<file>images/libpalette/back.png</file>
<file>images/libpalette/loading.png</file>
<file>images/stylusPalette/eraserArrow.png</file>
<file>images/stylusPalette/eraserOnArrow.png</file>
<file>images/stylusPalette/markerArrow.png</file>
<file>images/stylusPalette/markerOnArrow.png</file>
<file>images/stylusPalette/penArrow.png</file>
<file>images/stylusPalette/penOnArrow.png</file>
<file>images/libpalette/AudiosCategory.svg</file>
<file>images/libpalette/PicturesCategory.svg</file>
<file>images/libpalette/ShapesCategory.svg</file>
<file>images/libpalette/InteractivesCategory.svg</file>
<file>images/libpalette/movieIcon.svg</file>
<file>images/libpalette/soundIcon.svg</file>
<file>images/libpalette/folder.svg</file>
<file>images/libpalette/TrashCategory.svg</file>
<file>images/libpalette/FavoritesCategory.svg</file>
<file>images/libpalette/ApplicationsCategory.svg</file>
<file>images/libpalette/miniSearch.png</file>
<file>images/libpalette/miniTrash.png</file>
<file>images/libpalette/miniNewFolder.png</file>
<file>images/libpalette/miniFavorite.png</file>
<file>images/libpalette/home.png</file>
<file>images/toolbar/updates.png</file>
<file>images/paletteLibrary.png</file>
<file>images/paletteNavigator.png</file>
<file>images/toolbar/display.png</file>
<file>images/toolbar/hide.png</file>
<file>images/toolbar/record.png</file>
<file>images/libpalette/MoviesCategory.svg</file>
<file>images/virtual.keyboard/41/centre-passive.png</file>
<file>images/virtual.keyboard/41/left-passive.png</file>
<file>images/virtual.keyboard/41/right-passive.png</file>
<file>images/virtual.keyboard/41/centre-active.png</file>
<file>images/virtual.keyboard/41/left-active.png</file>
<file>images/virtual.keyboard/41/right-active.png</file>
<file>images/virtual.keyboard/41/backspace.png</file>
<file>images/virtual.keyboard/41/capslock.png</file>
<file>images/virtual.keyboard/41/shift.png</file>
<file>images/virtual.keyboard/41/tab.png</file>
<file>images/virtual.keyboard/29/centre-passive.png</file>
<file>images/virtual.keyboard/29/left-passive.png</file>
<file>images/virtual.keyboard/29/right-passive.png</file>
<file>images/virtual.keyboard/29/centre-active.png</file>
<file>images/virtual.keyboard/29/left-active.png</file>
<file>images/virtual.keyboard/29/right-active.png</file>
<file>images/virtual.keyboard/29/backspace.png</file>
<file>images/virtual.keyboard/29/capslock.png</file>
<file>images/virtual.keyboard/29/shift.png</file>
<file>images/virtual.keyboard/29/tab.png</file>
<file>images/toolPalette/cacheTool.png</file>
<file>images/libpalette/FlashCategory.svg</file>
<file>images/libpalette/FlashIcon.svg</file>
<file>images/toolbar/stylusTab.png</file>
<file>images/library_close.png</file>
<file>images/library_open.png</file>
<file>images/pages_close.png</file>
<file>images/pages_open.png</file>
<file>images/cache_close.png</file>
<file>images/cache_open.png</file>
<file>images/cache_circle.png</file>
<file>images/cache_square.png</file>
<file>images/down_arrow.png</file>
<file>images/up_arrow.png</file>
<file>images/left_arrow.png</file>
<file>images/right_arrow.png</file>
<file>images/moveUp.svg</file>
<file>images/moveDown.svg</file>
<file>images/moveDownDisabled.svg</file>
<file>images/closeDisabled.svg</file>
<file>images/menuDisabled.svg</file>
<file>images/moveUpDisabled.svg</file>
<file>style.qss</file>
<file>images/libpalette/WebSearchCategory.svg</file>
<file>images/download_close.png</file>
<file>images/download_open.png</file>
<file>images/tab_mask.png</file>
<file>images/duplicateDisabled.svg</file>
<file>images/roundeRrectangle.svg</file>
<file>images/circle.svg</file>
<file>images/rightAligned.svg</file>
<file>images/centerAligned.svg</file>
<file>images/leftAligned.svg</file>
<file>images/notAligned.svg</file>
<file>images/groupItems.svg</file>
<file>images/ungroupItems.svg</file>
<file>images/setAsBackground.svg</file>
<file>images/backgroundPalette/resetDefaultGridSize.svg</file>
<file>images/collapse-all.png</file>
<file>images/expand-all.png</file>
<file>images/asc.png</file>
<file>images/desc.png</file>
<file>images/toolPalette/axesTool.png</file>
<file>images/numbersTool.svg</file>
<file>images/stylusPalette/eraserArrow.svg</file>
<file>images/stylusPalette/penArrow.svg</file>
<file>images/stylusPalette/eraserOnArrow.svg</file>
<file>images/stylusPalette/eraserOn.svg</file>
<file>images/stylusPalette/eraser.svg</file>
<file>images/stylusPalette/marker.svg</file>
<file>images/stylusPalette/markerArrow.svg</file>
<file>images/stylusPalette/markerOn.svg</file>
<file>images/stylusPalette/markerOnArrow.svg</file>
<file>images/stylusPalette/pen.svg</file>
<file>images/stylusPalette/penOn.svg</file>
<file>images/stylusPalette/penOnArrow.svg</file>
<file>images/trash-my-documents.png</file>
<file>images/trash-empty.png</file>
<file>images/trash-folder.png</file>
<file>images/trash-delete-document.png</file>
<file>images/trash-delete-folder.png</file>
<file>images/trash-document.png</file>
<file>images/trash-document-page.png</file>
<file>webbrowser/go-bottom.png</file>
<file>webbrowser/edit-clear.png</file>
<file>webbrowser/process-stop.png</file>
<file>images/saicore-document.png</file>
<file>images/addToFavorites.png</file>
<file>images/removeFromFavorites.png</file>
<file>images/toolbar/tip.png</file>
<file>images/moveTool.svg</file>
</qresource>
</RCC>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,158 @@
[App]
HideCheckForSoftwareUpdate=false
HideSwapDisplayScreens=true
EnableAutomaticSoftwareUpdates=true
EnableSoftwareUpdates=true
EnableStartupHints=true
FavoriteToolURIs=openboardtool://openboard/mask, openboardtool://ruler, openboardtool://compass, openboardtool://protractor, openboardtool://triangle, openboardtool://magnifier, openboardtool://cache
IsInSoftwareUpdateProcess=false
LastSessionDocumentUUID=
LastSessionPageIndex=0
PageCacheSize=20
PreferredLanguage=ru
ProductWebAddress=https://saicore.ru
RotationAngleStep=5.
RunInWindow=false
SoftwareUpdateURL=https://saicore.ru/update.json
StartMode=
SwapControlAndDisplayScreens=false
ToolBarDisplayText=true
ToolBarOrientationVertical=false
ToolBarPositionedAtTop=true
TutorialUrl=https://saicore.ru
UseMultiscreenMode=true
UseSystemOnScreenKeyboard=true
[Board]
AutoSaveIntervalInMinutes=3
CrossColorDarkBackground=#C8C0C0C0
CrossColorLightBackground=#A5E1FF
DarkBackground=0
DefaultPageSize=@Size(1280 960)
EraserCircleWidthIndex=1
FeatureSliderPosition=40
GridDarkBackgroundColors=#FFFFFF, #FF3400, #66C0FF, #81FF5C, #FFFF00, #B68360, #FF497E, #8D69FF, #C8C0C0C0
GridLightBackgroundColors=#000000, #FF0000, #004080, #008000, #FFDD00, #C87400, #800040, #008080, #5F2D0A, #A5E1FF
InterpolateMarkerStrokes=true
InterpolatePenStrokes=true
KeyboardPaletteKeyBtnSize=16x16
LeftLibPaletteBoardModeIsCollapsed=true
LeftLibPaletteBoardModeWidth=270
LeftLibPaletteDesktopModeIsCollapsed=true
LeftLibPaletteDesktopModeWidth=270
MagnifierDrawingMode=0
MarkerAlpha=0.5
MarkerColorIndex=0
MarkerDarkBackgroundColors=#FFFF00, #FF4400, #66C0FF, #81FF5C, #B68360, #FF497E, #8D69FF, #FFFFFF
MarkerDarkBackgroundSelectedColors=#FFFF00, #FF4400, #66C0FF, #81FF5C, #B68360
MarkerFineWidth=12
MarkerLightBackgroundColors=#E3FF00, #FF0000, #004080, #008000, #C87400, #800040, #008080, #000000
MarkerLightBackgroundSelectedColors=#E3FF00, #FF0000, #004080, #008000, #C87400
MarkerMediumWidth=24
MarkerPressureSensitive=false
MarkerStrongWidth=48
pageDpi=0
PenColorIndex=0
PenDarkBackgroundColors=#FFFFFF, #FF3400, #66C0FF, #81FF5C, #FFFF00, #B68360, #FF497E, #8D69FF, #000000
PenDarkBackgroundSelectedColors=#FFFFFF, #FF3400, #66C0FF, #81FF5C, #FFFF00
PenFineWidth=1.5
PenLightBackgroundColors=#000000, #FF0000, #004080, #008000, #FFDD00, #C87400, #800040, #008080, #5F2D0A, #FFFFFF
PenLightBackgroundSelectedColors=#000000, #FF0000, #004080, #008000, #FFDD00
PenLineWidthIndex=0
PenMediumWidth=3
PenPressureSensitive=true
PenStrongWidth=8
PointerDiameter=40
RightLibPaletteBoardModeIsCollapsed=true
RightLibPaletteBoardModeWidth=270
RightLibPaletteDesktopModeIsCollapsed=true
RightLibPaletteDesktopModeWidth=270
SeyesRuledBackground=false
ShowEraserPreviewCircle=true
ShowMarkerPreviewCircle=true
ShowPenPreviewCircle=true
PenPreviewFromSize=5
ShowToolsPalette=false
SimplifyMarkerStrokes=true
SimplifyPenStrokes=true
SimplifyPenStrokesThresholdAngle=3
SimplifyPenStrokesThresholdWidthDifference=2
StartupKeyboardLocale=0
UseHighResTabletEvent=true
ZoomBase=1.0005
ZoomFactor=1.4099999999999999
[Community]
CredentialsPersistence=false
Password=
Username=
[Document]
EmptyGroupNames=@Invalid()
emptyTrashForOlderDocuments=false
emptyTrashDaysValue=30
ThumbnailWidth=150
SortKind=0
SortOrder=0
SplitterLeftSize=200
SplitterRightSize=800
ShowDateColumnOnAlphabeticalSort=false
[IntranetPodcast]
Author=
PublishToIntranet=false
PublishingUrl=
[Library]
AnimationsDirectory=./library/animations
ApplicationsDirectory=./library/applications
AudiosDirectory=./library/audios
ImageDirectory=./library/pictures
ImageThumbnailWidth=150
InteractivitiesDirectory=./library/interactivities
LibIconSize=80
ShapeDirectory=./library/shape
ShapeThumbnailWidth=50
ShowDetailsForLocalItems=false
SoundThumbnailWidth=50
VideoThumbnailWidth=80
VideosDirectory=./library/videos
[Mirroring]
RefreshRateInFramePerSecond=2
[PDF]
ExportBackgroundGrid=false
ExportBackgroundColor=false
Margin=20
PageFormat=A4
Resolution=300
UsePDFMerger=true
[Podcast]
AudioRecordingDevice=Default
FramesPerSecond=10
PublishToYouTube=false
QuickTimeQuality=High
VideoSize=Medium
WindowsMediaBitsPerSecond=1700000
[SVG]
ViewBoxMargin=50
[Web]
AlternativeUserAgent="Mozilla/5.0 (%1; %2; rv:91.0) Gecko/20100101 Firefox/91.0"
AlternativeUserAgentDomains=google.*
CookieAutoDelete=false
CookieKeepDomains=
CookiePolicy=DenyThirdParty
HistoryLimit=15
Homepage=https://saicore.ru
PrivateBrowsing=false
SearchEngineUrl=https://www.qwant.com/?q=%1
ShowPageImediatelyOnMirroredScreen=false
UseExternalBrowser=false
[YouTube]
CredentialsPersistence=false
UserEMail=

666
resources/etc/SaiCore.css Normal file
View File

@ -0,0 +1,666 @@
QWidget:enabled
{
color: #e8e6e3;
background-color: #1a0a0a;
}
QWidget:disabled
{
color: #71717a;
}
QComboBox,
QPushButton,
QComboBox QAbstractItemView
{
background: #2d1517;
color: #e8e6e3;
border: 1px solid #52525b;
border-radius: 4px;
padding: 4px;
}
QComboBox:hover,
QPushButton:hover
{
background: #3d1f1f;
border: 1px solid #dc2626;
}
QComboBox::drop-down
{
border: none;
width: 20px;
}
QComboBox QAbstractItemView
{
background: #2d1517;
color: #e8e6e3;
selection-background-color: #dc2626;
selection-color: white;
border: 1px solid #52525b;
}
QTextEdit,
QLineEdit,
QComboBox#DockPaletteWidgetComboBox QAbstractItemView
{
selection-background-color: #dc2626;
selection-color: white;
background: #1a0a0a;
color: #e8e6e3;
border: 1px solid #52525b;
border-radius: 3px;
padding: 2px;
}
QProgressBar:horizontal {
border: 1px solid #52525b;
border-radius: 3px;
background: #1a0a0a;
padding: 1px;
}
QProgressBar::chunk:horizontal {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #dc2626, stop:1 #991b1b);
}
QMainWindow
{
background-color: #1a0a0a;
}
QDialog
{
background-color: #2d1517;
color: #e8e6e3;
}
QMenu
{
border: 1px solid #52525b;
font-size: 12px;
background-color: #2d1517;
color: #e8e6e3;
border-radius: 4px;
padding: 4px;
}
QMenu::item
{
background-color: transparent;
color: #e8e6e3;
padding: 4px 8px;
border-radius: 3px;
}
QMenu::item:selected
{
background-color: #dc2626;
color: white;
}
QMenu::separator
{
background-color: #52525b;
border: none;
height: 1px;
margin: 4px 8px;
}
QToolBar
{
spacing: 0px;
background-color: #2d1517;
border: none;
border-bottom: 1px solid #52525b;
}
QToolBar::handle
{
margin: 2px;
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #3d1f1f, stop: 1 #2d1517);
}
QToolBar::separator
{
border: 1px dotted #52525b; border-left: none; margin-left: 5px; margin-right: 5px; width: 1px;
}
QToolBar QToolButton
{
margin: 4px;
margin-left: 0px;
margin-right: 0px;
padding: 0px;
border: none;
height: 58px;
color: #e8e6e3;
}
QToolBar QToolButton:hover
{
background: #3d1f1f;
border-radius: 4px;
}
QToolBar QToolButton:checked
{
background: #dc2626;
border-radius: 4px;
color: white;
}
QToolBar QToolButton:pressed
{
margin: 4px;
margin-left: 0px;
margin-right: 0px;
padding: 0px;
border: none;
height: 58px;
background: qradialgradient(cx:0.5, cy:0.5, radius: 0.5,
fx:0.5, fy:0.5, stop: 0 #3d1f1f, stop: 1 #2d1517);
}
QDialog QToolButton:pressed
{
background: qradialgradient(cx:0.5, cy:0.5, radius: 0.5,
fx:0.5, fy:0.5, stop: 0 #3d1f1f, stop: 1 #2d1517);
}
QToolButton#ubButtonGroupLeft,
QToolButton#desktop-ubButtonGroupLeft
{
background: qlineargradient(x1: 0, y1: 0.49, x2: 0, y2: 0.5, stop: 0 #3d1f1f, stop: 1 #2d1517);
margin-top: 1px;
margin-right: 0px;
padding: 5px;
border: 1px solid #52525b;
border-right: none;
border-top-left-radius : 3px;
border-bottom-left-radius : 3px;
height: 14px;
color: #e8e6e3;
}
QToolButton#ubButtonGroupCenter,
QToolButton#desktop-ubButtonGroupCenter
{
background: qlineargradient(x1: 0, y1: 0.49, x2: 0, y2: 0.5, stop: 0 #3d1f1f, stop: 1 #2d1517);
margin-top: 1px;
margin-right: 0px;
margin-left: 0px;
padding: 5px;
border: 1px solid #52525b;
border-right: none;
border-left: none;
height: 14px;
color: #e8e6e3;
}
QToolButton#ubButtonGroupRight,
QToolButton#desktop-ubButtonGroupRight
{
background: qlineargradient(x1: 0, y1: 0.49, x2: 0, y2: 0.5, stop: 0 #3d1f1f, stop: 1 #2d1517);
margin-top: 1px;
margin-left: 0px;
padding: 5px;
border: 1px solid #52525b;
border-left: none;
border-top-right-radius : 3px;
border-bottom-right-radius : 3px;
height: 14px;
color: #e8e6e3;
}
QToolButton#desktop-ubButtonGroupLeft
QToolButton#desktop-ubButtonGroupCenter,
QToolButton#desktop-ubButtonGroupRight
{
background: qlineargradient(x1: 0, y1: 0.49, x2: 0, y2: 0.5, stop: 0 #3d1f1f, stop: 1 #2d1517);
}
QToolButton#desktop-ubButtonGroupLeft:checked,
QToolButton#desktop-ubButtonGroupCenter:checked,
QToolButton#desktop-ubButtonGroupRight:checked
{
border:1px solid #dc2626;
background: qlineargradient(x1: 0, y1: 0.49, x2: 0, y2: 0.5, stop: 0 #dc2626, stop: 1 #991b1b);
}
QToolButton#ubButtonGroupLeft:checked,
QToolButton#ubButtonGroupCenter:checked,
QToolButton#ubButtonGroupRight:checked
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dc2626, stop: 1 #991b1b);
}
QToolButton#ubButtonGroupLeft:checked
{
border-right: 1px solid #52525b;
padding-right: 4px;
}
QToolButton#ubButtonGroupCenter:checked
{
border-left: 1px solid #52525b;
border-right: 1px solid #52525b;
padding-right: 4px;
padding-left: 4px;
}
QToolButton#ubButtonGroupRight:checked
{
border-left: 1px solid #52525b;
padding-left: 4px;
}
QToolButton#ubButtonMenu
{
margin-right: 2px;
padding-right: 12px;
}
QToolButton#ubButtonMenu:pressed,
QToolButton#ubButtonMenu:checked
{
background-color: #dc2626;
}
QToolButton#ubActionPaletteButton
{
margin: 0px;
}
QFrame#newDocumentFolderFrame,
QFrame#newDocumentFrame,
QFrame#newFolderFrame
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2d1517, stop: 1 #1a0a0a);
margin: 0px;
padding: 0px;
border: none;
}
QFrame#topLeftFrame,
QFrame#imageTopLeftFrame,
QFrame#interactiveTopLeftFrame,
QFrame#videoTopLeftFrame,
QFrame#toolTopLeftFrame
{
border: none;
border-right: 1px solid #52525b;
}
QWidget#topRightFrame
{
background-color: #2d1517;
}
QFrame#toolFrame
{
border-top: 1px solid #52525b;
}
QTreeView
{
background-color: #1a0a0a;
color: #e8e6e3;
border: 1px solid #52525b;
}
QTreeView::item:selected:active
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dc2626, stop: 1 #991b1b);
border: none;
}
QTreeView::item
{
padding-top: 1.5 px;
padding-bottom: 1.5 px;
color: #e8e6e3;
}
QTreeView::item:selected,
QTreeView::branch:selected
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dc2626, stop: 1 #991b1b);
border: none;
color: white;
}
QTreeView::branch:has-siblings:!adjoins-item,
QTreeView::branch:!has-children:!has-siblings:adjoins-item,
QTreeView::branch:has-siblings:adjoins-item
{
border-image: none;
image: none;
}
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings
{
border-image: none;
image: url(:/style/treeview-branch-closed.png);
}
QTreeView::branch:open:has-children:!has-siblings,
QTreeView::branch:open:has-children:has-siblings
{
border-image: none;
image: url(:/style/treeview-branch-open.png);
}
QSlider::groove:horizontal
{
border: 1px solid #52525b;
height: 2px;
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2d1517, stop:1 #1a0a0a);
margin: 2px 0;
}
QSlider::handle:horizontal
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dc2626, stop: 1 #991b1b);
border: 1px solid #52525b;
width: 18px;
margin: -2px 0;
border-radius: 6px;
}
QTabWidget::pane
{
border: 1px solid #52525b;
margin-top: -2px;
}
QTabWidget::pane#libraryTabWidget
{
border: none;
border-top: 1px solid #52525b;
}
QTabWidget::tab-bar
{
alignment: center;
}
QTabWidget::tab-bar#ubWebBrowserTabWidget
{
background: #2d1517;
alignment: left;
}
QTabBar::tab
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2d1517, stop: 1 #1a0a0a);
color: #e8e6e3;
border: 1px solid #52525b;
border-bottom: none;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
min-width: 12ex;
margin: 2px;
margin-top: 6px;
margin-bottom: 2px;
padding: 4px;
height: 14px;
}
QTabBar::tab:first
{
border-left: 1px solid #52525b;
}
QTabBar::tab:last
{
border-right: 1px solid #52525b;
}
QTabBar::tab:selected
{
border-right: 1px solid #dc2626;
border-left: 1px solid #dc2626;
border-bottom: none;
}
QTabBar::tab:first:selected
{
border-right: 1px solid #dc2626;
}
QTabBar::tab:last:selected
{
border-left: 1px solid #dc2626;
}
QTabBar::tab:selected
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dc2626, stop: 1 #991b1b);
color: white;
}
QTabBar::tab#ubWebBrowserTabBar
{
background: #2d1517;
min-width: 150px;
color: #e8e6e3;
}
QTabBar::tab:selected#ubWebBrowserTabBar
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dc2626, stop: 1 #991b1b);
color: white;
}
QLineEdit#ubWebBrowserLineEdit
{
border: 1px solid #52525b;
border-radius: 3px;
padding: 2 2px;
background: #1a0a0a;
color: #e8e6e3;
}
QLabel
{
color: #e8e6e3;
background: transparent;
}
QGroupBox
{
color: #e8e6e3;
border: 1px solid #52525b;
border-radius: 4px;
margin-top: 8px;
padding-top: 8px;
}
QGroupBox::title
{
color: #dc2626;
subcontrol-origin: margin;
left: 10px;
padding: 0 4px;
}
QCheckBox
{
color: #e8e6e3;
background: transparent;
}
QCheckBox::indicator
{
width: 16px;
height: 16px;
border: 1px solid #52525b;
border-radius: 3px;
background: #1a0a0a;
}
QCheckBox::indicator:checked
{
background: #dc2626;
border: 1px solid #dc2626;
}
QRadioButton
{
color: #e8e6e3;
background: transparent;
}
QRadioButton::indicator
{
width: 14px;
height: 14px;
border: 1px solid #52525b;
border-radius: 7px;
background: #1a0a0a;
}
QRadioButton::indicator:checked
{
background: #dc2626;
border: 1px solid #dc2626;
}
QScrollArea
{
background: #1a0a0a;
border: none;
}
QScrollBar:vertical
{
background: #1a0a0a;
width: 10px;
border: none;
}
QScrollBar::handle:vertical
{
background: #52525b;
border-radius: 5px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover
{
background: #dc2626;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical
{
height: 0px;
border: none;
}
QScrollBar:horizontal
{
background: #1a0a0a;
height: 10px;
border: none;
}
QScrollBar::handle:horizontal
{
background: #52525b;
border-radius: 5px;
min-width: 20px;
}
QScrollBar::handle:horizontal:hover
{
background: #dc2626;
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal
{
width: 0px;
border: none;
}
QToolTip
{
background: #1a0a0a;
color: #fafaf9;
border: 1px solid #52525b;
border-radius: 4px;
padding: 6px 8px;
font-size: 12px;
}
QDockWidget
{
color: #e8e6e3;
background: #2d1517;
titlebar-close-icon: url();
titlebar-normal-icon: url();
}
QDockWidget::title
{
background: #2d1517;
color: #e8e6e3;
padding: 6px;
border-bottom: 1px solid #52525b;
}
QListWidget
{
background: #1a0a0a;
color: #e8e6e3;
border: 1px solid #52525b;
}
QListWidget::item:selected
{
background: #dc2626;
color: white;
}
QListWidget::item:hover
{
background: #3d1f1f;
}
QTextBrowser
{
background: #1a0a0a;
color: #e8e6e3;
border: 1px solid #52525b;
}
QFrame
{
color: #e8e6e3;
}
QStatusBar
{
background: #2d1517;
color: #e8e6e3;
border-top: 1px solid #52525b;
}
QStatusBar::item
{
border: none;
}

29
resources/etc/asyncAPI.js Normal file
View File

@ -0,0 +1,29 @@
var ch = new QWebChannel(qt.webChannelTransport, function (channel) {
// expose channel
window.channel = channel.objects;
// expose widget
if (channel.objects.widget) {
window.widget = channel.objects.widget;
}
// expose sankore
if (channel.objects.sankore) {
window.sankore = channel.objects.sankore;
window.sankore.async = new Object;
window.sankore.async.preference = function (key, def){
return new Promise((resolve,reject) => {
window.sankore.preference(key,def,function(result){
resolve(result);
});
});
};
window.sankore.async.preferenceKeys = function (){
return new Promise((resolve,reject) => {
window.sankore.preferenceKeys(function(result){
resolve(result);
});
});
};
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<podcast>
<title>{title}</title>
<description>{description}</description>
<author>{author}</author>
<pubDate>{publicationISODateTime}</pubDate>
<duration>{duration}</duration>
<fileName>{fileName}</fileName>
<filePath>{filePath}</filePath>
<computerName>{computerName}</computerName>
<fileSize>{fileSize}</fileSize>
</podcast>

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Flash Widget</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
html, body, #content { height:100%; }
body { margin:0; padding:0; overflow:hidden; }
</style>
<script type="text/javascript">
window.onload = function(){
if (window.widget) {
window.widget.onremove = function(){
document.getElementById("content").innerHTML = "";
}
}
}
</script>
</head>
<body>
<div id="content">
<object type="application/x-shockwave-flash" data="{in.url}" width="100%" height="100%">
<param name="movie" value="{in.url}">
<param name="quality" value="high">
<param name="wmode" value="window">
</object>
</div>
</body>
</html>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets"
xmlns:ub="http://uniboard.mnemis.com/widgets"
id="{in.id}"
version="1.0"
width="{in.width}"
height="{in.height}"
ub:resizable="true" >
<name>{in.name}</name>
<content src="{in.startFile}"/>
<access network="true"/>
</widget>

View File

@ -0,0 +1,30 @@
2538bef70e75fcc6c3ebe2ec35e0a904,http://www.openboard.org/widget/application/geoinfo,GeoInfo.wgt
7ade330c8afe00e543169486f65b7ccf,http://www.openboard.org/widget/application/icell,iCell.wgt
0e417c056f2725a7147c12a74cc17bfd,http://www.openboard.org/widget/interactivity/assaudio,Ass audio.wgt
183b3cee755f695eef5fa7293d0acd32,http://www.openboard.org/widget/interactivity/assimages,Ass images.wgt
dadbfcf9fe185fcb3aa15cacec3d1aa1,http://www.openboard.org/widget/interactivity/calculation,Calculation.wgt
93004e4027408228ab54788ec7754915,http://www.openboard.org/widget/interactivity/catpict,Cat pict.wgt
a04486256d11cc73d5a2b00210211281,http://www.openboard.org/widget/interactivity/cattext,Cat text.wgt
df82092a6e4d68a7280278f297ddebf3,http://www.openboard.org/widget/interactivity/choose,Choose.wgt
56af5526829eb249e88dd41ac59560cb,http://www.openboard.org/widget/interactivity/contrast,Constrast.wgt
30f44e3b88a846717b0f020efee6f10b,http://www.openboard.org/widget/interactivity/dial,Dial.wgt
8c8db441ae7a2a8554bd9a807ee0c7f1,http://www.openboard.org/widget/interactivity/dice,Dice.wgt
c40320882277b0edb686fd027891d854,http://www.openboard.org/widget/interactivity/envelope,Envelope.wgt
8e7274c11333723fbeb6db69e7edd27e,http://www.openboard.org/widget/interactivity/magicbox,Magic Box.wgt
6a2cf78eb4d791ec07596c25c616ea9d,http://www.openboard.org/widget/interactivity/memory,Memory.wgt
9b5c2d0bd4a9848d840fcdc7a6dab24a,http://www.openboard.org/widget/interactivity/orderletters,Order Letters.wgt
d9830d432c24da2b4695c5adeb502912,http://www.openboard.org/widget/interactivity/orderpictures,Order Pictures.wgt
f2696b7b5278c9f9e3ce3cf7490e2668,http://www.openboard.org/widget/interactivity/ordersentences,Order Sentence.wgt
1bb03f419c71ac7106af1222121cd8f3,http://www.openboard.org/widget/interactivity/orderwords,Order Words.wgt
120f379267c547d4b624970b4d917984,http://www.openboard.org/widget/interactivity/scale,Scale.wgt
d5d3dad235dd54f07cbb6b0ae4618ccb,http://www.openboard.org/widget/interactivity/select,Select.wgt
56ee5ef50994624c00c1ae1b178361f8,http://www.openboard.org/widget/interactivity/slider,Slider.wgt
b3e86f344fc33af6cb3ecfc17cfd1f3a,http://www.openboard.org/widget/interactivity/splitsentence,Split Sentence.wgt
ee0b851c2438ea3acd719cddf2a78bc8,http://www.openboard.org/widget/interactivity/splittext,Split Text.wgt
219856bb4a48189398adfb502bfa6586,http://www.openboard.org/widget/interactivity/syllables,Syllables.wgt
4d3dac3789c2040f6dba421d274e7244,http://www.openboard.org/widget/interactivity/tables,Tables.wgt
a8005369615ba9c19e9ea54d2ee45fe8,http://www.openboard.org/widget/interactivity/tictactoe,Tic Tac Toe.wgt
a37dcb2b5ce3d9addbcb21d6edf71274,http://www.openboard.org/widget/interactivity/train,Train.wgt
ba898498398a537e70a3698ba9122588,https://openedu.fr/widgets/papier,Papier.wgt
0f0e431c9008dfbd1e4119a7304d61e7,http://www.openboard.org/widget/graphme,GraphMe.wgt
69542ba419dade10d6a394260e06e60f,http://uniboard.mnemis.com/widgets/wiktionary,Wiktionary.wgt

24
resources/fonts/COPYRIGHT Normal file
View File

@ -0,0 +1,24 @@
The fonts on this directory are a subset the fonts contained on the gsfonts package.
All the fonts are under the license above
Upstream Authors: URW++ Design & Development GmbH
Valek Filippov <frob@df.ru> et. al.
Copyright: Copyright (c) 2001- Valek Filippov, All Rights Reserved.
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301, USA.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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