Repository: YomikoR/VapourSynth-Editor Branch: vs-api4 Commit: 9c2237c720dc Files: 200 Total size: 1.2 MB Directory structure: gitextract_u1yk7l4b/ ├── .gitignore ├── BUILDING ├── CHANGELOG ├── LICENSE ├── README ├── TODO.txt ├── common-src/ │ ├── application_instance_file_guard/ │ │ ├── application_instance_file_guard.cpp │ │ └── application_instance_file_guard.h │ ├── chrono.h │ ├── frame_header_writers/ │ │ ├── frame_header_writer.cpp │ │ ├── frame_header_writer.h │ │ ├── frame_header_writer_null.cpp │ │ ├── frame_header_writer_null.h │ │ ├── frame_header_writer_y4m.cpp │ │ └── frame_header_writer_y4m.h │ ├── helpers.cpp │ ├── helpers.h │ ├── helpers_vs.h │ ├── ipc_defines.h │ ├── jobs/ │ │ ├── job.cpp │ │ ├── job.h │ │ ├── job_variables.cpp │ │ └── job_variables.h │ ├── libp2p/ │ │ ├── COPYING │ │ ├── README.md │ │ ├── p2p.h │ │ ├── p2p_api.cpp │ │ ├── p2p_api.h │ │ ├── simd/ │ │ │ ├── cpuinfo_x86.cpp │ │ │ ├── cpuinfo_x86.h │ │ │ ├── p2p_simd.cpp │ │ │ ├── p2p_simd.h │ │ │ └── p2p_sse41.cpp │ │ └── v210.cpp │ ├── log/ │ │ ├── log_styles_model.cpp │ │ ├── log_styles_model.h │ │ ├── styled_log_view.cpp │ │ ├── styled_log_view.h │ │ ├── styled_log_view_core.cpp │ │ ├── styled_log_view_core.h │ │ ├── styled_log_view_settings_dialog.cpp │ │ ├── styled_log_view_settings_dialog.h │ │ ├── styled_log_view_settings_dialog.ui │ │ ├── styled_log_view_structures.cpp │ │ ├── styled_log_view_structures.h │ │ ├── vs_editor_log.cpp │ │ ├── vs_editor_log.h │ │ ├── vs_editor_log_definitions.cpp │ │ └── vs_editor_log_definitions.h │ ├── settings/ │ │ ├── settings_definitions.cpp │ │ ├── settings_definitions.h │ │ ├── settings_definitions_core.cpp │ │ ├── settings_definitions_core.h │ │ ├── settings_manager.cpp │ │ ├── settings_manager.h │ │ ├── settings_manager_core.cpp │ │ └── settings_manager_core.h │ ├── timeline_slider/ │ │ ├── timeline_slider.cpp │ │ └── timeline_slider.h │ ├── vapoursynth/ │ │ ├── vapoursynth_script_processor.cpp │ │ ├── vapoursynth_script_processor.h │ │ ├── vs_pack_rgb.cpp │ │ ├── vs_pack_rgb.h │ │ ├── vs_script_library.cpp │ │ ├── vs_script_library.h │ │ ├── vs_script_processor_structures.cpp │ │ ├── vs_script_processor_structures.h │ │ ├── vs_set_matrix.cpp │ │ └── vs_set_matrix.h │ ├── version_info.cpp │ ├── version_info.h │ ├── win32_console.cpp │ └── win32_console.h ├── msvc/ │ ├── VapourSynthEditor.sln │ ├── VapourSynthEditor.vcxproj │ ├── VapourSynthEditor.vcxproj.filters │ ├── libp2p/ │ │ ├── libp2p.vcxproj │ │ └── libp2p.vcxproj.filters │ ├── vsedit/ │ │ ├── resource.h │ │ ├── vsedit.rc │ │ ├── vsedit.vcxproj │ │ └── vsedit.vcxproj.filters │ ├── vsedit-encode/ │ │ ├── resource.h │ │ ├── vsedit-encode.rc │ │ ├── vsedit-encode.vcxproj │ │ └── vsedit-encode.vcxproj.filters │ ├── vsedit-job-server/ │ │ ├── resource.h │ │ ├── vsedit-job-server.rc │ │ ├── vsedit-job-server.vcxproj │ │ └── vsedit-job-server.vcxproj.filters │ ├── vsedit-job-server-watcher/ │ │ ├── resource.h │ │ ├── vsedit-job-server-watcher.rc │ │ ├── vsedit-job-server-watcher.vcxproj │ │ └── vsedit-job-server-watcher.vcxproj.filters │ └── vsedit-previewer/ │ ├── resource.h │ ├── vsedit-previewer.rc │ ├── vsedit-previewer.vcxproj │ └── vsedit-previewer.vcxproj.filters ├── pro/ │ ├── build-inno-setup.iss │ ├── build-msvc.bat │ ├── common.pri │ ├── pro.pro │ ├── vsedit/ │ │ └── vsedit.pro │ ├── vsedit-encode/ │ │ └── vsedit-encode.pro │ ├── vsedit-job-server/ │ │ └── vsedit-job-server.pro │ ├── vsedit-job-server-watcher/ │ │ └── vsedit-job-server-watcher.pro │ └── vsedit-previewer/ │ └── vsedit-previewer.pro ├── resources/ │ ├── dark/ │ │ ├── LICENSE.rst │ │ ├── rc/ │ │ │ └── .keep │ │ ├── style.qrc │ │ └── style.qss │ ├── vsedit-job-server-watcher.qrc │ ├── vsedit.icns │ └── vsedit.qrc ├── vsedit/ │ └── src/ │ ├── frame_consumers/ │ │ ├── benchmark_dialog.cpp │ │ ├── benchmark_dialog.h │ │ ├── benchmark_dialog.ui │ │ ├── encode_dialog.cpp │ │ ├── encode_dialog.h │ │ └── encode_dialog.ui │ ├── job_server_watcher_socket.cpp │ ├── job_server_watcher_socket.h │ ├── main.cpp │ ├── main_window.cpp │ ├── main_window.h │ ├── main_window.ui │ ├── preview/ │ │ ├── preview_advanced_settings_dialog.cpp │ │ ├── preview_advanced_settings_dialog.h │ │ ├── preview_advanced_settings_dialog.ui │ │ ├── preview_area.cpp │ │ ├── preview_area.h │ │ ├── preview_dialog.cpp │ │ ├── preview_dialog.h │ │ ├── preview_dialog.ui │ │ ├── scroll_navigator.cpp │ │ ├── scroll_navigator.h │ │ ├── zoom_ratio_spinbox.cpp │ │ └── zoom_ratio_spinbox.h │ ├── script_editor/ │ │ ├── number_matcher.cpp │ │ ├── number_matcher.h │ │ ├── script_completer.cpp │ │ ├── script_completer.h │ │ ├── script_completer_model.cpp │ │ ├── script_completer_model.h │ │ ├── script_editor.cpp │ │ ├── script_editor.h │ │ ├── syntax_highlighter.cpp │ │ └── syntax_highlighter.h │ ├── script_status_bar_widget/ │ │ ├── script_status_bar_widget.cpp │ │ ├── script_status_bar_widget.h │ │ └── script_status_bar_widget.ui │ ├── script_templates/ │ │ ├── drop_file_category_model.cpp │ │ ├── drop_file_category_model.h │ │ ├── templates_dialog.cpp │ │ ├── templates_dialog.h │ │ └── templates_dialog.ui │ ├── settings/ │ │ ├── actions_hotkey_edit_model.cpp │ │ ├── actions_hotkey_edit_model.h │ │ ├── clearable_key_sequence_editor.cpp │ │ ├── clearable_key_sequence_editor.h │ │ ├── item_delegate_for_hotkey.cpp │ │ ├── item_delegate_for_hotkey.h │ │ ├── settings_dialog.cpp │ │ ├── settings_dialog.h │ │ ├── settings_dialog.ui │ │ ├── theme_elements_model.cpp │ │ └── theme_elements_model.h │ ├── vapoursynth/ │ │ ├── vapoursynth_plugins_manager.cpp │ │ ├── vapoursynth_plugins_manager.h │ │ ├── vs_plugin_data.cpp │ │ ├── vs_plugin_data.h │ │ ├── vs_script_processor_dialog.cpp │ │ └── vs_script_processor_dialog.h │ ├── vsedit_encode_main.cpp │ └── vsedit_previewer_main.cpp ├── vsedit-job-server/ │ └── src/ │ ├── job_server.cpp │ ├── job_server.h │ ├── jobs/ │ │ ├── job_definitions.h │ │ ├── jobs_manager.cpp │ │ └── jobs_manager.h │ └── main.cpp └── vsedit-job-server-watcher/ └── src/ ├── connect_to_server_dialog.cpp ├── connect_to_server_dialog.h ├── connect_to_server_dialog.ui ├── jobs/ │ ├── job_dependencies_delegate.cpp │ ├── job_dependencies_delegate.h │ ├── job_edit_dialog.cpp │ ├── job_edit_dialog.h │ ├── job_edit_dialog.ui │ ├── job_state_delegate.cpp │ ├── job_state_delegate.h │ ├── jobs_model.cpp │ └── jobs_model.h ├── main.cpp ├── main_window.cpp ├── main_window.h ├── main_window.ui ├── trusted_clients_addresses_dialog.cpp ├── trusted_clients_addresses_dialog.h └── trusted_clients_addresses_dialog.ui ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ !/.gitignore !*.pro !*.pri !*.bat !/common-src/ !/vsedit/src/ !/vsedit-job-server/src/ !/vsedit-job-server-watcher/src/ !/resources/ !/BUILDING !/CHANGELOG !/LICENSE !/README !/TODO.txt !/msvc/vsedit/vsedit.rc !/msvc/vsedit/resource.h !/msvc/vsedit-previewer/vsedit-previewer.rc !/msvc/vsedit-previewer/resource.h !/msvc/vsedit-job-server/vsedit-job-server.rc !/msvc/vsedit-job-server/resource.h !/msvc/vsedit-job-server-watcher/vsedit-job-server-watcher.rc !/msvc/vsedit-job-server-watcher/resource.h build pro/*/*.rc pro/*/Makefile.Release pro/*/Makefile.Debug pro/*/Makefile pro/*/*.cpp pro/dist pro/.qmake.stash pro/Makefile pro/local_quirks.pri **/generated **/Release **/Debug msvc/**/*.aps *.d *.slo *.lo *.o *.obj *.gch *.pch *.so *.dylib *.dll *.mod *.smod *.lai *.la *.a *.lib *.exe *.out *.app */.vs *.vcxproj.user *.log *.props ================================================ FILE: BUILDING ================================================ THIS MANUAL IS A DRAFT. PLEASE REPORT ANY MISTAKES OR ADDITIONS AT https://github.com/YomikoR/VapourSynth-Editor/issues ## Prerequisites You need C++17 (or higher) compiler and Qt 6 development distribution. In Ubuntu 22.04 LTS, for example, the packages qt6-base-dev and libqt6websockets6-dev are required. ## Building VapourSynth Editor from source: 1) make sure you meet the above prerequisites; 2) open the system terminal and change working directory to the "pro" directory in the source tree; 3) (optional) set environment variable `VS_INCLUDE_PATH` for the include path of VapourSynth header files, e.g. for Windows CMD ```CMD FOR /F "USEBACKQ tokens=*" %I IN (`python -c "import vapoursynth;print(vapoursynth.get_include())"`) DO (SET VS_INCLUDE_PATH=%I) ``` and for *nix ```shell export VS_INCLUDE_PATH=`/usr/bin/env python3 -c "import vapoursynth;print(vapoursynth.get_include())"` ``` 4) execute following command to generate the Makefile and other intermediate files `qmake6 -norecursive pro.pro CONFIG+=release` 5) "make" the Makefile with platform specific make-tool, e.g. `nmake` for MS Visual Studio. The program files will be built in compiler specific sub-directory in the "build" directory in source tree and ready to use. If you encounter path issues during the building related to missing headers, etc., you may include them in the file "pro/local_quirks.pri". Below are some tested compilers: Windows MSVC Linux GCC MacOS clang (within brew) ================================================ FILE: CHANGELOG ================================================ r19-mod-6.10 -BUGFIX: Cannot load old vsscript from list due to change on the file name. r19-mod-6.9 -BUGFIX: Aborting dead process in job server watcher causes ghost entry. -Add match/case keywords to text editor highlighting (chrschmidt). -Make "Home" toggle between the block start and the first column (chrschmidt). -Disable color filling in the progress bars of job server watcher. -Add display support for _Range frame property since VS R73. -Search for vsscript library in environments since VS R74. -Switch to Qt6 regex functions. r19-mod-6.8 -BUGFIX: Switching output index could fail after immediate playback. -Add hotkey options for switching to previous or next output index. -Add standalone console launcher for encode panel. r19-mod-6.7 -BUGFIX: Messages sent by core logging are missing after preview refresh. -Add combo box for switching output index (enabled with VS R69 or later). r19-mod-6.6 -BUGFIX: Previewer does not launch. -Allow all VS versions with API4. -Remove option that reloads script from disk before evaluation. -Add option to periodically reload script from disk to sync with external changes. -Add an asterisk in preview window title that indicates change in text editor. -Add info texts to certain reserved frame props when displayed. -Improve error handling of the previewer. -Better sync output frame numbers by timestamp. r19-mod-6.5 -Use same vapoursynth library as used for script evaluation for plugin list. r19-mod-6.4 -BUGFIX: Filter logs are not shown before script is evaluated. -BUGFIX: VS messages sent from core are duplicated. -BUGFIX: Each frame from script is requested twice (no influence in performance). -BUGFIX: Loading vsscript library from VS R66 portable directory fails. -BUGFIX: output nodes #10-#19 are not properly cleared. Add audio playback (Windows only). Add options to set output syncing mode. Add core cache usage data in status bar. 2020_CL option is removed from preview advanced settings. r19-mod-6.3 -BUGFIX: On Linux the previewer refuses to quit. -In Windows 10 and later console window can show colored text (with ANSI escape sequences). -Add option to hide (VS and Qt) debug messages. -Add option to set dither type. -Show _AbsoluteTime frame props on title of preview panel. -Do not load chapter for VFR clips. -Add hotkey in preview panel for jumping to frame by frame number. r19-mod-6.2 -BUGFIX: Encoding CLI won't start in Linux. -BUGFIX: Script is not loaded when file path encoding is not UTF-8. -VSEditor is a GUI app again with an option to toggle console window. -Adapt to Windows dark theme. -Offer qdarkstyle theme for all platforms. r19-mod-6.1 -BUGFIX: Memory leak when closing preview window (regression from mod-6). -Support switching to output indices 10-19. -Add a standalone preview panel launcher, i.e. a script previewer. r19-mod-6 -Drop Qt 5 support. -Switch to VapourSynth API4 (won't work with API3 VSScript libraries). -Add support for previewing video nodes with variational format or dimensions. r19-mod-5.6 -BUGFIX: Chroma location parameter ignored when resampling to RGB for preview. -A panel is added for displaying frame properties. r19-mod-5.5 -BUGFIX: Wrong dimensions in cropping when there are multiple video outputs. -Binaries are now working as console applications (instead of GUI apps) in Windows. -Binaries print version in Windows if the console is not automatically hidden on start. -A shortcut launching VSEditor in CMD is added to the installer. -Unused doc path options are removed. -Added an option to prefer loading VS libs from settings (other than Windows registry or sys PATH). r19-mod-5.4 -BUGFIX: Reject problematic output nodes with invalid format. -Binaries print version and exit with -v or --version. r19-mod-5.3 -BUGFIX: Flickering in playback due to a regression introduced with the dark theme. -BUGFIX: Image doesn't align to the top-left corner of the preview window in dark theme. -BUGFIX: Bright and dark themes were using the same setting groups. -BUGFIX: Image distortion with large zoom ratio. -Any change of switching between bright and dark themes will only take effect on program relaunching. -Modified zoom ratio step lengths when using fixed ratio. r19-mod-5.2 -BUGFIX: VapourSynth lib searching not working in Qt6 on Windows. (SaltyChiang) -BUGFIX: Certain UI fonts look horrible in Qt6 on Windows with kerning enabled. -BUGFIX: Cursor pixelation in color panel in HIDPI. -BUGFIX: Wrong pipette info when cropping. -BUGFIX: Drop file template panel crashing when empty. -BUGFIX: Incorrect scroll navigator ratio in HIDPI. -Add dark theme (Windows only). -Introduce snapshot template with a silent mode (in preview advanced settings). -Slightly enlarged timeline bar. -Show "Name" and "SceneName" frame properties on window title. -Show Qt version in About menu. -Color and font settings will be saved only when differing from default values. r19-mod-5 -BUGFIX: Building fails in Ubuntu LTS with older Qt versions. -BUGFIX: A blank .config file may be mistakenly created. -BUGFIX: Benchmark window doesn't normally stop and exit when pressing Escape. -Adapt to internal resizer changes since VS R58. -Support building with Qt 6 (with certain progress bar features disabled). -Enable HIDPI support (only with Qt 6). -Let playback rate respect frame durations (i.e. VFR). -Let inno installer work with Windows 7 SP1 and later. -Add .vpy file association in the inno installer. -Rename chroma location options by their position names and remove DV. -Set default chroma subsampling method to Bicubic b=0 c=0.5. -Let GRAY clip with RGB matrix be previewed as usual. -Edit program descriptions. -Don't search Program Files by path for finding vsscript. -Default hotkeys for zoom modes changed from 1/2/3 to Alt + 1/2/3. -When previewing, switch output nodes 0-9 by hotkeys 0-9. r19-mod-4: -BUGFIX: Encode preset header not correctly loaded. -BUGFIX: Loading chapters for VFR video results in division by zero. -BUGFIX: Failure when building in a recent version of MacOS. -BUGFIX: Program crashes when launching from second screen in MacOS with Qt 5.12. -BUGFIX: Only the main screen's bit depth is reported. -BUGFIX: Settings are lost in current session when switching to portable mode while config file is not writable. -Add Inno build script for a Windows installer (by rlaphoenix). -Add a setting entry for reloading script from disk before execution. -Improve open-box experience of the text editor: using spaces as tabs, 12pt font size, adjusted colors for dark themes. -Raise minimum VapourSynth version requirement to R47 (API 3.6). -Remove a number of obsolete Qt methods (raising Qt version requirement to 5.8). -Frame numbers computed from chapter timestamps are rounded instead of ceiling. -"Shell commands" in job server won't be executed with cmd /c or sh -c prefix anymore. r19-mod-3: -BUGFIX: Crash on file drop setting menu. -BUGFIX: Black screen for 10-bit preview. -Add back support for YCOCG and COMPAT colorspaces. -Show cursur position and on screen RGB values with color panel. -Add a setting entry for snapshot compression level. r19-mod-2: -Improve performance for RGB format packing (thanks to DJATOM and sekrit-twc). r19-mod-1: -Drop support for YCOCG and COMPAT colorspaces. -Work with VapourSynth v4 API (built with v3). -Dither to RGB for preview output. -Preview in 10-bit color depth in Unix when allowed. -Compress PNG file size when saving snapshots. -Replace legacy get_core() from the template. r19: -BUGFIX: Rapid settings updating on windows geometry change. -BUGFIX: Theme settings corruption when using job server. -Color picker update with a still mouse cursor in play mode. -Benchmark dialog remembers first and last frame for current script. r18: -BUGFIX: Crash on encode dialog initialization error. -BUGFIX: No error in log on encode dialog initialization error. -Import chapter files as preview bookmarks (by brainvlad@gmail.com). r17: -BUGFIX: Blank preview on variable size video with fixed zoom ratio. -BUGFIX: Saving new script. -BUGFIX: Invalid context menu for editor. -BUGFIX: Context menu behavior in preview. -New multi-app architecture: editor, job server, server watcher. r16: -BUGFIX: Default hotkey forced when trying to save an empty hotkey. -BUGFIX: Inactive actions in the log context menu. -Jobs queue with dependencies tracking. -Adjustable jobs table. -Pausable CLI encoding jobs. -Pausable process run jobs. -Shell command execute jobs. -Removed framebuffer monitoring. -Move text block up action. -Move text block down action. -Toggle comment action. -Fixed VS API version requested for internal plugins polling. -Larger settings dialog to remove the warning. r15: -BUGFIX: crash on colour picking while refreshing preview. -BUGFIX: random junk instead of black frame on preview refresh. -BUGFIX: wrong hours displayed in estimated finish time for benchmark and encoding. -Buildable with Qt version lower than 5.4. -Float formats support in yuv4mpeg video header for encoding. r14: -BUGFIX: Encoding logic. -Core buffer usage display. -Relative paths are resolved from the application directory, not CWD. -Benchmark and encoding progress in window title. -MS Windows: taskbar button progress for benchmark and encoding. -Script dialogs status bar reorganized. -WebP snapshots support. r13: -yuv4mpeg header for video encoding. r12: -Improved log. -Crash log is saved as HTML from the main log. r11: -BUGFIX: Default file drop template. -BUGFIX: Preview non-YUV clips. -An option to keep the currently previewed frame number on previewing different script. r10: -BUGFIX: Colour picking. -BUGFIX: VapourSynth messages handling. -BUGFIX: Frame processing errors handling in different modes. -BUGFIX: Pasting crop snippet into the last script line. -BUGFIX: Benchmark and encode dialogs forward initialization error to main window log and hide on error if open. -Crashlog on VapourSynth fatal errors. -Keep preview scrolling and frame number on refreshing the same script. Reset on previewing new script. Unsaved script preview is always reset. -Editor: selected text/current line duplication action. -Editor: comment/uncomment selected lines actions. -Editor: multiline tab and backtab. -Options to use spaces as Tab and set Tab size. -Editor: Replace Tab characters with spaces action. -Editor: smart Home key behaviour. -An option to remember and restore the last previewed frame between sessions. -New script template setting. -Code snippets. -File drop templates. -Option to highlight selection matches in script editor. -Timeline bookmarks with auto-saving/loading bookmarks file along the script file. -Remember timeline panel visibility. -Most timeline colours are bound to OS theme. -Changes in default standard hotkeys. Many default hotkeys are now OS-dependent. CTRL + arrows in preview window now move between bookmarks and CTRL + SHIFT + arrows jump time intervals. -Frames number and subsampling string tokens in encoder. -Estimated finish time output in benchmark and encoder. -Encoder argument tokens format changed into more readable one. -Colour picker moved into status bar. -Paste shown frame number into script action. r⑨: -Asynchronous frames processing. More responsive GUI. -Preview video playback. -Script processing benchmarking. -Encoding video with CLI tools. r8: -BUGFIX: Preview stride. r7: -BUGFIX: Bt.601 YUV to RGB conversion matrix. Not sure if it works correctly, but it works. -BUGFIX: Massive memory leak. -Late linking to vsscript library. Can start with no VapourSynth installed. -Better detection of VapourSynth installation on Windows. -Experimental colour picker. Shows values under cursor in preview window. Not thoroughly tested. r6: -Added some theme settings. -Switched preview to use the internal resizer instead of zimg. Requires VapourSynth R29+. -Support for building under MacOS X (not tested). r5: Fix release. -Fixed compatibility with VapourSynth r27. Patch by Myrsloik. -Fixed "Copy frame to clipboard" action. r4: -Custom font is embedded. -Internal format conversion for preview. All VapourSynth video formats are now supported. r3: -Fixed zoom ratio changed to real number. -New line autoindentation. r2: -File paths are changed to canonical before adding to recent files list to eliminate doubling records. -Change window title when script path changes. -Always create new script on start before trying to open any. ================================================ FILE: LICENSE ================================================ Copyright (c) 2014 - 2017 Aleksey [Mystery Keeper] Lyashin mystkeeper@gmail.com Copyright (c) 2022 - 2026 YomikoR Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README ================================================ VapourSynth Editor r19-mod-6.10 By Aleksey [Mystery Keeper] Lyashin mystkeeper@gmail.com http://mysterykeeper.ru Modified by YomikoR This software is free and distributed under MIT license. Software uses Qt framework by The Qt Company, distributed under LGPL license. https://qt.io/ Software includes Silk icons v1.3 by Mark James, distributed under Creative Commons Attribution 2.5 License. http://www.famfamfam.com/lab/icons/silk/ Software includes FatCow free icons, distributed under Creative Commons Attribution 3.0 License. http://www.fatcow.com/free-icons Software includes QDarkStyleSheet v3.0.3, distributed under MIT License for code, and Creative Commons Attribution 4.0 International License for images. https://github.com/ColinDuquesnoy/QDarkStyleSheet For additional information see LICENSE file. ================================================ FILE: TODO.txt ================================================ - search and replace text - choose the default vsscript library load path - insert formatted data from preview window into script - functions autocompletion refinement - python error parsing and script line highlighting - multiple output nodes comparison - reference and documentation ================================================ FILE: common-src/application_instance_file_guard/application_instance_file_guard.cpp ================================================ #include "application_instance_file_guard.h" #include #include #ifndef Q_OS_WIN #include #endif //============================================================================== ApplicationInstanceFileGuard::ApplicationInstanceFileGuard( const QString & a_fileName) { m_tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); if(!a_fileName.isEmpty()) lock(a_fileName); } //============================================================================== ApplicationInstanceFileGuard::~ApplicationInstanceFileGuard() { if(m_file.isOpen()) unlock(); } //============================================================================== bool ApplicationInstanceFileGuard::lock(const QString & a_fileName) { if(m_file.isOpen()) { if(a_fileName == m_fileName) return true; bool unlocked = unlock(); if(!unlocked) return false; } QString filePath = m_tempDir + "/" + a_fileName; #ifdef Q_OS_WIN if(QFile::exists(filePath)) { bool deleted = QFile::remove(filePath); if(!deleted) { m_error = QObject::tr("Could not delete file %1") .arg(filePath); return false; } } #endif m_fileName = a_fileName; m_file.setFileName(filePath); bool opened = m_file.open(QIODevice::ReadWrite); if(!opened) { m_error = QObject::tr("Could not open file %1").arg(filePath); return false; } #ifndef Q_OS_WIN int result = flock(m_file.handle(), LOCK_EX | LOCK_NB); if(result != 0) { m_error = QObject::tr("Could not lock file %1").arg(filePath); m_file.close(); return false; } #endif return true; } //============================================================================== bool ApplicationInstanceFileGuard::unlock() { if(m_file.isOpen()) { #ifndef Q_OS_WIN flock(m_file.handle(), LOCK_UN | LOCK_NB); #endif m_file.close(); } QString filePath = m_file.fileName(); if(filePath.isEmpty()) { m_error = QObject::tr("File name is empty."); return false; } if(!QFile::exists(filePath)) return true; bool deleted = QFile::remove(filePath); if(!deleted) m_error = QObject::tr("Could not delete file %1").arg(filePath); return deleted; } //============================================================================== bool ApplicationInstanceFileGuard::isLocked() const { return m_file.isOpen(); } //============================================================================== QString ApplicationInstanceFileGuard::error() const { return m_error; } //============================================================================== ================================================ FILE: common-src/application_instance_file_guard/application_instance_file_guard.h ================================================ #ifndef APPLICATION_INSTANCE_FILE_GUARD_H_INCLUDED #define APPLICATION_INSTANCE_FILE_GUARD_H_INCLUDED #include #include class ApplicationInstanceFileGuard { public: ApplicationInstanceFileGuard(const QString & a_fileName = QString()); virtual ~ApplicationInstanceFileGuard(); bool lock(const QString & a_fileName); bool unlock(); bool isLocked() const; QString error() const; private: QFile m_file; QString m_error; QString m_fileName; QString m_tempDir; }; #endif // APPLICATION_INSTANCE_FILE_GUARD_H_INCLUDED ================================================ FILE: common-src/chrono.h ================================================ #ifndef CHRONO_H_INCLUDED #define CHRONO_H_INCLUDED #include typedef std::chrono::high_resolution_clock::time_point hr_time_point; typedef std::chrono::high_resolution_clock hr_clock; typedef std::chrono::duration > double_duration; template double duration_to_double(const T & a_duration) { return std::chrono::duration_cast(a_duration).count(); } #endif // CHRONO_H_INCLUDED ================================================ FILE: common-src/frame_header_writers/frame_header_writer.cpp ================================================ #include "frame_header_writer.h" //============================================================================== FrameHeaderWriter::FrameHeaderWriter(const VSAPI * a_cpVSAPI, const VSVideoInfo * a_cpVideoInfo, QObject * a_pParent) : QObject(a_pParent) , m_cpVSAPI(a_cpVSAPI) , m_cpVideoInfo(a_cpVideoInfo) { } // END OF FrameHeaderWriter::FrameHeaderWriter(const VSAPI * a_cpVSAPI, // const VSVideoInfo * a_cpVideoInfo, QObject * a_pParent) //============================================================================== FrameHeaderWriter::~FrameHeaderWriter() { } // END OF FrameHeaderWriter::~FrameHeaderWriter() //============================================================================== void FrameHeaderWriter::setVSAPI(const VSAPI * a_cpVSAPI) { m_cpVSAPI = a_cpVSAPI; } // END OF void FrameHeaderWriter::setVSAPI(const VSAPI * a_cpVSAPI) //============================================================================== void FrameHeaderWriter::setVideoInfo(const VSVideoInfo * a_cpVideoInfo) { m_cpVideoInfo = a_cpVideoInfo; } // END OF void FrameHeaderWriter::setVideoInfo( // const VSVideoInfo * a_cpVideoInfo) //============================================================================== ================================================ FILE: common-src/frame_header_writers/frame_header_writer.h ================================================ #ifndef FRAME_HEADER_WRITER_H_INCLUDED #define FRAME_HEADER_WRITER_H_INCLUDED #include #include class FrameHeaderWriter : public QObject { Q_OBJECT public: FrameHeaderWriter(const VSAPI * a_cpVSAPI = nullptr, const VSVideoInfo * a_cpVideoInfo = nullptr, QObject * a_pParent = nullptr); virtual ~FrameHeaderWriter(); virtual void setVSAPI(const VSAPI * a_cpVSAPI); virtual void setVideoInfo(const VSVideoInfo * a_cpVideoInfo); virtual bool isCompatible() = 0; virtual bool needVideoHeader() = 0; virtual QByteArray videoHeader(int a_totalFrames = -1) = 0; virtual bool needFramePrefix() = 0; virtual QByteArray framePrefix(const VSFrame * a_cpFrame) = 0; virtual bool needFramePostfix() = 0; virtual QByteArray framePostfix(const VSFrame * a_cpFrame) = 0; protected: const VSAPI * m_cpVSAPI; const VSVideoInfo * m_cpVideoInfo; }; #endif // FRAME_HEADER_WRITER_H_INCLUDED ================================================ FILE: common-src/frame_header_writers/frame_header_writer_null.cpp ================================================ #include "frame_header_writer_null.h" //============================================================================== FrameHeaderWriterNull::FrameHeaderWriterNull(const VSAPI * a_cpVSAPI, const VSVideoInfo * a_cpVideoInfo, QObject * a_pParent) : FrameHeaderWriter(a_cpVSAPI, a_cpVideoInfo, a_pParent) { } // END OF FrameHeaderWriterNull::FrameHeaderWriterNull(const VSAPI * a_cpVSAPI, // const VSVideoInfo * a_cpVideoInfo, QObject * a_pParent) //============================================================================== bool FrameHeaderWriterNull::isCompatible() { Q_ASSERT(m_cpVideoInfo); if(!m_cpVideoInfo) return false; return true; } // END OF bool FrameHeaderWriterNull::isCompatible() //============================================================================== bool FrameHeaderWriterNull::needVideoHeader() { return false; } // END OF bool FrameHeaderWriterNull::needVideoHeader() //============================================================================== QByteArray FrameHeaderWriterNull::videoHeader(int a_totalFrames) { (void)a_totalFrames; return QByteArray(); } // END OF QByteArray FrameHeaderWriterNull::videoHeader(int a_totalFrames) //============================================================================== bool FrameHeaderWriterNull::needFramePrefix() { return false; } // END OF bool FrameHeaderWriterNull::needFramePrefix() //============================================================================== QByteArray FrameHeaderWriterNull::framePrefix(const VSFrame * a_cpFrame) { (void)a_cpFrame; return QByteArray(); } // END OF QByteArray FrameHeaderWriterNull::framePrefix( // const VSFrame * a_cpFrame) //============================================================================== bool FrameHeaderWriterNull::needFramePostfix() { return false; } // END OF bool FrameHeaderWriterNull::needFramePostfix() //============================================================================== QByteArray FrameHeaderWriterNull::framePostfix(const VSFrame * a_cpFrame) { (void)a_cpFrame; return QByteArray(); } // END OF QByteArray FrameHeaderWriterNull::framePostfix( // const VSFrame * a_cpFrame) //============================================================================== ================================================ FILE: common-src/frame_header_writers/frame_header_writer_null.h ================================================ #ifndef FRAME_HEADER_WRITER_NULL_H_INCLUDED #define FRAME_HEADER_WRITER_NULL_H_INCLUDED #include "frame_header_writer.h" class FrameHeaderWriterNull : public FrameHeaderWriter { Q_OBJECT public: FrameHeaderWriterNull(const VSAPI * a_cpVSAPI = nullptr, const VSVideoInfo * a_cpVideoInfo = nullptr, QObject * a_pParent = nullptr); virtual bool isCompatible() override; virtual bool needVideoHeader() override; virtual QByteArray videoHeader(int a_totalFrames = -1) override; virtual bool needFramePrefix() override; virtual QByteArray framePrefix(const VSFrame * a_cpFrame) override; virtual bool needFramePostfix() override; virtual QByteArray framePostfix(const VSFrame * a_cpFrame) override; }; #endif // FRAME_HEADER_WRITER_NULL_H_INCLUDED ================================================ FILE: common-src/frame_header_writers/frame_header_writer_y4m.cpp ================================================ #include "frame_header_writer_y4m.h" #include "../../../common-src/helpers.h" #include #include //============================================================================== FrameHeaderWriterY4M::FrameHeaderWriterY4M(const VSAPI * a_cpVSAPI, const VSVideoInfo * a_cpVideoInfo, QObject * a_pParent) : FrameHeaderWriter(a_cpVSAPI, a_cpVideoInfo, a_pParent) { } // END OF FrameHeaderWriterY4M::FrameHeaderWriterY4M(const VSAPI * a_cpVSAPI, // const VSVideoInfo * a_cpVideoInfo, QObject * a_pParent) //============================================================================== bool FrameHeaderWriterY4M::isCompatible() { Q_ASSERT(m_cpVideoInfo); if(!m_cpVideoInfo) return false; const VSVideoFormat * cpFormat = &m_cpVideoInfo->format; int compatibleColorFamily[] = {cfGray, cfYUV}; if(!vsedit::contains(compatibleColorFamily, cpFormat->colorFamily)) return false; if(cpFormat->sampleType == stFloat) { int acceptableBitDepths[] = {16, 32, 64}; if(!vsedit::contains(acceptableBitDepths, cpFormat->bitsPerSample)) return false; } std::pair compatibleSubsampling[] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 2}}; std::pair subsampling(cpFormat->subSamplingW, cpFormat->subSamplingH); if(!vsedit::contains(compatibleSubsampling, subsampling)) return false; return true; } // END OF bool FrameHeaderWriterY4M::isCompatible() //============================================================================== bool FrameHeaderWriterY4M::needVideoHeader() { return true; } // END OF bool FrameHeaderWriterY4M::needVideoHeader() //============================================================================== QByteArray FrameHeaderWriterY4M::videoHeader(int a_totalFrames) { Q_ASSERT(m_cpVideoInfo); Q_ASSERT(isCompatible()); const VSVideoFormat * cpFormat = &m_cpVideoInfo->format; std::string header; header += "YUV4MPEG2 C"; if(cpFormat->colorFamily == cfGray) { header += "mono"; if(cpFormat->bitsPerSample > 8) header += std::to_string(cpFormat->bitsPerSample); } else if(cpFormat->colorFamily == cfYUV) { std::map, std::string> subsamplingStringMap = { {{0, 0}, "444"}, {{0, 1}, "440"}, {{1, 0}, "422"}, {{1, 1}, "420"}, {{2, 0}, "411"}, {{2, 2}, "410"}, }; std::pair subsampling(cpFormat->subSamplingW, cpFormat->subSamplingH); header += subsamplingStringMap[subsampling]; if((cpFormat->bitsPerSample > 8) && (cpFormat->sampleType == stInteger)) header += "p" + std::to_string(cpFormat->bitsPerSample); else if(cpFormat->sampleType == stFloat) { header += "p"; if(cpFormat->bitsPerSample == 16) header += "h"; else if(cpFormat->bitsPerSample == 32) header += "s"; else if(cpFormat->bitsPerSample == 64) header += "d"; else { Q_ASSERT(false); header += "u"; } } } else { Q_ASSERT(false); } int totalFrames = (a_totalFrames < 0) ? m_cpVideoInfo->numFrames : a_totalFrames; header = header + " W" + std::to_string(m_cpVideoInfo->width) + " H" + std::to_string(m_cpVideoInfo->height) + " F" + std::to_string(m_cpVideoInfo->fpsNum) + ":" + std::to_string(m_cpVideoInfo->fpsDen) + " Ip A0:0" + " XLENGTH=" + std::to_string(totalFrames) + "\n"; QByteArray headerData(header.c_str()); return headerData; } // END OF QByteArray FrameHeaderWriterY4M::videoHeader(int a_totalFrames) //============================================================================== bool FrameHeaderWriterY4M::needFramePrefix() { return true; } // END OF bool FrameHeaderWriterY4M::needFramePrefix() //============================================================================== QByteArray FrameHeaderWriterY4M::framePrefix(const VSFrame * a_cpFrame) { (void)a_cpFrame; std::string prefix = "FRAME\n"; QByteArray prefixData(prefix.c_str()); return prefixData; } // END OF QByteArray FrameHeaderWriterY4M::framePrefix( // const VSFrame * a_cpFrame) //============================================================================== bool FrameHeaderWriterY4M::needFramePostfix() { return false; } // END OF bool FrameHeaderWriterY4M::needFramePostfix() //============================================================================== QByteArray FrameHeaderWriterY4M::framePostfix(const VSFrame * a_cpFrame) { (void)a_cpFrame; return QByteArray(); } // END OF QByteArray FrameHeaderWriterY4M::framePostfix( // const VSFrame * a_cpFrame) //============================================================================== ================================================ FILE: common-src/frame_header_writers/frame_header_writer_y4m.h ================================================ #ifndef FRAME_HEADER_WRITER_Y4M_H_INCLUDED #define FRAME_HEADER_WRITER_Y4M_H_INCLUDED #include "frame_header_writer.h" class FrameHeaderWriterY4M : public FrameHeaderWriter { Q_OBJECT public: FrameHeaderWriterY4M(const VSAPI * a_cpVSAPI = nullptr, const VSVideoInfo * a_cpVideoInfo = nullptr, QObject * a_pParent = nullptr); virtual bool isCompatible() override; virtual bool needVideoHeader() override; virtual QByteArray videoHeader(int a_totalFrames = -1) override; virtual bool needFramePrefix() override; virtual QByteArray framePrefix(const VSFrame * a_cpFrame) override; virtual bool needFramePostfix() override; virtual QByteArray framePostfix(const VSFrame * a_cpFrame) override; }; #endif // FRAME_HEADER_WRITER_Y4M_H_INCLUDED ================================================ FILE: common-src/helpers.cpp ================================================ #include "helpers.h" #include #include #include #include #include #include /* https://www.ffmpeg.org/ffmpeg-utils.html#Channel-Layout */ static std::map audioChannelToString = { {acFrontLeft, "FL"}, {acFrontRight, "FR"}, {acFrontCenter, "FC"}, {acLowFrequency, "LFE"}, {acBackLeft, "BL"}, {acBackRight, "BR"}, {acFrontLeftOFCenter, "FLC"}, {acFrontRightOFCenter, "FRC"}, {acBackCenter, "BC"}, {acSideLeft, "SL"}, {acSideRight, "SR"}, {acTopCenter, "TC"}, {acTopFrontLeft, "TFL"}, {acTopFrontCenter, "TFC"}, {acTopFrontRight, "TFR"}, {acTopBackLeft, "TBL"}, {acTopBackCenter, "TBC"}, {acTopBackRight, "TBR"}, {acStereoLeft, "DL"}, // "downmix left" {acStereoRight, "DR"}, // "downmix right" {acWideLeft, "WL"}, {acWideRight, "WR"}, {acSurroundDirectLeft, "SDL"}, {acSurroundDirectRight, "SDR"}, {acLowFrequency2, "LFE2"} }; static uint64_t genAudioChannelFlag(std::vector channels) { uint64_t flag = 0; for(VSAudioChannels c : channels) { flag |= (static_cast(1) << c); } return flag; } #define gACF genAudioChannelFlag static std::map audioChannelToPreset = { {gACF({acFrontCenter}), "mono"}, {gACF({acFrontLeft, acFrontRight}), "stereo"}, {gACF({acFrontLeft, acFrontRight, acLowFrequency}), "2.1"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter}), "3.0"}, {gACF({acFrontLeft, acFrontRight, acBackCenter}), "3.0(back)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acBackCenter}), "4.0"}, {gACF({acFrontLeft, acFrontRight, acBackLeft, acBackRight}), "quad"}, {gACF({acFrontLeft, acFrontRight, acSideLeft, acSideRight}), "quad(side)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency}), "3.1"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acSideLeft, acSideRight}), "5.0"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acBackCenter}), "4.1"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acBackLeft, acBackRight}), "5.1"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acSideLeft, acSideRight}), "5.1(side)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acBackCenter, acSideLeft, acSideRight}), "6.0"}, {gACF({acFrontLeft, acFrontRight, acFrontLeftOFCenter, acFrontRightOFCenter, acSideLeft, acSideRight}), "6.0(front)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acBackLeft, acBackRight, acBackCenter}), "hexagonal"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acBackLeft, acBackRight, acBackCenter}), "6.1"}, {gACF({acFrontLeft, acFrontRight, acLowFrequency, acFrontLeftOFCenter, acFrontRightOFCenter, acSideLeft, acSideRight}), "6.1(front)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acBackLeft, acBackRight, acSideLeft, acSideRight}), "7.0"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acFrontLeftOFCenter, acFrontRightOFCenter, acSideLeft, acSideRight}), "7.0(front)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acBackLeft, acBackRight, acSideLeft, acSideRight}), "7.1"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acBackLeft, acBackRight, acFrontLeftOFCenter, acFrontRightOFCenter}), "7.1(wide)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acLowFrequency, acFrontLeftOFCenter, acFrontRightOFCenter, acSideLeft, acSideRight}), "7.1(wide-side)"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acBackLeft, acBackRight, acBackCenter, acSideLeft, acSideRight}), "octagonal"}, {gACF({acFrontLeft, acFrontRight, acFrontCenter, acBackLeft, acBackRight, acBackCenter, acSideLeft, acSideRight, acWideLeft, acWideRight, acTopBackLeft, acTopBackRight, acTopBackCenter, acTopFrontCenter, acTopFrontLeft, acTopFrontRight}), "hexadecagonal"}, {gACF({acStereoLeft, acStereoRight}), "downmix"} }; //============================================================================== QString vsedit::timeToString(double a_seconds, bool a_fullFormat) { if(a_seconds <= 0.0) return QString("0"); // Milliseconds cut-off a_seconds = std::round(a_seconds * 1000.0) / 1000.0; // Seconds uint64_t integer = (uint64_t)a_seconds; int seconds = integer % 60ll; integer /= 60ll; int minutes = integer % 60ll; integer /= 60ll; int hours = integer; QString timeString; if((hours > 0) || a_fullFormat) { timeString = QString("%1:%2:%3").arg(hours) .arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0')); } else { timeString = QString("%1:%2") .arg(minutes) .arg(seconds, 2, 10, QChar('0')); } // Fraction double fraction = a_seconds - std::floor(a_seconds); if((fraction > 0.0) || a_fullFormat) timeString += QString::number(fraction, 'f', 3).mid(1); return timeString; } // END OF QString vsedit::timeToString(double a_seconds, bool a_fullFormat) //============================================================================== int vsedit::mod(int a_value) { int l_mod = 1 << 6; while(a_value % l_mod != 0) l_mod >>= 1; return l_mod; } // END OF int vsedit::mod(int a_value) //============================================================================== QString vsedit::videoInfoString(const VSVideoInfo * a_cpVideoInfo, const VSAPI * a_cpVSAPI) { double fps = 0.0; double time = 0.0; if(a_cpVideoInfo->fpsDen != 0) { fps = (double)a_cpVideoInfo->fpsNum / (double)a_cpVideoInfo->fpsDen; time = (double)a_cpVideoInfo->numFrames * (double)a_cpVideoInfo->fpsDen / (double)a_cpVideoInfo->fpsNum; } QString infoString = QString("Frames: %frames% | Time: %time% | Size: " "%width%x%height% | FPS: %fpsnum%/%fpsden% = %fps% | Format: %format%"); infoString.replace("%frames%", QString::number(a_cpVideoInfo->numFrames)); infoString.replace("%time%", vsedit::timeToString(time, true)); infoString.replace("%width%", QString::number(a_cpVideoInfo->width)); infoString.replace("%height%", QString::number(a_cpVideoInfo->height)); infoString.replace("%fpsnum%", QString::number(a_cpVideoInfo->fpsNum)); infoString.replace("%fpsden%", QString::number(a_cpVideoInfo->fpsDen)); infoString.replace("%fps%", QString::number(fps)); char formatName[32]; a_cpVSAPI->getVideoFormatName(&a_cpVideoInfo->format, formatName); infoString.replace("%format%", formatName); return infoString; } // END OF QString vsedit::videoInfoString(const VSVideoInfo * a_cpVideoInfo) //============================================================================== QString vsedit::audioInfoString(const VSAudioInfo * a_cpAudioInfo, const VSAPI * a_cpVSAPI) { QString infoString = QString("Frames: %frames% | Time: %time% | " "Sample Rate: %srate% Hz | Samples: %ns% | " "Channels: %channels%| Format: %format%"); infoString.replace("%frames%", QString::number(a_cpAudioInfo->numFrames)); infoString.replace("%time%", vsedit::timeToString( double(a_cpAudioInfo->numSamples) / a_cpAudioInfo->sampleRate, true)); infoString.replace("%srate%", QString::number(a_cpAudioInfo->sampleRate)); infoString.replace("%ns%", QString::number(a_cpAudioInfo->numSamples)); QString channelsString(""); uint64_t channelLayoutFlag = a_cpAudioInfo->format.channelLayout; auto found = audioChannelToPreset.find(channelLayoutFlag); if(found != audioChannelToPreset.end()) { channelsString += QString("[%1] ") .arg(audioChannelToPreset.at(channelLayoutFlag)); } for(auto it : audioChannelToString) { if((static_cast(1) << it.first) & channelLayoutFlag) { channelsString += QString("%1 ").arg(it.second); } } infoString.replace("%channels%", channelsString); char formatName[32]; a_cpVSAPI->getAudioFormatName(&a_cpAudioInfo->format, formatName); infoString.replace("%format%", formatName); return infoString; } // END OF QString vsedit::audioInfoString(const VSAudioInfo * a_cpAudioInfo, // const VSAPI * a_cpVSAPI) //============================================================================== QString vsedit::nodeInfoString(const VSNodeInfo & a_nodeInfo, const VSAPI * a_cpVSAPI) { if(a_nodeInfo.isInvalid()) return QString(""); else if(a_nodeInfo.isAudio()) return vsedit::audioInfoString(a_nodeInfo.getAsAudio(), a_cpVSAPI); else { Q_ASSERT(a_cpVSAPI); return vsedit::videoInfoString(a_nodeInfo.getAsVideo(), a_cpVSAPI); } } // END OF QString vsedit::nodeInfoString(const VSNodeInfo & a_nodeInfo, // const VSAPI * a_cpVSAPI) //============================================================================== double vsedit::qtimeToSeconds(const QTime & a_qtime) { double seconds = (double)a_qtime.msec() / 1000.0; seconds += (double)a_qtime.second(); seconds += (double)a_qtime.minute() * 60.0; seconds += (double)a_qtime.hour() * 360.0; return seconds; } // END OF double vsedit::qtimeToSeconds(const QTime & a_qtime) //============================================================================== QTime vsedit::secondsToQTime(double a_seconds) { QTime qtime; if(a_seconds <= 0.0) return qtime; // Milliseconds cut-off a_seconds = std::round(a_seconds * 1000.0) / 1000.0; // Seconds uint64_t integer = (uint64_t)a_seconds; int seconds = integer % 60ll; integer /= 60ll; int minutes = integer % 60ll; integer /= 60ll; int hours = integer % 60ll; int milliseconds = (int)(((a_seconds - std::round(a_seconds)) * 1000.0)); qtime.setHMS(hours, minutes, seconds, milliseconds); return qtime; } // END OF QTime vsedit::secondsToQTime(double a_seconds) //============================================================================== void vsedit::wait(int a_msec) { if(a_msec <= 0) return; QTime mark = QTime::currentTime().addMSecs(a_msec); while(QTime::currentTime() < mark) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } // END OF void vsedit::wait(int a_msec) //============================================================================== QString vsedit::subsamplingString(int a_subsamplingW, int a_subsamplingH) { if((a_subsamplingW == 0) && (a_subsamplingH == 0)) return QString("444"); if((a_subsamplingW == 0) && (a_subsamplingH == 1)) return QString("440"); if((a_subsamplingW == 1) && (a_subsamplingH == 0)) return QString("422"); if((a_subsamplingW == 1) && (a_subsamplingH == 1)) return QString("420"); if((a_subsamplingW == 2) && (a_subsamplingH == 0)) return QString("411"); if((a_subsamplingW == 2) && (a_subsamplingH == 2)) return QString("410"); return QString(); } // END OF QString vsedit::subsamplingString(int a_subsamplingW, // int a_subsamplingH) //============================================================================== QString vsedit::subsamplingString(const VSVideoFormat * a_cpFormat) { if(!a_cpFormat) return QString(); return subsamplingString(a_cpFormat->subSamplingW, a_cpFormat->subSamplingH); } // END OF QString vsedit::subsamplingString(const VSVideoFormat * a_cpFormat) //============================================================================== QString vsedit::resolvePathFromApplication(const QString & a_relativePath) { // Remember the working directory and change it to application directory. QString cwd = QDir::currentPath(); QString applicationDirPath = QCoreApplication::applicationDirPath(); QDir::setCurrent(applicationDirPath); QFileInfo fileInfo(a_relativePath); // If no parent directory is specified - leave the path as it is. if(fileInfo.path() == ".") return(a_relativePath); QString absolutePath = fileInfo.absoluteFilePath(); // Restore the working directory. QDir::setCurrent(cwd); return absolutePath; } // END OF QString vsedit::resolvePathFromApplication( // const QString & a_relativePath) //============================================================================== QByteArray vsedit::jsonMessage(const QString & a_command, const QJsonObject & a_jsonObject) { return vsedit::jsonMessage(a_command, QJsonDocument(a_jsonObject)); } // END OF QByteArray vsedit::jsonMessage(const QString & a_command, // const QJsonObject & a_jsonObject) //============================================================================== QByteArray vsedit::jsonMessage(const QString & a_command, const QJsonArray & a_jsonArray) { return vsedit::jsonMessage(a_command, QJsonDocument(a_jsonArray)); } // END OF QByteArray vsedit::jsonMessage(const QString & a_command, // const QJsonArray & a_jsonArray) //============================================================================== QByteArray vsedit::jsonMessage(const QString & a_command, const QJsonDocument & a_jsonDocument) { return a_command.toUtf8() + ' ' + a_jsonDocument.toJson(); } // END OF QByteArray vsedit::jsonMessage(const QString & a_command, // const QJsonDocument & a_jsonDocument) //============================================================================== vsedit::FP32 vsedit::halfToSingle(vsedit::FP16 a_half) { FP32 o = { 0 }; // From ISPC ref code if (a_half.parts.Exponent == 0 && a_half.parts.Mantissa == 0) // (Signed) zero o.parts.Sign = a_half.parts.Sign; else { if (a_half.parts.Exponent == 0) // Denormal (will convert to normalized) { // Adjust mantissa so it's normalized (and keep track of exp adjust) int e = -1; unsigned int m = a_half.parts.Mantissa; do { e++; m <<= 1; } while ((m & 0x400) == 0); o.parts.Mantissa = (m & 0x3ff) << 13; o.parts.Exponent = 127 - 15 - e; o.parts.Sign = a_half.parts.Sign; } else if (a_half.parts.Exponent == 0x1f) // Inf/NaN { // NOTE: It's safe to treat both with the same code path // by just truncating lower Mantissa bits in NaNs (this is valid). o.parts.Mantissa = a_half.parts.Mantissa << 13; o.parts.Exponent = 255; o.parts.Sign = a_half.parts.Sign; } else // Normalized number { o.parts.Mantissa = a_half.parts.Mantissa << 13; o.parts.Exponent = 127 - 15 + a_half.parts.Exponent; o.parts.Sign = a_half.parts.Sign; } } return o; } // END OF vsedit::FP32 vsedit::halfToSingle(vsedit::FP16 a_half) //============================================================================== ================================================ FILE: common-src/helpers.h ================================================ #ifndef HELPERS_H_INCLUDED #define HELPERS_H_INCLUDED #include #include "helpers_vs.h" #include #include #include #include #include #include #include #include #include #include #include namespace vsedit { struct VariableToken { QString token; QString description; std::function evaluate; }; QString timeToString(double a_seconds, bool a_fullFormat = false); int mod(int a_value); QString videoInfoString(const VSVideoInfo * a_cpVideoInfo, const VSAPI * a_cpVSAPI); QString audioInfoString(const VSAudioInfo * a_cpAudioInfo, const VSAPI * a_cpVSAPI); QString nodeInfoString(const VSNodeInfo &a_nodeInfo, const VSAPI * a_cpVSAPI); double qtimeToSeconds(const QTime & a_qtime); QTime secondsToQTime(double a_seconds); void wait(int a_msec); QString subsamplingString(int a_subsamplingW, int a_subsamplingH); QString subsamplingString(const VSVideoFormat * a_cpFormat); QString resolvePathFromApplication(const QString & a_relativePath); QByteArray jsonMessage(const QString & a_command, const QJsonObject & a_jsonObject); QByteArray jsonMessage(const QString & a_command, const QJsonArray & a_jsonArray); QByteArray jsonMessage(const QString & a_command, const QJsonDocument & a_jsonDocument); template void clamp(T1& a_value, const T2& a_low, const T3& a_high) { Q_ASSERT(a_high > a_low); if(a_value < a_low) a_value = a_low; else if(a_value > a_high) a_value = a_high; } template bool contains(const Container_T & a_container, const Value_T & a_value) { return (std::find(std::begin(a_container), std::end(a_container), a_value) != std::end(a_container)); } template T roundUp(T a_number, T a_multiple) { if(a_multiple == 0) return a_number; T remainder = a_number % a_multiple; if(remainder == 0) return a_number; return a_number + a_multiple - remainder; } template QByteArray toByteArray(const T & a_data) { QByteArray buf; QDataStream stream(&buf, QIODevice::WriteOnly); stream << a_data; return buf; } template T fromByteArray(const QByteArray & a_array) { QDataStream stream(a_array); T data; stream >> data; return data; } //------------------------------------------------------------------------------ // Half to single precision float conversion // by Fabian "ryg" Giesen. union FP32 { uint32_t u; float f; struct { unsigned int Mantissa : 23; unsigned int Exponent : 8; unsigned int Sign : 1; } parts; }; union FP16 { uint16_t u; struct { unsigned int Mantissa : 10; unsigned int Exponent : 5; unsigned int Sign : 1; } parts; }; FP32 halfToSingle(FP16 a_half); /* This is a patch for Qt 6 in Windows */ template void disableFontKerning(T * a_pWidget) { QFont font(a_pWidget->font()); font.setKerning(false); a_pWidget->setFont(font); } //------------------------------------------------------------------------------ } #endif // HELPERS_H_INCLUDED ================================================ FILE: common-src/helpers_vs.h ================================================ #ifndef HELPERS_VS_H_INCLUDED #define HELPERS_VS_H_INCLUDED #include #include #include enum class ProcessReason { Preview, Check, Benchmark, Encode, }; template class VSMediaTypePicker { protected: const TV * m_pTV; const TA * m_pTA; int m_mediaType; public: VSMediaTypePicker() : m_pTV(nullptr) , m_pTA(nullptr) , m_mediaType(-1) {} VSMediaTypePicker(const TV * a_pTV) : VSMediaTypePicker() { if(a_pTV) { m_pTV = a_pTV; m_mediaType = mtVideo; } } VSMediaTypePicker(const TA * a_pTA) : VSMediaTypePicker() { if(a_pTA) { m_pTA = a_pTA; m_mediaType = mtAudio; } } const void * get() const { switch (m_mediaType) { case mtAudio: return reinterpret_cast(m_pTA); case mtVideo: return reinterpret_cast(m_pTV); default: return nullptr; } } const TV * getAsVideo() const { return m_pTV; } const TA * getAsAudio() const { return m_pTA; } void set(const TV * a_pTV) { m_pTV = a_pTV; m_pTA = nullptr; m_mediaType = a_pTV ? mtVideo : -1; } void set(const TA * a_pTA) { m_pTV = nullptr; m_pTA = a_pTA; m_mediaType = a_pTA ? mtAudio : -1; } void setNull() { m_pTV = nullptr; m_pTA = nullptr; m_mediaType = -1; } bool isAudio() const { return m_mediaType == mtAudio; } bool isVideo() const { return m_mediaType == mtVideo; } bool isInvalid() const { return (!isVideo()) && (!isAudio()); } int mediaType() const { return m_mediaType; } }; class VSNodeInfo : public VSMediaTypePicker { public: VSNodeInfo() : VSMediaTypePicker() {} VSNodeInfo(VSNode * a_pNode, const VSAPI * a_cpVSAPI) { int mediaType = a_cpVSAPI->getNodeType(a_pNode); if(mediaType == mtAudio) set(a_cpVSAPI->getAudioInfo(a_pNode)); else set(a_cpVSAPI->getVideoInfo(a_pNode)); } int numFrames() const { switch (m_mediaType) { case mtAudio: return m_pTA->numFrames; case mtVideo: return m_pTV->numFrames; default: return -1; } } std::pair fpsPair() const { if (m_mediaType == mtAudio) { std::pair res = {m_pTA->sampleRate, VS_AUDIO_FRAME_SAMPLES}; vsh::reduceRational(&res.first, &res.second); return res; } else if (m_mediaType == mtVideo) return {m_pTV->fpsNum, m_pTV->fpsDen}; else return {0, 0}; } }; class VSFrameFormat : public VSMediaTypePicker { public: VSFrameFormat() : VSMediaTypePicker() {} VSFrameFormat(const VSFrame * a_cpFrame, const VSAPI * a_cpVSAPI) { int mediaType = a_cpVSAPI->getFrameType(a_cpFrame); if(mediaType == mtAudio) set(a_cpVSAPI->getAudioFrameFormat(a_cpFrame)); else set(a_cpVSAPI->getVideoFrameFormat(a_cpFrame)); } }; inline bool isVariableSize(const VSVideoInfo *vi) { return vi->width == 0 && vi->height == 0; } inline bool isVariableFPS(const VSVideoInfo *vi) { return vi->fpsDen == 0 && vi->fpsNum == 0; } inline bool isVariableFormat(const VSVideoInfo *vi) { return vi->format.colorFamily == cfUndefined || vi->format.bitsPerSample == 0 || vi->format.bytesPerSample == 0 || vi->format.numPlanes == 0; } #endif ================================================ FILE: common-src/ipc_defines.h ================================================ #ifndef IPC_DEFINES_H_INCLUDED #define IPC_DEFINES_H_INCLUDED // Watcher <-> Job server communication static const char JOB_SERVER_NAME[] = "vsedit_job_server"; static const uint16_t JOB_SERVER_PORT = 3370; // Client messages static const char MSG_GET_JOBS_INFO[] = "GJI"; static const char MSG_GET_LOG[] = "GL"; static const char MSG_SUBSCRIBE[] = "SS"; static const char MSG_UNSUBSCRIBE[] = "USS"; static const char MSG_CLOSE_SERVER[] = "CS"; static const char MSG_GET_TRUSTED_CLIENTS[] = "GTC"; static const char MSG_SET_TRUSTED_CLIENTS[] = "STC"; static const char MSG_CREATE_JOB[] = "CJ"; static const char MSG_CHANGE_JOB[] = "CHJ"; static const char MSG_SET_JOB_DEPENDENCIES[] = "SJD"; static const char MSG_SWAP_JOBS[] = "SJ"; static const char MSG_RESET_JOBS[] = "RJ"; static const char MSG_DELETE_JOBS[] = "DJ"; static const char MSG_START_ALL_WAITING_JOBS[] = "SAWJ"; static const char MSG_PAUSE_ACTIVE_JOBS[] = "PACJ"; static const char MSG_RESUME_PAUSED_JOBS[] = "RPJ"; static const char MSG_ABORT_ACTIVE_JOBS[] = "AACJ"; // Server messages static const char SMSG_JOBS_INFO[] = "JI"; static const char SMSG_COMPLETE_LOG[] = "LOG"; static const char SMSG_LOG_MESSAGE[] = "LM"; static const char SMSG_JOB_CREATED[] = "JC"; static const char SMSG_JOB_UPDATE[] = "JU"; static const char SMSG_JOB_STATE_UPDATE[] = "JSU"; static const char SMSG_JOB_PROGRESS_UPDATE[] = "JPU"; static const char SMSG_JOB_START_TIME_UPDATE[] = "JSTU"; static const char SMSG_JOB_END_TIME_UPDATE[] = "JETU"; static const char SMSG_JOB_DEPENDENCIES_UPDATE[] = "JDU"; static const char SMSG_JOBS_SWAPPED[] = "JSW"; static const char SMSG_JOBS_DELETED[] = "JD"; static const char SMSG_REFUSE[] = "RF"; static const char SMSG_CLOSING_SERVER[] = "SCS"; static const char SMSG_TRUSTED_CLIENTS_INFO[] = "TCI"; // Editor <-> Watcher communication static const char JOB_SERVER_WATCHER_LOCAL_SERVER_NAME[] = "vsedit_job_server_watcher"; static const char WMSG_SHOW_WINDOW[] = "S"; static const char WMSG_CLI_ENCODE_JOB[] = "CEJ"; #endif // IPC_DEFINES_H_INCLUDED ================================================ FILE: common-src/jobs/job.cpp ================================================ #include "job.h" #include "../../../common-src/helpers.h" #include "../../../common-src/settings/settings_manager_core.h" #include "../../../common-src/vapoursynth/vs_script_library.h" #include "../../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../frame_header_writers/frame_header_writer_null.h" #include "../frame_header_writers/frame_header_writer_y4m.h" #include "../../../common-src/jobs/job_variables.h" #include #include #include #include #ifdef Q_OS_WIN #ifndef NOMINMAX #define NOMINMAX #endif #include #else #include #endif //============================================================================== vsedit::Job::Job(const JobProperties & a_properties, SettingsManagerCore * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QObject * a_pParent) : QObject(a_pParent) , JobVariables() , m_properties(a_properties) , m_lastFrameProcessed(-1) , m_lastFrameRequested(-1) , m_encodingState(EncodingState::Idle) , m_bytesToWrite(0u) , m_bytesWritten(0u) , m_pSettingsManager(a_pSettingsManager) , m_pVSScriptLibrary(a_pVSScriptLibrary) , m_pVapourSynthScriptProcessor(nullptr) , m_cpVSAPI(nullptr) , m_cpVideoInfo(nullptr) , m_pFrameHeaderWriter(nullptr) , m_cachedFramesLimit(100) , m_framesInQueue(0) , m_framesInProcess(0) , m_maxThreads(0) , m_memorizedEncodingTime(0.0) { fillVariables(); if(a_pVSScriptLibrary) m_cpVSAPI = m_pVSScriptLibrary->getVSAPI(); connect(&m_process, SIGNAL(started()), this, SLOT(slotProcessStarted())); connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotProcessFinished(int, QProcess::ExitStatus))); connect(&m_process, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(slotProcessError(QProcess::ProcessError))); connect(&m_process, SIGNAL(readChannelFinished()), this, SLOT(slotProcessReadChannelFinished())); connect(&m_process, SIGNAL(bytesWritten(qint64)), this, SLOT(slotProcessBytesWritten(qint64))); connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(slotProcessReadyReadStandardError())); } // END OF vsedit::Job::Job(const JobProperties & a_properties, // SettingsManagerCore * a_pSettingsManager, // VSScriptLibrary * a_pVSScriptLibrary, QObject * a_pParent) //============================================================================== vsedit::Job::~Job() { } // END OF vsedit::Job::~Job() //============================================================================== bool vsedit::Job::isActive() const { return vsedit::contains(ACTIVE_JOB_STATES, m_properties.jobState); } // END OF bool vsedit::Job::isActive() const //============================================================================== QUuid vsedit::Job::id() const { return m_properties.id; } // END OF QUuid vsedit::Job::id() const //============================================================================== bool vsedit::Job::setId(const QUuid & a_id) { if(isActive()) return false; m_properties.id = a_id; return true; } // END OF bool vsedit::Job::setId(const QUuid & a_id) //============================================================================== JobType vsedit::Job::type() const { return m_properties.type; } // END OF JobType vsedit::Job::type() const //============================================================================== bool vsedit::Job::setType(JobType a_type) { if(isActive()) return false; m_properties.type = a_type; return true; } // END OF bool vsedit::Job::setType(JobType a_type) //============================================================================== QString vsedit::Job::scriptName() const { return m_properties.scriptName; } // END OF QString vsedit::Job::scriptName() const //============================================================================== bool vsedit::Job::setScriptName(const QString & a_scriptName) { if(isActive()) return false; m_properties.scriptName = a_scriptName; return true; } // END OF bool vsedit::Job::setScriptName(const QString & a_scriptName) //============================================================================== QString vsedit::Job::scriptText() const { return m_properties.scriptText; } // END OF QString vsedit::Job::scriptText() const //============================================================================== bool vsedit::Job::setScriptText(const QString & a_scriptText) { if(isActive()) return false; m_properties.scriptText = a_scriptText; return true; } // END OF bool vsedit::Job::setScriptName(const QString & a_scriptText) //============================================================================== EncodingHeaderType vsedit::Job::encodingHeaderType() const { return m_properties.encodingHeaderType; } // END OF EncodingHeaderType vsedit::Job::encodingHeaderType() const //============================================================================== bool vsedit::Job::setEncodingHeaderType(EncodingHeaderType a_headerType) { m_properties.encodingHeaderType = a_headerType; return true; } // END OF bool vsedit::Job::setEncodingHeaderType( // EncodingHeaderType a_headerType) //============================================================================== QString vsedit::Job::executablePath() const { return m_properties.executablePath; } // END OF QString vsedit::Job::executablePath() const //============================================================================== bool vsedit::Job::setExecutablePath(const QString & a_path) { m_properties.executablePath = a_path; return true; } // END OF bool vsedit::Job::setExecutablePath(const QString & a_path) //============================================================================== QString vsedit::Job::arguments() const { return m_properties.arguments; } // END OF QString vsedit::Job::arguments() const //============================================================================== bool vsedit::Job::setArguments(const QString & a_arguments) { if(isActive()) return false; m_properties.arguments = a_arguments; return true; } // END OF bool vsedit::Job::setArguments(const QString & a_arguments) //============================================================================== QString vsedit::Job::shellCommand() const { return m_properties.shellCommand; } // END OF QString vsedit::Job::shellCommand() const //============================================================================== bool vsedit::Job::setShellCommand(const QString & a_command) { if(isActive()) return false; m_properties.shellCommand = a_command; return true; } // END OF bool vsedit::Job::setShellCommand(const QString & a_command) //============================================================================== JobState vsedit::Job::state() const { return m_properties.jobState; } // END OF JobState vsedit::Job::state() const //============================================================================== bool vsedit::Job::setState(JobState a_state) { if(isActive()) return false; changeStateAndNotify(a_state); return true; } // END OF bool vsedit::Job::setState(JobState a_state) //============================================================================== std::vector vsedit::Job::dependsOnJobIds() const { return m_properties.dependsOnJobIds; } // END OF std::vector vsedit::Job::dependsOnJobIds() const //============================================================================== bool vsedit::Job::setDependsOnJobIds(const std::vector & a_ids) { if(isActive()) return false; m_properties.dependsOnJobIds = a_ids; return true; } // END OF bool vsedit::Job::setDependsOnJobIds(const std::vector & a_ids) //============================================================================== QString vsedit::Job::subject() const { QString subjectString; if(m_properties.type == JobType::EncodeScriptCLI) { subjectString = QString("%sn%:\n\"%ep%\" %arg%"); subjectString = subjectString.replace("%sn%", resolvePathFromApplication(m_properties.scriptName)); subjectString = subjectString.replace("%ep%", resolvePathFromApplication(m_properties.executablePath)); subjectString = subjectString.replace("%arg%", decodeArguments(m_properties.arguments)); } else if(m_properties.type == JobType::RunProcess) { subjectString = QString("\"%ep%\" %arg%"); subjectString = subjectString.replace("%ep%", resolvePathFromApplication(m_properties.executablePath)); subjectString = subjectString.replace("%arg%", m_properties.arguments.simplified()); } else if(m_properties.type == JobType::RunShellCommand) subjectString = m_properties.shellCommand.simplified(); return subjectString; } // END OF QString vsedit::Job::subject() const //============================================================================== int vsedit::Job::firstFrame() const { if(m_properties.type != JobType::EncodeScriptCLI) return -1; if(m_properties.firstFrameReal >= 0) return m_properties.firstFrameReal; return m_properties.firstFrame; } // END OF int vsedit::Job::firstFrame() const //============================================================================== bool vsedit::Job::setFirstFrame(int a_frame) { if(isActive()) return false; if(m_properties.type != JobType::EncodeScriptCLI) return false; if(a_frame < 0) return false; m_properties.firstFrame = a_frame; if(m_pVapourSynthScriptProcessor->isInitialized()) { Q_ASSERT(m_cpVideoInfo); m_properties.firstFrameReal = std::min(m_properties.firstFrame, m_cpVideoInfo->numFrames - 1); } else m_properties.firstFrameReal = -1; return true; } // END OF bool vsedit::Job::setFirstFrame(int a_frame) //============================================================================== int vsedit::Job::lastFrame() const { if(m_properties.type != JobType::EncodeScriptCLI) return -1; if(m_properties.lastFrameReal >= 0) return m_properties.lastFrameReal; return m_properties.lastFrame; } // END OF int vsedit::Job::lastFrame() const //============================================================================== bool vsedit::Job::setLastFrame(int a_frame) { if(isActive()) return false; if(m_properties.type != JobType::EncodeScriptCLI) return false; if(a_frame < 0) return false; m_properties.lastFrame = a_frame; if(m_pVapourSynthScriptProcessor->isInitialized()) { Q_ASSERT(m_cpVideoInfo); m_properties.lastFrameReal = std::min(m_properties.lastFrame, m_cpVideoInfo->numFrames - 1); } else m_properties.lastFrameReal = -1; return true; } // END OF bool vsedit::Job::setFirstFrame(int a_frame) //============================================================================== int vsedit::Job::framesProcessed() const { if(m_properties.type == JobType::EncodeScriptCLI) return m_properties.framesProcessed; return 0; } // END OF int vsedit::Job::framesProcessed() const //============================================================================== int vsedit::Job::framesTotal() const { if(m_properties.type == JobType::EncodeScriptCLI) return (m_properties.lastFrameReal - m_properties.firstFrameReal + 1); return 0; } // END OF int vsedit::Job::framesTotal() const //============================================================================== double vsedit::Job::fps() const { return m_properties.fps; } // END OF double vsedit::Job::fps() const //============================================================================== double vsedit::Job::secondsToFinish() const { int framesLeft = framesTotal() - framesProcessed(); double seconds = (double)framesLeft / m_properties.fps; return seconds; } // END OF double vsedit::Job::secondsToFinish() const //============================================================================== size_t vsedit::Job::framesInQueue() const { return m_framesInQueue; } // END OF size_t vsedit::Job::framesInQueue() const //============================================================================== size_t vsedit::Job::framesInProcess() const { return m_framesInProcess; } // END OF size_t vsedit::Job::framesInProcess() const //============================================================================== size_t vsedit::Job::maxThreads() const { return m_maxThreads; } // END OF size_t vsedit::Job::maxThreads() const //============================================================================== JobProperties vsedit::Job::properties() const { return m_properties; } // END OF JobProperties vsedit::Job::properties() const //============================================================================== bool vsedit::Job::setProperties(const JobProperties & a_properties) { if(isActive()) return false; m_properties = a_properties; return true; } // END OF bool vsedit::Job::setProperties(const JobProperties & a_properties) //============================================================================== const VSVideoInfo * vsedit::Job::videoInfo() const { if(m_properties.type != JobType::EncodeScriptCLI) return nullptr; if(!m_pVapourSynthScriptProcessor) return nullptr; return m_pVapourSynthScriptProcessor->nodeInfo().getAsVideo(); } // END OF const VSVideoInfo * vsedit::Job::videoInfo() const //============================================================================== bool vsedit::Job::initialize() { if(m_properties.type != JobType::EncodeScriptCLI) return false; if(isActive() && (m_encodingState != EncodingState::Idle)) { emit signalLogMessage(tr("Can not initialize an active job"), LOG_STYLE_ERROR); return false; } if(m_properties.scriptText.isEmpty()) { QString absoluteScriptPath = resolvePathFromApplication(m_properties.scriptName); QFile scriptFile(absoluteScriptPath); bool opened = scriptFile.open(QIODevice::ReadOnly); if(!opened) { emit signalLogMessage(tr("Could not open script file \"%1\".") .arg(m_properties.scriptName), LOG_STYLE_ERROR); changeStateAndNotify(JobState::Failed); return false; } m_properties.scriptText = QString::fromUtf8(scriptFile.readAll()); scriptFile.close(); } if((!m_pVSScriptLibrary) || (!m_pSettingsManager)) { emit signalLogMessage(tr("Job is not created properly."), LOG_STYLE_ERROR); changeStateAndNotify(JobState::Failed); return false; } m_cpVSAPI = m_pVSScriptLibrary->getVSAPI(); Q_ASSERT(m_cpVSAPI); if(!m_pVapourSynthScriptProcessor) { m_pVapourSynthScriptProcessor = new VapourSynthScriptProcessor( m_pSettingsManager, m_pVSScriptLibrary, this); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotWriteLogMessage(int, const QString &))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFrameQueueStateChanged(size_t, size_t, size_t, double)), this, SLOT(slotFrameQueueStateChanged(size_t, size_t, size_t, double))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFinalized()), this, SLOT(slotScriptProcessorFinalized())); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalDistributeFrame(int, int, const VSFrame *, const VSFrame *)), this, SLOT(slotReceiveFrame(int, int, const VSFrame *, const VSFrame *))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFrameRequestDiscarded(int, int, const QString &)), this, SLOT(slotFrameRequestDiscarded(int, int, const QString &))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFrameQueueStateChanged(size_t, size_t, size_t, double)), this, SLOT(slotFrameQueueStateChanged(size_t, size_t, size_t, double))); } if((!m_pVapourSynthScriptProcessor->isInitialized()) || (m_pVapourSynthScriptProcessor->scriptName() != m_properties.scriptName) || (m_pVapourSynthScriptProcessor->script() != m_properties.scriptText)) { bool scriptProcessorInitialized = m_pVapourSynthScriptProcessor->initialize( m_properties.scriptText, m_properties.scriptName, 0, ProcessReason::Encode); if(!scriptProcessorInitialized) { emit signalLogMessage(tr("Failed to initialize script.\n%1") .arg(m_pVapourSynthScriptProcessor->error()), LOG_STYLE_ERROR); changeStateAndNotify(JobState::Failed); return false; } } VSNodeInfo info = m_pVapourSynthScriptProcessor->nodeInfo(); Q_ASSERT(!info.isInvalid()); if(info.isAudio()) { emit signalLogMessage(tr("Audio encoding is not supported.")); changeStateAndNotify(JobState::Failed); return false; } m_cpVideoInfo = info.getAsVideo(); m_properties.framesProcessed = 0; m_properties.firstFrameReal = m_properties.firstFrame; vsedit::clamp(m_properties.firstFrameReal, 0, m_cpVideoInfo->numFrames - 1); m_properties.lastFrameReal = m_properties.lastFrame; if((m_properties.lastFrameReal < m_properties.firstFrameReal) || (m_properties.lastFrameReal >= m_cpVideoInfo->numFrames)) m_properties.lastFrameReal = m_cpVideoInfo->numFrames - 1; m_lastFrameRequested = m_properties.firstFrameReal - 1; m_lastFrameProcessed = m_lastFrameRequested; m_encodingState = EncodingState::Idle; m_bytesToWrite = 0u; m_bytesWritten = 0u; return true; } // END OF bool vsedit::Job::initialize() //============================================================================== void vsedit::Job::cleanUpEncoding() { if(m_process.state() == QProcess::Running) { if(m_encodingState != EncodingState::Aborting) m_encodingState = EncodingState::Finishing; m_process.closeWriteChannel(); } if(m_pVapourSynthScriptProcessor) m_pVapourSynthScriptProcessor->finalize(); clearFramesCache(); m_framebuffer.clear(); m_cpVideoInfo = nullptr; } // END OF void vsedit::Job::cleanUpEncoding() //============================================================================== void vsedit::Job::start() { if(m_properties.jobState == JobState::Paused) { changeStateAndNotify(JobState::Running); if(m_properties.type == JobType::EncodeScriptCLI) processFramesQueue(); else if(m_properties.type == JobType::RunProcess) { #ifdef Q_OS_WIN BOOL result = DebugActiveProcessStop((DWORD)m_process.processId()); if(result) changeStateAndNotify(JobState::Running); else emit signalLogMessage(tr("Failed to resume process. " "Error %1.").arg(GetLastError()), LOG_STYLE_ERROR); #else int error = kill((pid_t)m_process.processId(), SIGCONT); if(!error) changeStateAndNotify(JobState::Running); else emit signalLogMessage(tr("Failed to resume process. " "Error %1.").arg(error), LOG_STYLE_ERROR); #endif } } else if(!isActive()) { m_properties.timeStarted = QDateTime::currentDateTimeUtc(); changeStateAndNotify(JobState::Running); emit signalStartTimeChanged(); if(m_properties.type == JobType::EncodeScriptCLI) startEncodeScriptCLI(); else if(m_properties.type == JobType::RunProcess) startRunProcess(); else if(m_properties.type == JobType::RunShellCommand) startRunShellCommand(); } } // END OF void vsedit::Job::start() //============================================================================== void vsedit::Job::pause() { if(m_properties.jobState != JobState::Running) return; if(m_properties.type == JobType::EncodeScriptCLI) { EncodingState invalidEncodingStates[] = {EncodingState::Idle, EncodingState::EncoderCrashed, EncodingState::Finishing, EncodingState::Aborting}; if(vsedit::contains(invalidEncodingStates, m_encodingState)) return; changeStateAndNotify(JobState::Pausing); } else if(m_properties.type == JobType::RunProcess) { #ifdef Q_OS_WIN BOOL result = DebugActiveProcess((DWORD)m_process.processId()); if(result) changeStateAndNotify(JobState::Paused); else emit signalLogMessage(tr("Failed to pause process. Error %1.") .arg(GetLastError()), LOG_STYLE_ERROR); #else int error = kill((pid_t)m_process.processId(), SIGSTOP); if(!error) changeStateAndNotify(JobState::Paused); else emit signalLogMessage(tr("Failed to pause process. Error %1.") .arg(error), LOG_STYLE_ERROR); #endif } } // END OF void vsedit::Job::pause() //============================================================================== void vsedit::Job::abort() { if(!isActive()) return; if(m_process.state() != QProcess::Running){ cleanUpEncoding(); changeStateAndNotify(JobState::Aborted); return; } changeStateAndNotify(JobState::Aborting); if(m_properties.type == JobType::EncodeScriptCLI) { m_encodingState = EncodingState::Aborting; cleanUpEncoding(); return; } else if(m_properties.type == JobType::RunProcess) { if(m_process.state() == QProcess::Running) { m_process.kill(); m_process.waitForFinished(-1); } } changeStateAndNotify(JobState::Aborted); } // END OF void vsedit::Job::abort() //============================================================================== void vsedit::Job::slotProcessStarted() { if(m_encodingState == EncodingState::CheckingEncoderSanity) return; if(m_properties.type == JobType::EncodeScriptCLI) { emit signalLogMessage(tr("Encoder started. Beginning encoding.")); if(!m_process.isWritable()) { m_encodingState = EncodingState::Aborting; m_properties.jobState = JobState::Aborting; emit signalLogMessage(tr("Can not write to encoder. Aborting."), LOG_STYLE_ERROR); cleanUpEncoding(); return; } Q_ASSERT(m_pFrameHeaderWriter); if(m_pFrameHeaderWriter->needVideoHeader()) { QByteArray videoHeader = m_pFrameHeaderWriter->videoHeader(framesTotal()); if(m_properties.encodingHeaderType == EncodingHeaderType::Y4M) emit signalLogMessage(tr("Y4M header: ") + QString::fromLatin1(videoHeader), LOG_STYLE_DEBUG); m_bytesToWrite = videoHeader.size(); if(m_bytesToWrite > 0) { m_bytesWritten = 0; m_encodingState = EncodingState::WritingHeader; qint64 bytesWritten = m_process.write(videoHeader); if(bytesWritten < 0) { m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); emit signalLogMessage( tr("Error on writing header to encoder. Aborting."), LOG_STYLE_ERROR); cleanUpEncoding(); return; } return; } } m_memorizedEncodingTime = 0.0; m_encodeRangeStartTime = hr_clock::now(); m_encodingState = EncodingState::WaitingForFrames; processFramesQueue(); } } // END OF void vsedit::Job::slotProcessStarted() //============================================================================== void vsedit::Job::slotProcessFinished(int a_exitCode, QProcess::ExitStatus a_exitStatus) { if(m_properties.type == JobType::EncodeScriptCLI) { EncodingState workingStates[] = {EncodingState::WaitingForFrames, EncodingState::WritingFrame, EncodingState::WritingHeader}; if(m_encodingState == EncodingState::CheckingEncoderSanity) return; else if(m_encodingState == EncodingState::Idle) return; else if(m_encodingState == EncodingState::Finishing) changeStateAndNotify(JobState::CompletedCleanUp); else if(vsedit::contains(workingStates, m_encodingState)) { QString exitStatusString = (a_exitStatus == QProcess::CrashExit) ? tr("crash") : tr("normal exit"); emit signalLogMessage(tr("Encoder has finished " "unexpectedly.\nReason: %1; exit code: %2") .arg(exitStatusString).arg(a_exitCode), LOG_STYLE_ERROR); changeStateAndNotify(JobState::FailedCleanUp); } cleanUpEncoding(); finishEncodingCLI(); } else if(m_properties.type == JobType::RunProcess) { QString message = tr("Process has finished."); QString logStyle = LOG_STYLE_POSITIVE; JobState nextState = JobState::Completed; if(a_exitStatus == QProcess::CrashExit) { message = tr("Process has crashed."); logStyle = LOG_STYLE_ERROR; nextState = JobState::Failed; } else if(a_exitCode != 0) logStyle = LOG_STYLE_WARNING; emit signalLogMessage(tr("%1 Exit code: %2") .arg(message).arg(a_exitCode), logStyle); changeStateAndNotify(nextState); } } // END OF void vsedit::Job::slotProcessFinished(int a_exitCode, // QProcess::ExitStatus a_exitStatus) //============================================================================== void vsedit::Job::slotProcessError(QProcess::ProcessError a_error) { if(m_properties.type == JobType::EncodeScriptCLI) { if(m_encodingState == EncodingState::CheckingEncoderSanity) return; if(m_encodingState == EncodingState::Idle) { emit signalLogMessage(tr("Encoder has reported " "an error while it shouldn't be running at all. Ignoring."), LOG_STYLE_WARNING); return; } switch(a_error) { case QProcess::FailedToStart: emit signalLogMessage(tr("Encoder has failed to start. " "Aborting."), LOG_STYLE_ERROR); m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); break; case QProcess::Crashed: emit signalLogMessage(tr("Encoder has crashed. " "Aborting."), LOG_STYLE_ERROR); m_encodingState = EncodingState::EncoderCrashed; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); break; case QProcess::Timedout: break; case QProcess::WriteError: if(m_encodingState == EncodingState::WritingFrame) { emit signalLogMessage(tr("Writing to encoder " "failed. Aborting."), LOG_STYLE_ERROR); m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); } else { emit signalLogMessage(tr("Encoder has returned a " "writing error, but we were not writing. Ignoring."), LOG_STYLE_WARNING); } break; case QProcess::ReadError: emit signalLogMessage(tr("Error on reading the " "encoder feedback."), LOG_STYLE_WARNING); break; case QProcess::UnknownError: emit signalLogMessage(tr("Unknown error in encoder."), LOG_STYLE_WARNING); break; default: Q_ASSERT(false); } } else if(m_properties.type == JobType::RunProcess) { switch(a_error) { case QProcess::FailedToStart: emit signalLogMessage(tr("Process has failed to start."), LOG_STYLE_ERROR); changeStateAndNotify(JobState::Failed); break; case QProcess::Crashed: emit signalLogMessage(tr("Process has crashed."), LOG_STYLE_ERROR); changeStateAndNotify(JobState::Failed); break; default: break; } } } // END OF void vsedit::Job::slotProcessError(QProcess::ProcessError a_error) //============================================================================== void vsedit::Job::slotProcessReadChannelFinished() { if(m_properties.type != JobType::EncodeScriptCLI) return; if(m_encodingState == EncodingState::CheckingEncoderSanity) return; if(m_encodingState == EncodingState::Idle) { emit signalLogMessage(tr("Encoder has suddenly stopped " "accepting data while it shouldn't be running at all. Ignoring."), LOG_STYLE_WARNING); return; } if((m_encodingState != EncodingState::Finishing) && (m_encodingState != EncodingState::Aborting)) { emit signalLogMessage(tr("Encoder has suddenly stopped " "accepting data. Aborting."), LOG_STYLE_ERROR); m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); } } // END OF void vsedit::Job::slotProcessReadChannelFinished() //============================================================================== void vsedit::Job::slotProcessBytesWritten(qint64 a_bytes) { if(m_properties.type != JobType::EncodeScriptCLI) return; if(m_encodingState == EncodingState::CheckingEncoderSanity) return; if(m_encodingState == EncodingState::Idle) { emit signalLogMessage(tr("Encoder has reported written " "data while it shouldn't be running at all. Ignoring."), LOG_STYLE_WARNING); return; } if((m_encodingState == EncodingState::Aborting) || (m_encodingState == EncodingState::Finishing)) return; if((m_encodingState != EncodingState::WritingFrame) && (m_encodingState != EncodingState::WritingHeader)) { emit signalLogMessage(tr("Encoder reports successful " "write, but we were not writing anything.\nData written: " "%1 bytes.").arg(a_bytes), LOG_STYLE_WARNING); return; } if(a_bytes <= 0) { emit signalLogMessage(tr("Error on writing data to " "encoder.\nExpected to write: %1 bytes. Data written: %2 bytes.\n" "Aborting.").arg(m_bytesToWrite).arg(m_bytesWritten), LOG_STYLE_ERROR); m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); return; } m_bytesWritten += a_bytes; if((m_bytesWritten + m_process.bytesToWrite()) < m_bytesToWrite) { emit signalLogMessage(tr("Encoder has lost written " "data. Aborting."), LOG_STYLE_ERROR); m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); return; } if(m_bytesWritten < m_bytesToWrite) return; Q_ASSERT(m_cpVSAPI); if(m_encodingState == EncodingState::WritingHeader) { m_memorizedEncodingTime = 0.0; m_encodeRangeStartTime = hr_clock::now(); } else if(m_encodingState == EncodingState::WritingFrame) { Frame referenceFrame(m_lastFrameProcessed + 1, 0, nullptr); std::list::iterator it = std::find(m_framesCache.begin(), m_framesCache.end(), referenceFrame); Q_ASSERT(it != m_framesCache.end()); m_cpVSAPI->freeFrame(it->cpOutputFrame); m_framesCache.erase(it); m_lastFrameProcessed++; m_properties.framesProcessed++; updateFPS(); emit signalProgressChanged(); } m_encodingState = EncodingState::WaitingForFrames; if((m_properties.jobState == JobState::Pausing) && (m_framesInProcess == 0)) { changeStateAndNotify(JobState::Paused); return; } processFramesQueue(); } // END OF void vsedit::Job::slotProcessBytesWritten(qint64 a_bytes) //============================================================================== void vsedit::Job::slotProcessReadyReadStandardError() { QByteArray standardError = m_process.readAllStandardError(); QString standardErrorText = QString::fromUtf8(standardError); standardErrorText = standardErrorText.trimmed(); if(!standardErrorText.isEmpty()) emit signalLogMessage(standardErrorText); } // END OF void vsedit::Job::slotProcessReadyReadStandardError() //============================================================================== void vsedit::Job::slotWriteLogMessage(int a_messageType, const QString & a_message) { QString style = vsMessageTypeToStyleName(a_messageType); emit signalLogMessage(a_message, style); } // END OF void vsedit::Job::slotWriteLogMessage(int a_messageType, // const QString & a_message) //============================================================================== void vsedit::Job::slotFrameQueueStateChanged(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio) { m_framesInQueue = a_inQueue; m_framesInProcess = a_inProcess; m_maxThreads = a_maxThreads; } // END OF void vsedit::Job::slotFrameQueueStateChanged(size_t a_inQueue, // size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio) //============================================================================== void vsedit::Job::slotScriptProcessorFinalized() { Q_ASSERT(m_properties.type == JobType::EncodeScriptCLI); finishEncodingCLI(); } // END OF void vsedit::Job::slotScriptProcessorFinalized() //============================================================================== void vsedit::Job::slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrameRef) { (void)a_cpPreviewFrameRef; EncodingState validStates[] = {EncodingState::WaitingForFrames, EncodingState::WritingHeader, EncodingState::WritingFrame}; if(!vsedit::contains(validStates, m_encodingState)) return; if((a_frameNumber < m_properties.firstFrameReal) || (a_frameNumber > m_properties.lastFrameReal)) return; Q_ASSERT(m_cpVSAPI); const VSFrame * cpFrameRef = m_cpVSAPI->addFrameRef(a_cpOutputFrame); Frame newFrame(a_frameNumber, a_outputIndex, cpFrameRef); m_framesCache.push_back(newFrame); if(m_encodingState == EncodingState::WaitingForFrames) processFramesQueue(); } // END OF void vsedit::Job::slotReceiveFrame(int a_frameNumber, // int a_outputIndex, const VSFrame * a_cpOutputFrame, // const VSFrame * a_cpPreviewFrameRef) //============================================================================== void vsedit::Job::slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason) { (void)a_frameNumber; (void)a_outputIndex; (void)a_reason; EncodingState validStates[] = {EncodingState::WaitingForFrames, EncodingState::WritingHeader, EncodingState::WritingFrame}; if(!vsedit::contains(validStates, m_encodingState)) return; m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); } // END OF void vsedit::Job::slotFrameRequestDiscarded(int a_frameNumber, // int a_outputIndex, const QString & a_reason) //============================================================================== void vsedit::Job::fillVariables() { JobVariables::fillVariables(); struct JobVariableEvaluator { QString token; std::function evaluate; }; JobVariableEvaluator evaluators[] = { {TOKEN_WIDTH, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_WIDTH; return QString::number(m_cpVideoInfo->width); } }, {TOKEN_HEIGHT, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_HEIGHT; return QString::number(m_cpVideoInfo->height); } }, {TOKEN_FPS_NUMERATOR, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_FPS_NUMERATOR; return QString::number(m_cpVideoInfo->fpsNum); } }, {TOKEN_FPS_DENOMINATOR, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_FPS_DENOMINATOR; return QString::number(m_cpVideoInfo->fpsDen); } }, {TOKEN_FPS, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_FPS; double fps = (double)m_cpVideoInfo->fpsNum / (double)m_cpVideoInfo->fpsDen; return QString::number(fps, 'f', 10); } }, {TOKEN_BITDEPTH, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_BITDEPTH; return QString::number(m_cpVideoInfo->format.bitsPerSample); } }, {TOKEN_SCRIPT_DIRECTORY, [&]() -> QString { QFileInfo scriptFile(m_properties.scriptName); return scriptFile.canonicalPath(); } }, {TOKEN_SCRIPT_NAME, [&]() -> QString { QFileInfo scriptFile(m_properties.scriptName); return scriptFile.completeBaseName(); } }, {TOKEN_FRAMES_NUMBER, [&]() -> QString { return QString::number(framesTotal()); } }, {TOKEN_SUBSAMPLING, [&]() -> QString { if(!m_cpVideoInfo) return TOKEN_SUBSAMPLING; const VSVideoFormat * cpFormat = &m_cpVideoInfo->format; return vsedit::subsamplingString(cpFormat->subSamplingW, cpFormat->subSamplingH); } }, }; for(JobVariableEvaluator & evaluator : evaluators) { std::vector::iterator it = std::find_if(m_variables.begin(), m_variables.end(), [&](const vsedit::VariableToken & a_variable) -> bool { return (a_variable.token == evaluator.token); }); it->evaluate = evaluator.evaluate; } } // END OF void vsedit::Job::fillVariables() //============================================================================== void vsedit::Job::changeStateAndNotify(JobState a_state) { if(m_properties.jobState == a_state && a_state != JobState::Aborted) return; JobState oldState = m_properties.jobState; m_properties.jobState = a_state; if(oldState == JobState::Waiting) m_properties.timeStarted = QDateTime::currentDateTimeUtc(); const JobState finishStates[] = {JobState::Aborted, JobState::Failed, JobState::DependencyNotMet, JobState::Completed}; if(vsedit::contains(finishStates, a_state)) { m_properties.timeEnded = QDateTime::currentDateTimeUtc(); memorizeEncodingTime(); emit signalEndTimeChanged(); } if(a_state == JobState::Paused) memorizeEncodingTime(); if((oldState == JobState::Paused) && (a_state == JobState::Running)) m_encodeRangeStartTime = hr_clock::now(); if(a_state == JobState::Waiting) { m_properties.timeStarted = QDateTime(); m_properties.timeEnded = QDateTime(); m_memorizedEncodingTime = 0.0; m_properties.fps = 0.0; m_properties.framesProcessed = 0; } emit signalStateChanged(m_properties.jobState, oldState); } // END OF void vsedit::Job::changeStateAndNotify(JobState a_state) //============================================================================== void vsedit::Job::startEncodeScriptCLI() { if(!initialize()) return; emit signalPropertiesChanged(); if(m_pFrameHeaderWriter) delete m_pFrameHeaderWriter; if(m_properties.encodingHeaderType == EncodingHeaderType::Y4M) m_pFrameHeaderWriter = new FrameHeaderWriterY4M(m_cpVSAPI, m_cpVideoInfo, this); else m_pFrameHeaderWriter = new FrameHeaderWriterNull(m_cpVSAPI, m_cpVideoInfo, this); bool compatibleHeader = m_pFrameHeaderWriter->isCompatible(); if(!compatibleHeader) { emit signalLogMessage(tr("Video is not compatible " "with the chosen header."), LOG_STYLE_ERROR); changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); return; } QString executable = vsedit::resolvePathFromApplication( m_properties.executablePath); QString decodedArguments = decodeArguments(m_properties.arguments); QString commandLine = QString("\"%1\" %2").arg(executable) .arg(decodedArguments); emit signalLogMessage(tr("Command line:")); emit signalLogMessage(commandLine); emit signalLogMessage(tr("Checking the encoder sanity.")); m_encodingState = EncodingState::CheckingEncoderSanity; m_process.startCommand(commandLine); if(!m_process.waitForStarted(3000)) { emit signalLogMessage(tr("Encoder wouldn't start."), LOG_STYLE_ERROR); changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); return; } m_process.closeWriteChannel(); if(!m_process.waitForFinished(3000)) { emit signalLogMessage(tr("Program is not behaving " "like a CLI encoder. Terminating."), LOG_STYLE_ERROR); m_process.kill(); m_process.waitForFinished(-1); changeStateAndNotify(JobState::FailedCleanUp); cleanUpEncoding(); return; } emit signalLogMessage(tr("Encoder seems sane. Starting.")); m_encodingState = EncodingState::StartingEncoder; m_process.startCommand(commandLine); } // END OF void vsedit::Job::startEncodeScriptCLI() //============================================================================== void vsedit::Job::startRunProcess() { changeStateAndNotify(JobState::Running); QString executable = vsedit::resolvePathFromApplication( m_properties.executablePath); QString commandLine = QString("\"%1\" %2").arg(executable) .arg(m_properties.arguments); emit signalLogMessage(tr("Command line:")); emit signalLogMessage(commandLine); m_process.startCommand(commandLine); } // END OF void vsedit::Job::startRunProcess() //============================================================================== void vsedit::Job::startRunShellCommand() { changeStateAndNotify(JobState::Running); QString command = "%1"; m_process.startDetached(m_properties.shellCommand, QStringList()); changeStateAndNotify(JobState::Completed); } // END OF void vsedit::Job::startRunShellCommand() //============================================================================== QString vsedit::Job::decodeArguments(const QString & a_arguments) const { QString decodedString = a_arguments.simplified(); for(const vsedit::VariableToken & variable : m_variables) { decodedString = decodedString.replace(variable.token, variable.evaluate()); } return decodedString; } // END OF QString vsedit::Job::decodeArguments( // const QString & a_arguments) const //============================================================================== void vsedit::Job::clearFramesCache() { if(m_framesCache.empty()) return; Q_ASSERT(m_cpVSAPI); for(Frame & frame : m_framesCache) { m_cpVSAPI->freeFrame(frame.cpOutputFrame); m_cpVSAPI->freeFrame(frame.cpPreviewFrame); } m_framesCache.clear(); } // END OF void vsedit::Job::clearFramesCache() //============================================================================== void vsedit::Job::processFramesQueue() { if(m_encodingState != EncodingState::WaitingForFrames) return; if(m_properties.framesProcessed == framesTotal()) { Q_ASSERT(m_framesCache.empty()); memorizeEncodingTime(); updateFPS(); changeStateAndNotify(JobState::CompletedCleanUp); m_encodingState = EncodingState::Finishing; cleanUpEncoding(); return; } while((m_lastFrameRequested < m_properties.lastFrameReal) && (m_framesInProcess < m_maxThreads) && (m_framesCache.size() < m_cachedFramesLimit) && (m_properties.jobState == JobState::Running)) { m_pVapourSynthScriptProcessor->requestFrameAsync( m_lastFrameRequested + 1); m_lastFrameRequested++; } Frame frame(m_lastFrameProcessed + 1, 0, nullptr); std::list::iterator it = std::find(m_framesCache.begin(), m_framesCache.end(), frame); if(it == m_framesCache.end()) return; frame.cpOutputFrame = it->cpOutputFrame; // VapourSynth frames are padded so every line has aligned address. // But encoder expects frames tightly packed. We pack frame lines // into an intermediate buffer, because writing whole frame at once // is faster than feeding it to encoder line by line. size_t currentDataSize = 0; Q_ASSERT(m_cpVideoInfo); const VSVideoFormat * cpFormat = &m_cpVideoInfo->format; if(m_pFrameHeaderWriter->needFramePrefix()) { QByteArray framePrefix = m_pFrameHeaderWriter->framePrefix(frame.cpOutputFrame); int prefixSize = framePrefix.size(); if(prefixSize > 0) { if((size_t)prefixSize > m_framebuffer.size()) m_framebuffer.resize(prefixSize); memcpy(m_framebuffer.data(), framePrefix.data(), prefixSize); currentDataSize += prefixSize; } } for(int i = 0; i < cpFormat->numPlanes; ++i) { const uint8_t * cpPlane = m_cpVSAPI->getReadPtr(frame.cpOutputFrame, i); int stride = m_cpVSAPI->getStride(frame.cpOutputFrame, i); int width = m_cpVSAPI->getFrameWidth(frame.cpOutputFrame, i); int height = m_cpVSAPI->getFrameHeight(frame.cpOutputFrame, i); int bytes = cpFormat->bytesPerSample; size_t planeSize = width * bytes * height; size_t neededFramebufferSize = currentDataSize + planeSize; if(neededFramebufferSize > m_framebuffer.size()) m_framebuffer.resize(neededFramebufferSize); int framebufferStride = width * bytes; vsh::bitblt(m_framebuffer.data() + currentDataSize, framebufferStride, cpPlane, stride, framebufferStride, height); currentDataSize += planeSize; } if(m_pFrameHeaderWriter->needFramePostfix()) { QByteArray framePostfix = m_pFrameHeaderWriter->framePostfix(frame.cpOutputFrame); int postfixSize = framePostfix.size(); if(postfixSize > 0) { size_t neededFramebufferSize = currentDataSize + postfixSize; if(neededFramebufferSize > m_framebuffer.size()) m_framebuffer.resize(neededFramebufferSize); memcpy(m_framebuffer.data() + currentDataSize, framePostfix.data(), postfixSize); currentDataSize += postfixSize; } } m_encodingState = EncodingState::WritingFrame; m_bytesToWrite = currentDataSize; m_bytesWritten = 0; qint64 bytesWritten = m_process.write(m_framebuffer.data(), (qint64)m_bytesToWrite); if(bytesWritten < 0) { m_encodingState = EncodingState::Aborting; changeStateAndNotify(JobState::FailedCleanUp); emit signalLogMessage(tr("Error on writing data to encoder. " "Aborting."), LOG_STYLE_ERROR); cleanUpEncoding(); return; } // Wait until encoder reads the frame. // Then this function will be called again. } // END OF void vsedit::Job::processFramesQueue() //============================================================================== void vsedit::Job::finishEncodingCLI() { if((m_process.state() == QProcess::Running) || m_pVapourSynthScriptProcessor->isInitialized()) return; if(m_encodingState == EncodingState::Finishing) { emit signalLogMessage(tr("Finished encoding."), LOG_STYLE_POSITIVE); changeStateAndNotify(JobState::CompletedCleanUp); } else if(m_encodingState == EncodingState::Aborting) { emit signalLogMessage(tr("Aborted encoding."), LOG_STYLE_WARNING); } m_encodingState = EncodingState::Idle; const std::map stateToSwitch = { {JobState::Aborting, JobState::Aborted}, {JobState::FailedCleanUp, JobState::Failed}, {JobState::CompletedCleanUp, JobState::Completed}, }; std::map::const_iterator it = stateToSwitch.find(m_properties.jobState); if(it != stateToSwitch.cend()) changeStateAndNotify(it->second); } // END OF void vsedit::Job::finishEncodingCLI() //============================================================================== void vsedit::Job::memorizeEncodingTime() { if(m_properties.type != JobType::EncodeScriptCLI) return; m_memorizedEncodingTime += currentEncodingRangeTime(); m_encodeRangeStartTime = hr_clock::now(); } // END OF void vsedit::Job::memorizeEncodingTime() //============================================================================== void vsedit::Job::updateFPS() { if(m_properties.type != JobType::EncodeScriptCLI) return; double totalTime = m_memorizedEncodingTime; const JobState validStates[] = {JobState::Running, JobState::Pausing}; if(vsedit::contains(validStates, m_properties.jobState)) totalTime += currentEncodingRangeTime(); m_properties.fps = (double)m_properties.framesProcessed / totalTime; } // END OF void vsedit::Job::updateFPS() //============================================================================== double vsedit::Job::currentEncodingRangeTime() const { if(m_properties.type != JobType::EncodeScriptCLI) return 0.0; hr_time_point now = hr_clock::now(); double rangeTime = duration_to_double(now - m_encodeRangeStartTime); return rangeTime; } // END OF double vsedit::Job::currentEncodingRangeTime() const //============================================================================== ================================================ FILE: common-src/jobs/job.h ================================================ #ifndef JOB_H_INCLUDED #define JOB_H_INCLUDED #include "../../../common-src/settings/settings_definitions_core.h" #include "../../../common-src/chrono.h" #include "../../../common-src/helpers.h" #include "../../../common-src/log/styled_log_view_core.h" #include "../../../common-src/log/vs_editor_log_definitions.h" #include "../../../common-src/vapoursynth/vs_script_processor_structures.h" #include "../../../common-src/jobs/job_variables.h" #include #include #include #include #include class SettingsManagerCore; class VSScriptLibrary; class VapourSynthScriptProcessor; class FrameHeaderWriter; namespace vsedit { class Job : public QObject, public JobVariables { Q_OBJECT public: Job(const JobProperties & a_properties = JobProperties(), SettingsManagerCore * a_pSettingsManager = nullptr, VSScriptLibrary * a_pVSScriptLibrary = nullptr, QObject * a_pParent = nullptr); virtual ~Job(); enum class EncodingState { Idle, CheckingEncoderSanity, StartingEncoder, WritingHeader, WaitingForFrames, WritingFrame, EncoderCrashed, Finishing, Aborting, }; virtual bool isActive() const; virtual QUuid id() const; virtual bool setId(const QUuid & a_id); virtual JobType type() const; virtual bool setType(JobType a_type); virtual QString scriptName() const; virtual bool setScriptName(const QString & a_scriptName); virtual QString scriptText() const; virtual bool setScriptText(const QString & a_scriptText); virtual EncodingHeaderType encodingHeaderType() const; virtual bool setEncodingHeaderType(EncodingHeaderType a_headerType); virtual QString executablePath() const; virtual bool setExecutablePath(const QString & a_path); virtual QString arguments() const; virtual bool setArguments(const QString & a_arguments); virtual QString shellCommand() const; virtual bool setShellCommand(const QString & a_command); virtual JobState state() const; virtual bool setState(JobState a_state); virtual std::vector dependsOnJobIds() const; virtual bool setDependsOnJobIds(const std::vector & a_ids); virtual QString subject() const; virtual int firstFrame() const; virtual bool setFirstFrame(int a_frame); virtual int lastFrame() const; virtual bool setLastFrame(int a_frame); virtual int framesProcessed() const; virtual int framesTotal() const; virtual double fps() const; virtual double secondsToFinish() const; virtual size_t framesInQueue() const; virtual size_t framesInProcess() const; virtual size_t maxThreads() const; virtual JobProperties properties() const; virtual bool setProperties(const JobProperties & a_properties); virtual const VSVideoInfo * videoInfo() const; virtual bool initialize(); virtual void cleanUpEncoding(); public slots: virtual void start(); virtual void pause(); virtual void abort(); signals: void signalPropertiesChanged(); void signalStateChanged(JobState a_newState, JobState a_oldState); void signalProgressChanged(); void signalStartTimeChanged(); void signalEndTimeChanged(); void signalLogMessage(const QString & a_message, const QString & a_style = LOG_STYLE_DEFAULT); protected slots: virtual void slotProcessStarted(); virtual void slotProcessFinished(int a_exitCode, QProcess::ExitStatus a_exitStatus); virtual void slotProcessError(QProcess::ProcessError a_error); virtual void slotProcessReadChannelFinished(); virtual void slotProcessBytesWritten(qint64 a_bytes); virtual void slotProcessReadyReadStandardError(); virtual void slotWriteLogMessage(int a_messageType, const QString & a_message); virtual void slotFrameQueueStateChanged(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio); virtual void slotScriptProcessorFinalized(); virtual void slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame); virtual void slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason); protected: virtual void fillVariables() override; virtual void changeStateAndNotify(JobState a_state); virtual void startEncodeScriptCLI(); virtual void startRunProcess(); virtual void startRunShellCommand(); virtual QString decodeArguments(const QString & a_arguments) const; virtual void clearFramesCache(); virtual void processFramesQueue(); virtual void finishEncodingCLI(); virtual void memorizeEncodingTime(); virtual void updateFPS(); virtual double currentEncodingRangeTime() const; JobProperties m_properties; QProcess m_process; std::vector m_framebuffer; int m_lastFrameProcessed; int m_lastFrameRequested; EncodingState m_encodingState; size_t m_bytesToWrite; size_t m_bytesWritten; SettingsManagerCore * m_pSettingsManager; VSScriptLibrary * m_pVSScriptLibrary; VapourSynthScriptProcessor * m_pVapourSynthScriptProcessor; const VSAPI * m_cpVSAPI; const VSVideoInfo * m_cpVideoInfo; FrameHeaderWriter * m_pFrameHeaderWriter; std::list m_framesCache; size_t m_cachedFramesLimit; size_t m_framesInQueue; size_t m_framesInProcess; size_t m_maxThreads; hr_time_point m_encodeRangeStartTime; double m_memorizedEncodingTime; }; } #endif // JOB_H_INCLUDED ================================================ FILE: common-src/jobs/job_variables.cpp ================================================ #include "job_variables.h" #include //============================================================================== const QString JobVariables::TOKEN_WIDTH = "{w}"; const QString JobVariables::TOKEN_HEIGHT = "{h}"; const QString JobVariables::TOKEN_FPS_NUMERATOR = "{fpsn}"; const QString JobVariables::TOKEN_FPS_DENOMINATOR = "{fpsd}"; const QString JobVariables::TOKEN_FPS = "{fps}"; const QString JobVariables::TOKEN_BITDEPTH = "{bits}"; const QString JobVariables::TOKEN_SCRIPT_DIRECTORY = "{sd}"; const QString JobVariables::TOKEN_SCRIPT_NAME = "{sn}"; const QString JobVariables::TOKEN_FRAMES_NUMBER = "{f}"; const QString JobVariables::TOKEN_SUBSAMPLING = "{ss}"; //============================================================================== JobVariables::JobVariables() { fillVariables(); } // END OF JobVariables::JobVariables() //============================================================================== std::vector JobVariables::variables() const { std::vector cutVariables; for(const vsedit::VariableToken & variable : m_variables) { vsedit::VariableToken cutVariable = {variable.token, variable.description, std::function()}; cutVariables.push_back(cutVariable); } return cutVariables; } // END OF std::vector JobVariables::variables() const //============================================================================== void JobVariables::fillVariables() { m_variables = { {TOKEN_WIDTH, QObject::tr("video width"), std::function()}, {TOKEN_HEIGHT, QObject::tr("video height"), std::function()}, {TOKEN_FPS_NUMERATOR, QObject::tr("video framerate numerator"), std::function()}, {TOKEN_FPS_DENOMINATOR, QObject::tr("video framerate denominator"), std::function()}, {TOKEN_FPS, QObject::tr("video framerate as fraction"), std::function()}, {TOKEN_BITDEPTH, QObject::tr("video colour bitdepth"), std::function()}, {TOKEN_SCRIPT_DIRECTORY, QObject::tr("script directory"), std::function()}, {TOKEN_SCRIPT_NAME, QObject::tr("script name without extension"), std::function()}, {TOKEN_FRAMES_NUMBER, QObject::tr("total frames number"), std::function()}, {TOKEN_SUBSAMPLING, QObject::tr("subsampling string (like 420)"), std::function()}, }; std::sort(m_variables.begin(), m_variables.end(), [&](const vsedit::VariableToken & a_first, const vsedit::VariableToken & a_second) -> bool { return (a_first.token.length() > a_second.token.length()); }); } // END OF void JobVariables::fillVariables() //============================================================================== ================================================ FILE: common-src/jobs/job_variables.h ================================================ #ifndef JOB_VARIABLES_H_INCLUDED #define JOB_VARIABLES_H_INCLUDED #include "../helpers.h" #include #include class JobVariables { public: JobVariables(); virtual std::vector variables() const; protected: static const QString TOKEN_WIDTH; static const QString TOKEN_HEIGHT; static const QString TOKEN_FPS_NUMERATOR; static const QString TOKEN_FPS_DENOMINATOR; static const QString TOKEN_FPS; static const QString TOKEN_BITDEPTH; static const QString TOKEN_SCRIPT_DIRECTORY; static const QString TOKEN_SCRIPT_NAME; static const QString TOKEN_FRAMES_NUMBER; static const QString TOKEN_SUBSAMPLING; virtual void fillVariables(); std::vector m_variables; }; #endif // JOB_VARIABLES_H_INCLUDED ================================================ FILE: common-src/libp2p/COPYING ================================================ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. ================================================ FILE: common-src/libp2p/README.md ================================================ # libp2p Pack/unpack pixels. The "p2p" library implements conversion between packed and planar image formats. A packed format is any memory layout that stores more than one image component ("plane") in a single array. For example, the common ARGB format stores pixels as an array of DWORDs holding all components for each pixel. In contrast, a planar format stores each image component in its own array. Building ------ libp2p is intended for embedding within other libraries and applications. The header "p2p.h" contains a template library for generating packing and unpacking routines, which can be used to instantiate functions for various pixel formats. "v210.cpp" holds a special-case implementation for the Apple ProRes "v210" format. "p2p_api.h" and "p2p_api.cpp" implement a "C" wrapper for a fixed set of commonly encountered packed formats. If the "C" wrapper is used from another library, a method to control symbol visibility should be used to prevent name conflicts with other, potentially incompatible, instances of libp2p. ================================================ FILE: common-src/libp2p/p2p.h ================================================ #ifndef P2P_H_ #define P2P_H_ #include #include #include #include #ifdef P2P_SIMD #include #endif #ifdef _WIN32 #include // _byteswap_x #endif #ifdef P2P_USER_NAMESPACE #define P2P_NAMESPACE P2P_USER_NAMESPACE #else #define P2P_NAMESPACE p2p #endif #ifdef _WIN32 #define P2P_LITTLE_ENDIAN #elif defined(__BYTE_ORDER__) #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define P2P_BIG_ENDIAN #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define P2P_LITTLE_ENDIAN #endif #endif static_assert(CHAR_BIT == 8, "8-bit char required"); namespace P2P_NAMESPACE { // Tag types for endian. struct little_endian_t {}; struct big_endian_t {}; #if defined(P2P_BIG_ENDIAN) typedef big_endian_t native_endian_t; #elif defined(P2P_LITTLE_ENDIAN) typedef little_endian_t native_endian_t; #else #error wrong endian #endif #undef P2P_BIG_ENDIAN #undef P2P_LITTLE_ENDIAN namespace detail { // Size of object in bits. template struct bit_size { static const size_t value = sizeof(T) * CHAR_BIT; }; // Make integers from bytes. constexpr uint16_t make_u16(uint8_t a, uint8_t b) { return (a << 8) | b; } constexpr uint32_t make_u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { return (static_cast(make_u16(a, b)) << 16) | make_u16(c, d); } constexpr uint64_t make_u64(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h) { return (static_cast(make_u32(a, b, c, d)) << 32) | make_u32(e, f, g, h); } template constexpr uint8_t get_u8(T x, unsigned i) { return static_cast((x >> (bit_size::value - 8 * i - 8)) & 0xFF); } // Fake 24 and 48-bit integers. struct uint24 { uint8_t x[3]; uint24() = default; constexpr uint24(uint8_t a, uint8_t b, uint8_t c) : x{ a, b, c } { } template explicit constexpr uint24(uint32_t val, typename std::enable_if::value>::type * = 0) : x{ get_u8(val, 1), get_u8(val, 2), get_u8(val, 3) } { } template explicit constexpr uint24(uint32_t val, typename std::enable_if::value>::type * = 0) : x{ get_u8(val, 3), get_u8(val, 2), get_u8(val, 1) } { } template ::value>::type * = nullptr> constexpr uint32_t to_u32() const { return make_u32(0, x[0], x[1], x[2]); } template ::value>::type * = nullptr> constexpr uint32_t to_u32() const { return make_u32(0, x[2], x[1], x[0]); } constexpr operator uint32_t() const { return to_u32(); } }; struct uint48 { uint8_t x[6]; uint48() = default; constexpr uint48(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f) : x{ a, b, c, d, e, f } { } template explicit constexpr uint48(uint64_t val, typename std::enable_if::value>::type * = 0) : x{ get_u8(val, 2), get_u8(val, 3), get_u8(val, 4), get_u8(val, 5), get_u8(val, 6), get_u8(val, 7) } { } template explicit constexpr uint48(uint64_t val, typename std::enable_if::value>::type * = 0) : x{ get_u8(val, 7), get_u8(val, 6), get_u8(val, 5), get_u8(val, 4), get_u8(val, 3), get_u8(val, 2) } { } template ::value>::type * = nullptr> constexpr uint64_t to_u64() const { return make_u64(0, 0, x[0], x[1], x[2], x[3], x[4], x[5]); } template ::value>::type * = nullptr> constexpr uint64_t to_u64() const { return make_u64(0, 0, x[5], x[4], x[3], x[2], x[1], x[0]); } constexpr operator uint64_t() const { return to_u64(); } }; static_assert(std::is_pod::value, "uint24 must be POD"); static_assert(std::is_pod::value, "uint48 must be POD"); static_assert(sizeof(uint24) == 3, "uint24 must not have padding"); static_assert(sizeof(uint48) == 6, "uint48 must not have padding"); // Endian conversions. template ::value>::type * = nullptr> T endian_swap(T x) { return x; } template ::value>::type * = nullptr> uint16_t endian_swap(uint16_t x) { #ifdef _WIN32 return _byteswap_ushort(x); #else return __builtin_bswap16(x); #endif } template ::value>::type * = nullptr> uint32_t endian_swap(uint32_t x) { #ifdef _WIN32 return _byteswap_ulong(x); #else return __builtin_bswap32(x); #endif } template ::value>::type * = nullptr> uint64_t endian_swap(uint64_t x) { #ifdef _WIN32 return _byteswap_uint64(x); #else return __builtin_bswap64(x); #endif } template ::value>::type * = nullptr> uint24 endian_swap(uint24 x) { return{ x.x[2], x.x[1], x.x[0] }; } template ::value>::type * = nullptr> uint48 endian_swap(uint48 x) { return{ x.x[5], x.x[4], x.x[3], x.x[2], x.x[1], x.x[0] }; } // Treat u32 as array of u8. struct mask4 { uint32_t x; constexpr uint8_t operator[](unsigned i) const { return get_u8(x, i); } constexpr bool contains(unsigned val) const { return (*this)[0] == val || (*this)[1] == val || (*this)[2] == val || (*this)[3] == val; } constexpr unsigned find(uint8_t val) const { return (*this)[0] == val ? 0 : (*this)[1] == val ? 1 : (*this)[2] == val ? 2 : (*this)[3] == val ? 3 : ~0U; } }; // Native integer type for arithmetic. template struct numeric_type { typedef T type; }; template <> struct numeric_type { typedef uint32_t type; }; template <> struct numeric_type { typedef uint64_t type; }; } // namespace detail enum { C_Y = 0, C_U = 1, C_V = 2, C_R = 0, C_G = 1, C_B = 2, C_A = 3, C__ = 0xFF, }; // Packed integer constants for template parameters. constexpr uint32_t make_mask(uint8_t x) { return detail::make_u32(x, x, x, x); } constexpr uint32_t make_mask(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { return detail::make_u32(a, b, c, d); } // Select type by endian. template struct endian_select { typedef typename std::conditional::value, Big, Little>::type type; }; template struct pack_traits { static_assert(std::is_pod::value, "must be POD"); static_assert(std::is_pod::value, "must be POD"); typedef Planar planar_type; typedef Packed packed_type; typedef Endian endian; static const unsigned pel_per_pack = PelPerPack; static const unsigned subsampling = Subsampling; static constexpr detail::mask4 component_mask{ ComponentMask }; static constexpr detail::mask4 shift_mask{ ShiftMask }; static constexpr detail::mask4 depth_mask{ DepthMask }; }; template constexpr detail::mask4 pack_traits::component_mask; template constexpr detail::mask4 pack_traits::shift_mask; template constexpr detail::mask4 pack_traits::depth_mask; // Base template for 4:4:4 packings. // // Much literature treats 4:4:4 triplets as single machine words, implying a // reversed component order on LE and BE. // // The _be and _le templates accept a component mask beginning from the MSB of // the packed word to accomodate this. template using byte_packed_444_be = pack_traits< Planar, Packed, big_endian_t, 1, 0, ComponentMask, make_mask(3, 2, 1, 0) * detail::bit_size::value, make_mask(detail::bit_size::value)>; template using byte_packed_444_le = pack_traits< Planar, Packed, little_endian_t, 1, 0, ComponentMask, make_mask(3, 2, 1, 0) * detail::bit_size::value, make_mask(detail::bit_size::value)>; // Common 444 packings. using packed_rgb24_be = byte_packed_444_be; using packed_rgb24_le = byte_packed_444_le; using packed_rgb24 = endian_select::type; using packed_argb32_be = byte_packed_444_be; using packed_argb32_le = byte_packed_444_le; using packed_argb32 = endian_select::type; using packed_ayuv_be = packed_argb32_be; using packed_ayuv_le = packed_argb32_le; using packed_ayuv = packed_argb32; using packed_rgb48_be = byte_packed_444_be; using packed_rgb48_le = byte_packed_444_le; using packed_rgb48 = endian_select::type; using packed_argb64_be = byte_packed_444_be; using packed_argb64_le = byte_packed_444_le; using packed_argb64 = endian_select::type; using packed_rgba32_be = byte_packed_444_be; using packed_rgba32_le = byte_packed_444_le; using packed_rgba32 = endian_select::type; using packed_rgba64_be = byte_packed_444_be; using packed_rgba64_le = byte_packed_444_le; using packed_rgba64 = endian_select::type; using packed_abgr64_be = byte_packed_444_be; using packed_abgr64_le = byte_packed_444_le; using packed_abgr64 = endian_select::type; using packed_bgr48_be = byte_packed_444_be; using packed_bgr48_le = byte_packed_444_le; using packed_bgr48 = endian_select::type; using packed_bgra64_be = byte_packed_444_be; using packed_bgra64_le = byte_packed_444_le; using packed_bgra64 = endian_select::type; // D3D A2R10G10B10. using packed_rgb30_be = pack_traits< uint16_t, uint32_t, big_endian_t, 1, 0, make_mask(C_A, C_R, C_G, C_B), make_mask(30, 20, 10, 0), make_mask(2, 10, 10, 10)>; using packed_rgb30_le = pack_traits< uint16_t, uint32_t, little_endian_t, 1, 0, make_mask(C_A, C_R, C_G, C_B), make_mask(30, 20, 10, 0), make_mask(2, 10, 10, 10)>; using packed_rgb30 = endian_select::type; // MS Y410 and Y416 formats. using packed_y410_be = pack_traits< uint16_t, uint32_t, big_endian_t, 1, 0, make_mask(C_A, C_V, C_Y, C_U), make_mask(30, 20, 10, 0), make_mask(2, 10, 10, 10)>; using packed_y410_le = pack_traits< uint16_t, uint32_t, little_endian_t, 1, 0, make_mask(C_A, C_V, C_Y, C_U), make_mask(30, 20, 10, 0), make_mask(2, 10, 10, 10)>; using packed_y410 = endian_select::type; using packed_y416_be = byte_packed_444_be; using packed_y416_le = byte_packed_444_le; using packed_y416 = endian_select::type; // Base template for YUY2-like 4:2:2 packings. // // The component order in both BE and LE is the same. Only the bytes of the // individual component words are reversed. // // The _be and _le templates accept a component mask beginning from the low // memory address of the packed word to accomodate this. template using byte_packed_422_be = pack_traits< Planar, Packed, big_endian_t, 2, 1, ComponentMask, make_mask(3, 2, 1, 0) * detail::bit_size::value + make_mask(ExtraShift), make_mask(detail::bit_size::value) - make_mask(ExtraShift)>; template using byte_packed_422_le = pack_traits< Planar, Packed, little_endian_t, 2, 1, ComponentMask, make_mask(0, 1, 2, 3) * detail::bit_size::value + make_mask(ExtraShift), make_mask(detail::bit_size::value) - make_mask(ExtraShift)>; // YUY2. using packed_yuy2 = byte_packed_422_be; using packed_uyvy = byte_packed_422_be; // MS Y210 and Y216 formats. using packed_y210_be = byte_packed_422_be; using packed_y210_le = byte_packed_422_le; using packed_y210 = endian_select::type; using packed_y216_be = byte_packed_422_be; using packed_y216_le = byte_packed_422_le; using packed_y216 = endian_select::type; // Apple v210 format. Handled by special-case code. Only the LE ordering is found in Qt files. struct packed_v210_be {}; struct packed_v210_le {}; using packed_v210 = endian_select::type; // Apple v216 format. Only the LE ordering is found in Qt files. using packed_v216_be = byte_packed_422_be; using packed_v216_le = byte_packed_422_le; using packed_v216 = endian_select::type; // Base template for chroma-interleaved half packings. // // The literature treats UV pairs as single machine words, implying a reversed // component order between BE and LE. template using byte_packed_nv_be = pack_traits< Planar, Packed, big_endian_t, 2, 1, make_mask(C__, C__, C_V, C_U), make_mask(0, 0, 1, 0) * detail::bit_size::value + make_mask(ExtraShift), make_mask(detail::bit_size::value) - make_mask(ExtraShift)>; template using byte_packed_nv_le = pack_traits< Planar, Packed, little_endian_t, 2, 1, make_mask(C__, C__, C_V, C_U), make_mask(0, 0, 1, 0) * detail::bit_size::value + make_mask(ExtraShift), make_mask(detail::bit_size::value) - make_mask(ExtraShift)>; using packed_nv12_be = byte_packed_nv_be; // AKA NV21. using packed_nv12_le = byte_packed_nv_le; using packed_nv12 = endian_select::type; // MS P010, P016, P210, and P216 formats. using packed_p010_be = byte_packed_nv_be; using packed_p010_le = byte_packed_nv_le; using packed_p010 = endian_select::type; using packed_p016_be = byte_packed_nv_be; using packed_p016_le = byte_packed_nv_le; using packed_p016 = endian_select::type; using packed_p210_be = packed_p010_be; using packed_p210_le = packed_p010_le; using packed_p210 = packed_p010; using packed_p216_be = packed_p016_be; using packed_p216_le = packed_p016_le; using packed_p216 = packed_p016; #ifdef P2P_SIMD namespace detail { // Runtime function dispatch. typedef void (*unpack_func)(const void *, void * const *, unsigned, unsigned); typedef void (*pack_func)(const void * const *, void *, unsigned, unsigned); unpack_func search_unpack_func(const std::type_info &ti); pack_func search_pack_func(const std::type_info &ti, bool alpha_one_fill); template unpack_func search_unpack_func(unpack_func default_func) { unpack_func func = search_unpack_func(typeid(Traits)); return func ? func : default_func; } template pack_func search_pack_func(pack_func default_func) { pack_func func = search_pack_func(typeid(Traits), AlphaOneFill); return func ? func : default_func; } } // namespace detail #endif // P2P_SIMD // Conversions. template class packed_to_planar { typedef typename Traits::planar_type planar_type; typedef typename Traits::packed_type packed_type; typedef typename detail::numeric_type::type numeric_type; typedef typename Traits::endian endian; #ifdef P2P_SIMD static detail::unpack_func s_delegate; #endif static numeric_type get_mask(unsigned c) { return ~static_cast(0) >> (detail::bit_size::value - Traits::depth_mask[c]); } static planar_type get_component(numeric_type x, unsigned c) { return static_cast((x >> Traits::shift_mask[c]) & get_mask(c)); } static void unpack_impl(const void *src, void * const dst[4], unsigned left, unsigned right) { const packed_type *src_p = static_cast(src); planar_type *dst_p[4] = { static_cast(dst[0]), static_cast(dst[1]), static_cast(dst[2]), static_cast(dst[3]), }; bool alpha_enabled = dst[C_A] != nullptr; // Adjust pointers. src_p += left / Traits::pel_per_pack; dst_p[0] += Traits::component_mask.contains(0) ? left : 0; dst_p[1] += Traits::component_mask.contains(1) ? (left >> Traits::subsampling) : 0; dst_p[2] += Traits::component_mask.contains(2) ? (left >> Traits::subsampling) : 0; dst_p[3] += Traits::component_mask.contains(3) ? left : 0; #define P2P_COMPONENT_ENABLED(c) ((Traits::component_mask[c] != C__) && (Traits::component_mask[c] != C_A || alpha_enabled)) for (unsigned i = left; i < right; i += Traits::pel_per_pack) { numeric_type x = detail::endian_swap(*src_p++); if (P2P_COMPONENT_ENABLED(0)) *dst_p[Traits::component_mask[0]]++ = get_component(x, 0); if (P2P_COMPONENT_ENABLED(1)) *dst_p[Traits::component_mask[1]]++ = get_component(x, 1); if (P2P_COMPONENT_ENABLED(2)) *dst_p[Traits::component_mask[2]]++ = get_component(x, 2); if (P2P_COMPONENT_ENABLED(3)) *dst_p[Traits::component_mask[3]]++ = get_component(x, 3); } #undef P2P_COMPONENT_ENABLED } public: static void unpack(const void *src, void * const dst[4], unsigned left, unsigned right) { #ifdef P2P_SIMD s_delegate(src, dst, left, right); #else unpack_impl(src, dst, left, right); #endif } }; #ifdef P2P_SIMD template detail::unpack_func packed_to_planar::s_delegate = detail::search_unpack_func(packed_to_planar::unpack_impl); #endif template class planar_to_packed { typedef typename Traits::planar_type planar_type; typedef typename Traits::packed_type packed_type; typedef typename detail::numeric_type::type numeric_type; typedef typename Traits::endian endian; #ifdef P2P_SIMD static detail::pack_func s_delegate; #endif static numeric_type get_mask(unsigned c) { return ~static_cast(0) >> (detail::bit_size::value - Traits::depth_mask[c]); } static numeric_type get_component(planar_type x, unsigned c) { return (static_cast(x) & get_mask(c)) << Traits::shift_mask[c]; } static void pack_impl(const void * const src[4], void *dst, unsigned left, unsigned right) { const planar_type *src_p[4] = { static_cast(src[0]), static_cast(src[1]), static_cast(src[2]), static_cast(src[3]), }; packed_type *dst_p = static_cast(dst); bool alpha_enabled = src[C_A] != nullptr; // Adjust pointers. src_p[0] += Traits::component_mask.contains(0) ? left : 0; src_p[1] += Traits::component_mask.contains(1) ? (left >> Traits::subsampling) : 0; src_p[2] += Traits::component_mask.contains(2) ? (left >> Traits::subsampling) : 0; src_p[3] += Traits::component_mask.contains(3) ? left : 0; dst_p += left / Traits::pel_per_pack; #define P2P_COMPONENT_ENABLED(c) ((Traits::component_mask[c] != C__) && (Traits::component_mask[c] != C_A || alpha_enabled)) for (unsigned i = left; i < right; i += Traits::pel_per_pack) { numeric_type x = 0; if (AlphaOneFill && Traits::component_mask.contains(C_A) && !alpha_enabled) x |= get_component(~static_cast(0), Traits::component_mask.find(C_A)); if (P2P_COMPONENT_ENABLED(0)) x |= get_component(*src_p[Traits::component_mask[0]]++, 0); if (P2P_COMPONENT_ENABLED(1)) x |= get_component(*src_p[Traits::component_mask[1]]++, 1); if (P2P_COMPONENT_ENABLED(2)) x |= get_component(*src_p[Traits::component_mask[2]]++, 2); if (P2P_COMPONENT_ENABLED(3)) x |= get_component(*src_p[Traits::component_mask[3]]++, 3); *dst_p++ = detail::endian_swap(static_cast(x)); } #undef P2P_COMPONENT_ENABLED } public: static void pack(const void * const src[4], void *dst, unsigned left, unsigned right) { #ifdef P2P_SIMD s_delegate(src, dst, left, right); #else pack_impl(src, dst, left, right); #endif } }; #ifdef P2P_SIMD template detail::pack_func planar_to_packed::s_delegate = detail::search_pack_func(planar_to_packed::pack_impl); #endif // v210 specializations. template <> class packed_to_planar { public: static void unpack(const void *src, void * const dst[4], unsigned left, unsigned right); }; template <> class packed_to_planar { public: static void unpack(const void *src, void * const dst[4], unsigned left, unsigned right); }; template <> class planar_to_packed { public: static void pack(const void * const src[4], void *dst, unsigned left, unsigned right); }; template <> class planar_to_packed { public: static void pack(const void * const src[4], void *dst, unsigned left, unsigned right); }; template <> class planar_to_packed { public: static void pack(const void * const src[4], void *dst, unsigned left, unsigned right); }; template <> class planar_to_packed { public: static void pack(const void * const src[4], void *dst, unsigned left, unsigned right); }; } // namespace p2p #endif // P2P_H_ ================================================ FILE: common-src/libp2p/p2p_api.cpp ================================================ #include #include #include #include "p2p.h" #include "p2p_api.h" #ifdef P2P_USER_NAMESPACE #error API build must not use custom namespace #endif namespace { struct packing_traits { enum p2p_packing packing; p2p_unpack_func unpack; p2p_pack_func pack; p2p_pack_func pack_one_fill; bool native_endian; unsigned char subsample_w; unsigned char subsample_h; bool is_nv; unsigned char bytes_per_sample; // Only used to copy luma plane for NV12. unsigned char nv_shift; // Extra LSB to shift away for MS P010/P210, etc. }; #define CASE(x, ...) \ { p2p_##x, &p2p::packed_to_planar::unpack, &p2p::planar_to_packed::pack, &p2p::planar_to_packed::pack, ##__VA_ARGS__ } #define CASE2(x, ...) \ CASE(x##_be, std::is_same::value, ##__VA_ARGS__), \ CASE(x##_le, std::is_same::value, ##__VA_ARGS__), \ CASE(x, true, ##__VA_ARGS__) const packing_traits traits_table[] = { CASE2(rgb24, 0, 0), CASE2(argb32, 0, 0), CASE2(ayuv, 0, 0), CASE2(rgb48, 0, 0), CASE2(argb64, 0, 0), CASE2(rgb30, 0, 0), CASE2(y410, 0, 0), CASE2(y416, 0, 0), CASE(yuy2, true, 1, 0), CASE(uyvy, true, 1, 0), CASE2(y210, 1, 0), CASE2(y216, 1, 0), CASE2(v210, 1, 0), CASE2(v216, 1, 0), CASE2(nv12, 1, 1, true, 1), CASE2(p010, 1, 1, true, 2, 6), CASE2(p016, 1, 1, true, 2), CASE2(p210, 1, 0, true, 2, 6), CASE2(p216, 1, 0, true, 2), CASE2(rgba32, 0, 0), CASE2(rgba64, 0, 0), CASE2(abgr64, 0, 0), CASE2(bgr48, 0, 0), CASE2(bgra64, 0, 0), }; #undef CASE2 #undef CASE const packing_traits &lookup_traits(enum p2p_packing packing) { assert(packing >= 0); assert(packing < sizeof(traits_table) / sizeof(traits_table[0])); const packing_traits &traits = traits_table[packing]; assert(traits.packing == packing); assert(traits.subsample_h == 0 || traits.is_nv); return traits; } template T *increment_ptr(T *ptr, ptrdiff_t n) { return (T *)((const unsigned char *)ptr + n); } void copy_plane_fast(const void *src, void *dst, ptrdiff_t src_stride, ptrdiff_t dst_stride, unsigned linesize, unsigned height) { for (unsigned i = 0; i < height; ++i) { memcpy(dst, src, linesize); src = increment_ptr(src, src_stride); dst = increment_ptr(dst, dst_stride); } } void unpack_nv16_plane(const void *src, void *dst, ptrdiff_t src_stride, ptrdiff_t dst_stride, const packing_traits &traits, unsigned width, unsigned height) { assert(traits.bytes_per_sample == 2); for (unsigned i = 0; i < height; ++i) { std::transform(static_cast(src), static_cast(src) + width, static_cast(dst), [=](uint16_t x) { x = traits.native_endian ? x : (x >> 8) | (x << 8); return x >> traits.nv_shift; }); src = increment_ptr(src, src_stride); dst = increment_ptr(dst, dst_stride); } } void pack_nv16_plane(const void *src, void *dst, ptrdiff_t src_stride, ptrdiff_t dst_stride, const packing_traits &traits, unsigned width, unsigned height) { assert(traits.bytes_per_sample == 2); for (unsigned i = 0; i < height; ++i) { std::transform(static_cast(src), static_cast(src) + width, static_cast(dst), [=](uint16_t x) { x = x << traits.nv_shift; return traits.native_endian ? x : (x >> 8) | (x << 8); }); src = increment_ptr(src, src_stride); dst = increment_ptr(dst, dst_stride); } } } // namespace p2p_unpack_func p2p_select_unpack_func(enum p2p_packing packing) { return lookup_traits(packing).unpack; } p2p_pack_func p2p_select_pack_func(enum p2p_packing packing) { return p2p_select_pack_func_ex(packing, 0); } p2p_pack_func p2p_select_pack_func_ex(enum p2p_packing packing, int alpha_one_fill) { const packing_traits &traits = lookup_traits(packing); return alpha_one_fill ? traits.pack_one_fill : traits.pack; } void p2p_unpack_frame(const struct p2p_buffer_param *param, unsigned long flags) { const packing_traits &traits = lookup_traits(param->packing); // Process interleaved plane. const void *src_p = traits.is_nv ? param->src[1] : param->src[0]; ptrdiff_t src_stride = traits.is_nv ? param->src_stride[1] : param->src_stride[0]; void *dst_p[4] = { param->dst[0], param->dst[1], param->dst[2], param->dst[3] }; for (unsigned i = 0; i < (param->height >> traits.subsample_h); ++i) { traits.unpack(src_p, dst_p, 0, param->width); src_p = increment_ptr(src_p, src_stride); if (!traits.is_nv) { dst_p[0] = increment_ptr(dst_p[0], param->dst_stride[0]); dst_p[3] = increment_ptr(dst_p[3], param->dst_stride[3]); } dst_p[1] = increment_ptr(dst_p[1], param->dst_stride[1]); dst_p[2] = increment_ptr(dst_p[2], param->dst_stride[2]); } if (traits.is_nv && !(flags & P2P_SKIP_UNPACKED_PLANES) && param->src[0] && param->dst[0]) { if ((traits.bytes_per_sample == 1 || traits.native_endian) && !traits.nv_shift) { copy_plane_fast(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0], traits.bytes_per_sample * param->width, param->height); } else { unpack_nv16_plane(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0], traits, param->width, param->height); } } } void p2p_pack_frame(const struct p2p_buffer_param *param, unsigned long flags) { const packing_traits &traits = lookup_traits(param->packing); p2p_pack_func pack_func = flags & P2P_ALPHA_SET_ONE ? traits.pack_one_fill : traits.pack; // Process interleaved plane. const void *src_p[4] = { param->src[0], param->src[1], param->src[2], param->src[3] }; void *dst_p = traits.is_nv ? param->dst[1] : param->dst[0]; ptrdiff_t dst_stride = traits.is_nv ? param->dst_stride[1] : param->dst_stride[0]; for (unsigned i = 0; i < (param->height >> traits.subsample_h); ++i) { pack_func(src_p, dst_p, 0, param->width); if (!traits.is_nv) { src_p[0] = increment_ptr(src_p[0], param->src_stride[0]); src_p[3] = increment_ptr(src_p[3], param->src_stride[3]); } src_p[1] = increment_ptr(src_p[1], param->src_stride[1]); src_p[2] = increment_ptr(src_p[2], param->src_stride[2]); dst_p = increment_ptr(dst_p, dst_stride); } if (traits.is_nv && !(flags & P2P_SKIP_UNPACKED_PLANES) && param->src[0] && param->dst[0]) { if ((traits.bytes_per_sample == 1 || traits.native_endian) && !traits.nv_shift) { copy_plane_fast(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0], traits.bytes_per_sample * param->width, param->height); } else { pack_nv16_plane(param->src[0], param->dst[0], param->src_stride[0], param->dst_stride[0], traits, param->width, param->height); } } } ================================================ FILE: common-src/libp2p/p2p_api.h ================================================ #ifndef P2P_API_H_ #define P2P_API_H_ #include #ifdef __cplusplus extern "C" { #endif /** * Notation: [Xa-Ya-Za] * * [] denotes a machine word of the specified endianness. Xa-Ya-Za denote * component X, Y, and Z packed in the word, with bit depths a, b, c, in order * from MSB to LSB. Padding bits are represented by the component '!'. */ enum p2p_packing { /** [R8-G8-B8] */ p2p_rgb24_be, /* RGB */ p2p_rgb24_le, /* BGR */ p2p_rgb24, /** [A8-R8-G8-B8] */ p2p_argb32_be, /* ARGB */ p2p_argb32_le, /* BGRA */ p2p_argb32, /** [A8-Y8-U8-V8] */ p2p_ayuv_be, /* AYUV */ p2p_ayuv_le, /* VUYA */ p2p_ayuv, /** [R16-G16-B16] */ p2p_rgb48_be, /* RGB, big-endian components */ p2p_rgb48_le, /* BGR, little-endian components */ p2p_rgb48, /** [A16-R16-G16-B16] */ p2p_argb64_be, /* ARGB big-endian components */ p2p_argb64_le, /* BGRA little-endian components */ p2p_argb64, /** [A2-R10-G10-B10] */ p2p_rgb30_be, /* ARGB packed in big-endian DWORD */ p2p_rgb30_le, /* ARGB packed in little-endian DWORD */ p2p_rgb30, /** [A2-V10-Y10-U10] */ p2p_y410_be, /* AVYU packed in big-endian DWORD */ p2p_y410_le, /* AVYU packed in little-endian DWORD */ p2p_y410, /** [A16-V16-Y16-U16] */ p2p_y416_be, /* AVYU, big-endian components */ p2p_y416_le, /* UYVA, little-endian components */ p2p_y416, /** [Y8] [U8] [Y8] [V8] */ p2p_yuy2, /** [U8] [Y8] [V8] [Y8] */ p2p_uyvy, /** [Y10-!6] [U10-!6] [Y10-!6] [V10-!6] */ p2p_y210_be, /* YUYV, big-endian components, lower 6 bits zero */ p2p_y210_le, /* YUYV, little-endian components, lower 6 bits zero. Microsoft Y210. */ p2p_y210, /** [Y16] [U16] [Y16] [V16] */ p2p_y216_be, /* YUYV, big-endian components */ p2p_y216_le, /* YUYV, little-endian components. Microsoft Y216. */ p2p_y216, /** [!2-V10-Y10-U10] [!2-Y10-U10-Y10] [!2-U10-Y10-V10] [!2-Y10-V10-Y10] */ p2p_v210_be, /* v210 with big-endian DWORDs */ p2p_v210_le, /* Apple/QuickTime v210 */ p2p_v210, /** [U16] [Y16] [V16] [Y16] */ p2p_v216_be, /* UYVY, big-endian components */ p2p_v216_le, /* UYVY, little-endian components. Apple/QuickTime v216. */ p2p_v216, /** [U8-V8] */ p2p_nv12_be, /* aka NV21, V first */ p2p_nv12_le, /* NV12 */ p2p_nv12, /** [U10-!6-V10-!6] */ p2p_p010_be, /* NV21, big-endian components, lower 6 bits zero */ p2p_p010_le, /* NV12, little-endian components, lower 6 bits zero. Microsoft P010. */ p2p_p010, /** [U16-V16] */ p2p_p016_be, /* NV21, big-endian components */ p2p_p016_le, /* NV12, little-endian components. Microsoft P016. */ p2p_p016, /** [U10-!6-V10-!6] */ p2p_p210_be, /* NV21, big-endian components, lower 6 bits zero */ p2p_p210_le, /* NV12, little-endian components, lower 6 bits zero. Microsoft P210. */ p2p_p210, /** [U16-V16] */ p2p_p216_be, /* NV21, big-endian components */ p2p_p216_le, /* NV12, little-endian components. Microsoft P216. */ p2p_p216, /** [R8-G8-B8-A8] */ p2p_rgba32_be, /* RGBA */ p2p_rgba32_le, /* ABGR */ p2p_rgba32, /** [R16-G16-B16-A16] */ p2p_rgba64_be, /* RGBA, big-endian components */ p2p_rgba64_le, /* ABGR, little-endian components */ p2p_rgba64, /** [A16-B16-G16-R16] */ p2p_abgr64_be, /* ABGR, big-endian components */ p2p_abgr64_le, /* RGBA, little-endian components */ p2p_abgr64, /** [B16-G16-R16] */ p2p_bgr48_be, /* BGR, big-endian components */ p2p_bgr48_le, /* RGB, little-endian components */ p2p_bgr48, /** [B16-G16-R16-A16] */ p2p_bgra64_be, /* BGRA, big-endian components */ p2p_bgra64_le, /* ARGB, little-endian components */ p2p_bgra64, p2p_packing_max, }; struct p2p_buffer_param { /** * Planar order: R-G-B-A or Y-U-V-A. Alpha is optional. * Packed order: Y-UV if NV12/21, else single plane. Y optional for NV12/21. */ const void *src[4]; void *dst[4]; ptrdiff_t src_stride[4]; ptrdiff_t dst_stride[4]; unsigned width; unsigned height; enum p2p_packing packing; }; /** Pack/unpack a range of pixels from a scanline. */ typedef void (*p2p_unpack_func)(const void *src, void * const dst[4], unsigned left, unsigned right); typedef void (*p2p_pack_func)(const void * const src[4], void *dst, unsigned left, unsigned right); /** Select a line pack/unpack function. */ p2p_unpack_func p2p_select_unpack_func(enum p2p_packing packing); p2p_pack_func p2p_select_pack_func(enum p2p_packing packing); p2p_pack_func p2p_select_pack_func_ex(enum p2p_packing packing, int alpha_one_fill); /** When processing formats like NV12, ignore the unpacked plane. */ #define P2P_SKIP_UNPACKED_PLANES (1UL << 0) /** When packing, store a bit pattern of all ones in the alpha channel instead of all zeros. */ #define P2P_ALPHA_SET_ONE (1UL << 1) /** Helper function to pack/unpack between memory locations. */ void p2p_unpack_frame(const struct p2p_buffer_param *param, unsigned long flags); void p2p_pack_frame(const struct p2p_buffer_param *param, unsigned long flags); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* P2P_API_H_ */ ================================================ FILE: common-src/libp2p/simd/cpuinfo_x86.cpp ================================================ #ifdef P2P_SIMD #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) #if 0 #include #define TRACE(fmt, ...) fprintf(stderr, "[cpuinfo] " fmt, __VA_ARGS__) #else #define TRACE(fmt, ...) do {} while (0) #endif #if defined(_MSC_VER) #include #elif defined(__GNUC__) #include #endif #include "../p2p.h" #include "cpuinfo_x86.h" namespace P2P_NAMESPACE { namespace simd { namespace { /** * Execute the CPUID instruction. * * @param regs array to receive eax, ebx, ecx, edx * @param eax argument to instruction * @param ecx argument to instruction */ void do_cpuid(int regs[4], int eax, int ecx) { #if defined(_MSC_VER) __cpuidex(regs, eax, ecx); #elif defined(__GNUC__) __cpuid_count(eax, ecx, regs[0], regs[1], regs[2], regs[3]); #else regs[0] = 0; regs[1] = 0; regs[2] = 0; regs[3] = 0; #endif } /** * Execute the XGETBV instruction. * * @param ecx argument to instruction * @return (edx << 32) | eax */ unsigned long long do_xgetbv(unsigned ecx) { #if defined(_MSC_VER) return _xgetbv(ecx); #elif defined(__GNUC__) unsigned eax, edx; __asm("xgetbv" : "=a"(eax), "=d"(edx) : "c"(ecx) : ); return (static_cast(edx) << 32) | eax; #else return 0; #endif } X86Capabilities do_query_x86_capabilities() noexcept { X86Capabilities caps = { 0 }; unsigned long long xcr0 = 0; int regs[4] = { 0 }; int xmmymm = 0; int zmm = 0; do_cpuid(regs, 1, 0); caps.sse = !!(regs[3] & (1U << 25)); caps.sse2 = !!(regs[3] & (1U << 26)); caps.sse3 = !!(regs[2] & (1U << 0)); caps.ssse3 = !!(regs[2] & (1U << 9)); caps.fma = !!(regs[2] & (1U << 12)); caps.sse41 = !!(regs[2] & (1U << 19)); caps.sse42 = !!(regs[2] & (1U << 20)); // osxsave if (regs[2] & (1U << 27)) { xcr0 = do_xgetbv(0); xmmymm = (xcr0 & 0x06) == 0x06; zmm = (xcr0 & 0xE0) == 0xE0; } // XMM and YMM state. if (xmmymm) { caps.avx = !!(regs[2] & (1U << 28)); caps.f16c = !!(regs[2] & (1U << 29)); } do_cpuid(regs, 7, 0); if (xmmymm) { caps.avx2 = !!(regs[1] & (1U << 5)); } // ZMM state. if (zmm) { caps.avx512f = !!(regs[1] & (1U << 16)); caps.avx512dq = !!(regs[1] & (1U << 17)); caps.avx512ifma = !!(regs[1] & (1U << 21)); caps.avx512cd = !!(regs[1] & (1U << 28)); caps.avx512bw = !!(regs[1] & (1U << 30)); caps.avx512vl = !!(regs[1] & (1U << 31)); caps.avx512vbmi = !!(regs[2] & (1U << 1)); caps.avx512vbmi2 = !!(regs[2] & (1U << 6)); caps.avx512vnni = !!(regs[2] & (1U << 11)); caps.avx512bitalg = !!(regs[2] & (1U << 12)); caps.avx512vpopcntdq = !!(regs[2] & (1U << 14)); caps.avx512vp2intersect = !!(regs[3] & (1U << 8)); caps.avx512fp16 = !!(regs[3] & (1U << 23)); } do_cpuid(regs, 7, 1); if (zmm) { caps.avx512bf16 = !!(regs[0] & (1U << 5)); } // Extended processor info. do_cpuid(regs, 0x80000001U, 0); caps.xop = !!(regs[2] & (1U << 11)); // Zen1 vs Zen2. do_cpuid(regs, 0, 1); if (regs[1] == 0x68747541U && regs[3] == 0x69746E65U && regs[2] == 0x444D4163U /* AuthenticAMD */) { unsigned model; unsigned family; do_cpuid(regs, 1, 0); model = (regs[0] >> 4) & 0x0FU; family = (regs[0] >> 8) & 0x0FU; if (family == 15) { family += ((regs[0] >> 20) & 0x0FU); model += ((regs[0] >> 16) & 0x0FU) << 4; } caps.piledriver = family == 0x15 && model == 0x02; caps.zen1 = family == 0x17 && model <= 0x2F; caps.zen2 = family == 0x17 && model >= 0x30; caps.zen3 = family == 0x19; } return caps; } // Query leaf 4h (Intel) or leaf 8000001Dh (AMD). void do_query_x86_deterministic_cache_parameters(X86CacheHierarchy &cache, int leaf) noexcept { int regs[4]; for (int i = 0; i < 8; ++i) { unsigned threads; unsigned long line_size; unsigned long partitions; unsigned long ways; unsigned long sets; unsigned long cache_size; int cache_type; bool inclusive; do_cpuid(regs, leaf, i); cache_type = regs[0] & 0x1FU; TRACE("L%u cache, type %d\n", (static_cast(regs[0]) >> 5) & 0x07U, cache_type); // No more caches. if (cache_type == 0) break; // Not data or unified cache. if (cache_type != 1 && cache_type != 3) continue; threads = ((static_cast(regs[0]) >> 14) & 0x0FFFU) + 1; line_size = ((static_cast(regs[1]) >> 0) & 0x0FFFU) + 1; partitions = ((static_cast(regs[1]) >> 12) & 0x03FFU) + 1; ways = ((static_cast(regs[1]) >> 22) & 0x03FFU) + 1; sets = static_cast(regs[2]) + 1; cache_size = line_size * partitions * ways * sets; inclusive = regs[3] & (1U << 1); TRACE("%u threads, %lu bytes, %s\n", threads, cache_size, inclusive ? "inclusive" : "non-inclusive"); // Cache level. switch ((static_cast(regs[0]) >> 5) & 0x07U) { case 1: cache.l1d = cache_size; cache.l1d_threads = threads; break; case 2: cache.l2 = cache_size; cache.l2_threads = threads; cache.l2_inclusive = inclusive; break; case 3: cache.l3 = cache_size; cache.l3_threads = threads; cache.l3_inclusive = inclusive; break; default: break; } } } X86CacheHierarchy do_query_x86_cache_hierarchy_intel(int max_feature) noexcept { X86CacheHierarchy cache = { 0 }; int regs[4]; if (max_feature < 2) return cache; // Detect cache size of single-threaded CPU from flags. if (max_feature >= 2 && max_feature < 4) return cache; // Detect cache hierarchy. if (max_feature >= 4) do_query_x86_deterministic_cache_parameters(cache, 4); // Detect logical processor count on x2APIC systems. if (max_feature >= 0x0B) { unsigned l1d_threads = cache.l1d_threads; unsigned l2_threads = cache.l2_threads; unsigned l3_threads = cache.l3_threads; for (int i = 0; i < 8; ++i) { unsigned logical_processors; do_cpuid(regs, 0x0B, i); TRACE("APIC level %u\n", (static_cast(regs[2]) >> 8) & 0xFFU); if (((regs[2] >> 8) & 0xFFU) == 0) break; logical_processors = regs[1] & 0xFFFFU; TRACE("logical processors: %u\n", logical_processors); l1d_threads = logical_processors <= cache.l1d_threads ? logical_processors : l1d_threads; l2_threads = logical_processors <= cache.l2_threads ? logical_processors : l2_threads; l3_threads = logical_processors <= cache.l3_threads ? logical_processors : l3_threads; TRACE("updated cache sharing: %u %u %u\n", l1d_threads, l2_threads, l3_threads); } cache.l1d_threads = l1d_threads; cache.l2_threads = l2_threads; cache.l3_threads = l3_threads; } return cache; } X86CacheHierarchy do_query_x86_cache_hierarchy_amd(int max_feature) noexcept { X86CacheHierarchy cache = { 0 }; int regs[4]; unsigned max_extended_feature; do_cpuid(regs, 0x80000000U, 0); max_extended_feature = static_cast(regs[0]); TRACE("max extended feature #: 0x%x\n", max_extended_feature); if (max_extended_feature >= 0x80000005U) { do_cpuid(regs, 0x80000005U, 0); cache.l1d = ((static_cast(regs[2]) >> 24) & 0xFFU) * 1024U; cache.l1d_threads = cache.l1d ? 1 : cache.l1d_threads; TRACE("L1d: %lu\n", cache.l1d); } if (max_extended_feature >= 0x80000006U) { do_cpuid(regs, 0x80000006U, 0); cache.l2 = ((static_cast(regs[2]) >> 16) & 0xFFFFU) * 1024U; cache.l3 = ((static_cast(regs[3]) >> 18) & 0x3FFFU) * 512U * 1024U; cache.l2_threads = cache.l2 ? 1 : cache.l2_threads; cache.l3_threads = cache.l3 ? 1 : cache.l3_threads; TRACE("L2: %lu\n", cache.l2); TRACE("L3: %lu\n", cache.l3); } if (max_extended_feature >= 0x80000008U) { unsigned threads; unsigned family; do_cpuid(regs, 0x80000008U, 0); threads = (regs[2] & 0xFFU) + 1; cache.l3_threads = cache.l3 ? threads : cache.l3_threads; TRACE("package threads: %u\n", threads); do_cpuid(regs, 1, 0); family = ((static_cast(regs[0]) >> 20) & 0xFFU) + ((static_cast(regs[0]) >> 8) & 0x0FU); TRACE("family %xh\n", family); if (family == 0x15) cache.l2_threads = 2; // Bulldozer shared L2 cache. else if (family == 0x16) cache.l2_threads = threads; // Jaguar L2 LLC. } if (max_extended_feature >= 0x8000001DU) do_query_x86_deterministic_cache_parameters(cache, 0x8000001DU); return cache; } X86CacheHierarchy do_query_x86_cache_hierarchy() noexcept { enum { GENUINEINTEL, AUTHENTICAMD, OTHER } vendor; X86CacheHierarchy cache = { 0 }; int regs[4] = { 0 }; int max_feature; do_cpuid(regs, 0, 1); max_feature = regs[0] & 0xFFU; TRACE("max feature #: %d\n", max_feature); if (regs[1] == 0x756E6547U && regs[3] == 0x49656E69U && regs[2] == 0x6C65746EU) { vendor = GENUINEINTEL; TRACE("%s\n", "GenuineIntel"); } else if (regs[1] == 0x68747541U && regs[3] == 0x69746E65U && regs[2] == 0x444D4163U) { vendor = AUTHENTICAMD; TRACE("%s\n", "AuthenticAMD"); } else { vendor = OTHER; TRACE("vendor %08x-%08x-%08x\n", regs[0], regs[2], regs[1]); } if (vendor == GENUINEINTEL) cache = do_query_x86_cache_hierarchy_intel(max_feature); else if (vendor == AUTHENTICAMD) cache = do_query_x86_cache_hierarchy_amd(max_feature); TRACE("final hierarchy: L1 %lu / %lu, L2: %lu / %lu, L3: %lu / %lu\n", cache.l1d, cache.l1d_threads, cache.l2, cache.l2_threads, cache.l3, cache.l3_threads); cache.valid = cache.l1d && cache.l1d_threads && !(cache.l2 && !cache.l2_threads) && !(cache.l3 && !cache.l3_threads); return cache; } } // namespace X86Capabilities query_x86_capabilities() noexcept { static const X86Capabilities caps = do_query_x86_capabilities(); return caps; } X86CacheHierarchy query_x86_cache_hierarchy() noexcept { static const X86CacheHierarchy cache = do_query_x86_cache_hierarchy(); return cache; } unsigned long cpu_cache_size_x86() noexcept { const X86CacheHierarchy cache = query_x86_cache_hierarchy(); if (!cache.valid) return 0; // Detect Skylake-SP cache hierarchy and report L2 size instead of L3. if (cache.l3 && !cache.l3_inclusive && cache.l2 >= 1024 * 1024U && cache.l2_threads <= 2) return cache.l2 / cache.l2_threads; if (cache.l3) return cache.l3 / cache.l3_threads; else if (cache.l2) return cache.l2 / cache.l2_threads; else return cache.l1d / cache.l1d_threads; } } // namespace simd } // namespace p2p #endif // x86 #endif // P2P_SIMD ================================================ FILE: common-src/libp2p/simd/cpuinfo_x86.h ================================================ #pragma once #ifndef P2P_CPUINFO_X86_H_ #define P2P_CPUINFO_X86_H_ #ifdef P2P_SIMD #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) #include "../p2p.h" namespace P2P_NAMESPACE { namespace simd { /** * Bitfield of selected x86 feature flags. */ struct X86Capabilities { unsigned sse : 1; unsigned sse2 : 1; unsigned sse3 : 1; unsigned ssse3 : 1; unsigned fma : 1; unsigned sse41 : 1; unsigned sse42 : 1; unsigned avx : 1; unsigned f16c : 1; unsigned avx2 : 1; unsigned avx512f : 1; unsigned avx512dq : 1; unsigned avx512ifma : 1; unsigned avx512cd : 1; unsigned avx512bw : 1; unsigned avx512vl : 1; unsigned avx512vbmi : 1; unsigned avx512vbmi2 : 1; unsigned avx512vnni : 1; unsigned avx512bitalg : 1; unsigned avx512vpopcntdq : 1; unsigned avx512vp2intersect : 1; unsigned avx512fp16 : 1; unsigned avx512bf16 : 1; /* AMD architectures needing workarounds. */ unsigned xop : 1; unsigned piledriver : 1; unsigned zen1 : 1; unsigned zen2 : 1; unsigned zen3 : 1; }; /** * Representation of processor cache topology. */ struct X86CacheHierarchy { unsigned long l1d; unsigned long l1d_threads; unsigned long l2; unsigned long l2_threads; unsigned long l3; unsigned long l3_threads; bool l2_inclusive; bool l3_inclusive; bool valid; }; /** * Get the x86 feature flags on the current CPU. * * @return capabilities */ X86Capabilities query_x86_capabilities() noexcept; /** * Get the cache topology of the current CPU. * * On a multi-processor system, the returned topology corresponds to the first * processor package on which the function is called. The behaviour is * undefined if the platform contains non-identical processors. * * @return cache hierarchy */ X86CacheHierarchy query_x86_cache_hierarchy() noexcept; unsigned long cpu_cache_size_x86() noexcept; } // namespace simd } // namespace p2p #endif // x86 #endif // P2P_SIMD #endif // P2P_CPUINFO_X86_H_ ================================================ FILE: common-src/libp2p/simd/p2p_simd.cpp ================================================ #ifdef P2P_SIMD #include #include #include #include #include "../p2p.h" #include "cpuinfo_x86.h" #include "p2p_simd.h" namespace P2P_NAMESPACE { namespace detail { namespace { typedef std::pair unpack_table_entry; typedef std::tuple pack_table_entry; auto populate_unpack_table() { std::array table; size_t idx = 0; #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) simd::X86Capabilities x86 = simd::query_x86_capabilities(); if (x86.sse41) { #define ENTRY(format, cpu) table[idx++] = unpack_table_entry{ &typeid(packed_##format), simd::unpack_##format##_##cpu } ENTRY(argb32_be, sse41); ENTRY(argb32_le, sse41); ENTRY(rgba32_be, sse41); ENTRY(rgba32_le, sse41); #undef ENTRY } #endif return table; } auto populate_pack_table() { std::array table; size_t idx = 0; #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) simd::X86Capabilities x86 = simd::query_x86_capabilities(); if (x86.sse41) { #define ENTRY(format, cpu) table[idx++] = pack_table_entry{ &typeid(packed_##format), simd::pack_##format##_0_##cpu, simd::pack_##format##_1_##cpu } ENTRY(argb32_be, sse41); ENTRY(argb32_le, sse41); ENTRY(rgba32_be, sse41); ENTRY(rgba32_le, sse41); #undef ENTRY } #endif return table; } } // namespace unpack_func search_unpack_func(const std::type_info &ti) { static const auto g_unpack_table = populate_unpack_table(); for (const auto &entry : g_unpack_table) { if (entry.first == &ti) return entry.second; if (!entry.first) break; } return nullptr; } pack_func search_pack_func(const std::type_info &ti, bool alpha_one_fill) { static const auto g_pack_table = populate_pack_table(); for (const auto &entry : g_pack_table) { if (std::get<0>(entry) == &ti) return alpha_one_fill ? std::get<2>(entry) : std::get<1>(entry); if (!std::get<0>(entry)) break; } return nullptr; } } // namespace detail } // namespace p2p #endif // P2P_SIMD ================================================ FILE: common-src/libp2p/simd/p2p_simd.h ================================================ #pragma once #ifndef P2P_SIMD_H_ #define P2P_SIMD_H_ #ifdef P2P_SIMD #include "../p2p.h" namespace P2P_NAMESPACE { namespace simd { #define UNPACK(format, cpu) void unpack_##format##_##cpu(const void *, void * const *, unsigned, unsigned); #define PACK(format, cpu) \ void pack_##format##_0_##cpu(const void * const *, void *, unsigned, unsigned); \ void pack_##format##_1_##cpu(const void * const *, void *, unsigned, unsigned); #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) UNPACK(argb32_be, sse41) UNPACK(argb32_le, sse41) UNPACK(rgba32_be, sse41) UNPACK(rgba32_le, sse41) PACK(argb32_be, sse41) PACK(argb32_le, sse41) PACK(rgba32_be, sse41) PACK(rgba32_le, sse41) #endif // x86 #undef PACK #undef UNPACK } // namespace simd } // namespace p2p #endif // P2P_SIMD #endif // P2P_SIMD_H_ ================================================ FILE: common-src/libp2p/simd/p2p_sse41.cpp ================================================ #ifdef P2P_SIMD #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) #include #include #include "../p2p.h" namespace P2P_NAMESPACE { namespace simd { namespace { template uint32_t extract_epi32(__m128i x) { return Idx == 0 ? _mm_cvtsi128_si32(x) : _mm_extract_epi32(x, Idx); } template void unpack_rgb32_sse41(const void *src, void * const * dst, unsigned left, unsigned right) { const __m128i shuffle = _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0); const uint32_t *src_p = static_cast(src); uint8_t *dst_r = static_cast(dst[0]); uint8_t *dst_g = static_cast(dst[1]); uint8_t *dst_b = static_cast(dst[2]); uint8_t *dst_a = static_cast(dst[3]); if (!dst_a) dst_a = dst_r; // Write alpha to some other channel if disabled. size_t vec4_left = (left + 3) & ~3U; size_t vec16_left = (left + 15) & ~15U; size_t vec16_right = right & ~15U; size_t vec4_right = right & ~3U; // Must always write alpha component first! auto scalar_iter = [&](size_t i) { uint32_t x = src_p[i]; dst_a[i] = static_cast((x >> (IdxA * 8)) & 0xFFU); dst_r[i] = static_cast((x >> (IdxR * 8)) & 0xFFU); dst_g[i] = static_cast((x >> (IdxG * 8)) & 0xFFU); dst_b[i] = static_cast((x >> (IdxB * 8)) & 0xFFU); }; auto vec4_iter = [&](size_t i) { __m128i x = _mm_loadu_si128((const __m128i *)(src_p + i)); x = _mm_shuffle_epi8(x, shuffle); *reinterpret_cast(dst_a + i) = extract_epi32(x); *reinterpret_cast(dst_r + i) = extract_epi32(x); *reinterpret_cast(dst_g + i) = extract_epi32(x); *reinterpret_cast(dst_b + i) = extract_epi32(x); }; auto vec16_iter = [&](size_t i) { __m128i x0 = _mm_loadu_si128((const __m128i *)(src_p + i)); __m128i x1 = _mm_loadu_si128((const __m128i *)(src_p + i + 4)); __m128i x2 = _mm_loadu_si128((const __m128i *)(src_p + i + 8)); __m128i x3 = _mm_loadu_si128((const __m128i *)(src_p + i + 12)); x0 = _mm_shuffle_epi8(x0, shuffle); x1 = _mm_shuffle_epi8(x1, shuffle); x2 = _mm_shuffle_epi8(x2, shuffle); x3 = _mm_shuffle_epi8(x3, shuffle); __m128 x0s = _mm_castsi128_ps(x0), x1s = _mm_castsi128_ps(x1), x2s = _mm_castsi128_ps(x2), x3s = _mm_castsi128_ps(x3); _MM_TRANSPOSE4_PS(x0s, x1s, x2s, x3s); x0 = _mm_castps_si128(x0s); x1 = _mm_castps_si128(x1s); x2 = _mm_castps_si128(x2s); x3 = _mm_castps_si128(x3s); __m128i regs[4] = { x0, x1, x2, x3 }; _mm_storeu_si128((__m128i *)(dst_a + i), regs[IdxA]); _mm_storeu_si128((__m128i *)(dst_r + i), regs[IdxR]); _mm_storeu_si128((__m128i *)(dst_g + i), regs[IdxG]); _mm_storeu_si128((__m128i *)(dst_b + i), regs[IdxB]); }; for (size_t i = left; i < vec4_left; ++i) scalar_iter(i); for (size_t i = vec4_left; i < vec16_left; i += 4) vec4_iter(i); for (size_t i = vec16_left; i < vec16_right; i += 16) vec16_iter(i); for (size_t i = vec16_right; i < vec4_right; i += 4) vec4_iter(i); for (size_t i = vec4_right; i < right; ++i) scalar_iter(i); } template void pack_rgb32_sse41(const void * const *src, void *dst, unsigned left, unsigned right) { #define X (AlphaOneFill ? 0xFF : 0) alignas(16) static constexpr uint8_t alpha_fill[16] = { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }; #undef X const __m128i shuffle = _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0); const uint8_t *src_r = static_cast(src[0]); const uint8_t *src_g = static_cast(src[1]); const uint8_t *src_b = static_cast(src[2]); const uint8_t *src_a = static_cast(src[3]); size_t alpha_addr_mask = ~static_cast(0); uint32_t *dst_p = static_cast(dst); size_t vec4_left = (left + 3) & ~3U; size_t vec16_left = (left + 15) & ~15U; size_t vec16_right = right & ~15U; size_t vec4_right = right & ~3U; if (!src_a) { src_a = alpha_fill; alpha_addr_mask = 15; } auto scalar_iter = [&](size_t i) { uint8_t r = src_r[i]; uint8_t g = src_g[i]; uint8_t b = src_b[i]; uint8_t a = src_a[i & alpha_addr_mask]; uint32_t val = (static_cast(r) << (IdxR * 8)) | (static_cast(g) << (IdxG * 8)) | (static_cast(b) << (IdxB * 8)) | (static_cast(a) << (IdxA * 8)); dst_p[i] = val; }; auto vec4_iter = [&](size_t i) { uint32_t r = *reinterpret_cast(src_r + i); uint32_t g = *reinterpret_cast(src_g + i); uint32_t b = *reinterpret_cast(src_b + i); uint32_t a = *reinterpret_cast(src_a + (i & alpha_addr_mask)); __m128i x = IdxR == 0 ? _mm_cvtsi32_si128(r) : IdxG == 0 ? _mm_cvtsi32_si128(g) : IdxB == 0 ? _mm_cvtsi32_si128(b) : IdxA == 0 ? _mm_cvtsi32_si128(a) : throw 1; x = IdxR == 0 ? x : _mm_insert_epi32(x, r, IdxR); x = IdxG == 0 ? x : _mm_insert_epi32(x, g, IdxG); x = IdxB == 0 ? x : _mm_insert_epi32(x, b, IdxB); x = IdxA == 0 ? x : _mm_insert_epi32(x, a, IdxA); x = _mm_shuffle_epi8(x, shuffle); _mm_storeu_si128((__m128i *)(dst_p + i), x); }; auto vec16_iter = [&](size_t i) { __m128i r = _mm_loadu_si128((const __m128i *)(src_r + i)); __m128i g = _mm_loadu_si128((const __m128i *)(src_g + i)); __m128i b = _mm_loadu_si128((const __m128i *)(src_b + i)); __m128i a = _mm_loadu_si128((const __m128i *)(src_a + (i & alpha_addr_mask))); __m128 regs[4]; regs[IdxR] = _mm_castsi128_ps(r); regs[IdxG] = _mm_castsi128_ps(g); regs[IdxB] = _mm_castsi128_ps(b); regs[IdxA] = _mm_castsi128_ps(a); _MM_TRANSPOSE4_PS(regs[0], regs[1], regs[2], regs[3]); __m128i x0 = _mm_castps_si128(regs[0]), x1 = _mm_castps_si128(regs[1]), x2 = _mm_castps_si128(regs[2]), x3 = _mm_castps_si128(regs[3]); x0 = _mm_shuffle_epi8(x0, shuffle); x1 = _mm_shuffle_epi8(x1, shuffle); x2 = _mm_shuffle_epi8(x2, shuffle); x3 = _mm_shuffle_epi8(x3, shuffle); _mm_storeu_si128((__m128i *)(dst_p + i + 0), x0); _mm_storeu_si128((__m128i *)(dst_p + i + 4), x1); _mm_storeu_si128((__m128i *)(dst_p + i + 8), x2); _mm_storeu_si128((__m128i *)(dst_p + i + 12), x3); }; for (size_t i = left; i < vec4_left; ++i) scalar_iter(i); for (size_t i = vec4_left; i < vec16_left; i += 4) vec4_iter(i); for (size_t i = vec16_left; i < vec16_right; i += 16) vec16_iter(i); for (size_t i = vec16_right; i < vec4_right; i += 4) vec4_iter(i); for (size_t i = vec4_right; i < right; ++i) scalar_iter(i); } } // namespace #define RGB32_SSE41(format, a, b, c, d) \ void unpack_##format##_sse41(const void *src, void * const * dst, unsigned left, unsigned right) \ { \ unpack_rgb32_sse41(src, dst, left, right); \ } \ void pack_##format##_0_sse41(const void * const *src, void *dst, unsigned left, unsigned right) \ { \ pack_rgb32_sse41(src, dst, left, right); \ } \ void pack_##format##_1_sse41(const void * const *src, void *dst, unsigned left, unsigned right) \ { \ pack_rgb32_sse41(src, dst, left, right); \ } RGB32_SSE41(argb32_be, 1, 2, 3, 0) RGB32_SSE41(argb32_le, 2, 1, 0, 3) RGB32_SSE41(rgba32_be, 0, 1, 2, 3) RGB32_SSE41(rgba32_le, 3, 2, 1, 0) } // namespace simd } // namespace p2p #endif // x86 #endif // P2P_SIMD ================================================ FILE: common-src/libp2p/v210.cpp ================================================ #include "p2p.h" namespace P2P_NAMESPACE { namespace { template void unpack_v210(const void *src, void * const dst[4], unsigned left, unsigned right) { const unsigned lsb_10b = 0x3FF; const uint32_t *src_p = static_cast(src); uint16_t *dst_p[3] = { static_cast(dst[0]), static_cast(dst[1]), static_cast(dst[2]) }; // v210 packs 6 pixels in 4 DWORDs. left = left - (left % 6); // Adjust pointers. src_p += left * 4 / 6; dst_p[C_Y] += left; dst_p[C_U] += left / 2; dst_p[C_V] += left / 2; for (unsigned i = left; i < right - right % 6; i += 6) { uint32_t w0 = detail::endian_swap(*src_p++); uint32_t w1 = detail::endian_swap(*src_p++); uint32_t w2 = detail::endian_swap(*src_p++); uint32_t w3 = detail::endian_swap(*src_p++); *dst_p[C_U]++ = static_cast((w0 >> 0) & lsb_10b); *dst_p[C_Y]++ = static_cast((w0 >> 10) & lsb_10b); *dst_p[C_V]++ = static_cast((w0 >> 20) & lsb_10b); *dst_p[C_Y]++ = static_cast((w1 >> 0) & lsb_10b); *dst_p[C_U]++ = static_cast((w1 >> 10) & lsb_10b); *dst_p[C_Y]++ = static_cast((w1 >> 20) & lsb_10b); *dst_p[C_V]++ = static_cast((w2 >> 0) & lsb_10b); *dst_p[C_Y]++ = static_cast((w2 >> 10) & lsb_10b); *dst_p[C_U]++ = static_cast((w2 >> 20) & lsb_10b); *dst_p[C_Y]++ = static_cast((w3 >> 0) & lsb_10b); *dst_p[C_V]++ = static_cast((w3 >> 10) & lsb_10b); *dst_p[C_Y]++ = static_cast((w3 >> 20) & lsb_10b); } if (right % 6) { // No check needed as v210 is 128-byte aligned. uint32_t w0 = detail::endian_swap(*src_p++); uint32_t w1 = detail::endian_swap(*src_p++); uint32_t w2 = detail::endian_swap(*src_p++); uint32_t w3 = detail::endian_swap(*src_p++); { *dst_p[C_U]++ = static_cast((w0 >> 0) & lsb_10b); *dst_p[C_Y]++ = static_cast((w0 >> 10) & lsb_10b); *dst_p[C_V]++ = static_cast((w0 >> 20) & lsb_10b); *dst_p[C_Y]++ = static_cast((w1 >> 0) & lsb_10b); } if (right % 6 > 2) { *dst_p[C_U]++ = static_cast((w1 >> 10) & lsb_10b); *dst_p[C_Y]++ = static_cast((w1 >> 20) & lsb_10b); *dst_p[C_V]++ = static_cast((w2 >> 0) & lsb_10b); *dst_p[C_Y]++ = static_cast((w2 >> 10) & lsb_10b); } if (right % 6 > 4) { *dst_p[C_U]++ = static_cast((w2 >> 20) & lsb_10b); *dst_p[C_Y]++ = static_cast((w3 >> 0) & lsb_10b); *dst_p[C_V]++ = static_cast((w3 >> 10) & lsb_10b); *dst_p[C_Y]++ = static_cast((w3 >> 20) & lsb_10b); } } } template void pack_v210(const void * const src[4], void *dst, unsigned left, unsigned right) { const unsigned lsb_10b = 0x3FF; const uint16_t *src_p[3] = { static_cast(src[0]), static_cast(src[1]), static_cast(src[2]) }; uint32_t *dst_p = static_cast(dst); // v210 packs 6 pixels in 4 DWORDs. left = left - (left % 6); // Adjust pointers. src_p[C_Y] += left; src_p[C_U] += left / 2; src_p[C_V] += left / 2; dst_p += left * 4 / 6; for (unsigned i = left; i < right - right % 6; i += 6) { uint32_t w0 = 0; uint32_t w1 = 0; uint32_t w2 = 0; uint32_t w3 = 0; w0 |= static_cast(*src_p[C_U]++ & lsb_10b) << 0; w0 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 10; w0 |= static_cast(*src_p[C_V]++ & lsb_10b) << 20; w1 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 0; w1 |= static_cast(*src_p[C_U]++ & lsb_10b) << 10; w1 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 20; w2 |= static_cast(*src_p[C_V]++ & lsb_10b) << 0; w2 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 10; w2 |= static_cast(*src_p[C_U]++ & lsb_10b) << 20; w3 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 0; w3 |= static_cast(*src_p[C_V]++ & lsb_10b) << 10; w3 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 20; *dst_p++ = detail::endian_swap(w0); *dst_p++ = detail::endian_swap(w1); *dst_p++ = detail::endian_swap(w2); *dst_p++ = detail::endian_swap(w3); } if (right % 6) { uint32_t w0 = 0; uint32_t w1 = 0; uint32_t w2 = 0; uint32_t w3 = 0; { w0 |= static_cast(*src_p[C_U]++ & lsb_10b) << 0; w0 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 10; w0 |= static_cast(*src_p[C_V]++ & lsb_10b) << 20; w1 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 0; } if (right % 6 > 2) { w1 |= static_cast(*src_p[C_U]++ & lsb_10b) << 10; w1 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 20; w2 |= static_cast(*src_p[C_V]++ & lsb_10b) << 0; w2 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 10; } if (right % 6 > 4) { w2 |= static_cast(*src_p[C_U]++ & lsb_10b) << 20; w3 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 0; w3 |= static_cast(*src_p[C_V]++ & lsb_10b) << 10; w3 |= static_cast(*src_p[C_Y]++ & lsb_10b) << 20; } // No check needed as v210 is 128-byte aligned. *dst_p++ = detail::endian_swap(w0); *dst_p++ = detail::endian_swap(w1); *dst_p++ = detail::endian_swap(w2); *dst_p++ = detail::endian_swap(w3); } } } // namespace void packed_to_planar::unpack(const void *src, void * const dst[4], unsigned left, unsigned right) { unpack_v210(src, dst, left, right); } void packed_to_planar::unpack(const void *src, void * const dst[4], unsigned left, unsigned right) { unpack_v210(src, dst, left, right); } void planar_to_packed::pack(const void * const src[4], void *dst, unsigned left, unsigned right) { pack_v210(src, dst, left, right); } void planar_to_packed::pack(const void * const src[4], void *dst, unsigned left, unsigned right) { pack_v210(src, dst, left, right); } void planar_to_packed::pack(const void * const src[4], void *dst, unsigned left, unsigned right) { pack_v210(src, dst, left, right); } void planar_to_packed::pack(const void * const src[4], void *dst, unsigned left, unsigned right) { pack_v210(src, dst, left, right); } } // namespace p2p ================================================ FILE: common-src/log/log_styles_model.cpp ================================================ #include "log_styles_model.h" //============================================================================== LogStylesModel::LogStylesModel(QObject * a_pParent) : QAbstractItemModel(a_pParent) { } // END OF LogStylesModel::LogStylesModel(QObject * a_pParent) //============================================================================== LogStylesModel::~LogStylesModel() { } // END OF LogStylesModel::~LogStylesModel() //============================================================================== QModelIndex LogStylesModel::index(int a_row, int a_column, const QModelIndex & a_parent) const { (void)a_parent; return createIndex(a_row, a_column); } // END OF QModelIndex LogStylesModel::index(int a_row, int a_column, // const QModelIndex & a_parent) const //============================================================================== QModelIndex LogStylesModel::parent(const QModelIndex & a_child) const { (void)a_child; return QModelIndex(); } // END OF QModelIndex LogStylesModel::parent(const QModelIndex & a_child) // const //============================================================================== Qt::ItemFlags LogStylesModel::flags(const QModelIndex & a_index) const { if (!a_index.isValid()) { return Qt::NoItemFlags; } Qt::ItemFlags cellFlags = Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable ; return cellFlags; } // END OF Qt::ItemFlags LogStylesModel::flags(const QModelIndex & a_index) // const //============================================================================== QVariant LogStylesModel::data(const QModelIndex & a_index, int a_role) const { if(!a_index.isValid()) return QVariant(); int row = a_index.row(); int column = a_index.column(); if((row >= (int)m_styles.size()) || (column >= 1)) return QVariant(); if((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole)) return m_styles[row].title; else if(a_role == Qt::UserRole) return m_styles[row].name; else if(a_role == Qt::CheckStateRole) return m_styles[row].isVisible ? Qt::Checked : Qt::Unchecked; return QVariant(); } // END OF QVariant LogStylesModel::data(const QModelIndex & a_index, // int a_role) const //============================================================================== int LogStylesModel::rowCount(const QModelIndex & a_parent) const { (void)a_parent; return (int)m_styles.size(); } // END OF int LogStylesModel::rowCount(const QModelIndex & a_parent) const //============================================================================== int LogStylesModel::columnCount(const QModelIndex & a_parent) const { (void)a_parent; return 1; } // END OF int LogStylesModel::columnCount(const QModelIndex & a_parent) // const //============================================================================== bool LogStylesModel::setData(const QModelIndex & a_index, const QVariant & a_value, int a_role) { if(!a_index.isValid()) return false; int row = a_index.row(); int column = a_index.column(); if((row >= (int)m_styles.size()) || (column >= 1)) return false; if(a_role == Qt::CheckStateRole) { m_styles[row].isVisible = (a_value == Qt::Checked) ? true : false; return true; } return false; } // END OF bool LogStylesModel::setData(const QModelIndex & a_index, // const QVariant & a_value, int a_role) //============================================================================== std::vector LogStylesModel::styles() const { return m_styles; } // END OF std::vector LogStylesModel::styles() const //============================================================================== void LogStylesModel::setStyles(const std::vector & a_styles) { beginResetModel(); m_styles = a_styles; endResetModel(); } // END OF void LogStylesModel::setStyles( // const std::vector & a_styles) //============================================================================== TextBlockStyle LogStylesModel::style(int a_index) const { Q_ASSERT(styleIndexValid(a_index)); return m_styles[a_index]; } // END OF TextBlockStyle LogStylesModel::style(int a_index) const //============================================================================== TextBlockStyle LogStylesModel::style(const QModelIndex & a_index) const { return style(a_index.row()); } // END OF TextBlockStyle LogStylesModel::style(const QModelIndex & a_index) // const //============================================================================== bool LogStylesModel::setStyleFont(int a_index, const QFont & a_font) { if(!styleIndexValid(a_index)) return false; m_styles[a_index].textFormat.setFont(a_font); return true; } // END OF bool LogStylesModel::setStyleFont(int a_index, const QFont & a_font) //============================================================================== bool LogStylesModel::setStyleTextColor(int a_index, const QColor & a_color) { if(!styleIndexValid(a_index)) return false; m_styles[a_index].textFormat.setForeground(a_color); return true; } // END OF bool LogStylesModel::setStyleTextColor(int a_index, // const QColor & a_color) //============================================================================== bool LogStylesModel::setStyleBackgroundColor(int a_index, const QColor & a_color) { if(!styleIndexValid(a_index)) return false; m_styles[a_index].textFormat.setBackground(a_color); return true; } // END OF bool LogStylesModel::setStyleBackgroundColor(int a_index, // const QColor & a_color) //============================================================================== bool LogStylesModel::styleIndexValid(int a_index) const { return ((a_index >= 0) && (a_index < (int)m_styles.size())); } // END OF bool LogStylesModel::styleIndexValid(int a_index) const //============================================================================== ================================================ FILE: common-src/log/log_styles_model.h ================================================ #ifndef LOG_STYLES_MODEL_H_INCLUDED #define LOG_STYLES_MODEL_H_INCLUDED #include "styled_log_view_structures.h" #include class LogStylesModel : public QAbstractItemModel { Q_OBJECT public: LogStylesModel(QObject * a_pParent = nullptr); virtual ~LogStylesModel(); virtual QModelIndex index(int a_row, int a_column, const QModelIndex & a_parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex & a_child) const override; virtual Qt::ItemFlags flags(const QModelIndex & a_index) const override; virtual QVariant data(const QModelIndex & a_index, int a_role = Qt::DisplayRole) const override; virtual int rowCount(const QModelIndex & a_parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex & a_parent = QModelIndex()) const override; virtual bool setData(const QModelIndex & a_index, const QVariant & a_value, int a_role = Qt::EditRole) override; virtual std::vector styles() const; virtual void setStyles(const std::vector & a_styles); virtual TextBlockStyle style(int a_index) const; virtual TextBlockStyle style(const QModelIndex & a_index) const; virtual bool setStyleFont(int a_index, const QFont & a_font); virtual bool setStyleTextColor(int a_index, const QColor & a_color); virtual bool setStyleBackgroundColor(int a_index, const QColor & a_color); protected: virtual bool styleIndexValid(int a_index) const; std::vector m_styles; }; #endif // LOG_STYLES_MODEL_H_INCLUDED ================================================ FILE: common-src/log/styled_log_view.cpp ================================================ #include "styled_log_view.h" #include "styled_log_view_settings_dialog.h" #include #include #include #include #include //============================================================================== const size_t StyledLogView::DEFAULT_MAX_ENTRIES_TO_SHOW = 200; //============================================================================== StyledLogView::StyledLogView(QWidget * a_pParent) : QTextEdit(a_pParent) , m_millisecondsToDivideBlocks(2000) , m_pContextMenu(nullptr) , m_pSettingsDialog(nullptr) , m_maxEntriesToShow(DEFAULT_MAX_ENTRIES_TO_SHOW) { setReadOnly(true); setContextMenuPolicy(Qt::CustomContextMenu); addStyle(TextBlockStyle(LOG_STYLE_DEFAULT)); createActionsAndMenus(); m_pSettingsDialog = new StyledLogViewSettingsDialog(); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotShowCustomMenu(const QPoint &))); connect(m_pSettingsDialog, SIGNAL(signalSettingsChanged()), this, SLOT(slotLogSettingsChanged())); } // END OF StyledLogView::StyledLogView(QWidget * a_pParent) //============================================================================== StyledLogView::~StyledLogView() { delete m_pSettingsDialog; } // END OF StyledLogView::~StyledLogView() //============================================================================== TextBlockStyle StyledLogView::getStyle(const QString & a_styleName) const { TextBlockStyle style(a_styleName); QString styleName = a_styleName; QStringList foundAliasesList; std::vector::const_iterator it = m_styles.end(); while(true) { it = std::find_if(m_styles.begin(), m_styles.end(), [&](const TextBlockStyle & a_style) -> bool { return (a_style.name == styleName); }); if(it == m_styles.end()) return style; // Alias retains its own visibility and title if(it->name == a_styleName) { style.isVisible = it->isVisible; style.title = it->title; } if(!it->isAlias) { style.textFormat = it->textFormat; return style; } foundAliasesList += it->name; // Check for aliasing loop if(foundAliasesList.contains(it->originalStyleName)) return style; styleName = it->originalStyleName; } return style; } // END OF TextBlockStyle StyledLogView::getStyle( // const QString & a_styleName) const //============================================================================== void StyledLogView::addStyle(const TextBlockStyle & a_style, bool a_updateExisting) { TextBlockStyle newStyle(a_style); // Resolve original style for alias to prevent alias loop if(newStyle.isAlias) { TextBlockStyle originalStyle = getStyle(newStyle.originalStyleName); newStyle.originalStyleName = originalStyle.name; } std::vector::iterator it = std::find_if(m_styles.begin(), m_styles.end(), [&](const TextBlockStyle & la_style) -> bool { return (la_style.name == newStyle.name); }); if(it == m_styles.end()) m_styles.push_back(newStyle); else if(a_updateExisting) *it = newStyle; } // END OF void StyledLogView::addStyle(const TextBlockStyle & a_style, // bool a_updateExisting) //============================================================================== void StyledLogView::addStyle(const QString & a_aliasName, const QString & a_title, const QString & a_originalStyleName) { // no aliasing default style name if(a_aliasName == LOG_STYLE_DEFAULT) return; addStyle(TextBlockStyle(a_aliasName, a_title, a_originalStyleName)); } // END OF void StyledLogView::addStyle(const QString & a_aliasName, // const QString & a_title, const QString & a_originalStyleName) //============================================================================== void StyledLogView::startNewBlock() { if(m_entries.empty()) return; if(m_entries.back().isDivider) return; m_entries.push_back(LogEntry::divider()); } // END OF void StyledLogView::startNewBlock() //============================================================================== qint64 StyledLogView::millisecondsToDivideBlocks() const { return m_millisecondsToDivideBlocks; } // END OF qint64 StyledLogView::millisecondsToDivideBlocks() const //============================================================================== bool StyledLogView::setMillisecondsToDivideBlocks(qint64 a_value) { if(a_value < 0) return false; m_millisecondsToDivideBlocks = a_value; updateHtml(); return true; } // END OF bool StyledLogView::setMillisecondsToDivideBlocks(qint64 a_value) //============================================================================== QStringList StyledLogView::styles(bool a_excludeAliases) const { QStringList stylesList; for(const TextBlockStyle & style : m_styles) if(!(a_excludeAliases && style.isAlias)) stylesList += style.name; return stylesList; } // END OF QStringList StyledLogView::styles(bool a_excludeAliases) const //============================================================================== bool StyledLogView::saveHtml(const QString & a_filePath, bool a_excludeFiltered) { if(a_filePath.isEmpty()) return false; QFile file(a_filePath); bool result = file.open(QIODevice::WriteOnly); if(!result) return false; QByteArray htmlData = realHtml(a_excludeFiltered).toUtf8(); qint64 bytesWritten = file.write(htmlData); file.close(); if(bytesWritten != htmlData.size()) return false; return true; } // END OF bool StyledLogView::saveHtml(const QString & a_filePath, // bool a_excludeFiltered) //============================================================================== bool StyledLogView::saveHtml(bool a_excludeFiltered) { QString timeString = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss-zzz"); QString fileName = tr("vapoursynth_editor_log_{time}.html") .replace("{time}", timeString); QString currentDir = QDir(".").absolutePath(); QString filePath = currentDir + QString("/") + fileName; filePath = QFileDialog::getSaveFileName(this, tr("Save log"), filePath, tr("HTML files (*.html);;All files (*.*)")); return saveHtml(filePath, a_excludeFiltered); } // END OF bool StyledLogView::saveHtml(bool a_excludeFiltered) //============================================================================== void StyledLogView::addEntry(const QString & a_text, const QString & a_style) { m_entries.push_back(LogEntry(a_text, a_style)); updateHtml(); } // END OF void StyledLogView::addEntry(const QString & a_text, // const QString & a_style) //============================================================================== void StyledLogView::addEntry(const LogEntry & a_entry) { m_entries.push_back(a_entry); updateHtml(); } // END OF void void StyledLogView::addEntry(const LogEntry & a_entry) //============================================================================== void StyledLogView::clear() { m_entries.clear(); QTextEdit::clear(); } // END OF void StyledLogView::clear() //============================================================================== void StyledLogView::slotSaveHtml() { saveHtml(); } // END OF void StyledLogView::slotSaveHtml() //============================================================================== void StyledLogView::slotSaveHtmlFiltered() { saveHtml(true); } // END OF void StyledLogView::slotSaveHtmlFiltered() //============================================================================== void StyledLogView::slotShowCustomMenu(const QPoint & a_position) { createActionsAndMenus(); QPoint globalPosition = mapToGlobal(a_position); m_pContextMenu->popup(globalPosition); } // END OF void StyledLogView::slotShowCustomMenu(const QPoint & a_position) //============================================================================== void StyledLogView::slotLogSettings() { Q_ASSERT(m_pSettingsDialog); m_pSettingsDialog->setStyles(m_styles); m_pSettingsDialog->show(); } // END OF void StyledLogView::slotLogSettings() //============================================================================== void StyledLogView::slotLogSettingsChanged() { m_styles = m_pSettingsDialog->styles(); updateHtml(); } // END OF void StyledLogView::slotLogSettingsChanged() //============================================================================== void StyledLogView::updateHtml() { if(m_entries.empty()) { QTextEdit::clear(); return; } QString borderColor = palette().color(QPalette::Dark).name(); QString html = QString( "\n" "\n" "\n" ).replace("{table-border-color}", borderColor); bool openBlock = false; QDateTime lastTime; QString lastStyle; size_t firstEntryToShow = 0; if(m_entries.size() > m_maxEntriesToShow) { firstEntryToShow = m_entries.size() - m_maxEntriesToShow; html += tr("\n").arg(firstEntryToShow); } for(size_t i = firstEntryToShow; i < m_entries.size(); ++i) { const LogEntry & entry = m_entries[i]; TextBlockStyle style = getStyle(entry.style); if(!style.isVisible) continue; if(openBlock) { if(entry.isDivider || (lastTime.msecsTo(entry.time) > m_millisecondsToDivideBlocks) || (entry.style != lastStyle)) { html += QString("\n"); openBlock = false; } } lastStyle = entry.style; lastTime = entry.time; if(entry.isDivider) continue; QTextCharFormat format = style.textFormat; QFont styleFont = format.font(); if(!openBlock) { html += QString("\n"); html += QString("
%1 entries not shown. " "Save the log to read.
") .arg(format.background().color().name()); QString timeString = entry.time.toString("yyyy-MM-dd hh:mm:ss.zzz"); html += QString("
%2
") .arg(format.foreground().color().name()) .arg(timeString); openBlock = true; } QString entryHtml = entry.text; entryHtml.replace("\n", "
"); if(styleFont.bold()) entryHtml = QString("%1").arg(entryHtml); if(styleFont.italic()) entryHtml = QString("%1").arg(entryHtml); if(styleFont.underline()) entryHtml = QString("%1").arg(entryHtml); if(styleFont.strikeOut()) entryHtml = QString("%1").arg(entryHtml); html += QString("
%3
") .arg(styleFont.family()) .arg(format.foreground().color().name()) .arg(entryHtml); } if(openBlock) html += QString("
\n"); setHtml(html); verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } // END OF void StyledLogView::updateHtml() //============================================================================== void StyledLogView::createActionsAndMenus() { if(m_pContextMenu) delete m_pContextMenu; m_pContextMenu = createStandardContextMenu(); struct ActionToCreate { QString title; const char * slotToConnect; bool isSeparator; ActionToCreate(const QString & a_title, const char * a_slotToConnect) : title(a_title), slotToConnect(a_slotToConnect), isSeparator(false) {}; ActionToCreate() : title(), slotToConnect(nullptr), isSeparator(true) {}; }; const ActionToCreate SEPARATOR; ActionToCreate actionsToCreate[] = { SEPARATOR, {tr("Log settings"), SLOT(slotLogSettings())}, SEPARATOR, {tr("Save"), SLOT(slotSaveHtml())}, {tr("Save filtered"), SLOT(slotSaveHtmlFiltered())}, {tr("Clear"), SLOT(clear())}, }; for(const ActionToCreate & action : actionsToCreate) { if(action.isSeparator) m_pContextMenu->addSeparator(); else m_pContextMenu->addAction(action.title, this, action.slotToConnect); } } // END OF void StyledLogView::createActionsAndMenus() //============================================================================== QString StyledLogView::realHtml(bool a_excludeFiltered) const { QString title = tr("VapourSynth Editor log ") + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); QString borderColor = palette().color(QPalette::Dark).name(); QString styleText = QString( "\n"; QString html = QString( "\n" "\n" "\n" "{title}\n" "{style}" "\n" "\n" "\n" ).replace("{title}", title) .replace("{style}", styleText); bool openBlock = false; QDateTime lastTime; QString lastStyle; for(const LogEntry & entry : m_entries) { TextBlockStyle style = getStyle(entry.style); if(a_excludeFiltered && (!style.isVisible)) continue; if(openBlock) { if(entry.isDivider || (lastTime.msecsTo(entry.time) > m_millisecondsToDivideBlocks) || (entry.style != lastStyle)) { html += QString("\n"); openBlock = false; } } lastStyle = entry.style; lastTime = entry.time; if(entry.isDivider) continue; QTextCharFormat format = style.textFormat; QFont styleFont = format.font(); if(!openBlock) { html += QString("\n\n"); html += "
\n") .arg(format.background().color().name()); QString timeString = entry.time.toString("yyyy-MM-dd hh:mm:ss.zzz"); html += QString("

%2

\n") .arg(format.foreground().color().name()) .arg(timeString); openBlock = true; } QString entryHtml = entry.text; entryHtml.replace("\n", "
\n"); if(styleFont.bold()) entryHtml = QString("%1").arg(entryHtml); if(styleFont.italic()) entryHtml = QString("%1").arg(entryHtml); if(styleFont.underline()) entryHtml = QString("%1").arg(entryHtml); if(styleFont.strikeOut()) entryHtml = QString("%1").arg(entryHtml); html += QString("

%3

\n") .arg(styleFont.family()) .arg(format.foreground().color().name()) .arg(entryHtml); } if(openBlock) html += QString("
\n\n\n"; return html; } // END OF QString StyledLogView::realHtml(bool a_excludeFiltered) const //============================================================================== ================================================ FILE: common-src/log/styled_log_view.h ================================================ #ifndef STYLED_LOG_VIEW_H_INCLUDED #define STYLED_LOG_VIEW_H_INCLUDED #include "styled_log_view_structures.h" #include #include class StyledLogViewSettingsDialog; class StyledLogView : public QTextEdit { Q_OBJECT public: static const size_t DEFAULT_MAX_ENTRIES_TO_SHOW; StyledLogView(QWidget * a_pParent = nullptr); virtual ~StyledLogView(); virtual TextBlockStyle getStyle(const QString & a_styleName) const; virtual void addStyle(const TextBlockStyle & a_style, bool a_updateExisting = true); virtual void addStyle(const QString & a_aliasName, const QString & a_title, const QString & a_originalStyleName = LOG_STYLE_DEFAULT); virtual void startNewBlock(); virtual qint64 millisecondsToDivideBlocks() const; virtual bool setMillisecondsToDivideBlocks(qint64 a_value); virtual QStringList styles(bool a_excludeAliases = false) const; virtual bool saveHtml(const QString & a_filePath, bool a_excludeFiltered = false); virtual bool saveHtml(bool a_excludeFiltered = false); public slots: virtual void addEntry(const QString & a_text, const QString & a_style = LOG_STYLE_DEFAULT); virtual void addEntry(const LogEntry & a_entry); virtual void clear(); virtual void slotSaveHtml(); virtual void slotSaveHtmlFiltered(); protected slots: virtual void slotShowCustomMenu(const QPoint & a_position); virtual void slotLogSettings(); virtual void slotLogSettingsChanged(); protected: virtual void updateHtml(); virtual void createActionsAndMenus(); virtual QString realHtml(bool a_excludeFiltered = false) const; std::vector m_styles; std::vector m_entries; qint64 m_millisecondsToDivideBlocks; QMenu * m_pContextMenu; StyledLogViewSettingsDialog * m_pSettingsDialog; size_t m_maxEntriesToShow; }; #endif // STYLED_LOG_VIEW_H_INCLUDED ================================================ FILE: common-src/log/styled_log_view_core.cpp ================================================ #include "styled_log_view_core.h" #include //============================================================================== const char LOG_STYLE_DEFAULT[] = "info"; const char LOG_STYLE_TITLE_DEFAULT[] = "Info message"; const char LE_IS_DIVIDER[] = "isDivider"; const char LE_TIME[] = "time"; const char LE_TEXT[] = "text"; const char LE_STYLE[] = "style"; //============================================================================== LogEntry::LogEntry(bool a_isDivider, const QString & a_text, const QString & a_style) : isDivider(a_isDivider) , time(QDateTime::currentDateTime()) , text(a_text) , style(a_style) { } // END OF LogEntry::LogEntry(bool a_isDivider, const QString & a_text, // const QString & a_style) //============================================================================== LogEntry::LogEntry(const QString & a_text, const QString & a_style) : isDivider(false) , time(QDateTime::currentDateTime()) , text(a_text) , style(a_style) { } // END OF LogEntry::LogEntry(const QString & a_text, const QString & a_style) //============================================================================== LogEntry LogEntry::divider() { return LogEntry(true, QString(), QString()); } // END OF LogEntry LogEntry::divider() //============================================================================== QJsonObject LogEntry::toJson() const { QJsonObject jsLogEntry; jsLogEntry[LE_IS_DIVIDER] = isDivider; jsLogEntry[LE_TIME] = time.toMSecsSinceEpoch(); if(isDivider) return jsLogEntry; jsLogEntry[LE_TEXT] = text; jsLogEntry[LE_STYLE] = style; return jsLogEntry; } // END OF QJsonObject LogEntry::toJson() const //============================================================================== LogEntry LogEntry::fromJson(const QJsonObject & a_object) { LogEntry entry; if(a_object.contains(LE_IS_DIVIDER)) entry.isDivider = a_object[LE_IS_DIVIDER].toBool(); if(a_object.contains(LE_TIME)) entry.time = QDateTime::fromMSecsSinceEpoch( a_object[LE_TIME].toVariant().toLongLong()); if(a_object.contains(LE_TEXT)) entry.text = a_object[LE_TEXT].toString(); if(a_object.contains(LE_STYLE)) entry.style = a_object[LE_STYLE].toString(); return entry; } // END OF LogEntry LogEntry::fromJson(const QJsonObject & a_object) //============================================================================== ================================================ FILE: common-src/log/styled_log_view_core.h ================================================ #ifndef STYLED_LOG_VIEW_CORE_H_INCLUDED #define STYLED_LOG_VIEW_CORE_H_INCLUDED #include #include #include //============================================================================== extern const char LOG_STYLE_DEFAULT[]; extern const char LOG_STYLE_TITLE_DEFAULT[]; extern const char LE_IS_DIVIDER[]; extern const char LE_TIME[]; extern const char LE_TEXT[]; extern const char LE_STYLE[]; //============================================================================== struct LogEntry { bool isDivider; QDateTime time; QString text; QString style; LogEntry(bool a_isDivider = false, const QString & a_text = QString(), const QString & a_style = LOG_STYLE_DEFAULT); LogEntry(const QString & a_text, const QString & a_style = LOG_STYLE_DEFAULT); static LogEntry divider(); QJsonObject toJson() const; static LogEntry fromJson(const QJsonObject & a_object); }; //============================================================================== #endif // STYLED_LOG_VIEW_CORE_H_INCLUDED ================================================ FILE: common-src/log/styled_log_view_settings_dialog.cpp ================================================ #include "styled_log_view_settings_dialog.h" #include "log_styles_model.h" #include #include #include //============================================================================== StyledLogViewSettingsDialog::StyledLogViewSettingsDialog(QWidget * a_pParent): QDialog(a_pParent, Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint) , m_pLogStylesModel(nullptr) { m_ui.setupUi(this); m_pLogStylesModel = new LogStylesModel(this); m_ui.stylesView->setModel(m_pLogStylesModel); connect(m_ui.okButton, SIGNAL(clicked()), this, SLOT(slotOk())); connect(m_ui.applyButton, SIGNAL(clicked()), this, SLOT(slotApply())); connect(m_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_ui.stylesView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotStyleSelected(const QModelIndex &))); connect(m_ui.fontButton, SIGNAL(clicked()), this, SLOT(slotFontButtonClicked())); connect(m_ui.textColorButton, SIGNAL(clicked()), this, SLOT(slotTextColorButtonClicked())); connect(m_ui.backgroundColorButton, SIGNAL(clicked()), this, SLOT(slotBackgroundColorButtonClicked())); } // END OF StyledLogViewSettingsDialog::StyledLogViewSettingsDialog( // QWidget * a_pParent) //============================================================================== StyledLogViewSettingsDialog::~StyledLogViewSettingsDialog() { } // END OF StyledLogViewSettingsDialog::~StyledLogViewSettingsDialog() //============================================================================== std::vector StyledLogViewSettingsDialog::styles() const { Q_ASSERT(m_pLogStylesModel); return m_pLogStylesModel->styles(); } // END OF std::vector StyledLogViewSettingsDialog::styles() // const //============================================================================== void StyledLogViewSettingsDialog::setStyles( const std::vector & a_styles) { Q_ASSERT(m_pLogStylesModel); m_pLogStylesModel->setStyles(a_styles); m_ui.stylesView->resizeRowsToContents(); } // END OF void StyledLogViewSettingsDialog::setStyles( // const std::vector & a_styles) //============================================================================== void StyledLogViewSettingsDialog::slotOk() { slotApply(); close(); } // END OF void StyledLogViewSettingsDialog::slotOk() //============================================================================== void StyledLogViewSettingsDialog::slotApply() { emit signalSettingsChanged(); } // END OF void StyledLogViewSettingsDialog::slotApply() //============================================================================== void StyledLogViewSettingsDialog::slotStyleSelected(const QModelIndex & a_index) { TextBlockStyle style = m_pLogStylesModel->style(a_index); QTextCharFormat format = style.textFormat; enableStyleSettingsControls(!style.isAlias); QPalette palette = QGuiApplication::palette(); QString textColorString = style.isAlias ? palette.color(QPalette::Disabled, QPalette::WindowText).name() : format.foreground().color().name(); QString backgroundColorString = style.isAlias ? palette.color(QPalette::Disabled, QPalette::Window).name() : format.background().color().name(); QString styleSheetString = QString("QLabel {background-color : \"%1\"; color : \"%2\";}") .arg(backgroundColorString).arg(textColorString); m_ui.fontLabel->setStyleSheet(styleSheetString); if(style.isAlias) { m_ui.fontLabel->clear(); } else { m_ui.fontLabel->setFont(format.font()); m_ui.fontLabel->setText(format.font().family()); } } // END OF void StyledLogViewSettingsDialog::slotStyleSelected( // const QModelIndex & a_index) //============================================================================== void StyledLogViewSettingsDialog::slotFontButtonClicked() { QModelIndex index = m_ui.stylesView->currentIndex(); if(!index.isValid()) return; TextBlockStyle style = m_pLogStylesModel->style(index); QFontDialog fontDialog; fontDialog.setCurrentFont(style.textFormat.font()); int returnCode = fontDialog.exec(); if(returnCode == QDialog::Rejected) return; QFont newFont = fontDialog.selectedFont(); m_pLogStylesModel->setStyleFont(index.row(), newFont); slotStyleSelected(index); } // END OF void StyledLogViewSettingsDialog::slotFontButtonClicked() //============================================================================== void StyledLogViewSettingsDialog::slotTextColorButtonClicked() { QModelIndex index = m_ui.stylesView->currentIndex(); if(!index.isValid()) return; TextBlockStyle style = m_pLogStylesModel->style(index); QColorDialog colorDialog; colorDialog.setCurrentColor(style.textFormat.foreground().color()); int returnCode = colorDialog.exec(); if(returnCode == QDialog::Rejected) return; QColor newColor = colorDialog.selectedColor(); m_pLogStylesModel->setStyleTextColor(index.row(), newColor); slotStyleSelected(index); } // END OF void StyledLogViewSettingsDialog::slotTextColorButtonClicked() //============================================================================== void StyledLogViewSettingsDialog::slotBackgroundColorButtonClicked() { QModelIndex index = m_ui.stylesView->currentIndex(); if(!index.isValid()) return; TextBlockStyle style = m_pLogStylesModel->style(index); QColorDialog colorDialog; colorDialog.setCurrentColor(style.textFormat.background().color()); int returnCode = colorDialog.exec(); if(returnCode == QDialog::Rejected) return; QColor newColor = colorDialog.selectedColor(); m_pLogStylesModel->setStyleBackgroundColor(index.row(), newColor); slotStyleSelected(index); } // END OF void StyledLogViewSettingsDialog::slotBackgroundColorButtonClicked() //============================================================================== void StyledLogViewSettingsDialog::enableStyleSettingsControls(bool a_enable) { QWidget * widgetsToEnable[] = {m_ui.fontButton, m_ui.textColorButton, m_ui.backgroundColorButton, m_ui.fontLabel}; for(QWidget * pWidget : widgetsToEnable) pWidget->setEnabled(a_enable); } // END OF void StyledLogViewSettingsDialog::enableStyleSettingsControls( // bool a_enable) //============================================================================== ================================================ FILE: common-src/log/styled_log_view_settings_dialog.h ================================================ #ifndef STYLED_LOG_VIEW_SETTINGS_DIALOG_H_INCLUDED #define STYLED_LOG_VIEW_SETTINGS_DIALOG_H_INCLUDED #include #include "styled_log_view_structures.h" class LogStylesModel; class StyledLogViewSettingsDialog : public QDialog { Q_OBJECT public: StyledLogViewSettingsDialog(QWidget * a_pParent = nullptr); virtual ~StyledLogViewSettingsDialog(); virtual std::vector styles() const; virtual void setStyles(const std::vector & a_styles); signals: void signalSettingsChanged(); protected slots: virtual void slotOk(); virtual void slotApply(); virtual void slotStyleSelected(const QModelIndex & a_index); virtual void slotFontButtonClicked(); virtual void slotTextColorButtonClicked(); virtual void slotBackgroundColorButtonClicked(); protected: virtual void enableStyleSettingsControls(bool a_enable = true); Ui::StyledLogViewSettingsDialog m_ui; LogStylesModel * m_pLogStylesModel; }; #endif // STYLED_LOG_VIEW_SETTINGS_DIALOG_H_INCLUDED ================================================ FILE: common-src/log/styled_log_view_settings_dialog.ui ================================================ StyledLogViewSettingsDialog 0 0 306 497 Log settings 4 2 2 2 2 true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows false true false 0 0 QFrame::Box 4 Font Text color Background color 4 OK Apply Cancel true ================================================ FILE: common-src/log/styled_log_view_structures.cpp ================================================ #include "styled_log_view_structures.h" #include #include //============================================================================== TextBlockStyle::TextBlockStyle(const QString & a_name, const QString & a_title): name(a_name) , title(a_title) , isAlias(false) , isVisible(true) { QPalette palette = QGuiApplication::palette(); textFormat.setBackground(palette.color(QPalette::Active, QPalette::Base)); textFormat.setForeground(palette.color(QPalette::Active, QPalette::Text)); } // END OFTextBlockStyle::TextBlockStyle(const QString & a_name, // const QString & a_title) //============================================================================== TextBlockStyle::TextBlockStyle(const QString & a_name, const QString & a_title, const QTextCharFormat & a_textFormat): name(a_name) , title(a_title) , textFormat(a_textFormat) , isAlias(false) , isVisible(true) { } // END OF TextBlockStyle::TextBlockStyle(const QString & a_name, // const QString & a_title, const QTextCharFormat & a_textFormat) //============================================================================== TextBlockStyle::TextBlockStyle(const QString & a_name, const QString & a_title, const QColor & a_backgroundColor, const QColor & a_textColor): name(a_name) , title(a_title) , isAlias(false) , isVisible(true) { textFormat.setForeground(a_textColor); textFormat.setBackground(a_backgroundColor); } // END OF TextBlockStyle::TextBlockStyle(const QString & a_name, // const QString & a_title, const QColor & a_backgroundColor, // const QColor & a_textColor) //============================================================================== TextBlockStyle::TextBlockStyle(const QString & a_aliasName, const QString & a_title, const QString & a_originalStyleName): name(a_aliasName) , title(a_title) , isAlias(true) , originalStyleName(a_originalStyleName) , isVisible(true) { } // END OF TextBlockStyle::TextBlockStyle(const QString & a_aliasName, // const QString & a_title, const QString & a_originalStyleName) //============================================================================== ================================================ FILE: common-src/log/styled_log_view_structures.h ================================================ #ifndef STYLED_LOG_VIEW_STRUCTURES_H_INCLUDED #define STYLED_LOG_VIEW_STRUCTURES_H_INCLUDED #include "styled_log_view_core.h" #include #include //============================================================================== struct TextBlockStyle { QString name; QString title; QTextCharFormat textFormat; bool isAlias; QString originalStyleName; bool isVisible; TextBlockStyle(const QString & a_name = LOG_STYLE_DEFAULT, const QString & a_title = LOG_STYLE_TITLE_DEFAULT); TextBlockStyle(const QString & a_name, const QString & a_title, const QTextCharFormat & a_textFormat); TextBlockStyle(const QString & a_name, const QString & a_title, const QColor & a_backgroundColor, const QColor & a_textColor); TextBlockStyle(const QString & a_aliasName, const QString & a_title, const QString & a_originalStyleName); }; //============================================================================== #endif // STYLED_LOG_VIEW_STRUCTURES_H_INCLUDED ================================================ FILE: common-src/log/vs_editor_log.cpp ================================================ #include "vs_editor_log.h" #include "../settings/settings_manager.h" //============================================================================== VSEditorLog::VSEditorLog(QWidget * a_pParent) : StyledLogView(a_pParent) , m_pSettingsManager(nullptr) { initializeStyles(); } // END OF VSEditorLog::VSEditorLog(QWidget * a_pParent) //============================================================================== VSEditorLog::~VSEditorLog() { } // END OF VSEditorLog::~VSEditorLog() //============================================================================== QString VSEditorLog::name() const { return m_name; } // END OF QString VSEditorLog::name() const //============================================================================== void VSEditorLog::setName(const QString & a_name) { m_name = a_name; } // END OF void VSEditorLog::setName(const QString & a_name) //============================================================================== void VSEditorLog::setSettingsManager(SettingsManager * a_pSettingsManager) { m_pSettingsManager = a_pSettingsManager; } // END OF void VSEditorLog::setSettingsManager( // SettingsManager * a_pSettingsManager) //============================================================================== bool VSEditorLog::loadSettings() { if(!m_pSettingsManager) return false; if(m_name.isEmpty()) return false; const std::vector styles = m_pSettingsManager->getLogStyles(m_name); for(TextBlockStyle & style : m_styles) { std::vector::const_iterator it = std::find_if(styles.begin(), styles.end(), [&](const TextBlockStyle & a_style) -> bool { return (a_style.name == style.name); }); if(it != styles.end()) style = *it; } return true; } // END OF bool VSEditorLog::loadSettings() //============================================================================== bool VSEditorLog::saveSettings() { if(!m_pSettingsManager) return false; if(m_name.isEmpty()) return false; return m_pSettingsManager->setLogStyles(m_name, m_styles); } // END OF bool VSEditorLog::saveSettings() //============================================================================== void VSEditorLog::slotLogSettingsChanged() { StyledLogView::slotLogSettingsChanged(); saveSettings(); } // END OF void VSEditorLog::slotLogSettingsChanged() //============================================================================== void VSEditorLog::initializeStyles() { TextBlockStyle stylesToCreate[] = { {LOG_STYLE_ERROR, tr("Error"), QColor("#ffeeee"), Qt::darkRed}, {LOG_STYLE_DEBUG, tr("Debug message"), palette().color(QPalette::Active, QPalette::Base), palette().color(QPalette::Active, QPalette::Dark)}, {LOG_STYLE_WARNING, tr("Warning"), QColor("#eeeeff"), Qt::darkBlue}, {LOG_STYLE_POSITIVE, tr("Positive message"), QColor("#eeffee"), Qt::darkGreen}, // Aliases {LOG_STYLE_VS_DEBUG, tr("VapourSynth debug message"), LOG_STYLE_DEBUG}, {LOG_STYLE_VS_WARNING, tr("VapourSynth warning"), LOG_STYLE_WARNING}, {LOG_STYLE_VS_CRITICAL, tr("VapourSynth error"), LOG_STYLE_ERROR}, {LOG_STYLE_VS_FATAL, tr("VapourSynth fatal error"), LOG_STYLE_ERROR}, {LOG_STYLE_QT_DEBUG, tr("Qt debug message"), LOG_STYLE_DEBUG}, {LOG_STYLE_QT_INFO, tr("Qt info"), LOG_STYLE_DEFAULT}, {LOG_STYLE_QT_WARNING, tr("Qt warning"), LOG_STYLE_WARNING}, {LOG_STYLE_QT_CRITICAL, tr("Qt critical error"), LOG_STYLE_ERROR}, {LOG_STYLE_QT_FATAL, tr("Qt fatal error"), LOG_STYLE_ERROR}, }; for(TextBlockStyle & styleToCreate : stylesToCreate) addStyle(styleToCreate); } // END OF void VSEditorLog::initializeStyles() //============================================================================== ================================================ FILE: common-src/log/vs_editor_log.h ================================================ #ifndef VS_EDITOR_LOG_H_INCLUDED #define VS_EDITOR_LOG_H_INCLUDED #include "styled_log_view.h" #include "vs_editor_log_definitions.h" class SettingsManager; class VSEditorLog : public StyledLogView { Q_OBJECT public: VSEditorLog(QWidget * a_pParent = nullptr); virtual ~VSEditorLog(); virtual QString name() const; virtual void setName(const QString & a_name); virtual void setSettingsManager(SettingsManager * a_pSettingsManager); virtual bool loadSettings(); virtual bool saveSettings(); protected slots: virtual void slotLogSettingsChanged() override; protected: virtual void initializeStyles(); QString m_name; SettingsManager * m_pSettingsManager; }; #endif // VS_EDITOR_LOG_H_INCLUDED ================================================ FILE: common-src/log/vs_editor_log_definitions.cpp ================================================ #include "vs_editor_log_definitions.h" #include "styled_log_view_core.h" #include #include #include //============================================================================== const char LOG_STYLE_ERROR[] = "error"; const char LOG_STYLE_DEBUG[] = "debug"; const char LOG_STYLE_WARNING[] = "warning"; const char LOG_STYLE_POSITIVE[] = "positive"; const char LOG_STYLE_VS_DEBUG[] = "vs_debug"; const char LOG_STYLE_VS_INFO[] = "vs_info"; const char LOG_STYLE_VS_WARNING[] = "vs_warning"; const char LOG_STYLE_VS_CRITICAL[] = "vs_critical"; const char LOG_STYLE_VS_FATAL[] = "vs_fatal"; const char LOG_STYLE_QT_DEBUG[] = "qt_debug"; const char LOG_STYLE_QT_INFO[] = "qt_info"; const char LOG_STYLE_QT_WARNING[] = "qt_warning"; const char LOG_STYLE_QT_CRITICAL[] = "qt_critical"; const char LOG_STYLE_QT_FATAL[] = "qt_fatal"; //============================================================================== QString vsMessageTypeToStyleName(int a_messageType) { QString style(LOG_STYLE_DEFAULT); static std::map vsTypeToStyleMap = { {mtDebug, LOG_STYLE_VS_DEBUG}, {mtInformation, LOG_STYLE_VS_INFO}, {mtWarning, LOG_STYLE_VS_WARNING}, {mtCritical, LOG_STYLE_VS_CRITICAL}, {mtFatal, LOG_STYLE_VS_FATAL}, }; std::map::const_iterator it = vsTypeToStyleMap.find(a_messageType); if(it != vsTypeToStyleMap.end()) return it->second; return style; } // END OF QString vsMessageTypeToStyleName(int a_messageType) //============================================================================== ================================================ FILE: common-src/log/vs_editor_log_definitions.h ================================================ #ifndef VS_EDITOR_LOG_DEFINITIONS_H_INCLUDED #define VS_EDITOR_LOG_DEFINITIONS_H_INCLUDED #include extern const char LOG_STYLE_ERROR[]; extern const char LOG_STYLE_DEBUG[]; extern const char LOG_STYLE_WARNING[]; extern const char LOG_STYLE_POSITIVE[]; extern const char LOG_STYLE_VS_DEBUG[]; extern const char LOG_STYLE_VS_INFO[]; extern const char LOG_STYLE_VS_WARNING[]; extern const char LOG_STYLE_VS_CRITICAL[]; extern const char LOG_STYLE_VS_FATAL[]; extern const char LOG_STYLE_QT_DEBUG[]; extern const char LOG_STYLE_QT_INFO[]; extern const char LOG_STYLE_QT_WARNING[]; extern const char LOG_STYLE_QT_CRITICAL[]; extern const char LOG_STYLE_QT_FATAL[]; QString vsMessageTypeToStyleName(int a_messageType); #endif // VS_EDITOR_LOG_DEFINITIONS_H_INCLUDED ================================================ FILE: common-src/settings/settings_definitions.cpp ================================================ #include "settings_definitions.h" //============================================================================== const bool DEFAULT_MAIN_WINDOW_MAXIMIZED = false; const bool DEFAULT_PREVIEW_DIALOG_MAXIMIZED = false; const bool DEFAULT_JOBS_DIALOG_MAXIMIZED = false; const bool DEFAULT_JOB_SERVER_WATCHER_MAXIMIZED = false; const bool DEFAULT_AUTO_LOAD_LAST_SCRIPT = true; const bool DEFAULT_ZOOM_PANEL_VISIBLE = true; const ZoomMode DEFAULT_ZOOM_MODE = ZoomMode::NoZoom; const double DEFAULT_ZOOM_RATIO = 2.0; const Qt::TransformationMode DEFAULT_SCALE_MODE = Qt::FastTransformation; const CropMode DEFAULT_CROP_MODE = CropMode::Relative; const int DEFAULT_CROP_ZOOM_RATIO = 1; const bool DEFAULT_PROMPT_TO_SAVE_CHANGES = true; const unsigned int DEFAULT_MAX_RECENT_FILES_NUMBER = 10; const int DEFAULT_CHARACTERS_TYPED_TO_START_COMPLETION = 2; const double DEFAULT_TIME_STEP = 5.0; const TimeLineSlider::DisplayMode DEFAULT_TIMELINE_MODE = TimeLineSlider::DisplayMode::Time; const bool DEFAULT_COLOR_PICKER_VISIBLE = false; const PlayFPSLimitMode DEFAULT_PLAY_FPS_LIMIT_MODE = PlayFPSLimitMode::FromVideo; const double DEFAULT_PLAY_FPS_LIMIT = 23.976; const bool DEFAULT_USE_SPACES_AS_TAB = true; const int DEFAULT_SPACES_IN_TAB = 4; const bool DEFAULT_REMEMBER_LAST_PREVIEW_FRAME = false; const int DEFAULT_LAST_PREVIEW_FRAME = 0; const qlonglong DEFAULT_LAST_PREVIEW_TIMESTAMP = 0; const SyncOutputNodesMode DEFAULT_SYNC_OUTPUT_MODE = SyncOutputNodesMode::Frame; const bool DEFAULT_HIGHLIGHT_SELECTION_MATCHES = true; const int DEFAULT_HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH = 3; const bool DEFAULT_TIMELINE_PANEL_VISIBLE = true; const bool DEFAULT_ALWAYS_KEEP_CURRENT_FRAME = true; const QString DEFAULT_LAST_SNAPSHOT_EXTENSION = "png"; const int DEFAULT_FPS_DISPLAY_PRECISION = 3; const double DEFAULT_TIMELINE_LABELS_HEIGHT = 5.0; const char DEFAULT_DROP_FILE_TEMPLATE[] = "r\'{f}\'"; const int DEFAULT_MAX_WATCHER_CONNECTION_ATTEMPTS = 5; const int DEFAULT_PNG_COMPRESSION_LEVEL = 0; const bool DEFAULT_RELOAD_FROM_DISK = false; const bool DEFAULT_DEBUG_MESSAGES = false; const bool DEFAULT_DARK_MODE = false; const bool DEFAULT_SILENT_SNAPSHOT = false; const QString DEFAULT_SNAPSHOT_TEMPLATE = "{f}-{i}-{o}.png"; //============================================================================== const char ACTION_ID_NEW_SCRIPT[] = "new_script"; const char ACTION_ID_OPEN_SCRIPT[] = "open_script"; const char ACTION_ID_SAVE_SCRIPT[] = "save_script"; const char ACTION_ID_SAVE_SCRIPT_AS[] = "save_script_as"; const char ACTION_ID_TEMPLATES[] = "templates"; const char ACTION_ID_SETTINGS[] = "settings"; const char ACTION_ID_PREVIEW[] = "preview"; const char ACTION_ID_CHECK_SCRIPT[] = "check_script"; const char ACTION_ID_BENCHMARK[] = "benchmark"; const char ACTION_ID_CLI_ENCODE[] = "cli_encode"; const char ACTION_ID_ENQUEUE_ENCODE_JOB[] = "enqueue_encode_job"; const char ACTION_ID_TOGGLE_CONSOLE[] = "toggle_console"; const char ACTION_ID_JOBS[] = "jobs"; const char ACTION_ID_EXIT[] = "exit"; const char ACTION_ID_ABOUT[] = "about"; const char ACTION_ID_AUTOCOMPLETE[] = "autocomplete"; const char ACTION_ID_SAVE_SNAPSHOT[] = "save_snapshot"; const char ACTION_ID_TOGGLE_ZOOM_PANEL[] = "toggle_zoom_panel"; const char ACTION_ID_SET_ZOOM_MODE_NO_ZOOM[] = "set_zoom_mode_no_zoom"; const char ACTION_ID_SET_ZOOM_MODE_FIXED_RATIO[] = "set_zoom_mode_fixed_ratio"; const char ACTION_ID_SET_ZOOM_MODE_FIT_TO_FRAME[] = "set_zoom_mode_fit_to_frame"; const char ACTION_ID_SET_ZOOM_SCALE_MODE_NEAREST[] = "set_zoom_scale_mode_nearest"; const char ACTION_ID_SET_ZOOM_SCALE_MODE_BILINEAR[] = "set_zoom_scale_mode_bilinear"; const char ACTION_ID_TOGGLE_CROP_PANEL[] = "toggle_crop_panel"; const char ACTION_ID_PASTE_CROP_SNIPPET_INTO_SCRIPT[] = "paste_crop_snippet_into_script"; const char ACTION_ID_FRAME_TO_CLIPBOARD[] = "frame_to_clipboard"; const char ACTION_ID_TOGGLE_TIMELINE_PANEL[] = "toggle_timeline_panel"; const char ACTION_ID_SET_TIMELINE_MODE_TIME[] = "set_timeline_mode_time"; const char ACTION_ID_SET_TIMELINE_MODE_FRAMES[] = "set_timeline_mode_frames"; const char ACTION_ID_TIME_STEP_FORWARD[] = "time_step_forward"; const char ACTION_ID_TIME_STEP_BACK[] = "time_step_back"; const char ACTION_ID_ADVANCED_PREVIEW_SETTINGS[] = "advanced_preview_settings"; const char ACTION_ID_TOGGLE_COLOR_PICKER[] = "toggle_color_picker"; const char ACTION_ID_PLAY[] = "play"; const char ACTION_ID_DUPLICATE_SELECTION[] = "duplicate_selection"; const char ACTION_ID_COMMENT_SELECTION[] = "comment_selection"; const char ACTION_ID_UNCOMMENT_SELECTION[] = "uncomment_selection"; const char ACTION_ID_REPLACE_TAB_WITH_SPACES[] = "replace_tab_with_spaces"; const char ACTION_ID_TIMELINE_LOAD_CHAPTERS[] = "timeline_load_chapters"; const char ACTION_ID_TIMELINE_CLEAR_BOOKMARKS[] = "timeline_clear_bookmarks"; const char ACTION_ID_TIMELINE_BOOKMARK_CURRENT_FRAME[] = "timeline_bookmark_current_frame"; const char ACTION_ID_TIMELINE_UNBOOKMARK_CURRENT_FRAME[] = "timeline_unbookmark_current_frame"; const char ACTION_ID_TIMELINE_GO_TO_PREVIOUS_BOOKMARK[] = "timeline_go_to_previous_bookmark"; const char ACTION_ID_TIMELINE_GO_TO_NEXT_BOOKMARK[] = "timeline_go_to_next_bookmark"; const char ACTION_ID_PASTE_SHOWN_FRAME_NUMBER_INTO_SCRIPT[] = "paste_shown_frame_number_into_script"; const char ACTION_ID_MOVE_TEXT_BLOCK_UP[] = "move_text_block_up"; const char ACTION_ID_MOVE_TEXT_BLOCK_DOWN[] = "move_text_block_down"; const char ACTION_ID_TOGGLE_COMMENT[] = "toggle_comment"; const char ACTION_ID_SHUTDOWN_SERVER_AND_EXIT[] = "shutdown_server_and_exit"; const char ACTION_ID_SET_TRUSTED_CLIENTS_ADDRESSES[] = "set_trusted_clients_addresses"; const char ACTION_ID_JUMP_TO_FRAME[] = "jump_to_frame"; const char ACTION_ID_TOGGLE_FRAME_PROPS[] = "toggle_frame_props_panel"; const char ACTION_ID_SET_OUTPUT_INDEX_0[] = "switch_to_output_index_0"; const char ACTION_ID_SET_OUTPUT_INDEX_1[] = "switch_to_output_index_1"; const char ACTION_ID_SET_OUTPUT_INDEX_2[] = "switch_to_output_index_2"; const char ACTION_ID_SET_OUTPUT_INDEX_3[] = "switch_to_output_index_3"; const char ACTION_ID_SET_OUTPUT_INDEX_4[] = "switch_to_output_index_4"; const char ACTION_ID_SET_OUTPUT_INDEX_5[] = "switch_to_output_index_5"; const char ACTION_ID_SET_OUTPUT_INDEX_6[] = "switch_to_output_index_6"; const char ACTION_ID_SET_OUTPUT_INDEX_7[] = "switch_to_output_index_7"; const char ACTION_ID_SET_OUTPUT_INDEX_8[] = "switch_to_output_index_8"; const char ACTION_ID_SET_OUTPUT_INDEX_9[] = "switch_to_output_index_9"; const char ACTION_ID_SET_OUTPUT_INDEX_10[] = "switch_to_output_index_10"; const char ACTION_ID_SET_OUTPUT_INDEX_11[] = "switch_to_output_index_11"; const char ACTION_ID_SET_OUTPUT_INDEX_12[] = "switch_to_output_index_12"; const char ACTION_ID_SET_OUTPUT_INDEX_13[] = "switch_to_output_index_13"; const char ACTION_ID_SET_OUTPUT_INDEX_14[] = "switch_to_output_index_14"; const char ACTION_ID_SET_OUTPUT_INDEX_15[] = "switch_to_output_index_15"; const char ACTION_ID_SET_OUTPUT_INDEX_16[] = "switch_to_output_index_16"; const char ACTION_ID_SET_OUTPUT_INDEX_17[] = "switch_to_output_index_17"; const char ACTION_ID_SET_OUTPUT_INDEX_18[] = "switch_to_output_index_18"; const char ACTION_ID_SET_OUTPUT_INDEX_19[] = "switch_to_output_index_19"; const char ACTION_ID_PREVIOUS_OUTPUT_INDEX[] = "switch_to_previous_output_index"; const char ACTION_ID_NEXT_OUTPUT_INDEX[] = "switch_to_next_output_index"; //============================================================================== const char TEXT_FORMAT_ID_COMMON_SCRIPT_TEXT[] = "common_script_text"; const char TEXT_FORMAT_ID_KEYWORD[] = "keyword"; const char TEXT_FORMAT_ID_OPERATOR[] = "operator"; const char TEXT_FORMAT_ID_STRING[] = "string"; const char TEXT_FORMAT_ID_NUMBER[] = "number"; const char TEXT_FORMAT_ID_COMMENT[] = "comment"; const char TEXT_FORMAT_ID_VS_CORE[] = "vs_core"; const char TEXT_FORMAT_ID_VS_NAMESPACE[] = "vs_namespace"; const char TEXT_FORMAT_ID_VS_FUNCTION[] = "vs_function"; const char TEXT_FORMAT_ID_VS_ARGUMENT[] = "vs_argument"; const char TEXT_FORMAT_ID_TIMELINE[] = "timeline_text"; const char COLOR_ID_TEXT_BACKGROUND[] = "text_background_color"; const char COLOR_ID_ACTIVE_LINE[] = "active_line_color"; const char COLOR_ID_SELECTION_MATCHES[] = "selection_matches"; const char COLOR_ID_TIMELINE_BOOKMARKS[] = "timeline_bookmarks"; //============================================================================== bool StandardAction::operator==(const StandardAction & a_other) const { return id == a_other.id; } bool StandardAction::operator<(const StandardAction & a_other) const { return id < a_other.id; } //============================================================================== CodeSnippet::CodeSnippet(const QString & a_name, const QString & a_text) : name(a_name) , text(a_text) { } bool CodeSnippet::operator==(const CodeSnippet & a_other) const { return (name == a_other.name); } bool CodeSnippet::operator<(const CodeSnippet & a_other) const { return (name < a_other.name); } bool CodeSnippet::isEmpty() const { return (name.isEmpty() && text.isEmpty()); } //============================================================================== ================================================ FILE: common-src/settings/settings_definitions.h ================================================ #ifndef SETTINGS_DEFINITIONS_H_INCLUDED #define SETTINGS_DEFINITIONS_H_INCLUDED #include "../timeline_slider/timeline_slider.h" #include "../log/styled_log_view_structures.h" #include #include #include #include #include #include //============================================================================== enum class ZoomMode { NoZoom, FixedRatio, FitToFrame, }; enum class CropMode { Absolute, Relative, }; enum class PlayFPSLimitMode { FromVideo, NoLimit, Custom, }; enum class SyncOutputNodesMode { Frame, Time, FromTimeLine, }; struct StandardAction { QString id; QString title; QIcon icon; QKeySequence hotkey; bool operator==(const StandardAction & a_other) const; bool operator<(const StandardAction & a_other) const; }; struct CodeSnippet { QString name; QString text; CodeSnippet(const QString & a_name = QString(), const QString & a_text = QString()); bool operator==(const CodeSnippet & a_other) const; bool operator<(const CodeSnippet & a_other) const; bool isEmpty() const; }; struct DropFileCategory { QString name; QStringList maskList; QString sourceTemplate; }; //============================================================================== extern const bool DEFAULT_MAIN_WINDOW_MAXIMIZED; extern const bool DEFAULT_PREVIEW_DIALOG_MAXIMIZED; extern const bool DEFAULT_JOBS_DIALOG_MAXIMIZED; extern const bool DEFAULT_JOB_SERVER_WATCHER_MAXIMIZED; extern const bool DEFAULT_AUTO_LOAD_LAST_SCRIPT; extern const bool DEFAULT_ZOOM_PANEL_VISIBLE; extern const ZoomMode DEFAULT_ZOOM_MODE; extern const double DEFAULT_ZOOM_RATIO; extern const Qt::TransformationMode DEFAULT_SCALE_MODE; extern const CropMode DEFAULT_CROP_MODE; extern const int DEFAULT_CROP_ZOOM_RATIO; extern const bool DEFAULT_PROMPT_TO_SAVE_CHANGES; extern const unsigned int DEFAULT_MAX_RECENT_FILES_NUMBER; extern const QStringList DEFAULT_DOCUMENTATION_PATHS; extern const int DEFAULT_CHARACTERS_TYPED_TO_START_COMPLETION; extern const double DEFAULT_TIME_STEP; extern const TimeLineSlider::DisplayMode DEFAULT_TIMELINE_MODE; extern const bool DEFAULT_COLOR_PICKER_VISIBLE; extern const PlayFPSLimitMode DEFAULT_PLAY_FPS_LIMIT_MODE; extern const double DEFAULT_PLAY_FPS_LIMIT; extern const bool DEFAULT_USE_SPACES_AS_TAB; extern const int DEFAULT_SPACES_IN_TAB; extern const bool DEFAULT_REMEMBER_LAST_PREVIEW_FRAME; extern const int DEFAULT_LAST_PREVIEW_FRAME; extern const qlonglong DEFAULT_LAST_PREVIEW_TIMESTAMP; extern const SyncOutputNodesMode DEFAULT_SYNC_OUTPUT_MODE; extern const bool DEFAULT_HIGHLIGHT_SELECTION_MATCHES; extern const int DEFAULT_HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH; extern const bool DEFAULT_TIMELINE_PANEL_VISIBLE; extern const bool DEFAULT_ALWAYS_KEEP_CURRENT_FRAME; extern const QString DEFAULT_LAST_SNAPSHOT_EXTENSION; extern const int DEFAULT_FPS_DISPLAY_PRECISION; extern const double DEFAULT_TIMELINE_LABELS_HEIGHT; extern const char DEFAULT_DROP_FILE_TEMPLATE[]; extern const int DEFAULT_MAX_WATCHER_CONNECTION_ATTEMPTS; extern const int DEFAULT_PNG_COMPRESSION_LEVEL; extern const bool DEFAULT_RELOAD_FROM_DISK; extern const bool DEFAULT_DEBUG_MESSAGES; extern const bool DEFAULT_DARK_MODE; extern const bool DEFAULT_SILENT_SNAPSHOT; extern const QString DEFAULT_SNAPSHOT_TEMPLATE; //============================================================================== extern const char ACTION_ID_NEW_SCRIPT[]; extern const char ACTION_ID_OPEN_SCRIPT[]; extern const char ACTION_ID_SAVE_SCRIPT[]; extern const char ACTION_ID_SAVE_SCRIPT_AS[]; extern const char ACTION_ID_TEMPLATES[]; extern const char ACTION_ID_SETTINGS[]; extern const char ACTION_ID_PREVIEW[]; extern const char ACTION_ID_CHECK_SCRIPT[]; extern const char ACTION_ID_BENCHMARK[]; extern const char ACTION_ID_CLI_ENCODE[]; extern const char ACTION_ID_ENQUEUE_ENCODE_JOB[]; extern const char ACTION_ID_TOGGLE_CONSOLE[]; extern const char ACTION_ID_JOBS[]; extern const char ACTION_ID_EXIT[]; extern const char ACTION_ID_ABOUT[]; extern const char ACTION_ID_AUTOCOMPLETE[]; extern const char ACTION_ID_SAVE_SNAPSHOT[]; extern const char ACTION_ID_TOGGLE_ZOOM_PANEL[]; extern const char ACTION_ID_SET_ZOOM_MODE_NO_ZOOM[]; extern const char ACTION_ID_SET_ZOOM_MODE_FIXED_RATIO[]; extern const char ACTION_ID_SET_ZOOM_MODE_FIT_TO_FRAME[]; extern const char ACTION_ID_SET_ZOOM_SCALE_MODE_NEAREST[]; extern const char ACTION_ID_SET_ZOOM_SCALE_MODE_BILINEAR[]; extern const char ACTION_ID_TOGGLE_CROP_PANEL[]; extern const char ACTION_ID_PASTE_CROP_SNIPPET_INTO_SCRIPT[]; extern const char ACTION_ID_FRAME_TO_CLIPBOARD[]; extern const char ACTION_ID_TOGGLE_TIMELINE_PANEL[]; extern const char ACTION_ID_SET_TIMELINE_MODE_TIME[]; extern const char ACTION_ID_SET_TIMELINE_MODE_FRAMES[]; extern const char ACTION_ID_TIME_STEP_FORWARD[]; extern const char ACTION_ID_TIME_STEP_BACK[]; extern const char ACTION_ID_ADVANCED_PREVIEW_SETTINGS[]; extern const char ACTION_ID_TOGGLE_COLOR_PICKER[]; extern const char ACTION_ID_PLAY[]; extern const char ACTION_ID_DUPLICATE_SELECTION[]; extern const char ACTION_ID_COMMENT_SELECTION[]; extern const char ACTION_ID_UNCOMMENT_SELECTION[]; extern const char ACTION_ID_REPLACE_TAB_WITH_SPACES[]; extern const char ACTION_ID_TIMELINE_LOAD_CHAPTERS[]; extern const char ACTION_ID_TIMELINE_CLEAR_BOOKMARKS[]; extern const char ACTION_ID_TIMELINE_BOOKMARK_CURRENT_FRAME[]; extern const char ACTION_ID_TIMELINE_UNBOOKMARK_CURRENT_FRAME[]; extern const char ACTION_ID_TIMELINE_GO_TO_PREVIOUS_BOOKMARK[]; extern const char ACTION_ID_TIMELINE_GO_TO_NEXT_BOOKMARK[]; extern const char ACTION_ID_PASTE_SHOWN_FRAME_NUMBER_INTO_SCRIPT[]; extern const char ACTION_ID_MOVE_TEXT_BLOCK_UP[]; extern const char ACTION_ID_MOVE_TEXT_BLOCK_DOWN[]; extern const char ACTION_ID_TOGGLE_COMMENT[]; extern const char ACTION_ID_SHUTDOWN_SERVER_AND_EXIT[]; extern const char ACTION_ID_SET_TRUSTED_CLIENTS_ADDRESSES[]; extern const char ACTION_ID_JUMP_TO_FRAME[]; extern const char ACTION_ID_TOGGLE_FRAME_PROPS[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_0[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_1[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_2[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_3[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_4[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_5[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_6[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_7[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_8[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_9[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_10[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_11[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_12[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_13[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_14[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_15[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_16[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_17[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_18[]; extern const char ACTION_ID_SET_OUTPUT_INDEX_19[]; extern const char ACTION_ID_PREVIOUS_OUTPUT_INDEX[]; extern const char ACTION_ID_NEXT_OUTPUT_INDEX[]; //============================================================================== extern const char TEXT_FORMAT_ID_COMMON_SCRIPT_TEXT[]; extern const char TEXT_FORMAT_ID_KEYWORD[]; extern const char TEXT_FORMAT_ID_OPERATOR[]; extern const char TEXT_FORMAT_ID_STRING[]; extern const char TEXT_FORMAT_ID_NUMBER[]; extern const char TEXT_FORMAT_ID_COMMENT[]; extern const char TEXT_FORMAT_ID_VS_CORE[]; extern const char TEXT_FORMAT_ID_VS_NAMESPACE[]; extern const char TEXT_FORMAT_ID_VS_FUNCTION[]; extern const char TEXT_FORMAT_ID_VS_ARGUMENT[]; extern const char TEXT_FORMAT_ID_TIMELINE[]; extern const char COLOR_ID_TEXT_BACKGROUND[]; extern const char COLOR_ID_ACTIVE_LINE[]; extern const char COLOR_ID_SELECTION_MATCHES[]; extern const char COLOR_ID_TIMELINE_BOOKMARKS[]; //============================================================================== #endif // SETTINGS_DEFINITIONS_H_INCLUDED ================================================ FILE: common-src/settings/settings_definitions_core.cpp ================================================ #include "settings_definitions_core.h" #include #include #include #include //============================================================================== const bool DEFAULT_PREFER_VS_LIBRARIES_FROM_LIST = false; const ResamplingFilter DEFAULT_CHROMA_RESAMPLING_FILTER = ResamplingFilter::Bicubic; const YuvMatrixCoefficients DEFAULT_YUV_MATRIX_COEFFICIENTS = YuvMatrixCoefficients::m709; const ChromaPlacement DEFAULT_CHROMA_PLACEMENT = ChromaPlacement::LEFT; const double DEFAULT_BICUBIC_FILTER_PARAMETER_B = 0.0; const double DEFAULT_BICUBIC_FILTER_PARAMETER_C = 0.5; const int DEFAULT_LANCZOS_FILTER_TAPS = 3; const DitherType DEFAULT_DITHER_TYPE = DitherType::ERROR_DIFFUSION; const EncodingType DEFAULT_ENCODING_TYPE = EncodingType::CLI; const EncodingHeaderType DEFAULT_ENCODING_HEADER_TYPE = EncodingHeaderType::NoHeader; const JobType DEFAULT_JOB_TYPE = JobType::EncodeScriptCLI; const JobState DEFAULT_JOB_STATE = JobState::Waiting; const int DEFAULT_JOB_FIRST_FRAME = -1; const int DEFAULT_JOB_LAST_FRAME = -1; const int DEFAULT_JOB_FRAMES_PROCESSED = 0; const double DEFAULT_JOB_FPS = 0.0; const int DEFAULT_RECENT_JOB_SERVERS_NUMBER = 10; const int DEFAULT_WINDOW_GEOMETRY_SAVE_DELAY = 2000; //============================================================================== const std::vector ACTIVE_JOB_STATES = {JobState::Running, JobState::Pausing, JobState::Paused, JobState::Aborting, JobState::FailedCleanUp, JobState::CompletedCleanUp}; JobProperties::JobProperties(): id(QUuid::createUuid()) , type(JobType::EncodeScriptCLI) , jobState(JobState::Waiting) , encodingType(EncodingType::CLI) , encodingHeaderType(EncodingHeaderType::NoHeader) , firstFrame(-1) , firstFrameReal(-1) , lastFrame(-1) , lastFrameReal(-1) , framesProcessed(0) , fps(0.0) { } QString JobProperties::typeName(JobType a_type) { static std::map typeNameMap = { {JobType::EncodeScriptCLI, QObject::tr("CLI encoding")}, {JobType::RunProcess, QObject::tr("Process run")}, {JobType::RunShellCommand, QObject::tr("Shell command")}, }; return typeNameMap[a_type]; } QString JobProperties::stateName(JobState a_state) { static std::map stateNameMap = { {JobState::Waiting, QObject::tr("Waiting")}, {JobState::Running, QObject::tr("Running")}, {JobState::Paused, QObject::tr("Paused")}, {JobState::Pausing, QObject::tr("Pausing")}, {JobState::Aborted, QObject::tr("Aborted")}, {JobState::Aborting, QObject::tr("Aborting")}, {JobState::FailedCleanUp, QObject::tr("Failed. Cleaning up.")}, {JobState::Failed, QObject::tr("Failed")}, {JobState::DependencyNotMet, QObject::tr("Dependency not met")}, {JobState::CompletedCleanUp, QObject::tr("Completing")}, {JobState::Completed, QObject::tr("Completed")}, }; return stateNameMap[a_state]; } QString JobProperties::subject() const { QString subjectString; if(type == JobType::EncodeScriptCLI) { subjectString = QString("%sn%:\n\"%ep%\" %arg%"); subjectString = subjectString.replace("%sn%", scriptName); subjectString = subjectString.replace("%ep%", executablePath); subjectString = subjectString.replace("%arg%", arguments); } else if(type == JobType::RunProcess) { subjectString = QString("\"%ep%\" %arg%"); subjectString = subjectString.replace("%ep%", executablePath); subjectString = subjectString.replace("%arg%", arguments); } else if(type == JobType::RunShellCommand) subjectString = shellCommand; subjectString = subjectString.simplified(); return subjectString; } int JobProperties::framesTotal() const { return lastFrameReal - firstFrameReal + 1; } const char JP_ID[] = "id"; const char JP_TYPE[] = "type"; const char JP_JOB_STATE[] = "jobState"; const char JP_DEPENDS_ON_JOB_IDS[] = "dependsOnJobIds"; const char JP_TIME_STARTED[] = "timeStarted"; const char JP_TIME_ENDED[] = "timeEnded"; const char JP_SCRIPT_NAME[] = "scriptName"; const char JP_SCRIPT_TEXT[] = "scriptText"; const char JP_ENCODING_TYPE[] = "encodingType"; const char JP_ENCODING_HEADER_TYPE[] = "encodingHeaderType"; const char JP_EXECUTABLE_PATH[] = "executablePath"; const char JP_ARGUMENTS[] = "arguments"; const char JP_SHELL_COMMAND[] = "shellCommand"; const char JP_FIRST_FRAME[] = "firstFrame"; const char JP_FIRST_FRAME_REAL[] = "firstFrameReal"; const char JP_LAST_FRAME[] = "lastFrame"; const char JP_LAST_FRAME_REAL[] = "lastFrameReal"; const char JP_FRAMES_PROCESSED[] = "framesProcessed"; const char JP_FPS[] = "fps"; QJsonObject JobProperties::toJson() const { QJsonObject jsJob; jsJob[JP_ID] = id.toString(); jsJob[JP_TYPE] = (int)type; jsJob[JP_JOB_STATE] = (int)jobState; QJsonArray jsDependencies; for(const QUuid & dependencyId : dependsOnJobIds) jsDependencies.push_back(QJsonValue(dependencyId.toString())); jsJob[JP_DEPENDS_ON_JOB_IDS] = jsDependencies; jsJob[JP_TIME_STARTED] = timeStarted.toMSecsSinceEpoch(); jsJob[JP_TIME_ENDED] = timeEnded.toMSecsSinceEpoch(); jsJob[JP_SCRIPT_NAME] = scriptName; jsJob[JP_SCRIPT_TEXT] = scriptText; jsJob[JP_ENCODING_TYPE] = (int)encodingType; jsJob[JP_ENCODING_HEADER_TYPE] = (int)encodingHeaderType; jsJob[JP_EXECUTABLE_PATH] = executablePath; jsJob[JP_ARGUMENTS] = arguments; jsJob[JP_SHELL_COMMAND] = shellCommand; jsJob[JP_FIRST_FRAME] = firstFrame; jsJob[JP_FIRST_FRAME_REAL] = firstFrameReal; jsJob[JP_LAST_FRAME] = lastFrame; jsJob[JP_LAST_FRAME_REAL] = lastFrameReal; jsJob[JP_FRAMES_PROCESSED] = framesProcessed; jsJob[JP_FPS] = fps; return jsJob; } JobProperties JobProperties::fromJson(const QJsonObject & a_object) { JobProperties properties; if(a_object.contains(JP_ID)) properties.id = QUuid(a_object[JP_ID].toString()); if(a_object.contains(JP_TYPE)) properties.type = (JobType)a_object[JP_TYPE].toInt(); if(a_object.contains(JP_JOB_STATE)) properties.jobState = (JobState)a_object[JP_JOB_STATE].toInt(); if(a_object.contains(JP_DEPENDS_ON_JOB_IDS)) { if(a_object[JP_DEPENDS_ON_JOB_IDS].isArray()) { for(const QJsonValue & value : a_object[JP_DEPENDS_ON_JOB_IDS].toArray()) properties.dependsOnJobIds.push_back(QUuid(value.toString())); } } if(a_object.contains(JP_TIME_STARTED)) properties.timeStarted = QDateTime::fromMSecsSinceEpoch( a_object[JP_TIME_STARTED].toVariant().toLongLong()); if(a_object.contains(JP_TIME_ENDED)) properties.timeEnded = QDateTime::fromMSecsSinceEpoch( a_object[JP_TIME_ENDED].toVariant().toLongLong()); if(a_object.contains(JP_SCRIPT_NAME)) properties.scriptName = a_object[JP_SCRIPT_NAME].toString(); if(a_object.contains(JP_SCRIPT_TEXT)) properties.scriptText = a_object[JP_SCRIPT_TEXT].toString(); if(a_object.contains(JP_ENCODING_TYPE)) properties.encodingType = (EncodingType)a_object[JP_ENCODING_TYPE].toInt(); if(a_object.contains(JP_ENCODING_HEADER_TYPE)) properties.encodingHeaderType = (EncodingHeaderType)a_object[JP_ENCODING_HEADER_TYPE].toInt(); if(a_object.contains(JP_EXECUTABLE_PATH)) properties.executablePath = a_object[JP_EXECUTABLE_PATH].toString(); if(a_object.contains(JP_ARGUMENTS)) properties.arguments = a_object[JP_ARGUMENTS].toString(); if(a_object.contains(JP_SHELL_COMMAND)) properties.shellCommand = a_object[JP_SHELL_COMMAND].toString(); if(a_object.contains(JP_FIRST_FRAME)) properties.firstFrame = a_object[JP_FIRST_FRAME].toInt(); if(a_object.contains(JP_FIRST_FRAME_REAL)) properties.firstFrameReal = a_object[JP_FIRST_FRAME_REAL].toInt(); if(a_object.contains(JP_LAST_FRAME)) properties.lastFrame = a_object[JP_LAST_FRAME].toInt(); if(a_object.contains(JP_LAST_FRAME_REAL)) properties.lastFrameReal= a_object[JP_LAST_FRAME_REAL].toInt(); if(a_object.contains(JP_FRAMES_PROCESSED)) properties.framesProcessed = a_object[JP_FRAMES_PROCESSED].toInt(); if(a_object.contains(JP_FPS)) properties.fps = a_object[JP_FPS].toDouble(); return properties; } //============================================================================== EncodingPreset::EncodingPreset(const QString & a_name): name(a_name) , type(DEFAULT_ENCODING_TYPE) , headerType(DEFAULT_ENCODING_HEADER_TYPE) { } bool EncodingPreset::operator==(const EncodingPreset & a_other) const { return (name == a_other.name); } bool EncodingPreset::operator<(const EncodingPreset & a_other) const { return (name < a_other.name); } bool EncodingPreset::isEmpty() const { return name.isEmpty(); } //============================================================================== ================================================ FILE: common-src/settings/settings_definitions_core.h ================================================ #ifndef SETTINGS_DEFINITIONS_CORE_H_INCLUDED #define SETTINGS_DEFINITIONS_CORE_H_INCLUDED #include #include #include #include #include //============================================================================== enum class ResamplingFilter : int { Point, Bilinear, Bicubic, Spline16, Spline36, Spline64, Lanczos, }; enum class YuvMatrixCoefficients : int { m709, m470BG, m170M, m2020_NCL, }; enum class ChromaPlacement : int { LEFT, CENTER, TOP_LEFT, }; enum class DitherType: int { NONE, ORDERED, RANDOM, ERROR_DIFFUSION, }; enum class EncodingType { CLI, Raw, VfW, }; enum class EncodingHeaderType { NoHeader, Y4M, }; enum class JobType { EncodeScriptCLI, RunProcess, RunShellCommand, }; enum class JobState { Waiting, Running, Pausing, Paused, Aborting, Aborted, FailedCleanUp, Failed, DependencyNotMet, CompletedCleanUp, Completed, }; extern const std::vector ACTIVE_JOB_STATES; extern const char JP_ID[]; extern const char JP_TYPE[]; extern const char JP_JOB_STATE[]; extern const char JP_DEPENDS_ON_JOB_IDS[]; extern const char JP_TIME_STARTED[]; extern const char JP_TIME_ENDED[]; extern const char JP_SCRIPT_NAME[]; extern const char JP_ENCODING_TYPE[]; extern const char JP_ENCODING_HEADER_TYPE[]; extern const char JP_EXECUTABLE_PATH[]; extern const char JP_ARGUMENTS[]; extern const char JP_SHELL_COMMAND[]; extern const char JP_FIRST_FRAME[]; extern const char JP_FIRST_FRAME_REAL[]; extern const char JP_LAST_FRAME[]; extern const char JP_LAST_FRAME_REAL[]; extern const char JP_FRAMES_PROCESSED[]; extern const char JP_FPS[]; struct JobProperties { QUuid id; JobType type; JobState jobState; std::vector dependsOnJobIds; QDateTime timeStarted; QDateTime timeEnded; QString scriptName; QString scriptText; EncodingType encodingType; EncodingHeaderType encodingHeaderType; QString executablePath; QString arguments; QString shellCommand; int firstFrame; int firstFrameReal; int lastFrame; int lastFrameReal; int framesProcessed; double fps; JobProperties(); JobProperties(const JobProperties &) = default; JobProperties(JobProperties &&) = default; JobProperties & operator=(const JobProperties &) = default; JobProperties & operator=(JobProperties &&) = default; static QString typeName(JobType a_type); static QString stateName(JobState a_state); QString subject() const; int framesTotal() const; QJsonObject toJson() const; static JobProperties fromJson(const QJsonObject & a_object); }; struct EncodingPreset { QString name; EncodingType type; EncodingHeaderType headerType; QString executablePath; QString arguments; EncodingPreset(const QString & a_name = QString()); bool operator==(const EncodingPreset & a_other) const; bool operator<(const EncodingPreset & a_other) const; bool isEmpty() const; }; //============================================================================== extern const bool DEFAULT_PREFER_VS_LIBRARIES_FROM_LIST; extern const ResamplingFilter DEFAULT_CHROMA_RESAMPLING_FILTER; extern const YuvMatrixCoefficients DEFAULT_YUV_MATRIX_COEFFICIENTS; extern const ChromaPlacement DEFAULT_CHROMA_PLACEMENT; extern const double DEFAULT_BICUBIC_FILTER_PARAMETER_B; extern const double DEFAULT_BICUBIC_FILTER_PARAMETER_C; extern const int DEFAULT_LANCZOS_FILTER_TAPS; extern const DitherType DEFAULT_DITHER_TYPE; extern const EncodingType DEFAULT_ENCODING_TYPE; extern const EncodingHeaderType DEFAULT_ENCODING_HEADER_TYPE; extern const JobType DEFAULT_JOB_TYPE; extern const JobState DEFAULT_JOB_STATE; extern const int DEFAULT_JOB_FIRST_FRAME; extern const int DEFAULT_JOB_LAST_FRAME; extern const int DEFAULT_JOB_FRAMES_PROCESSED; extern const double DEFAULT_JOB_FPS; extern const int DEFAULT_RECENT_JOB_SERVERS_NUMBER; extern const int DEFAULT_WINDOW_GEOMETRY_SAVE_DELAY; //============================================================================== #endif // SETTINGS_DEFINITIONS_CORE_H_INCLUDED ================================================ FILE: common-src/settings/settings_manager.cpp ================================================ #include "settings_manager.h" #include "../helpers.h" #include #include #include #include #include //============================================================================== const char MAIN_WINDOW_GEOMETRY_KEY[] = "main_window_geometry"; const char PREVIEW_DIALOG_GEOMETRY_KEY[] = "prewiew_dialog_geometry"; const char LAST_PREVIEW_SCROLLBAR_POS_X[] = "last_preview_scrollbar_position_x"; const char LAST_PREVIEW_SCROLLBAR_POS_Y[] = "last_preview_scrollbar_position_y"; const char JOB_SERVER_WATCHER_GEOMETRY_KEY[] = "job_server_watcher_geometry"; const char MAIN_WINDOW_MAXIMIZED_KEY[] = "main_window_maximized"; const char PREVIEW_DIALOG_MAXIMIZED_KEY[] = "preview_dialog_maximized"; const char JOB_SERVER_WATCHER_MAXIMIZED_KEY[] = "job_server_watcher_maximized"; const char JOBS_HEADER_STATE_KEY[] = "jobs_header_state"; const char AUTO_LOAD_LAST_SCRIPT_KEY[] = "auto_load_last_script"; const char ZOOM_PANEL_VISIBLE_KEY[] = "zoom_panel_visible"; const char ZOOM_MODE_KEY[] = "zoom_mode"; const char ZOOM_RATIO_KEY[] = "zoom_ratio"; const char SCALE_MODE_KEY[] = "scale_mode"; const char CROP_MODE_KEY[] = "crop_mode"; const char CROP_ZOOM_RATIO_KEY[] = "crop_zoom_ratio"; const char RELOAD_FROM_DISK_KEY[] = "period_reload_from_disk"; const char PROMPT_TO_SAVE_CHANGES_KEY[] = "prompt_to_save_changes"; const char RECENT_FILES_LIST_KEY[] = "recent_files_list"; const char MAX_RECENT_FILES_NUMBER_KEY[] = "max_recent_files_number"; const char CHARACTERS_TYPED_TO_START_COMPLETION_KEY[] = "characters_typed_to_start_completion"; const char TIMELINE_MODE_KEY[] = "timeline_mode"; const char TIME_STEP_KEY[] = "time_step_mode"; const char COLOR_PICKER_VISIBLE_KEY[] = "color_picker_visible"; const char PLAY_FPS_LIMIT_MODE_KEY[] = "play_fps_limit_mode"; const char PLAY_FPS_LIMIT_KEY[] = "play_fps_limit"; const char USE_SPACES_AS_TAB_KEY[] = "use_spaces_as_tab"; const char SPACES_IN_TAB_KEY[] = "spaces_in_tab"; const char REMEMBER_LAST_PREVIEW_FRAME_KEY[] = "remember_last_preview_frame"; const char LAST_PREVIEW_FRAME_KEY[] = "last_preview_frame"; const char LAST_PREVIEW_TIMESTAMP_KEY[] = "last_preview_timestamp"; const char SYNC_OUTPUT_MODE_KEY[] = "sync_output_node_mode"; const char NEW_SCRIPT_TEMPLATE_KEY[] = "new_script_template"; const char HIGHLIGHT_SELECTION_MATCHES_KEY[] = "highlight_selection_matches"; const char HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH_KEY[] = "highlight_selection_matches_min_length"; const char TIMELINE_PANEL_VISIBLE_KEY[] = "timeline_panel_visible"; const char ALWAYS_KEEP_CURRENT_FRAME_KEY[] = "always_keep_current_frame"; const char LAST_SNAPSHOT_EXTENSION_KEY[] = "last_snapshot_extension"; const char PNG_COMPRESSION_LEVEL_KEY[] = "png_compression_level"; const char DEBUG_MESSAGES_KEY[] = "show_debug_messages"; const char DARK_MODE_KEY[] = "dark_mode"; const char SILENT_SNAPSHOT_KEY[] = "silent_snapshot"; const char SNAPSHOT_TEMPLATE_KEY[] = "snapshot_template"; //============================================================================== const char HOTKEYS_GROUP[] = "hotkeys"; //============================================================================== const char THEME_GROUP[] = "theme"; //============================================================================== const char DARK_THEME_GROUP[] = "dark_theme"; //============================================================================== const char PREVIEWER_GROUP[] = "previewer"; //============================================================================== const char CODE_SNIPPETS_GROUP[] = "code_snippets"; //============================================================================== const char DROP_FILE_TEMPLATES_GROUP[] = "drop_file_templates"; const char DROP_FILE_CATEGORY_MASK_LIST_KEY[] = "mask_list"; const char DROP_FILE_CATEGORY_SOURCE_TEMPLATE_KEY[] = "template"; //============================================================================== const char LOGS_GROUP[] = "logs"; const char LOG_STYLES_GROUP[] = "styles"; const char LOG_STYLE_TITLE_KEY[] = "title"; const char LOG_STYLE_TEXT_FORMAT_KEY[] = "text_format"; const char LOG_STYLE_IS_ALIAS_KEY[] = "is_alias"; const char LOG_STYLE_ORIGINAL_STYLE_NAME_KEY[] = "original_style_name"; const char LOG_STYLE_IS_VISIBLE_KEY[] = "is_visible"; //============================================================================== SettingsManager::SettingsManager(QObject * a_pParent) : SettingsManagerCore(a_pParent) { initializeStandardActions(); m_bInDarkMode = getDarkMode(); } SettingsManager::~SettingsManager() { } //============================================================================== void SettingsManager::initializeStandardActions() { m_standardActions = { {ACTION_ID_NEW_SCRIPT, tr("New script"), QIcon(":new.png"), QKeySequence::New}, {ACTION_ID_OPEN_SCRIPT, tr("Open script"), QIcon(":load.png"), QKeySequence::Open}, {ACTION_ID_SAVE_SCRIPT, tr("Save script"), QIcon(":save.png"), QKeySequence::Save}, {ACTION_ID_SAVE_SCRIPT_AS, tr("Save script as..."), QIcon(":save_as.png"), QKeySequence::SaveAs}, {ACTION_ID_EXIT, tr("Exit"), QIcon(":exit.png"), QKeySequence::Quit}, {ACTION_ID_DUPLICATE_SELECTION, tr("Duplicate selection or line"), QIcon(), QKeySequence(Qt::CTRL | Qt::Key_D)}, {ACTION_ID_COMMENT_SELECTION, tr("Comment lines"), QIcon(), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C)}, {ACTION_ID_UNCOMMENT_SELECTION, tr("Uncomment lines"), QIcon(), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_X)}, {ACTION_ID_REPLACE_TAB_WITH_SPACES, tr("Replace Tab characters with spaces"), QIcon(), QKeySequence()}, {ACTION_ID_TEMPLATES, tr("Snippets and templates"), QIcon(), QKeySequence()}, {ACTION_ID_SETTINGS, tr("Settings"), QIcon(":settings.png"), QKeySequence()}, {ACTION_ID_PREVIEW, tr("Preview"), QIcon(":preview.png"), QKeySequence(Qt::Key_F5)}, {ACTION_ID_CHECK_SCRIPT, tr("Check script"), QIcon(":check.png"), QKeySequence(Qt::Key_F6)}, {ACTION_ID_BENCHMARK, tr("Benchmark"), QIcon(":benchmark.png"), QKeySequence(Qt::Key_F7)}, {ACTION_ID_CLI_ENCODE, tr("Encode video"), QIcon(":film_save.png"), QKeySequence(Qt::Key_F8)}, {ACTION_ID_ENQUEUE_ENCODE_JOB, tr("Enqueue encode job"), QIcon(":jobs.png"), QKeySequence(Qt::Key_F9)}, {ACTION_ID_JOBS, tr("Jobs"), QIcon(":jobs.png"), QKeySequence(Qt::Key_F10)}, #if defined(Q_OS_WIN) {ACTION_ID_TOGGLE_CONSOLE, tr("Toggle console"), QIcon(), QKeySequence(Qt::CTRL | Qt::Key_T)}, #endif {ACTION_ID_ABOUT, tr("About..."), QIcon(), QKeySequence()}, {ACTION_ID_AUTOCOMPLETE, tr("Autocomplete"), QIcon(), QKeySequence(Qt::CTRL | Qt::Key_Space)}, {ACTION_ID_FRAME_TO_CLIPBOARD, tr("Copy frame to clipboard"), QIcon(":image_to_clipboard.png"), QKeySequence(Qt::Key_X)}, {ACTION_ID_SAVE_SNAPSHOT, tr("Save snapshot"), QIcon(":snapshot.png"), QKeySequence(Qt::Key_S)}, {ACTION_ID_TOGGLE_ZOOM_PANEL, tr("Show zoom panel"), QIcon(":zoom.png"), QKeySequence(Qt::Key_Z)}, {ACTION_ID_SET_ZOOM_MODE_NO_ZOOM, tr("Zoom: No zoom"), QIcon(":zoom_no_zoom.png"), QKeySequence( Qt::ALT | Qt::Key_1)}, {ACTION_ID_SET_ZOOM_MODE_FIXED_RATIO, tr("Zoom: Fixed ratio"), QIcon(":zoom_fixed_ratio.png"), QKeySequence( Qt::ALT | Qt::Key_2)}, {ACTION_ID_SET_ZOOM_MODE_FIT_TO_FRAME, tr("Zoom: Fit to frame"), QIcon(":zoom_fit_to_frame.png"), QKeySequence( Qt::ALT | Qt::Key_3)}, {ACTION_ID_SET_ZOOM_SCALE_MODE_NEAREST, tr("Scale: Nearest"), QIcon(), QKeySequence()}, {ACTION_ID_SET_ZOOM_SCALE_MODE_BILINEAR, tr("Scale: Bilinear"), QIcon(), QKeySequence()}, {ACTION_ID_TOGGLE_CROP_PANEL, tr("Crop assistant"), QIcon(":crop.png"), QKeySequence(Qt::Key_C)}, {ACTION_ID_PASTE_CROP_SNIPPET_INTO_SCRIPT, tr("Paste crop snippet into script"), QIcon(":paste.png"), QKeySequence()}, {ACTION_ID_TOGGLE_TIMELINE_PANEL, tr("Show timeline panel"), QIcon(":timeline.png"), QKeySequence(Qt::Key_T)}, {ACTION_ID_SET_TIMELINE_MODE_TIME, tr("Timeline mode: Time"), QIcon(":timeline.png"), QKeySequence()}, {ACTION_ID_SET_TIMELINE_MODE_FRAMES, tr("Timeline mode: Frames"), QIcon(":timeline_frames.png"), QKeySequence()}, {ACTION_ID_TIME_STEP_FORWARD, tr("Time: step forward"), QIcon(":time_forward.png"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Right)}, {ACTION_ID_TIME_STEP_BACK, tr("Time: step back"), QIcon(":time_back.png"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Left)}, {ACTION_ID_ADVANCED_PREVIEW_SETTINGS, tr("Preview advanced settings"), QIcon(":settings.png"), QKeySequence()}, {ACTION_ID_TOGGLE_COLOR_PICKER, tr("Color panel"), QIcon(":color_picker.png"), QKeySequence()}, {ACTION_ID_PLAY, tr("Play"), QIcon(":play.png"), QKeySequence()}, {ACTION_ID_TIMELINE_LOAD_CHAPTERS, tr("Load chapters"), QIcon(":load.png"), QKeySequence()}, {ACTION_ID_TIMELINE_CLEAR_BOOKMARKS, tr("Clear bookmarks"), QIcon(":timeline_bookmark.png"), QKeySequence()}, {ACTION_ID_TIMELINE_BOOKMARK_CURRENT_FRAME, tr("Bookmark current frame"), QIcon(":timeline_bookmark_add.png"), QKeySequence(Qt::CTRL | Qt::Key_B)}, {ACTION_ID_TIMELINE_UNBOOKMARK_CURRENT_FRAME, tr("Unbookmark current frame"), QIcon(":timeline_bookmark_remove.png"), QKeySequence(Qt::CTRL | Qt::Key_U)}, {ACTION_ID_TIMELINE_GO_TO_PREVIOUS_BOOKMARK, tr("Go to previous bookmark"), QIcon(":timeline_bookmark_previous.png"), QKeySequence(Qt::CTRL | Qt::Key_Left)}, {ACTION_ID_TIMELINE_GO_TO_NEXT_BOOKMARK, tr("Go to next bookmark"), QIcon(":timeline_bookmark_next.png"), QKeySequence(Qt::CTRL | Qt::Key_Right)}, {ACTION_ID_PASTE_SHOWN_FRAME_NUMBER_INTO_SCRIPT, tr("Paste shown frame number into script"), QIcon(), QKeySequence()}, {ACTION_ID_MOVE_TEXT_BLOCK_UP, tr("Move text block up"), QIcon(), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Up)}, {ACTION_ID_MOVE_TEXT_BLOCK_DOWN, tr("Move text block down"), QIcon(), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Down)}, {ACTION_ID_TOGGLE_COMMENT, tr("Toggle comment"), QIcon(), QKeySequence(Qt::CTRL | Qt::Key_Slash)}, {ACTION_ID_SHUTDOWN_SERVER_AND_EXIT, tr("Shutdown server and exit"), QIcon(":exit.png"), QKeySequence()}, {ACTION_ID_SET_TRUSTED_CLIENTS_ADDRESSES, tr("Set trusted clients addresses"), QIcon(), QKeySequence()}, {ACTION_ID_JUMP_TO_FRAME, tr("Jump to frame..."), QIcon(), QKeySequence(Qt::Key_J)}, {ACTION_ID_TOGGLE_FRAME_PROPS, tr("Toggle frame properties panel"), QIcon(), QKeySequence(Qt::Key_P)}, {ACTION_ID_SET_OUTPUT_INDEX_0, tr("Switch to output index 0"), QIcon(), QKeySequence(Qt::Key_0)}, {ACTION_ID_SET_OUTPUT_INDEX_1, tr("Switch to output index 1"), QIcon(), QKeySequence(Qt::Key_1)}, {ACTION_ID_SET_OUTPUT_INDEX_2, tr("Switch to output index 2"), QIcon(), QKeySequence(Qt::Key_2)}, {ACTION_ID_SET_OUTPUT_INDEX_3, tr("Switch to output index 3"), QIcon(), QKeySequence(Qt::Key_3)}, {ACTION_ID_SET_OUTPUT_INDEX_4, tr("Switch to output index 4"), QIcon(), QKeySequence(Qt::Key_4)}, {ACTION_ID_SET_OUTPUT_INDEX_5, tr("Switch to output index 5"), QIcon(), QKeySequence(Qt::Key_5)}, {ACTION_ID_SET_OUTPUT_INDEX_6, tr("Switch to output index 6"), QIcon(), QKeySequence(Qt::Key_6)}, {ACTION_ID_SET_OUTPUT_INDEX_7, tr("Switch to output index 7"), QIcon(), QKeySequence(Qt::Key_7)}, {ACTION_ID_SET_OUTPUT_INDEX_8, tr("Switch to output index 8"), QIcon(), QKeySequence(Qt::Key_8)}, {ACTION_ID_SET_OUTPUT_INDEX_9, tr("Switch to output index 9"), QIcon(), QKeySequence(Qt::Key_9)}, {ACTION_ID_SET_OUTPUT_INDEX_10, tr("Switch to output index 10"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_11, tr("Switch to output index 11"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_12, tr("Switch to output index 12"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_13, tr("Switch to output index 13"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_14, tr("Switch to output index 14"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_15, tr("Switch to output index 15"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_16, tr("Switch to output index 16"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_17, tr("Switch to output index 17"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_18, tr("Switch to output index 18"), QIcon(), QKeySequence()}, {ACTION_ID_SET_OUTPUT_INDEX_19, tr("Switch to output index 19"), QIcon(), QKeySequence()}, {ACTION_ID_PREVIOUS_OUTPUT_INDEX, "Switch to previous output index", QIcon(), QKeySequence()}, {ACTION_ID_NEXT_OUTPUT_INDEX, "Switch to next output index", QIcon(), QKeySequence()}, }; } std::vector SettingsManager::getStandardActions() const { return m_standardActions; } QAction * SettingsManager::createStandardAction(const QString & a_actionID, QObject * a_pParent) { StandardAction actionToFind; actionToFind.id = a_actionID; std::vector::const_iterator it = std::find( m_standardActions.begin(), m_standardActions.end(), actionToFind); if(it == m_standardActions.end()) return nullptr; QKeySequence hotkey = getHotkey(it->id); QAction * pAction = new QAction(it->icon, it->title, a_pParent); pAction->setData(it->id); pAction->setShortcut(hotkey); vsedit::disableFontKerning(pAction); return pAction; } QKeySequence SettingsManager::getDefaultHotkey(const QString & a_actionID) const { StandardAction actionToFind; actionToFind.id = a_actionID; std::vector::const_iterator it = std::find( m_standardActions.begin(), m_standardActions.end(), actionToFind); if(it != m_standardActions.end()) return it->hotkey; return QKeySequence(); } QKeySequence SettingsManager::getHotkey(const QString & a_actionID) const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(HOTKEYS_GROUP); if(!settings.contains(a_actionID)) return getDefaultHotkey(a_actionID); QKeySequence hotkey = settings.value(a_actionID).value(); return hotkey; } bool SettingsManager::setHotkey(const QString & a_actionID, const QKeySequence & a_hotkey) { return setValueInGroup(HOTKEYS_GROUP, a_actionID, a_hotkey); } //============================================================================== QTextCharFormat SettingsManager::getDefaultTextFormat( const QString & a_textFormatID) const { // Standard "Icecream" theme QTextCharFormat defaultFormat; if(a_textFormatID == TEXT_FORMAT_ID_COMMON_SCRIPT_TEXT) { QFont commonScriptFont = defaultFormat.font(); #ifdef Q_OS_MACOS commonScriptFont.setFamily("menlo"); #else commonScriptFont.setFamily("monospace"); #endif commonScriptFont.setStyleHint(QFont::Monospace); commonScriptFont.setFixedPitch(true); commonScriptFont.setKerning(false); commonScriptFont.setPointSize(12); defaultFormat.setFont(commonScriptFont); QColor defaultColor = getColor(COLOR_ID_TEXT_BACKGROUND); int refColor = 255 - (defaultColor.red() + defaultColor.green() + defaultColor.blue() + 64) * 0.25; defaultFormat.setForeground(QColor(refColor, refColor, refColor)); } else if(a_textFormatID == TEXT_FORMAT_ID_KEYWORD) { defaultFormat.setForeground(QColor("#0EAA95")); defaultFormat.setFontWeight(QFont::Bold); } else if(a_textFormatID == TEXT_FORMAT_ID_OPERATOR) { defaultFormat.setForeground(QColor("#b9672a")); } else if(a_textFormatID == TEXT_FORMAT_ID_STRING) { defaultFormat.setForeground(QColor("#a500bc")); } else if(a_textFormatID == TEXT_FORMAT_ID_NUMBER) { defaultFormat.setForeground(QColor("#3f8300")); } else if(a_textFormatID == TEXT_FORMAT_ID_COMMENT) { defaultFormat.setForeground(QColor("#387000")); } else if(a_textFormatID == TEXT_FORMAT_ID_VS_CORE) { defaultFormat.setForeground(QColor("#0673E0")); defaultFormat.setFontWeight(QFont::Bold); } else if(a_textFormatID == TEXT_FORMAT_ID_VS_NAMESPACE) { defaultFormat.setForeground(QColor("#0673E0")); defaultFormat.setFontWeight(QFont::Bold); } else if(a_textFormatID == TEXT_FORMAT_ID_VS_FUNCTION) { defaultFormat.setForeground(QColor("#0673E0")); defaultFormat.setFontWeight(QFont::Bold); } else if(a_textFormatID == TEXT_FORMAT_ID_VS_ARGUMENT) { defaultFormat.setForeground(QColor("#a500bc")); } else if(a_textFormatID == TEXT_FORMAT_ID_TIMELINE) { QFont timelineLabelsFont = defaultFormat.font(); timelineLabelsFont.setFamily(QString("Digital Mini")); QFontMetricsF metrics(timelineLabelsFont); qreal factor = (qreal)DEFAULT_TIMELINE_LABELS_HEIGHT / metrics.tightBoundingRect("9").height(); qreal currentFontSize = timelineLabelsFont.pointSizeF(); timelineLabelsFont.setPointSizeF(currentFontSize * factor); defaultFormat.setFont(timelineLabelsFont); } return defaultFormat; } QTextCharFormat SettingsManager::getTextFormat(const QString & a_textFormatID) const { QVariant textFormatValue = valueInGroup(inDarkMode() ? DARK_THEME_GROUP : THEME_GROUP, a_textFormatID); if(textFormatValue.isNull()) return getDefaultTextFormat(a_textFormatID); return vsedit::fromByteArray( textFormatValue.toByteArray()); } bool SettingsManager::setTextFormat(const QString & a_textFormatID, const QTextCharFormat & a_format) { // Only record to settings if it doesn't match the default QTextCharFormat defaultFmt = getTextFormat(a_textFormatID); if(defaultFmt == a_format) return true; return setValueInGroup(inDarkMode() ? DARK_THEME_GROUP : THEME_GROUP, a_textFormatID, vsedit::toByteArray(a_format)); } //============================================================================== QColor SettingsManager::getDefaultColor(const QString & a_colorID) const { QColor defaultColor; const QPalette defaultPalette; if(a_colorID == COLOR_ID_TEXT_BACKGROUND) { if(inDarkMode()) return QColor(16, 16, 24); #ifdef Q_OS_WIN static bool inWindowsDarkMode = defaultPalette.color(QPalette::WindowText).lightness() > defaultPalette.color(QPalette::Window).lightness(); if(inWindowsDarkMode) return QColor(16, 16, 24); else return QColor(255, 255, 255); #else return defaultPalette.color(QPalette::Active, QPalette::Base); #endif } if(a_colorID == COLOR_ID_ACTIVE_LINE) { defaultColor = getColor(COLOR_ID_TEXT_BACKGROUND); int refColor = (defaultColor.red() + defaultColor.green() + defaultColor.blue() + 128) * 0.25; return QColor(refColor, refColor, refColor); } if(a_colorID == COLOR_ID_SELECTION_MATCHES) return QColor("#FFCCFF"); if(a_colorID == COLOR_ID_TIMELINE_BOOKMARKS) return Qt::magenta; return defaultColor; } QColor SettingsManager::getColor(const QString & a_colorID) const { QVariant colorValue = valueInGroup(inDarkMode() ? DARK_THEME_GROUP : THEME_GROUP, a_colorID); if(colorValue.isNull()) return getDefaultColor(a_colorID); return QColor(colorValue.toString()); } bool SettingsManager::setColor(const QString & a_colorID, const QColor & a_color) { // Only record to settings if it doesn't match the default QColor defaultColor = getDefaultColor(a_colorID); if(defaultColor == a_color) return true; return setValueInGroup(inDarkMode() ? DARK_THEME_GROUP : THEME_GROUP, a_colorID, a_color.name()); } //============================================================================== QString SettingsManager::getLastUsedPath() const { QStringList recentFilesList = getRecentFilesList(); if(!recentFilesList.isEmpty()) return recentFilesList.first(); return QString(); } bool SettingsManager::setLastUsedPath(const QString& a_lastUsedPath) { return addToRecentFilesList(a_lastUsedPath); } //============================================================================== QByteArray SettingsManager::getMainWindowGeometry() const { return value(MAIN_WINDOW_GEOMETRY_KEY).toByteArray(); } bool SettingsManager::setMainWindowGeometry( const QByteArray & a_mainWindowGeometry) { return setValue(MAIN_WINDOW_GEOMETRY_KEY, a_mainWindowGeometry); } //============================================================================== bool SettingsManager::getMainWindowMaximized() const { return value(MAIN_WINDOW_MAXIMIZED_KEY, DEFAULT_MAIN_WINDOW_MAXIMIZED).toBool(); } bool SettingsManager::setMainWindowMaximized(bool a_mainWindowMaximized) { return setValue(MAIN_WINDOW_MAXIMIZED_KEY, a_mainWindowMaximized); } //============================================================================== QByteArray SettingsManager::getPreviewDialogGeometry() const { return value(PREVIEW_DIALOG_GEOMETRY_KEY).toByteArray(); } bool SettingsManager::setPreviewDialogGeometry( const QByteArray & a_previewDialogGeometry) { return setValue(PREVIEW_DIALOG_GEOMETRY_KEY, a_previewDialogGeometry); } //============================================================================== bool SettingsManager::getPreviewDialogMaximized() const { return value(PREVIEW_DIALOG_MAXIMIZED_KEY, DEFAULT_PREVIEW_DIALOG_MAXIMIZED).toBool(); } bool SettingsManager::setPreviewDialogMaximized(bool a_previewDialogMaximized) { return setValue(PREVIEW_DIALOG_MAXIMIZED_KEY, a_previewDialogMaximized); } QPoint SettingsManager::getLastPreviewScrollBarPositions() const { int x = valueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_SCROLLBAR_POS_X, 0).toInt(); int y = valueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_SCROLLBAR_POS_Y, 0).toInt(); return QPoint(x, y); } bool SettingsManager::setLastPreviewScrollBarPositions(const QPoint &pos) { int x = pos.x(); int y = pos.y(); return setValueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_SCROLLBAR_POS_X, x) && setValueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_SCROLLBAR_POS_Y, y); } //============================================================================== QByteArray SettingsManager::getJobServerWatcherGeometry() const { return value(JOB_SERVER_WATCHER_GEOMETRY_KEY).toByteArray(); } bool SettingsManager::setJobServerWatcherGeometry( const QByteArray & a_geometry) { return setValue(JOB_SERVER_WATCHER_GEOMETRY_KEY, a_geometry); } //============================================================================== bool SettingsManager::getJobServerWatcherMaximized() const { return value(JOB_SERVER_WATCHER_MAXIMIZED_KEY, DEFAULT_JOB_SERVER_WATCHER_MAXIMIZED).toBool(); } bool SettingsManager::setJobServerWatcherMaximized(bool a_maximized) { return setValue(JOB_SERVER_WATCHER_MAXIMIZED_KEY, a_maximized); } //============================================================================== QByteArray SettingsManager::getJobsHeaderState() const { return value(JOBS_HEADER_STATE_KEY).toByteArray(); } bool SettingsManager::setJobsHeaderState(const QByteArray & a_headerState) { return setValue(JOBS_HEADER_STATE_KEY, a_headerState); } //============================================================================== bool SettingsManager::getAutoLoadLastScript() const { return value(AUTO_LOAD_LAST_SCRIPT_KEY, DEFAULT_AUTO_LOAD_LAST_SCRIPT) .toBool(); } bool SettingsManager::setAutoLoadLastScript(bool a_autoLoadLastScript) { return setValue(AUTO_LOAD_LAST_SCRIPT_KEY, a_autoLoadLastScript); } //============================================================================== bool SettingsManager::getZoomPanelVisible() const { return value(ZOOM_PANEL_VISIBLE_KEY, DEFAULT_ZOOM_PANEL_VISIBLE).toBool(); } bool SettingsManager::setZoomPanelVisible(bool a_zoomPanelVisible) { return setValue(ZOOM_PANEL_VISIBLE_KEY, a_zoomPanelVisible); } //============================================================================== ZoomMode SettingsManager::getZoomMode() const { return (ZoomMode)value(ZOOM_MODE_KEY, (int)DEFAULT_ZOOM_MODE).toInt(); } bool SettingsManager::setZoomMode(ZoomMode a_zoomMode) { return setValue(ZOOM_MODE_KEY, (int)a_zoomMode); } //============================================================================== double SettingsManager::getZoomRatio() const { return value(ZOOM_RATIO_KEY, DEFAULT_ZOOM_RATIO).toDouble(); } bool SettingsManager::setZoomRatio(double a_zoomRatio) { return setValue(ZOOM_RATIO_KEY, a_zoomRatio); } //============================================================================== Qt::TransformationMode SettingsManager::getScaleMode() const { return (Qt::TransformationMode)value(SCALE_MODE_KEY, (int)DEFAULT_SCALE_MODE).toInt(); } bool SettingsManager::setScaleMode(Qt::TransformationMode a_scaleMode) { return setValue(SCALE_MODE_KEY, (int)a_scaleMode); } //============================================================================== CropMode SettingsManager::getCropMode() const { return (CropMode)value(CROP_MODE_KEY, (int)DEFAULT_CROP_MODE).toInt(); } bool SettingsManager::setCropMode(CropMode a_cropMode) { return setValue(CROP_MODE_KEY, (int)a_cropMode); } //============================================================================== int SettingsManager::getCropZoomRatio() const { return value(CROP_ZOOM_RATIO_KEY, DEFAULT_CROP_ZOOM_RATIO).toInt(); } bool SettingsManager::setCropZoomRatio(int a_cropZoomRatio) { return setValue(CROP_ZOOM_RATIO_KEY, a_cropZoomRatio); } //============================================================================== bool SettingsManager::getPromptToSaveChanges() const { return value(PROMPT_TO_SAVE_CHANGES_KEY, DEFAULT_PROMPT_TO_SAVE_CHANGES).toBool(); } bool SettingsManager::setPromptToSaveChanges(bool a_prompt) { return setValue(PROMPT_TO_SAVE_CHANGES_KEY, a_prompt); } //============================================================================== QStringList SettingsManager::getRecentFilesList() const { return value(RECENT_FILES_LIST_KEY).toStringList(); } bool SettingsManager::addToRecentFilesList(const QString & a_filePath) { QFileInfo fileInfo(a_filePath); QString canonicalPath = fileInfo.canonicalFilePath(); QStringList recentFilesList = getRecentFilesList(); recentFilesList.removeAll(canonicalPath); recentFilesList.prepend(canonicalPath); unsigned int maxRecentFilesNumber = getMaxRecentFilesNumber(); while((unsigned int)recentFilesList.size() > maxRecentFilesNumber) recentFilesList.removeLast(); return setValue(RECENT_FILES_LIST_KEY, recentFilesList); } //============================================================================== unsigned int SettingsManager::getMaxRecentFilesNumber() const { return value(MAX_RECENT_FILES_NUMBER_KEY, DEFAULT_MAX_RECENT_FILES_NUMBER).toUInt(); } bool SettingsManager::setMaxRecentFilesNumber( unsigned int a_maxRecentFilesNumber) { return setValue(MAX_RECENT_FILES_NUMBER_KEY, a_maxRecentFilesNumber); } bool SettingsManager::getReloadScriptFromDisk() const { return value(RELOAD_FROM_DISK_KEY, DEFAULT_RELOAD_FROM_DISK).toBool(); } bool SettingsManager::setReloadScriptFromDisk(bool a_reload) { return setValue(RELOAD_FROM_DISK_KEY, a_reload); } //============================================================================== int SettingsManager::getCharactersTypedToStartCompletion() const { return value(CHARACTERS_TYPED_TO_START_COMPLETION_KEY, DEFAULT_CHARACTERS_TYPED_TO_START_COMPLETION).toInt(); } bool SettingsManager::setCharactersTypedToStartCompletion( int a_charactersNumber) { return setValue(CHARACTERS_TYPED_TO_START_COMPLETION_KEY, a_charactersNumber); } //============================================================================== TimeLineSlider::DisplayMode SettingsManager::getTimeLineMode() const { return (TimeLineSlider::DisplayMode)value(TIMELINE_MODE_KEY, (int)DEFAULT_TIMELINE_MODE).toInt(); } bool SettingsManager::setTimeLineMode( TimeLineSlider::DisplayMode a_timeLineMode) { return setValue(TIMELINE_MODE_KEY, (int)a_timeLineMode); } //============================================================================== double SettingsManager::getTimeStep() const { return value(TIME_STEP_KEY, DEFAULT_TIME_STEP).toDouble(); } bool SettingsManager::setTimeStep(double a_timeStep) { return setValue(TIME_STEP_KEY, a_timeStep); } //============================================================================== bool SettingsManager::getColorPickerVisible() const { return value(COLOR_PICKER_VISIBLE_KEY, DEFAULT_COLOR_PICKER_VISIBLE).toBool(); } bool SettingsManager::setColorPickerVisible(bool a_colorPickerVisible) { return setValue(COLOR_PICKER_VISIBLE_KEY, a_colorPickerVisible); } //============================================================================== PlayFPSLimitMode SettingsManager::getPlayFPSLimitMode() const { return (PlayFPSLimitMode)value(PLAY_FPS_LIMIT_MODE_KEY, (int)DEFAULT_PLAY_FPS_LIMIT_MODE).toInt(); } bool SettingsManager::setPlayFPSLimitMode(PlayFPSLimitMode a_mode) { return setValue(PLAY_FPS_LIMIT_MODE_KEY, (int)a_mode); } //============================================================================== double SettingsManager::getPlayFPSLimit() const { return value(PLAY_FPS_LIMIT_KEY, DEFAULT_PLAY_FPS_LIMIT).toDouble(); } bool SettingsManager::setPlayFPSLimit(double a_limit) { return setValue(PLAY_FPS_LIMIT_KEY, a_limit); } //============================================================================== bool SettingsManager::getUseSpacesAsTab() const { return value(USE_SPACES_AS_TAB_KEY, DEFAULT_USE_SPACES_AS_TAB).toBool(); } bool SettingsManager::setUseSpacesAsTab(bool a_value) { return setValue(USE_SPACES_AS_TAB_KEY, a_value); } //============================================================================== int SettingsManager::getSpacesInTab() const { return value(SPACES_IN_TAB_KEY, DEFAULT_SPACES_IN_TAB).toInt(); } bool SettingsManager::setSpacesInTab(int a_spacesNumber) { return setValue(SPACES_IN_TAB_KEY, a_spacesNumber); } //============================================================================== QString SettingsManager::getTabText() const { QString text = "\t"; bool useSpacesAsTab = getUseSpacesAsTab(); if(useSpacesAsTab) { int spacesInTab = getSpacesInTab(); text.fill(' ', spacesInTab); } return text; } //============================================================================== bool SettingsManager::getRememberLastPreviewFrame() const { return value(REMEMBER_LAST_PREVIEW_FRAME_KEY, DEFAULT_REMEMBER_LAST_PREVIEW_FRAME).toBool(); } bool SettingsManager::setRememberLastPreviewFrame(bool a_remember) { return setValue(REMEMBER_LAST_PREVIEW_FRAME_KEY, a_remember); } //============================================================================== int SettingsManager::getLastPreviewFrame(bool a_inPreviewer) const { if(a_inPreviewer) { return valueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_FRAME_KEY, DEFAULT_LAST_PREVIEW_FRAME).toInt(); } return value(LAST_PREVIEW_FRAME_KEY, DEFAULT_LAST_PREVIEW_FRAME).toInt(); } bool SettingsManager::setLastPreviewFrame(int a_frameNumber, bool a_inPreviewer) { if(a_inPreviewer) { return setValueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_FRAME_KEY, a_frameNumber); } return setValue(LAST_PREVIEW_FRAME_KEY, a_frameNumber); } qlonglong SettingsManager::getLastPreviewTimestamp(bool a_inPreviewer) const { if(a_inPreviewer) return valueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_TIMESTAMP_KEY, DEFAULT_LAST_PREVIEW_TIMESTAMP).toLongLong(); return value(LAST_PREVIEW_TIMESTAMP_KEY, DEFAULT_LAST_PREVIEW_TIMESTAMP).toLongLong(); } bool SettingsManager::setLastPreviewTimestamp(qlonglong a_ms, bool a_inPreviewer) { if(a_inPreviewer) return setValueInGroup(PREVIEWER_GROUP, LAST_PREVIEW_TIMESTAMP_KEY, a_ms); return setValue(LAST_PREVIEW_TIMESTAMP_KEY, a_ms); } SyncOutputNodesMode SettingsManager::getSyncOutputMode() const { return (SyncOutputNodesMode)value(SYNC_OUTPUT_MODE_KEY, (int)DEFAULT_SYNC_OUTPUT_MODE).toInt(); } bool SettingsManager::setSyncOutputMode(SyncOutputNodesMode a_mode) { return setValue(SYNC_OUTPUT_MODE_KEY, (int)a_mode); } //============================================================================== QString SettingsManager::getDefaultNewScriptTemplate() { return QString( "import vapoursynth as vs\n" "core = vs.core\n" ); } QString SettingsManager::getNewScriptTemplate() { return value(NEW_SCRIPT_TEMPLATE_KEY, getDefaultNewScriptTemplate()).toString(); } bool SettingsManager::setNewScriptTemplate(const QString & a_text) { return setValue(NEW_SCRIPT_TEMPLATE_KEY, a_text); } //============================================================================== std::vector SettingsManager::getAllCodeSnippets() const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(CODE_SNIPPETS_GROUP); std::vector snippets; QStringList snippetNames = settings.childKeys(); for(const QString & snippetName : snippetNames) { CodeSnippet snippet(snippetName); snippet.text = settings.value(snippetName).toString(); snippets.push_back(snippet); } return snippets; } CodeSnippet SettingsManager::getCodeSnippet(const QString & a_name) const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(CODE_SNIPPETS_GROUP); CodeSnippet snippet; if(!settings.contains(a_name)) return snippet; snippet.name = a_name; snippet.text = valueInGroup(CODE_SNIPPETS_GROUP, a_name).toString(); return snippet; } bool SettingsManager::saveCodeSnippet(const CodeSnippet & a_snippet) { return setValueInGroup(CODE_SNIPPETS_GROUP, a_snippet.name, a_snippet.text); } bool SettingsManager::deleteCodeSnippet(const QString & a_name) { return deleteValueInGroup(CODE_SNIPPETS_GROUP, a_name); } //============================================================================== std::vector SettingsManager::getAllDropFileTemplates() const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(DROP_FILE_TEMPLATES_GROUP); std::vector categories; QStringList categoryNames = settings.childGroups(); for(const QString & categoryName : categoryNames) { settings.beginGroup(categoryName); DropFileCategory category; category.name = categoryName; category.maskList = settings.value(DROP_FILE_CATEGORY_MASK_LIST_KEY).toStringList(); category.sourceTemplate = settings.value(DROP_FILE_CATEGORY_SOURCE_TEMPLATE_KEY).toString(); categories.push_back(category); settings.endGroup(); } return categories; } bool SettingsManager::setDropFileTemplates( const std::vector & a_categories) { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.remove(DROP_FILE_TEMPLATES_GROUP); settings.beginGroup(DROP_FILE_TEMPLATES_GROUP); for(const DropFileCategory & category : a_categories) { settings.beginGroup(category.name); settings.setValue(DROP_FILE_CATEGORY_MASK_LIST_KEY, category.maskList); settings.setValue(DROP_FILE_CATEGORY_SOURCE_TEMPLATE_KEY, category.sourceTemplate); settings.endGroup(); } settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } QString SettingsManager::getDropFileTemplate(const QString & a_filePath) const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(DROP_FILE_TEMPLATES_GROUP); std::vector categories = getAllDropFileTemplates(); for(const DropFileCategory & category : categories) { for(const QString & mask : category.maskList) { auto re = QRegularExpression::fromWildcard(mask, Qt::CaseInsensitive, QRegularExpression::UnanchoredWildcardConversion); if(re.match(a_filePath).hasMatch()) return category.sourceTemplate; } } return QString(DEFAULT_DROP_FILE_TEMPLATE); } //============================================================================== bool SettingsManager::getHighlightSelectionMatches() const { return value(HIGHLIGHT_SELECTION_MATCHES_KEY, DEFAULT_HIGHLIGHT_SELECTION_MATCHES).toBool(); } bool SettingsManager::setHighlightSelectionMatches(bool a_highlight) { return setValue(HIGHLIGHT_SELECTION_MATCHES_KEY, a_highlight); } //============================================================================== int SettingsManager::getHighlightSelectionMatchesMinLength() const { return value(HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH_KEY, DEFAULT_HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH).toInt(); } bool SettingsManager::setHighlightSelectionMatchesMinLength(int a_length) { return setValue(HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH_KEY, a_length); } //============================================================================== bool SettingsManager::getTimeLinePanelVisible() const { return value(TIMELINE_PANEL_VISIBLE_KEY, DEFAULT_TIMELINE_PANEL_VISIBLE).toBool(); } bool SettingsManager::setTimeLinePanelVisible(bool a_visible) { return setValue(TIMELINE_PANEL_VISIBLE_KEY, a_visible); } //============================================================================== bool SettingsManager::getAlwaysKeepCurrentFrame() const { return value(ALWAYS_KEEP_CURRENT_FRAME_KEY, DEFAULT_ALWAYS_KEEP_CURRENT_FRAME).toBool(); } bool SettingsManager::setAlwaysKeepCurrentFrame(bool a_keep) { return setValue(ALWAYS_KEEP_CURRENT_FRAME_KEY, a_keep); } //============================================================================== std::vector SettingsManager::getLogStyles( const QString & a_logName) const { std::vector styles; if(a_logName.isEmpty()) return styles; QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(LOGS_GROUP); QStringList logNames = settings.childGroups(); if(!logNames.contains(a_logName)) return styles; settings.beginGroup(a_logName); settings.beginGroup(LOG_STYLES_GROUP); QStringList styleNames = settings.childGroups(); for(const QString & styleName : styleNames) { settings.beginGroup(styleName); TextBlockStyle style; style.name = styleName; style.title = settings.value(LOG_STYLE_TITLE_KEY).toString(); if(settings.contains(LOG_STYLE_TEXT_FORMAT_KEY)) style.textFormat = qvariant_cast( settings.value(LOG_STYLE_TEXT_FORMAT_KEY)).toCharFormat(); style.isAlias = settings.value(LOG_STYLE_IS_ALIAS_KEY, false).toBool(); style.originalStyleName = settings.value(LOG_STYLE_ORIGINAL_STYLE_NAME_KEY).toString(); style.isVisible = settings.value(LOG_STYLE_IS_VISIBLE_KEY, true).toBool(); styles.push_back(style); settings.endGroup(); } return styles; } bool SettingsManager::setLogStyles(const QString & a_logName, const std::vector a_styles) { if(a_logName.isEmpty()) return false; if(a_styles.empty()) return false; QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(LOGS_GROUP); settings.beginGroup(a_logName); settings.remove(LOG_STYLES_GROUP); settings.beginGroup(LOG_STYLES_GROUP); for(const TextBlockStyle & style : a_styles) { settings.beginGroup(style.name); settings.setValue(LOG_STYLE_TITLE_KEY, style.title); settings.setValue(LOG_STYLE_TEXT_FORMAT_KEY, style.textFormat); settings.setValue(LOG_STYLE_IS_ALIAS_KEY, style.isAlias); settings.setValue(LOG_STYLE_ORIGINAL_STYLE_NAME_KEY, style.originalStyleName); settings.setValue(LOG_STYLE_IS_VISIBLE_KEY, style.isVisible); settings.endGroup(); } settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } //============================================================================== QString SettingsManager::getLastSnapshotExtension() const { return value(LAST_SNAPSHOT_EXTENSION_KEY, DEFAULT_LAST_SNAPSHOT_EXTENSION).toString(); } bool SettingsManager::setLastSnapshotExtension(const QString & a_extension) { return setValue(LAST_SNAPSHOT_EXTENSION_KEY, a_extension); } int SettingsManager::getPNGSnapshotCompressionLevel() const { return value(PNG_COMPRESSION_LEVEL_KEY, DEFAULT_PNG_COMPRESSION_LEVEL).toInt(); } bool SettingsManager::setPNGSnapshotCompressionLevel(int a_level) { return setValue(PNG_COMPRESSION_LEVEL_KEY, a_level); } bool SettingsManager::getShowDebugMessages() const { return value(DEBUG_MESSAGES_KEY, DEFAULT_DEBUG_MESSAGES).toBool(); } bool SettingsManager::setShowDebugMessages(bool a_debug) { return setValue(DEBUG_MESSAGES_KEY, a_debug); } bool SettingsManager::getDarkMode() const { return value(DARK_MODE_KEY, DEFAULT_DARK_MODE).toBool(); } bool SettingsManager::setDarkMode(bool a_dark) { return setValue(DARK_MODE_KEY, a_dark); } bool SettingsManager::getSilentSnapshot() const { return value(SILENT_SNAPSHOT_KEY, DEFAULT_SILENT_SNAPSHOT).toBool(); } bool SettingsManager::setSilentSnapshot(bool a_set) { return setValue(SILENT_SNAPSHOT_KEY, a_set); } QString SettingsManager::getSnapshotTemplate() const { return value(SNAPSHOT_TEMPLATE_KEY, DEFAULT_SNAPSHOT_TEMPLATE).toString(); } bool SettingsManager::setSnapshotTemplate(const QString & a_template) { return setValue(SNAPSHOT_TEMPLATE_KEY, a_template); } //============================================================================== ================================================ FILE: common-src/settings/settings_manager.h ================================================ #ifndef SETTINGSMANAGER_H #define SETTINGSMANAGER_H #include "settings_manager_core.h" #include "settings_definitions.h" #include #include #include #include //============================================================================== class SettingsManager : public SettingsManagerCore { public: SettingsManager(QObject * a_pParent); virtual ~SettingsManager(); //---------------------------------------------------------------------- QAction * createStandardAction(const QString & a_actionID, QObject * a_pParent); std::vector getStandardActions() const; QKeySequence getDefaultHotkey(const QString & a_actionID) const; QKeySequence getHotkey(const QString & a_actionID) const; bool setHotkey(const QString & a_actionID, const QKeySequence & a_hotkey); //---------------------------------------------------------------------- QTextCharFormat getDefaultTextFormat(const QString & a_textFormatID) const; QTextCharFormat getTextFormat(const QString & a_textFormatID) const; bool setTextFormat(const QString & a_textFormatID, const QTextCharFormat & a_format); //---------------------------------------------------------------------- QColor getDefaultColor(const QString & a_colorID) const; QColor getColor(const QString & a_colorID) const; bool setColor(const QString & a_colorID, const QColor & a_color); //---------------------------------------------------------------------- QString getLastUsedPath() const; bool setLastUsedPath(const QString& a_lastUsedPath); QByteArray getMainWindowGeometry() const; bool setMainWindowGeometry(const QByteArray & a_mainWindowGeometry); bool getMainWindowMaximized() const; bool setMainWindowMaximized(bool a_mainWindowMaximized); QByteArray getPreviewDialogGeometry() const; bool setPreviewDialogGeometry(const QByteArray & a_previewDialogGeometry); bool getPreviewDialogMaximized() const; bool setPreviewDialogMaximized(bool a_previewDialogMaximized); QPoint getLastPreviewScrollBarPositions() const; bool setLastPreviewScrollBarPositions(const QPoint &pos); QByteArray getJobServerWatcherGeometry() const; bool setJobServerWatcherGeometry(const QByteArray & a_geometry); bool getJobServerWatcherMaximized() const; bool setJobServerWatcherMaximized(bool a_maximized); QByteArray getJobsHeaderState() const; bool setJobsHeaderState(const QByteArray & a_headerState); bool getAutoLoadLastScript() const; bool setAutoLoadLastScript(bool a_autoLoadLastScript); bool getZoomPanelVisible() const; bool setZoomPanelVisible(bool a_zoomPanelVisible); ZoomMode getZoomMode() const; bool setZoomMode(ZoomMode a_zoomMode); double getZoomRatio() const; bool setZoomRatio(double a_zoomRatio); Qt::TransformationMode getScaleMode() const; bool setScaleMode(Qt::TransformationMode a_scaleMode); CropMode getCropMode() const; bool setCropMode(CropMode a_cropMode); int getCropZoomRatio() const; bool setCropZoomRatio(int a_cropZoomRatio); bool getPromptToSaveChanges() const; bool setPromptToSaveChanges(bool a_prompt); QStringList getRecentFilesList() const; bool addToRecentFilesList(const QString & a_filePath); unsigned int getMaxRecentFilesNumber() const; bool setMaxRecentFilesNumber(unsigned int a_maxRecentFilesNumber); bool getReloadScriptFromDisk() const; bool setReloadScriptFromDisk(bool a_reload); int getCharactersTypedToStartCompletion() const; bool setCharactersTypedToStartCompletion(int a_charactersNumber); TimeLineSlider::DisplayMode getTimeLineMode() const; bool setTimeLineMode(TimeLineSlider::DisplayMode a_timeLineMode); double getTimeStep() const; bool setTimeStep(double a_timeStep); bool getColorPickerVisible() const; bool setColorPickerVisible(bool a_colorPickerVisible); PlayFPSLimitMode getPlayFPSLimitMode() const; bool setPlayFPSLimitMode(PlayFPSLimitMode a_mode); double getPlayFPSLimit() const; bool setPlayFPSLimit(double a_limit); bool getUseSpacesAsTab() const; bool setUseSpacesAsTab(bool a_value); int getSpacesInTab() const; bool setSpacesInTab(int a_spacesNumber); QString getTabText() const; bool getRememberLastPreviewFrame() const; bool setRememberLastPreviewFrame(bool a_remember); int getLastPreviewFrame(bool a_inPreviewer = false) const; bool setLastPreviewFrame(int a_frameNumber, bool a_inPreviewer = false); // timestamp in ms qlonglong getLastPreviewTimestamp(bool a_inPreviewer = false) const; bool setLastPreviewTimestamp(qlonglong a_ms, bool a_inPreviewer = false); SyncOutputNodesMode getSyncOutputMode() const; bool setSyncOutputMode(SyncOutputNodesMode a_mode); QString getDefaultNewScriptTemplate(); QString getNewScriptTemplate(); bool setNewScriptTemplate(const QString & a_text); std::vector getAllCodeSnippets() const; CodeSnippet getCodeSnippet(const QString & a_name) const; bool saveCodeSnippet(const CodeSnippet & a_snippet); bool deleteCodeSnippet(const QString & a_name); std::vector getAllDropFileTemplates() const; bool setDropFileTemplates( const std::vector & a_categories); QString getDropFileTemplate(const QString & a_filePath) const; bool getHighlightSelectionMatches() const; bool setHighlightSelectionMatches(bool a_highlight); int getHighlightSelectionMatchesMinLength() const; bool setHighlightSelectionMatchesMinLength(int a_length); bool getTimeLinePanelVisible() const; bool setTimeLinePanelVisible(bool a_visible); bool getAlwaysKeepCurrentFrame() const; bool setAlwaysKeepCurrentFrame(bool a_keep); std::vector getLogStyles(const QString & a_logName) const; bool setLogStyles(const QString & a_logName, const std::vector a_styles); QString getLastSnapshotExtension() const; bool setLastSnapshotExtension(const QString & a_extension); int getPNGSnapshotCompressionLevel() const; bool setPNGSnapshotCompressionLevel(int a_level); bool getShowDebugMessages() const; bool setShowDebugMessages(bool a_debug); bool getDarkMode() const; bool setDarkMode(bool a_dark); bool inDarkMode() const { return m_bInDarkMode; } bool getSilentSnapshot() const; bool setSilentSnapshot(bool a_set); QString getSnapshotTemplate() const; bool setSnapshotTemplate(const QString & a_template); private: void initializeStandardActions(); std::vector m_standardActions; bool m_bInDarkMode; }; //============================================================================== #endif // SETTINGSMANAGER_H ================================================ FILE: common-src/settings/settings_manager_core.cpp ================================================ #include "settings_manager_core.h" #include #include #include #include #include //============================================================================== const char SETTINGS_FILE_NAME[] = "/vsedit.config"; //============================================================================== const char COMMON_GROUP[] = "common"; const char VAPOURSYNTH_LIBRARY_PATHS_KEY[] = "vapoursynth_library_paths"; const char PREFER_VS_LIBRARIES_FROM_LIST_KEY[] = "prefer_vs_libs_from_list"; const char CHROMA_RESAMPLING_FILTER_KEY[] = "chroma_resampling_filter"; const char YUV_MATRIX_COEFFICIENTS_KEY[] = "yuv_matrix_coefficients"; const char CHROMA_PLACEMENT_KEY[] = "chroma_placement"; const char BICUBIC_FILTER_PARAMETER_B_KEY[] = "bicubic_filter_parameter_b"; const char BICUBIC_FILTER_PARAMETER_C_KEY[] = "bicubic_filter_parameter_c"; const char LANCZOS_FILTER_TAPS_KEY[] = "lanczos_filter_taps"; const char DITHER_TYPE_KEY[] = "dither_type"; const char RECENT_JOB_SERVERS_KEY[] = "recent_job_servers"; const char TRUSTED_CLIENTS_ADDRESSES_KEY[] = "trusted_clients_addresses"; //============================================================================== const char ENCODING_PRESETS_GROUP[] = "encoding_presets"; const char ENCODING_PRESET_ENCODING_TYPE_KEY[] = "encoding_type"; const char ENCODING_PRESET_HEADER_TYPE_KEY[] = "header_type"; const char ENCODING_PRESET_EXECUTABLE_PATH_KEY[] = "executable_path"; const char ENCODING_PRESET_ARGUMENTS_KEY[] = "arguments"; //============================================================================== const char JOBS_GROUP[] = "jobs"; const char JOB_ID_KEY[] = "id"; const char JOB_TYPE_KEY[] = "type"; const char JOB_STATE_KEY[] = "state"; const char JOB_DEPENDS_ON_JOBS_KEY[] = "depends_on_jobs"; const char JOB_TIME_STARTED_KEY[] = "time_started"; const char JOB_TIME_ENDED_KEY[] = "time_ended"; const char JOB_SCRIPT_NAME_KEY[] = "script_name"; const char JOB_SCRIPT_TEXT_KEY[] = "script_text"; const char JOB_ENCODING_TYPE_KEY[] = "encoding_type"; const char JOB_ENCODING_HEADER_TYPE_KEY[] = "encoding_header_type"; const char JOB_EXECUTABLE_PATH_KEY[] = "executable_path"; const char JOB_ARGUMENTS_KEY[] = "arguments"; const char JOB_SHELL_COMMAND_KEY[] = "shell_command"; const char JOB_FIRST_FRAME_KEY[] = "first_frame"; const char JOB_FIRST_FRAME_REAL_KEY[] = "first_frame_real"; const char JOB_LAST_FRAME_KEY[] = "last_frame"; const char JOB_LAST_FRAME_REAL_KEY[] = "last_frame_real"; const char JOB_FRAME_PROCESSED_KEY[] = "frames_processed"; const char JOB_FPS_KEY[] = "fps"; //============================================================================== SettingsManagerCore::SettingsManagerCore(QObject * a_pParent) : QObject(a_pParent) { QString applicationDir = QCoreApplication::applicationDirPath(); bool portableMode = getPortableMode(); if(portableMode) m_settingsFilePath = applicationDir + SETTINGS_FILE_NAME; else { m_settingsFilePath = QStandardPaths::writableLocation( QStandardPaths::GenericConfigLocation) + SETTINGS_FILE_NAME; } } SettingsManagerCore::~SettingsManagerCore() { } //============================================================================== bool SettingsManagerCore::getPortableMode() const { QString applicationDir = QCoreApplication::applicationDirPath(); QString settingsFilePath = applicationDir + SETTINGS_FILE_NAME; QFileInfo settingsFileInfo(settingsFilePath); bool portableMode = (settingsFileInfo.exists() && settingsFileInfo.isWritable()); return portableMode; } bool SettingsManagerCore::setPortableMode(bool a_portableMod) { QString applicationDir = QCoreApplication::applicationDirPath(); QString settingsFilePath = applicationDir + SETTINGS_FILE_NAME; QFileInfo settingsFileInfo(settingsFilePath); bool portableExists = settingsFileInfo.exists(); bool currentModePortable = portableExists && settingsFileInfo.isWritable(); if(a_portableMod == currentModePortable) return true; // In Windows, even if a dir is not writable, a file in it may still be // writable. Therefore, we should take a test by writing a file. bool portableWritable = false; if(a_portableMod && !portableExists) { QFile portableFile(applicationDir + SETTINGS_FILE_NAME); if((portableWritable = portableFile.open(QIODevice::WriteOnly))) portableFile.close(); } else portableWritable = currentModePortable; if(a_portableMod && !portableWritable) return false; QString genericConfigDir = QStandardPaths::writableLocation( QStandardPaths::GenericConfigLocation); QString newSettingsFilePath; if(a_portableMod) newSettingsFilePath = applicationDir + SETTINGS_FILE_NAME; else newSettingsFilePath = genericConfigDir + SETTINGS_FILE_NAME; // When copying portable settings to common folder - another settings // file may already exist there. Need to delete it first. if(QFile::exists(newSettingsFilePath)) { bool settingsFileDeleted = QFile::remove(newSettingsFilePath); if(!settingsFileDeleted) return false; } bool settingsFileCopied = QFile::copy(m_settingsFilePath, newSettingsFilePath); QString oldSettingsFilePath = m_settingsFilePath; m_settingsFilePath = newSettingsFilePath; if(a_portableMod) return settingsFileCopied; else if(settingsFileCopied) { bool portableSettingsFileDeleted = QFile::remove(oldSettingsFilePath); return portableSettingsFileDeleted; } return false; } bool SettingsManagerCore::getPreferVSLibrariesFromList() const { return value(PREFER_VS_LIBRARIES_FROM_LIST_KEY, DEFAULT_PREFER_VS_LIBRARIES_FROM_LIST).toBool(); } bool SettingsManagerCore::setPreferVSLibrariesFromList(bool a_prior) { return setValue(PREFER_VS_LIBRARIES_FROM_LIST_KEY, a_prior); } //============================================================================== QVariant SettingsManagerCore::valueInGroup(const QString & a_group, const QString & a_key, const QVariant & a_defaultValue) const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(a_group); return settings.value(a_key, a_defaultValue); } bool SettingsManagerCore::setValueInGroup(const QString & a_group, const QString & a_key, const QVariant & a_value) { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(a_group); settings.setValue(a_key, a_value); settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } bool SettingsManagerCore::deleteValueInGroup(const QString & a_group, const QString & a_key) { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(a_group); settings.remove(a_key); settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } //============================================================================== QVariant SettingsManagerCore::value(const QString & a_key, const QVariant & a_defaultValue) const { return valueInGroup(COMMON_GROUP, a_key, a_defaultValue); } bool SettingsManagerCore::setValue(const QString & a_key, const QVariant & a_value) { return setValueInGroup(COMMON_GROUP, a_key, a_value); } //============================================================================== QStringList SettingsManagerCore::getVapourSynthLibraryPaths() const { QStringList paths = value(VAPOURSYNTH_LIBRARY_PATHS_KEY).toStringList(); paths.removeDuplicates(); return paths; } bool SettingsManagerCore::setVapourSynthLibraryPaths( const QStringList & a_pathsList) { return setValue(VAPOURSYNTH_LIBRARY_PATHS_KEY, a_pathsList); } //============================================================================== ResamplingFilter SettingsManagerCore::getChromaResamplingFilter() const { return (ResamplingFilter)value(CHROMA_RESAMPLING_FILTER_KEY, (int)DEFAULT_CHROMA_RESAMPLING_FILTER).toInt(); } bool SettingsManagerCore::setChromaResamplingFilter(ResamplingFilter a_filter) { return setValue(CHROMA_RESAMPLING_FILTER_KEY, (int)a_filter); } //============================================================================== YuvMatrixCoefficients SettingsManagerCore::getYuvMatrixCoefficients() const { return (YuvMatrixCoefficients)value(YUV_MATRIX_COEFFICIENTS_KEY, (int)DEFAULT_YUV_MATRIX_COEFFICIENTS).toInt(); } bool SettingsManagerCore::setYuvMatrixCoefficients( YuvMatrixCoefficients a_matrix) { return setValue(YUV_MATRIX_COEFFICIENTS_KEY, (int)a_matrix); } //============================================================================== ChromaPlacement SettingsManagerCore::getChromaPlacement() const { return (ChromaPlacement)value(CHROMA_PLACEMENT_KEY, (int)DEFAULT_CHROMA_PLACEMENT).toInt(); } bool SettingsManagerCore::setChromaPlacement(ChromaPlacement a_placement) { return setValue(CHROMA_PLACEMENT_KEY, (int)a_placement); } //============================================================================== double SettingsManagerCore::getBicubicFilterParameterB() const { return value(BICUBIC_FILTER_PARAMETER_B_KEY, DEFAULT_BICUBIC_FILTER_PARAMETER_B).toDouble(); } bool SettingsManagerCore::setBicubicFilterParameterB(double a_parameterB) { return setValue(BICUBIC_FILTER_PARAMETER_B_KEY, a_parameterB); } //============================================================================== double SettingsManagerCore::getBicubicFilterParameterC() const { return value(BICUBIC_FILTER_PARAMETER_C_KEY, DEFAULT_BICUBIC_FILTER_PARAMETER_C).toDouble(); } bool SettingsManagerCore::setBicubicFilterParameterC(double a_parameterC) { return setValue(BICUBIC_FILTER_PARAMETER_C_KEY, a_parameterC); } //============================================================================== int SettingsManagerCore::getLanczosFilterTaps() const { return value(LANCZOS_FILTER_TAPS_KEY, DEFAULT_LANCZOS_FILTER_TAPS).toInt(); } bool SettingsManagerCore::setLanczosFilterTaps(int a_taps) { return setValue(LANCZOS_FILTER_TAPS_KEY, a_taps); } DitherType SettingsManagerCore::getDitherType() const { return (DitherType)value(DITHER_TYPE_KEY, (int)DEFAULT_DITHER_TYPE).toInt(); } bool SettingsManagerCore::setDitherType(DitherType a_dither) { return setValue(DITHER_TYPE_KEY, (int)a_dither); } //============================================================================== std::vector SettingsManagerCore::getAllEncodingPresets() const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(ENCODING_PRESETS_GROUP); std::vector presets; QStringList presetNames = settings.childGroups(); for(const QString & presetName : presetNames) { settings.beginGroup(presetName); EncodingPreset preset; preset.name = presetName; preset.type = (EncodingType)settings.value( ENCODING_PRESET_ENCODING_TYPE_KEY, (int)DEFAULT_ENCODING_TYPE) .toInt(); preset.headerType = (EncodingHeaderType)settings.value( ENCODING_PRESET_HEADER_TYPE_KEY, (int)DEFAULT_ENCODING_HEADER_TYPE) .toInt(); preset.executablePath = settings.value( ENCODING_PRESET_EXECUTABLE_PATH_KEY).toString(); preset.arguments = settings.value( ENCODING_PRESET_ARGUMENTS_KEY).toString(); presets.push_back(preset); settings.endGroup(); } return presets; } EncodingPreset SettingsManagerCore::getEncodingPreset(const QString & a_name) const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(ENCODING_PRESETS_GROUP); EncodingPreset preset; QStringList presetNames = settings.childGroups(); if(!presetNames.contains(a_name)) return preset; preset.name = a_name; settings.beginGroup(a_name); preset.type = (EncodingType)settings.value( ENCODING_PRESET_ENCODING_TYPE_KEY, (int)DEFAULT_ENCODING_TYPE) .toInt(); preset.headerType = (EncodingHeaderType)settings.value( ENCODING_PRESET_HEADER_TYPE_KEY, (int)DEFAULT_ENCODING_HEADER_TYPE) .toInt(); preset.executablePath = settings.value( ENCODING_PRESET_EXECUTABLE_PATH_KEY).toString(); preset.arguments = settings.value( ENCODING_PRESET_ARGUMENTS_KEY).toString(); return preset; } bool SettingsManagerCore::saveEncodingPreset(const EncodingPreset & a_preset) { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(ENCODING_PRESETS_GROUP); settings.beginGroup(a_preset.name); settings.setValue(ENCODING_PRESET_ENCODING_TYPE_KEY, (int)a_preset.type); settings.setValue(ENCODING_PRESET_HEADER_TYPE_KEY, (int)a_preset.headerType); settings.setValue(ENCODING_PRESET_EXECUTABLE_PATH_KEY, a_preset.executablePath); settings.setValue(ENCODING_PRESET_ARGUMENTS_KEY, a_preset.arguments); settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } bool SettingsManagerCore::deleteEncodingPreset(const QString & a_name) { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(ENCODING_PRESETS_GROUP); QStringList subGroups = settings.childGroups(); if(!subGroups.contains(a_name)) return false; settings.remove(a_name); settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } //============================================================================== std::vector SettingsManagerCore::getJobs() const { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.beginGroup(JOBS_GROUP); std::vector jobs; QStringList numbers = settings.childGroups(); for(const QString & number : numbers) { settings.beginGroup(number); JobProperties job; QString idString = settings.value(JOB_ID_KEY).toString(); if(idString.isEmpty()) job.id = QUuid::createUuid(); else job.id = QUuid(idString); job.type = (JobType)settings.value(JOB_TYPE_KEY, (int)DEFAULT_JOB_TYPE).toInt(); job.jobState = (JobState)settings.value(JOB_STATE_KEY, (int)DEFAULT_JOB_STATE).toInt(); QStringList dependencyIdStrings = settings.value(JOB_DEPENDS_ON_JOBS_KEY).toStringList(); for(const QString & dependencyIdString : dependencyIdStrings) { QUuid dependencyId(dependencyIdString); job.dependsOnJobIds.push_back(dependencyId); } job.timeStarted = settings.value(JOB_TIME_STARTED_KEY).toDateTime(); job.timeEnded = settings.value(JOB_TIME_ENDED_KEY).toDateTime(); job.scriptName = settings.value(JOB_SCRIPT_NAME_KEY).toString(); job.scriptText = settings.value(JOB_SCRIPT_TEXT_KEY).toString(); job.encodingType = (EncodingType)settings.value(JOB_ENCODING_TYPE_KEY, (int)DEFAULT_ENCODING_TYPE).toInt(); job.encodingHeaderType = (EncodingHeaderType)settings.value( JOB_ENCODING_HEADER_TYPE_KEY, (int)DEFAULT_ENCODING_HEADER_TYPE).toInt(); job.executablePath = settings.value(JOB_EXECUTABLE_PATH_KEY).toString(); job.arguments = settings.value(JOB_ARGUMENTS_KEY).toString(); job.shellCommand = settings.value(JOB_SHELL_COMMAND_KEY).toString(); job.firstFrame = settings.value(JOB_FIRST_FRAME_KEY, DEFAULT_JOB_FIRST_FRAME).toInt(); job.firstFrameReal = settings.value(JOB_FIRST_FRAME_REAL_KEY, job.firstFrame).toInt(); job.lastFrame = settings.value(JOB_LAST_FRAME_KEY, DEFAULT_JOB_LAST_FRAME).toInt(); job.lastFrameReal = settings.value(JOB_LAST_FRAME_REAL_KEY, job.lastFrame).toInt(); job.framesProcessed = settings.value(JOB_FRAME_PROCESSED_KEY, DEFAULT_JOB_FRAMES_PROCESSED).toInt(); job.fps = settings.value(JOB_FPS_KEY, DEFAULT_JOB_FPS).toDouble(); jobs.push_back(job); settings.endGroup(); } return jobs; } bool SettingsManagerCore::setJobs(const std::vector & a_jobs) { QSettings settings(m_settingsFilePath, QSettings::IniFormat); settings.remove(JOBS_GROUP); settings.beginGroup(JOBS_GROUP); for(size_t i = 0; i < a_jobs.size(); ++i) { const JobProperties & job = a_jobs[i]; settings.beginGroup(QString("%1").arg(i, 7, 10, QChar('0'))); settings.setValue(JOB_ID_KEY, job.id.toString()); settings.setValue(JOB_TYPE_KEY, (int)job.type); settings.setValue(JOB_STATE_KEY, (int)job.jobState); QStringList dependencyIdStrings; for(const QUuid & id : job.dependsOnJobIds) dependencyIdStrings << id.toString(); settings.setValue(JOB_DEPENDS_ON_JOBS_KEY, dependencyIdStrings); settings.setValue(JOB_TIME_STARTED_KEY, job.timeStarted); settings.setValue(JOB_TIME_ENDED_KEY, job.timeEnded); settings.setValue(JOB_SCRIPT_NAME_KEY, job.scriptName); settings.setValue(JOB_SCRIPT_TEXT_KEY, job.scriptText); settings.setValue(JOB_ENCODING_TYPE_KEY, (int)job.encodingType); settings.setValue(JOB_ENCODING_HEADER_TYPE_KEY, (int)job.encodingHeaderType); settings.setValue(JOB_EXECUTABLE_PATH_KEY, job.executablePath); settings.setValue(JOB_ARGUMENTS_KEY, job.arguments); settings.setValue(JOB_SHELL_COMMAND_KEY, job.shellCommand); settings.setValue(JOB_FIRST_FRAME_KEY, job.firstFrame); settings.setValue(JOB_FIRST_FRAME_REAL_KEY, job.firstFrameReal); settings.setValue(JOB_LAST_FRAME_KEY, job.lastFrame); settings.setValue(JOB_LAST_FRAME_REAL_KEY, job.lastFrameReal); settings.setValue(JOB_FRAME_PROCESSED_KEY, job.framesProcessed); settings.setValue(JOB_FPS_KEY, job.fps); settings.endGroup(); } settings.sync(); bool success = (QSettings::NoError == settings.status()); return success; } //============================================================================== QStringList SettingsManagerCore::getRecentJobServers() const { QStringList recentServers = value(RECENT_JOB_SERVERS_KEY).toStringList(); recentServers.removeDuplicates(); return recentServers.mid(0, DEFAULT_RECENT_JOB_SERVERS_NUMBER); } bool SettingsManagerCore::setRecentJobServers(const QStringList & a_servers) { QStringList recentServers = a_servers; recentServers.removeDuplicates(); recentServers = recentServers.mid(0, DEFAULT_RECENT_JOB_SERVERS_NUMBER); return setValue(RECENT_JOB_SERVERS_KEY, recentServers); } //============================================================================== QStringList SettingsManagerCore::getTrustedClientsAddresses() const { QStringList addresses = value(TRUSTED_CLIENTS_ADDRESSES_KEY).toStringList(); addresses.removeDuplicates(); return addresses; } bool SettingsManagerCore::setTrustedClientsAddresses( const QStringList & a_addresses) { QStringList addresses = a_addresses; addresses.removeDuplicates(); return setValue(TRUSTED_CLIENTS_ADDRESSES_KEY, addresses); } //============================================================================== ================================================ FILE: common-src/settings/settings_manager_core.h ================================================ #ifndef SETTINGS_MANAGER_CORE_H_INCLUDED #define SETTINGS_MANAGER_CORE_H_INCLUDED #include "settings_definitions_core.h" #include #include #include /// Base class that manages non-GUI related settings class SettingsManagerCore : public QObject { public: SettingsManagerCore(QObject * a_pParent); virtual ~SettingsManagerCore(); //---------------------------------------------------------------------- bool getPortableMode() const; bool setPortableMode(bool a_portableMod); //---------------------------------------------------------------------- QStringList getVapourSynthLibraryPaths() const; bool setVapourSynthLibraryPaths(const QStringList & a_pathsList); bool getPreferVSLibrariesFromList() const; bool setPreferVSLibrariesFromList(bool a_prior); ResamplingFilter getChromaResamplingFilter() const; bool setChromaResamplingFilter(ResamplingFilter a_filter); YuvMatrixCoefficients getYuvMatrixCoefficients() const; bool setYuvMatrixCoefficients(YuvMatrixCoefficients a_matrix); ChromaPlacement getChromaPlacement() const; bool setChromaPlacement(ChromaPlacement a_placement); double getBicubicFilterParameterB() const; bool setBicubicFilterParameterB(double a_parameterB); double getBicubicFilterParameterC() const; bool setBicubicFilterParameterC(double a_parameterC); int getLanczosFilterTaps() const; bool setLanczosFilterTaps(int a_taps); DitherType getDitherType() const; bool setDitherType(DitherType a_dither); std::vector getAllEncodingPresets() const; EncodingPreset getEncodingPreset(const QString & a_name) const; bool saveEncodingPreset(const EncodingPreset & a_preset); bool deleteEncodingPreset(const QString & a_name); std::vector getJobs() const; bool setJobs(const std::vector & a_jobs); QStringList getRecentJobServers() const; bool setRecentJobServers(const QStringList & a_servers); QStringList getTrustedClientsAddresses() const; bool setTrustedClientsAddresses(const QStringList & a_addresses); protected: QVariant valueInGroup(const QString & a_group, const QString & a_key, const QVariant & a_defaultValue = QVariant()) const; bool setValueInGroup(const QString & a_group, const QString & a_key, const QVariant & a_value); bool deleteValueInGroup(const QString & a_group, const QString & a_key); QVariant value(const QString & a_key, const QVariant & a_defaultValue = QVariant()) const; bool setValue(const QString & a_key, const QVariant & a_value); QString m_settingsFilePath; }; #endif // SETTINGS_MANAGER_CORE_H_INCLUDED ================================================ FILE: common-src/timeline_slider/timeline_slider.cpp ================================================ #include "timeline_slider.h" #include "../helpers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== TimeLineSlider::TimeLineSlider(QWidget * a_pParent) : QWidget(a_pParent) , m_maxFrame(999) , m_fps(0.0) , m_currentFrame(0) , m_pointerAtFrame(0) , m_displayMode(Time) , m_bigStep(10) , m_sideMargin(6) , m_bottomMargin(2) , m_slideLineHeight(12) , m_slideLineFrameWidth(1) , m_slideLineTicksSpacing(1) , m_shortTickHeight(2) , m_mediumTickHeight(3) , m_longTickHeight(4) , m_tickTextSpacing(1) , m_textHeight(6) , m_topMargin(2) , m_minimumTicksSpacing(4) , m_sliderPressed(false) , m_labelsFont("Digital Mini") { Q_ASSERT(m_bigStep > 0); setAutoFillBackground(true); setFocusPolicy(Qt::StrongFocus); setMouseTracking(true); QFontMetricsF metrics(m_labelsFont); qreal factor = (qreal)m_textHeight / metrics.tightBoundingRect("9").height(); m_labelsFont.setPointSizeF(m_labelsFont.pointSizeF() * factor); m_slideLineColor = palette().color(QPalette::Base); m_activeFrameColor = palette().color(QPalette::Highlight); m_inactiveFrameColor = palette().color(QPalette::Dark); m_currentFramePointerColor = palette().color(QPalette::Dark); m_slidingPointerColor = palette().color(QPalette::Text); m_bookmarkColor = Qt::magenta; m_colorRoleMap = { {SlideLine, &m_slideLineColor}, {ActiveFrame, &m_activeFrameColor}, {InactiveFrame, &m_inactiveFrameColor}, {CurrentFramePointer, &m_currentFramePointerColor}, {SlidingPointer, &m_slidingPointerColor}, {Bookmark, &m_bookmarkColor}, }; setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); recalculateMinimumSize(); } // END OF TimeLineSlider::TimeLineSlider(QWidget * a_pParent) //============================================================================== TimeLineSlider::~TimeLineSlider() { } // END OF TimeLineSlider::~TimeLineSlider() //============================================================================== int TimeLineSlider::frame() const { return m_currentFrame; } // END OF int TimeLineSlider::frame() const //============================================================================== void TimeLineSlider::setFrame(int a_frame, bool a_refreshCache) { int oldCurrentFrame = m_currentFrame; if(a_frame > m_maxFrame) m_currentFrame = m_maxFrame; else m_currentFrame = a_frame; if(m_currentFrame != m_pointerAtFrame) m_pointerAtFrame = m_currentFrame; if(m_currentFrame == oldCurrentFrame) return; repaint(); emit signalFrameChanged(m_currentFrame, a_refreshCache); } // END OF void TimeLineSlider::setFrame(int a_frame, bool a_refreshCache) //============================================================================== void TimeLineSlider::setFramesNumber(int a_framesNumber, bool a_refreshCache) { m_maxFrame = a_framesNumber - 1; if(m_currentFrame > m_maxFrame) setFrame(m_maxFrame, a_refreshCache); repaint(); } // END OF void TimeLineSlider::setFramesNumber(int a_framesNumber, // bool a_refreshCache) //============================================================================== void TimeLineSlider::setFPS(double a_fps) { m_fps = a_fps; repaint(); } // END OF void TimeLineSlider::setFPS(double a_fps) //============================================================================== TimeLineSlider::DisplayMode TimeLineSlider::displayMode() const { return m_displayMode; } // END OF TimeLineSlider::DisplayMode TimeLineSlider::displayMode() const //============================================================================== void TimeLineSlider::setDisplayMode(DisplayMode a_displayMode) { m_displayMode = a_displayMode; repaint(); } // END OF void TimeLineSlider::setDisplayMode(DisplayMode a_displayMode) //============================================================================== void TimeLineSlider::setBigStep(int a_bigStep) { m_bigStep = std::max(std::abs(a_bigStep), 1); } // END OF void TimeLineSlider::setBigStep(int a_bigStep) //============================================================================== void TimeLineSlider::setLabelsFont(const QFont & a_font) { m_labelsFont = a_font; QFontMetrics metrics(m_labelsFont); m_textHeight = metrics.tightBoundingRect("9").height(); recalculateMinimumSize(); update(); } // END OF void TimeLineSlider::setLabelsFont(const QFont & a_font) //============================================================================== void TimeLineSlider::setColor(ColorRole a_role, const QColor & a_color) { std::map::iterator it = m_colorRoleMap.find(a_role); if(it == m_colorRoleMap.end()) return; *(it->second) = a_color; update(); } // END OF void TimeLineSlider::setColor(ColorRole a_role, // const QColor & a_color) //============================================================================== void TimeLineSlider::addBookmark(int a_bookmark) { if(a_bookmark < 0) return; m_bookmarks.insert(a_bookmark); update(); } // END OF void TimeLineSlider::addBookmark(int a_bookmark) //============================================================================== void TimeLineSlider::removeBookmark(int a_bookmark) { m_bookmarks.erase(a_bookmark); update(); } // END OF void TimeLineSlider::removeBookmark(int a_bookmark) //============================================================================== std::set TimeLineSlider::bookmarks() const { return m_bookmarks; } // END OF std::set TimeLineSlider::bookmarks() const //============================================================================== void TimeLineSlider::setBookmarks(const std::set & a_bookmarks) { std::set::iterator it = a_bookmarks.lower_bound(0); m_bookmarks = std::set(it, a_bookmarks.end()); update(); } // END OF void TimeLineSlider::setBookmarks(const std::set & a_bookmarks) //============================================================================== void TimeLineSlider::clearBookmarks() { m_bookmarks.clear(); update(); } // END OF void TimeLineSlider::clearBookmarks() //============================================================================== int TimeLineSlider::getClosestBookmark(int a_frame) const { if(m_bookmarks.size() == 0) return a_frame; if(m_bookmarks.size() == 1) return *m_bookmarks.begin(); std::set::iterator next = m_bookmarks.upper_bound(a_frame); if(next == m_bookmarks.begin()) return *next; if(next == m_bookmarks.end()) return *m_bookmarks.rbegin(); if(*next > m_maxFrame) { next = m_bookmarks.upper_bound(m_maxFrame); next--; return *next; } std::set::iterator prev = next; prev--; int backDiff = a_frame - *prev; int forwardDiff = *next - a_frame; if(forwardDiff < backDiff) return *next; return *prev; } // END OF int TimeLineSlider::getClosestBookmark(int a_frame) const //============================================================================== void TimeLineSlider::slotStepUp() { if(m_currentFrame < m_maxFrame) setFrame(m_currentFrame + 1, false); } // END OF void TimeLineSlider::slotStepUp() //============================================================================== void TimeLineSlider::slotStepDown() { if(m_currentFrame > 0) setFrame(m_currentFrame - 1, false); } // END OF void TimeLineSlider::slotStepDown() //============================================================================== void TimeLineSlider::slotBigStepUp() { slotStepBy(m_bigStep); } // END OF void TimeLineSlider::slotBigStepUp() //============================================================================== void TimeLineSlider::slotBigStepDown() { slotStepBy(-m_bigStep); } // END OF void TimeLineSlider::slotBigStepDown() //============================================================================== void TimeLineSlider::slotStepBy(int a_step) { if(a_step < 0) { if(-a_step <= m_currentFrame) setFrame(m_currentFrame + a_step, false); else setFrame(0, false); } else { if(a_step <= (m_maxFrame - m_currentFrame)) setFrame(m_currentFrame + a_step, false); else setFrame(m_maxFrame, false); } } // END OF void TimeLineSlider::slotStepBy(int a_step) //============================================================================== void TimeLineSlider::slotStepBySeconds(double a_seconds) { if(m_fps == 0.0) return; int framesStep = std::round(a_seconds * m_fps); slotStepBy(framesStep); } // END OF void TimeLineSlider::slotStepBySeconds(double a_seconds) //============================================================================== void TimeLineSlider::slotBookmarkCurrentFrame() { addBookmark(m_currentFrame); } // END OF void TimeLineSlider::slotBookmarkCurrentFrame() //============================================================================== void TimeLineSlider::slotUnbookmarkCurrentFrame() { removeBookmark(m_currentFrame); } // END OF void TimeLineSlider::slotUnbookmarkCurrentFrame() //============================================================================== void TimeLineSlider::slotGoToPreviousBookmark() { if(m_bookmarks.size() == 0) return; if(m_currentFrame == 0) return; std::set::iterator it = m_bookmarks.upper_bound(m_currentFrame - 1); if(it == m_bookmarks.end()) { setFrame(*m_bookmarks.rbegin(), false); return; } if(it == m_bookmarks.begin()) return; it--; if(*it < 0) return; setFrame(*it, false); } // END OF void TimeLineSlider::slotGoToPreviousBookmark() //============================================================================== void TimeLineSlider::slotGoToNextBookmark() { if(m_bookmarks.size() == 0) return; std::set::iterator it = m_bookmarks.upper_bound(m_currentFrame); if(it == m_bookmarks.end()) return; if(*it > m_maxFrame) return; setFrame(*it, false); } // END OF void TimeLineSlider::slotGoToNextBookmark() //============================================================================== void TimeLineSlider::keyPressEvent(QKeyEvent * a_pEvent) { if(a_pEvent->modifiers() != Qt::NoModifier) { QWidget::keyPressEvent(a_pEvent); return; } int key = a_pEvent->key(); if((key == Qt::Key_Left) || (key == Qt::Key_Down)) { slotStepDown(); a_pEvent->accept(); return; } else if((key == Qt::Key_Right) || (key == Qt::Key_Up)) { slotStepUp(); a_pEvent->accept(); return; } else if(key == Qt::Key_PageUp) { slotBigStepUp(); a_pEvent->accept(); return; } else if(key == Qt::Key_PageDown) { slotBigStepDown(); a_pEvent->accept(); return; } else if(key == Qt::Key_Home) { setFrame(0, true); a_pEvent->accept(); return; } else if(key == Qt::Key_End) { setFrame(m_maxFrame, true); a_pEvent->accept(); return; } QWidget::keyPressEvent(a_pEvent); } // END OF void TimeLineSlider::keyPressEvent(QKeyEvent * a_pEvent) //============================================================================== void TimeLineSlider::mouseMoveEvent(QMouseEvent * a_pEvent) { if(m_sliderPressed) { setPointerAtFrame(a_pEvent); repaint(); emit signalSliderMoved(m_pointerAtFrame); } // Display tooltip if(m_sliderPressed || slideLineActiveRect().contains(a_pEvent->pos())) { int l_frame = m_sliderPressed ? m_pointerAtFrame : posToFrame(a_pEvent->pos().x()); QString tipString = QString::number(l_frame); if(m_fps != 0.0) { tipString += " - "; tipString += vsedit::timeToString(((double)l_frame) / m_fps); } QToolTip::showText(a_pEvent->globalPosition().toPoint(), tipString); } QWidget::mouseMoveEvent(a_pEvent); } // END OF void TimeLineSlider::mouseMoveEvent(QMouseEvent * a_pEvent) //============================================================================== void TimeLineSlider::mousePressEvent(QMouseEvent * a_pEvent) { if(slideLineActiveRect().contains(a_pEvent->pos())) { m_sliderPressed = true; setPointerAtFrame(a_pEvent); repaint(); emit signalSliderPressed(); } QWidget::mousePressEvent(a_pEvent); } // END OF void TimeLineSlider::mousePressEvent(QMouseEvent * a_pEvent) //============================================================================== void TimeLineSlider::mouseReleaseEvent(QMouseEvent * a_pEvent) { bool emitSignal = m_sliderPressed; m_sliderPressed = false; setFrame(m_pointerAtFrame, true); if(emitSignal) emit signalSliderReleased(); QWidget::mouseReleaseEvent(a_pEvent); } // END OF void TimeLineSlider::mouseReleaseEvent(QMouseEvent * a_pEvent) //============================================================================== void TimeLineSlider::paintEvent(QPaintEvent * a_pEvent) { if(slideLineActiveRect().width() < 2) { a_pEvent->ignore(); return; } QPainter painter(this); // Slide line QRect l_slideLineRect = slideLineRect(); painter.setBrush(m_slideLineColor); QPen pen = painter.pen(); QColor frameColor; if(hasFocus()) frameColor = m_activeFrameColor; else frameColor = m_inactiveFrameColor; pen.setColor(frameColor); pen.setWidth(m_slideLineFrameWidth); painter.setPen(pen); QMargins slideLineMargins(0, 0, m_slideLineFrameWidth, m_slideLineFrameWidth); painter.drawRect(l_slideLineRect.marginsRemoved(slideLineMargins)); // Bookmarks pen.setColor(m_bookmarkColor); pen.setWidth(1); painter.setPen(pen); int pointerTop = l_slideLineRect.top() + m_slideLineFrameWidth; int pointerBottom = l_slideLineRect.bottom() - m_slideLineFrameWidth; for(int i : m_bookmarks) { if(i > m_maxFrame) break; int pointerPos = frameToPos(i); painter.drawLine(pointerPos, pointerTop, pointerPos, pointerBottom); } // Current frame pointer painter.setPen(m_currentFramePointerColor); int pointerPos = frameToPos(m_currentFrame); painter.drawLine(pointerPos, pointerTop, pointerPos, pointerBottom); // Sliding frame pointer painter.setPen(m_slidingPointerColor); pointerPos = frameToPos(m_pointerAtFrame); painter.drawLine(pointerPos, pointerTop, pointerPos, pointerBottom); // Ruler painter.setPen(palette().color(QPalette::WindowText)); DisplayMode l_displayMode = Time; if((m_displayMode == Frames) || (m_fps == 0.0)) l_displayMode = Frames; double unitsInPixel = (double)m_maxFrame / (double)(slideLineInnerWidth() - 1); double maxUnits = (double)m_maxFrame; bool ticksAtExactFrames = (unitsInPixel < 1.0 / (double)m_minimumTicksSpacing); if(l_displayMode == Time) { unitsInPixel /= m_fps; maxUnits /= m_fps; } painter.setFont(m_labelsFont); int tickBottom = height() - m_bottomMargin - m_slideLineHeight - m_slideLineTicksSpacing - 1; int shortTickTop = tickBottom - m_shortTickHeight + 1; int mediumTickTop = tickBottom - m_mediumTickHeight + 1; int longTickTop = tickBottom - m_longTickHeight + 1; int startPos = m_sideMargin + m_slideLineFrameWidth; QPoint labelPos(0, longTickTop - m_tickTextSpacing); QFontMetricsF labelsFontMetrics(m_labelsFont); if(ticksAtExactFrames) { for(int n = 0; n <= m_maxFrame; ++n) { int tickPos = frameToPos(n); if(n % 10 == 0) { painter.drawLine(tickPos, longTickTop, tickPos, tickBottom); QString labelString; if(l_displayMode == Frames) labelString = QString::number(n); else labelString = vsedit::timeToString(((double)n) / m_fps); labelPos.setX(tickPos - labelsFontMetrics.horizontalAdvance( labelString) / 2 + 1); painter.drawText(labelPos, labelString); } else if(n % 5 == 0) painter.drawLine(tickPos, mediumTickTop, tickPos, tickBottom); else painter.drawLine(tickPos, shortTickTop, tickPos, tickBottom); } } else { double minUnitsPerTick = unitsInPixel * (double)m_minimumTicksSpacing; double unitsPerTick = std::pow(10.0, std::floor(std::log10((double)maxUnits))); while(unitsPerTick > minUnitsPerTick) unitsPerTick /= 10.0; // Do sane division for seconds and minutes. if((l_displayMode == Time) && (minUnitsPerTick > 0.1)) { unitsPerTick = 0.2; // 2 sec if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.5; // 5 sec if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 10 sec if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 20 sec if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 1.5; // 30 sec if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 1 min if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 2 min if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.5; // 5 min if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 10 min if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 20 min if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 1.5; // 30 min if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; // 1 hour } while(unitsPerTick < minUnitsPerTick) { unitsPerTick *= 2.0; if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 1.25; if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; if(unitsPerTick < minUnitsPerTick) unitsPerTick *= 2.0; } double units = 0.0; int n = 0; while(units - maxUnits < 0.001) { int tickPos = startPos + (int)(units / unitsInPixel); if(n % 10 == 0) { painter.drawLine(tickPos, longTickTop, tickPos, tickBottom); QString labelString; if(l_displayMode == Frames) labelString = QString::number(units); else labelString = vsedit::timeToString(units); labelPos.setX(tickPos - labelsFontMetrics.horizontalAdvance( labelString) / 2 + 1); painter.drawText(labelPos, labelString); } else if(n % 5 == 0) painter.drawLine(tickPos, mediumTickTop, tickPos, tickBottom); else painter.drawLine(tickPos, shortTickTop, tickPos, tickBottom); n++; units += unitsPerTick; } } a_pEvent->accept(); } // END OF void TimeLineSlider::paintEvent(QPaintEvent * a_pEvent) //============================================================================== void TimeLineSlider::wheelEvent(QWheelEvent * a_pEvent) { if(a_pEvent->modifiers() != Qt::NoModifier) { QWidget::wheelEvent(a_pEvent); return; } QPoint delta = a_pEvent->angleDelta(); if(delta.x() == 0) { if(delta.y() < 0) slotStepDown(); else slotStepUp(); } else { if(delta.x() < 0) slotStepDown(); else slotStepUp(); } a_pEvent->accept(); } // END OF void TimeLineSlider::wheelEvent(QWheelEvent * a_pEvent) //============================================================================== int TimeLineSlider::slideLineInnerWidth() const { int l_slideLineInnerWidth = width() - m_sideMargin * 2 - m_slideLineFrameWidth * 2; return l_slideLineInnerWidth; } // END OF int TimeLineSlider::slideLineInnerWidth() const //============================================================================== int TimeLineSlider::frameToPos(int a_frame) const { if(a_frame > m_maxFrame) a_frame = m_maxFrame; int framePos = (int)((double)(slideLineInnerWidth() - 1) / (double)m_maxFrame * (double)a_frame); framePos += m_sideMargin + m_slideLineFrameWidth; return framePos; } // END OF int TimeLineSlider::frameToPos(int a_frame) const //============================================================================== int TimeLineSlider::posToFrame(int a_pos) const { int start = m_sideMargin + m_slideLineFrameWidth; int last = width() - m_sideMargin - m_slideLineFrameWidth - 1; if(a_pos < start) return 0; else if(a_pos > last) return m_maxFrame; int l_frame = (int)std::round((double)m_maxFrame / (double)(slideLineInnerWidth() - 1) * (double)(a_pos - start)); return l_frame; } // END OF int TimeLineSlider::posToFrame(int a_pos) const //============================================================================== QRect TimeLineSlider::slideLineRect() const { QRect l_slideLineRect; l_slideLineRect.setLeft(m_sideMargin); int slideLineWidth = width() - m_sideMargin * 2; l_slideLineRect.setWidth(slideLineWidth); l_slideLineRect.setTop(height() - m_bottomMargin - m_slideLineHeight); l_slideLineRect.setHeight(m_slideLineHeight); return l_slideLineRect; } // END OF QRect TimeLineSlider::slideLineRect() const //============================================================================== QRect TimeLineSlider::slideLineActiveRect() const { QMargins margins(m_slideLineFrameWidth, m_slideLineFrameWidth, m_slideLineFrameWidth, m_slideLineFrameWidth); return slideLineRect().marginsRemoved(margins); } // END OF QRect TimeLineSlider::slideLineActiveRect() const //============================================================================== void TimeLineSlider::recalculateMinimumSize() { int widgetHeight = m_bottomMargin + m_slideLineHeight + m_slideLineTicksSpacing + m_longTickHeight + m_tickTextSpacing + m_textHeight + m_topMargin; setMinimumSize(2 * m_sideMargin + 2 * m_slideLineFrameWidth + 2, widgetHeight); } // END OF void TimeLineSlider::recalculateMinimumSize() //=================================================================================== void TimeLineSlider::setPointerAtFrame(const QMouseEvent * a_pEvent) { int frame = posToFrame(a_pEvent->pos().x()); if(a_pEvent->modifiers() == Qt::ControlModifier) m_pointerAtFrame = getClosestBookmark(frame); else m_pointerAtFrame = frame; } // END OF void TimeLineSlider::setPointerAtFrame(const QMouseEvent * a_pEvent) //============================================================================== ================================================ FILE: common-src/timeline_slider/timeline_slider.h ================================================ #ifndef TIMELINESLIDER_H #define TIMELINESLIDER_H #include #include class QKeyEvent; class QMouseEvent; class QPaintEvent; class QWheelEvent; class TimeLineSlider : public QWidget { Q_OBJECT public: TimeLineSlider(QWidget * a_pParent = nullptr); virtual ~TimeLineSlider(); enum DisplayMode { Time, Frames, }; enum ColorRole { SlideLine, ActiveFrame, InactiveFrame, CurrentFramePointer, SlidingPointer, Bookmark, }; int frame() const; void setFrame(int a_frame, bool a_refreshCache); void setFramesNumber(int a_framesNumber, bool a_refreshCache); void setFPS(double a_fps); DisplayMode displayMode() const; void setDisplayMode(DisplayMode a_displayMode); void setBigStep(int a_bigStep); void setLabelsFont(const QFont & a_font); void setColor(ColorRole a_role, const QColor & a_color); void addBookmark(int a_bookmark); void removeBookmark(int a_bookmark); std::set bookmarks() const; void setBookmarks(const std::set & a_bookmarks); void clearBookmarks(); int getClosestBookmark(int a_frame) const; public slots: void slotStepUp(); void slotStepDown(); void slotBigStepUp(); void slotBigStepDown(); void slotStepBy(int a_step); void slotStepBySeconds(double a_seconds); void slotBookmarkCurrentFrame(); void slotUnbookmarkCurrentFrame(); void slotGoToPreviousBookmark(); void slotGoToNextBookmark(); signals: void signalSliderMoved(int a_frame); void signalFrameChanged(int a_frame, bool a_refreshCache); void signalSliderPressed(); void signalSliderReleased(); protected: void keyPressEvent(QKeyEvent * a_pEvent); void mouseMoveEvent(QMouseEvent * a_pEvent); void mousePressEvent(QMouseEvent * a_pEvent); void mouseReleaseEvent(QMouseEvent * a_pEvent); void paintEvent(QPaintEvent * a_pEvent); void wheelEvent(QWheelEvent * a_pEvent); private: int slideLineInnerWidth() const; int frameToPos(int a_frame) const; int posToFrame(int a_pos) const; QRect slideLineRect() const; QRect slideLineActiveRect() const; void recalculateMinimumSize(); void setPointerAtFrame(const QMouseEvent * a_pEvent); int m_maxFrame; double m_fps; int m_currentFrame; int m_pointerAtFrame; DisplayMode m_displayMode; int m_bigStep; int m_sideMargin; int m_bottomMargin; int m_slideLineHeight; int m_slideLineFrameWidth; int m_slideLineTicksSpacing; int m_shortTickHeight; int m_mediumTickHeight; int m_longTickHeight; int m_tickTextSpacing; int m_textHeight; int m_topMargin; int m_minimumTicksSpacing; bool m_sliderPressed; QFont m_labelsFont; QColor m_slideLineColor; QColor m_activeFrameColor; QColor m_inactiveFrameColor; QColor m_currentFramePointerColor; QColor m_slidingPointerColor; QColor m_bookmarkColor; std::map m_colorRoleMap; std::set m_bookmarks; }; #endif // TIMELINESLIDER_H ================================================ FILE: common-src/vapoursynth/vapoursynth_script_processor.cpp ================================================ #include "vapoursynth_script_processor.h" #include "../helpers.h" #include "vs_script_library.h" #include "vs_pack_rgb.h" #include "vs_set_matrix.h" #include #include #include #include #include #include #include #include #include //============================================================================== void VS_CC frameReady(void * a_pUserData, const VSFrame * a_cpFrame, int a_frameNumber, VSNode * a_pNode, const char * a_errorMessage) { VapourSynthScriptProcessor * pScriptProcessor = static_cast(a_pUserData); Q_ASSERT(pScriptProcessor); QString errorMessage(a_errorMessage); QMetaObject::invokeMethod(pScriptProcessor, "slotReceiveFrameAndProcessQueue", Qt::QueuedConnection, Q_ARG(const VSFrame *, a_cpFrame), Q_ARG(int, a_frameNumber), Q_ARG(VSNode *, a_pNode), Q_ARG(QString, errorMessage)); } // END OF void VS_CC frameReady(void * a_pUserData, // const VSFrame * a_cpFrame, int a_frameNumber, // VSNode * a_pNode, const char * a_errorMessage) //============================================================================== VapourSynthScriptProcessor::VapourSynthScriptProcessor( SettingsManagerCore * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QObject * a_pParent): QObject(a_pParent) , m_pSettingsManager(a_pSettingsManager) , m_pVSScriptLibrary(a_pVSScriptLibrary) , m_script() , m_scriptName() , m_error() , m_initialized(false) , m_cpVSAPI(nullptr) , m_pVSScript(nullptr) , m_pCore(nullptr) , m_nodeInfo() , m_finalizing(false) { Q_ASSERT(m_pSettingsManager); Q_ASSERT(m_pVSScriptLibrary); slotResetSettings(); } // END OF VapourSynthScriptProcessor::VapourSynthScriptProcessor( // SettingsManagerCore * a_pSettingsManager, // VSScriptLibrary * a_pVSScriptLibrary, QObject * a_pParent) //============================================================================== VapourSynthScriptProcessor::~VapourSynthScriptProcessor() { finalize(); } // END OF VapourSynthScriptProcessor::~VapourSynthScriptProcessor() //============================================================================== bool VapourSynthScriptProcessor::initialize(const QString& a_script, const QString& a_scriptName, int a_outputIndex, ProcessReason a_reason) { if(m_initialized || m_finalizing) { m_error = tr("Script processor is already in use."); emit signalWriteLogMessage(mtCritical, m_error); return false; } if(!m_pVSScript || !m_pCore) { m_pCore = m_pVSScriptLibrary->createCore(); m_pVSScript = m_pVSScriptLibrary->createScript(m_pCore); } if(!m_pVSScript) { m_error = tr("Failed to create VSScript handle!"); emit signalWriteLogMessage(mtCritical, m_error); finalize(); return false; } m_cpVSAPI = m_pVSScriptLibrary->getVSAPI(); if(!m_cpVSAPI) { finalize(); return false; } m_cpVSAPI->getCoreInfo(m_pCore, &m_cpCoreInfo); int opresult = m_pVSScriptLibrary->evaluateScript(m_pVSScript, a_script.toUtf8().constData(), a_scriptName.toUtf8().constData()); if(opresult) { m_error = tr("Failed to evaluate the script"); const char * vsError = m_pVSScriptLibrary->getError(m_pVSScript); if(vsError) m_error += QString(":\n") + vsError; else m_error += '.'; emit signalWriteLogMessage(mtCritical, m_error); finalize(); return false; } VSNode * pOutputNode = m_pVSScriptLibrary->getOutput( m_pVSScript, a_outputIndex); if(!pOutputNode) { m_error = tr("Failed to get output node #%1.") .arg(a_outputIndex); emit signalWriteLogMessage(mtCritical, m_error); finalize(); return false; } m_nodeInfo = VSNodeInfo(pOutputNode, m_cpVSAPI); #ifdef Q_OS_WIN // AUDIO if(a_reason == ProcessReason::Encode && m_nodeInfo.isAudio()) #else if((a_reason == ProcessReason::Preview || a_reason == ProcessReason::Encode) && m_nodeInfo.isAudio()) #endif { m_cpVSAPI->freeNode(pOutputNode); m_error = tr("Output node #%1 is audio. " #ifdef Q_OS_WIN // AUDIO "Encoding audio are not supported.") #else "Previewing and encoding audio are not supported.") #endif .arg(a_outputIndex); emit signalWriteLogMessage( a_outputIndex == 0 ? mtCritical : mtWarning, m_error); finalize(); return false; } if(m_nodeInfo.isVideo() && m_nodeInfo.getAsVideo()->format.colorFamily == cfUndefined) { emit signalWriteLogMessage(mtWarning, tr("Video output node #%1 has Undefined format.") .arg(a_outputIndex)); } m_cpVSAPI->freeNode(pOutputNode); m_script = a_script; m_scriptName = a_scriptName; m_error.clear(); m_initialized = true; sendFrameQueueChangeSignal(); return true; } // END OF bool VapourSynthScriptProcessor::initialize(const QString& a_script, // const QString& a_scriptName, int a_outputIndex, ProcessReason a_reason) //============================================================================== bool VapourSynthScriptProcessor::finalize() { m_finalizing = true; bool noFrameTicketsInProcess = flushFrameTicketsQueue(); if(!noFrameTicketsInProcess) return false; for(std::pair & mapItem : m_nodePairForOutputIndex) { NodePair & nodePair = mapItem.second; if(nodePair.pOutputNode) m_cpVSAPI->freeNode(nodePair.pOutputNode); if(nodePair.pPreviewNode) m_cpVSAPI->freeNode(nodePair.pPreviewNode); } m_nodePairForOutputIndex.clear(); if(m_pVSScript) { m_pVSScriptLibrary->freeScript(m_pVSScript); m_pVSScript = nullptr; } m_cpVSAPI = nullptr; m_script.clear(); m_scriptName.clear(); m_initialized = false; m_finalizing = false; emit signalFinalized(); return true; } // END OF bool VapourSynthScriptProcessor::finalize() //============================================================================== bool VapourSynthScriptProcessor::isInitialized() const { return m_initialized; } // END OF bool VapourSynthScriptProcessor::isInitialized() const //============================================================================== QString VapourSynthScriptProcessor::error() const { return m_error; } // END OF QString VapourSynthScriptProcessor::error() const //============================================================================== std::vector VapourSynthScriptProcessor::getOutputIndices() const { if(!isInitialized() || !m_pVSScript) return std::vector(); return m_pVSScriptLibrary->getOutputIndices(m_pVSScript); } VSNodeInfo VapourSynthScriptProcessor::nodeInfo(int a_outputIndex) { if(!m_initialized) return VSNodeInfo(); Q_ASSERT(m_cpVSAPI); Q_ASSERT(m_pVSScript); VSNode * pNode = m_pVSScriptLibrary->getOutput(m_pVSScript, a_outputIndex); if(!pNode) { m_error = tr("Couldn't resolve output node #%1.") .arg(a_outputIndex); emit signalWriteLogMessage( a_outputIndex == 0 ? mtCritical : mtWarning, m_error); return VSNodeInfo(); } VSNodeInfo nodeInfo(pNode, m_cpVSAPI); m_cpVSAPI->freeNode(pNode); return nodeInfo; } // END OF VSNodeInfo VapourSynthScriptProcessor::nodeInfo(int a_outputIndex) //============================================================================== bool VapourSynthScriptProcessor::requestFrameAsync(int a_frameNumber, int a_outputIndex, bool a_needPreview) { if(!m_initialized) return false; if(a_frameNumber < 0) { m_error = tr("Requested frame number %1 is negative.") .arg(a_frameNumber); emit signalWriteLogMessage(mtCritical, m_error); return false; } Q_ASSERT(m_cpVSAPI); NodePair & nodePair = getNodePair(a_outputIndex, a_needPreview); if(!nodePair.pOutputNode) return false; int numFrames; int mediaType = m_cpVSAPI->getNodeType(nodePair.pOutputNode); if(mediaType == mtAudio) { auto *info = m_cpVSAPI->getAudioInfo(nodePair.pOutputNode); Q_ASSERT(info); numFrames = info->numFrames; } else { auto *info = m_cpVSAPI->getVideoInfo(nodePair.pOutputNode); Q_ASSERT(info); numFrames = info->numFrames; } if(a_frameNumber >= numFrames) { m_error = tr("Requested frame number %1 is outside the frame " "range.").arg(a_outputIndex); emit signalWriteLogMessage(mtCritical, m_error); return false; } if(a_needPreview && (!nodePair.pPreviewNode)) return false; FrameTicket newFrameTicket(a_frameNumber, a_outputIndex, nodePair.pOutputNode, a_needPreview, nodePair.pPreviewNode); m_frameTicketsQueue.push_back(newFrameTicket); sendFrameQueueChangeSignal(); processFrameTicketsQueue(); return true; } // END OF void VapourSynthScriptProcessor::requestFrameAsync(int a_frameNumber, // int a_outputIndex, bool a_needPreview) //============================================================================== bool VapourSynthScriptProcessor::flushFrameTicketsQueue() { // Check the processing queue. for(FrameTicket & ticket : m_frameTicketsInProcess) ticket.discard = true; size_t queueSize = m_frameTicketsQueue.size(); m_frameTicketsQueue.clear(); if(queueSize) sendFrameQueueChangeSignal(); return m_frameTicketsInProcess.empty(); } // END OF bool VapourSynthScriptProcessor::flushFrameTicketsQueue() //============================================================================== const QString & VapourSynthScriptProcessor::script() const { return m_script; } // END OF const QString & VapourSynthScriptProcessor::script() const //============================================================================== const QString & VapourSynthScriptProcessor::scriptName() const { return m_scriptName; } // END OF const QString & VapourSynthScriptProcessor::scriptName() const //============================================================================== void VapourSynthScriptProcessor::setScriptName(const QString & a_scriptName) { m_scriptName = a_scriptName; } // END OF void VapourSynthScriptProcessor::setScriptName( // const QString & a_scriptName) //============================================================================== void VapourSynthScriptProcessor::slotReceiveFrameAndProcessQueue( const VSFrame * a_cpFrame, int a_frameNumber, VSNode * a_pNode, QString a_errorMessage) { receiveFrame(a_cpFrame, a_frameNumber, a_pNode, a_errorMessage); processFrameTicketsQueue(); } // END OF void VapourSynthScriptProcessor::slotReceiveFrameAndProcessQueue( // const VSFrame * a_cpFrame, int a_frameNumber, // VSNode * a_pNode, QString a_errorMessage) //============================================================================== void VapourSynthScriptProcessor::slotResetSettings() { m_yuvMatrix = m_pSettingsManager->getYuvMatrixCoefficients(); m_chromaResamplingFilter = m_pSettingsManager->getChromaResamplingFilter(); m_resamplingFilterParameterA = NAN; m_resamplingFilterParameterB = NAN; if(m_chromaResamplingFilter == ResamplingFilter::Bicubic) { m_resamplingFilterParameterA = m_pSettingsManager->getBicubicFilterParameterB(); m_resamplingFilterParameterB = m_pSettingsManager->getBicubicFilterParameterC(); } else if(m_chromaResamplingFilter == ResamplingFilter::Lanczos) { m_resamplingFilterParameterA = (double)m_pSettingsManager->getLanczosFilterTaps(); } m_chromaPlacement = m_pSettingsManager->getChromaPlacement(); m_ditherType = m_pSettingsManager->getDitherType(); for(std::pair & mapItem : m_nodePairForOutputIndex) { NodePair & nodePair = mapItem.second; if(nodePair.pPreviewNode) recreatePreviewNode(nodePair); } } // END OF void VapourSynthScriptProcessor::slotResetSettings() //============================================================================== void VapourSynthScriptProcessor::receiveFrame( const VSFrame * a_cpFrame, int a_frameNumber, VSNode * a_pNode, const QString & a_errorMessage) { Q_ASSERT(m_cpVSAPI); if(!a_errorMessage.isEmpty()) { m_error = tr("Error on frame %1 request:\n%2") .arg(a_frameNumber).arg(a_errorMessage); emit signalWriteLogMessage(mtCritical, m_error); } FrameTicket ticket(a_frameNumber, -1, nullptr); std::vector::iterator it = std::find_if( m_frameTicketsInProcess.begin(), m_frameTicketsInProcess.end(), [&](const FrameTicket & a_ticket) { return ((a_ticket.frameNumber == a_frameNumber) && ((a_ticket.pOutputNode == a_pNode) || (a_ticket.pPreviewNode == a_pNode))); }); if(it != m_frameTicketsInProcess.end()) { // Save frame references and free node references in ticket at once. if(it->pOutputNode == a_pNode) { it->cpOutputFrame = a_cpFrame; m_cpVSAPI->freeNode(it->pOutputNode); it->pOutputNode = nullptr; if(it->needPreview) { Q_ASSERT(it->pPreviewNode); if(a_cpFrame) { m_cpVSAPI->getFrameAsync(it->frameNumber, it->pPreviewNode, frameReady, this); } else { m_cpVSAPI->freeNode(it->pPreviewNode); it->pPreviewNode = nullptr; } } } else if(it->pPreviewNode == a_pNode) { it->cpPreviewFrame = a_cpFrame; m_cpVSAPI->freeNode(it->pPreviewNode); it->pPreviewNode = nullptr; } // Since we nullify the nodes in ticket - we can check if it can be // removed from the queue by checking the nodes. if((it->pOutputNode != nullptr) || (it->needPreview && (it->pPreviewNode != nullptr))) return; ticket = *it; m_frameTicketsInProcess.erase(it); sendFrameQueueChangeSignal(); } else { QString warning = tr("Warning: received frame not registered in " "processing. Frame number: %1; Node: %2\n") .arg(a_frameNumber).arg((intptr_t)a_pNode); emit signalWriteLogMessage(mtCritical, warning); m_cpVSAPI->freeFrame(a_cpFrame); } if(!ticket.discard) { if(ticket.isComplete()) { emit signalDistributeFrame(ticket.frameNumber, ticket.outputIndex, ticket.cpOutputFrame, ticket.cpPreviewFrame); } else { emit signalFrameRequestDiscarded(ticket.frameNumber, ticket.outputIndex, QString()); } } freeFrameTicket(ticket); } // END OF void VapourSynthScriptProcessor::receiveFrame( // const VSFrame * a_cpFrame, int a_frameNumber, // VSNode * a_pNode, const QString & a_errorMessage) //============================================================================== void VapourSynthScriptProcessor::processFrameTicketsQueue() { Q_ASSERT(m_cpVSAPI); size_t oldInQueue = m_frameTicketsQueue.size(); size_t oldInProcess = m_frameTicketsInProcess.size(); while(((int)m_frameTicketsInProcess.size() < m_cpCoreInfo.numThreads) && (!m_frameTicketsQueue.empty())) { FrameTicket ticket = std::move(m_frameTicketsQueue.front()); m_frameTicketsQueue.pop_front(); // In case preview node was hot-swapped. NodePair & nodePair = getNodePair(ticket.outputIndex, ticket.needPreview); bool validPair = (nodePair.pOutputNode != nullptr); if(ticket.needPreview) validPair = validPair && (nodePair.pPreviewNode != nullptr); if(!validPair) { QString reason = tr("No nodes to produce the frame " "%1 at output #%2.").arg(ticket.frameNumber) .arg(ticket.outputIndex); emit signalFrameRequestDiscarded(ticket.frameNumber, ticket.outputIndex, reason); continue; } ticket.pOutputNode = m_cpVSAPI->addNodeRef(nodePair.pOutputNode); if(ticket.needPreview) ticket.pPreviewNode = m_cpVSAPI->addNodeRef(nodePair.pPreviewNode); m_cpVSAPI->getFrameAsync(ticket.frameNumber, ticket.pOutputNode, frameReady, this); m_frameTicketsInProcess.push_back(ticket); } size_t inQueue = m_frameTicketsQueue.size(); size_t inProcess = m_frameTicketsInProcess.size(); if((inQueue != oldInQueue) || (oldInProcess != inProcess)) sendFrameQueueChangeSignal(); if(m_finalizing) finalize(); } // END OF void VapourSynthScriptProcessor::processFrameTicketsQueue() //============================================================================== void VapourSynthScriptProcessor::sendFrameQueueChangeSignal() { size_t inQueue = m_frameTicketsQueue.size(); size_t inProcess = m_frameTicketsInProcess.size(); m_cpVSAPI->getCoreInfo(m_pCore, &m_cpCoreInfo); size_t maxThreads = m_cpCoreInfo.numThreads; double usedCacheRatio = (double)m_cpCoreInfo.usedFramebufferSize / (double)m_cpCoreInfo.maxFramebufferSize; emit signalFrameQueueStateChanged(inQueue, inProcess, maxThreads, usedCacheRatio); } // END OF void VapourSynthScriptProcessor::sendFrameQueueChangeSignal() //============================================================================== bool VapourSynthScriptProcessor::recreatePreviewNode(NodePair & a_nodePair) { if(!a_nodePair.pOutputNode) return false; if(!m_cpVSAPI) return false; if(a_nodePair.pPreviewNode) { m_cpVSAPI->freeNode(a_nodePair.pPreviewNode); a_nodePair.pPreviewNode = nullptr; } int outputMediaType = m_cpVSAPI->getNodeType(a_nodePair.pOutputNode); if(outputMediaType == mtAudio) { #ifdef Q_OS_WIN // AUDIO return recreateAudioPreviewNode(a_nodePair); #else m_error = tr("Audio playback is supported on Windows only."); emit signalWriteLogMessage(mtCritical, m_error); return false; #endif } const VSVideoInfo * cpVideoInfo = m_cpVSAPI->getVideoInfo(a_nodePair.pOutputNode); if(!cpVideoInfo) return false; const VSVideoFormat * cpFormat = &cpVideoInfo->format; bool to_10_bit = (QColormap::instance().depth() == 30); VSMap * pResultMap = nullptr; if(vsh::isSameVideoPresetFormat(pfRGB24, cpFormat, m_pCore, m_cpVSAPI)) { to_10_bit = false; pResultMap = m_cpVSAPI->createMap(); m_cpVSAPI->mapSetNode(pResultMap, "clip", a_nodePair.pOutputNode, maReplace); } else if(to_10_bit && vsh::isSameVideoPresetFormat(pfRGB30, cpFormat, m_pCore, m_cpVSAPI)) { pResultMap = m_cpVSAPI->createMap(); m_cpVSAPI->mapSetNode(pResultMap, "clip", a_nodePair.pOutputNode, maReplace); } else { bool isVF = isVariableFormat(cpVideoInfo); bool isYUV = cpFormat->colorFamily == cfYUV; VSPlugin * pResizePlugin = m_cpVSAPI->getPluginByID( "com.vapoursynth.resize", m_pCore); const char * resizeName = "Point"; VSMap * pArgumentMap = m_cpVSAPI->createMap(); VSCoreInfo coreInfo; m_cpVSAPI->getCoreInfo(m_pCore, &coreInfo); if(coreInfo.core < 58) m_cpVSAPI->mapSetInt(pArgumentMap, "prefer_props", 1, maReplace); // Set matrix and chromaloc int64_t matrixIn; switch(m_yuvMatrix) { case YuvMatrixCoefficients::m709: matrixIn = VSC_MATRIX_BT709; break; case YuvMatrixCoefficients::m470BG: matrixIn = VSC_MATRIX_BT470_BG; break; case YuvMatrixCoefficients::m170M: matrixIn = VSC_MATRIX_ST170_M; break; case YuvMatrixCoefficients::m2020_NCL: matrixIn = VSC_MATRIX_BT2020_NCL; break; default: Q_ASSERT(false); } m_cpVSAPI->mapSetInt(pArgumentMap, "matrix_in", matrixIn, maReplace); int64_t chromaLoc; switch(m_chromaPlacement) { case ChromaPlacement::LEFT: chromaLoc = VSC_CHROMA_LEFT; break; case ChromaPlacement::CENTER: chromaLoc = VSC_CHROMA_CENTER; break; case ChromaPlacement::TOP_LEFT: chromaLoc = VSC_CHROMA_TOP_LEFT; break; default: Q_ASSERT(false); } m_cpVSAPI->mapSetInt(pArgumentMap, "chromaloc_in", chromaLoc, maReplace); QString ditherType; switch (m_ditherType) { case DitherType::NONE: ditherType = "none"; break; case DitherType::ORDERED: ditherType = "ordered"; break; case DitherType::RANDOM: ditherType = "random"; break; case DitherType::ERROR_DIFFUSION: ditherType = "error_diffusion"; break; default: Q_ASSERT(false); } m_cpVSAPI->mapSetData(pArgumentMap, "dither_type", ditherType.toStdString().c_str(), ditherType.size(), dtUtf8, maReplace); VSMap *pTempMap = m_cpVSAPI->createMap(); m_cpVSAPI->copyMap(pArgumentMap, pTempMap); m_cpVSAPI->mapSetNode(pTempMap, "clip", a_nodePair.pOutputNode, maReplace); setMatrixFilter(pTempMap, pArgumentMap, m_pCore, m_cpVSAPI); m_cpVSAPI->clearMap(pTempMap); m_cpVSAPI->mapSetInt(pArgumentMap, "format", (to_10_bit ? pfRGB30 : pfRGB24), maReplace); if(isYUV || isVF) { switch(m_chromaResamplingFilter) { case ResamplingFilter::Point: resizeName = "Point"; break; case ResamplingFilter::Bilinear: resizeName = "Bilinear"; break; case ResamplingFilter::Bicubic: resizeName = "Bicubic"; m_cpVSAPI->mapSetFloat(pArgumentMap, "filter_param_a_uv", m_resamplingFilterParameterA, maReplace); m_cpVSAPI->mapSetFloat(pArgumentMap, "filter_param_b_uv", m_resamplingFilterParameterB, maReplace); break; case ResamplingFilter::Lanczos: resizeName = "Lanczos"; m_cpVSAPI->mapSetFloat(pArgumentMap, "filter_param_a_uv", m_resamplingFilterParameterA, maReplace); break; case ResamplingFilter::Spline16: resizeName = "Spline16"; break; case ResamplingFilter::Spline36: resizeName = "Spline36"; break; case ResamplingFilter::Spline64: resizeName = "Spline64"; default: Q_ASSERT(false); } } pResultMap = m_cpVSAPI->invoke(pResizePlugin, resizeName, pArgumentMap); m_cpVSAPI->freeMap(pArgumentMap); } const char * cpResultError = m_cpVSAPI->mapGetError(pResultMap); if(cpResultError) { m_error = tr("Failed to convert to RGB:\n"); m_error += cpResultError; emit signalWriteLogMessage(mtCritical, m_error); m_cpVSAPI->freeMap(pResultMap); return false; } VSNode * pRGBNode = m_cpVSAPI->mapGetNode(pResultMap, "clip", 0, nullptr); m_cpVSAPI->freeMap(pResultMap); VSNode * pPreviewNode = packRGBFilter(pRGBNode, a_nodePair.pOutputNode, to_10_bit, m_pCore, m_cpVSAPI); Q_ASSERT(pPreviewNode); a_nodePair.pPreviewNode = pPreviewNode; return true; } // END OF bool VapourSynthScriptProcessor::recreatePreviewNode( // NodePair & a_nodePair) //============================================================================== bool VapourSynthScriptProcessor::recreateAudioPreviewNode(NodePair &a_nodePair) { Q_ASSERT(m_pCore); const VSAudioInfo * cpAudioInfo = m_cpVSAPI->getAudioInfo(a_nodePair.pOutputNode); VSMap * map = m_cpVSAPI->createMap(); VSPlugin *stdPlugin = m_cpVSAPI->getPluginByID(VSH_STD_PLUGIN_ID, m_pCore); m_cpVSAPI->mapSetInt(map, "length", cpAudioInfo->numFrames, maReplace); VSMap * blankMap = m_cpVSAPI->invoke(stdPlugin, "BlankClip", map); m_cpVSAPI->clearMap(map); QString props = vsedit::nodeInfoString( nodeInfo(a_nodePair.outputIndex), m_cpVSAPI); QString text = QString("Audio node #%1\n\n").arg(a_nodePair.outputIndex) + props.replace("| ", "\n"); m_cpVSAPI->mapSetData(blankMap, "text", text.toStdString().c_str(), text.size(), dtUtf8, maReplace); VSPlugin * textPlugin = m_cpVSAPI->getPluginByID(VSH_TEXT_PLUGIN_ID, m_pCore); VSMap * textMap = m_cpVSAPI->invoke(textPlugin, "Text", blankMap); m_cpVSAPI->clearMap(blankMap); VSNode * textClip = m_cpVSAPI->mapGetNode(textMap, "clip", 0, nullptr); m_cpVSAPI->clearMap(textMap); a_nodePair.pPreviewNode = packRGBFilter(textClip, a_nodePair.pOutputNode, false, m_pCore, m_cpVSAPI); return true; } void VapourSynthScriptProcessor::freeFrameTicket(FrameTicket & a_ticket) { Q_ASSERT(m_cpVSAPI); a_ticket.discard = true; if(a_ticket.cpOutputFrame) { m_cpVSAPI->freeFrame(a_ticket.cpOutputFrame); a_ticket.cpOutputFrame = nullptr; } if(a_ticket.cpPreviewFrame) { m_cpVSAPI->freeFrame(a_ticket.cpPreviewFrame); a_ticket.cpPreviewFrame = nullptr; } if(a_ticket.pOutputNode) { m_cpVSAPI->freeNode(a_ticket.pOutputNode); a_ticket.pOutputNode = nullptr; } if(a_ticket.pPreviewNode) { m_cpVSAPI->freeNode(a_ticket.pPreviewNode); a_ticket.pPreviewNode = nullptr; } } // END OF void VapourSynthScriptProcessor::freeFrameTicket( // FrameTicket & a_ticket) //============================================================================== NodePair & VapourSynthScriptProcessor::getNodePair(int a_outputIndex, bool a_needPreview) { NodePair & nodePair = m_nodePairForOutputIndex[a_outputIndex]; nodePair.outputIndex = a_outputIndex; if(!nodePair.pOutputNode) { Q_ASSERT(!nodePair.pPreviewNode); nodePair.pOutputNode = m_pVSScriptLibrary->getOutput(m_pVSScript, a_outputIndex); if(!nodePair.pOutputNode) { m_error = tr("Couldn't resolve output node #%1.") .arg(a_outputIndex); emit signalWriteLogMessage( a_outputIndex == 0 ? mtCritical : mtWarning, m_error); return nodePair; } } if(a_needPreview && (!nodePair.pPreviewNode)) { bool previewNodeCreated = recreatePreviewNode(nodePair); if(!previewNodeCreated) { m_error = tr("Couldn't create preview node for output " "#%1.").arg(a_outputIndex); emit signalWriteLogMessage(mtCritical, m_error); return nodePair; } } return nodePair; } // END OF NodePair VapourSynthScriptProcessor::getNodePair(int a_outputIndex, // bool a_needPreview) //============================================================================== QString VapourSynthScriptProcessor::framePropsString( const VSFrame * a_cpFrame) const { if(!a_cpFrame) return tr("Null frame."); Q_ASSERT(m_cpVSAPI); QString propsString; QStringList propsStringList; static std::map propTypeToString = { {ptUnset, ""}, {ptInt, "int"}, {ptFloat, "float"}, {ptData, "data"}, {ptFunction, "function"}, {ptVideoNode, "vnode"}, {ptVideoFrame, "vframe"}, {ptAudioNode, "anode"}, {ptAudioFrame, "aframe"} }; static std::map _ChromaLocationToString = { {VSC_CHROMA_LEFT, "left"}, {VSC_CHROMA_CENTER, "center"}, {VSC_CHROMA_TOP_LEFT, "top left"}, {VSC_CHROMA_BOTTOM_LEFT, "bottom left"}, {VSC_CHROMA_BOTTOM, "bottom"}, }; static std::map _ColorRangeToString = { {VSC_RANGE_FULL, "full"}, {VSC_RANGE_LIMITED, "limited"}, }; static std::map _PrimariesToString = { {VSC_PRIMARIES_BT709, "709"}, {VSC_PRIMARIES_UNSPECIFIED, "unspec"}, {VSC_PRIMARIES_BT470_M, "470m"}, {VSC_PRIMARIES_BT470_BG, "470bg"}, {VSC_PRIMARIES_ST170_M, "170m"}, {VSC_PRIMARIES_ST240_M, "240m"}, {VSC_PRIMARIES_FILM, "film"}, {VSC_PRIMARIES_BT2020, "2020"}, {VSC_PRIMARIES_ST428, "st428"}, {VSC_PRIMARIES_ST431_2, "st431-2"}, {VSC_PRIMARIES_ST432_1, "st432-1"}, {VSC_PRIMARIES_EBU3213_E, "jedec-p22"}, }; static std::map _MatrixToString = { {VSC_MATRIX_RGB, "rgb"}, {VSC_MATRIX_BT709, "709"}, {VSC_MATRIX_UNSPECIFIED, "unspec"}, {VSC_MATRIX_FCC, "fcc"}, {VSC_MATRIX_BT470_BG, "470bg"}, {VSC_MATRIX_ST170_M, "170m"}, {VSC_MATRIX_ST240_M, "240m"}, {VSC_MATRIX_YCGCO, "ycgco"}, {VSC_MATRIX_BT2020_NCL, "2020ncl"}, {VSC_MATRIX_BT2020_CL, "2020cl"}, {VSC_MATRIX_CHROMATICITY_DERIVED_NCL, "chromancl"}, {VSC_MATRIX_CHROMATICITY_DERIVED_CL, "chromacl"}, {VSC_MATRIX_ICTCP, "ictcp"}, }; static std::map _TransferToString = { {VSC_TRANSFER_BT709, "709"}, {VSC_TRANSFER_UNSPECIFIED, "unspec"}, {VSC_TRANSFER_BT470_M, "470m"}, {VSC_TRANSFER_BT470_BG, "470bg"}, {VSC_TRANSFER_BT601, "601"}, {VSC_TRANSFER_ST240_M, "240m"}, {VSC_TRANSFER_LINEAR, "linear"}, {VSC_TRANSFER_LOG_100, "log100"}, {VSC_TRANSFER_LOG_316, "log316"}, {VSC_TRANSFER_IEC_61966_2_4, "xvycc"}, {VSC_TRANSFER_IEC_61966_2_1, "srgb"}, {VSC_TRANSFER_BT2020_10, "2020_10"}, {VSC_TRANSFER_BT2020_12, "2020_12"}, {VSC_TRANSFER_ST2084, "st2084"}, //{VSC_TRANSFER_ST428, "st428"}, {17, "st428"}, {VSC_TRANSFER_ARIB_B67, "std-b67"}, }; static std::map _FieldBasedToString = { {VSC_FIELD_PROGRESSIVE, "progressive"}, {VSC_FIELD_BOTTOM, "bottom field first"}, {VSC_FIELD_TOP, "top field first"}, }; static std::map _FieldToString = { {0, "from bottom field"}, {1, "from top field"}, }; static std::map> reservedPropToMap = { {"_ChromaLocation", _ChromaLocationToString}, {"_Range", _ColorRangeToString}, {"_ColorRange", _ColorRangeToString}, {"_Primaries", _PrimariesToString}, {"_Matrix", _MatrixToString}, {"_Transfer", _TransferToString}, {"_FieldBased", _FieldBasedToString}, {"_Field", _FieldToString}, }; const VSMap * cpProps = m_cpVSAPI->getFramePropertiesRO(a_cpFrame); int propsNumber = m_cpVSAPI->mapNumKeys(cpProps); for(int i = 0; i < propsNumber; ++i) { const char * propKey = m_cpVSAPI->mapGetKey(cpProps, i); if(!propKey) continue; QString currentPropString = QString("%1 : ").arg(propKey); auto propType = (VSPropertyType)m_cpVSAPI->mapGetType(cpProps, propKey); currentPropString += propTypeToString[propType]; int elementsNumber = m_cpVSAPI->mapNumElements(cpProps, propKey); if(elementsNumber > 1) currentPropString += "[]"; switch(propType) { case ptVideoFrame: case ptVideoNode: case ptAudioFrame: case ptAudioNode: case ptFunction: break; case ptUnset: currentPropString += ": "; break; case ptInt: { currentPropString += " : "; QStringList elementStringList; for(int j = 0; j < elementsNumber; ++j) { QString elementString; int error; int64_t element = m_cpVSAPI->mapGetInt(cpProps, propKey, j, &error); if(error) elementString = ""; else { auto it = reservedPropToMap.find(QString(propKey)); if(it == reservedPropToMap.end()) elementString = QString::number(element); else { auto it2 = it->second.find(element); if(it2 == it->second.end()) elementString = QString::number(element); else elementString = QString("%1 (%2)") .arg(element).arg(it2->second); } } elementStringList += elementString; } currentPropString += elementStringList.join(", "); break; } case ptFloat: { currentPropString += " : "; QStringList elementStringList; for(int j = 0; j < elementsNumber; ++j) { QString elementString; int error; double element = m_cpVSAPI->mapGetFloat(cpProps, propKey, j, &error); if(error) elementString = ""; else elementString = QString::number(element); elementStringList += elementString; } currentPropString += elementStringList.join(", "); break; } case ptData: { currentPropString += " : "; QStringList elementStringList; for(int j = 0; j < elementsNumber; ++j) { QString elementString; int error; int hint = m_cpVSAPI->mapGetDataTypeHint(cpProps, propKey, j, &error); if(error) elementString = ""; else if(hint == dtUtf8) { const char * element = m_cpVSAPI->mapGetData(cpProps, propKey, j, &error); if(error) elementString = ""; else elementString = QString::fromUtf8(element); } else if(hint == dtBinary) { int len = m_cpVSAPI->mapGetDataSize(cpProps, propKey, j, &error); if(error) elementString = ""; else elementString = QString("") .arg(len); } else { const char * element = m_cpVSAPI->mapGetData(cpProps, propKey, j, &error); if(error) elementString = ""; else { elementString = QString::fromUtf8(element); QByteArray elementAsUtf8 = elementString.toUtf8(); int len = m_cpVSAPI->mapGetDataSize(cpProps, propKey, j, &error); if(elementAsUtf8.length() != len || elementAsUtf8.toStdString() != element) elementString = ""; } } elementStringList += elementString; } currentPropString += elementStringList.join(", "); break; } default: Q_ASSERT(false); } propsStringList += currentPropString; } propsString = propsStringList.join(" \n"); return propsString; } // END OF QString VapourSynthScriptProcessor::framePropsString( // const VSFrame * a_cpFrame) const //============================================================================== bool VapourSynthScriptProcessor::clearCoreCaches() { return m_pVSScriptLibrary->clearCoreCaches(m_pCore); } ================================================ FILE: common-src/vapoursynth/vapoursynth_script_processor.h ================================================ #ifndef VAPOURSYNTHSCRIPTPROCESSOR_H #define VAPOURSYNTHSCRIPTPROCESSOR_H #include "vs_script_processor_structures.h" #include "../settings/settings_manager_core.h" #include "../helpers_vs.h" #include #include #include #include #include class VSScriptLibrary; //============================================================================== class VapourSynthScriptProcessor : public QObject { Q_OBJECT public: VapourSynthScriptProcessor(SettingsManagerCore * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QObject * a_pParent = nullptr); virtual ~VapourSynthScriptProcessor(); bool initialize(const QString& a_script, const QString& a_scriptName, int a_outputIndex, ProcessReason a_reason); bool finalize(); bool isInitialized() const; QString error() const; std::vector getOutputIndices() const; VSNodeInfo nodeInfo(int a_outputIndex = 0); bool requestFrameAsync(int a_frameNumber, int a_outputIndex = 0, bool a_needPreview = false); bool flushFrameTicketsQueue(); const QString & script() const; const QString & scriptName() const; void setScriptName(const QString & a_scriptName); QString framePropsString(const VSFrame * a_cpFrame) const; bool clearCoreCaches(); public slots: void slotResetSettings(); signals: void signalWriteLogMessage(int a_messageType, const QString & a_message); void signalDistributeFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame); void signalFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason); void signalFrameQueueStateChanged(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio); void signalFinalized(); private slots: void slotReceiveFrameAndProcessQueue( const VSFrame * a_cpFrame, int a_frameNumber, VSNode * a_pNode, QString a_errorMessage); private: void receiveFrame(const VSFrame * a_cpFrame, int a_frameNumber, VSNode * a_pNode, const QString & a_errorMessage); void processFrameTicketsQueue(); void sendFrameQueueChangeSignal(); bool recreatePreviewNode(NodePair & a_nodePair); bool recreateAudioPreviewNode(NodePair & a_nodePair); void freeFrameTicket(FrameTicket & a_ticket); NodePair & getNodePair(int a_outputIndex, bool a_needPreview); SettingsManagerCore * m_pSettingsManager; VSScriptLibrary * m_pVSScriptLibrary; QString m_script; QString m_scriptName; QString m_error; bool m_initialized; const VSAPI * m_cpVSAPI; VSScript * m_pVSScript; VSCore * m_pCore; VSNodeInfo m_nodeInfo; VSCoreInfo m_cpCoreInfo; std::deque m_frameTicketsQueue; std::vector m_frameTicketsInProcess; std::map m_nodePairForOutputIndex; ResamplingFilter m_chromaResamplingFilter; ChromaPlacement m_chromaPlacement; double m_resamplingFilterParameterA; double m_resamplingFilterParameterB; YuvMatrixCoefficients m_yuvMatrix; DitherType m_ditherType; bool m_finalizing; }; //============================================================================== #endif // VAPOURSYNTHSCRIPTPROCESSOR_H ================================================ FILE: common-src/vapoursynth/vs_pack_rgb.cpp ================================================ #include "vs_pack_rgb.h" #include "../libp2p/p2p_api.h" #include #include struct PackData { VSNode *rgbNode; VSNode *outputNode; }; void packFree(void *instanceData, [[maybe_unused]] VSCore *core, const VSAPI *vsapi) { PackData *d = reinterpret_cast(instanceData); vsapi->freeNode(d->rgbNode); // The outputNode is freed somewhere else // vsapi->freeNode(d->outputNode); delete d; } template const VSFrame *packGetFrame(int n, int activationReason, void *instanceData, [[maybe_unused]] void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { PackData *d = reinterpret_cast(instanceData); if (activationReason == arInitial) { vsapi->requestFrameFilter(n, d->outputNode, frameCtx); vsapi->requestFrameFilter(n, d->rgbNode, frameCtx); } else if (activationReason == arAllFramesReady) { const VSFrame *srcFrame = vsapi->getFrameFilter(n, d->rgbNode, frameCtx); VSVideoFormat frameFormat; vsapi->getVideoFormatByID(&frameFormat, pfGray8, core); int width = vsapi->getFrameWidth(srcFrame, 0); int height = vsapi->getFrameHeight(srcFrame, 0); VSFrame *dstFrame = vsapi->newVideoFrame(&frameFormat, width * 4, height, nullptr, core); p2p_buffer_param p = {}; p.width = width; p.height = height; p.packing = packing_fmt; for (int plane = 0; plane < 3; ++plane) { p.src[plane] = vsapi->getReadPtr(srcFrame, plane); p.src_stride[plane] = vsapi->getStride(srcFrame, plane); } p.dst[0] = vsapi->getWritePtr(dstFrame, 0); p.dst_stride[0] = vsapi->getStride(dstFrame, 0); p2p_pack_frame(&p, P2P_ALPHA_SET_ONE); VSMap *props = vsapi->getFramePropertiesRW(dstFrame); vsapi->mapSetInt(props, "PackingFormat", static_cast(packing_fmt), maReplace); const VSFrame *outputFrame = vsapi->getFrameFilter(n, d->outputNode, frameCtx); vsapi->mapConsumeFrame(props, "OutputFrame", outputFrame, maReplace); vsapi->freeFrame(srcFrame); return dstFrame; } return nullptr; } VSNode *packRGBFilter(VSNode *rgbNode, VSNode *outputNode, bool use10bit, VSCore *core, const VSAPI *vsapi) { VSVideoInfo vi = *vsapi->getVideoInfo(rgbNode); vi.width *= 4; vsapi->getVideoFormatByID(&vi.format, pfGray8, core); PackData *d = new PackData{rgbNode, outputNode}; std::vector deps = { {rgbNode, rpStrictSpatial}, {outputNode, rpStrictSpatial}}; if (use10bit) return vsapi->createVideoFilter2("PackRGB30", &vi, packGetFrame, packFree, fmParallel, deps.data(), deps.size(), d, core); else return vsapi->createVideoFilter2("PackRGB24", &vi, packGetFrame, packFree, fmParallel, deps.data(), deps.size(), d, core); } ================================================ FILE: common-src/vapoursynth/vs_pack_rgb.h ================================================ #ifndef VS_PACK_RGB_H_INCLUDED #define VS_PACK_RGB_H_INCLUDED #include VSNode *packRGBFilter(VSNode *rgbNode, VSNode *outputNode, bool use10bit, VSCore *core, const VSAPI *vsapi); #endif ================================================ FILE: common-src/vapoursynth/vs_script_library.cpp ================================================ #include "vs_script_library.h" #include "../settings/settings_manager_core.h" #include "../helpers.h" #include #include #include //============================================================================== void VS_CC vsMessageHandler(int a_msgType, const char * a_message, void * a_pUserData) { VSScriptLibrary * pVSScriptLibrary = reinterpret_cast(a_pUserData); pVSScriptLibrary->handleVSMessage(a_msgType, a_message); } // END OF void VS_CC vsMessageHandler(int a_msgType, const char * a_message, // void * a_pUserData) //============================================================================== VSScriptLibrary::VSScriptLibrary(SettingsManagerCore * a_pSettingsManager, QObject * a_pParent): QObject(a_pParent) , m_pSettingsManager(a_pSettingsManager) , m_vsScriptLibrary(this) , m_initialized(false) , m_cpVSSAPI(nullptr) , m_cpVSAPI(nullptr) , m_VSAPIMajor(VSE_VS_API_VER_MAJOR) , m_VSAPIMinor(VSE_VS_API_VER_MINOR) , m_VSSAPIMajor(VSE_VSS_API_VER_MAJOR) , m_VSSAPIMinor(VSE_VSS_API_VER_MINOR) { Q_ASSERT(m_pSettingsManager); } // END OF VSScriptLibrary::VSScriptLibrary( // SettingsManagerCore * a_pSettingsManager, QObject * a_pParent) //============================================================================== VSScriptLibrary::~VSScriptLibrary() { m_VSCoreLogHandles.clear(); finalize(); } // END OF VSScriptLibrary::~VSScriptLibrary() //============================================================================== bool VSScriptLibrary::initialize() { if(m_initialized) return true; bool libraryInitialized = initLibrary2(); if(!libraryInitialized) return false; for(; m_VSAPIMinor >= 0; --m_VSAPIMinor) { int apiVer = VS_MAKE_VERSION(m_VSAPIMajor, m_VSAPIMinor); m_cpVSAPI = m_cpVSSAPI->getVSAPI(apiVer); if(m_cpVSAPI) break; } if(!m_cpVSAPI) { QString errorString = tr("Failed to get VapourSynth API!"); emit signalWriteLogMessage(mtCritical, errorString); finalize(); return false; } m_initialized = true; return true; } // END OF bool VSScriptLibrary::initialize() //============================================================================== bool VSScriptLibrary::finalize() { m_cpVSAPI = nullptr; freeLibrary(); m_initialized = false; return true; } // END OF bool VSScriptLibrary::finalize() //============================================================================== bool VSScriptLibrary::isInitialized() const { return m_initialized; } // END OF bool VSScriptLibrary::isInitialized() const //============================================================================== const VSAPI * VSScriptLibrary::getVSAPI() { if(!initialize()) return nullptr; return m_cpVSAPI; } // END OF const VSAPI * VSScriptLibrary::getVSAPI() //============================================================================== VSScript * VSScriptLibrary::createScript(VSCore * a_pCore) { if(!initialize()) return nullptr; VSScript * pScript = m_cpVSSAPI->createScript(a_pCore); if(pScript) m_cpVSSAPI->evalSetWorkingDir(pScript, 1); return pScript; } // END OF VSScript * VSScriptLibrary::createScript() //============================================================================== int VSScriptLibrary::evaluateScript(VSScript * a_pScript, const char * a_scriptText, const char * a_scriptFilename) { if(!initialize()) return 1; return m_cpVSSAPI->evaluateBuffer(a_pScript, a_scriptText, a_scriptFilename); } // END OF int VSScriptLibrary::evaluateScript(VSScript * a_ppScript, // const char * a_scriptText, const char * a_scriptFilename) //============================================================================== const char * VSScriptLibrary::getError(VSScript * a_pScript) { if(!initialize()) return nullptr; return m_cpVSSAPI->getError(a_pScript); } // END OF const char * VSScriptLibrary::getError(VSScript * a_pScript) //============================================================================== VSCore *VSScriptLibrary::createCore(int a_flag) { if(!initialize()) return nullptr; VSCore * pCore = m_cpVSAPI->createCore(a_flag); if(!pCore) { QString errorString = tr("Failed to get VapourSynth Core!"); emit signalWriteLogMessage(mtCritical, errorString); finalize(); return nullptr; } if(m_VSCoreLogHandles.find(pCore) == m_VSCoreLogHandles.end()) m_VSCoreLogHandles[pCore] = m_cpVSAPI->addLogHandler( vsMessageHandler, nullptr, this, pCore); return pCore; } std::vector VSScriptLibrary::getOutputIndices(VSScript *a_pScript) const { #if(VSSCRIPT_API_MAJOR == 4) && (VSSCRIPT_API_MINOR >= 2) if(m_initialized && a_pScript && vssVersionCompare(4, 2) >= 0) { int size = m_cpVSSAPI->getAvailableOutputNodes(a_pScript, 0, nullptr); if(size <= 0) return std::vector(); std::vector idx(size); m_cpVSSAPI->getAvailableOutputNodes(a_pScript, size, idx.data()); return idx; } else return std::vector(); #else return std::vector(); #endif } VSNode * VSScriptLibrary::getOutput(VSScript * a_pScript, int a_index) { if(!initialize()) return nullptr; return m_cpVSSAPI->getOutputNode(a_pScript, a_index); } // END OF VSNode * VSScriptLibrary::getOutput(VSScript * a_pScript, // int a_index) //============================================================================== bool VSScriptLibrary::freeScript(VSScript * a_pScript) { if(!initialize()) return false; m_cpVSSAPI->freeScript(a_pScript); return true; } bool VSScriptLibrary::clearCoreCaches(VSCore * a_pCore) { #if(VAPOURSYNTH_API_MAJOR == 4) && (VAPOURSYNTH_API_MINOR >= 1) if(vsVersionCompare(4, 1) >= 0) { if(!m_initialized) return false; m_cpVSAPI->clearCoreCaches(a_pCore); return true; } #endif return false; } QString VSScriptLibrary::VSAPIInfo() { if(!m_initialized) return QString(); return QString("R%1.%2").arg(m_VSAPIMajor).arg(m_VSAPIMinor); } QString VSScriptLibrary::VSSAPIInfo() { if(!m_initialized) return QString(); return QString("R%1.%2").arg(m_VSSAPIMajor).arg(m_VSSAPIMinor); } // END OF bool VSScriptLibrary::freeScript(VSScript * a_pScript) //============================================================================== bool VSScriptLibrary::initLibrary2() { if(m_vsScriptLibrary.isLoaded()) { Q_ASSERT(vssGetAPI); return true; } QString libraryName = "vsscript"; #ifdef Q_OS_WIN QString libraryNameOld = "vsscript"; QString libraryNameExt = "vsscript.dll"; bool libraryName2CS = false; int libraryName2Chop = 4; #elif defined(Q_OS_MACOS) QString libraryNameOld = "vapoursynth-script"; QString libraryNameExt = "libvsscript.4.dylib"; bool libraryName2CS = true; int libraryName2Chop = 8; #else QString libraryNameOld = "vapoursynth-script"; QString libraryNameExt = "libvsscript.so.4"; bool libraryName2CS = true; int libraryName2Chop = 5; #endif QString libraryDir; QString libraryFullPath = QString(); bool loaded = false; QString path = QString::fromLocal8Bit(qgetenv("PATH")); QString path_backup = path; QFunctionPointer * ppGetAPI = (QFunctionPointer *)&vssGetAPI; QFunctionPointer * ppLastError = (QFunctionPointer *)&vssGetAPILastError; auto set_path = [&]() { #ifdef Q_OS_WIN path = libraryDir + ";" + path; qputenv("PATH", path.toLocal8Bit()); #endif }; auto reset_path = [&]() { #ifdef Q_OS_WIN path = path_backup; qputenv("PATH", path.toLocal8Bit()); #endif }; auto load_vssapi = [&] (int max_minor_ver, int min_minor_ver) { *ppGetAPI = m_vsScriptLibrary.resolve("getVSScriptAPI"); if(!*ppGetAPI) return; if(max_minor_ver >= 3) *ppLastError = m_vsScriptLibrary.resolve("getVSScriptAPILastError"); const char * errVSSMsg = nullptr; for(int vminor = max_minor_ver; vminor >= min_minor_ver; --vminor) { int apiVer = VS_MAKE_VERSION(m_VSSAPIMajor, vminor); m_cpVSSAPI = vssGetAPI(apiVer); if(m_cpVSSAPI) { m_VSSAPIMinor = vminor; break; } if(vminor >= 3 && *ppLastError) { const char * lastError = vssGetAPILastError(); if(lastError && !errVSSMsg) errVSSMsg = lastError; } } if(m_cpVSSAPI) return; else { QString errMsg = QString("Library found in %1" " but failed to get VSScript API!").arg(libraryFullPath); if(errVSSMsg) emit signalWriteLogMessage(mtWarning, QString("%1\n%2").arg(errMsg).arg(errVSSMsg)); else emit signalWriteLogMessage(mtWarning, errMsg); return; } }; auto load_from_list2 = [&] () { QStringList librarySearchPaths = m_pSettingsManager->getVapourSynthLibraryPaths(); for(const QString & libPath : librarySearchPaths) { m_vsScriptLibrary.unload(); libraryDir = vsedit::resolvePathFromApplication(libPath); libraryFullPath = libraryDir + QString("/") + libraryName; m_vsScriptLibrary.setFileName(libraryFullPath); set_path(); loaded = m_vsScriptLibrary.load(); reset_path(); if(loaded) { load_vssapi(m_VSSAPIMinor, 3); loaded = m_cpVSSAPI != nullptr; } } }; auto load_from_list = [&] () { QStringList librarySearchPaths = m_pSettingsManager->getVapourSynthLibraryPaths(); for(const QString & libPath : librarySearchPaths) { m_vsScriptLibrary.unload(); libraryDir = vsedit::resolvePathFromApplication(libPath); libraryFullPath = libraryDir + QString("/") + libraryNameOld; m_vsScriptLibrary.setFileName(libraryFullPath); set_path(); loaded = m_vsScriptLibrary.load(); reset_path(); if(loaded) { load_vssapi(2, 0); loaded = m_cpVSSAPI != nullptr; } } }; auto load_from_python = [&] () { auto venv = qgetenv("VIRTUAL_ENV"); if(!venv.isEmpty()) { emit signalWriteLogMessage(mtInformation, QString( "You are in a Python virtual environment with path %1") .arg(QString::fromLocal8Bit(venv))); } QProcess vssProc; #ifdef Q_OS_WIN vssProc.startCommand("python -c \"import vapoursynth;" #else vssProc.startCommand("/usr/bin/env python3 -c \"import vapoursynth;" #endif "print(vapoursynth.get_vsscript())\""); if(vssProc.waitForFinished(3000)) { QString ret = QString::fromLocal8Bit( vssProc.readAllStandardOutput()).trimmed(); if(ret.count('\n') == 0 && ret.endsWith(libraryNameExt, libraryName2CS ? Qt::CaseSensitive : Qt::CaseInsensitive)) { #ifdef Q_OS_WIN ret.chop(libraryName2Chop); #endif libraryFullPath = ret; } } if(libraryFullPath.isEmpty()) return; m_vsScriptLibrary.unload(); m_vsScriptLibrary.setFileName(libraryFullPath); loaded = m_vsScriptLibrary.load(); if(loaded) { load_vssapi(m_VSSAPIMinor, 3); loaded = m_cpVSSAPI != nullptr; } }; auto load_from_env = [&] () { auto envPath = qgetenv("VSSCRIPT_PATH"); if(!envPath.isEmpty()) libraryFullPath = QString::fromLocal8Bit(envPath); if(libraryFullPath.isEmpty()) return; m_vsScriptLibrary.unload(); m_vsScriptLibrary.setFileName(libraryFullPath); loaded = m_vsScriptLibrary.load(); if(loaded) { load_vssapi(m_VSSAPIMinor, 0); loaded = m_cpVSSAPI != nullptr; } }; auto load_from_registry = [&] () { #ifdef Q_OS_WIN QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE", QSettings::NativeFormat); libraryFullPath = settings.value("VapourSynth/VSScriptDLL").toString(); if(libraryFullPath.isEmpty()) return; m_vsScriptLibrary.unload(); m_vsScriptLibrary.setFileName(libraryFullPath); loaded = m_vsScriptLibrary.load(); if(loaded) { load_vssapi(m_VSSAPIMinor, 0); loaded = m_cpVSSAPI != nullptr; } #endif }; auto load_from_path = [&]() { m_vsScriptLibrary.unload(); m_vsScriptLibrary.setFileName(libraryNameOld); loaded = m_vsScriptLibrary.load(); if(loaded) { load_vssapi(2, 0); loaded = m_cpVSSAPI != nullptr; } }; if(m_pSettingsManager->getPreferVSLibrariesFromList()) { if(!loaded) load_from_list2(); if(!loaded) load_from_list(); if(!loaded) load_from_python(); if(!loaded) load_from_env(); if(!loaded) load_from_registry(); if(!loaded) load_from_path(); } else { if(!loaded) load_from_python(); if(!loaded) load_from_env(); if(!loaded) load_from_list2(); if(!loaded) load_from_list(); if(!loaded) load_from_registry(); if(!loaded) load_from_path(); } if(!m_cpVSSAPI) { QString errorStr = QString("Failed to get VSScript API!"); emit signalWriteLogMessage(mtCritical, errorStr); freeLibrary(); return false; } return true; } // END OF bool VSScriptLibrary::initLibrary2() //============================================================================== void VSScriptLibrary::freeLibrary() { vssGetAPI = nullptr; if(m_vsScriptLibrary.isLoaded()) m_vsScriptLibrary.unload(); } // END OF void VSScriptLibrary::freeLibrary() //============================================================================== void VSScriptLibrary::handleVSMessage(int a_messageType, const QString & a_message) { emit signalWriteLogMessage(a_messageType, a_message); } // END OF void VSScriptLibrary::handleVSMessage(int a_messageType, // const QString & a_message) //============================================================================== ================================================ FILE: common-src/vapoursynth/vs_script_library.h ================================================ #ifndef VS_SCRIPT_LIBRARY_H_INCLUDED #define VS_SCRIPT_LIBRARY_H_INCLUDED #include "../version_info.h" #include #include #include #include #include class SettingsManagerCore; //============================================================================== typedef const VSSCRIPTAPI * (VS_CC * FNP_getVSSAPI)(int); typedef const char * (VS_CC * FNP_getVSSAPILastError)(); //============================================================================== class VSScriptLibrary : public QObject { Q_OBJECT public: VSScriptLibrary(SettingsManagerCore * a_pSettingsManager, QObject * a_pParent = nullptr); virtual ~VSScriptLibrary(); bool initialize(); bool finalize(); bool isInitialized() const; const VSAPI * getVSAPI(); VSScript * createScript(VSCore * a_pCore = nullptr); int evaluateScript(VSScript * a_pScript, const char * a_scriptText, const char * a_scriptFilename); const char * getError(VSScript * a_pScript); VSCore * createCore(int a_flag = 0); // Returns empty vector if not supported by API std::vector getOutputIndices(VSScript * a_pScript) const; VSNode * getOutput(VSScript * a_pScript, int a_index); bool freeScript(VSScript * a_pScript); bool clearCoreCaches(VSCore * a_pCore); QString VSAPIInfo(); QString VSSAPIInfo(); signals: void signalWriteLogMessage(int a_messageType, const QString & a_message); private: bool initLibrary2(); // For VSScript API 4.3 and later void freeLibrary(); void handleVSMessage(int a_messageType, const QString & a_message); friend void VS_CC vsMessageHandler(int a_msgType, const char * a_message, void * a_pUserData); SettingsManagerCore * m_pSettingsManager; QLibrary m_vsScriptLibrary; FNP_getVSSAPI vssGetAPI; FNP_getVSSAPILastError vssGetAPILastError; bool m_initialized; const VSSCRIPTAPI * m_cpVSSAPI; const VSAPI * m_cpVSAPI; std::map m_VSCoreLogHandles; int m_VSAPIMajor; int m_VSAPIMinor; int m_VSSAPIMajor; int m_VSSAPIMinor; int vsVersionCompare(int a_major, int a_minor) const { return version_compare(m_VSAPIMajor, m_VSAPIMinor, a_major, a_minor); } int vssVersionCompare(int a_major, int a_minor) const { return version_compare(m_VSSAPIMajor, m_VSSAPIMinor, a_major, a_minor); } }; //============================================================================== #endif // VS_SCRIPT_LIBRARY_H_INCLUDED ================================================ FILE: common-src/vapoursynth/vs_script_processor_structures.cpp ================================================ #include "vs_script_processor_structures.h" //============================================================================== Frame::Frame(int a_number, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame): number(a_number) , outputIndex(a_outputIndex) , cpOutputFrame(a_cpOutputFrame) , cpPreviewFrame(a_cpPreviewFrame) { } bool Frame::operator==(const Frame & a_other) const { return ((number == a_other.number) && (outputIndex == a_other.outputIndex)); } //============================================================================== FrameTicket::FrameTicket(int a_frameNumber, int a_outputIndex, VSNode * a_pOutputNode, bool a_needPreview, VSNode * a_pPreviewNode): frameNumber(a_frameNumber) , outputIndex(a_outputIndex) , pOutputNode(a_pOutputNode) , needPreview(a_needPreview) , pPreviewNode(a_pPreviewNode) , cpOutputFrame(nullptr) , cpPreviewFrame(nullptr) , discard(false) { } //============================================================================== bool FrameTicket::isComplete() const { bool complete = (cpOutputFrame != nullptr); if(needPreview) complete = complete && (cpPreviewFrame != nullptr); return complete; } //============================================================================== NodePair::NodePair(): outputIndex(-1) , pOutputNode(nullptr) , pPreviewNode(nullptr) { } //============================================================================== NodePair::NodePair(int a_outputIndex, VSNode * a_pOutputNode, VSNode * a_pPreviewNode): outputIndex(a_outputIndex) , pOutputNode(a_pOutputNode) , pPreviewNode(a_pPreviewNode) { } //============================================================================== bool NodePair::isNull() const { return ((outputIndex == -1) && (pOutputNode == nullptr) && (pPreviewNode == nullptr)); } //============================================================================== bool NodePair::isValid() const { return ((outputIndex >= 0) && (pOutputNode != nullptr) && (pPreviewNode != nullptr)); } //============================================================================== ================================================ FILE: common-src/vapoursynth/vs_script_processor_structures.h ================================================ #ifndef VS_SCRIPT_PROCESSOR_STRUCTURES_H_INCLUDED #define VS_SCRIPT_PROCESSOR_STRUCTURES_H_INCLUDED #include //============================================================================== struct Frame { int number; int outputIndex; const VSFrame * cpOutputFrame; const VSFrame * cpPreviewFrame; Frame(int a_number, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame = nullptr); bool operator==(const Frame & a_other) const; }; //============================================================================== struct FrameTicket { int frameNumber; int outputIndex; VSNode * pOutputNode; bool needPreview; VSNode * pPreviewNode; const VSFrame * cpOutputFrame; const VSFrame * cpPreviewFrame; bool discard; FrameTicket(int a_frameNumber, int a_outputIndex, VSNode * a_pOutputNode, bool a_needPreview = false, VSNode * a_pPreviewNode = nullptr); bool isComplete() const; }; //============================================================================== struct NodePair { int outputIndex; VSNode * pOutputNode; VSNode * pPreviewNode; NodePair(); NodePair(int a_outputIndex, VSNode * a_pOutputNode, VSNode * a_pPreviewNode); bool isNull() const; bool isValid() const; }; //============================================================================== #endif // VS_SCRIPT_PROCESSOR_STRUCTURES_H_INCLUDED ================================================ FILE: common-src/vapoursynth/vs_set_matrix.cpp ================================================ #include "vs_set_matrix.h" #include #include #include #include struct setMatrixData { VSNode *node; VSVideoInfo vi; int64_t matrix_in; }; const VSFrame * VS_CC setMatrixGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { setMatrixData *d = reinterpret_cast(instanceData); if (activationReason == arInitial) { vsapi->requestFrameFilter(n, d->node, frameCtx); } else if (activationReason == arAllFramesReady) { const VSFrame *src_frame = vsapi->getFrameFilter(n, d->node, frameCtx); /* RGB: tag RGB matrix YUV: if missing matrix, add it GRAY: if having RGB matrix, drop it */ const VSVideoFormat *vff = vsapi->getVideoFrameFormat(src_frame); VSFrame *dst_frame = vsapi->copyFrame(src_frame, core); vsapi->freeFrame(src_frame); VSMap *props = vsapi->getFramePropertiesRW(dst_frame); if (vff->colorFamily == cfRGB) { vsapi->mapSetInt(props, "_Matrix", VSC_MATRIX_RGB, maReplace); } else if (vff->colorFamily == cfYUV) { int err; int64_t matrix = vsapi->mapGetInt(props, "_Matrix", 0, &err); if (err) { vsapi->mapSetInt(props, "_Matrix", d->matrix_in, maReplace); } } else { int err; int64_t matrix = vsapi->mapGetInt(props, "_Matrix", 0, &err); if (!err && matrix == 0) { vsapi->mapDeleteKey(props, "_Matrix"); } } return dst_frame; } return nullptr; } void VS_CC setMatrixFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { setMatrixData *d = reinterpret_cast(instanceData); vsapi->freeNode(d->node); delete d; } void VS_CC setMatrixFilter(const VSMap *in, VSMap *out, VSCore *core, const VSAPI *vsapi) { std::unique_ptr d(new setMatrixData()); d->node = vsapi->mapGetNode(in, "clip", 0, nullptr); const VSVideoInfo *vi = vsapi->getVideoInfo(d->node); d->vi = *vi; int err; d->matrix_in = vsapi->mapGetInt(in, "matrix_in", 0, &err); VSFilterDependency deps[] = {{d->node, rpStrictSpatial}}; vsapi->createVideoFilter(out, "SetMatrix", &d->vi, setMatrixGetFrame, setMatrixFree, fmParallel, deps, 1, d.get(), core); d.release(); } ================================================ FILE: common-src/vapoursynth/vs_set_matrix.h ================================================ #ifndef VS_SET_MATRIX_H_INCLUDED #define VS_SET_MATRIX_H_INCLUDED #include void VS_CC setMatrixFilter(const VSMap *in, VSMap *out, VSCore *core, const VSAPI *vsapi); #endif ================================================ FILE: common-src/version_info.cpp ================================================ #include "version_info.h" #include void print_version() { std::cerr << "VapourSynth Editor " << VSE_VERSION_STR << std::endl; } int version_compare(int major1, int minor1, int major2, int minor2) { if(major1 > major2) return 1; else if(major1 < major2) return -1; else if(minor1 > minor2) return 1; else if(minor1 < minor2) return -1; else return 0; } ================================================ FILE: common-src/version_info.h ================================================ #ifndef VERSION_INFO_H_INCLUDED #define VERSION_INFO_H_INCLUDED #define VSE_VERSION_STR "R19-mod-6.10" #define VS_USE_LATEST_API #define VSE_VS_API_VER_MAJOR 4 #define VSE_VS_API_VER_MINOR 2 #define VSSCRIPT_USE_LATEST_API #define VSE_VSS_API_VER_MAJOR 4 #define VSE_VSS_API_VER_MINOR 3 void print_version(); int version_compare(int major1, int minor1, int major2, int minor2); #endif ================================================ FILE: common-src/win32_console.cpp ================================================ #pragma comment (lib, "kernel32.lib") #pragma comment (lib, "user32.lib") #if defined(_WIN32) #include "win32_console.h" void AttachedConsole::init() { if (console) return; if (!AllocConsole()) return; console = GetConsoleWindow(); if (!console) return; freopen_s(&file, "CONOUT$", "w", stderr); freopen_s(&file, "CONOUT$", "w", stdout); HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); DWORD mode; GetConsoleMode(handle, &mode); mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; mode |= DISABLE_NEWLINE_AUTO_RETURN; SetConsoleMode(handle, mode); } AttachedConsole::AttachedConsole() : console(NULL), file(nullptr) { init(); if (console) ShowWindow(console, SW_HIDE); } bool AttachedConsole::visible() { if (!console) return false; return IsWindowVisible(console); } void AttachedConsole::show() { if (!console) { init(); if (!console) return; } ShowWindow(console, SW_SHOWNOACTIVATE); } void AttachedConsole::hide() { if (!console) return; clear(); ShowWindow(console, SW_HIDE); } void AttachedConsole::destroy() { if (!console) return; clear(); FreeConsole(); } void AttachedConsole::clear() { std::wcerr.clear(); std::wcout.clear(); std::wclog.clear(); std::cerr.clear(); std::cout.clear(); std::clog.clear(); if (console) { SetConsoleTextAttribute(console, 0); } } #endif ================================================ FILE: common-src/win32_console.h ================================================ #ifndef WIN32_CONSOLE_H_INCLUDED #define WIN32_CONSOLE_H_INCLUDED #if defined(_WIN32) #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include "Windows.h" #include class AttachedConsole { private: HWND console; FILE *file; void init(); void clear(); public: AttachedConsole(); bool visible(); void show(); void hide(); void destroy(); }; #endif #endif ================================================ FILE: msvc/VapourSynthEditor.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsedit", "vsedit\vsedit.vcxproj", "{CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libp2p", "libp2p\libp2p.vcxproj", "{DD503806-0462-4CFE-BC5D-6355CF100832}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsedit-previewer", "vsedit-previewer\vsedit-previewer.vcxproj", "{654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsedit-job-server", "vsedit-job-server\vsedit-job-server.vcxproj", "{BF53C69D-89A2-40B4-977E-1C535B4C7EB8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsedit-job-server-watcher", "vsedit-job-server-watcher\vsedit-job-server-watcher.vcxproj", "{1550A490-74AA-413F-97C4-AFFA1BB470C2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsedit-encode", "vsedit-encode\vsedit-encode.vcxproj", "{FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Debug|x64.ActiveCfg = Debug|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Debug|x64.Build.0 = Debug|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Debug|x86.ActiveCfg = Debug|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Debug|x86.Build.0 = Debug|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Release|x64.ActiveCfg = Release|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Release|x64.Build.0 = Release|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Release|x86.ActiveCfg = Release|x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD}.Release|x86.Build.0 = Release|x64 {DD503806-0462-4CFE-BC5D-6355CF100832}.Debug|x64.ActiveCfg = Debug|x64 {DD503806-0462-4CFE-BC5D-6355CF100832}.Debug|x64.Build.0 = Debug|x64 {DD503806-0462-4CFE-BC5D-6355CF100832}.Debug|x86.ActiveCfg = Debug|Win32 {DD503806-0462-4CFE-BC5D-6355CF100832}.Debug|x86.Build.0 = Debug|Win32 {DD503806-0462-4CFE-BC5D-6355CF100832}.Release|x64.ActiveCfg = Release|x64 {DD503806-0462-4CFE-BC5D-6355CF100832}.Release|x64.Build.0 = Release|x64 {DD503806-0462-4CFE-BC5D-6355CF100832}.Release|x86.ActiveCfg = Release|Win32 {DD503806-0462-4CFE-BC5D-6355CF100832}.Release|x86.Build.0 = Release|Win32 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Debug|x64.ActiveCfg = Debug|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Debug|x64.Build.0 = Debug|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Debug|x86.ActiveCfg = Debug|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Debug|x86.Build.0 = Debug|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Release|x64.ActiveCfg = Release|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Release|x64.Build.0 = Release|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Release|x86.ActiveCfg = Release|x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04}.Release|x86.Build.0 = Release|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Debug|x64.ActiveCfg = Debug|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Debug|x64.Build.0 = Debug|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Debug|x86.ActiveCfg = Debug|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Debug|x86.Build.0 = Debug|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Release|x64.ActiveCfg = Release|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Release|x64.Build.0 = Release|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Release|x86.ActiveCfg = Release|x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8}.Release|x86.Build.0 = Release|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Debug|x64.ActiveCfg = Debug|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Debug|x64.Build.0 = Debug|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Debug|x86.ActiveCfg = Debug|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Debug|x86.Build.0 = Debug|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Release|x64.ActiveCfg = Release|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Release|x64.Build.0 = Release|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Release|x86.ActiveCfg = Release|x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2}.Release|x86.Build.0 = Release|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Debug|x64.ActiveCfg = Debug|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Debug|x64.Build.0 = Debug|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Debug|x86.ActiveCfg = Debug|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Debug|x86.Build.0 = Debug|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Release|x64.ActiveCfg = Release|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Release|x64.Build.0 = Release|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Release|x86.ActiveCfg = Release|x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B21A7A0A-C50F-4BF0-AD63-9DA782B23EEE} EndGlobalSection EndGlobal ================================================ FILE: msvc/VapourSynthEditor.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {0688c101-c065-46f9-a1ca-9e11280c2ace} VapourSynthEditor 10.0 Application true v143 Unicode Application false v143 true Unicode Application true v143 Unicode Application false v143 true Unicode Level3 true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true ================================================ FILE: msvc/VapourSynthEditor.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms ================================================ FILE: msvc/libp2p/libp2p.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {dd503806-0462-4cfe-bc5d-6355cf100832} libp2p 10.0 Application true v143 Unicode Application false v143 true Unicode Application true v143 Unicode StaticLibrary false v143 true Unicode Level3 true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level3 true true true P2P_SIMD;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true ================================================ FILE: msvc/libp2p/libp2p.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files ================================================ FILE: msvc/vsedit/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by vsedit.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: msvc/vsedit/vsedit.vcxproj ================================================  Debug x64 Release x64 {CD54B5E3-31F8-49BC-9D02-E6E7B9D25FAD} QtVS_v304 10.0.22621.0 10.0 $(MSBuildProjectDirectory)\QtMsBuild Application v143 Application v143 true C:\Qt\6.7.2\msvc2019_64 debug C:\Qt\6.7.2\msvc2019_64 core;gui;multimedia;widgets release true C:\Program Files\VapourSynth\sdk\include;$(IncludePath) ..\..\vsedit\generated\moc ..\..\vsedit\generated\rcc ..\..\vsedit\generated\ui true true ProgramDatabase Disabled Windows true true true None MaxSpeed Windows false {dd503806-0462-4cfe-bc5d-6355cf100832} ================================================ FILE: msvc/vsedit/vsedit.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {99349809-55BA-4b9d-BF79-8FDBB0286EB3} ui {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} ts Form Files Form Files Form Files Form Files Form Files Form Files Form Files Form Files Form Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files Resource Files Resource Files Resource Files ================================================ FILE: msvc/vsedit-encode/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by vsedit.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: msvc/vsedit-encode/vsedit-encode.vcxproj ================================================  Debug x64 Release x64 {FFEEADE6-3DBE-4371-B1BC-D67DC80FE0CF} QtVS_v304 10.0 10.0 $(MSBuildProjectDirectory)\QtMsBuild Application v143 true Unicode Application v143 false true Unicode 6.7.2_msvc2019_64 core debug 6.7.2_msvc2019_64 core;gui;widgets release true C:\Program Files\VapourSynth\sdk\include;$(VC_IncludePath);$(WindowsSDK_IncludePath); ..\..\vsedit\generated\moc ..\..\vsedit\generated\rcc ..\..\vsedit\generated\ui true Level3 true true Console true true Level3 true true true true Console false true true {dd503806-0462-4cfe-bc5d-6355cf100832} ================================================ FILE: msvc/vsedit-encode/vsedit-encode.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {99349809-55BA-4b9d-BF79-8FDBB0286EB3} ui {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} ts Form Files Form Files Form Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Resource Files Resource Files Resource Files Resource Files ================================================ FILE: msvc/vsedit-job-server/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by vsedit.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: msvc/vsedit-job-server/vsedit-job-server.vcxproj ================================================  Debug x64 Release x64 {BF53C69D-89A2-40B4-977E-1C535B4C7EB8} QtVS_v304 10.0.22621.0 10.0 $(MSBuildProjectDirectory)\QtMsBuild Application v143 Application v143 true C:\Qt\6.7.2\msvc2019_64 debug C:\Qt\6.7.2\msvc2019_64 core;;widgets;websockets release true C:\Program Files\VapourSynth\sdk\include;$(IncludePath) ..\..\vsedit-job-server\generated\moc ..\..\vsedit-job-server\generated\rcc ..\..\vsedit-job-server\generated\ui true true ProgramDatabase Disabled Windows true true true None MaxSpeed Console false {dd503806-0462-4cfe-bc5d-6355cf100832} ================================================ FILE: msvc/vsedit-job-server/vsedit-job-server.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {99349809-55BA-4b9d-BF79-8FDBB0286EB3} ui {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} ts Resource Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files ================================================ FILE: msvc/vsedit-job-server-watcher/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by vsedit.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: msvc/vsedit-job-server-watcher/vsedit-job-server-watcher.vcxproj ================================================  Debug x64 Release x64 {1550A490-74AA-413F-97C4-AFFA1BB470C2} QtVS_v304 10.0.22621.0 10.0 $(MSBuildProjectDirectory)\QtMsBuild Application v143 Application v143 true C:\Qt\6.7.2\msvc2019_64 debug C:\Qt\6.7.2\msvc2019_64 core;gui;widgets;websockets release true C:\Program Files\VapourSynth\sdk\include;$(IncludePath) ..\..\vsedit-job-server-watcher\generated\moc ..\..\vsedit-job-server-watcher\generated\rcc ..\..\vsedit-job-server-watcher\generated\ui true true ProgramDatabase Disabled Windows true true true None MaxSpeed Windows false {dd503806-0462-4cfe-bc5d-6355cf100832} ================================================ FILE: msvc/vsedit-job-server-watcher/vsedit-job-server-watcher.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {99349809-55BA-4b9d-BF79-8FDBB0286EB3} ui {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} ts Resource Files Resource Files Resource Files Resource Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Form Files Form Files Form Files Form Files Form Files ================================================ FILE: msvc/vsedit-previewer/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by vsedit.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: msvc/vsedit-previewer/vsedit-previewer.vcxproj ================================================  Debug x64 Release x64 {654CE67F-6EB3-4DBD-86F2-D3DB7081FA04} QtVS_v304 10.0.22621.0 10.0 $(MSBuildProjectDirectory)\QtMsBuild Application v143 Application v143 true C:\Qt\6.7.2\msvc2019_64 debug C:\Qt\6.7.2\msvc2019_64 core;gui;multimedia;widgets release true C:\Program Files\VapourSynth\sdk\include;$(IncludePath) ..\..\vsedit\generated\moc ..\..\vsedit\generated\rcc ..\..\vsedit\generated\ui true true ProgramDatabase Disabled Windows true true true None MaxSpeed Console false {dd503806-0462-4cfe-bc5d-6355cf100832} ================================================ FILE: msvc/vsedit-previewer/vsedit-previewer.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {99349809-55BA-4b9d-BF79-8FDBB0286EB3} ui {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} ts Form Files Form Files Form Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files Resource Files Resource Files Resource Files ================================================ FILE: pro/build-inno-setup.iss ================================================ ; https://jrsoftware.org/ishelp/index.php #define AppName "VapourSynth Editor" #define ExeName "vsedit" #define Version "r19-mod-6.10" [Setup] AppId={#AppName} AppName={#AppName} AppPublisher=YomikoR AppPublisherURL=https://github.com/YomikoR/VapourSynth-Editor AppReadmeFile=https://github.com/YomikoR/VapourSynth-Editor/blob/vs-api4/README AppSupportURL=https://github.com/YomikoR/VapourSynth-Editor/issues AppUpdatesURL=https://github.com/YomikoR/VapourSynth-Editor/blob/vs-api4/CHANGELOG AppVerName={#AppName} {#Version} AppVersion={#Version} ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 ChangesAssociations=yes Compression=lzma2/max DefaultDirName={autopf}\{#AppName} DefaultGroupName={#AppName} LicenseFile=..\LICENSE OutputBaseFilename={#AppName}-{#Version}-setup OutputDir=dist OutputManifestFile={#AppName}-{#Version}-setup-manifest.txt PrivilegesRequired=lowest PrivilegesRequiredOverridesAllowed=dialog commandline SetupIconFile=..\resources\{#ExeName}.ico SolidCompression=yes VersionInfoVersion=1.0.0 WizardStyle=modern dynamic [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: ..\build\release-64bit-msvc\vsedit.exe; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\vsedit-job-server.exe; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\vsedit-job-server-watcher.exe; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\vsedit-previewer.exe; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\vsedit-encode.exe; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\vsedit.ico; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\vsedit.svg; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\README; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\LICENSE; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\CHANGELOG; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\Qt6Core.dll; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\Qt6Gui.dll; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\Qt6Multimedia.dll; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\Qt6Network.dll; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\Qt6WebSockets.dll; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\Qt6Widgets.dll; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\imageformats\qwebp.dll; DestDir: {app}\imageformats; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\platforms\qwindows.dll; DestDir: {app}\platforms; Flags: ignoreversion uninsrestartdelete restartreplace Source: ..\build\release-64bit-msvc\multimedia\windowsmediaplugin.dll; DestDir: {app}\multimedia; Flags: ignoreversion uninsrestartdelete restartreplace [Icons] Name: "{group}\{#AppName}"; Filename: "{app}\{#ExeName}.exe" Name: "{group}\VapourSynth Jobs Server Watcher"; Filename: "{app}\{#ExeName}-job-server-watcher.exe" Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#ExeName}.exe"; Tasks: desktopicon [Run] Filename: "{app}\{#ExeName}.exe"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent ================================================ FILE: pro/build-msvc.bat ================================================ @ECHO THIS SCRIPT IS OUTDATED AND NO MORE MAINTAINED. USE IT AS A REFERENCE ONLY. @SET BRANCH=%1 @IF "%BRANCH%"=="" ( SET GIT_BRANCH= SET NAME_BRANCH= ) @IF NOT "%BRANCH%"=="" ( SET GIT_BRANCH=-b %BRANCH% SET NAME_BRANCH=_%BRANCH% ) @SET BUILDROOT=vsedit-build @pushd "%TEMP%" @rd /q /s %BUILDROOT% @mkdir %BUILDROOT% git clone %GIT_BRANCH% https://bitbucket.org/mystery_keeper/vapoursynth-editor.git vsedit-build @cd /d %BUILDROOT%\pro @SET ORIGINAL_PATH=%PATH% :: MSVC 64 bit build @ECHO === SETTING UP ENVIRONMENT === @call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" @SET PATH=%PATH%;S:\sdk\qt\5.12.0\msvc2017_64\bin\ @ECHO === RUNNING QMAKE === qmake.exe -nocache "CONFIG+=release" pro.pro @ECHO === BUILDING === nmake.exe release -f Makefile @ECHO === ARCHIVING === @cd /d ../build/release-64bit-msvc @del vc_redist.x64.exe @7z a -y -m0=LZMA -mx9 "%~dp0/vsedit%NAME_BRANCH%.7z" "*" @iscc build-inno-setup.iss :: Clean up the environment @SET PATH=%ORIGINAL_PATH% @popd ================================================ FILE: pro/common.pri ================================================ VER_MAJ = 19 VERSION = $$VER_MAJ QMAKE_TARGET_COMPANY = 'Aleksey [Mystery Keeper] Lyashin' QMAKE_TARGET_COPYRIGHT = $$QMAKE_TARGET_COMPANY ================================================ FILE: pro/pro.pro ================================================ lessThan(QT_MAJOR_VERSION, 6) { error(You must build with Qt 6 or later) } TEMPLATE = subdirs SUBDIRS += vsedit SUBDIRS += vsedit-previewer SUBDIRS += vsedit-job-server SUBDIRS += vsedit-job-server-watcher SUBDIRS += vsedit-encode vsedit.file = ./vsedit/vsedit.pro vsedit-previewer.file = ./vsedit-previewer/vsedit-previewer.pro vsedit-job-server.file = ./vsedit-job-server/vsedit-job-server.pro vsedit-job-server-watcher.file = ./vsedit-job-server-watcher/vsedit-job-server-watcher.pro vsedit-encode.file = ./vsedit-encode/vsedit-encode.pro ================================================ FILE: pro/vsedit/vsedit.pro ================================================ CONFIG += qt QT += widgets QT += network win32 { QT += multimedia } HOST_64_BIT = contains(QMAKE_HOST.arch, "x86_64") TARGET_64_BIT = contains(QMAKE_TARGET.arch, "x86_64") ARCHITECTURE_64_BIT = $$HOST_64_BIT | $$TARGET_64_BIT PROJECT_DIRECTORY = ../../vsedit COMMON_DIRECTORY = ../.. INCLUDEPATH += $$(VS_INCLUDE_PATH) CONFIG(debug, debug|release) { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-gcc TARGET = vsedit-debug-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-gcc TARGET = vsedit-debug-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-gcc } QMAKE_CXXFLAGS += -O0 QMAKE_CXXFLAGS += -g QMAKE_CXXFLAGS += -ggdb3 } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-msvc TARGET = vsedit-debug-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-msvc TARGET = vsedit-debug-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-msvc } } } else { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-gcc TARGET = vsedit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-gcc TARGET = vsedit-32bit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-gcc } QMAKE_CXXFLAGS += -O2 QMAKE_CXXFLAGS += -fexpensive-optimizations QMAKE_CXXFLAGS += -funit-at-a-time } macx { QMAKE_CXXFLAGS -= -fexpensive-optimizations } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-msvc TARGET = vsedit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-msvc TARGET = vsedit-32bit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-msvc } } DEFINES += NDEBUG } S = $${DIR_SEPARATOR} D = $$DESTDIR D = $$replace(D, /, $$S) SC = $${COMMON_DIRECTORY}/ SC = $$replace(SC, /, $$S) E = $$escape_expand(\n\t) QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}resources$${S}vsedit.ico $${D}$${S}vsedit.ico $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}resources$${S}vsedit.svg $${D}$${S}vsedit.svg $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}README $${D}$${S}README $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}LICENSE $${D}$${S}LICENSE $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}CHANGELOG $${D}$${S}CHANGELOG $${E} macx { INCLUDEPATH += /usr/local/include ICON = $${COMMON_DIRECTORY}/resources/vsedit.icns } win32 { QMAKE_LFLAGS += '/entry:mainCRTStartup' INCLUDEPATH += 'C:/Program Files/VapourSynth/sdk/include/vapoursynth' DEPLOY_COMMAND = windeployqt DEPLOY_TARGET = $$shell_quote($$shell_path($${D}/$${TARGET}.exe)) QMAKE_POST_LINK += $${DEPLOY_COMMAND} --no-translations --no-svg --no-opengl-sw --no-system-d3d-compiler $${DEPLOY_TARGET} $${E} if($$ARCHITECTURE_64_BIT) { message("x86_64 build") } else { message("x86 build") contains(QMAKE_COMPILER, gcc) { QMAKE_LFLAGS += -Wl,--large-address-aware } contains(QMAKE_COMPILER, msvc) { QMAKE_LFLAGS += /LARGEADDRESSAWARE } } } contains(QMAKE_COMPILER, clang) { QMAKE_CXXFLAGS += -stdlib=libc++ } contains(QMAKE_COMPILER, gcc) { QMAKE_CXXFLAGS += -std=c++17 QMAKE_CXXFLAGS += -Wall QMAKE_CXXFLAGS += -Wextra QMAKE_CXXFLAGS += -Wredundant-decls QMAKE_CXXFLAGS += -Wshadow #QMAKE_CXXFLAGS += -Weffc++ QMAKE_CXXFLAGS += -pedantic LIBS += -L$$[QT_INSTALL_LIBS] } else { CONFIG += c++17 } include($${COMMON_DIRECTORY}/pro/common.pri) TEMPLATE = app RC_ICONS = $${COMMON_DIRECTORY}/resources/vsedit.ico QMAKE_TARGET_PRODUCT = 'VapourSynth Editor' QMAKE_TARGET_DESCRIPTION = 'VapourSynth Editor' #SUBDIRS MOC_DIR = $${PROJECT_DIRECTORY}/generated/moc UI_DIR = $${PROJECT_DIRECTORY}/generated/ui RCC_DIR = $${PROJECT_DIRECTORY}/generated/rcc #DEFINES #TRANSLATIONS RESOURCES = $${COMMON_DIRECTORY}/resources/vsedit.qrc RESOURCES += $${COMMON_DIRECTORY}/resources/dark/style.qrc FORMS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/settings/settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.ui FORMS += $${PROJECT_DIRECTORY}/src/preview/preview_advanced_settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/preview/preview_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/frame_consumers/benchmark_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/frame_consumers/encode_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/script_templates/templates_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/main_window.ui HEADERS += $${COMMON_DIRECTORY}/common-src/helpers.h HEADERS += $${COMMON_DIRECTORY}/common-src/helpers_vs.h HEADERS += $${COMMON_DIRECTORY}/common-src/version_info.h HEADERS += $${COMMON_DIRECTORY}/common-src/win32_console.h HEADERS += $${COMMON_DIRECTORY}/common-src/chrono.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/log_styles_model.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_null.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_y4m.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.h HEADERS += $${COMMON_DIRECTORY}/common-src/timeline_slider/timeline_slider.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/actions_hotkey_edit_model.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/clearable_key_sequence_editor.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/item_delegate_for_hotkey.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/theme_elements_model.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/settings_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/scroll_navigator.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/preview_area.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/preview_advanced_settings_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/preview_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/zoom_ratio_spinbox.h HEADERS += $${PROJECT_DIRECTORY}/src/script_editor/number_matcher.h HEADERS += $${PROJECT_DIRECTORY}/src/script_editor/syntax_highlighter.h HEADERS += $${PROJECT_DIRECTORY}/src/script_editor/script_completer_model.h HEADERS += $${PROJECT_DIRECTORY}/src/script_editor/script_completer.h HEADERS += $${PROJECT_DIRECTORY}/src/script_editor/script_editor.h HEADERS += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_plugin_data.h HEADERS += $${PROJECT_DIRECTORY}/src/vapoursynth/vapoursynth_plugins_manager.h HEADERS += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_script_processor_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/job_server_watcher_socket.h HEADERS += $${PROJECT_DIRECTORY}/src/frame_consumers/benchmark_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/frame_consumers/encode_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/script_templates/drop_file_category_model.h HEADERS += $${PROJECT_DIRECTORY}/src/script_templates/templates_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/main_window.h SOURCES += $${COMMON_DIRECTORY}/common-src/helpers.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/version_info.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/win32_console.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/log_styles_model.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_null.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_y4m.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/timeline_slider/timeline_slider.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/actions_hotkey_edit_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/clearable_key_sequence_editor.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/item_delegate_for_hotkey.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/theme_elements_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/settings_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/scroll_navigator.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/preview_area.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/preview_advanced_settings_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/preview_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/zoom_ratio_spinbox.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_editor/number_matcher.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_editor/syntax_highlighter.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_editor/script_completer_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_editor/script_completer.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_editor/script_editor.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_plugin_data.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vapoursynth/vapoursynth_plugins_manager.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_script_processor_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/job_server_watcher_socket.cpp SOURCES += $${PROJECT_DIRECTORY}/src/frame_consumers/benchmark_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/frame_consumers/encode_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_templates/drop_file_category_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_templates/templates_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/main_window.cpp SOURCES += $${PROJECT_DIRECTORY}/src/main.cpp # libp2p SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/v210.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.cpp if($$ARCHITECTURE_64_BIT) { SOURCES_P2P_SSE41 += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_sse41.cpp } p2p.name = p2p p2p.input = SOURCES_P2P p2p.dependency_type = TYPE_C p2p.variable_out = OBJECTS p2p.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p.commands += -Fo${QMAKE_FILE_OUT} } else { p2p.commands += -o ${QMAKE_FILE_OUT} p2p.commands += -std=c++14 p2p.commands += -Wno-missing-field-initializers } macx { p2p.commands += -Wno-gnu } QMAKE_EXTRA_COMPILERS += p2p if($$ARCHITECTURE_64_BIT) { p2p_sse41.name = p2p_sse41 p2p_sse41.input = SOURCES_P2P_SSE41 p2p_sse41.dependency_type = TYPE_C p2p_sse41.variable_out = OBJECTS p2p_sse41.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p_sse41.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p_sse41.commands += -Fo${QMAKE_FILE_OUT} } else { p2p_sse41.commands += -msse4.1 p2p_sse41.commands += -o ${QMAKE_FILE_OUT} p2p_sse41.commands += -std=c++14 p2p_sse41.commands += -Wno-missing-field-initializers } } macx { p2p_sse41.commands += -Wno-gnu } if($$ARCHITECTURE_64_BIT) { QMAKE_EXTRA_COMPILERS += p2p_sse41 } include($${COMMON_DIRECTORY}/pro/local_quirks.pri) ================================================ FILE: pro/vsedit-encode/vsedit-encode.pro ================================================ CONFIG += qt QT += widgets win32 { CONFIG += console } HOST_64_BIT = contains(QMAKE_HOST.arch, "x86_64") TARGET_64_BIT = contains(QMAKE_TARGET.arch, "x86_64") ARCHITECTURE_64_BIT = $$HOST_64_BIT | $$TARGET_64_BIT PROJECT_DIRECTORY = ../../vsedit COMMON_DIRECTORY = ../.. INCLUDEPATH += $$(VS_INCLUDE_PATH) CONFIG(debug, debug|release) { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-gcc TARGET = vsedit-encode-debug-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-gcc TARGET = vsedit-encode-debug-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-gcc } QMAKE_CXXFLAGS += -O0 QMAKE_CXXFLAGS += -g QMAKE_CXXFLAGS += -ggdb3 } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-msvc TARGET = vsedit-encode-debug-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-msvc TARGET = vsedit-encode-debug-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-msvc } } } else { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-gcc TARGET = vsedit-encode OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-gcc TARGET = vsedit-encode-32bit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-gcc } QMAKE_CXXFLAGS += -O2 QMAKE_CXXFLAGS += -fexpensive-optimizations QMAKE_CXXFLAGS += -funit-at-a-time } macx { QMAKE_CXXFLAGS -= -fexpensive-optimizations } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-msvc TARGET = vsedit-encode OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-msvc TARGET = vsedit-encode-32bit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-msvc } } DEFINES += NDEBUG } S = $${DIR_SEPARATOR} D = $$DESTDIR D = $$replace(D, /, $$S) SC = $${COMMON_DIRECTORY}/ SC = $$replace(SC, /, $$S) E = $$escape_expand(\n\t) QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}resources$${S}vsedit.ico $${D}$${S}vsedit.ico $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}resources$${S}vsedit.svg $${D}$${S}vsedit.svg $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}README $${D}$${S}README $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}LICENSE $${D}$${S}LICENSE $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}CHANGELOG $${D}$${S}CHANGELOG $${E} macx { INCLUDEPATH += /usr/local/include ICON = $${COMMON_DIRECTORY}/resources/vsedit.icns } win32 { INCLUDEPATH += 'C:/Program Files/VapourSynth/sdk/include/vapoursynth' DEPLOY_COMMAND = windeployqt DEPLOY_TARGET = $$shell_quote($$shell_path($${D}/$${TARGET}.exe)) QMAKE_POST_LINK += $${DEPLOY_COMMAND} --no-translations --no-svg --no-opengl-sw --no-system-d3d-compiler $${DEPLOY_TARGET} $${E} if($$ARCHITECTURE_64_BIT) { message("x86_64 build") } else { message("x86 build") contains(QMAKE_COMPILER, gcc) { QMAKE_LFLAGS += -Wl,--large-address-aware } contains(QMAKE_COMPILER, msvc) { QMAKE_LFLAGS += /LARGEADDRESSAWARE } } } contains(QMAKE_COMPILER, clang) { QMAKE_CXXFLAGS += -stdlib=libc++ } contains(QMAKE_COMPILER, gcc) { QMAKE_CXXFLAGS += -std=c++17 QMAKE_CXXFLAGS += -Wall QMAKE_CXXFLAGS += -Wextra QMAKE_CXXFLAGS += -Wredundant-decls QMAKE_CXXFLAGS += -Wshadow #QMAKE_CXXFLAGS += -Weffc++ QMAKE_CXXFLAGS += -pedantic LIBS += -L$$[QT_INSTALL_LIBS] } else { CONFIG += c++17 } include($${COMMON_DIRECTORY}/pro/common.pri) TEMPLATE = app RC_ICONS = $${COMMON_DIRECTORY}/resources/vsedit.ico QMAKE_TARGET_PRODUCT = 'VapourSynth Editor Encode Panel' QMAKE_TARGET_DESCRIPTION = 'VapourSynth Editor Encode Panel' #SUBDIRS MOC_DIR = $${PROJECT_DIRECTORY}/generated/moc UI_DIR = $${PROJECT_DIRECTORY}/generated/ui RCC_DIR = $${PROJECT_DIRECTORY}/generated/rcc #DEFINES #TRANSLATIONS RESOURCES = $${COMMON_DIRECTORY}/resources/vsedit.qrc RESOURCES += $${COMMON_DIRECTORY}/resources/dark/style.qrc FORMS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.ui FORMS += $${PROJECT_DIRECTORY}/src/frame_consumers/encode_dialog.ui HEADERS += $${COMMON_DIRECTORY}/common-src/helpers.h HEADERS += $${COMMON_DIRECTORY}/common-src/helpers_vs.h HEADERS += $${COMMON_DIRECTORY}/common-src/version_info.h HEADERS += $${COMMON_DIRECTORY}/common-src/chrono.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/log_styles_model.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_null.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_y4m.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/actions_hotkey_edit_model.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/clearable_key_sequence_editor.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/item_delegate_for_hotkey.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/theme_elements_model.h HEADERS += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.h HEADERS += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_script_processor_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/frame_consumers/encode_dialog.h SOURCES += $${COMMON_DIRECTORY}/common-src/helpers.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/version_info.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/log_styles_model.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_null.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_y4m.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/actions_hotkey_edit_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/clearable_key_sequence_editor.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/item_delegate_for_hotkey.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/theme_elements_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.cpp SOURCES += $${PROJECT_DIRECTORY}/src/frame_consumers/encode_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_script_processor_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vsedit_encode_main.cpp # libp2p SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/v210.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.cpp if($$ARCHITECTURE_64_BIT) { SOURCES_P2P_SSE41 += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_sse41.cpp } p2p.name = p2p p2p.input = SOURCES_P2P p2p.dependency_type = TYPE_C p2p.variable_out = OBJECTS p2p.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p.commands += -Fo${QMAKE_FILE_OUT} } else { p2p.commands += -o ${QMAKE_FILE_OUT} p2p.commands += -std=c++14 p2p.commands += -Wno-missing-field-initializers } macx { p2p.commands += -Wno-gnu } QMAKE_EXTRA_COMPILERS += p2p if($$ARCHITECTURE_64_BIT) { p2p_sse41.name = p2p_sse41 p2p_sse41.input = SOURCES_P2P_SSE41 p2p_sse41.dependency_type = TYPE_C p2p_sse41.variable_out = OBJECTS p2p_sse41.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p_sse41.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p_sse41.commands += -Fo${QMAKE_FILE_OUT} } else { p2p_sse41.commands += -msse4.1 p2p_sse41.commands += -o ${QMAKE_FILE_OUT} p2p_sse41.commands += -std=c++14 p2p_sse41.commands += -Wno-missing-field-initializers } } macx { p2p_sse41.commands += -Wno-gnu } if($$ARCHITECTURE_64_BIT) { QMAKE_EXTRA_COMPILERS += p2p_sse41 } include($${COMMON_DIRECTORY}/pro/local_quirks.pri) ================================================ FILE: pro/vsedit-job-server/vsedit-job-server.pro ================================================ CONFIG += qt QT += websockets QT += widgets win32 { CONFIG += console } HOST_64_BIT = contains(QMAKE_HOST.arch, "x86_64") TARGET_64_BIT = contains(QMAKE_TARGET.arch, "x86_64") ARCHITECTURE_64_BIT = $$HOST_64_BIT | $$TARGET_64_BIT PROJECT_DIRECTORY = ../../vsedit-job-server COMMON_DIRECTORY = ../.. INCLUDEPATH += $$(VS_INCLUDE_PATH) TARGET = vsedit-job-server CONFIG(debug, debug|release) { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-gcc } QMAKE_CXXFLAGS += -O0 QMAKE_CXXFLAGS += -g QMAKE_CXXFLAGS += -ggdb3 } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-msvc } } } else { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-gcc } QMAKE_CXXFLAGS += -O2 QMAKE_CXXFLAGS += -fexpensive-optimizations QMAKE_CXXFLAGS += -funit-at-a-time } macx { QMAKE_CXXFLAGS -= -fexpensive-optimizations } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-msvc } } DEFINES += NDEBUG } macx { INCLUDEPATH += /usr/local/include } E = $$escape_expand(\n\t) win32 { INCLUDEPATH += 'C:/Program Files/VapourSynth/sdk/include/vapoursynth' DEPLOY_COMMAND = windeployqt DEPLOY_TARGET = $$shell_quote($$shell_path($${DESTDIR}/$${TARGET}.exe)) QMAKE_POST_LINK += $${DEPLOY_COMMAND} --no-translations --no-svg --no-opengl-sw --no-system-d3d-compiler $${DEPLOY_TARGET} $${E} if($$ARCHITECTURE_64_BIT) { message("x86_64 build") } else { message("x86 build") contains(QMAKE_COMPILER, gcc) { QMAKE_LFLAGS += -Wl,--large-address-aware } contains(QMAKE_COMPILER, msvc) { QMAKE_LFLAGS += /LARGEADDRESSAWARE } } } contains(QMAKE_COMPILER, clang) { QMAKE_CXXFLAGS += -stdlib=libc++ } contains(QMAKE_COMPILER, gcc) { QMAKE_CXXFLAGS += -std=c++17 QMAKE_CXXFLAGS += -Wall QMAKE_CXXFLAGS += -Wextra QMAKE_CXXFLAGS += -Wredundant-decls QMAKE_CXXFLAGS += -Wshadow #QMAKE_CXXFLAGS += -Weffc++ QMAKE_CXXFLAGS += -pedantic LIBS += -L$$[QT_INSTALL_LIBS] } else { CONFIG += c++17 } TEMPLATE = app include($${COMMON_DIRECTORY}/pro/common.pri) QMAKE_TARGET_PRODUCT = 'VapourSynth Editor Job Server' QMAKE_TARGET_DESCRIPTION = 'VapourSynth Editor Job Server' #SUBDIRS MOC_DIR = $${PROJECT_DIRECTORY}/generated/moc RCC_DIR = $${PROJECT_DIRECTORY}/generated/rcc #DEFINES #TRANSLATIONS #RESOURCES = $${COMMON_DIRECTORY}/resources/vsedit-job-server.qrc HEADERS += $${COMMON_DIRECTORY}/common-src/helpers.h HEADERS += $${COMMON_DIRECTORY}/common-src/helpers_vs.h HEADERS += $${COMMON_DIRECTORY}/common-src/version_info.h HEADERS += $${COMMON_DIRECTORY}/common-src/chrono.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_null.h HEADERS += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_y4m.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.h HEADERS += $${COMMON_DIRECTORY}/common-src/application_instance_file_guard/application_instance_file_guard.h HEADERS += $${COMMON_DIRECTORY}/common-src/ipc_defines.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.h HEADERS += $${PROJECT_DIRECTORY}/src/jobs/job_definitions.h HEADERS += $${PROJECT_DIRECTORY}/src/jobs/jobs_manager.h HEADERS += $${PROJECT_DIRECTORY}/src/job_server.h SOURCES += $${COMMON_DIRECTORY}/common-src/helpers.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/version_info.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_null.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/frame_header_writers/frame_header_writer_y4m.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/application_instance_file_guard/application_instance_file_guard.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.cpp SOURCES += $${PROJECT_DIRECTORY}/src/jobs/jobs_manager.cpp SOURCES += $${PROJECT_DIRECTORY}/src/job_server.cpp SOURCES += $${PROJECT_DIRECTORY}/src/main.cpp # libp2p SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/v210.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.cpp if($$ARCHITECTURE_64_BIT) { SOURCES_P2P_SSE41 += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_sse41.cpp } p2p.name = p2p p2p.input = SOURCES_P2P p2p.dependency_type = TYPE_C p2p.variable_out = OBJECTS p2p.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p.commands += -Fo${QMAKE_FILE_OUT} } else { p2p.commands += -o ${QMAKE_FILE_OUT} p2p.commands += -std=c++14 p2p.commands += -Wno-missing-field-initializers } macx { p2p.commands += -Wno-gnu } QMAKE_EXTRA_COMPILERS += p2p if($$ARCHITECTURE_64_BIT) { p2p_sse41.name = p2p_sse41 p2p_sse41.input = SOURCES_P2P_SSE41 p2p_sse41.dependency_type = TYPE_C p2p_sse41.variable_out = OBJECTS p2p_sse41.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p_sse41.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p_sse41.commands += -Fo${QMAKE_FILE_OUT} } else { p2p_sse41.commands += -msse4.1 p2p_sse41.commands += -o ${QMAKE_FILE_OUT} p2p_sse41.commands += -std=c++14 p2p_sse41.commands += -Wno-missing-field-initializers } } macx { p2p_sse41.commands += -Wno-gnu } if($$ARCHITECTURE_64_BIT) { QMAKE_EXTRA_COMPILERS += p2p_sse41 } include($${COMMON_DIRECTORY}/pro/local_quirks.pri) ================================================ FILE: pro/vsedit-job-server-watcher/vsedit-job-server-watcher.pro ================================================ CONFIG += qt QT += widgets QT += websockets HOST_64_BIT = contains(QMAKE_HOST.arch, "x86_64") TARGET_64_BIT = contains(QMAKE_TARGET.arch, "x86_64") ARCHITECTURE_64_BIT = $$HOST_64_BIT | $$TARGET_64_BIT PROJECT_DIRECTORY = ../../vsedit-job-server-watcher COMMON_DIRECTORY = ../.. INCLUDEPATH += $$(VS_INCLUDE_PATH) TARGET = vsedit-job-server-watcher CONFIG(debug, debug|release) { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-gcc } QMAKE_CXXFLAGS += -O0 QMAKE_CXXFLAGS += -g QMAKE_CXXFLAGS += -ggdb3 } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-msvc } } } else { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-gcc } QMAKE_CXXFLAGS += -O2 QMAKE_CXXFLAGS += -fexpensive-optimizations QMAKE_CXXFLAGS += -funit-at-a-time } macx { QMAKE_CXXFLAGS -= -fexpensive-optimizations } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-msvc } } DEFINES += NDEBUG } E = $$escape_expand(\n\t) macx { INCLUDEPATH += /usr/local/include ICON = $${COMMON_DIRECTORY}/resources/vsedit.icns } win32 { QMAKE_LFLAGS += '/entry:mainCRTStartup' INCLUDEPATH += 'C:/Program Files/VapourSynth/sdk/include/vapoursynth' DEPLOY_COMMAND = windeployqt DEPLOY_TARGET = $$shell_quote($$shell_path($${DESTDIR}/$${TARGET}.exe)) QMAKE_POST_LINK += $${DEPLOY_COMMAND} --no-translations --no-svg --no-opengl-sw --no-system-d3d-compiler $${DEPLOY_TARGET} $${E} if($$ARCHITECTURE_64_BIT) { message("x86_64 build") } else { message("x86 build") contains(QMAKE_COMPILER, gcc) { QMAKE_LFLAGS += -Wl,--large-address-aware } contains(QMAKE_COMPILER, msvc) { QMAKE_LFLAGS += /LARGEADDRESSAWARE } } } contains(QMAKE_COMPILER, clang) { QMAKE_CXXFLAGS += -stdlib=libc++ } contains(QMAKE_COMPILER, gcc) { QMAKE_CXXFLAGS += -std=c++17 QMAKE_CXXFLAGS += -Wall QMAKE_CXXFLAGS += -Wextra QMAKE_CXXFLAGS += -Wredundant-decls QMAKE_CXXFLAGS += -Wshadow #QMAKE_CXXFLAGS += -Weffc++ QMAKE_CXXFLAGS += -pedantic LIBS += -L$$[QT_INSTALL_LIBS] } else { CONFIG += c++17 } include($${COMMON_DIRECTORY}/pro/common.pri) TEMPLATE = app RC_ICONS = $${COMMON_DIRECTORY}/resources/vsedit.ico QMAKE_TARGET_PRODUCT = 'VapourSynth Editor Job Server Watcher' QMAKE_TARGET_DESCRIPTION = 'VapourSynth Editor Job Server Watcher' #SUBDIRS MOC_DIR = $${PROJECT_DIRECTORY}/generated/moc UI_DIR = $${PROJECT_DIRECTORY}/generated/ui RCC_DIR = $${PROJECT_DIRECTORY}/generated/rcc #DEFINES #TRANSLATIONS RESOURCES = $${COMMON_DIRECTORY}/resources/vsedit-job-server-watcher.qrc RESOURCES += $${COMMON_DIRECTORY}/resources/dark/style.qrc FORMS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/jobs/job_edit_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/connect_to_server_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/trusted_clients_addresses_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/main_window.ui HEADERS += $${COMMON_DIRECTORY}/common-src/application_instance_file_guard/application_instance_file_guard.h HEADERS += $${COMMON_DIRECTORY}/common-src/helpers.h HEADERS += $${COMMON_DIRECTORY}/common-src/helpers_vs.h HEADERS += $${COMMON_DIRECTORY}/common-src/version_info.h HEADERS += $${COMMON_DIRECTORY}/common-src/ipc_defines.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/log_styles_model.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.h HEADERS += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.h HEADERS += $${PROJECT_DIRECTORY}/src/jobs/jobs_model.h HEADERS += $${PROJECT_DIRECTORY}/src/jobs/job_edit_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/jobs/job_dependencies_delegate.h HEADERS += $${PROJECT_DIRECTORY}/src/jobs/job_state_delegate.h HEADERS += $${PROJECT_DIRECTORY}/src/connect_to_server_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/trusted_clients_addresses_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/main_window.h SOURCES += $${COMMON_DIRECTORY}/common-src/helpers.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/version_info.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/application_instance_file_guard/application_instance_file_guard.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/log_styles_model.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_settings_dialog.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/jobs/job_variables.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.cpp SOURCES += $${PROJECT_DIRECTORY}/src/jobs/jobs_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/jobs/job_edit_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/jobs/job_dependencies_delegate.cpp SOURCES += $${PROJECT_DIRECTORY}/src/jobs/job_state_delegate.cpp SOURCES += $${PROJECT_DIRECTORY}/src/connect_to_server_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/trusted_clients_addresses_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/main_window.cpp SOURCES += $${PROJECT_DIRECTORY}/src/main.cpp # libp2p SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/v210.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.cpp if($$ARCHITECTURE_64_BIT) { SOURCES_P2P_SSE41 += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_sse41.cpp } p2p.name = p2p p2p.input = SOURCES_P2P p2p.dependency_type = TYPE_C p2p.variable_out = OBJECTS p2p.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p.commands += -Fo${QMAKE_FILE_OUT} } else { p2p.commands += -o ${QMAKE_FILE_OUT} p2p.commands += -std=c++14 p2p.commands += -Wno-missing-field-initializers } macx { p2p.commands += -Wno-gnu } QMAKE_EXTRA_COMPILERS += p2p if($$ARCHITECTURE_64_BIT) { p2p_sse41.name = p2p_sse41 p2p_sse41.input = SOURCES_P2P_SSE41 p2p_sse41.dependency_type = TYPE_C p2p_sse41.variable_out = OBJECTS p2p_sse41.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p_sse41.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p_sse41.commands += -Fo${QMAKE_FILE_OUT} } else { p2p_sse41.commands += -msse4.1 p2p_sse41.commands += -o ${QMAKE_FILE_OUT} p2p_sse41.commands += -std=c++14 p2p_sse41.commands += -Wno-missing-field-initializers } } macx { p2p_sse41.commands += -Wno-gnu } if($$ARCHITECTURE_64_BIT) { QMAKE_EXTRA_COMPILERS += p2p_sse41 } include($${COMMON_DIRECTORY}/pro/local_quirks.pri) ================================================ FILE: pro/vsedit-previewer/vsedit-previewer.pro ================================================ CONFIG += qt QT += widgets win32 { QT += multimedia CONFIG += console } HOST_64_BIT = contains(QMAKE_HOST.arch, "x86_64") TARGET_64_BIT = contains(QMAKE_TARGET.arch, "x86_64") ARCHITECTURE_64_BIT = $$HOST_64_BIT | $$TARGET_64_BIT PROJECT_DIRECTORY = ../../vsedit COMMON_DIRECTORY = ../.. INCLUDEPATH += $$(VS_INCLUDE_PATH) CONFIG(debug, debug|release) { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-gcc TARGET = vsedit-previewer-debug-64bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-gcc TARGET = vsedit-previewer-debug-32bit-gcc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-gcc } QMAKE_CXXFLAGS += -O0 QMAKE_CXXFLAGS += -g QMAKE_CXXFLAGS += -ggdb3 } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/debug-64bit-msvc TARGET = vsedit-previewer-debug-64bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/debug-32bit-msvc TARGET = vsedit-previewer-debug-32bit-msvc OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-debug-32bit-msvc } } } else { contains(QMAKE_COMPILER, gcc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-gcc TARGET = vsedit-previewer OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-gcc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-gcc TARGET = vsedit-previewer-32bit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-gcc } QMAKE_CXXFLAGS += -O2 QMAKE_CXXFLAGS += -fexpensive-optimizations QMAKE_CXXFLAGS += -funit-at-a-time } macx { QMAKE_CXXFLAGS -= -fexpensive-optimizations } contains(QMAKE_COMPILER, msvc) { if($$ARCHITECTURE_64_BIT) { DESTDIR = $${COMMON_DIRECTORY}/build/release-64bit-msvc TARGET = vsedit-previewer OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-64bit-msvc } else { DESTDIR = $${COMMON_DIRECTORY}/build/release-32bit-msvc TARGET = vsedit-previewer-32bit OBJECTS_DIR = $${PROJECT_DIRECTORY}/generated/obj-release-32bit-msvc } } DEFINES += NDEBUG } S = $${DIR_SEPARATOR} D = $$DESTDIR D = $$replace(D, /, $$S) SC = $${COMMON_DIRECTORY}/ SC = $$replace(SC, /, $$S) E = $$escape_expand(\n\t) QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}resources$${S}vsedit.ico $${D}$${S}vsedit.ico $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}resources$${S}vsedit.svg $${D}$${S}vsedit.svg $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}README $${D}$${S}README $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}LICENSE $${D}$${S}LICENSE $${E} QMAKE_POST_LINK += $${QMAKE_COPY} $${SC}$${S}CHANGELOG $${D}$${S}CHANGELOG $${E} macx { INCLUDEPATH += /usr/local/include ICON = $${COMMON_DIRECTORY}/resources/vsedit.icns } win32 { INCLUDEPATH += 'C:/Program Files/VapourSynth/sdk/include/vapoursynth' DEPLOY_COMMAND = windeployqt DEPLOY_TARGET = $$shell_quote($$shell_path($${D}/$${TARGET}.exe)) QMAKE_POST_LINK += $${DEPLOY_COMMAND} --no-translations --no-svg --no-opengl-sw --no-system-d3d-compiler $${DEPLOY_TARGET} $${E} if($$ARCHITECTURE_64_BIT) { message("x86_64 build") } else { message("x86 build") contains(QMAKE_COMPILER, gcc) { QMAKE_LFLAGS += -Wl,--large-address-aware } contains(QMAKE_COMPILER, msvc) { QMAKE_LFLAGS += /LARGEADDRESSAWARE } } } contains(QMAKE_COMPILER, clang) { QMAKE_CXXFLAGS += -stdlib=libc++ } contains(QMAKE_COMPILER, gcc) { QMAKE_CXXFLAGS += -std=c++17 QMAKE_CXXFLAGS += -Wall QMAKE_CXXFLAGS += -Wextra QMAKE_CXXFLAGS += -Wredundant-decls QMAKE_CXXFLAGS += -Wshadow #QMAKE_CXXFLAGS += -Weffc++ QMAKE_CXXFLAGS += -pedantic LIBS += -L$$[QT_INSTALL_LIBS] } else { CONFIG += c++17 } include($${COMMON_DIRECTORY}/pro/common.pri) TEMPLATE = app RC_ICONS = $${COMMON_DIRECTORY}/resources/vsedit.ico QMAKE_TARGET_PRODUCT = 'VapourSynth Editor Previewer' QMAKE_TARGET_DESCRIPTION = 'VapourSynth Editor Previewer' #SUBDIRS MOC_DIR = $${PROJECT_DIRECTORY}/generated/moc UI_DIR = $${PROJECT_DIRECTORY}/generated/ui RCC_DIR = $${PROJECT_DIRECTORY}/generated/rcc #DEFINES #TRANSLATIONS RESOURCES = $${COMMON_DIRECTORY}/resources/vsedit.qrc RESOURCES += $${COMMON_DIRECTORY}/resources/dark/style.qrc FORMS += $${PROJECT_DIRECTORY}/src/settings/settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.ui FORMS += $${PROJECT_DIRECTORY}/src/preview/preview_advanced_settings_dialog.ui FORMS += $${PROJECT_DIRECTORY}/src/preview/preview_dialog.ui HEADERS += $${COMMON_DIRECTORY}/common-src/helpers.h HEADERS += $${COMMON_DIRECTORY}/common-src/helpers_vs.h HEADERS += $${COMMON_DIRECTORY}/common-src/version_info.h HEADERS += $${COMMON_DIRECTORY}/common-src/chrono.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.h HEADERS += $${COMMON_DIRECTORY}/common-src/timeline_slider/timeline_slider.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.h HEADERS += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.h HEADERS += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/actions_hotkey_edit_model.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/clearable_key_sequence_editor.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/item_delegate_for_hotkey.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/theme_elements_model.h HEADERS += $${PROJECT_DIRECTORY}/src/settings/settings_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/scroll_navigator.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/preview_area.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/preview_advanced_settings_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/preview_dialog.h HEADERS += $${PROJECT_DIRECTORY}/src/preview/zoom_ratio_spinbox.h HEADERS += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_script_processor_dialog.h SOURCES += $${COMMON_DIRECTORY}/common-src/helpers.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/version_info.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/settings/settings_manager.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_core.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/styled_log_view_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/log/vs_editor_log_definitions.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_library.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_script_processor_structures.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vapoursynth_script_processor.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/timeline_slider/timeline_slider.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_pack_rgb.cpp SOURCES += $${COMMON_DIRECTORY}/common-src/vapoursynth/vs_set_matrix.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/actions_hotkey_edit_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/clearable_key_sequence_editor.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/item_delegate_for_hotkey.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/theme_elements_model.cpp SOURCES += $${PROJECT_DIRECTORY}/src/settings/settings_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/script_status_bar_widget/script_status_bar_widget.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/scroll_navigator.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/preview_area.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/preview_advanced_settings_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/preview_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/preview/zoom_ratio_spinbox.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vapoursynth/vs_script_processor_dialog.cpp SOURCES += $${PROJECT_DIRECTORY}/src/vsedit_previewer_main.cpp # libp2p SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/p2p_api.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/v210.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/cpuinfo_x86.cpp SOURCES_P2P += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_simd.cpp if($$ARCHITECTURE_64_BIT) { SOURCES_P2P_SSE41 += $${COMMON_DIRECTORY}/common-src/libp2p/simd/p2p_sse41.cpp } p2p.name = p2p p2p.input = SOURCES_P2P p2p.dependency_type = TYPE_C p2p.variable_out = OBJECTS p2p.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p.commands += -Fo${QMAKE_FILE_OUT} } else { p2p.commands += -o ${QMAKE_FILE_OUT} p2p.commands += -std=c++14 p2p.commands += -Wno-missing-field-initializers } macx { p2p.commands += -Wno-gnu } QMAKE_EXTRA_COMPILERS += p2p if($$ARCHITECTURE_64_BIT) { p2p_sse41.name = p2p_sse41 p2p_sse41.input = SOURCES_P2P_SSE41 p2p_sse41.dependency_type = TYPE_C p2p_sse41.variable_out = OBJECTS p2p_sse41.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${first(QMAKE_EXT_OBJ)} p2p_sse41.commands = $${QMAKE_CXX} $(CXXFLAGS) -DP2P_SIMD $(INCPATH) -c ${QMAKE_FILE_IN} contains(QMAKE_COMPILER, msvc) { p2p_sse41.commands += -Fo${QMAKE_FILE_OUT} } else { p2p_sse41.commands += -msse4.1 p2p_sse41.commands += -o ${QMAKE_FILE_OUT} p2p_sse41.commands += -std=c++14 p2p_sse41.commands += -Wno-missing-field-initializers } } macx { p2p_sse41.commands += -Wno-gnu } if($$ARCHITECTURE_64_BIT) { QMAKE_EXTRA_COMPILERS += p2p_sse41 } include($${COMMON_DIRECTORY}/pro/local_quirks.pri) ================================================ FILE: resources/dark/LICENSE.rst ================================================ License ======= The MIT License (MIT) - Code ---------------------------- Copyright (c) 2013-2019 Colin Duquesnoy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Creative Commons Attribution International 4.0 - Images ------------------------------------------------------- QDarkStyle (c) 2013-2019 Colin Duquesnoy QDarkStyle (c) 2019-2019 Daniel Cosmo Pizetta Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. - **Considerations for licensors:** Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. `More considerations for licensors `__. - **Considerations for the public:** By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. `More considerations for the public `__. Creative Commons Attribution 4.0 International Public License ------------------------------------------------------------- By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 – Definitions ~~~~~~~~~~~~~~~~~~~~~~~ a. **Adapted Material** means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. **Adapter's License** means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. **Copyright and Similar Rights** means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. **Effective Technological Measures** means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. **Exceptions and Limitations** means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. **Licensed Material** means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. **Licensed Rights** means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. **Licensor** means the individual(s) or entity(ies) granting rights under this Public License. i. **Share** means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. **Sui Generis Database Rights** means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. **You** means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 – Scope ~~~~~~~~~~~~~~~~~ a. **License grant.** 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: A. reproduce and Share the Licensed Material, in whole or in part; and B. produce, reproduce, and Share Adapted Material. 2. **Exceptions and Limitations.** For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. **Term.** The term of this Public License is specified in Section 6(a). 4. **Media and formats; technical modifications allowed.** The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 5. **Downstream recipients.** A. **Offer from the Licensor – Licensed Material.** Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. B. **No downstream restrictions.** You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. **No endorsement.** Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. **Other rights.** 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 – License Conditions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. **Attribution.** 1. If You Share the Licensed Material (including in modified form), You must: A. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 – Sui Generis Database Rights ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 – Disclaimer of Warranties and Limitation of Liability ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 – Term and Termination ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 – Other Terms and Conditions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 – Interpretation ~~~~~~~~~~~~~~~~~~~~~~~~~~ a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at `creativecommons.org/policies `__, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org ================================================ FILE: resources/dark/rc/.keep ================================================ ================================================ FILE: resources/dark/style.qrc ================================================ rc/.keep rc/arrow_down.png rc/arrow_down@2x.png rc/arrow_down_disabled.png rc/arrow_down_disabled@2x.png rc/arrow_down_focus.png rc/arrow_down_focus@2x.png rc/arrow_down_pressed.png rc/arrow_down_pressed@2x.png rc/arrow_left.png rc/arrow_left@2x.png rc/arrow_left_disabled.png rc/arrow_left_disabled@2x.png rc/arrow_left_focus.png rc/arrow_left_focus@2x.png rc/arrow_left_pressed.png rc/arrow_left_pressed@2x.png rc/arrow_right.png rc/arrow_right@2x.png rc/arrow_right_disabled.png rc/arrow_right_disabled@2x.png rc/arrow_right_focus.png rc/arrow_right_focus@2x.png rc/arrow_right_pressed.png rc/arrow_right_pressed@2x.png rc/arrow_up.png rc/arrow_up@2x.png rc/arrow_up_disabled.png rc/arrow_up_disabled@2x.png rc/arrow_up_focus.png rc/arrow_up_focus@2x.png rc/arrow_up_pressed.png rc/arrow_up_pressed@2x.png rc/base_icon.png rc/base_icon@2x.png rc/base_icon_disabled.png rc/base_icon_disabled@2x.png rc/base_icon_focus.png rc/base_icon_focus@2x.png rc/base_icon_pressed.png rc/base_icon_pressed@2x.png rc/branch_closed.png rc/branch_closed@2x.png rc/branch_closed_disabled.png rc/branch_closed_disabled@2x.png rc/branch_closed_focus.png rc/branch_closed_focus@2x.png rc/branch_closed_pressed.png rc/branch_closed_pressed@2x.png rc/branch_end.png rc/branch_end@2x.png rc/branch_end_disabled.png rc/branch_end_disabled@2x.png rc/branch_end_focus.png rc/branch_end_focus@2x.png rc/branch_end_pressed.png rc/branch_end_pressed@2x.png rc/branch_line.png rc/branch_line@2x.png rc/branch_line_disabled.png rc/branch_line_disabled@2x.png rc/branch_line_focus.png rc/branch_line_focus@2x.png rc/branch_line_pressed.png rc/branch_line_pressed@2x.png rc/branch_more.png rc/branch_more@2x.png rc/branch_more_disabled.png rc/branch_more_disabled@2x.png rc/branch_more_focus.png rc/branch_more_focus@2x.png rc/branch_more_pressed.png rc/branch_more_pressed@2x.png rc/branch_open.png rc/branch_open@2x.png rc/branch_open_disabled.png rc/branch_open_disabled@2x.png rc/branch_open_focus.png rc/branch_open_focus@2x.png rc/branch_open_pressed.png rc/branch_open_pressed@2x.png rc/checkbox_checked.png rc/checkbox_checked@2x.png rc/checkbox_checked_disabled.png rc/checkbox_checked_disabled@2x.png rc/checkbox_checked_focus.png rc/checkbox_checked_focus@2x.png rc/checkbox_checked_pressed.png rc/checkbox_checked_pressed@2x.png rc/checkbox_indeterminate.png rc/checkbox_indeterminate@2x.png rc/checkbox_indeterminate_disabled.png rc/checkbox_indeterminate_disabled@2x.png rc/checkbox_indeterminate_focus.png rc/checkbox_indeterminate_focus@2x.png rc/checkbox_indeterminate_pressed.png rc/checkbox_indeterminate_pressed@2x.png rc/checkbox_unchecked.png rc/checkbox_unchecked@2x.png rc/checkbox_unchecked_disabled.png rc/checkbox_unchecked_disabled@2x.png rc/checkbox_unchecked_focus.png rc/checkbox_unchecked_focus@2x.png rc/checkbox_unchecked_pressed.png rc/checkbox_unchecked_pressed@2x.png rc/line_horizontal.png rc/line_horizontal@2x.png rc/line_horizontal_disabled.png rc/line_horizontal_disabled@2x.png rc/line_horizontal_focus.png rc/line_horizontal_focus@2x.png rc/line_horizontal_pressed.png rc/line_horizontal_pressed@2x.png rc/line_vertical.png rc/line_vertical@2x.png rc/line_vertical_disabled.png rc/line_vertical_disabled@2x.png rc/line_vertical_focus.png rc/line_vertical_focus@2x.png rc/line_vertical_pressed.png rc/line_vertical_pressed@2x.png rc/radio_checked.png rc/radio_checked@2x.png rc/radio_checked_disabled.png rc/radio_checked_disabled@2x.png rc/radio_checked_focus.png rc/radio_checked_focus@2x.png rc/radio_checked_pressed.png rc/radio_checked_pressed@2x.png rc/radio_unchecked.png rc/radio_unchecked@2x.png rc/radio_unchecked_disabled.png rc/radio_unchecked_disabled@2x.png rc/radio_unchecked_focus.png rc/radio_unchecked_focus@2x.png rc/radio_unchecked_pressed.png rc/radio_unchecked_pressed@2x.png rc/toolbar_move_horizontal.png rc/toolbar_move_horizontal@2x.png rc/toolbar_move_horizontal_disabled.png rc/toolbar_move_horizontal_disabled@2x.png rc/toolbar_move_horizontal_focus.png rc/toolbar_move_horizontal_focus@2x.png rc/toolbar_move_horizontal_pressed.png rc/toolbar_move_horizontal_pressed@2x.png rc/toolbar_move_vertical.png rc/toolbar_move_vertical@2x.png rc/toolbar_move_vertical_disabled.png rc/toolbar_move_vertical_disabled@2x.png rc/toolbar_move_vertical_focus.png rc/toolbar_move_vertical_focus@2x.png rc/toolbar_move_vertical_pressed.png rc/toolbar_move_vertical_pressed@2x.png rc/toolbar_separator_horizontal.png rc/toolbar_separator_horizontal@2x.png rc/toolbar_separator_horizontal_disabled.png rc/toolbar_separator_horizontal_disabled@2x.png rc/toolbar_separator_horizontal_focus.png rc/toolbar_separator_horizontal_focus@2x.png rc/toolbar_separator_horizontal_pressed.png rc/toolbar_separator_horizontal_pressed@2x.png rc/toolbar_separator_vertical.png rc/toolbar_separator_vertical@2x.png rc/toolbar_separator_vertical_disabled.png rc/toolbar_separator_vertical_disabled@2x.png rc/toolbar_separator_vertical_focus.png rc/toolbar_separator_vertical_focus@2x.png rc/toolbar_separator_vertical_pressed.png rc/toolbar_separator_vertical_pressed@2x.png rc/transparent.png rc/transparent@2x.png rc/transparent_disabled.png rc/transparent_disabled@2x.png rc/transparent_focus.png rc/transparent_focus@2x.png rc/transparent_pressed.png rc/transparent_pressed@2x.png rc/window_close.png rc/window_close@2x.png rc/window_close_disabled.png rc/window_close_disabled@2x.png rc/window_close_focus.png rc/window_close_focus@2x.png rc/window_close_pressed.png rc/window_close_pressed@2x.png rc/window_grip.png rc/window_grip@2x.png rc/window_grip_disabled.png rc/window_grip_disabled@2x.png rc/window_grip_focus.png rc/window_grip_focus@2x.png rc/window_grip_pressed.png rc/window_grip_pressed@2x.png rc/window_minimize.png rc/window_minimize@2x.png rc/window_minimize_disabled.png rc/window_minimize_disabled@2x.png rc/window_minimize_focus.png rc/window_minimize_focus@2x.png rc/window_minimize_pressed.png rc/window_minimize_pressed@2x.png rc/window_undock.png rc/window_undock@2x.png rc/window_undock_disabled.png rc/window_undock_disabled@2x.png rc/window_undock_focus.png rc/window_undock_focus@2x.png rc/window_undock_pressed.png rc/window_undock_pressed@2x.png style.qss ================================================ FILE: resources/dark/style.qss ================================================ /* Modified for VapourSynth Editor */ /* --------------------------------------------------------------------------- WARNING! File created programmatically. All changes made in this file will be lost! Created by the qtsass compiler v0.3.0 The definitions are in the "qdarkstyle.qss._styles.scss" module --------------------------------------------------------------------------- */ /* Light Style - QDarkStyleSheet ------------------------------------------ */ /* See Qt documentation: - https://doc.qt.io/qt-5/stylesheet.html - https://doc.qt.io/qt-5/stylesheet-reference.html - https://doc.qt.io/qt-5/stylesheet-examples.html --------------------------------------------------------------------------- */ /* Reset elements ------------------------------------------------------------ Resetting everything helps to unify styles across different operating systems --------------------------------------------------------------------------- */ * { padding: 0px; margin: 0px; border: 0px; border-style: none; border-image: none; outline: 0; } /* specific reset for elements inside QToolBar */ QToolBar * { margin: 0px; padding: 0px; } /* QWidget ---------------------------------------------------------------- --------------------------------------------------------------------------- */ QWidget { background-color: #19232D; border: 0px solid #455364; padding: 0px; color: #E0E1E3; selection-background-color: #346792; selection-color: #E0E1E3; } QWidget:disabled { background-color: #19232D; color: #9DA9B5; selection-background-color: #26486B; selection-color: #9DA9B5; } QWidget::item:selected { background-color: #346792; } QWidget::item:hover:!selected { background-color: #1A72BB; } /* QMainWindow ------------------------------------------------------------ This adjusts the splitter in the dock widget, not qsplitter https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmainwindow --------------------------------------------------------------------------- */ QMainWindow::separator { background-color: #455364; border: 0px solid #19232D; spacing: 0px; padding: 2px; } QMainWindow::separator:hover { background-color: #60798B; border: 0px solid #1A72BB; } QMainWindow::separator:horizontal { width: 5px; margin-top: 2px; margin-bottom: 2px; image: url(":/dark/rc/toolbar_separator_vertical.png"); } QMainWindow::separator:vertical { height: 5px; margin-left: 2px; margin-right: 2px; image: url(":/dark/rc/toolbar_separator_horizontal.png"); } /* QToolTip --------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtooltip --------------------------------------------------------------------------- */ QToolTip { background-color: #346792; color: #E0E1E3; /* If you remove the border property, background stops working on Windows */ border: none; /* Remove padding, for fix combo box tooltip */ padding: 0px; /* Remove opacity, fix #174 - may need to use RGBA */ font-kerning: none; } /* QStatusBar ------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar --------------------------------------------------------------------------- */ QStatusBar { border: 1px solid #455364; /* Fixes Spyder #9120, #9121 */ background: #455364; /* Fixes #205, white vertical borders separating items */ } QStatusBar::item { border: none; } QStatusBar QToolTip { background-color: #1A72BB; border: 1px solid #19232D; color: #19232D; /* Remove padding, for fix combo box tooltip */ padding: 0px; /* Reducing transparency to read better */ opacity: 230; } QStatusBar QLabel { /* Fixes Spyder #9120, #9121 */ background: transparent; } /* QCheckBox -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcheckbox --------------------------------------------------------------------------- */ QCheckBox { background-color: #19232D; color: #E0E1E3; spacing: 4px; outline: none; padding-top: 4px; padding-bottom: 4px; } QCheckBox:focus { border: none; } QCheckBox QWidget:disabled { background-color: #19232D; color: #9DA9B5; } QCheckBox::indicator { margin-left: 2px; height: 14px; width: 14px; } QCheckBox::indicator:unchecked { image: url(":/dark/rc/checkbox_unchecked.png"); } QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus, QCheckBox::indicator:unchecked:pressed { border: none; image: url(":/dark/rc/checkbox_unchecked_focus.png"); } QCheckBox::indicator:unchecked:disabled { image: url(":/dark/rc/checkbox_unchecked_disabled.png"); } QCheckBox::indicator:checked { image: url(":/dark/rc/checkbox_checked.png"); } QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus, QCheckBox::indicator:checked:pressed { border: none; image: url(":/dark/rc/checkbox_checked_focus.png"); } QCheckBox::indicator:checked:disabled { image: url(":/dark/rc/checkbox_checked_disabled.png"); } QCheckBox::indicator:indeterminate { image: url(":/dark/rc/checkbox_indeterminate.png"); } QCheckBox::indicator:indeterminate:disabled { image: url(":/dark/rc/checkbox_indeterminate_disabled.png"); } QCheckBox::indicator:indeterminate:focus, QCheckBox::indicator:indeterminate:hover, QCheckBox::indicator:indeterminate:pressed { image: url(":/dark/rc/checkbox_indeterminate_focus.png"); } /* QGroupBox -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox --------------------------------------------------------------------------- */ QGroupBox { font-weight: bold; border: 1px solid #455364; border-radius: 4px; padding: 2px; margin-top: 16px; /* was 6px */ margin-bottom: 4px; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left: 4px; top: 2px; /* was not specified */ bottom: 2px; /* was not specified */ padding-left: 2px; padding-right: 4px; padding-top: -4px; } QGroupBox::indicator { margin-left: 2px; margin-top: 2px; padding: 0; height: 14px; width: 14px; } QGroupBox::indicator:unchecked { border: none; image: url(":/dark/rc/checkbox_unchecked.png"); } QGroupBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:focus, QGroupBox::indicator:unchecked:pressed { border: none; image: url(":/dark/rc/checkbox_unchecked_focus.png"); } QGroupBox::indicator:unchecked:disabled { image: url(":/dark/rc/checkbox_unchecked_disabled.png"); } QGroupBox::indicator:checked { border: none; image: url(":/dark/rc/checkbox_checked.png"); } QGroupBox::indicator:checked:hover, QGroupBox::indicator:checked:focus, QGroupBox::indicator:checked:pressed { border: none; image: url(":/dark/rc/checkbox_checked_focus.png"); } QGroupBox::indicator:checked:disabled { image: url(":/dark/rc/checkbox_checked_disabled.png"); } /* QRadioButton ----------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qradiobutton --------------------------------------------------------------------------- */ QRadioButton { background-color: #19232D; color: #E0E1E3; spacing: 4px; padding-top: 4px; padding-bottom: 4px; border: none; outline: none; } QRadioButton:focus { border: none; } QRadioButton:disabled { background-color: #19232D; color: #9DA9B5; border: none; outline: none; } QRadioButton QWidget { background-color: #19232D; color: #E0E1E3; spacing: 0px; padding: 0px; outline: none; border: none; } QRadioButton::indicator { border: none; outline: none; margin-left: 2px; height: 14px; width: 14px; } QRadioButton::indicator:unchecked { image: url(":/dark/rc/radio_unchecked.png"); } QRadioButton::indicator:unchecked:hover, QRadioButton::indicator:unchecked:focus, QRadioButton::indicator:unchecked:pressed { border: none; outline: none; image: url(":/dark/rc/radio_unchecked_focus.png"); } QRadioButton::indicator:unchecked:disabled { image: url(":/dark/rc/radio_unchecked_disabled.png"); } QRadioButton::indicator:checked { border: none; outline: none; image: url(":/dark/rc/radio_checked.png"); } QRadioButton::indicator:checked:hover, QRadioButton::indicator:checked:focus, QRadioButton::indicator:checked:pressed { border: none; outline: none; image: url(":/dark/rc/radio_checked_focus.png"); } QRadioButton::indicator:checked:disabled { outline: none; image: url(":/dark/rc/radio_checked_disabled.png"); } /* QMenuBar --------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar --------------------------------------------------------------------------- */ QMenuBar { background-color: #455364; padding: 2px; border: 1px solid #19232D; color: #E0E1E3; selection-background-color: #1A72BB; } QMenuBar:focus { border: 1px solid #346792; } QMenuBar::item { background: transparent; padding: 4px; } QMenuBar::item:selected { padding: 4px; background: transparent; border: 0px solid #455364; background-color: #1A72BB; } QMenuBar::item:pressed { padding: 4px; border: 0px solid #455364; background-color: #1A72BB; color: #E0E1E3; margin-bottom: 0px; padding-bottom: 0px; } /* QMenu ------------------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu --------------------------------------------------------------------------- */ QMenu { border: 0px solid #455364; color: #E0E1E3; margin: 0px; background-color: #37414F; selection-background-color: #1A72BB; } QMenu::separator { height: 1px; background-color: #60798B; color: #E0E1E3; } QMenu::item { background-color: #37414F; padding: 4px 24px 4px 28px; /* Reserve space for selection border */ border: 1px transparent #455364; } QMenu::item:selected { color: #E0E1E3; background-color: #1A72BB; } QMenu::item:pressed { background-color: #1A72BB; } QMenu::icon { padding-left: 10px; width: 14px; height: 14px; } QMenu::indicator { padding-left: 8px; width: 12px; height: 12px; /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ } QMenu::indicator:non-exclusive:unchecked { image: url(":/dark/rc/checkbox_unchecked.png"); } QMenu::indicator:non-exclusive:unchecked:hover, QMenu::indicator:non-exclusive:unchecked:focus, QMenu::indicator:non-exclusive:unchecked:pressed { border: none; image: url(":/dark/rc/checkbox_unchecked_focus.png"); } QMenu::indicator:non-exclusive:unchecked:disabled { image: url(":/dark/rc/checkbox_unchecked_disabled.png"); } QMenu::indicator:non-exclusive:checked { image: url(":/dark/rc/checkbox_checked.png"); } QMenu::indicator:non-exclusive:checked:hover, QMenu::indicator:non-exclusive:checked:focus, QMenu::indicator:non-exclusive:checked:pressed { border: none; image: url(":/dark/rc/checkbox_checked_focus.png"); } QMenu::indicator:non-exclusive:checked:disabled { image: url(":/dark/rc/checkbox_checked_disabled.png"); } QMenu::indicator:non-exclusive:indeterminate { image: url(":/dark/rc/checkbox_indeterminate.png"); } QMenu::indicator:non-exclusive:indeterminate:disabled { image: url(":/dark/rc/checkbox_indeterminate_disabled.png"); } QMenu::indicator:non-exclusive:indeterminate:focus, QMenu::indicator:non-exclusive:indeterminate:hover, QMenu::indicator:non-exclusive:indeterminate:pressed { image: url(":/dark/rc/checkbox_indeterminate_focus.png"); } QMenu::indicator:exclusive:unchecked { image: url(":/dark/rc/radio_unchecked.png"); } QMenu::indicator:exclusive:unchecked:hover, QMenu::indicator:exclusive:unchecked:focus, QMenu::indicator:exclusive:unchecked:pressed { border: none; outline: none; image: url(":/dark/rc/radio_unchecked_focus.png"); } QMenu::indicator:exclusive:unchecked:disabled { image: url(":/dark/rc/radio_unchecked_disabled.png"); } QMenu::indicator:exclusive:checked { border: none; outline: none; image: url(":/dark/rc/radio_checked.png"); } QMenu::indicator:exclusive:checked:hover, QMenu::indicator:exclusive:checked:focus, QMenu::indicator:exclusive:checked:pressed { border: none; outline: none; image: url(":/dark/rc/radio_checked_focus.png"); } QMenu::indicator:exclusive:checked:disabled { outline: none; image: url(":/dark/rc/radio_checked_disabled.png"); } QMenu::right-arrow { margin: 5px; padding-left: 12px; image: url(":/dark/rc/arrow_right.png"); height: 12px; width: 12px; } /* QAbstractItemView ------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox --------------------------------------------------------------------------- */ QAbstractItemView { alternate-background-color: #19232D; color: #E0E1E3; border: 1px solid #455364; border-radius: 4px; } QAbstractItemView QLineEdit { padding: 2px; } /* QAbstractScrollArea ---------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea --------------------------------------------------------------------------- */ QAbstractScrollArea { background-color: #19232D; border: 1px solid #455364; border-radius: 4px; /* fix #159 */ padding: 0px; /* remove min-height to fix #244 */ color: #E0E1E3; } QAbstractScrollArea:disabled { color: #9DA9B5; } /* QScrollArea ------------------------------------------------------------ --------------------------------------------------------------------------- */ QScrollArea QWidget QWidget:disabled { background-color: #19232D; } /* QScrollBar ------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qscrollbar --------------------------------------------------------------------------- */ QScrollBar:horizontal { height: 16px; margin: 2px 16px 2px 16px; border: 1px solid #455364; border-radius: 4px; background-color: #19232D; } QScrollBar:vertical { background-color: #19232D; width: 16px; margin: 16px 2px 16px 2px; border: 1px solid #455364; border-radius: 4px; } QScrollBar::handle:horizontal { background-color: #60798B; border: 1px solid #455364; border-radius: 4px; min-width: 8px; } QScrollBar::handle:horizontal:hover { background-color: #346792; border: #346792; border-radius: 4px; min-width: 8px; } QScrollBar::handle:horizontal:focus { border: 1px solid #1A72BB; } QScrollBar::handle:vertical { background-color: #60798B; border: 1px solid #455364; min-height: 8px; border-radius: 4px; } QScrollBar::handle:vertical:hover { background-color: #346792; border: #346792; border-radius: 4px; min-height: 8px; } QScrollBar::handle:vertical:focus { border: 1px solid #1A72BB; } QScrollBar::add-line:horizontal { margin: 0px 0px 0px 0px; border-image: url(":/dark/rc/arrow_right_disabled.png"); height: 12px; width: 12px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::add-line:horizontal:hover, QScrollBar::add-line:horizontal:on { border-image: url(":/dark/rc/arrow_right.png"); height: 12px; width: 12px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::add-line:vertical { margin: 3px 0px 3px 0px; border-image: url(":/dark/rc/arrow_down_disabled.png"); height: 12px; width: 12px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { border-image: url(":/dark/rc/arrow_down.png"); height: 12px; width: 12px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal { margin: 0px 3px 0px 3px; border-image: url(":/dark/rc/arrow_left_disabled.png"); height: 12px; width: 12px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on { border-image: url(":/dark/rc/arrow_left.png"); height: 12px; width: 12px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar::sub-line:vertical { margin: 3px 0px 3px 0px; border-image: url(":/dark/rc/arrow_up_disabled.png"); height: 12px; width: 12px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::sub-line:vertical:hover, QScrollBar::sub-line:vertical:on { border-image: url(":/dark/rc/arrow_up.png"); height: 12px; width: 12px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal { background: none; } QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { background: none; } QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; } /* QTextEdit -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-specific-widgets --------------------------------------------------------------------------- */ QTextEdit { background-color: #19232D; color: #E0E1E3; border-radius: 4px; border: 1px solid #455364; } QTextEdit:focus { border: 1px solid #1A72BB; } QTextEdit:selected { background: #346792; color: #455364; } /* QPlainTextEdit --------------------------------------------------------- --------------------------------------------------------------------------- */ QPlainTextEdit { background-color: #19232D; color: #E0E1E3; border-radius: 4px; border: 1px solid #455364; } QPlainTextEdit:focus { border: 1px solid #1A72BB; } QPlainTextEdit:selected { background: #346792; color: #455364; } /* QSizeGrip -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsizegrip --------------------------------------------------------------------------- */ QSizeGrip { background: transparent; width: 12px; height: 12px; image: url(":/dark/rc/window_grip.png"); } /* QStackedWidget --------------------------------------------------------- --------------------------------------------------------------------------- */ QStackedWidget { padding: 2px; border: 1px solid #455364; border: 1px solid #19232D; } /* QToolBar --------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbar --------------------------------------------------------------------------- */ QToolBar { background-color: #455364; border-bottom: 1px solid #19232D; padding: 1px; font-weight: bold; spacing: 2px; } QToolBar:disabled { /* Fixes #272 */ background-color: #455364; } QToolBar::handle:horizontal { width: 16px; image: url(":/dark/rc/toolbar_move_horizontal.png"); } QToolBar::handle:vertical { height: 16px; image: url(":/dark/rc/toolbar_move_vertical.png"); } QToolBar::separator:horizontal { width: 16px; image: url(":/dark/rc/toolbar_separator_horizontal.png"); } QToolBar::separator:vertical { height: 16px; image: url(":/dark/rc/toolbar_separator_vertical.png"); } QToolButton#qt_toolbar_ext_button { background: #455364; border: 0px; color: #E0E1E3; image: url(":/dark/rc/arrow_right.png"); } /* QAbstractSpinBox ------------------------------------------------------- --------------------------------------------------------------------------- */ QAbstractSpinBox { background-color: #19232D; border: 1px solid #455364; color: #E0E1E3; /* This fixes 103, 111 */ padding-top: 2px; /* This fixes 103, 111 */ padding-bottom: 2px; padding-left: 4px; padding-right: 4px; border-radius: 4px; /* min-width: 5px; removed to fix 109 */ } QAbstractSpinBox:up-button { background-color: transparent #19232D; subcontrol-origin: border; subcontrol-position: top right; border-left: 1px solid #455364; border-bottom: 1px solid #455364; border-top-left-radius: 0; border-bottom-left-radius: 0; margin: 1px; width: 12px; margin-bottom: -1px; } QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { image: url(":/dark/rc/arrow_up_disabled.png"); height: 8px; width: 8px; } QAbstractSpinBox::up-arrow:hover { image: url(":/dark/rc/arrow_up.png"); } QAbstractSpinBox:down-button { background-color: transparent #19232D; subcontrol-origin: border; subcontrol-position: bottom right; border-left: 1px solid #455364; border-top: 1px solid #455364; border-top-left-radius: 0; border-bottom-left-radius: 0; margin: 1px; width: 12px; margin-top: -1px; } QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { image: url(":/dark/rc/arrow_down_disabled.png"); height: 8px; width: 8px; } QAbstractSpinBox::down-arrow:hover { image: url(":/dark/rc/arrow_down.png"); } QAbstractSpinBox:hover { border: 1px solid #346792; color: #E0E1E3; } QAbstractSpinBox:focus { border: 1px solid #1A72BB; } QAbstractSpinBox:selected { background: #346792; color: #455364; } /* ------------------------------------------------------------------------ */ /* DISPLAYS --------------------------------------------------------------- */ /* ------------------------------------------------------------------------ */ /* QLabel ----------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe --------------------------------------------------------------------------- */ QLabel { background-color: #19232D; border: 0px solid #455364; padding: 0px; margin: 0px; color: #E0E1E3; } QLabel:disabled { background-color: #19232D; border: 0px solid #455364; color: #9DA9B5; } /* QTextBrowser ----------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea --------------------------------------------------------------------------- */ QTextBrowser { background-color: #19232D; border: 1px solid #455364; color: #E0E1E3; border-radius: 4px; } QTextBrowser:disabled { background-color: #19232D; border: 1px solid #455364; color: #9DA9B5; border-radius: 4px; } QTextBrowser:hover, QTextBrowser:!hover, QTextBrowser:selected, QTextBrowser:pressed { border: 1px solid #455364; } /* QGraphicsView ---------------------------------------------------------- --------------------------------------------------------------------------- */ QGraphicsView { background-color: #19232D; border: 1px solid #455364; color: #E0E1E3; border-radius: 4px; } QGraphicsView:disabled { background-color: #19232D; border: 1px solid #455364; color: #9DA9B5; border-radius: 4px; } QGraphicsView:hover, QGraphicsView:!hover, QGraphicsView:selected, QGraphicsView:pressed { border: 1px solid #455364; } /* QCalendarWidget -------------------------------------------------------- --------------------------------------------------------------------------- */ QCalendarWidget { border: 1px solid #455364; border-radius: 4px; } QCalendarWidget:disabled { background-color: #19232D; color: #9DA9B5; } /* QLCDNumber ------------------------------------------------------------- --------------------------------------------------------------------------- */ QLCDNumber { background-color: #19232D; color: #E0E1E3; } QLCDNumber:disabled { background-color: #19232D; color: #9DA9B5; } /* QProgressBar ----------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qprogressbar --------------------------------------------------------------------------- */ QProgressBar { background-color: #19232D; border: 1px solid #455364; color: #E0E1E3; border-radius: 4px; text-align: center; } QProgressBar:disabled { background-color: #19232D; border: 1px solid #455364; color: #9DA9B5; border-radius: 4px; text-align: center; } QProgressBar::chunk { background-color: #346792; color: #19232D; border-radius: 4px; } QProgressBar::chunk:disabled { background-color: #26486B; color: #9DA9B5; border-radius: 4px; } /* ------------------------------------------------------------------------ */ /* BUTTONS ---------------------------------------------------------------- */ /* ------------------------------------------------------------------------ */ /* QPushButton ------------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qpushbutton --------------------------------------------------------------------------- */ QPushButton { background-color: #455364; color: #E0E1E3; border-radius: 4px; padding: 2px; outline: none; border: none; } QPushButton:disabled { background-color: #455364; color: #9DA9B5; border-radius: 4px; padding: 2px; } QPushButton:checked { background-color: #60798B; border-radius: 4px; padding: 2px; outline: none; } QPushButton:checked:disabled { background-color: #60798B; color: #9DA9B5; border-radius: 4px; padding: 2px; outline: none; } QPushButton:checked:selected { background: #60798B; } QPushButton:hover { background-color: #54687A; color: #E0E1E3; } QPushButton:pressed { background-color: #60798B; } QPushButton:selected { background: #60798B; color: #E0E1E3; } QPushButton::menu-indicator { subcontrol-origin: padding; subcontrol-position: bottom right; bottom: 4px; } QDialogButtonBox QPushButton { /* Issue #194 #248 - Special case of QPushButton inside dialogs, for better UI */ min-width: 80px; } /* QToolButton ------------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton --------------------------------------------------------------------------- */ QToolButton { background-color: #455364; color: #E0E1E3; border-radius: 4px; padding: 2px; outline: none; border: none; /* The subcontrols below are used only in the DelayedPopup mode */ /* The subcontrols below are used only in the MenuButtonPopup mode */ /* The subcontrol below is used only in the InstantPopup or DelayedPopup mode */ } QToolButton:disabled { background-color: #455364; color: #9DA9B5; border-radius: 4px; padding: 2px; } QToolButton:checked { background-color: #60798B; border-radius: 4px; padding: 2px; outline: none; } QToolButton:checked:disabled { background-color: #60798B; color: #9DA9B5; border-radius: 4px; padding: 2px; outline: none; } QToolButton:checked:hover { background-color: #54687A; color: #E0E1E3; } QToolButton:checked:pressed { background-color: #60798B; } QToolButton:checked:selected { background: #60798B; color: #E0E1E3; } QToolButton:hover { background-color: #54687A; color: #E0E1E3; } QToolButton:pressed { background-color: #60798B; } QToolButton:selected { background: #60798B; color: #E0E1E3; } QToolButton[popupMode="0"] { /* Only for DelayedPopup */ padding-right: 2px; } QToolButton[popupMode="1"] { /* Only for MenuButtonPopup */ padding-right: 20px; } QToolButton[popupMode="1"]::menu-button { border: none; } QToolButton[popupMode="1"]::menu-button:hover { border: none; border-left: 1px solid #455364; border-radius: 0; } QToolButton[popupMode="2"] { /* Only for InstantPopup */ padding-right: 2px; } QToolButton::menu-button { padding: 2px; border-radius: 4px; width: 12px; border: none; outline: none; } QToolButton::menu-button:hover { border: 1px solid #346792; } QToolButton::menu-button:checked:hover { border: 1px solid #346792; } QToolButton::menu-indicator { image: url(":/dark/rc/arrow_down.png"); height: 8px; width: 8px; top: 0; /* Exclude a shift for better image */ left: -2px; /* Shift it a bit */ } QToolButton::menu-arrow { image: url(":/dark/rc/arrow_down.png"); height: 8px; width: 8px; } QToolButton::menu-arrow:hover { image: url(":/dark/rc/arrow_down_focus.png"); } /* QCommandLinkButton ----------------------------------------------------- --------------------------------------------------------------------------- */ QCommandLinkButton { background-color: transparent; border: 1px solid #455364; color: #E0E1E3; border-radius: 4px; padding: 0px; margin: 0px; } QCommandLinkButton:disabled { background-color: transparent; color: #9DA9B5; } /* ------------------------------------------------------------------------ */ /* INPUTS - NO FIELDS ----------------------------------------------------- */ /* ------------------------------------------------------------------------ */ /* QComboBox -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox --------------------------------------------------------------------------- */ QComboBox { border: 1px solid #455364; border-radius: 4px; selection-background-color: #346792; padding-left: 4px; padding-right: 4px; /* padding-right = 36; 4 + 16*2 See scrollbar size */ /* changed to 4px to fix #239 */ /* Fixes #103, #111 */ min-height: 1.5em; /* padding-top: 2px; removed to fix #132 */ /* padding-bottom: 2px; removed to fix #132 */ /* min-width: 75px; removed to fix #109 */ /* Needed to remove indicator - fix #132 */ } QComboBox QAbstractItemView { border: 1px solid #455364; border-radius: 0; background-color: #19232D; selection-background-color: #346792; } QComboBox QAbstractItemView:hover { background-color: #19232D; color: #E0E1E3; } QComboBox QAbstractItemView:selected { background: #346792; color: #455364; } QComboBox QAbstractItemView:alternate { background: #19232D; } QComboBox:disabled { background-color: #19232D; color: #9DA9B5; } QComboBox:hover { border: 1px solid #346792; } QComboBox:focus { border: 1px solid #1A72BB; } QComboBox:on { selection-background-color: #346792; } QComboBox::indicator { border: none; border-radius: 0; background-color: transparent; selection-background-color: transparent; color: transparent; selection-color: transparent; /* Needed to remove indicator - fix #132 */ } QComboBox::indicator:alternate { background: #19232D; } QComboBox::item { /* Remove to fix #282, #285 and MR #288*/ /*&:checked { font-weight: bold; } &:selected { border: 0px solid transparent; } */ } QComboBox::item:alternate { background: #19232D; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 12px; border-left: 1px solid #455364; } QComboBox::down-arrow { image: url(":/dark/rc/arrow_down_disabled.png"); height: 8px; width: 8px; } QComboBox::down-arrow:on, QComboBox::down-arrow:hover, QComboBox::down-arrow:focus { image: url(":/dark/rc/arrow_down.png"); } /* QSlider ---------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qslider --------------------------------------------------------------------------- */ QSlider:disabled { background: #19232D; } QSlider:focus { border: none; } QSlider::groove:horizontal { background: #455364; border: 1px solid #455364; height: 4px; margin: 0px; border-radius: 4px; } QSlider::groove:vertical { background: #455364; border: 1px solid #455364; width: 4px; margin: 0px; border-radius: 4px; } QSlider::add-page:vertical { background: #346792; border: 1px solid #455364; width: 4px; margin: 0px; border-radius: 4px; } QSlider::add-page:vertical :disabled { background: #26486B; } QSlider::sub-page:horizontal { background: #346792; border: 1px solid #455364; height: 4px; margin: 0px; border-radius: 4px; } QSlider::sub-page:horizontal:disabled { background: #26486B; } QSlider::handle:horizontal { background: #9DA9B5; border: 1px solid #455364; width: 8px; height: 8px; margin: -8px 0px; border-radius: 4px; } QSlider::handle:horizontal:hover { background: #346792; border: 1px solid #346792; } QSlider::handle:horizontal:focus { border: 1px solid #1A72BB; } QSlider::handle:vertical { background: #9DA9B5; border: 1px solid #455364; width: 8px; height: 8px; margin: 0 -8px; border-radius: 4px; } QSlider::handle:vertical:hover { background: #346792; border: 1px solid #346792; } QSlider::handle:vertical:focus { border: 1px solid #1A72BB; } /* QLineEdit -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlineedit --------------------------------------------------------------------------- */ QLineEdit { background-color: #19232D; padding-top: 2px; /* This QLineEdit fix 103, 111 */ padding-bottom: 2px; /* This QLineEdit fix 103, 111 */ padding-left: 4px; padding-right: 4px; border-style: solid; border: 1px solid #455364; border-radius: 4px; color: #E0E1E3; } QLineEdit:disabled { background-color: #19232D; color: #9DA9B5; } QLineEdit:hover { border: 1px solid #346792; color: #E0E1E3; } QLineEdit:focus { border: 1px solid #1A72BB; } QLineEdit:selected { background-color: #346792; color: #455364; } /* QTabWiget -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar --------------------------------------------------------------------------- */ QTabWidget { padding: 2px; selection-background-color: #455364; } QTabWidget QWidget { /* Fixes #189 */ border-radius: 4px; } QTabWidget::pane { border: 1px solid #455364; border-radius: 4px; margin: 0px; /* Fixes double border inside pane with pyqt5 */ padding: 0px; } QTabWidget::pane:selected { background-color: #455364; border: 1px solid #346792; } /* QTabBar ---------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar --------------------------------------------------------------------------- */ QTabBar, QDockWidget QTabBar { qproperty-drawBase: 0; border-radius: 4px; margin: 0px; padding: 2px; border: 0; /* left: 5px; move to the right by 5px - removed for fix */ } QTabBar::close-button, QDockWidget QTabBar::close-button { border: 0; margin: 0; padding: 4px; image: url(":/dark/rc/window_close.png"); } QTabBar::close-button:hover, QDockWidget QTabBar::close-button:hover { image: url(":/dark/rc/window_close_focus.png"); } QTabBar::close-button:pressed, QDockWidget QTabBar::close-button:pressed { image: url(":/dark/rc/window_close_pressed.png"); } QTabBar::tab, QDockWidget QTabBar::tab { /* !selected and disabled ----------------------------------------- */ /* selected ------------------------------------------------------- */ } QTabBar::tab:top:selected:disabled, QDockWidget QTabBar::tab:top:selected:disabled { border-bottom: 3px solid #26486B; color: #9DA9B5; background-color: #455364; } QTabBar::tab:bottom:selected:disabled, QDockWidget QTabBar::tab:bottom:selected:disabled { border-top: 3px solid #26486B; color: #9DA9B5; background-color: #455364; } QTabBar::tab:left:selected:disabled, QDockWidget QTabBar::tab:left:selected:disabled { border-right: 3px solid #26486B; color: #9DA9B5; background-color: #455364; } QTabBar::tab:right:selected:disabled, QDockWidget QTabBar::tab:right:selected:disabled { border-left: 3px solid #26486B; color: #9DA9B5; background-color: #455364; } QTabBar::tab:top:!selected:disabled, QDockWidget QTabBar::tab:top:!selected:disabled { border-bottom: 3px solid #19232D; color: #9DA9B5; background-color: #19232D; } QTabBar::tab:bottom:!selected:disabled, QDockWidget QTabBar::tab:bottom:!selected:disabled { border-top: 3px solid #19232D; color: #9DA9B5; background-color: #19232D; } QTabBar::tab:left:!selected:disabled, QDockWidget QTabBar::tab:left:!selected:disabled { border-right: 3px solid #19232D; color: #9DA9B5; background-color: #19232D; } QTabBar::tab:right:!selected:disabled, QDockWidget QTabBar::tab:right:!selected:disabled { border-left: 3px solid #19232D; color: #9DA9B5; background-color: #19232D; } QTabBar::tab:top:!selected, QDockWidget QTabBar::tab:top:!selected { border-bottom: 2px solid #19232D; margin-top: 2px; } QTabBar::tab:bottom:!selected, QDockWidget QTabBar::tab:bottom:!selected { border-top: 2px solid #19232D; margin-bottom: 2px; } QTabBar::tab:left:!selected, QDockWidget QTabBar::tab:left:!selected { border-left: 2px solid #19232D; margin-right: 2px; } QTabBar::tab:right:!selected, QDockWidget QTabBar::tab:right:!selected { border-right: 2px solid #19232D; margin-left: 2px; } QTabBar::tab:top, QDockWidget QTabBar::tab:top { background-color: #455364; margin-left: 2px; padding-left: 4px; padding-right: 4px; padding-top: 2px; padding-bottom: 2px; min-width: 5px; border-bottom: 3px solid #455364; border-top-left-radius: 4px; border-top-right-radius: 4px; } QTabBar::tab:top:selected, QDockWidget QTabBar::tab:top:selected { background-color: #54687A; border-bottom: 3px solid #259AE9; border-top-left-radius: 4px; border-top-right-radius: 4px; } QTabBar::tab:top:!selected:hover, QDockWidget QTabBar::tab:top:!selected:hover { border: 1px solid #1A72BB; border-bottom: 3px solid #1A72BB; /* Fixes spyder-ide/spyder#9766 and #243 */ padding-left: 3px; padding-right: 3px; } QTabBar::tab:bottom, QDockWidget QTabBar::tab:bottom { border-top: 3px solid #455364; background-color: #455364; margin-left: 2px; padding-left: 4px; padding-right: 4px; padding-top: 2px; padding-bottom: 2px; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; min-width: 5px; } QTabBar::tab:bottom:selected, QDockWidget QTabBar::tab:bottom:selected { background-color: #54687A; border-top: 3px solid #259AE9; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; } QTabBar::tab:bottom:!selected:hover, QDockWidget QTabBar::tab:bottom:!selected:hover { border: 1px solid #1A72BB; border-top: 3px solid #1A72BB; /* Fixes spyder-ide/spyder#9766 and #243 */ padding-left: 3px; padding-right: 3px; } QTabBar::tab:left, QDockWidget QTabBar::tab:left { background-color: #455364; margin-top: 2px; padding-left: 2px; padding-right: 2px; padding-top: 4px; padding-bottom: 4px; border-top-left-radius: 4px; border-bottom-left-radius: 4px; min-height: 5px; } QTabBar::tab:left:selected, QDockWidget QTabBar::tab:left:selected { background-color: #54687A; border-right: 3px solid #259AE9; } QTabBar::tab:left:!selected:hover, QDockWidget QTabBar::tab:left:!selected:hover { border: 1px solid #1A72BB; border-right: 3px solid #1A72BB; /* Fixes different behavior #271 */ margin-right: 0px; padding-right: -1px; } QTabBar::tab:right, QDockWidget QTabBar::tab:right { background-color: #455364; margin-top: 2px; padding-left: 2px; padding-right: 2px; padding-top: 4px; padding-bottom: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; min-height: 5px; } QTabBar::tab:right:selected, QDockWidget QTabBar::tab:right:selected { background-color: #54687A; border-left: 3px solid #259AE9; } QTabBar::tab:right:!selected:hover, QDockWidget QTabBar::tab:right:!selected:hover { border: 1px solid #1A72BB; border-left: 3px solid #1A72BB; /* Fixes different behavior #271 */ margin-left: 0px; padding-left: 0px; } QTabBar QToolButton, QDockWidget QTabBar QToolButton { /* Fixes #136 */ background-color: #455364; height: 12px; width: 12px; } QTabBar QToolButton:pressed, QDockWidget QTabBar QToolButton:pressed { background-color: #455364; } QTabBar QToolButton:pressed:hover, QDockWidget QTabBar QToolButton:pressed:hover { border: 1px solid #346792; } QTabBar QToolButton::left-arrow:enabled, QDockWidget QTabBar QToolButton::left-arrow:enabled { image: url(":/dark/rc/arrow_left.png"); } QTabBar QToolButton::left-arrow:disabled, QDockWidget QTabBar QToolButton::left-arrow:disabled { image: url(":/dark/rc/arrow_left_disabled.png"); } QTabBar QToolButton::right-arrow:enabled, QDockWidget QTabBar QToolButton::right-arrow:enabled { image: url(":/dark/rc/arrow_right.png"); } QTabBar QToolButton::right-arrow:disabled, QDockWidget QTabBar QToolButton::right-arrow:disabled { image: url(":/dark/rc/arrow_right_disabled.png"); } /* QDockWiget ------------------------------------------------------------- --------------------------------------------------------------------------- */ QDockWidget { outline: 1px solid #455364; background-color: #19232D; border: 1px solid #455364; border-radius: 4px; titlebar-close-icon: url(":/dark/rc/transparent.png"); titlebar-normal-icon: url(":/dark/rc/transparent.png"); } QDockWidget::title { /* Better size for title bar */ padding: 3px; padding-bottom: -3px; /* was not specified */ spacing: 4px; border: none; background-color: #455364; } QDockWidget::close-button { icon-size: 12px; border: none; background: transparent; background-image: transparent; border: 0; margin: 0; padding: 0; image: url(":/dark/rc/window_close.png"); } QDockWidget::close-button:hover { image: url(":/dark/rc/window_close_focus.png"); } QDockWidget::close-button:pressed { image: url(":/dark/rc/window_close_pressed.png"); } QDockWidget::float-button { icon-size: 12px; border: none; background: transparent; background-image: transparent; border: 0; margin: 0; padding: 0; image: url(":/dark/rc/window_undock.png"); } QDockWidget::float-button:hover { image: url(":/dark/rc/window_undock_focus.png"); } QDockWidget::float-button:pressed { image: url(":/dark/rc/window_undock_pressed.png"); } /* QTreeView QListView QTableView ----------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtreeview https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview --------------------------------------------------------------------------- */ QTreeView:branch:selected, QTreeView:branch:hover { background: url(":/dark/rc/transparent.png"); } QTreeView:branch:has-siblings:!adjoins-item { border-image: url(":/dark/rc/branch_line.png") 0; } QTreeView:branch:has-siblings:adjoins-item { border-image: url(":/dark/rc/branch_more.png") 0; } QTreeView:branch:!has-children:!has-siblings:adjoins-item { border-image: url(":/dark/rc/branch_end.png") 0; } QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings { border-image: none; image: url(":/dark/rc/branch_closed.png"); } QTreeView:branch:open:has-children:!has-siblings, QTreeView:branch:open:has-children:has-siblings { border-image: none; image: url(":/dark/rc/branch_open.png"); } QTreeView:branch:has-children:!has-siblings:closed:hover, QTreeView:branch:closed:has-children:has-siblings:hover { image: url(":/dark/rc/branch_closed_focus.png"); } QTreeView:branch:open:has-children:!has-siblings:hover, QTreeView:branch:open:has-children:has-siblings:hover { image: url(":/dark/rc/branch_open_focus.png"); } QTreeView::indicator:checked, QListView::indicator:checked, QTableView::indicator:checked, QColumnView::indicator:checked { image: url(":/dark/rc/checkbox_checked.png"); } QTreeView::indicator:checked:hover, QTreeView::indicator:checked:focus, QTreeView::indicator:checked:pressed, QListView::indicator:checked:hover, QListView::indicator:checked:focus, QListView::indicator:checked:pressed, QTableView::indicator:checked:hover, QTableView::indicator:checked:focus, QTableView::indicator:checked:pressed, QColumnView::indicator:checked:hover, QColumnView::indicator:checked:focus, QColumnView::indicator:checked:pressed { image: url(":/dark/rc/checkbox_checked_focus.png"); } QTreeView::indicator:unchecked, QListView::indicator:unchecked, QTableView::indicator:unchecked, QColumnView::indicator:unchecked { image: url(":/dark/rc/checkbox_unchecked.png"); } QTreeView::indicator:unchecked:hover, QTreeView::indicator:unchecked:focus, QTreeView::indicator:unchecked:pressed, QListView::indicator:unchecked:hover, QListView::indicator:unchecked:focus, QListView::indicator:unchecked:pressed, QTableView::indicator:unchecked:hover, QTableView::indicator:unchecked:focus, QTableView::indicator:unchecked:pressed, QColumnView::indicator:unchecked:hover, QColumnView::indicator:unchecked:focus, QColumnView::indicator:unchecked:pressed { image: url(":/dark/rc/checkbox_unchecked_focus.png"); } QTreeView::indicator:indeterminate, QListView::indicator:indeterminate, QTableView::indicator:indeterminate, QColumnView::indicator:indeterminate { image: url(":/dark/rc/checkbox_indeterminate.png"); } QTreeView::indicator:indeterminate:hover, QTreeView::indicator:indeterminate:focus, QTreeView::indicator:indeterminate:pressed, QListView::indicator:indeterminate:hover, QListView::indicator:indeterminate:focus, QListView::indicator:indeterminate:pressed, QTableView::indicator:indeterminate:hover, QTableView::indicator:indeterminate:focus, QTableView::indicator:indeterminate:pressed, QColumnView::indicator:indeterminate:hover, QColumnView::indicator:indeterminate:focus, QColumnView::indicator:indeterminate:pressed { image: url(":/dark/rc/checkbox_indeterminate_focus.png"); } QTreeView, QListView, QTableView, QColumnView { background-color: #19232D; border: 1px solid #455364; color: #E0E1E3; gridline-color: #455364; border-radius: 4px; } QTreeView:disabled, QListView:disabled, QTableView:disabled, QColumnView:disabled { background-color: #19232D; color: #9DA9B5; } QTreeView:selected, QListView:selected, QTableView:selected, QColumnView:selected { background-color: #346792; color: #455364; } QTreeView:focus, QListView:focus, QTableView:focus, QColumnView:focus { border: 1px solid #1A72BB; } QTreeView::item:pressed, QListView::item:pressed, QTableView::item:pressed, QColumnView::item:pressed { background-color: #346792; } QTreeView::item:selected:active, QListView::item:selected:active, QTableView::item:selected:active, QColumnView::item:selected:active { background-color: #346792; } QTreeView::item:selected:!active, QListView::item:selected:!active, QTableView::item:selected:!active, QColumnView::item:selected:!active { color: #E0E1E3; background-color: #37414F; } QTreeView::item:!selected:hover, QListView::item:!selected:hover, QTableView::item:!selected:hover, QColumnView::item:!selected:hover { outline: 0; color: #E0E1E3; background-color: #37414F; } QTableCornerButton::section { background-color: #19232D; border: 1px transparent #455364; border-radius: 0px; } /* QHeaderView ------------------------------------------------------------ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview --------------------------------------------------------------------------- */ QHeaderView { background-color: #455364; border: 0px transparent #455364; padding: 0; margin: 0; border-radius: 0; } QHeaderView:disabled { background-color: #455364; border: 1px transparent #455364; } QHeaderView::section { background-color: #455364; color: #E0E1E3; border-radius: 0; text-align: left; font-size: 13px; } QHeaderView::section::horizontal { padding-top: 0; padding-bottom: 0; padding-left: 4px; padding-right: 4px; border-left: 1px solid #19232D; } QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { border-left: 1px solid #455364; } QHeaderView::section::horizontal:disabled { color: #9DA9B5; } QHeaderView::section::vertical { padding-top: 0; padding-bottom: 0; padding-left: 4px; padding-right: 4px; border-top: 1px solid #19232D; } QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { border-top: 1px solid #455364; } QHeaderView::section::vertical:disabled { color: #9DA9B5; } QHeaderView::down-arrow { /* Those settings (border/width/height/background-color) solve bug */ /* transparent arrow background and size */ background-color: #455364; border: none; height: 12px; width: 12px; padding-left: 2px; padding-right: 2px; image: url(":/dark/rc/arrow_down.png"); } QHeaderView::up-arrow { background-color: #455364; border: none; height: 12px; width: 12px; padding-left: 2px; padding-right: 2px; image: url(":/dark/rc/arrow_up.png"); } /* QToolBox -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbox --------------------------------------------------------------------------- */ QToolBox { padding: 0px; border: 0px; border: 1px solid #455364; } QToolBox:selected { padding: 0px; border: 2px solid #346792; } QToolBox::tab { background-color: #19232D; border: 1px solid #455364; color: #E0E1E3; border-top-left-radius: 4px; border-top-right-radius: 4px; } QToolBox::tab:disabled { color: #9DA9B5; } QToolBox::tab:selected { background-color: #60798B; border-bottom: 2px solid #346792; } QToolBox::tab:selected:disabled { background-color: #455364; border-bottom: 2px solid #26486B; } QToolBox::tab:!selected { background-color: #455364; border-bottom: 2px solid #455364; } QToolBox::tab:!selected:disabled { background-color: #19232D; } QToolBox::tab:hover { border-color: #1A72BB; border-bottom: 2px solid #1A72BB; } QToolBox QScrollArea QWidget QWidget { padding: 0px; border: 0px; background-color: #19232D; } /* QFrame ----------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe https://doc.qt.io/qt-5/qframe.html#-prop https://doc.qt.io/qt-5/qframe.html#details https://stackoverflow.com/questions/14581498/qt-stylesheet-for-hline-vline-color --------------------------------------------------------------------------- */ /* (dot) .QFrame fix #141, #126, #123 */ .QFrame { border-radius: 4px; border: 1px solid #455364; /* No frame */ /* HLine */ /* HLine */ } .QFrame[frameShape="0"] { border-radius: 4px; border: 1px transparent #455364; } .QFrame[frameShape="4"] { max-height: 2px; border: none; background-color: #455364; } .QFrame[frameShape="5"] { max-width: 2px; border: none; background-color: #455364; } /* QSplitter -------------------------------------------------------------- https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsplitter --------------------------------------------------------------------------- */ QSplitter { background-color: #455364; spacing: 0px; padding: 0px; margin: 0px; } QSplitter::handle { background-color: #455364; border: 0px solid #19232D; spacing: 0px; padding: 1px; margin: 0px; } QSplitter::handle:hover { background-color: #9DA9B5; } QSplitter::handle:horizontal { width: 5px; image: url(":/dark/rc/line_vertical.png"); } QSplitter::handle:vertical { height: 5px; image: url(":/dark/rc/line_horizontal.png"); } /* QDateEdit, QDateTimeEdit ----------------------------------------------- --------------------------------------------------------------------------- */ QDateEdit, QDateTimeEdit { selection-background-color: #346792; border-style: solid; border: 1px solid #455364; border-radius: 4px; /* This fixes 103, 111 */ padding-top: 2px; /* This fixes 103, 111 */ padding-bottom: 2px; padding-left: 4px; padding-right: 4px; min-width: 10px; } QDateEdit:on, QDateTimeEdit:on { selection-background-color: #346792; } QDateEdit::drop-down, QDateTimeEdit::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 12px; border-left: 1px solid #455364; } QDateEdit::down-arrow, QDateTimeEdit::down-arrow { image: url(":/dark/rc/arrow_down_disabled.png"); height: 8px; width: 8px; } QDateEdit::down-arrow:on, QDateEdit::down-arrow:hover, QDateEdit::down-arrow:focus, QDateTimeEdit::down-arrow:on, QDateTimeEdit::down-arrow:hover, QDateTimeEdit::down-arrow:focus { image: url(":/dark/rc/arrow_down.png"); } QDateEdit QAbstractItemView, QDateTimeEdit QAbstractItemView { background-color: #19232D; border-radius: 4px; border: 1px solid #455364; selection-background-color: #346792; } /* QAbstractView ---------------------------------------------------------- --------------------------------------------------------------------------- */ QAbstractView:hover { border: 1px solid #346792; color: #E0E1E3; } QAbstractView:selected { background: #346792; color: #455364; } /* PlotWidget ------------------------------------------------------------- --------------------------------------------------------------------------- */ PlotWidget { /* Fix cut labels in plots #134 */ padding: 0px; } ================================================ FILE: resources/vsedit-job-server-watcher.qrc ================================================ vsedit-job-server-watcher.ico door_in.png cog.png folder.png information.png sheduled_task.png link.png add.png delete.png ================================================ FILE: resources/vsedit.qrc ================================================ page_white_text.png disk.png disk_edit.png folder_page_white.png film.png tick_light_blue.png door_in.png picture_save.png zoom.png zoom_actual.png zoom_in.png zoom_fit.png transform_crop.png paste_plain.png vsedit.ico cog.png folder.png folder_add.png folder_delete.png erase.png image_to_clipboard.png timeline.png timeline_frames.png time_back.png time_forward.png color_swatch.png font.png color_picker.png play_green.png pause_green.png tick.png benchmark.png film_save.png information.png hourglass.png cross.png timeline_marker.png timeline_marker_add.png timeline_marker_remove.png timeline_marker_previous.png timeline_marker_next.png sheduled_task.png ../README fonts/DigitalMini.ttf ================================================ FILE: vsedit/src/frame_consumers/benchmark_dialog.cpp ================================================ #include "benchmark_dialog.h" #include "../../../common-src/helpers.h" #include "../../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../../../common-src/settings/settings_manager.h" #include //============================================================================== ScriptBenchmarkDialog::ScriptBenchmarkDialog( SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent): VSScriptProcessorDialog(a_pSettingsManager, a_pVSScriptLibrary, a_pParent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint ) , m_processing(false) , m_framesTotal(0) , m_framesProcessed(0) , m_framesFailed(0) , m_lastFromFrame(-1) , m_lastToFrame(-1) { vsedit::disableFontKerning(this); m_ui.setupUi(this); setWindowIcon(QIcon(":benchmark.png")); createStatusBar(); m_ui.feedbackTextEdit->setName("benchmark_log"); m_ui.feedbackTextEdit->setSettingsManager(m_pSettingsManager); m_ui.feedbackTextEdit->loadSettings(); connect(m_ui.wholeVideoButton, SIGNAL(clicked()), this, SLOT(slotWholeVideoButtonPressed())); connect(m_ui.startStopBenchmarkButton, SIGNAL(clicked()), this, SLOT(slotStartStopBenchmarkButtonPressed())); } // END OF ScriptBenchmarkDialog::ScriptBenchmarkDialog( // SettingsManager * a_pSettingsManager, // VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent //============================================================================== ScriptBenchmarkDialog::~ScriptBenchmarkDialog() { } // END OF ScriptBenchmarkDialog::~ScriptBenchmarkDialog() //============================================================================== bool ScriptBenchmarkDialog::initialize(const QString & a_script, const QString & a_scriptName, ProcessReason a_reason) { bool initialized = VSScriptProcessorDialog::initialize(a_script, a_scriptName, a_reason); if(!initialized) emit signalWriteLogMessage(mtCritical, m_pVapourSynthScriptProcessor->error()); return initialized; } // END OF bool ScriptBenchmarkDialog::initialize(const QString & a_script, // const QString & a_scriptName) //============================================================================== void ScriptBenchmarkDialog::resetSavedRange() { m_lastFromFrame = -1; m_lastToFrame = -1; } // END OF void ScriptBenchmarkDialog::resetSavedRange() //============================================================================== void ScriptBenchmarkDialog::call() { if(m_processing) { show(); return; } if((!m_pVapourSynthScriptProcessor->isInitialized()) || m_wantToFinalize) return; Q_ASSERT(!m_nodeInfo[0].isInvalid()); m_ui.feedbackTextEdit->clear(); setWindowTitle(tr("Benchmark: %1").arg(scriptName())); QString text = tr("Ready to benchmark script %1").arg(scriptName()); m_ui.feedbackTextEdit->addEntry(text); m_ui.metricsEdit->clear(); int firstFrame = 0; int lastFrame = m_nodeInfo[0].numFrames() - 1; m_ui.fromFrameSpinBox->setMaximum(lastFrame); m_ui.toFrameSpinBox->setMaximum(lastFrame); m_ui.processingProgressBar->setMaximum(m_nodeInfo[0].numFrames()); m_ui.processingProgressBar->setValue(0); if(m_lastFromFrame >= 0) firstFrame = std::min(m_lastFromFrame, lastFrame); m_ui.fromFrameSpinBox->setValue(firstFrame); if(m_lastToFrame >= 0) lastFrame = std::min(m_lastToFrame, lastFrame); m_ui.toFrameSpinBox->setValue(lastFrame); show(); } // END OF void ScriptBenchmarkDialog::call() //============================================================================== void ScriptBenchmarkDialog::stopAndCleanUp() { stopProcessing(); m_ui.metricsEdit->clear(); m_ui.processingProgressBar->setValue(0); } // END OF void ScriptBenchmarkDialog::stopAndCleanUp() //============================================================================== void ScriptBenchmarkDialog::slotWriteLogMessage(int a_messageType, const QString & a_message) { QString style = vsMessageTypeToStyleName(a_messageType); QString debugTypes[] = { LOG_STYLE_DEBUG, LOG_STYLE_QT_DEBUG, LOG_STYLE_VS_DEBUG, }; if(m_pSettingsManager->getShowDebugMessages() || !vsedit::contains(debugTypes, style)) { m_ui.feedbackTextEdit->addEntry(a_message, style); } } // END OF void ScriptBenchmarkDialog::slotWriteLogMessage(int a_messageType, // const QString & a_message) //============================================================================== void ScriptBenchmarkDialog::slotWholeVideoButtonPressed() { Q_ASSERT(!m_nodeInfo[0].isInvalid()); int lastFrame = m_nodeInfo[0].numFrames() - 1; m_ui.fromFrameSpinBox->setValue(0); m_ui.toFrameSpinBox->setValue(lastFrame); } // END OF void ScriptBenchmarkDialog::slotWholeVideoButtonPressed() //============================================================================== void ScriptBenchmarkDialog::slotStartStopBenchmarkButtonPressed() { if(m_processing) { stopProcessing(); return; } m_framesProcessed = 0; m_framesFailed = 0; int firstFrame = m_ui.fromFrameSpinBox->value(); int lastFrame = m_ui.toFrameSpinBox->value(); if(firstFrame > lastFrame) { m_ui.feedbackTextEdit->addEntry(tr( "First frame number is larger than the last frame number."), LOG_STYLE_WARNING); return; } m_framesTotal = lastFrame - firstFrame + 1; m_ui.processingProgressBar->setMaximum(m_framesTotal); m_ui.startStopBenchmarkButton->setText(tr("Stop")); setWindowTitle(tr("0% Benchmark: %1").arg(scriptName())); m_lastFromFrame = firstFrame; m_lastToFrame = lastFrame; m_processing = true; m_benchmarkStartTime = hr_clock::now(); for(int i = firstFrame; i <= lastFrame; ++i) m_pVapourSynthScriptProcessor->requestFrameAsync(i); } // END OF void ScriptBenchmarkDialog::slotStartStopBenchmarkButtonPressed() //============================================================================== void ScriptBenchmarkDialog::slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame) { (void)a_frameNumber; (void)a_outputIndex; (void)a_cpOutputFrame; (void)a_cpPreviewFrame; if(!m_processing) return; m_framesProcessed++; updateMetrics(); } // END OF void ScriptBenchmarkDialog::slotReceiveFrame(int a_frameNumber, // int a_outputIndex, const VSFrame * a_cpOutputFrame, // const VSFrame * a_cpPreviewFrame) //============================================================================== void ScriptBenchmarkDialog::slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason) { (void)a_frameNumber; (void)a_outputIndex; (void)a_reason; if(!m_processing) return; m_framesProcessed++; m_framesFailed++; updateMetrics(); } // END OF void ScriptBenchmarkDialog::slotFrameRequestDiscarded( // int a_frameNumber, int a_outputIndex, const QString & a_reason) //============================================================================== void ScriptBenchmarkDialog::stopProcessing() { if(!m_processing) return; m_processing = false; m_pVapourSynthScriptProcessor->flushFrameTicketsQueue(); m_ui.startStopBenchmarkButton->setText(tr("Start")); } // END OF void ScriptBenchmarkDialog::stopProcessing() //============================================================================== void ScriptBenchmarkDialog::updateMetrics() { hr_time_point now = hr_clock::now(); m_ui.processingProgressBar->setValue(m_framesProcessed); double passed = duration_to_double(now - m_benchmarkStartTime); QString passedString = vsedit::timeToString(passed); double fps = (double)m_framesProcessed / passed; QString text = tr("Time elapsed: %1 - %2 FPS") .arg(passedString).arg(QString::number(fps, 'f', 20)); if(m_framesFailed > 0) text += tr("; %1 frames failed").arg(m_framesFailed); if(m_framesProcessed < m_framesTotal) { double estimated = (m_framesTotal - m_framesProcessed) / fps; QString estimatedString = vsedit::timeToString(estimated); text += tr("; estimated time to finish: %1").arg(estimatedString); } m_ui.metricsEdit->setText(text); int percentage = (int)((double)m_framesProcessed * 100.0 / (double)m_framesTotal); setWindowTitle(tr("%1% Benchmark: %2") .arg(percentage).arg(scriptName())); if(m_framesProcessed == m_framesTotal) stopProcessing(); } // END OF void ScriptBenchmarkDialog::updateMetrics() //============================================================================== void ScriptBenchmarkDialog::keyPressEvent(QKeyEvent * a_pEvent) { Qt::KeyboardModifiers modifiers = a_pEvent->modifiers(); if(modifiers != Qt::NoModifier) { QDialog::keyPressEvent(a_pEvent); return; } if(a_pEvent->key() == Qt::Key_Escape) close(); else QDialog::keyPressEvent(a_pEvent); } // END OF void ScriptBenchmarkDialog::keyPressEvent(QKeyEvent * a_pEvent) //============================================================================== ================================================ FILE: vsedit/src/frame_consumers/benchmark_dialog.h ================================================ #ifndef BENCHMARK_DIALOG_H_INCLUDED #define BENCHMARK_DIALOG_H_INCLUDED #include #include "../vapoursynth/vs_script_processor_dialog.h" #include "../../../common-src/chrono.h" #include class ScriptBenchmarkDialog : public VSScriptProcessorDialog { Q_OBJECT public: ScriptBenchmarkDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent = nullptr); virtual ~ScriptBenchmarkDialog(); virtual bool initialize(const QString & a_script, const QString & a_scriptName, ProcessReason a_reason = ProcessReason::Benchmark) override; void resetSavedRange(); public slots: void call(); protected slots: virtual void slotWriteLogMessage(int a_messageType, const QString & a_message) override; virtual void slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame) override; virtual void slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason) override; void slotWholeVideoButtonPressed(); void slotStartStopBenchmarkButtonPressed(); protected: virtual void stopAndCleanUp() override; void stopProcessing(); void updateMetrics(); void keyPressEvent(QKeyEvent * a_pEvent); Ui::ScriptBenchmarkDialog m_ui; bool m_processing; int m_framesTotal; int m_framesProcessed; int m_framesFailed; hr_time_point m_benchmarkStartTime; int m_lastFromFrame; int m_lastToFrame; }; #endif // BENCHMARK_DIALOG_H_INCLUDED ================================================ FILE: vsedit/src/frame_consumers/benchmark_dialog.ui ================================================ ScriptBenchmarkDialog 0 0 400 191 Script benchmark 4 4 4 4 4 true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse true Qt::AlignCenter %v / %m 4 Frames: to Full length Qt::Horizontal 13 20 Start true VSEditorLog QTextEdit
../../../common-src/log/vs_editor_log.h
================================================ FILE: vsedit/src/frame_consumers/encode_dialog.cpp ================================================ #include "encode_dialog.h" #include "../../../common-src/helpers.h" #include "../../../common-src/settings/settings_manager.h" #include #include #include #include #include //============================================================================== EncodeDialog::EncodeDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent) : QDialog(a_pParent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint ) , m_pSettingsManager(a_pSettingsManager) , m_pJob(nullptr) { vsedit::disableFontKerning(this); m_ui.setupUi(this); m_pJob = new vsedit::Job(JobProperties(), a_pSettingsManager, a_pVSScriptLibrary, this); setUpEncodingPresets(); m_ui.feedbackTextEdit->setName("encode_log"); m_ui.feedbackTextEdit->setSettingsManager(m_pSettingsManager); m_ui.feedbackTextEdit->loadSettings(); connect(m_ui.wholeVideoButton, SIGNAL(clicked()), this, SLOT(slotWholeVideoButtonPressed())); connect(m_ui.startEncodeButton, SIGNAL(clicked()), this, SLOT(slotStartEncodeButtonPressed())); connect(m_ui.pauseEncodeButton, SIGNAL(clicked()), m_pJob, SLOT(pause())); connect(m_ui.abortEncodeButton, SIGNAL(clicked()), m_pJob, SLOT(abort())); connect(m_ui.executableBrowseButton, SIGNAL(clicked()), this, SLOT(slotExecutableBrowseButtonPressed())); connect(m_ui.argumentsHelpButton, SIGNAL(clicked()), this, SLOT(slotArgumentsHelpButtonPressed())); connect(m_pJob, &vsedit::Job::signalStateChanged, this, &EncodeDialog::slotJobStateChanged); connect(m_pJob, &vsedit::Job::signalProgressChanged, this, &EncodeDialog::slotJobProgressChanged); connect(m_pJob, &vsedit::Job::signalPropertiesChanged, this, &EncodeDialog::slotJobPropertiesChanged); connect(m_pJob, SIGNAL(signalLogMessage(const QString &, const QString &)), this, SLOT(slotWriteLogMessage(const QString &, const QString &))); } // END OF EncodeDialog::EncodeDialog(SettingsManager * a_pSettingsManager, // VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent) //============================================================================== EncodeDialog::~EncodeDialog() { } // END OF EncodeDialog::~EncodeDialog() //============================================================================== bool EncodeDialog::initialize(const QString & a_script, const QString & a_scriptName) { if(m_pJob->isActive()) return false; JobProperties properties; properties.scriptName = a_scriptName; properties.scriptText = a_script; m_pJob->setProperties(properties); bool initialized = m_pJob->initialize(); if(!initialized) return false; properties = m_pJob->properties(); const VSVideoInfo * cpVideoInfo = m_pJob->videoInfo(); Q_ASSERT(cpVideoInfo); m_ui.feedbackTextEdit->clear(); setWindowTitle(tr("Encode: %1").arg(a_scriptName)); QString text = tr("Ready to encode script %1").arg(a_scriptName); m_ui.feedbackTextEdit->addEntry(text); m_ui.metricsEdit->clear(); int lastFrame = cpVideoInfo->numFrames - 1; m_ui.fromFrameSpinBox->setMaximum(lastFrame); m_ui.toFrameSpinBox->setMaximum(lastFrame); m_ui.fromFrameSpinBox->setValue(properties.firstFrameReal); m_ui.toFrameSpinBox->setValue(properties.lastFrameReal); m_ui.processingProgressBar->setMaximum(lastFrame); m_ui.processingProgressBar->setValue(0); setUiEnabled(); return true; } // END OF bool EncodeDialog::initialize(const QString & a_script, // const QString & a_scriptName) //============================================================================== bool EncodeDialog::busy() const { return m_pJob->isActive(); } // END OF bool EncodeDialog::busy() const //============================================================================== void EncodeDialog::showActive() { if(isMinimized()) showNormal(); else show(); activateWindow(); } // END OF void EncodeDialog::showActive() //============================================================================== void EncodeDialog::showEvent(QShowEvent * a_pEvent) { QDialog::showEvent(a_pEvent); } // END OF void EncodeDialog::showEvent(QShowEvent * a_pEvent) //============================================================================== void EncodeDialog::closeEvent(QCloseEvent * a_pEvent) { if(m_pJob->isActive()) { a_pEvent->ignore(); return; } m_pJob->cleanUpEncoding(); QDialog::closeEvent(a_pEvent); } // END OF void EncodeDialog::closeEvent(QCloseEvent * a_pEvent) //============================================================================== void EncodeDialog::slotWholeVideoButtonPressed() { const VSVideoInfo * cpVideoInfo = m_pJob->videoInfo(); Q_ASSERT(cpVideoInfo); int lastFrame = cpVideoInfo->numFrames - 1; m_ui.fromFrameSpinBox->setValue(0); m_ui.toFrameSpinBox->setValue(lastFrame); } // END OF void EncodeDialog::slotWholeVideoButtonPressed() //============================================================================== void EncodeDialog::slotStartEncodeButtonPressed() { if(m_pJob->state() == JobState::Paused) { m_pJob->start(); return; } if(m_pJob->isActive()) return; int firstFrame = m_ui.fromFrameSpinBox->value(); int lastFrame = m_ui.toFrameSpinBox->value(); if(firstFrame > lastFrame) { m_ui.feedbackTextEdit->addEntry(tr("First frame number is " "larger than the last frame number."), LOG_STYLE_WARNING); return; } m_pJob->setFirstFrame(firstFrame); m_pJob->setLastFrame(lastFrame); m_pJob->setExecutablePath(vsedit::resolvePathFromApplication( m_ui.executablePathEdit->text())); m_pJob->setArguments(m_ui.argumentsTextEdit->toPlainText()); m_pJob->setEncodingHeaderType((EncodingHeaderType) m_ui.headerTypeComboBox->currentData().toInt()); m_pJob->start(); } // END OF void EncodeDialog::slotStartEncodeButtonPressed() //============================================================================== void EncodeDialog::slotExecutableBrowseButtonPressed() { QString applicationPath = QCoreApplication::applicationDirPath(); QFileDialog fileDialog; fileDialog.setWindowTitle(tr("Choose encoder executable")); fileDialog.setDirectory(applicationPath); if(!fileDialog.exec()) return; QStringList filesList = fileDialog.selectedFiles(); m_ui.executablePathEdit->setText(filesList[0]); } // END OF void EncodeDialog::slotExecutableBrowseButtonPressed() //============================================================================== void EncodeDialog::slotArgumentsHelpButtonPressed() { JobVariables variables; QString argumentsHelpString = tr("Use following placeholders:"); for(const vsedit::VariableToken & variable : variables.variables()) { argumentsHelpString += QString("\n%1 - %2") .arg(variable.token).arg(variable.description); } QString title = tr("Encoder arguments"); QMessageBox msgBox(this); msgBox.setWindowTitle(title); msgBox.setText(argumentsHelpString); vsedit::disableFontKerning(&msgBox); msgBox.exec(); } // END OF void EncodeDialog::slotArgumentsHelpButtonPressed() //============================================================================== void EncodeDialog::slotEncodingPresetSaveButtonPressed() { EncodingPreset preset(m_ui.encodingPresetComboBox->currentText()); if(preset.name.isEmpty()) { m_ui.feedbackTextEdit->addEntry( tr("Preset name must not be empty."), LOG_STYLE_WARNING); return; } if(preset.type == EncodingType::CLI) { preset.executablePath = m_ui.executablePathEdit->text(); if(preset.executablePath.isEmpty()) { m_ui.feedbackTextEdit->addEntry( tr("Executable path must not be empty."), LOG_STYLE_WARNING); return; } preset.arguments = m_ui.argumentsTextEdit->toPlainText(); } preset.headerType = (EncodingHeaderType) m_ui.headerTypeComboBox->currentData().toInt(); bool success = m_pSettingsManager->saveEncodingPreset(preset); if(!success) { m_ui.feedbackTextEdit->addEntry(tr("Error saving preset."), LOG_STYLE_ERROR); return; } std::vector::iterator it = std::find( m_encodingPresets.begin(), m_encodingPresets.end(), preset); if(it == m_encodingPresets.end()) { Q_ASSERT(m_ui.encodingPresetComboBox->findText(preset.name) == -1); m_encodingPresets.push_back(preset); m_ui.encodingPresetComboBox->addItem(preset.name); m_ui.encodingPresetComboBox->model()->sort(0); } else { Q_ASSERT(m_ui.encodingPresetComboBox->findText(preset.name) != -1); *it = preset; } m_ui.feedbackTextEdit->addEntry(tr("Preset \'%1\' saved.") .arg(preset.name), LOG_STYLE_POSITIVE); } // END OF void EncodeDialog::slotEncodingPresetSaveButtonPressed() //============================================================================== void EncodeDialog::slotEncodingPresetDeleteButtonPressed() { EncodingPreset preset(m_ui.encodingPresetComboBox->currentText()); if(preset.name.isEmpty()) return; QMessageBox quesBox(this); vsedit::disableFontKerning(&quesBox); quesBox.setWindowTitle(tr("Delete preset")); quesBox.setText(tr("Do you really want to delete " "preset \'%1\'?").arg(preset.name)); quesBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); quesBox.setDefaultButton(QMessageBox::No); int result = quesBox.exec(); if(result == QMessageBox::No) return; std::vector::iterator it = std::find( m_encodingPresets.begin(), m_encodingPresets.end(), preset); if(it == m_encodingPresets.end()) { Q_ASSERT(m_ui.encodingPresetComboBox->findText(preset.name) == -1); m_ui.feedbackTextEdit->addEntry(tr("Error deleting preset. " "Preset was never saved."), LOG_STYLE_ERROR); return; } int index = m_ui.encodingPresetComboBox->findText(preset.name); Q_ASSERT(index != -1); m_ui.encodingPresetComboBox->removeItem(index); m_encodingPresets.erase(it); m_ui.encodingPresetComboBox->setCurrentIndex(0); slotEncodingPresetComboBoxActivated( m_ui.encodingPresetComboBox->currentText()); bool success = m_pSettingsManager->deleteEncodingPreset(preset.name); if(!success) { m_ui.feedbackTextEdit->addEntry(tr("Error deleting " "preset \'%1\'.").arg(preset.name), LOG_STYLE_ERROR); return; } m_ui.feedbackTextEdit->addEntry(tr("Preset \'%1\' deleted.") .arg(preset.name), LOG_STYLE_POSITIVE); } // END OF void EncodeDialog::slotEncodingPresetDeleteButtonPressed() //============================================================================== void EncodeDialog::slotEncodingPresetComboBoxActivated(const QString & a_text) { if(a_text.isEmpty()) { m_ui.executablePathEdit->clear(); m_ui.argumentsTextEdit->clear(); return; } EncodingPreset preset(a_text); std::vector::iterator it = std::find( m_encodingPresets.begin(), m_encodingPresets.end(), preset); if(it == m_encodingPresets.end()) { m_ui.feedbackTextEdit->addEntry(tr("Error. There is no preset " "named \'%1\'.").arg(preset.name), LOG_STYLE_ERROR); return; } preset = *it; m_ui.executablePathEdit->setText(preset.executablePath); m_ui.argumentsTextEdit->setPlainText(preset.arguments); int headerTypeIndex = m_ui.headerTypeComboBox->findData((int)preset.headerType); if(headerTypeIndex < 0) { m_ui.feedbackTextEdit->addEntry(tr("Error. Preset \'%1\' " "has unknown header type.").arg(preset.name), LOG_STYLE_ERROR); headerTypeIndex = 0; } m_ui.headerTypeComboBox->setCurrentIndex(headerTypeIndex); } // END OF void EncodeDialog::slotEncodingPresetComboBoxActivated( // const QString & a_text) //============================================================================== void EncodeDialog::slotJobStateChanged(JobState a_newState, JobState a_oldState) { setUiEnabled(); JobProperties properties = m_pJob->properties(); JobState pauseStates[] = {JobState::Pausing, JobState::Paused}; JobState failStates[] = {JobState::FailedCleanUp, JobState::Failed, JobState::Aborted, JobState::Aborting}; if(a_newState == JobState::Running) { JobState idleStates[] = {JobState::Waiting, JobState::Failed, JobState::Aborted, JobState::Completed}; if(!vsedit::contains(idleStates, a_oldState)) return; setWindowTitle(tr("0% Encode: %1").arg(properties.scriptName)); m_ui.processingProgressBar->setMaximum(properties.framesTotal()); m_ui.processingProgressBar->setValue(0); } } // END OF void EncodeDialog::slotJobStateChanged(JobState a_newState, // JobState a_oldState) //============================================================================== void EncodeDialog::slotJobProgressChanged() { JobProperties properties = m_pJob->properties(); m_ui.processingProgressBar->setValue(properties.framesProcessed); QDateTime now = QDateTime::currentDateTimeUtc(); double passed = ((double)properties.timeStarted.msecsTo(now)) / 1000.0; QString passedString = vsedit::timeToString(passed); QString text = tr("Time elapsed: %1 - %2 FPS") .arg(passedString).arg(QString::number(properties.fps, 'f', 20)); if((properties.framesProcessed > 0) && (properties.framesProcessed < properties.framesTotal())) { Q_ASSERT(properties.fps > 0.0); double estimated = (properties.framesTotal() - properties.framesProcessed) / properties.fps; QString estimatedString = vsedit::timeToString(estimated); text += tr("; estimated time to finish: %1") .arg(estimatedString); } m_ui.metricsEdit->setText(text); int percentage = (int)((double)properties.framesProcessed * 100.0 / (double)properties.framesTotal()); setWindowTitle(tr("%1% Encode: %2") .arg(percentage).arg(properties.scriptName)); } // END OF void EncodeDialog::slotJobProgressChanged() //============================================================================== void EncodeDialog::slotJobPropertiesChanged() { JobProperties properties = m_pJob->properties(); m_ui.processingProgressBar->setMaximum(properties.framesTotal()); } // END OF void EncodeDialog::slotJobPropertiesChanged() //============================================================================== void EncodeDialog::slotWriteLogMessage(const QString & a_message, const QString & a_style) { if(!isVisible()) emit signalWriteLogMessage(a_message, a_style); else { QString debugTypes[] = { LOG_STYLE_DEBUG, LOG_STYLE_QT_DEBUG, LOG_STYLE_VS_DEBUG, }; if(m_pSettingsManager->getShowDebugMessages() || !vsedit::contains(debugTypes, a_style)) { m_ui.feedbackTextEdit->addEntry(a_message, a_style); } } } // END OF void EncodeDialog::slotWriteLogMessage(const QString & a_message, // const QString & a_style) //============================================================================== void EncodeDialog::setUpEncodingPresets() { m_encodingPresets = m_pSettingsManager->getAllEncodingPresets(); for(const EncodingPreset & preset : m_encodingPresets) m_ui.encodingPresetComboBox->addItem(preset.name); m_ui.headerTypeComboBox->addItem(tr("No header"), (int)EncodingHeaderType::NoHeader); m_ui.headerTypeComboBox->addItem(tr("Y4M"), (int)EncodingHeaderType::Y4M); connect(m_ui.encodingPresetSaveButton, SIGNAL(clicked()), this, SLOT(slotEncodingPresetSaveButtonPressed())); connect(m_ui.encodingPresetDeleteButton, SIGNAL(clicked()), this, SLOT(slotEncodingPresetDeleteButtonPressed())); connect(m_ui.encodingPresetComboBox, SIGNAL(textActivated(const QString &)), this, SLOT(slotEncodingPresetComboBoxActivated(const QString &))); m_ui.encodingPresetComboBox->setCurrentIndex(0); slotEncodingPresetComboBoxActivated( m_ui.encodingPresetComboBox->currentText()); } // END OF void EncodeDialog::setUpEncodingPresets() //============================================================================== void EncodeDialog::setUiEnabled() { } // END OF void EncodeDialog::setUiEnabled() //============================================================================== ================================================ FILE: vsedit/src/frame_consumers/encode_dialog.h ================================================ #ifndef ENCODE_DIALOG_H_INCLUDED #define ENCODE_DIALOG_H_INCLUDED #include #include "../../../common-src/jobs/job.h" class SettingsManager; class VSScriptLibrary; class EncodeDialog : public QDialog { Q_OBJECT public: EncodeDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent = nullptr); virtual ~EncodeDialog(); bool initialize(const QString & a_script, const QString & a_scriptName); bool busy() const; public slots: void showActive(); signals: void signalWriteLogMessage(const QString & a_style, const QString & a_message); protected: virtual void showEvent(QShowEvent * a_pEvent) override; virtual void closeEvent(QCloseEvent * a_pEvent) override; private slots: void slotWholeVideoButtonPressed(); void slotStartEncodeButtonPressed(); void slotExecutableBrowseButtonPressed(); void slotArgumentsHelpButtonPressed(); void slotEncodingPresetSaveButtonPressed(); void slotEncodingPresetDeleteButtonPressed(); void slotEncodingPresetComboBoxActivated(const QString & a_text); void slotJobStateChanged(JobState a_newState, JobState a_oldState); void slotJobProgressChanged(); void slotJobPropertiesChanged(); void slotWriteLogMessage(const QString & a_message, const QString & a_style); private: void setUpEncodingPresets(); void setUiEnabled(); Ui::EncodeDialog m_ui; SettingsManager * m_pSettingsManager; vsedit::Job * m_pJob; std::vector m_encodingPresets; }; #endif // ENCODE_DIALOG_H_INCLUDED ================================================ FILE: vsedit/src/frame_consumers/encode_dialog.ui ================================================ EncodeDialog 0 0 677 648 Encode video :/film_save.png:/film_save.png 4 4 4 4 4 true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 4 Preset: 0 0 true QComboBox::NoInsert Save Delete 4 Header: QComboBox::AdjustToContents Executable: :/folder.png:/folder.png Arguments (newlines will be replaced with spaces): :/information.png:/information.png Qt::Horizontal 40 20 true Qt::AlignCenter %v / %m 4 Frames: to Whole video Qt::Horizontal 13 20 Start true Pause Abort VSEditorLog QTextEdit
../../../common-src/log/vs_editor_log.h
================================================ FILE: vsedit/src/job_server_watcher_socket.cpp ================================================ #include "job_server_watcher_socket.h" #include "../../common-src/helpers.h" #include "../../common-src/ipc_defines.h" #include "../../common-src/log/vs_editor_log_definitions.h" #include //============================================================================== JobServerWatcherSocket::JobServerWatcherSocket(QObject * a_pParent): QObject(a_pParent) , m_pSocket(nullptr) { } // END OF JobServerWatcherSocket::JobServerWatcherSocket(QObject * a_pParent) //============================================================================== JobServerWatcherSocket::~JobServerWatcherSocket() { } // END OF JobServerWatcherSocket::~JobServerWatcherSocket() //============================================================================== bool JobServerWatcherSocket::sendMessage(const QByteArray & a_data) { bool connected = connectToJobServerWatcher(); if(!connected) return false; m_pSocket->write(a_data); return true; } // END OF bool JobServerWatcherSocket::sendMessage(const QByteArray & a_data) //============================================================================== bool JobServerWatcherSocket::connectToJobServerWatcher() { // In Linux QLocalSocket wouldn't reconnect once disconnected. // So we recreate it before each connection attempt. resetSocket(); // Must connect in Read/Write mode, or named pipe won't disconnect. const QIODevice::OpenMode openMode = QIODevice::ReadWrite; m_pSocket->connectToServer(openMode); bool connected = m_pSocket->waitForConnected(1000); if(connected) return true; QString watcherPath = vsedit::resolvePathFromApplication( "./vsedit-job-server-watcher"); QString thisDir = vsedit::resolvePathFromApplication("."); QProcess watcherProcess; bool started = watcherProcess.startDetached(watcherPath, QStringList(), thisDir); if(!started) { emit signalWriteLogMessage(tr("Could not start " "job server watcher."), LOG_STYLE_ERROR); return false; } for(int i = 0; i < 10; ++i) { resetSocket(); m_pSocket->connectToServer(openMode); connected = m_pSocket->waitForConnected(1000); if(connected) break; vsedit::wait(1000); } if(!connected) { emit signalWriteLogMessage(tr("Started job server watcher, " "but could not connect."), LOG_STYLE_ERROR); return false; } return true; } // END OF bool JobServerWatcherSocket::connectToJobServerWatcher() //============================================================================== void JobServerWatcherSocket::resetSocket() { if(m_pSocket) delete m_pSocket; m_pSocket = new QLocalSocket(this); m_pSocket->setServerName(JOB_SERVER_WATCHER_LOCAL_SERVER_NAME); } // END OF void JobServerWatcherSocket::resetSocket() //============================================================================== ================================================ FILE: vsedit/src/job_server_watcher_socket.h ================================================ #ifndef JOB_SERVER_WATCHER_SOCKET_H_INCLUDED #define JOB_SERVER_WATCHER_SOCKET_H_INCLUDED #include class JobServerWatcherSocket : public QObject { Q_OBJECT public: JobServerWatcherSocket(QObject * a_pParent = nullptr); virtual ~JobServerWatcherSocket(); bool sendMessage(const QByteArray & a_data); signals: void signalWriteLogMessage(const QString & a_message, const QString & a_style); private: bool connectToJobServerWatcher(); void resetSocket(); QLocalSocket * m_pSocket; }; #endif // JOB_SERVER_WATCHER_SOCKET_H_INCLUDED ================================================ FILE: vsedit/src/main.cpp ================================================ #include "main_window.h" #include "../../common-src/log/vs_editor_log.h" #include "../../common-src/settings/settings_manager.h" #include "../../common-src/version_info.h" #include "../../common-src/win32_console.h" #include #include Q_DECLARE_OPAQUE_POINTER(const VSFrame *) Q_DECLARE_OPAQUE_POINTER(VSNode *) MainWindow * pMainWindow = nullptr; #if defined(Q_OS_WIN) AttachedConsole * pConsole = nullptr; void toggleAttachedConsole() { if(pConsole->visible()) pConsole->hide(); else pConsole->show(); } #endif void handleQtMessage(QtMsgType a_type, const QMessageLogContext & a_context, const QString & a_message) { QString prefix = "Qt debug"; QString style = LOG_STYLE_DEFAULT; switch(a_type) { case QtDebugMsg: prefix = "Qt debug"; style = LOG_STYLE_QT_DEBUG; break; case QtInfoMsg: prefix = "Qt info"; style = LOG_STYLE_QT_INFO; break; case QtWarningMsg: prefix = "Qt warning"; style = LOG_STYLE_QT_WARNING; break; case QtCriticalMsg: prefix = "Qt critical"; style = LOG_STYLE_QT_CRITICAL; break; case QtFatalMsg: prefix = "Qt fatal"; style = LOG_STYLE_QT_FATAL; break; default: Q_ASSERT(false); } QString fullMessage = QString("%1: %2").arg(prefix).arg(a_message); QString fileString(a_context.file); QString lineString = QString::number(a_context.line); QString functionString(a_context.function); QString lineInfo = QString("\n(%1:%2").arg(fileString).arg(lineString); if(!functionString.isEmpty()) lineInfo += QString(", %1").arg(functionString); lineInfo += QString(")"); if(!fileString.isEmpty()) fullMessage += lineInfo; pMainWindow->slotWriteLogMessage(fullMessage, style); if(a_type == QtFatalMsg) abort(); } int main(int argc, char *argv[]) { if(argc > 1) { if(strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { print_version(); return 0; } } #if defined(Q_OS_WIN) pConsole = new AttachedConsole(); print_version(); std::cerr << "Do not close this window unless you are ready to quit!" << std::endl; #endif QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); QGuiApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::Floor); QApplication application(argc, argv); SettingsManager *settings = new SettingsManager(qApp); vsedit::disableFontKerning(qApp); // Make text in message box selectable application.setStyleSheet( "QToolTip { font-kerning: none; }" "QMessageBox { messagebox-text-interaction-flags: 5; }" "QLabel { padding: 0px; }"); qRegisterMetaType("const VSFrame *"); qRegisterMetaType("VSNode *"); pMainWindow = new MainWindow(settings); qInstallMessageHandler(handleQtMessage); #if defined(Q_OS_WIN) QObject::connect(pMainWindow, &MainWindow::signalToggleAttachedConsole, toggleAttachedConsole); #endif pMainWindow->show(); int exitCode = application.exec(); delete pMainWindow; delete settings; #if defined(Q_OS_WIN) if(pConsole) { pConsole->destroy(); delete pConsole; } #endif return exitCode; } ================================================ FILE: vsedit/src/main_window.cpp ================================================ #include "main_window.h" #include "../../common-src/settings/settings_manager.h" #include "../../common-src/vapoursynth/vs_script_library.h" #include "../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../../common-src/helpers.h" #include "../../common-src/ipc_defines.h" #include "vapoursynth/vapoursynth_plugins_manager.h" #include "preview/preview_dialog.h" #include "settings/settings_dialog.h" #include "frame_consumers/benchmark_dialog.h" #include "frame_consumers/encode_dialog.h" #include "script_templates/templates_dialog.h" #include "job_server_watcher_socket.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== MainWindow::MainWindow(SettingsManager *settings) : QMainWindow() , m_pSettingsManager(settings) , m_pVapourSynthPluginsManager(nullptr) , m_pVSScriptLibrary(nullptr) , m_pActionNewScript(nullptr) , m_pActionOpenScript(nullptr) , m_pActionSaveScript(nullptr) , m_pActionSaveScriptAs(nullptr) , m_pActionTemplates(nullptr) , m_pActionSettings(nullptr) , m_pActionPreview(nullptr) , m_pActionCheckScript(nullptr) , m_pActionBenchmark(nullptr) , m_pActionEncode(nullptr) , m_pActionEnqueueEncodeJob(nullptr) , m_pActionJobs(nullptr) , m_pActionConsole(nullptr) , m_pActionExit(nullptr) , m_pActionAbout(nullptr) , m_settableActionsList() , m_pMenuRecentScripts(nullptr) , m_pPreviewDialog(nullptr) , m_pSettingsDialog(nullptr) , m_pBenchmarkDialog(nullptr) , m_pEncodeDialog(nullptr) , m_pTemplatesDialog(nullptr) , m_scriptFilePath() , m_lastSavedText() , m_pJobServerWatcherSocket(nullptr) , m_pGeometrySaveTimer(nullptr) , m_pReloadTextTimer(nullptr) { loadFonts(); vsedit::disableFontKerning(this); m_ui.setupUi(this); setWindowIcon(QIcon(":vsedit.ico")); if(m_pSettingsManager->inDarkMode()) { // Load qDarkStyle colors QFile styleSheetDark(":/dark/style.qss"); if(!styleSheetDark.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::critical(this, QString::fromUtf8("File open error"), QString::fromUtf8("Failed to open stylesheet file ") + styleSheetDark.errorString()); } qApp->setStyleSheet(styleSheetDark.readAll()); // With the current impl of the timeline slider // we have to relaunch anyway QPalette newPal(qApp->palette()); newPal.setColor(QPalette::Base, QColor(0, 0, 0)); newPal.setColor(QPalette::Highlight, QColor(128, 128, 128)); newPal.setColor(QPalette::Dark, QColor(192, 192, 192)); newPal.setColor(QPalette::Text, QColor(64, 192, 0)); qApp->setPalette(newPal); } #ifdef Q_OS_WIN else qApp->setStyle("fusion"); #endif m_pSettingsDialog = new SettingsDialog(m_pSettingsManager, nullptr); connect(m_pSettingsDialog, SIGNAL(signalSettingsChanged()), this, SLOT(slotSettingsChanged())); m_pVSScriptLibrary = new VSScriptLibrary(m_pSettingsManager, this); connect(m_pVSScriptLibrary, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotWriteLogMessage(int, const QString &))); m_pVapourSynthPluginsManager = new VapourSynthPluginsManager( m_pSettingsManager, m_pVSScriptLibrary->getVSAPI(), this); VSPluginsList vsPluginsList = m_pVapourSynthPluginsManager->pluginsList(); m_ui.scriptEdit->setPluginsList(vsPluginsList); m_ui.scriptEdit->setSettingsManager(m_pSettingsManager); connect(m_ui.scriptEdit, SIGNAL(textChanged()), this, SLOT(slotEditorTextChanged())); connect(m_ui.scriptEdit, SIGNAL(modificationChanged(bool)), this, SLOT(slotChangeWindowTitle())); connect(m_ui.scriptEdit, SIGNAL(signalScriptFileDropped(const QString &, bool *)), this, SLOT(slotScriptFileDropped(const QString &, bool *))); m_ui.logView->setName("main_log"); m_ui.logView->setSettingsManager(m_pSettingsManager); m_ui.logView->loadSettings(); m_pPreviewDialog = new PreviewDialog(m_pSettingsManager, m_pVSScriptLibrary); connect(m_pPreviewDialog, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotWriteLogMessage(int, const QString &))); connect(m_pPreviewDialog, SIGNAL(signalPasteIntoScriptAtNewLine(const QString &)), this, SLOT(slotInsertTextIntoScriptAtNewLine(const QString &))); connect(m_pPreviewDialog, SIGNAL(signalPasteIntoScriptAtCursor(const QString &)), this, SLOT(slotInsertTextIntoScriptAtCursor(const QString &))); connect(m_pSettingsDialog, SIGNAL(signalSettingsChanged()), m_pPreviewDialog, SLOT(slotSettingsChanged())); connect(m_ui.scriptEdit, &QPlainTextEdit::textChanged, m_pPreviewDialog, &PreviewDialog::slotScriptTextChanged); m_pBenchmarkDialog = new ScriptBenchmarkDialog(m_pSettingsManager, m_pVSScriptLibrary); connect(m_pBenchmarkDialog, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotWriteLogMessage(int, const QString &))); m_pEncodeDialog = new EncodeDialog(m_pSettingsManager, m_pVSScriptLibrary); connect(m_pEncodeDialog, SIGNAL(signalWriteLogMessage(const QString &, const QString &)), this, SLOT(slotWriteLogMessage(const QString &, const QString &))); m_pTemplatesDialog = new TemplatesDialog(m_pSettingsManager); m_pTemplatesDialog->setPluginsList(vsPluginsList); connect(m_pTemplatesDialog, SIGNAL(signalPasteCodeSnippet(const QString &)), this, SLOT(slotInsertTextIntoScriptAtNewLine(const QString &))); m_orphanQObjects = { (QObject **)&m_pPreviewDialog, (QObject **)&m_pSettingsDialog, (QObject **)&m_pBenchmarkDialog, (QObject **)&m_pEncodeDialog, (QObject **)&m_pTemplatesDialog }; m_pJobServerWatcherSocket = new JobServerWatcherSocket(this); connect(m_pJobServerWatcherSocket, SIGNAL(signalWriteLogMessage(const QString &, const QString &)), this, SLOT(slotWriteLogMessage(const QString &, const QString &))); m_pGeometrySaveTimer = new QTimer(this); m_pGeometrySaveTimer->setInterval(DEFAULT_WINDOW_GEOMETRY_SAVE_DELAY); connect(m_pGeometrySaveTimer, &QTimer::timeout, this, &MainWindow::slotSaveGeometry); m_pReloadTextTimer = new QTimer(this); m_pReloadTextTimer->setInterval(500); connect(m_pReloadTextTimer, &QTimer::timeout, this, &MainWindow::slotReloadTextFromDisk); createActionsAndMenus(); slotChangeWindowTitle(); m_windowGeometry = m_pSettingsManager->getMainWindowGeometry(); if(!m_windowGeometry.isEmpty()) restoreGeometry(m_windowGeometry); if(m_pSettingsManager->getMainWindowMaximized()) showMaximized(); loadStartUpScript(); if(m_pSettingsManager->getReloadScriptFromDisk()) m_pReloadTextTimer->start(); } // END OF MainWindow::MainWindow() //============================================================================== MainWindow::~MainWindow() { if(m_pGeometrySaveTimer->isActive()) { m_pGeometrySaveTimer->stop(); slotSaveGeometry(); } if(m_pReloadTextTimer->isActive()) m_pReloadTextTimer->stop(); qInstallMessageHandler(0); destroyOrphanQObjects(); } // END OF MainWindow::~MainWindow() //============================================================================== void MainWindow::slotWriteLogMessage(int a_messageType, const QString & a_message) { QString style = vsMessageTypeToStyleName(a_messageType); slotWriteLogMessage(a_message, style); } // END OF void MainWindow::slotWriteLogMessage(int a_messageType, // const QString & a_message) //============================================================================== void MainWindow::slotWriteLogMessage(const QString & a_message, const QString & a_style) { QString debugTypes[] = { LOG_STYLE_DEBUG, LOG_STYLE_QT_DEBUG, LOG_STYLE_VS_DEBUG, }; if(m_pSettingsManager->getShowDebugMessages() || !vsedit::contains(debugTypes, a_style)) { m_ui.logView->addEntry(a_message, a_style); } QString fatalTypes[] = {LOG_STYLE_VS_FATAL, LOG_STYLE_QT_FATAL}; if(!vsedit::contains(fatalTypes, a_style)) return; QDateTime now = QDateTime::currentDateTime(); QString timeString = now.toString("hh:mm:ss.zzz"); QString dateString = now.toString("yyyy-MM-dd"); QString caption = QObject::tr("VapourSynth Editor fatal error!"); QString fullMessage = dateString + QString(" ") + timeString + QString("\n") + caption + QString("\n") + a_message; QString tempPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation); if(tempPath.isEmpty()) { QMessageBox::critical(nullptr, caption, fullMessage); return; } QString filePath = tempPath + QString("/") + QString("VapourSynth-Editor-crashlog-") + dateString + QString("-") + timeString.replace(':', '-') + QString(".html"); bool saved = m_ui.logView->saveHtml(filePath); if(!saved) { QMessageBox::critical(nullptr, caption, fullMessage); return; } QUrl fileUrl = QUrl::fromLocalFile(filePath); QDesktopServices::openUrl(fileUrl); } // END OF void MainWindow::slotWriteLogMessage(const QString & a_message, // const QString & a_style); //============================================================================== void MainWindow::slotInsertTextIntoScriptAtNewLine(const QString & a_text) { m_ui.scriptEdit->slotInsertTextAtNewLine(a_text); } // END OF void MainWindow::slotInsertTextIntoScriptAtNewLine( // const QString & a_text) //============================================================================== void MainWindow::slotInsertTextIntoScriptAtCursor(const QString & a_text) { m_ui.scriptEdit->insertPlainText(a_text); } // END OF void MainWindow::slotInsertTextIntoScriptAtCursor( // const QString & a_text) //============================================================================== void MainWindow::closeEvent(QCloseEvent * a_pEvent) { if(!safeToCloseFile()) { a_pEvent->ignore(); return; } destroyOrphanQObjects(); QMainWindow::closeEvent(a_pEvent); } // END OF void MainWindow::closeEvent(QCloseEvent * a_pEvent) //============================================================================== void MainWindow::moveEvent(QMoveEvent * a_pEvent) { QMainWindow::moveEvent(a_pEvent); saveGeometryDelayed(); } // END OF void MainWindow::moveEvent(QMoveEvent * a_pEvent) //============================================================================== void MainWindow::resizeEvent(QResizeEvent * a_pEvent) { QMainWindow::resizeEvent(a_pEvent); saveGeometryDelayed(); } // END OF void MainWindow::resizeEvent(QResizeEvent * a_pEvent) //============================================================================== void MainWindow::changeEvent(QEvent * a_pEvent) { if(a_pEvent->type() == QEvent::WindowStateChange) { if(isMaximized()) m_pSettingsManager->setMainWindowMaximized(true); else m_pSettingsManager->setMainWindowMaximized(false); } QMainWindow::changeEvent(a_pEvent); } // END OF void MainWindow::changeEvent(QEvent * a_pEvent) //============================================================================== void MainWindow::slotNewScript() { if(!safeToCloseFile()) return; QString newScriptTemplate = m_pSettingsManager->getNewScriptTemplate(); m_scriptFilePath.clear(); m_lastSavedText = newScriptTemplate; m_ui.scriptEdit->setPlainText(newScriptTemplate); m_ui.scriptEdit->moveCursor(QTextCursor::End); m_ui.scriptEdit->setModified(true); m_pBenchmarkDialog->resetSavedRange(); } // END OF void MainWindow::slotNewScript() //============================================================================== bool MainWindow::slotSaveScript() { if(!m_ui.scriptEdit->isModified()) return false; if(m_scriptFilePath.isEmpty()) { slotSaveScriptAs(); return false; } return saveScriptToFile(m_scriptFilePath); } // END OF bool MainWindow::slotSaveScript() //============================================================================== bool MainWindow::slotSaveScriptAs() { QString offeredFilePath = m_scriptFilePath; if(offeredFilePath.isEmpty()) { QFileInfo fileInfo(m_pSettingsManager->getLastUsedPath()); offeredFilePath = fileInfo.absoluteDir().path() + tr("/Untitled.vpy"); } QString filePath = QFileDialog::getSaveFileName(this, tr("Save VapourSynth script"), offeredFilePath, tr("VapourSynth script (*.vpy);;All files (*)")); if(!filePath.isEmpty()) { bool success = saveScriptToFile(filePath); // Copy bookmarks if((!success) || offeredFilePath.isEmpty()) return success; QFile::copy(offeredFilePath + TIMELINE_BOOKMARKS_FILE_SUFFIX, filePath + TIMELINE_BOOKMARKS_FILE_SUFFIX); } return false; } // END OF bool MainWindow::slotSaveScriptAs() //============================================================================== bool MainWindow::slotOpenScript() { if(!safeToCloseFile()) return false; QFileInfo fileInfo(m_pSettingsManager->getLastUsedPath()); QString offeredPath = fileInfo.absoluteDir().path(); QString filePath = QFileDialog::getOpenFileName(this, tr("Open VapourSynth script"), offeredPath, tr("VapourSynth script (*.vpy);;All files (*)")); return loadScriptFromFile(filePath); } // END OF bool MainWindow::slotOpenScript() //============================================================================== void MainWindow::slotTemplates() { m_pTemplatesDialog->call(); } // END OF void MainWindow::slotTemplates() //============================================================================== void MainWindow::slotPreview() { if(m_pPreviewDialog->busy()) { QString message = tr("Preview dialog appears busy processing " "frames. Please stop any active actions in the dialog and wait " "for script processor to finish processing."); m_ui.logView->addEntry(message, LOG_STYLE_WARNING); return; } m_pPreviewDialog->previewScript(m_ui.scriptEdit->text(), m_scriptFilePath); } // END OF void MainWindow::slotPreview() //============================================================================== void MainWindow::slotCheckScript() { VapourSynthScriptProcessor tempProcessor(m_pSettingsManager, m_pVSScriptLibrary, this); const VSAPI * cpVSAPI = m_pVSScriptLibrary->getVSAPI(); connect(&tempProcessor, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotWriteLogMessage(int, const QString &))); bool correct = tempProcessor.initialize(m_ui.scriptEdit->text(), m_scriptFilePath, 0, ProcessReason::Check); if(correct) { VSNodeInfo info = tempProcessor.nodeInfo(); QString message = tr("Script was successfully evaluated. " "Output %1 info:\n").arg(info.isAudio() ? "audio" : "video"); message += vsedit::nodeInfoString(info, cpVSAPI); m_ui.logView->addEntry(message, LOG_STYLE_POSITIVE); } } // END OF void MainWindow::slotCheckScript() //============================================================================== void MainWindow::slotBenchmark() { if(m_pBenchmarkDialog->busy()) { QString message = tr("Benchmark dialog appears busy processing " "frames. Please stop any active actions in the dialog and wait " "for script processor to finish processing."); m_ui.logView->addEntry(message, LOG_STYLE_WARNING); return; } m_pBenchmarkDialog->initialize(m_ui.scriptEdit->text(), m_scriptFilePath); m_pBenchmarkDialog->call(); } // END OF void MainWindow::slotBenchmark() //============================================================================== void MainWindow::slotEncode() { if(m_pEncodeDialog->busy()) { m_pEncodeDialog->showActive(); return; } bool initialized = m_pEncodeDialog->initialize( m_ui.scriptEdit->text(), m_scriptFilePath); if(initialized) m_pEncodeDialog->showActive(); } // END OF void MainWindow::slotEncode() //============================================================================== void MainWindow::slotEnqueueEncodeJob() { if(m_scriptFilePath.isEmpty()) return; JobProperties properties; properties.type = JobType::EncodeScriptCLI; properties.scriptName = m_scriptFilePath; QByteArray message = vsedit::jsonMessage(WMSG_CLI_ENCODE_JOB, properties.toJson()); m_pJobServerWatcherSocket->sendMessage(message); } // END OF void MainWindow::slotEnqueueEncodeJob() //============================================================================== void MainWindow::slotJobs() { m_pJobServerWatcherSocket->sendMessage(WMSG_SHOW_WINDOW); } // END OF void MainWindow::slotJobs() //============================================================================== void MainWindow::slotToggleConsole() { #if defined(Q_OS_WIN) emit signalToggleAttachedConsole(); #endif } void MainWindow::slotAbout() { QResource aboutResource(":readme"); QByteArray aboutData((const char *)aboutResource.data(), aboutResource.size()); QString aboutString = QString::fromUtf8(aboutData); aboutString.append(QString("\n\nBuilt with Qt %1").arg(QT_VERSION_STR)); QString VSAPIInfo = m_pVSScriptLibrary->VSAPIInfo(); if(VSAPIInfo.isEmpty()) { aboutString.append(QString("\nVapourSynth not well configured.")); } else { aboutString.append(QString("\nVapourSynth API Version: " + VSAPIInfo)); QString VSSAPIInfo = m_pVSScriptLibrary->VSSAPIInfo(); aboutString.append(QString("\nVSScript API Version: " + VSSAPIInfo)); } QMessageBox msgBox(this); msgBox.setText(aboutString); msgBox.setWindowTitle("About VapourSynth Editor"); vsedit::disableFontKerning(&msgBox); msgBox.exec(); } // END OF void MainWindow::slotAbout() //============================================================================== void MainWindow::slotChangeWindowTitle() { QString windowTitleText = QString::fromUtf8("VapourSynth Editor - "); if(m_scriptFilePath.isEmpty()) windowTitleText += QString::fromUtf8("(Untitled)"); else windowTitleText += m_scriptFilePath; if(m_ui.scriptEdit->isModified()) windowTitleText += "*"; setWindowTitle(windowTitleText); } // END OF void MainWindow::slotChangeWindowTitle() //============================================================================== void MainWindow::slotEditorTextChanged() { bool textMatchesSaved = (m_lastSavedText == m_ui.scriptEdit->text()); m_ui.scriptEdit->setModified(!textMatchesSaved); slotChangeWindowTitle(); } // END OF void MainWindow::slotEditorTextChanged() //============================================================================== void MainWindow::slotOpenRecentScriptActionTriggered() { QAction * pAction = qobject_cast(sender()); if(pAction == nullptr) return; if(!safeToCloseFile()) return; loadScriptFromFile(pAction->data().toString()); } // END OF bool MainWindow::safeToCloseFile() //============================================================================== void MainWindow::slotSettingsChanged() { QKeySequence hotkey; for(QAction * pAction : m_settableActionsList) { hotkey = m_pSettingsManager->getHotkey(pAction->data().toString()); pAction->setShortcut(hotkey); } m_pVapourSynthPluginsManager->slotRefill(m_pVSScriptLibrary->getVSAPI()); VSPluginsList vsPluginsList = m_pVapourSynthPluginsManager->pluginsList(); m_ui.scriptEdit->setPluginsList(vsPluginsList); m_ui.scriptEdit->slotLoadSettings(); m_pTemplatesDialog->setPluginsList(vsPluginsList); m_pTemplatesDialog->slotLoadSettings(); if(m_pSettingsManager->getReloadScriptFromDisk()) m_pReloadTextTimer->start(500); else m_pReloadTextTimer->stop(); } // END OF void MainWindow::slotSettingsChanged() //============================================================================== void MainWindow::slotScriptFileDropped(const QString & a_filePath, bool * a_pHandled) { *a_pHandled = true; if(!safeToCloseFile()) return; loadScriptFromFile(a_filePath); } // END OF void MainWindow::slotScriptFileDropped(const QString & a_filePath, // bool * a_pHandled) //============================================================================== void MainWindow::slotReloadTextFromDisk() { if(m_ui.scriptEdit->isModified()) return; if(m_scriptFilePath.isEmpty()) return; QFile scriptFile(m_scriptFilePath); bool loadSuccess = scriptFile.open(QIODevice::ReadOnly | QIODevice::Text); if(!loadSuccess) return; QByteArray utf8Script = scriptFile.readAll(); QString scriptText = QString::fromUtf8(utf8Script); if(scriptText.isEmpty()) // To prevent an occasional bug? return; if(scriptText == m_ui.scriptEdit->text()) return; QPoint pos = m_ui.scriptEdit->cursorPosition(); m_ui.scriptEdit->setPlainText(scriptText); m_ui.scriptEdit->setCursorPosition(pos); m_ui.scriptEdit->setModified(false); m_pBenchmarkDialog->resetSavedRange(); } void MainWindow::slotSaveGeometry() { m_pGeometrySaveTimer->stop(); m_pSettingsManager->setMainWindowGeometry(m_windowGeometry); } // END OF void MainWindow::slotSaveGeometry() //============================================================================== void MainWindow::createActionsAndMenus() { struct ActionToCreate { QAction ** ppAction; const char * id; QObject * pObjectToConnect; const char * slotToConnect; }; ActionToCreate actionsToCreate[] = { {&m_pActionNewScript, ACTION_ID_NEW_SCRIPT, this, SLOT(slotNewScript())}, {&m_pActionOpenScript, ACTION_ID_OPEN_SCRIPT, this, SLOT(slotOpenScript())}, {&m_pActionSaveScript, ACTION_ID_SAVE_SCRIPT, this, SLOT(slotSaveScript())}, {&m_pActionSaveScriptAs, ACTION_ID_SAVE_SCRIPT_AS, this, SLOT(slotSaveScriptAs())}, {&m_pActionExit, ACTION_ID_EXIT, this, SLOT(close())}, {&m_pActionTemplates, ACTION_ID_TEMPLATES, this, SLOT(slotTemplates())}, {&m_pActionSettings, ACTION_ID_SETTINGS, m_pSettingsDialog, SLOT(slotCall())}, {&m_pActionPreview, ACTION_ID_PREVIEW, this, SLOT(slotPreview())}, {&m_pActionCheckScript, ACTION_ID_CHECK_SCRIPT, this, SLOT(slotCheckScript())}, {&m_pActionBenchmark, ACTION_ID_BENCHMARK, this, SLOT(slotBenchmark())}, {&m_pActionEncode, ACTION_ID_CLI_ENCODE, this, SLOT(slotEncode())}, {&m_pActionEnqueueEncodeJob, ACTION_ID_ENQUEUE_ENCODE_JOB, this, SLOT(slotEnqueueEncodeJob())}, {&m_pActionJobs, ACTION_ID_JOBS, this, SLOT(slotJobs())}, #if defined(Q_OS_WIN) {&m_pActionConsole, ACTION_ID_TOGGLE_CONSOLE, this, SLOT(slotToggleConsole())}, #endif {&m_pActionAbout, ACTION_ID_ABOUT, this, SLOT(slotAbout())}, }; for(ActionToCreate & item : actionsToCreate) { QAction * pAction = m_pSettingsManager->createStandardAction( item.id, this); *item.ppAction = pAction; m_settableActionsList.push_back(pAction); connect(pAction, SIGNAL(triggered()), item.pObjectToConnect, item.slotToConnect); } //------------------------------------------------------------------------------ QMenu * pFileMenu = m_ui.menuBar->addMenu(tr("File")); vsedit::disableFontKerning(pFileMenu); pFileMenu->addAction(m_pActionNewScript); pFileMenu->addAction(m_pActionOpenScript); pFileMenu->addAction(m_pActionSaveScript); pFileMenu->addAction(m_pActionSaveScriptAs); pFileMenu->addSeparator(); m_pMenuRecentScripts = new QMenu(tr("Recent scripts"), this); vsedit::disableFontKerning(m_pMenuRecentScripts); pFileMenu->addMenu(m_pMenuRecentScripts); fillRecentScriptsMenu(); pFileMenu->addSeparator(); pFileMenu->addAction(m_pActionExit); //------------------------------------------------------------------------------ QMenu * pEditMenu = m_ui.menuBar->addMenu(tr("Edit")); vsedit::disableFontKerning(pEditMenu); std::vector editorActions = m_ui.scriptEdit->actionsForMenu(); for(QAction * pAction : editorActions) pEditMenu->addAction(pAction); pEditMenu->addSeparator(); pEditMenu->addAction(m_pActionTemplates); pEditMenu->addAction(m_pActionSettings); //------------------------------------------------------------------------------ QMenu * pScriptMenu = m_ui.menuBar->addMenu(tr("Script")); vsedit::disableFontKerning(pScriptMenu); pScriptMenu->addAction(m_pActionPreview); pScriptMenu->addAction(m_pActionCheckScript); pScriptMenu->addAction(m_pActionBenchmark); pScriptMenu->addAction(m_pActionEncode); pScriptMenu->addAction(m_pActionEnqueueEncodeJob); pScriptMenu->addAction(m_pActionJobs); #if defined(Q_OS_WIN) pScriptMenu->addSeparator(); pScriptMenu->addAction(m_pActionConsole); #endif //------------------------------------------------------------------------------ QMenu * pHelpMenu = m_ui.menuBar->addMenu(tr("Help")); vsedit::disableFontKerning(pHelpMenu); pHelpMenu->addAction(m_pActionAbout); } // END OF void MainWindow::createActionsAndMenus() //============================================================================== void MainWindow::fillRecentScriptsMenu() { m_pMenuRecentScripts->clear(); QStringList recentSciptsList = m_pSettingsManager->getRecentFilesList(); for(const QString & filePath : recentSciptsList) { QAction * pAction = new QAction(m_pMenuRecentScripts); pAction->setIconText(filePath); pAction->setData(filePath); m_pMenuRecentScripts->addAction(pAction); connect(pAction, SIGNAL(triggered()), this, SLOT(slotOpenRecentScriptActionTriggered())); } } // END OF void MainWindow::fillRecentScriptsMenu() //============================================================================== bool MainWindow::saveScriptToFile(const QString& a_filePath) { if(a_filePath.isEmpty()) return false; QFile scriptFile(a_filePath); bool openSuccess = scriptFile.open(QIODevice::WriteOnly | QIODevice::Text); if(!openSuccess) { QMessageBox::critical(this, QString::fromUtf8("File open error"), QString::fromUtf8("Failed to open the file ") + a_filePath + QString::fromUtf8("for writing!")); return false; } QByteArray utf8Script = m_ui.scriptEdit->text().toUtf8(); qint64 writtenBytes = scriptFile.write(utf8Script); if(writtenBytes != utf8Script.size()) { QMessageBox::critical(this, QString::fromUtf8("File write error"), QString::fromUtf8("Error while writing to the file ") + a_filePath); return false; } setCurrentScriptFilePath(a_filePath); m_lastSavedText = m_ui.scriptEdit->text(); m_ui.scriptEdit->setModified(false); return true; } // END OF bool MainWindow::saveScriptToFile(const QString& a_filePath) //============================================================================== bool MainWindow::loadScriptFromFile(const QString& a_filePath) { if(a_filePath.isEmpty()) return false; QFile scriptFile(a_filePath); bool loadSuccess = scriptFile.open(QIODevice::ReadOnly | QIODevice::Text); if(!loadSuccess) { QMessageBox::critical(this, QString::fromUtf8("File open error"), QString::fromUtf8("Failed to open the file %1.").arg(a_filePath)); return false; } setCurrentScriptFilePath(a_filePath); QByteArray utf8Script = scriptFile.readAll(); QString scriptText = QString::fromUtf8(utf8Script); m_lastSavedText = scriptText; m_ui.scriptEdit->setPlainText(scriptText); m_pBenchmarkDialog->resetSavedRange(); return true; } // END OF bool MainWindow::loadScriptFromFile(const QString& a_filePath) //============================================================================== bool MainWindow::safeToCloseFile() { bool needPrompt = (m_pSettingsManager->getPromptToSaveChanges() && m_ui.scriptEdit->isModified()); if(!needPrompt) return true; QMessageBox quesBox(this); vsedit::disableFontKerning(&quesBox); quesBox.setWindowTitle(tr("Save script?")); if(m_scriptFilePath.isEmpty()) { quesBox.setText( tr("Would you like to save your script before closing?")); } else { quesBox.setText( tr("Would you like to save script \"%1\" before closing?") .arg(m_scriptFilePath)); } quesBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); int ret = quesBox.exec(); switch(ret) { case QMessageBox::Yes: return slotSaveScript(); case QMessageBox::Cancel: return false; default: return true; } } // END OF bool MainWindow::safeToCloseFile() //============================================================================== void MainWindow::setCurrentScriptFilePath(const QString & a_filePath) { if(m_scriptFilePath == a_filePath) return; m_scriptFilePath = a_filePath; m_pSettingsManager->setLastUsedPath(a_filePath); slotChangeWindowTitle(); fillRecentScriptsMenu(); } // END OF void MainWindow::setCurrentScriptFilePath(const QString & a_filePath) //============================================================================== void MainWindow::loadStartUpScript() { slotNewScript(); QStringList argumentsList = QCoreApplication::arguments(); if(argumentsList.size() > 1) loadScriptFromFile(argumentsList.at(1)); else if(m_pSettingsManager->getAutoLoadLastScript()) { QString lastUsedPath = m_pSettingsManager->getLastUsedPath(); if(!lastUsedPath.isEmpty()) loadScriptFromFile(lastUsedPath); } } // END OF void MainWindow::loadStartUpScript() //============================================================================== void MainWindow::loadFonts() { QResource digitalMiniFontResource(":/fonts/DigitalMini.ttf"); QByteArray digitalMiniFontData((const char *)digitalMiniFontResource.data(), digitalMiniFontResource.size()); QFontDatabase::addApplicationFontFromData(digitalMiniFontData); } // END OF void MainWindow::loadFonts() //============================================================================== void MainWindow::destroyOrphanQObjects() { for(QObject ** ppObject : m_orphanQObjects) { if(!ppObject) continue; if(!*ppObject) continue; delete *ppObject; *ppObject = nullptr; } } // END OF void MainWindow::destroyOrphanQObjects() //============================================================================== void MainWindow::saveGeometryDelayed() { QApplication::processEvents(); if(!isMaximized()) { m_windowGeometry = saveGeometry(); m_pGeometrySaveTimer->start(); } } // END OF void MainWindow::saveGeometryDelayed() //============================================================================== void MainWindow::reloadTexts() { QPoint pos = m_ui.scriptEdit->cursorPosition(); if(!loadScriptFromFile(m_scriptFilePath)) m_ui.scriptEdit->setModified(true); m_ui.scriptEdit->setCursorPosition(pos); } // END OF void MainWindow::reloadTexts() //============================================================================== ================================================ FILE: vsedit/src/main_window.h ================================================ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include class SettingsManager; class VapourSynthPluginsManager; class VSScriptLibrary; class PreviewDialog; class SettingsDialog; class ScriptBenchmarkDialog; class EncodeDialog; class TemplatesDialog; class JobServerWatcherSocket; class QTimer; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(SettingsManager *settings); virtual ~MainWindow(); public slots: void slotWriteLogMessage(int a_messageType, const QString & a_message); void slotWriteLogMessage(const QString & a_message, const QString & a_style = LOG_STYLE_DEFAULT); void slotInsertTextIntoScriptAtNewLine(const QString & a_text); void slotInsertTextIntoScriptAtCursor(const QString & a_text); signals: void signalToggleAttachedConsole(); protected: void closeEvent(QCloseEvent * a_pEvent) override; void moveEvent(QMoveEvent * a_pEvent) override; void resizeEvent(QResizeEvent * a_pEvent) override; void changeEvent(QEvent * a_pEvent) override; private slots: void slotNewScript(); bool slotSaveScript(); bool slotSaveScriptAs(); bool slotOpenScript(); void slotTemplates(); void slotPreview(); void slotCheckScript(); void slotBenchmark(); void slotEncode(); void slotEnqueueEncodeJob(); void slotJobs(); void slotToggleConsole(); void slotAbout(); void slotChangeWindowTitle(); void slotEditorTextChanged(); void slotOpenRecentScriptActionTriggered(); void slotSettingsChanged(); void slotScriptFileDropped(const QString & a_filePath, bool * a_pHandled); void slotSaveGeometry(); void slotReloadTextFromDisk(); private: void createActionsAndMenus(); void fillRecentScriptsMenu(); bool saveScriptToFile(const QString& a_filePath); bool loadScriptFromFile(const QString& a_filePath); bool safeToCloseFile(); void setCurrentScriptFilePath(const QString & a_filePath); void loadStartUpScript(); void loadFonts(); void destroyOrphanQObjects(); void saveGeometryDelayed(); void reloadTexts(); Ui::MainWindow m_ui; SettingsManager * m_pSettingsManager; VapourSynthPluginsManager * m_pVapourSynthPluginsManager; VSScriptLibrary * m_pVSScriptLibrary; QAction * m_pActionNewScript; QAction * m_pActionOpenScript; QAction * m_pActionSaveScript; QAction * m_pActionSaveScriptAs; QAction * m_pActionTemplates; QAction * m_pActionSettings; QAction * m_pActionPreview; QAction * m_pActionCheckScript; QAction * m_pActionBenchmark; QAction * m_pActionEncode; QAction * m_pActionEnqueueEncodeJob; QAction * m_pActionJobs; QAction * m_pActionConsole; QAction * m_pActionExit; QAction * m_pActionAbout; std::vector m_settableActionsList; QMenu * m_pMenuRecentScripts; PreviewDialog * m_pPreviewDialog; SettingsDialog * m_pSettingsDialog; ScriptBenchmarkDialog * m_pBenchmarkDialog; EncodeDialog * m_pEncodeDialog; TemplatesDialog * m_pTemplatesDialog; QString m_scriptFilePath; QString m_lastSavedText; std::vector m_orphanQObjects; JobServerWatcherSocket * m_pJobServerWatcherSocket; QTimer * m_pGeometrySaveTimer; QByteArray m_windowGeometry; QTimer * m_pReloadTextTimer; }; #endif // MAINWINDOW_H ================================================ FILE: vsedit/src/main_window.ui ================================================ MainWindow 0 0 847 721 VapourSynth Editor 0 0 0 0 0 0 0 847 21 QDockWidget::NoDockWidgetFeatures Qt::BottomDockWidgetArea Log 8 0 0 0 0 0 Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse ScriptEditor QPlainTextEdit
../../src/script_editor/script_editor.h
VSEditorLog QTextEdit
../../common-src/log/vs_editor_log.h
================================================ FILE: vsedit/src/preview/preview_advanced_settings_dialog.cpp ================================================ #include "preview_advanced_settings_dialog.h" #include "../../../common-src/settings/settings_manager.h" #include "../../../common-src/helpers.h" #include //============================================================================== PreviewAdvancedSettingsDialog::PreviewAdvancedSettingsDialog( SettingsManager * a_pSettingsManager, QWidget * a_pParent) : QDialog(a_pParent, Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint) , m_pSettingsManager(a_pSettingsManager) { vsedit::disableFontKerning(this); m_ui.setupUi(this); setWindowIcon(QIcon(":settings.png")); m_ui.yuvMatrixCoefficientsComboBox->addItem( tr("709"), (int)YuvMatrixCoefficients::m709); m_ui.yuvMatrixCoefficientsComboBox->addItem( tr("470BG"), (int)YuvMatrixCoefficients::m470BG); m_ui.yuvMatrixCoefficientsComboBox->addItem( tr("170M"), (int)YuvMatrixCoefficients::m170M); m_ui.yuvMatrixCoefficientsComboBox->addItem( tr("2020 NCL"), (int)YuvMatrixCoefficients::m2020_NCL); m_ui.chromaResamplingFilterComboBox->addItem(tr("Point"), (int)ResamplingFilter::Point); m_ui.chromaResamplingFilterComboBox->addItem(tr("Bilinear"), (int)ResamplingFilter::Bilinear); m_ui.chromaResamplingFilterComboBox->addItem(tr("Bicubic"), (int)ResamplingFilter::Bicubic); m_ui.chromaResamplingFilterComboBox->addItem(tr("Spline16"), (int)ResamplingFilter::Spline16); m_ui.chromaResamplingFilterComboBox->addItem(tr("Spline36"), (int)ResamplingFilter::Spline36); m_ui.chromaResamplingFilterComboBox->addItem(tr("Spline64"), (int)ResamplingFilter::Spline64); m_ui.chromaResamplingFilterComboBox->addItem(tr("Lanczos"), (int)ResamplingFilter::Lanczos); m_ui.chromaPlacementComboBox->addItem(tr("Left / MPEG2"), (int)ChromaPlacement::LEFT); m_ui.chromaPlacementComboBox->addItem(tr("Center / MPEG1 / JPEG"), (int)ChromaPlacement::CENTER); m_ui.chromaPlacementComboBox->addItem(tr("Top-left"), (int)ChromaPlacement::TOP_LEFT); m_ui.ditherTypeComboBox->addItem("Error Diffusion", (int)DitherType::ERROR_DIFFUSION); m_ui.ditherTypeComboBox->addItem("None", (int)DitherType::NONE); m_ui.ditherTypeComboBox->addItem("Ordered", (int)DitherType::ORDERED); m_ui.ditherTypeComboBox->addItem("Random", (int)DitherType::RANDOM); m_ui.syncOutputComboBox->addItem("Frame", (int)SyncOutputNodesMode::Frame); m_ui.syncOutputComboBox->addItem("Timestamp", (int)SyncOutputNodesMode::Time); m_ui.syncOutputComboBox->addItem("From Timeline", (int)SyncOutputNodesMode::FromTimeLine); connect(m_ui.okButton, SIGNAL(clicked()), this, SLOT(slotOk())); connect(m_ui.applyButton, SIGNAL(clicked()), this, SLOT(slotApply())); connect(m_ui.resetToDefaultButton, SIGNAL(clicked()), this, SLOT(slotResetToDefault())); connect(m_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_ui.silentSnapshotCheckBox, SIGNAL(clicked()), this, SLOT(slotSilentSnapshotChanged())); connect(m_ui.argumentsHelpButton, SIGNAL(clicked()), this, SLOT(slotArgumentsHelpButtonPressed())); } // END OF PreviewAdvancedSettingsDialog::PreviewAdvancedSettingsDialog( // SettingsManager * a_pSettingsManager, QWidget * a_pParent) //============================================================================== PreviewAdvancedSettingsDialog::~PreviewAdvancedSettingsDialog() { } // END OF PreviewAdvancedSettingsDialog::~PreviewAdvancedSettingsDialog() //============================================================================== void PreviewAdvancedSettingsDialog::slotCall() { YuvMatrixCoefficients matrix = m_pSettingsManager->getYuvMatrixCoefficients(); int comboIndex = m_ui.yuvMatrixCoefficientsComboBox->findData((int)matrix); if(comboIndex != -1) m_ui.yuvMatrixCoefficientsComboBox->setCurrentIndex(comboIndex); ResamplingFilter filter = m_pSettingsManager->getChromaResamplingFilter(); comboIndex = m_ui.chromaResamplingFilterComboBox->findData((int)filter); if(comboIndex != -1) m_ui.chromaResamplingFilterComboBox->setCurrentIndex(comboIndex); ChromaPlacement chromaPlacement = m_pSettingsManager->getChromaPlacement(); comboIndex = m_ui.chromaPlacementComboBox->findData((int)chromaPlacement); if(comboIndex != -1) m_ui.chromaPlacementComboBox->setCurrentIndex(comboIndex); DitherType ditherType = m_pSettingsManager->getDitherType(); comboIndex = m_ui.ditherTypeComboBox->findData((int)ditherType); if(comboIndex != -1) m_ui.ditherTypeComboBox->setCurrentIndex(comboIndex); m_ui.bicubicFilterParameterBSpinBox->setValue( m_pSettingsManager->getBicubicFilterParameterB()); m_ui.bicubicFilterParameterCSpinBox->setValue( m_pSettingsManager->getBicubicFilterParameterC()); m_ui.lanczosFilterTapsSpinBox->setValue( m_pSettingsManager->getLanczosFilterTaps()); bool silentSnapshotEnabled = m_pSettingsManager->getSilentSnapshot(); m_ui.silentSnapshotCheckBox->setChecked(silentSnapshotEnabled); m_ui.saveSnapshotTemplateLineEdit->setText( m_pSettingsManager->getSnapshotTemplate()); m_ui.saveSnapshotTemplateLineEdit->setEnabled(silentSnapshotEnabled); SyncOutputNodesMode syncOutputMode = m_pSettingsManager->getSyncOutputMode(); comboIndex = m_ui.syncOutputComboBox->findData((int)syncOutputMode); if(comboIndex != -1) m_ui.syncOutputComboBox->setCurrentIndex(comboIndex); show(); } // END OF void PreviewAdvancedSettingsDialog::slotCall() //============================================================================== void PreviewAdvancedSettingsDialog::slotOk() { slotApply(); accept(); } // END OF void PreviewAdvancedSettingsDialog::slotOk() //============================================================================== void PreviewAdvancedSettingsDialog::slotApply() { m_pSettingsManager->setChromaResamplingFilter((ResamplingFilter) m_ui.chromaResamplingFilterComboBox->currentData().toInt()); m_pSettingsManager->setYuvMatrixCoefficients((YuvMatrixCoefficients) m_ui.yuvMatrixCoefficientsComboBox->currentData().toInt()); m_pSettingsManager->setChromaPlacement((ChromaPlacement) m_ui.chromaPlacementComboBox->currentData().toInt()); m_pSettingsManager->setBicubicFilterParameterB( m_ui.bicubicFilterParameterBSpinBox->value()); m_pSettingsManager->setBicubicFilterParameterC( m_ui.bicubicFilterParameterCSpinBox->value()); m_pSettingsManager->setLanczosFilterTaps( m_ui.lanczosFilterTapsSpinBox->value()); m_pSettingsManager->setDitherType((DitherType) m_ui.ditherTypeComboBox->currentData().toInt()); m_pSettingsManager->setSilentSnapshot( m_ui.silentSnapshotCheckBox->isChecked()); m_pSettingsManager->setSnapshotTemplate( m_ui.saveSnapshotTemplateLineEdit->text()); m_pSettingsManager->setSyncOutputMode((SyncOutputNodesMode) m_ui.syncOutputComboBox->currentData().toInt()); emit signalSettingsChanged(); } // END OF void PreviewAdvancedSettingsDialog::slotApply() //============================================================================== void PreviewAdvancedSettingsDialog::slotResetToDefault() { YuvMatrixCoefficients matrix = DEFAULT_YUV_MATRIX_COEFFICIENTS; int comboIndex = m_ui.yuvMatrixCoefficientsComboBox->findData((int)matrix); if(comboIndex != -1) m_ui.yuvMatrixCoefficientsComboBox->setCurrentIndex(comboIndex); ResamplingFilter filter = DEFAULT_CHROMA_RESAMPLING_FILTER; comboIndex = m_ui.chromaResamplingFilterComboBox->findData((int)filter); if(comboIndex != -1) m_ui.chromaResamplingFilterComboBox->setCurrentIndex(comboIndex); ChromaPlacement chromaPlacement = DEFAULT_CHROMA_PLACEMENT; comboIndex = m_ui.chromaPlacementComboBox->findData((int)chromaPlacement); if(comboIndex != -1) m_ui.chromaPlacementComboBox->setCurrentIndex(comboIndex); m_ui.bicubicFilterParameterBSpinBox->setValue( DEFAULT_BICUBIC_FILTER_PARAMETER_B); m_ui.bicubicFilterParameterCSpinBox->setValue( DEFAULT_BICUBIC_FILTER_PARAMETER_C); m_ui.lanczosFilterTapsSpinBox->setValue( DEFAULT_LANCZOS_FILTER_TAPS); DitherType ditherType = DEFAULT_DITHER_TYPE; comboIndex = m_ui.ditherTypeComboBox->findData((int)ditherType); if(comboIndex != -1) m_ui.ditherTypeComboBox->setCurrentIndex(comboIndex); SyncOutputNodesMode syncOutputMode = DEFAULT_SYNC_OUTPUT_MODE; comboIndex = m_ui.syncOutputComboBox->findData((int)syncOutputMode); if(comboIndex != -1) m_ui.syncOutputComboBox->setCurrentIndex(comboIndex); m_ui.silentSnapshotCheckBox->setChecked(DEFAULT_SILENT_SNAPSHOT); m_ui.saveSnapshotTemplateLineEdit->setText(DEFAULT_SNAPSHOT_TEMPLATE); m_ui.saveSnapshotTemplateLineEdit->setEnabled(false); } // END OF void PreviewAdvancedSettingsDialog::slotResetToDefault() //============================================================================== void PreviewAdvancedSettingsDialog::slotSilentSnapshotChanged() { bool silentSnapshotEnabled = m_ui.silentSnapshotCheckBox->isChecked(); m_ui.saveSnapshotTemplateLineEdit->setEnabled(silentSnapshotEnabled); emit signalSilentSnapshotChanged(); } void PreviewAdvancedSettingsDialog::slotArgumentsHelpButtonPressed() { QString argumentsHelpString = tr("Use the following placeholders:"); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{f}")).arg(tr("script file path")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{d}")).arg(tr("script file directory")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{n}")).arg(tr("script file name")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{o}")).arg(tr("output index")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{i}")).arg(tr("frame number")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{t}")).arg(tr("timestamp")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{nm}")).arg(tr("clip name")); argumentsHelpString += QString("\n%1 - %2") .arg(tr("{sc}")).arg(tr("scene name")); QString title = tr("Snapshot template arguments"); QMessageBox msgBox(this); msgBox.setWindowTitle(title); msgBox.setText(argumentsHelpString); vsedit::disableFontKerning(&msgBox); msgBox.exec(); } ================================================ FILE: vsedit/src/preview/preview_advanced_settings_dialog.h ================================================ #ifndef PREVIEW_ADVANCED_SETTINGS_DIALOG_H_INCLUDED #define PREVIEW_ADVANCED_SETTINGS_DIALOG_H_INCLUDED #include class SettingsManager; class PreviewAdvancedSettingsDialog : public QDialog { Q_OBJECT public: PreviewAdvancedSettingsDialog(SettingsManager * a_pSettingsManager, QWidget * a_pParent = nullptr); virtual ~PreviewAdvancedSettingsDialog(); public slots: void slotCall(); signals: void signalSettingsChanged(); void signalSilentSnapshotChanged(); private slots: void slotOk(); void slotApply(); void slotResetToDefault(); void slotSilentSnapshotChanged(); void slotArgumentsHelpButtonPressed(); private: Ui::PreviewAdvancedSettingsDialog m_ui; SettingsManager * m_pSettingsManager; }; #endif // PREVIEW_ADVANCED_SETTINGS_DIALOG_H_INCLUDED ================================================ FILE: vsedit/src/preview/preview_advanced_settings_dialog.ui ================================================ PreviewAdvancedSettingsDialog 0 0 364 181 Preview Advanced Settings 6 6 6 6 4 Settings below may be overriden by frame properties. Bicubic filter parameter b: Chroma resampling filter: Y'CbCr / Y'UV matrix coefficients: Chroma placement: Lanczos filter taps: Bicubic filter parameter c: 6 -100000.000000000000000 100000.000000000000000 0.000001000000000 0.333333000000000 6 -100000.000000000000000 100000.000000000000000 0.000001000000000 0.333333000000000 Sync output nodes with: OK true Apply Reset to default Cancel Dither type: 2 4096 3 Silently save snapshots with template: :/information.png:/information.png ================================================ FILE: vsedit/src/preview/preview_area.cpp ================================================ #include "preview_area.h" #include "scroll_navigator.h" #include #include #include #include #include #include #include #include //============================================================================== PreviewArea::PreviewArea(QWidget * a_pParent) : QScrollArea(a_pParent) , m_pPreviewLabel(nullptr) , m_pScrollNavigator(nullptr) , m_draggingPreview(false) , m_lastCursorPos(0, 0) , m_lastPreviewLabelPos(0, 0) , m_lastScenePos(0.0, 0.0) , m_newToPreviewer(false) { m_pPreviewLabel = new QLabel(this); m_pPreviewLabel->setPixmap(QPixmap()); m_pPreviewLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_pPreviewLabel->move(0, 0); QScrollArea::setWidget(m_pPreviewLabel); setWidgetResizable(true); m_pScrollNavigator = new ScrollNavigator(this); int scrollFrameWidth = frameWidth(); m_pScrollNavigator->move(pos() + QPoint(scrollFrameWidth, scrollFrameWidth)); m_pScrollNavigator->setVisible(false); setAttribute(Qt::WA_Hover, true); setMouseTracking(true); m_pPreviewLabel->setMouseTracking(true); } // END OF PreviewArea::PreviewArea(QWidget * a_pParent) //============================================================================== PreviewArea::~PreviewArea() { } // END OF PreviewArea::~PreviewArea() //============================================================================== void PreviewArea::setPixmap(const QPixmap & a_pixmap, bool a_isVideoFrame) { m_pPreviewLabel->setPixmap(a_pixmap); m_pixmapWidth = a_pixmap.width(); m_pixmapHeight = a_pixmap.height(); if(a_isVideoFrame && m_newToPreviewer) { m_newToPreviewer = false; QCoreApplication::processEvents(); QTimer::singleShot(0, this, SLOT(slotSetScrollBarPositions())); } } // END OF void PreviewArea::setPixmap(const QPixmap & a_pixmap) //============================================================================== void PreviewArea::checkMouseOverPreview(const QPointF & a_pixelPos) { if(!m_pPreviewLabel->underMouse()) return; double pX = a_pixelPos.x(); double pY = a_pixelPos.y(); if(pX < 0 || pY < 0 || pX >= pixmapWidth() || pY >= pixmapHeight()) return; emit signalMouseOverPoint(pX, pY); } // END OF void PreviewArea::checkMouseOverPreview( // const QPoint & a_globalMousePos) //============================================================================== void PreviewArea::slotScrollLeft() { horizontalScrollBar()->setValue(0); } // END OF void PreviewArea::slotScrollLeft() //============================================================================== void PreviewArea::slotScrollRight() { QCoreApplication::processEvents(); QScrollBar * pHorizontalScrollbar = horizontalScrollBar(); pHorizontalScrollbar->setValue(pHorizontalScrollbar->maximum()); } // END OF void PreviewArea::slotScrollRight() //============================================================================== void PreviewArea::slotScrollTop() { verticalScrollBar()->setValue(0); } // END OF void PreviewArea::slotScrollTop() //============================================================================== void PreviewArea::slotScrollBottom() { QCoreApplication::processEvents(); QScrollBar * pVerticalScrollbar = verticalScrollBar(); pVerticalScrollbar->setValue(pVerticalScrollbar->maximum()); } // END OF void PreviewArea::slotScrollBottom() //============================================================================== void PreviewArea::resizeEvent(QResizeEvent * a_pEvent) { QScrollArea::resizeEvent(a_pEvent); emit signalSizeChanged(); } // END OF void PreviewArea::resizeEvent(QResizeEvent * a_pEvent) //============================================================================== void PreviewArea::keyPressEvent(QKeyEvent * a_pEvent) { if(a_pEvent->modifiers() != Qt::NoModifier) { QScrollArea::keyPressEvent(a_pEvent); return; } int key = a_pEvent->key(); int wantedKeys[] = {Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down, Qt::Key_PageUp, Qt::Key_PageDown, Qt::Key_Home, Qt::Key_End}; int * pKeysEnd = wantedKeys + sizeof(wantedKeys) / sizeof(*wantedKeys); if(pKeysEnd != std::find(wantedKeys, pKeysEnd, key)) { a_pEvent->ignore(); return; } QScrollArea::keyPressEvent(a_pEvent); } // END OF void PreviewArea::keyPressEvent(QKeyEvent * a_pEvent) //============================================================================== void PreviewArea::wheelEvent(QWheelEvent * a_pEvent) { if(a_pEvent->modifiers() == Qt::ControlModifier) { emit signalCtrlWheel(a_pEvent->angleDelta()); a_pEvent->ignore(); return; } QScrollArea::wheelEvent(a_pEvent); } // END OF void PreviewArea::wheelEvent(QWheelEvent * a_pEvent) //============================================================================== void PreviewArea::mousePressEvent(QMouseEvent * a_pEvent) { if(a_pEvent->buttons() == Qt::LeftButton) { m_draggingPreview = true; m_lastCursorPos = a_pEvent->globalPosition().toPoint(); m_lastPreviewLabelPos = m_pPreviewLabel->pos(); m_pScrollNavigator->setVisible(true); drawScrollNavigator(); a_pEvent->accept(); return; } QScrollArea::mousePressEvent(a_pEvent); } // END OF void PreviewArea::mousePressEvent(QMouseEvent * a_pEvent) //============================================================================== void PreviewArea::mouseMoveEvent(QMouseEvent * a_pEvent) { if((a_pEvent->buttons() & Qt::LeftButton) && m_draggingPreview) { QPoint newCursorPos = a_pEvent->globalPosition().toPoint(); QPoint posDifference = newCursorPos - m_lastCursorPos; QPoint newPreviewLabelPos = m_lastPreviewLabelPos + posDifference; horizontalScrollBar()->setValue(-newPreviewLabelPos.x()); verticalScrollBar()->setValue(-newPreviewLabelPos.y()); drawScrollNavigator(); a_pEvent->accept(); return; } m_lastScenePos = a_pEvent->scenePosition(); checkMouseOverPreview(pixelPosition()); QScrollArea::mouseMoveEvent(a_pEvent); } // END OF void PreviewArea::mouseMoveEvent(QMouseEvent * a_pEvent) //============================================================================== void PreviewArea::mouseReleaseEvent(QMouseEvent * a_pEvent) { Qt::MouseButton releasedButton = a_pEvent->button(); if(releasedButton == Qt::LeftButton) { m_draggingPreview = false; m_pScrollNavigator->setVisible(false); a_pEvent->accept(); return; } else if(releasedButton == Qt::MiddleButton) emit signalMouseMiddleButtonReleased(); else if(releasedButton == Qt::RightButton) emit signalMouseRightButtonReleased(); QScrollArea::mouseReleaseEvent(a_pEvent); } void PreviewArea::enterEvent(QEnterEvent *a_pEvent) { m_lastScenePos = a_pEvent->scenePosition(); checkMouseOverPreview(pixelPosition()); QScrollArea::enterEvent(a_pEvent); setAttribute(Qt::WA_Hover, false); } // END OF void PreviewArea::mouseReleaseEvent(QMouseEvent * a_pEvent) //============================================================================== void PreviewArea::drawScrollNavigator() { int contentsWidth = this->pixmapWidth(); int contentsHeight = this->pixmapHeight(); int viewportX = -m_pPreviewLabel->x(); int viewportY = -m_pPreviewLabel->y(); int viewportWidth = viewport()->width(); int viewportHeight = viewport()->height(); m_pScrollNavigator->draw(contentsWidth, contentsHeight, viewportX, viewportY, viewportWidth, viewportHeight); } // END OF void PreviewArea::drawScrollNavigator() //============================================================================== QPointF PreviewArea::pixelPosition() const { QPoint lPos = m_pPreviewLabel->geometry().topLeft(); QMargins lMargin = contentsMargins(); QPoint mOffset = QPoint(lMargin.left(), lMargin.top()); return m_lastScenePos - lPos - mOffset; } QPoint PreviewArea::getScrollBarPositions() const { int x = horizontalScrollBar()->value(); int y = verticalScrollBar()->value(); return QPoint(x, y); } void PreviewArea::slotSetScrollBarPositions() { horizontalScrollBar()->setValue(m_lastScrollBarPos.x()); verticalScrollBar()->setValue(m_lastScrollBarPos.y()); } void PreviewArea::getScrollBarPositionsFromPreviewer(const QPoint & pos) { m_newToPreviewer = true; m_lastScrollBarPos = pos; } ================================================ FILE: vsedit/src/preview/preview_area.h ================================================ #ifndef PREVIEWAREA_H #define PREVIEWAREA_H #include #include #include class QLabel; class ScrollNavigator; class QKeyEvent; class QWheelEvent; class QMouseEvent; class QEnterEvent; class PreviewArea : public QScrollArea { Q_OBJECT public: PreviewArea(QWidget * a_pParent = nullptr); virtual ~PreviewArea(); void setWidget(QWidget * a_pWidget) = delete; int pixmapWidth() const { return m_pixmapWidth; } int pixmapHeight() const { return m_pixmapHeight; } void setPixmap(const QPixmap & a_pixmap, bool a_isVideoFrame = false); void checkMouseOverPreview(const QPointF & a_pixelPos); QPointF pixelPosition() const; QPoint getScrollBarPositions() const; void getScrollBarPositionsFromPreviewer(const QPoint & pos); public slots: void slotScrollLeft(); void slotScrollRight(); void slotScrollTop(); void slotScrollBottom(); void slotSetScrollBarPositions(); protected: void resizeEvent(QResizeEvent * a_pEvent) override; void keyPressEvent(QKeyEvent * a_pEvent) override; void wheelEvent(QWheelEvent * a_pEvent) override; void mousePressEvent(QMouseEvent * a_pEvent) override; void mouseMoveEvent(QMouseEvent * a_pEvent) override; void mouseReleaseEvent(QMouseEvent * a_pEvent) override; void enterEvent(QEnterEvent * a_pEvent) override; signals: void signalSizeChanged(); void signalCtrlWheel(QPoint a_angleDelta); void signalMouseMiddleButtonReleased(); void signalMouseRightButtonReleased(); void signalMouseOverPoint(double a_normX, double a_normY); private: void drawScrollNavigator(); QLabel * m_pPreviewLabel; ScrollNavigator * m_pScrollNavigator; bool m_draggingPreview; QPoint m_lastCursorPos; QPoint m_lastPreviewLabelPos; QPointF m_lastScenePos; int m_pixmapWidth = 0; int m_pixmapHeight = 0; bool m_newToPreviewer; QPoint m_lastScrollBarPos; }; #endif // PREVIEWAREA_H ================================================ FILE: vsedit/src/preview/preview_dialog.cpp ================================================ #include "preview_dialog.h" #include "../../../common-src/helpers.h" #include "../../../common-src/libp2p/p2p_api.h" #include "../../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../../../common-src/settings/settings_manager.h" #include "scroll_navigator.h" #include "../../../common-src/timeline_slider/timeline_slider.h" #include "preview_advanced_settings_dialog.h" #include "zoom_ratio_spinbox.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN // AUDIO #include #include // Random numbers std::random_device rd; std::mt19937_64 gen(rd()); std::uniform_int_distribution unif16(-32768, 32768); static inline uint16_t dither32to16(uint32_t a) { int64_t perturbed = VSMAX(VSMIN((int64_t)a + unif16(gen) + unif16(gen) + 32768, 0xFFFFFFFF), 0); uint16_t base = perturbed >> 16; return base; } static inline int16_t dither32Fto16(float a) { static double den = 65536 * 32767; float perturbed = VSMAX(VSMIN(a + 1.0f + (float)((unif16(gen) + unif16(gen)) / den), 2.0f), 0.0f); return int(perturbed * 32767.0f) - 32767; } PreviewDialog::AudioFrame::AudioFrame() : number(-1), outputIndex(-1), data(QByteArray()) { } PreviewDialog::AudioFrame::AudioFrame(int a_number, int a_outputIndex, QByteArray a_data) : number(a_number), outputIndex(a_outputIndex), data(a_data) { data.detach(); } bool PreviewDialog::AudioFrame::operator==(const AudioFrame &a_other) const { return ((number == a_other.number) && (outputIndex == a_other.outputIndex)); } #endif //============================================================================== #define BEGIN_CROP_VALUES_CHANGE \ if(m_changingCropValues) \ return; \ m_changingCropValues = true; #define END_CROP_VALUES_CHANGE \ m_changingCropValues = false; //============================================================================== const char TIMELINE_BOOKMARKS_FILE_SUFFIX[] = ".bookmarks"; //============================================================================== PreviewDialog::PreviewDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, bool a_inPreviewer, QWidget * a_pParent) : VSScriptProcessorDialog(a_pSettingsManager, a_pVSScriptLibrary, a_pParent) , m_pAdvancedSettingsDialog(nullptr) , m_frameExpected(0) , m_frameTimestampExpected(0) , m_frameShown(-1) , m_lastFrameRequestedForPlay(-1) , m_bigFrameStep(10) , m_cpFrame(nullptr) , m_cpPreviewFrame(nullptr) , m_changingCropValues(false) , m_pPreviewContextMenu(nullptr) , m_pActionFrameToClipboard(nullptr) , m_pActionSaveSnapshot(nullptr) , m_pActionToggleZoomPanel(nullptr) , m_pMenuZoomModes(nullptr) , m_pActionGroupZoomModes(nullptr) , m_pActionSetZoomModeNoZoom(nullptr) , m_pActionSetZoomModeFixedRatio(nullptr) , m_pActionSetZoomModeFitToFrame(nullptr) , m_pMenuZoomScaleModes(nullptr) , m_pActionGroupZoomScaleModes(nullptr) , m_pActionSetZoomScaleModeNearest(nullptr) , m_pActionSetZoomScaleModeBilinear(nullptr) , m_pActionToggleCropPanel(nullptr) , m_pActionToggleTimeLinePanel(nullptr) , m_pMenuTimeLineModes(nullptr) , m_pActionGroupTimeLineModes(nullptr) , m_pActionSetTimeLineModeTime(nullptr) , m_pActionSetTimeLineModeFrames(nullptr) , m_pActionTimeStepForward(nullptr) , m_pActionTimeStepBack(nullptr) , m_pActionPasteCropSnippetIntoScript(nullptr) , m_pActionAdvancedSettingsDialog(nullptr) , m_pActionToggleColorPicker(nullptr) , m_pActionPlay(nullptr) , m_pActionLoadChapters(nullptr) , m_pActionClearBookmarks(nullptr) , m_pActionBookmarkCurrentFrame(nullptr) , m_pActionUnbookmarkCurrentFrame(nullptr) , m_pActionGoToPreviousBookmark(nullptr) , m_pActionGoToNextBookmark(nullptr) , m_pActionPasteShownFrameNumberIntoScript(nullptr) , m_pActionJumpToFrame(nullptr) , m_pActionToggleFramePropsPanel(nullptr) , m_pActionSwitchToOutputIndex0(nullptr) , m_pActionSwitchToOutputIndex1(nullptr) , m_pActionSwitchToOutputIndex2(nullptr) , m_pActionSwitchToOutputIndex3(nullptr) , m_pActionSwitchToOutputIndex4(nullptr) , m_pActionSwitchToOutputIndex5(nullptr) , m_pActionSwitchToOutputIndex6(nullptr) , m_pActionSwitchToOutputIndex7(nullptr) , m_pActionSwitchToOutputIndex8(nullptr) , m_pActionSwitchToOutputIndex9(nullptr) , m_pActionSwitchToOutputIndex10(nullptr) , m_pActionSwitchToOutputIndex11(nullptr) , m_pActionSwitchToOutputIndex12(nullptr) , m_pActionSwitchToOutputIndex13(nullptr) , m_pActionSwitchToOutputIndex14(nullptr) , m_pActionSwitchToOutputIndex15(nullptr) , m_pActionSwitchToOutputIndex16(nullptr) , m_pActionSwitchToOutputIndex17(nullptr) , m_pActionSwitchToOutputIndex18(nullptr) , m_pActionSwitchToOutputIndex19(nullptr) , m_pActionSwitchToPreviousOutputIndex(nullptr) , m_pActionSwitchToNextOutputIndex(nullptr) , m_playing(false) , m_processingPlayQueue(false) , m_nativePlaybackRate(false) , m_secondsBetweenFrames(0) , m_pPlayTimer(nullptr) , m_alwaysKeepCurrentFrame(DEFAULT_ALWAYS_KEEP_CURRENT_FRAME) , m_pGeometrySaveTimer(nullptr) , m_devicePixelRatio(-1) , m_pFramePropsPanel(nullptr) , m_toChangeTitle(false) , m_inPreviewer(a_inPreviewer) { vsedit::disableFontKerning(this); m_ui.setupUi(this); setWindowIcon(QIcon(":preview.png")); m_iconPlay = QIcon(":play.png"); m_iconPause = QIcon(":pause.png"); m_pAdvancedSettingsDialog = new PreviewAdvancedSettingsDialog( m_pSettingsManager, this); m_pPlayTimer = new QTimer(this); m_pPlayTimer->setTimerType(Qt::PreciseTimer); m_pPlayTimer->setSingleShot(true); m_pFramePropsPanel = new FramePropsPanel(a_pSettingsManager, this); createActionsAndMenus(); createStatusBar(); m_pStatusBarWidget->setColorPickerVisible( m_pSettingsManager->getColorPickerVisible()); m_ui.frameNumberSlider->setBigStep(m_bigFrameStep); m_ui.frameNumberSlider->setDisplayMode( m_pSettingsManager->getTimeLineMode()); m_ui.frameToClipboardButton->setDefaultAction(m_pActionFrameToClipboard); m_ui.saveSnapshotButton->setDefaultAction(m_pActionSaveSnapshot); m_ui.advancedSettingsButton->setDefaultAction( m_pActionAdvancedSettingsDialog); setUpZoomPanel(); setUpCropPanel(); setUpTimeLinePanel(); m_ui.colorPickerButton->setDefaultAction(m_pActionToggleColorPicker); m_pGeometrySaveTimer = new QTimer(this); m_pGeometrySaveTimer->setInterval(DEFAULT_WINDOW_GEOMETRY_SAVE_DELAY); connect(m_pGeometrySaveTimer, &QTimer::timeout, this, &PreviewDialog::slotSaveGeometry); m_windowGeometry = m_pSettingsManager->getPreviewDialogGeometry(); if(!m_windowGeometry.isEmpty()) restoreGeometry(m_windowGeometry); connect(m_pAdvancedSettingsDialog, SIGNAL(signalSettingsChanged()), this, SLOT(slotAdvancedSettingsChanged())); connect(m_ui.frameNumberSlider, SIGNAL(signalFrameChanged(int, bool)), this, SLOT(slotShowFrame(int, bool))); connect(m_ui.frameNumberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotShowFrame(int))); connect(m_ui.previewArea, SIGNAL(signalSizeChanged()), this, SLOT(slotPreviewAreaSizeChanged())); connect(m_ui.previewArea, SIGNAL(signalCtrlWheel(QPoint)), this, SLOT(slotPreviewAreaCtrlWheel(QPoint))); connect(m_ui.previewArea, SIGNAL(signalMouseMiddleButtonReleased()), this, SLOT(slotPreviewAreaMouseMiddleButtonReleased())); connect(m_ui.previewArea, SIGNAL(signalMouseRightButtonReleased()), this, SLOT(slotPreviewAreaMouseRightButtonReleased())); connect(m_ui.previewArea, SIGNAL(signalMouseOverPoint(double, double)), this, SLOT(slotPreviewAreaMouseOverPoint(double, double))); connect(m_pPlayTimer, SIGNAL(timeout()), this, SLOT(slotProcessPlayQueue())); connect(this, SIGNAL(signalProcessorIdle(bool)), this, SLOT(slotEnableSwitchOutputIndex(bool))); #ifdef Q_OS_WIN // AUDIO qputenv("QT_MEDIA_BACKEND", QString("windows").toLocal8Bit()); m_pAudioPlayTimer = new QTimer(this); m_pAudioPlayTimer->setTimerType(Qt::PreciseTimer); m_pAudioPlayTimer->setSingleShot(true); connect(m_pAudioPlayTimer, &QTimer::timeout, this, &PreviewDialog::slotProcessAudioPlayQueue); #endif slotSettingsChanged(); if(m_inPreviewer) { m_frameExpected = m_pSettingsManager->getLastPreviewFrame(true); m_frameTimestampExpected = m_pSettingsManager->getLastPreviewTimestamp(true); QPoint scrollBarPos = loadLastScrollBarPositions(); m_ui.previewArea->getScrollBarPositionsFromPreviewer(scrollBarPos); } else if (m_pSettingsManager->getRememberLastPreviewFrame()) { m_frameExpected = m_pSettingsManager->getLastPreviewFrame(); m_frameTimestampExpected = m_pSettingsManager->getLastPreviewTimestamp(); setScriptName(m_pSettingsManager->getLastUsedPath()); } } // END OF PreviewDialog::PreviewDialog(SettingsManager * a_pSettingsManager, // VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent) //============================================================================== PreviewDialog::~PreviewDialog() { if(m_pGeometrySaveTimer->isActive()) { m_pGeometrySaveTimer->stop(); slotSaveGeometry(); } delete m_pFramePropsPanel; } // END OF PreviewDialog::~PreviewDialog() //============================================================================== void PreviewDialog::setScriptName(const QString & a_scriptName) { VSScriptProcessorDialog::setScriptName(a_scriptName); setTitle(); } // END OF void PreviewDialog::setScriptName(const QString & a_scriptName) //============================================================================== void PreviewDialog::previewScript(const QString& a_script, const QString& a_scriptName) { QString previousScript = script(); QString previousScriptName = scriptName(); m_scriptTextChanged = false; if(!m_inPreviewer) stopAndCleanUp(); bool initialized = initialize(a_script, a_scriptName, ProcessReason::Preview); if(!initialized) return; m_outputIndices = m_pVapourSynthScriptProcessor->getOutputIndices(); if(m_outputIndices.size() > 0) { m_ui.outputIndexComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_ui.outputIndexComboBox->clear(); for(auto i : m_outputIndices) m_ui.outputIndexComboBox->addItem(QString::number(i)); m_ui.outputIndexComboBox->setCurrentText(QString::number(m_outputIndex)); } else { // Use old layout m_ui.outputIndexLabel->hide(); m_ui.outputIndexComboBox->hide(); m_ui.frameLabel->hide(); } int lastFrameNumber; auto mt = m_nodeInfo[m_outputIndex].mediaType(); #ifdef Q_OS_WIN // AUDIO if(mt == mtVideo) { m_currentIsAudio = false; const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) return; lastFrameNumber = vi->numFrames - 1; m_ui.frameNumberSpinBox->setMaximum(lastFrameNumber); m_ui.frameNumberSlider->setFramesNumber(vi->numFrames, false); auto fpsPair = m_nodeInfo[m_outputIndex].fpsPair(); m_fpsNum = fpsPair.first; m_fpsDen = fpsPair.second; m_ui.frameNumberSlider->setFPS(m_fpsDen == 0 ? 0.0 : (double)m_fpsNum / (double)m_fpsDen); } else { m_currentIsAudio = true; const VSAudioInfo * ai = m_nodeInfo[m_outputIndex].getAsAudio(); if(!ai) return; lastFrameNumber = ai->numFrames - 1; m_ui.frameNumberSpinBox->setMaximum(lastFrameNumber); m_ui.frameNumberSlider->setFramesNumber(ai->numFrames, false); auto fpsPair = m_nodeInfo[m_outputIndex].fpsPair(); m_fpsNum = fpsPair.first; m_fpsDen = fpsPair.second; m_ui.frameNumberSlider->setFPS((double)m_fpsNum / (double)m_fpsDen); if(m_ui.cropCheckButton->isChecked()) m_ui.cropCheckButton->click(); m_ui.cropCheckButton->setEnabled(false); m_pActionToggleCropPanel->setEnabled(false); m_ui.saveSnapshotButton->setEnabled(false); m_pActionSaveSnapshot->setEnabled(false); m_pStatusBarWidget->setColorPickerString(""); m_ui.playFpsLimitSpinBox->setEnabled(false); m_ui.playFpsLimitModeComboBox->setEnabled(false); int comboIndex = m_ui.playFpsLimitModeComboBox->findData( (int)PlayFPSLimitMode::FromVideo); if(comboIndex != -1) m_ui.playFpsLimitModeComboBox->setCurrentIndex(comboIndex); setAudioOutput(); } #else const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) return; lastFrameNumber = vi->numFrames - 1; m_ui.frameNumberSpinBox->setMaximum(lastFrameNumber); m_ui.frameNumberSlider->setFramesNumber(vi->numFrames, false); auto fpsPair = m_nodeInfo[m_outputIndex].fpsPair(); m_fpsNum = fpsPair.first; m_fpsDen = fpsPair.second; m_ui.frameNumberSlider->setFPS(m_fpsDen == 0 ? 0.0 : (double)m_fpsNum / (double)m_fpsDen); #endif bool scriptChanged = ((previousScript != a_script) && (previousScriptName != a_scriptName)); if(scriptChanged && (!m_alwaysKeepCurrentFrame)) { m_frameExpected = 0; m_ui.previewArea->setPixmap(QPixmap()); } if(m_frameExpected > lastFrameNumber) m_frameExpected = lastFrameNumber; resetCropSpinBoxes(); slotSetPlayFPSLimit(); setScriptName(a_scriptName); loadTimelineBookmarks(); if(m_pSettingsManager->getPreviewDialogMaximized()) showMaximized(); else showNormal(); auto timelineMode = m_pSettingsManager->getTimeLineMode(); if(timelineMode == TimeLineSlider::DisplayMode::Frames) m_frameTimestampExpected = frameToTimestamp(m_frameExpected); else m_frameExpected = timestampToFrame(m_frameTimestampExpected); if(m_frameExpected > lastFrameNumber) setExpectedFrame(lastFrameNumber); else if(m_frameExpected < 0) setExpectedFrame(0); slotShowFrame(m_frameExpected, false); if(m_outputIndices.size() > 0) { m_ui.outputIndexComboBox->disconnect(m_outputIndexComboBoxConnection); m_outputIndexComboBoxConnection = connect(m_ui.outputIndexComboBox, &QComboBox::currentTextChanged, [this]() { int idx = m_ui.outputIndexComboBox->currentText().toInt(); slotSwitchOutputIndex(idx); }); } setTitle(); } // END OF void PreviewDialog::previewScript(const QString& a_script, // const QString& a_scriptName) //============================================================================== void PreviewDialog::stopAndCleanUp() { slotPlay(false); if(m_ui.cropCheckButton->isChecked()) m_ui.cropCheckButton->click(); bool rememberLastPreviewFrame = m_inPreviewer || m_pSettingsManager->getRememberLastPreviewFrame(); if(rememberLastPreviewFrame && (!scriptName().isEmpty()) && (m_frameExpected > -1)) { m_pSettingsManager->setLastPreviewFrame(m_frameExpected, m_inPreviewer); m_pSettingsManager->setLastPreviewTimestamp(m_frameTimestampExpected, m_inPreviewer); } m_frameShown = -1; m_framePixmap = QPixmap(); // Replace shown image with a blank one of the same dimension: // -helps to keep the scrolling position when refreshing the script; // -leaves the image blank on sudden error; // -creates a blinking effect indicating the script is being refreshed. int pixmapWidth = m_ui.previewArea->pixmapWidth(); int pixmapHeight = m_ui.previewArea->pixmapHeight(); QPixmap blackPixmap(pixmapWidth, pixmapHeight); blackPixmap.fill(Qt::black); m_ui.previewArea->setPixmap(blackPixmap); if(m_cpFrame) { Q_ASSERT(m_cpVSAPI); m_cpVSAPI->freeFrame(m_cpFrame); m_cpFrame = nullptr; } if(m_cpPreviewFrame) { Q_ASSERT(m_cpVSAPI); m_cpVSAPI->freeFrame(m_cpPreviewFrame); m_cpPreviewFrame = nullptr; } VSScriptProcessorDialog::stopAndCleanUp(); #ifdef Q_OS_WIN // AUDIO m_audioCache.clear(); #endif } // END OF void PreviewDialog::stopAndCleanUp() //============================================================================== void PreviewDialog::moveEvent(QMoveEvent * a_pEvent) { QDialog::moveEvent(a_pEvent); saveGeometryDelayed(); } // END OF void PreviewDialog::moveEvent(QMoveEvent * a_pEvent) //============================================================================== void PreviewDialog::resizeEvent(QResizeEvent * a_pEvent) { QDialog::resizeEvent(a_pEvent); saveGeometryDelayed(); } // END OF void PreviewDialog::resizeEvent(QResizeEvent * a_pEvent) //============================================================================== void PreviewDialog::changeEvent(QEvent * a_pEvent) { QDialog::changeEvent(a_pEvent); if(a_pEvent->type() == QEvent::WindowStateChange) { if(isMaximized()) m_pSettingsManager->setPreviewDialogMaximized(true); else m_pSettingsManager->setPreviewDialogMaximized(false); } } // END OF void PreviewDialog::changeEvent(QEvent * a_pEvent) //============================================================================== void PreviewDialog::closeEvent(QCloseEvent *a_pEvent) { m_pFramePropsPanel->setVisible(false); if(m_inPreviewer) { slotSaveGeometry(); bool rememberLastPreviewFrame = m_inPreviewer || m_pSettingsManager->getRememberLastPreviewFrame(); if(rememberLastPreviewFrame && (m_frameExpected > -1)) { m_pSettingsManager->setLastPreviewFrame(m_frameExpected, m_inPreviewer); m_pSettingsManager->setLastPreviewTimestamp(m_frameTimestampExpected, m_inPreviewer); } saveLastScrollBarPositions(); reject(); } VSScriptProcessorDialog::closeEvent(a_pEvent); } // END OF void PreviewDialog::closeEvent(QCloseEvent * a_pEvent) //============================================================================== void PreviewDialog::keyPressEvent(QKeyEvent * a_pEvent) { Qt::KeyboardModifiers modifiers = a_pEvent->modifiers(); if(modifiers != Qt::NoModifier) { QDialog::keyPressEvent(a_pEvent); return; } if(!m_pVapourSynthScriptProcessor->isInitialized()) { QDialog::keyPressEvent(a_pEvent); return; } #ifdef Q_OS_WIN // AUDIO #else if(!m_nodeInfo[m_outputIndex].isVideo()) return; #endif int key = a_pEvent->key(); if(((key == Qt::Key_Left) || (key == Qt::Key_Down)) && (m_frameExpected > 0)) slotShowFrame(m_frameExpected - 1, false); else if(((key == Qt::Key_Right) || (key == Qt::Key_Up)) && (m_frameExpected < (m_nodeInfo[m_outputIndex].numFrames() - 1))) slotShowFrame(m_frameExpected + 1, false); else if((key == Qt::Key_PageDown) && (m_frameExpected > 0)) slotShowFrame(std::max(0, m_frameExpected - m_bigFrameStep), true); else if((key == Qt::Key_PageUp) && (m_frameExpected < (m_nodeInfo[m_outputIndex].numFrames() - 1))) { slotShowFrame(std::min(m_nodeInfo[m_outputIndex].numFrames() - 1, m_frameExpected + m_bigFrameStep), true); } else if(key == Qt::Key_Home) slotShowFrame(0, true); else if(key == Qt::Key_End) slotShowFrame(m_nodeInfo[m_outputIndex].numFrames() - 1, true); else if(key == Qt::Key_Escape) close(); else QDialog::keyPressEvent(a_pEvent); } // END OF void PreviewDialog::keyPressEvent(QKeyEvent * a_pEvent) //============================================================================== void PreviewDialog::slotScriptTextChanged() { m_scriptTextChanged = true; setTitle(); } void PreviewDialog::slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame) { if(!a_cpOutputFrame) return; Q_ASSERT(m_cpVSAPI); const VSFrame * cpOutputFrame = m_cpVSAPI->addFrameRef(a_cpOutputFrame); const VSFrame * cpPreviewFrame = m_cpVSAPI->addFrameRef(a_cpPreviewFrame); if(m_playing) { Frame newFrame(a_frameNumber, a_outputIndex, cpOutputFrame, cpPreviewFrame); m_framesCache[m_outputIndex].push_back(newFrame); #ifdef Q_OS_WIN // AUDIO if(m_currentIsAudio && m_pAudioSink) { QByteArray audioData = readAudioFrame(a_cpOutputFrame); AudioFrame newAudioFrame(a_frameNumber, a_outputIndex, audioData); m_audioCache[a_frameNumber] = newAudioFrame; slotProcessAudioPlayQueue(); } else slotProcessPlayQueue(); #else slotProcessPlayQueue(); #endif } else { setCurrentFrame(cpOutputFrame, cpPreviewFrame); m_frameShown = a_frameNumber; if(m_frameShown == m_frameExpected) m_ui.frameStatusLabel->setPixmap(m_readyPixmap); } } // END OF void PreviewDialog::slotReceiveFrame(int a_frameNumber, // int a_outputIndex, const VSFrame * a_cpOutputFrame, // const VSFrame * a_cpPreviewFrame) //============================================================================== void PreviewDialog::slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason) { (void)a_outputIndex; (void)a_reason; if(m_playing) { slotPlay(false); } else { if(a_frameNumber != m_frameExpected) return; if(m_frameShown == -1) { if(m_frameExpected == 0) { // Nowhere to roll back m_ui.frameNumberSlider->setFrame(0, false); m_ui.frameNumberSpinBox->setValue(0); m_ui.frameStatusLabel->setPixmap(m_errorPixmap); } else slotShowFrame(0, false); return; } setExpectedFrame(m_frameShown); m_ui.frameNumberSlider->setFrame(m_frameShown, false); m_ui.frameNumberSpinBox->setValue(m_frameShown); m_ui.frameStatusLabel->setPixmap(m_readyPixmap); } } // END OF void PreviewDialog::slotFrameRequestDiscarded(int a_frameNumber, // int a_outputIndex, const QString & a_reason) //============================================================================== void PreviewDialog::slotShowFrame(int a_frameNumber, bool a_refreshCache) { if((m_frameShown == a_frameNumber) && (!m_framePixmap.isNull())) return; if(m_playing) return; static bool requestingFrame = false; if(requestingFrame) return; requestingFrame = true; if(a_refreshCache) { int frameDiff = m_frameShown - a_frameNumber; if(frameDiff < 0) frameDiff = -frameDiff; if(frameDiff > 10 && m_usedCacheRatio > 0.75) { m_pVapourSynthScriptProcessor->clearCoreCaches(); } } m_ui.frameNumberSpinBox->setValue(a_frameNumber); m_ui.frameNumberSlider->setFrame(a_frameNumber, a_refreshCache); bool requested = requestShowFrame(a_frameNumber); if(requested) { setExpectedFrame(a_frameNumber); m_ui.frameStatusLabel->setPixmap(m_busyPixmap); } else { m_ui.frameNumberSpinBox->setValue(m_frameExpected); m_ui.frameNumberSlider->setFrame(m_frameExpected, a_refreshCache); } requestingFrame = false; } // END OF void PreviewDialog::slotShowFrame(int a_frameNumber, bool a_refreshCache) //============================================================================== void PreviewDialog::slotSaveSnapshot() { if((m_frameShown < 0) || m_framePixmap.isNull()) return; if(!m_nodeInfo[m_outputIndex].isVideo()) return; static std::map extensionToFilterMap = { {"png", tr("PNG image (*.png)")}, }; QString fileExtension = m_pSettingsManager->getLastSnapshotExtension(); QList supportedFormats = QImageWriter::supportedImageFormats(); bool webpSupported = (supportedFormats.indexOf("webp") > -1); if(webpSupported) extensionToFilterMap["webp"] = tr("WebP image (*.webp)"); QString currScriptName = scriptName(); bool currScriptNotSaved = currScriptName.isEmpty(); // Parse the template QString snapshotTemplate = m_pSettingsManager->getSnapshotTemplate(); if(!currScriptNotSaved) { std::vector variables = { {"{f}", tr("script file path"), [&]() { return currScriptName; } }, {"{d}", tr("script file directory"), [&]() { QFileInfo file(currScriptName); return QDir::toNativeSeparators(file.path()); } }, {"{n}", tr("script file name"), [&]() { QFileInfo file(currScriptName); return file.completeBaseName(); } }, {"{o}", tr("output index"), [&]() { return QString::number(m_outputIndex); } }, {"{i}", tr("frame number"), [&]() { return QString::number(m_frameShown); } }, {"{t}", tr("timestamp"), [&]() { if(m_fpsDen == 0 || m_fpsNum == 0) return QString(); QString timeStr = vsedit::timeToString( (double)m_frameShown / m_fpsNum * m_fpsDen, true) .replace(":", "."); return timeStr; } }, {"{nm}", tr("clip name"), [&]() { return m_clipName; } }, {"{sc}", tr("scene name"), [&]() { return m_sceneName; } }, }; for(const vsedit::VariableToken & var : variables) { snapshotTemplate = snapshotTemplate.replace( var.token, var.evaluate()); } } bool silentSnapshot = m_pSettingsManager->getSilentSnapshot(); QString snapshotFilePath = snapshotTemplate; if(snapshotFilePath.isEmpty()) silentSnapshot = false; if(currScriptNotSaved || snapshotFilePath.isEmpty()) { snapshotFilePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); snapshotFilePath += QString("/%1-%2").arg( QString::number(m_frameShown), QString::number(m_outputIndex)); snapshotFilePath += fileExtension; } QStringList saveFormatsList; for(const std::pair & pair : extensionToFilterMap) saveFormatsList << pair.second; QString selectedFilter = extensionToFilterMap[fileExtension]; if(currScriptNotSaved || !silentSnapshot) { snapshotFilePath = QFileDialog::getSaveFileName(this, tr("Save frame as image"), snapshotFilePath, saveFormatsList.join(";;"), &selectedFilter); } QFileInfo fileInfo(snapshotFilePath); QString suffix = fileInfo.suffix().toLower(); QByteArray format("png"); if((suffix == "webp") && webpSupported) format = "webp"; else if(silentSnapshot && suffix != "png") snapshotFilePath += ".png"; if(!snapshotFilePath.isEmpty()) { bool success = m_framePixmap.save(snapshotFilePath, format, format == "webp" ? 100 : m_pSettingsManager->getPNGSnapshotCompressionLevel()); if(success) m_pSettingsManager->setLastSnapshotExtension(suffix); else { QMessageBox::critical(this, tr("Image save error"), tr("Error while saving image ") + snapshotFilePath); } } } // END OF void PreviewDialog::slotSaveSnapshot() //============================================================================== void PreviewDialog::slotToggleZoomPanelVisible(bool a_zoomPanelVisible) { m_ui.zoomPanel->setVisible(a_zoomPanelVisible); m_pSettingsManager->setZoomPanelVisible(a_zoomPanelVisible); } // END OF void PreviewDialog::slotToggleZoomPanelVisible( // bool a_zoomPanelVisible) //============================================================================== void PreviewDialog::slotZoomModeChanged() { static bool changingZoomMode = false; if(changingZoomMode) return; changingZoomMode = true; ZoomMode zoomMode = (ZoomMode)m_ui.zoomModeComboBox->currentData().toInt(); QObject * pSender = sender(); if(pSender == m_ui.zoomModeComboBox) { for(QAction * pAction : m_pActionGroupZoomModes->actions()) { ZoomMode actionZoomMode = m_actionIDToZoomModeMap[pAction->data().toString()]; if(actionZoomMode == zoomMode) { pAction->setChecked(true); break; } } } else { // If signal wasn't sent by combo box - presume it was sent by action. QAction * pSenderAction = qobject_cast(pSender); zoomMode = m_actionIDToZoomModeMap[pSenderAction->data().toString()]; int zoomModeIndex = m_ui.zoomModeComboBox->findData((int)zoomMode); m_ui.zoomModeComboBox->setCurrentIndex(zoomModeIndex); } setPreviewPixmap(); bool fixedRatio(zoomMode == ZoomMode::FixedRatio); m_ui.zoomRatioSpinBox->setEnabled(fixedRatio); bool noZoom = (zoomMode == ZoomMode::NoZoom); m_ui.scaleModeComboBox->setEnabled(!noZoom); m_pMenuZoomScaleModes->setEnabled(!noZoom); m_pSettingsManager->setZoomMode(zoomMode); changingZoomMode = false; } // END OF void PreviewDialog::slotZoomModeChanged() //============================================================================== void PreviewDialog::slotZoomRatioChanged(double a_zoomRatio) { setPreviewPixmap(); m_pSettingsManager->setZoomRatio(a_zoomRatio); } // END OF void PreviewDialog::slotZoomRatioChanged(double a_zoomRatio) //============================================================================== void PreviewDialog::slotScaleModeChanged() { static bool changingScaleMode = false; if(changingScaleMode) return; changingScaleMode = true; Qt::TransformationMode scaleMode = (Qt::TransformationMode) m_ui.scaleModeComboBox->currentData().toInt(); QObject * pSender = sender(); if(pSender == m_ui.scaleModeComboBox) { for(QAction * pAction : m_pActionGroupZoomScaleModes->actions()) { Qt::TransformationMode actionScaleMode = m_actionIDToZoomScaleModeMap[pAction->data().toString()]; if(actionScaleMode == scaleMode) { pAction->setChecked(true); break; } } } else { // If signal wasn't sent by combo box - presume it was sent by action. QAction * pSenderAction = qobject_cast(pSender); scaleMode = m_actionIDToZoomScaleModeMap[pSenderAction->data().toString()]; int scaleModeIndex = m_ui.scaleModeComboBox->findData((int)scaleMode); m_ui.scaleModeComboBox->setCurrentIndex(scaleModeIndex); } m_ui.zoomRatioSpinBox->setScaleMode(scaleMode); setPreviewPixmap(); m_pSettingsManager->setScaleMode(scaleMode); changingScaleMode = false; } // END OF void PreviewDialog::slotScaleModeChanged() //============================================================================== void PreviewDialog::slotToggleCropPanelVisible(bool a_cropPanelVisible) { m_ui.cropPanel->setVisible(a_cropPanelVisible); setPreviewPixmap(); } // END OF void PreviewDialog::slotToggleCropPanelVisible( // bool a_cropPanelVisible) //============================================================================== void PreviewDialog::slotCropModeChanged() { CropMode cropMode = (CropMode)m_ui.cropModeComboBox->currentData().toInt(); if(cropMode == CropMode::Absolute) { m_ui.cropWidthSpinBox->setEnabled(true); m_ui.cropHeightSpinBox->setEnabled(true); m_ui.cropRightSpinBox->setEnabled(false); m_ui.cropBottomSpinBox->setEnabled(false); } else { m_ui.cropWidthSpinBox->setEnabled(false); m_ui.cropHeightSpinBox->setEnabled(false); m_ui.cropRightSpinBox->setEnabled(true); m_ui.cropBottomSpinBox->setEnabled(true); } m_pSettingsManager->setCropMode(cropMode); } // END OF void PreviewDialog::slotCropModeChanged() //============================================================================== void PreviewDialog::slotCropLeftValueChanged(int a_value) { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } int remainder = vi->width - a_value; m_ui.cropWidthSpinBox->setMaximum(remainder); m_ui.cropRightSpinBox->setMaximum(remainder - 1); CropMode cropMode = (CropMode)m_ui.cropModeComboBox->currentData().toInt(); if(cropMode == CropMode::Absolute) { if(m_ui.cropWidthSpinBox->value() > remainder) m_ui.cropWidthSpinBox->setValue(remainder); m_ui.cropRightSpinBox->setValue(remainder - m_ui.cropWidthSpinBox->value()); } else { if(m_ui.cropRightSpinBox->value() > remainder - 1) m_ui.cropRightSpinBox->setValue(remainder - 1); m_ui.cropWidthSpinBox->setValue(remainder - m_ui.cropRightSpinBox->value()); } recalculateCropMods(); setPreviewPixmap(); m_ui.previewArea->slotScrollLeft(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::slotCropLeftValueChanged(int a_value) //============================================================================== void PreviewDialog::slotCropTopValueChanged(int a_value) { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } int remainder = vi->height - a_value; m_ui.cropHeightSpinBox->setMaximum(remainder); m_ui.cropBottomSpinBox->setMaximum(remainder - 1); CropMode cropMode = (CropMode)m_ui.cropModeComboBox->currentData().toInt(); if(cropMode == CropMode::Absolute) { if(m_ui.cropHeightSpinBox->value() > remainder) m_ui.cropHeightSpinBox->setValue(remainder); m_ui.cropBottomSpinBox->setValue(remainder - m_ui.cropHeightSpinBox->value()); } else { if(m_ui.cropBottomSpinBox->value() > remainder - 1) m_ui.cropBottomSpinBox->setValue(remainder - 1); m_ui.cropHeightSpinBox->setValue(remainder - m_ui.cropBottomSpinBox->value()); } recalculateCropMods(); setPreviewPixmap(); m_ui.previewArea->slotScrollTop(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::slotCropTopValueChanged(int a_value) //============================================================================== void PreviewDialog::slotCropWidthValueChanged(int a_value) { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } m_ui.cropRightSpinBox->setValue(vi->width - m_ui.cropLeftSpinBox->value() - a_value); recalculateCropMods(); setPreviewPixmap(); m_ui.previewArea->slotScrollRight(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::slotCropWidthValueChanged(int a_value) //============================================================================== void PreviewDialog::slotCropHeightValueChanged(int a_value) { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } m_ui.cropBottomSpinBox->setValue(vi->height - m_ui.cropTopSpinBox->value() - a_value); recalculateCropMods(); setPreviewPixmap(); m_ui.previewArea->slotScrollBottom(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::slotCropHeightValueChanged(int a_value) //============================================================================== void PreviewDialog::slotCropRightValueChanged(int a_value) { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } m_ui.cropWidthSpinBox->setValue(vi->width - m_ui.cropLeftSpinBox->value() - a_value); recalculateCropMods(); setPreviewPixmap(); m_ui.previewArea->slotScrollRight(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::slotCropRightValueChanged(int a_value) //============================================================================== void PreviewDialog::slotCropBottomValueChanged(int a_value) { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } m_ui.cropHeightSpinBox->setValue(vi->height - m_ui.cropTopSpinBox->value() - a_value); recalculateCropMods(); setPreviewPixmap(); m_ui.previewArea->slotScrollBottom(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::slotCropBottomValueChanged(int a_value) //============================================================================== void PreviewDialog::slotCropZoomRatioValueChanged(int a_cropZoomRatio) { m_pSettingsManager->setCropZoomRatio(a_cropZoomRatio); setPreviewPixmap(); } // END OF void PreviewDialog::slotCropZoomRatioValueChanged(int a_cropZoomRatio) //============================================================================== void PreviewDialog::slotPasteCropSnippetIntoScript() { if(m_inPreviewer) return; if(!m_ui.cropPanel->isVisible()) return; CropMode cropMode = (CropMode)m_ui.cropModeComboBox->currentData().toInt(); QString cropString; if(cropMode == CropMode::Absolute) { cropString = QString("***CLIP*** = core.std.CropAbs" "(***CLIP***, x=%1, y=%2, width=%3, height=%4)") .arg(m_ui.cropLeftSpinBox->value()) .arg(m_ui.cropTopSpinBox->value()) .arg(m_ui.cropWidthSpinBox->value()) .arg(m_ui.cropHeightSpinBox->value()); } else { cropString = QString("***CLIP*** = core.std.CropRel" "(***CLIP***, left=%1, top=%2, right=%3, bottom=%4)") .arg(m_ui.cropLeftSpinBox->value()) .arg(m_ui.cropTopSpinBox->value()) .arg(m_ui.cropRightSpinBox->value()) .arg(m_ui.cropBottomSpinBox->value()); } emit signalPasteIntoScriptAtNewLine(cropString); } // END OF void PreviewDialog::slotPasteCropSnippetIntoScript() //============================================================================== void PreviewDialog::slotCallAdvancedSettingsDialog() { m_pAdvancedSettingsDialog->slotCall(); } // END OF void PreviewDialog::slotCallAdvancedSettingsDialog() //============================================================================== void PreviewDialog::slotToggleTimeLinePanelVisible(bool a_timeLinePanelVisible) { m_ui.timeLinePanel->setVisible(a_timeLinePanelVisible); m_pSettingsManager->setTimeLinePanelVisible(a_timeLinePanelVisible); } // END OF void PreviewDialog::slotToggleTimeLinePanelVisible( // bool a_timeLinePanelVisible) //============================================================================== void PreviewDialog::slotTimeLineModeChanged() { static bool changingTimeLineMode = false; if(changingTimeLineMode) return; changingTimeLineMode = true; TimeLineSlider::DisplayMode timeLineMode = (TimeLineSlider::DisplayMode) m_ui.timeLineModeComboBox->currentData().toInt(); QObject * pSender = sender(); if(pSender == m_ui.timeLineModeComboBox) { for(QAction * pAction : m_pActionGroupTimeLineModes->actions()) { TimeLineSlider::DisplayMode actionTimeLineMode = m_actionIDToTimeLineModeMap[pAction->data().toString()]; if(actionTimeLineMode == timeLineMode) { pAction->setChecked(true); break; } } } else { // If signal wasn't sent by combo box - presume it was sent by action. QAction * pSenderAction = qobject_cast(pSender); timeLineMode = m_actionIDToTimeLineModeMap[pSenderAction->data().toString()]; int timeLineModeIndex = m_ui.timeLineModeComboBox->findData( (int)timeLineMode); m_ui.timeLineModeComboBox->setCurrentIndex(timeLineModeIndex); } m_ui.frameNumberSlider->setDisplayMode(timeLineMode); m_pSettingsManager->setTimeLineMode(timeLineMode); changingTimeLineMode = false; } // END OF void PreviewDialog::slotTimeLineModeChanged() //============================================================================== void PreviewDialog::slotTimeStepChanged(const QTime & a_time) { m_pSettingsManager->setTimeStep(vsedit::qtimeToSeconds(a_time)); } // END OF void PreviewDialog::slotTimeStepChanged(const QTime & a_time) //============================================================================== void PreviewDialog::slotTimeStepForward() { double step = vsedit::qtimeToSeconds(m_ui.timeStepEdit->time()); m_ui.frameNumberSlider->slotStepBySeconds(step); } // END OF void PreviewDialog::slotTimeStepForward() //============================================================================== void PreviewDialog::slotTimeStepBack() { double step = vsedit::qtimeToSeconds(m_ui.timeStepEdit->time()); m_ui.frameNumberSlider->slotStepBySeconds(-step); } // END OF void PreviewDialog::slotTimeStepBack() //============================================================================== void PreviewDialog::slotSettingsChanged() { QKeySequence hotkey; for(QAction * pAction : m_settableActionsList) { hotkey = m_pSettingsManager->getHotkey(pAction->data().toString()); pAction->setShortcut(hotkey); } m_alwaysKeepCurrentFrame = m_pSettingsManager->getAlwaysKeepCurrentFrame(); m_ui.frameNumberSlider->setUpdatesEnabled(false); QFont sliderLabelsFont = m_pSettingsManager->getTextFormat(TEXT_FORMAT_ID_TIMELINE).font(); m_ui.frameNumberSlider->setLabelsFont(sliderLabelsFont); QColor bookmarksColor = m_pSettingsManager->getColor(COLOR_ID_TIMELINE_BOOKMARKS); m_ui.frameNumberSlider->setColor(TimeLineSlider::Bookmark, bookmarksColor); m_ui.frameNumberSlider->setUpdatesEnabled(true); } // END OF void PreviewDialog::slotSettingsChanged() //============================================================================== void PreviewDialog::slotPreviewAreaSizeChanged() { ZoomMode zoomMode = (ZoomMode)m_ui.zoomModeComboBox->currentData().toInt(); if(zoomMode == ZoomMode::FitToFrame) setPreviewPixmap(); } // END OF void PreviewDialog::slotPreviewAreaSizeChanged() //============================================================================== void PreviewDialog::slotPreviewAreaCtrlWheel(QPoint a_angleDelta) { #ifdef Q_OS_WIN // AUDIO if(m_currentIsAudio && m_pAudioSink) { int deltaY = a_angleDelta.y(); m_audioVolume = m_pAudioSink->volume(); if(deltaY > 0) { m_audioVolume += 0.1; m_pAudioSink->setVolume(m_audioVolume); } else if(deltaY < 0) { m_audioVolume -= 0.1; m_pAudioSink->setVolume(m_audioVolume); } return; } #endif ZoomMode zoomMode = (ZoomMode)m_ui.zoomModeComboBox->currentData().toInt(); int deltaY = a_angleDelta.y(); if(m_ui.cropCheckButton->isChecked()) { if(deltaY > 0) m_ui.cropZoomRatioSpinBox->stepBy(1); else if(deltaY < 0) m_ui.cropZoomRatioSpinBox->stepBy(-1); } else if(zoomMode == ZoomMode::FixedRatio) { if(deltaY > 0) m_ui.zoomRatioSpinBox->stepBy(1); else if(deltaY < 0) m_ui.zoomRatioSpinBox->stepBy(-1); } } // END OF void PreviewDialog::slotPreviewAreaCtrlWheel(QPoint a_angleDelta) //============================================================================== void PreviewDialog::slotPreviewAreaMouseMiddleButtonReleased() { if(m_ui.cropCheckButton->isChecked()) return; int zoomModeIndex = m_ui.zoomModeComboBox->currentIndex(); zoomModeIndex++; if(zoomModeIndex >= m_ui.zoomModeComboBox->count()) zoomModeIndex = 0; m_ui.zoomModeComboBox->setCurrentIndex(zoomModeIndex); } // END OF void PreviewDialog::slotPreviewAreaMouseMiddleButtonReleased() //============================================================================== void PreviewDialog::slotPreviewAreaMouseRightButtonReleased() { m_pPreviewContextMenu->popup(QCursor::pos()); } // END OF void PreviewDialog::slotPreviewAreaMouseRightButtonReleased() //============================================================================== void PreviewDialog::slotPreviewAreaMouseOverPoint(double a_pX, double a_pY) { if(!m_cpFrame) return; if(m_cpVSAPI->getFrameType(m_cpFrame) == mtAudio) return; if(!m_pStatusBarWidget->colorPickerVisible()) return; double value1 = 0.0; double value2 = 0.0; double value3 = 0.0; int preview_values[3] = {0, 0, 0}; size_t frameX = 0; size_t frameY = 0; double zoomRatio; int width = m_cpVSAPI->getFrameWidth(m_cpFrame, 0); int height = m_cpVSAPI->getFrameHeight(m_cpFrame, 0); const VSVideoFormat * cpFormat = m_cpVSAPI->getVideoFrameFormat(m_cpFrame); if(m_ui.cropPanel->isVisible()) { zoomRatio = m_ui.cropZoomRatioSpinBox->value(); int cropLeft = m_ui.cropLeftSpinBox->value(); int cropTop = m_ui.cropTopSpinBox->value(); int cropWidth = m_ui.cropWidthSpinBox->value(); int cropHeight = m_ui.cropHeightSpinBox->value(); frameX = (size_t)(a_pX * m_devicePixelRatio / zoomRatio + cropLeft); frameY = (size_t)(a_pY * m_devicePixelRatio / zoomRatio + cropTop); if(frameX >= (size_t)(cropLeft + cropWidth) || frameY >= (size_t)(cropTop + cropHeight)) return; } else { ZoomMode zoomMode = (ZoomMode)m_ui.zoomModeComboBox->currentData().toInt(); if(zoomMode == ZoomMode::NoZoom) { zoomRatio = 1.0; } else if(zoomMode == ZoomMode::FixedRatio) { zoomRatio = m_ui.zoomRatioSpinBox->value(); } else { if(m_framePixmap.isNull()) { m_pStatusBarWidget->setColorPickerString(QString()); return; } QRect previewRect = m_ui.previewArea->geometry(); int cropSize = m_ui.previewArea->frameWidth() * 2; int frameWidth = previewRect.width() * m_devicePixelRatio - cropSize; int frameHeight = previewRect.height() * m_devicePixelRatio - cropSize; double scaleW = (double)frameWidth / m_framePixmap.width(); double scaleH = (double)frameHeight / m_framePixmap.height(); zoomRatio = scaleW < scaleH ? scaleW : scaleH; } if(zoomRatio <= 0) return; frameX = (size_t)(a_pX * m_devicePixelRatio / zoomRatio); frameY = (size_t)(a_pY * m_devicePixelRatio / zoomRatio); if((frameX >= (size_t)width) || (frameY >= (size_t)height)) return; } value1 = valueAtPoint(frameX, frameY, 0); if(cpFormat->numPlanes > 1) value2 = valueAtPoint(frameX, frameY, 1); if(cpFormat->numPlanes > 2) value3 = valueAtPoint(frameX, frameY, 2); previewValueAtPoint(frameX, frameY, preview_values); QString l1("1"); QString l2("2"); QString l3("3"); int colorFamily = m_nodeInfo[m_outputIndex].getAsVideo()-> format.colorFamily; if(colorFamily == cfYUV) { l1 = "Y"; l2 = "U"; l3 = "V"; } else if(colorFamily == cfRGB) { l1 = "R"; l2 = "G"; l3 = "B"; } QString colorString; if(colorFamily == cfGray) colorString = QString("Video: G:%1").arg(value1); else { colorString = QString("Video: %1:%2|%3:%4|%5:%6") .arg(l1).arg(value1).arg(l2).arg(value2).arg(l3).arg(value3); } QString coordString = QString(" Position: X:%1|Y:%2") .arg(frameX).arg(frameY); QString dispString = QString(" Display: R:%1|G:%2|B:%3") .arg(preview_values[0]).arg(preview_values[1]).arg(preview_values[2]); m_pStatusBarWidget->setColorPickerString(colorString + coordString + dispString); } // END OF void PreviewDialog::slotPreviewAreaMouseOverPoint(float a_normX, // float a_normY) //============================================================================== void PreviewDialog::slotFrameToClipboard() { if(m_framePixmap.isNull()) return; QClipboard * pClipboard = QApplication::clipboard(); pClipboard->setPixmap(m_framePixmap); } // END OF void PreviewDialog::slotFrameToClipboard() //============================================================================== void PreviewDialog::slotAdvancedSettingsChanged() { m_pVapourSynthScriptProcessor->slotResetSettings(); if(!m_playing) requestShowFrame(m_frameExpected); } // END OF void PreviewDialog::slotAdvancedSettingsChanged() //============================================================================== void PreviewDialog::slotToggleColorPicker(bool a_colorPickerVisible) { m_pStatusBarWidget->setColorPickerVisible(a_colorPickerVisible); m_pSettingsManager->setColorPickerVisible(a_colorPickerVisible); } // END OF void PreviewDialog::slotToggleColorPicker(bool a_colorPickerVisible) //============================================================================== void PreviewDialog::slotSetPlayFPSLimit() { double limit = m_ui.playFpsLimitSpinBox->value(); if(limit < 1e-3) limit = 1e-3; m_nativePlaybackRate = false; #ifdef Q_OS_WIN // AUDIO PlayFPSLimitMode mode = m_currentIsAudio ? PlayFPSLimitMode::FromVideo : #else PlayFPSLimitMode mode = #endif (PlayFPSLimitMode)m_ui.playFpsLimitModeComboBox->currentData().toInt(); if(mode == PlayFPSLimitMode::NoLimit) m_secondsBetweenFrames = 0.0; else if(mode == PlayFPSLimitMode::Custom) m_secondsBetweenFrames = 1.0 / limit; else if(mode == PlayFPSLimitMode::FromVideo) { if(m_fpsDen == 0 || m_fpsNum == 0) { m_nativePlaybackRate = true; m_secondsBetweenFrames = 0.0; } else { m_secondsBetweenFrames = 1.0 / m_fpsNum * m_fpsDen; } } else Q_ASSERT(false); #ifdef Q_OS_WIN // AUDIO if(m_currentIsAudio) return; #endif m_pSettingsManager->setPlayFPSLimitMode(mode); m_pSettingsManager->setPlayFPSLimit(limit); } // END OF void PreviewDialog::void slotSetPlayFPSLimit() //============================================================================== void PreviewDialog::slotPlay(bool a_play) { if(m_playing == a_play) return; m_playing = a_play; m_pActionPlay->setChecked(m_playing); if(m_playing) { m_ui.outputIndexComboBox->setEnabled(false); m_pActionPlay->setIcon(m_iconPause); m_lastFrameRequestedForPlay = m_frameShown; #ifdef Q_OS_WIN // AUDIO if(m_currentIsAudio && m_pAudioSink) slotProcessAudioPlayQueue(); else slotProcessPlayQueue(); #else slotProcessPlayQueue(); #endif } else { clearFramesCache(); #ifdef Q_OS_WIN // AUDIO m_audioCache.clear(); #endif m_pVapourSynthScriptProcessor->flushFrameTicketsQueue(); m_ui.outputIndexComboBox->setEnabled(true); m_pActionPlay->setIcon(m_iconPlay); setTitle(); } } // END OF void PreviewDialog::slotPlay(bool a_play) //============================================================================== void PreviewDialog::slotProcessPlayQueue() { if(!m_playing) return; if(m_processingPlayQueue) return; m_processingPlayQueue = true; const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) return; int nextFrame = (m_frameShown + 1) % vi->numFrames; Frame referenceFrame(nextFrame, m_outputIndex, nullptr); while(!m_framesCache[m_outputIndex].empty()) { std::list::const_iterator it = std::find(m_framesCache[m_outputIndex].begin(), m_framesCache[m_outputIndex].end(), referenceFrame); if(it == m_framesCache[m_outputIndex].end()) break; if(m_nativePlaybackRate) { Q_ASSERT(m_cpVSAPI); const VSMap * frameProps = m_cpVSAPI->getFramePropertiesRO( it->cpOutputFrame); int err_num, err_den; int64_t durNum = m_cpVSAPI->mapGetInt(frameProps, "_DurationNum", 0, &err_num); int64_t durDen = m_cpVSAPI->mapGetInt(frameProps, "_DurationDen", 0, &err_den); if(err_num || err_den || durNum <= 0 || durDen <= 0) { // Fallback to custom double limit = m_ui.playFpsLimitSpinBox->value(); if(limit < 1e-3) limit = 1e-3; m_secondsBetweenFrames = 1.0 / limit; } else m_secondsBetweenFrames = (double)durNum / (double)durDen; } hr_time_point now = hr_clock::now(); double passed = duration_to_double(now - m_lastFrameShowTime); double secondsToNextFrame = m_secondsBetweenFrames - passed; if(secondsToNextFrame > 0) { int millisecondsToNextFrame = std::ceil(secondsToNextFrame * 1000); m_pPlayTimer->start(millisecondsToNextFrame); break; } setCurrentFrame(it->cpOutputFrame, it->cpPreviewFrame); m_lastFrameShowTime = hr_clock::now(); m_frameShown = nextFrame; setExpectedFrame(m_frameShown); m_ui.frameNumberSpinBox->setValue(m_frameExpected); m_ui.frameNumberSlider->setFrame(m_frameExpected, false); m_framesCache[m_outputIndex].erase(it); nextFrame = (m_frameShown + 1) % vi->numFrames; referenceFrame.number = nextFrame; } nextFrame = (m_lastFrameRequestedForPlay + 1) % vi->numFrames; while(((m_framesInQueue[m_outputIndex] + m_framesInProcess[m_outputIndex]) < m_maxThreads) && (m_framesCache[m_outputIndex].size() <= m_cachedFramesLimit)) { m_pVapourSynthScriptProcessor->requestFrameAsync(nextFrame, m_outputIndex, true); m_lastFrameRequestedForPlay = nextFrame; nextFrame = (nextFrame + 1) % vi->numFrames; } m_processingPlayQueue = false; } // END OF void PreviewDialog::slotProcessPlayQueue() //============================================================================== #ifdef Q_OS_WIN // AUDIO void PreviewDialog::slotProcessAudioPlayQueue() { if(!m_playing) return; if(m_processingPlayQueue) return; m_processingPlayQueue = true; int numFrames = m_nodeInfo[m_outputIndex].numFrames(); int nextFrame = (m_frameShown + 1) % numFrames; static double delay_estimate = 0.0; while(!m_framesCache[m_outputIndex].empty()) { auto ait = m_audioCache.find(nextFrame); if(ait == m_audioCache.end()) break; auto af = m_audioCache[nextFrame]; double ms_offset = 0.0; hr_time_point now = hr_clock::now(); double passed = duration_to_double(now - m_lastFrameShowTime); double secondsToNextFrame = m_secondsBetweenFrames - passed - delay_estimate; if(secondsToNextFrame > 0) { int millisecondsToNextFrame = std::round(secondsToNextFrame * 1000); ms_offset = secondsToNextFrame - millisecondsToNextFrame / 1000.0; m_pAudioPlayTimer->start(millisecondsToNextFrame); break; } m_lastFrameShowTime = hr_clock::now(); m_pAudioIODevice->write(af.data); auto time_post = hr_clock::now(); delay_estimate = duration_to_double(time_post - m_lastFrameShowTime) - ms_offset; m_audioCache.erase(nextFrame); Frame referenceFrame(nextFrame, m_outputIndex, nullptr); std::list::const_iterator it = std::find(m_framesCache[m_outputIndex].begin(), m_framesCache[m_outputIndex].end(), referenceFrame); // caches are synced so this won't happen... if(it == m_framesCache[m_outputIndex].end()) break; setCurrentFrame(it->cpOutputFrame, it->cpPreviewFrame); m_frameShown = nextFrame; setExpectedFrame(m_frameShown); m_ui.frameNumberSpinBox->setValue(m_frameExpected); m_ui.frameNumberSlider->setFrame(m_frameExpected, false); m_framesCache[m_outputIndex].erase(it); nextFrame = (m_frameShown + 1) % numFrames; referenceFrame.number = nextFrame; } nextFrame = (m_lastFrameRequestedForPlay + 1) % numFrames; while(((m_framesInQueue[m_outputIndex] + m_framesInProcess[m_outputIndex]) < m_maxThreads) && (m_framesCache[m_outputIndex].size() <= m_cachedFramesLimit)) { m_pVapourSynthScriptProcessor->requestFrameAsync(nextFrame, m_outputIndex, true); m_lastFrameRequestedForPlay = nextFrame; nextFrame = (nextFrame + 1) % numFrames; } if(m_framesCache[m_outputIndex].empty()) m_audioCache.clear(); m_processingPlayQueue = false; } #endif void PreviewDialog::slotLoadChapters() { if(m_playing) return; if (m_fpsDen == 0) { QString infoString = tr( "Warning: Load chapters requires clip having constant frame rate. Skipped"); emit signalWriteLogMessage(mtWarning, infoString); return; } const QString lastUsedPath = m_pSettingsManager->getLastUsedPath(); const QString filePath = QFileDialog::getOpenFileName(this, tr("Load chapters"), lastUsedPath, tr("Chapters file (*.txt;*.xml);;All files (*)")); QFile chaptersFile(filePath); if(!chaptersFile.open(QIODevice::ReadOnly | QIODevice::Text)) return; static QRegularExpression re = QRegularExpression( R"((\d{1,3}):(\d{2}):(\d{2})[\.:](\d{3})?)"); while(!chaptersFile.atEnd()) { const QByteArray line = chaptersFile.readLine(); auto match = re.match(line); if(!match.hasMatch()) continue; double timecode = match.captured(1).toDouble() * 3600 + match.captured(2).toDouble() * 60 + match.captured(3).toDouble() + match.captured(4).toDouble() / 1000; const int frameIndex = round(timecode * m_fpsNum / m_fpsDen); m_ui.frameNumberSlider->addBookmark(frameIndex); } saveTimelineBookmarks(); } // END OF void PreviewDialog::slotLoadBookmarks() //============================================================================== void PreviewDialog::slotClearBookmarks() { if(m_playing) return; QMessageBox quesBox(this); vsedit::disableFontKerning(&quesBox); quesBox.setWindowTitle(tr("Clear Bookmarks")); quesBox.setText(tr("Do you really want to clear timeline bookmarks?")); quesBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); quesBox.setDefaultButton(QMessageBox::No); int result = quesBox.exec(); if(result == QMessageBox::No) return; m_ui.frameNumberSlider->clearBookmarks(); saveTimelineBookmarks(); } // END OF void PreviewDialog::slotClearBookmarks() //============================================================================== void PreviewDialog::slotBookmarkCurrentFrame() { if(m_playing) return; m_ui.frameNumberSlider->slotBookmarkCurrentFrame(); saveTimelineBookmarks(); } // END OF void PreviewDialog::slotBookmarkCurrentFrame() //============================================================================== void PreviewDialog::slotUnbookmarkCurrentFrame() { if(m_playing) return; m_ui.frameNumberSlider->slotUnbookmarkCurrentFrame(); saveTimelineBookmarks(); } // END OF void PreviewDialog::slotUnbookmarkCurrentFrame() //============================================================================== void PreviewDialog::slotGoToPreviousBookmark() { if(m_playing) return; m_ui.frameNumberSlider->slotGoToPreviousBookmark(); } // END OF void PreviewDialog::slotGoToPreviousBookmark() //============================================================================== void PreviewDialog::slotGoToNextBookmark() { if(m_playing) return; m_ui.frameNumberSlider->slotGoToNextBookmark(); } // END OF void PreviewDialog::slotGoToNextBookmark() //============================================================================== void PreviewDialog::slotPasteShownFrameNumberIntoScript() { emit signalPasteIntoScriptAtCursor(QString::number(m_frameShown)); } // END OF void PreviewDialog::slotPasteShownFrameNumberIntoScript() //============================================================================== void PreviewDialog::slotSaveGeometry() { m_pGeometrySaveTimer->stop(); m_pSettingsManager->setPreviewDialogGeometry(m_windowGeometry); } void PreviewDialog::slotJumpToFrame() { if(m_playing) return; if(busy()) return; int currFrameNumber = m_frameExpected; int lastFrameNumber = m_nodeInfo[m_outputIndex].numFrames() - 1; int frame = QInputDialog::getInt(this, "Jump to...", "Jump to frame...", currFrameNumber); if(frame < 0) // Allowing -1, for example frame = lastFrameNumber + frame + 1; if(frame < 0) frame = 0; if(frame > lastFrameNumber) frame = lastFrameNumber; if(frame != currFrameNumber) { slotShowFrame(frame, true); } } QPoint PreviewDialog::loadLastScrollBarPositions() const { return m_pSettingsManager->getLastPreviewScrollBarPositions(); } void PreviewDialog::saveLastScrollBarPositions() { QPoint pos = m_ui.previewArea->getScrollBarPositions(); m_pSettingsManager->setLastPreviewScrollBarPositions(pos); } // END OF void PreviewDialog::slotSaveGeometry() //============================================================================== #ifdef Q_OS_WIN // AUDIO void PreviewDialog::setAudioOutput() { const VSAudioInfo * pAI = m_nodeInfo[m_outputIndex].getAsAudio(); QAudioFormat af; int numChannels = pAI->format.numChannels; int bytesPerSample = pAI->format.bytesPerSample; af.setChannelCount(numChannels); af.setSampleRate(pAI->sampleRate); // Always convert to int16 bytesPerSample = 2; af.setSampleFormat(QAudioFormat::Int16); stopAudioOutput(); QAudioDevice device = QMediaDevices::defaultAudioOutput(); if(numChannels <= 2 && device.isFormatSupported(af)) { m_pAudioSink = new QAudioSink(device, af); m_pAudioSink->setVolume(m_audioVolume); m_pAudioSink->setBufferSize(numChannels * bytesPerSample * VS_AUDIO_FRAME_SAMPLES * 3); m_pAudioIODevice = m_pAudioSink->start(); } else { qWarning() << QString("Audio format of node #%1 is not yet supported for playback.") .arg(m_outputIndex).toStdString().c_str(); } } void PreviewDialog::stopAudioOutput() { if(m_pAudioSink) { if(m_pAudioIODevice) m_pAudioIODevice->close(); m_pAudioSink->stop(); delete m_pAudioSink; m_pAudioSink = nullptr; m_pAudioIODevice = nullptr; } m_audioCache.clear(); } void PreviewDialog::playAudioFrame() { QByteArray frameData = readAudioFrame(m_cpFrame); if(frameData.size() > 0 && m_pAudioSink) { m_pAudioIODevice->write(frameData); } } QByteArray PreviewDialog::readAudioFrame(const VSFrame *a_cpFrame) { if(!a_cpFrame) return QByteArray(); if(m_cpVSAPI->getFrameType(a_cpFrame) != mtAudio) return QByteArray(); if(!m_pAudioSink) return QByteArray(); int numChannels = m_nodeInfo[m_outputIndex].getAsAudio()->format.numChannels; int bytesPerSample = m_nodeInfo[m_outputIndex].getAsAudio()->format.bytesPerSample; auto sampleType = m_nodeInfo[m_outputIndex].getAsAudio()->format.sampleType; if(numChannels == 1) { if(bytesPerSample == 2) return QByteArray::fromRawData(reinterpret_cast( m_cpVSAPI->getReadPtr(a_cpFrame, 0)), bytesPerSample * VS_AUDIO_FRAME_SAMPLES); else if(sampleType == stFloat) { std::vector cache; cache.reserve(numChannels * VS_AUDIO_FRAME_SAMPLES); auto ptr0 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 0)); auto stride = m_cpVSAPI->getStride(a_cpFrame, 0) / bytesPerSample; for (ptrdiff_t i = 0; i < stride; ++i) cache[i] = dither32Fto16(ptr0[i]); return QByteArray::fromRawData(reinterpret_cast(cache.data()), numChannels * 2 * VS_AUDIO_FRAME_SAMPLES); } else { std::vector cache; cache.reserve(numChannels * VS_AUDIO_FRAME_SAMPLES); auto ptr0 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 0)); auto stride = m_cpVSAPI->getStride(a_cpFrame, 0) / bytesPerSample; for (ptrdiff_t i = 0; i < stride; ++i) cache[i] = dither32to16(ptr0[i]); return QByteArray::fromRawData(reinterpret_cast(cache.data()), numChannels * 2 * VS_AUDIO_FRAME_SAMPLES); } } else { if(sampleType == stFloat) { std::vector cache; cache.reserve(numChannels * VS_AUDIO_FRAME_SAMPLES); auto ptr0 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 0)); auto ptr1 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 1)); auto stride = m_cpVSAPI->getStride(a_cpFrame, 0) / bytesPerSample; for (ptrdiff_t i = 0; i < stride; ++i) { cache[2 * i] = dither32Fto16(ptr0[i]); cache[2 * i + 1] = dither32Fto16(ptr1[i]); } return QByteArray::fromRawData(reinterpret_cast(cache.data()), numChannels * 2 * VS_AUDIO_FRAME_SAMPLES); } else if(bytesPerSample == 2) { std::vector cache; cache.reserve(numChannels * VS_AUDIO_FRAME_SAMPLES); auto ptr0 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 0)); auto ptr1 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 1)); auto stride = m_cpVSAPI->getStride(a_cpFrame, 0) / bytesPerSample; for (ptrdiff_t i = 0; i < stride; ++i) { cache[2 * i] = ptr0[i]; cache[2 * i + 1] = ptr1[i]; } return QByteArray::fromRawData(reinterpret_cast(cache.data()), numChannels * 2 * VS_AUDIO_FRAME_SAMPLES); } else { std::vector cache; cache.reserve(numChannels * VS_AUDIO_FRAME_SAMPLES); auto ptr0 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 0)); auto ptr1 = reinterpret_cast(m_cpVSAPI->getReadPtr(a_cpFrame, 1)); auto stride = m_cpVSAPI->getStride(a_cpFrame, 0) / bytesPerSample; for (ptrdiff_t i = 0; i < stride; ++i) { cache[2 * i] = dither32to16(ptr0[i]); cache[2 * i + 1] = dither32to16(ptr1[i]); } return QByteArray::fromRawData(reinterpret_cast(cache.data()), numChannels * 2 * VS_AUDIO_FRAME_SAMPLES); } } } #endif void PreviewDialog::slotToggleFrameProps() { if(m_pFramePropsPanel->isVisible()) { m_pFramePropsPanel->setVisible(false); } else { updateFrameProps(true); m_pFramePropsPanel->setVisible(true); } } // END OF void PreviewDialog::slotToggleFrameProps() //============================================================================== void PreviewDialog::slotSwitchOutputIndex(int a_outputIndex) { // Assuming there's a change if(a_outputIndex == m_outputIndex) return; // Assuming already initialized if(!m_pVapourSynthScriptProcessor->isInitialized()) return; // Don't switch when playing (avoiding big troubles ahead) if(m_playing) return; // Don't switch when busy processing the current frame if(busy()) return; // Check if output index is available VSNodeInfo ni = m_pVapourSynthScriptProcessor->nodeInfo(a_outputIndex); if(ni.isInvalid()) return; #ifdef Q_OS_WIN // AUDIO stopAudioOutput(); m_currentIsAudio = ni.mediaType() == mtAudio; #else if(ni.isAudio()) { QString errorString = tr("Output node #%1 is audio. " "Previewing and encoding audio are not supported.") .arg(a_outputIndex); emit signalWriteLogMessage( a_outputIndex == 0 ? mtCritical : mtWarning, errorString); return; } #endif m_outputIndex = a_outputIndex; // Update stuff m_clipName = ""; m_sceneName = ""; m_toChangeTitle = true; m_nodeInfo[m_outputIndex] = ni; int lastFrameNumber = m_nodeInfo[m_outputIndex].numFrames() - 1; m_ui.frameNumberSpinBox->setMaximum(lastFrameNumber); m_ui.frameNumberSlider->setFramesNumber( m_nodeInfo[m_outputIndex].numFrames(), false); auto fpsPair = m_nodeInfo[m_outputIndex].fpsPair(); m_fpsNum = fpsPair.first; m_fpsDen = fpsPair.second; m_ui.frameNumberSlider->setFPS(m_fpsDen == 0 ? 0.0 : (double)m_fpsNum / (double)m_fpsDen); m_pStatusBarWidget->setNodeInfo(m_nodeInfo[m_outputIndex], m_cpVSAPI); bool setFrameFromTimestamps = false; SyncOutputNodesMode syncMode = m_pSettingsManager->getSyncOutputMode(); if(syncMode == SyncOutputNodesMode::Time) setFrameFromTimestamps = true; else if(syncMode == SyncOutputNodesMode::FromTimeLine) { TimeLineSlider::DisplayMode timeLineMode = (TimeLineSlider::DisplayMode) m_ui.timeLineModeComboBox->currentData().toInt(); if(timeLineMode == TimeLineSlider::DisplayMode::Time) setFrameFromTimestamps = true; } if(setFrameFromTimestamps) m_frameExpected = timestampToFrame(m_frameTimestampExpected); if(m_frameExpected > lastFrameNumber) setExpectedFrame(lastFrameNumber); else if(m_frameExpected < 0) setExpectedFrame(0); #ifdef Q_OS_WIN // AUDIO if(m_currentIsAudio) { if(m_ui.cropCheckButton->isChecked()) m_ui.cropCheckButton->click(); m_ui.cropCheckButton->setEnabled(false); m_pActionToggleCropPanel->setEnabled(false); m_ui.saveSnapshotButton->setEnabled(false); m_pActionSaveSnapshot->setEnabled(false); m_pStatusBarWidget->setColorPickerString(""); m_ui.playFpsLimitSpinBox->setEnabled(false); m_ui.playFpsLimitModeComboBox->setEnabled(false); int comboIndex = m_ui.playFpsLimitModeComboBox->findData( (int)PlayFPSLimitMode::FromVideo); if(comboIndex != -1) m_ui.playFpsLimitModeComboBox->setCurrentIndex(comboIndex); setAudioOutput(); } else { resetCropSpinBoxes(); m_ui.cropCheckButton->setEnabled(true); m_pActionToggleCropPanel->setEnabled(true); m_ui.saveSnapshotButton->setEnabled(true); m_pActionSaveSnapshot->setEnabled(true); m_ui.playFpsLimitSpinBox->setEnabled(true); m_ui.playFpsLimitModeComboBox->setEnabled(true); } #else resetCropSpinBoxes(); #endif slotSetPlayFPSLimit(); resetCropSpinBoxes(); m_pVapourSynthScriptProcessor->requestFrameAsync(m_frameExpected, m_outputIndex, true); slotShowFrame(m_frameExpected, false); } // END OF void PreviewDialog::slotSwitchOutputIndex(int a_outputIndex) //============================================================================== void PreviewDialog::slotEnableSwitchOutputIndex(bool a_idle) { if(m_playing) { m_ui.outputIndexComboBox->setEnabled(false); return; } m_ui.outputIndexComboBox->setEnabled(a_idle); } void PreviewDialog::setOutputIndex(int a_index) { if(!m_ui.outputIndexComboBox->isEnabled()) return; if(m_playing) return; if(a_index == m_outputIndex) return; if(m_outputIndices.size() == 0) return slotSwitchOutputIndex(a_index); if(vsedit::contains(m_outputIndices, a_index)) { m_ui.outputIndexComboBox->setCurrentText(QString::number(a_index)); } else { emit signalWriteLogMessage(mtWarning, QString("Couldn't resolve output node #%1.").arg(a_index)); } } void PreviewDialog::slotSwitchToPreviousOutputIndex() { size_t num = m_outputIndices.size(); if(num <= 1) return; if(m_outputIndex == m_outputIndices[0]) return setOutputIndex(m_outputIndices[num - 1]); auto idx = std::upper_bound(m_outputIndices.begin(), m_outputIndices.end(), m_outputIndex - 1); if(idx == m_outputIndices.end()) return setOutputIndex(m_outputIndices[num - 1]); else return setOutputIndex(*(idx-1)); } void PreviewDialog::slotSwitchToNextOutputIndex() { size_t num = m_outputIndices.size(); if(num <= 1) return; if(m_outputIndex == m_outputIndices[num - 1]) return setOutputIndex(m_outputIndices[0]); auto idx = std::lower_bound(m_outputIndices.begin(), m_outputIndices.end(), m_outputIndex + 1); if(idx == m_outputIndices.end()) return setOutputIndex(m_outputIndices[0]); else return setOutputIndex(*idx); } void PreviewDialog::createActionsAndMenus() { struct ActionToCreate { QAction ** ppAction; const char * id; bool checkable; const char * slotToConnect; }; ActionToCreate actionsToCreate[] = { {&m_pActionFrameToClipboard, ACTION_ID_FRAME_TO_CLIPBOARD, false, SLOT(slotFrameToClipboard())}, {&m_pActionSaveSnapshot, ACTION_ID_SAVE_SNAPSHOT, false, SLOT(slotSaveSnapshot())}, {&m_pActionToggleZoomPanel, ACTION_ID_TOGGLE_ZOOM_PANEL, true, SLOT(slotToggleZoomPanelVisible(bool))}, {&m_pActionSetZoomModeNoZoom, ACTION_ID_SET_ZOOM_MODE_NO_ZOOM, true, SLOT(slotZoomModeChanged())}, {&m_pActionSetZoomModeFixedRatio, ACTION_ID_SET_ZOOM_MODE_FIXED_RATIO, true, SLOT(slotZoomModeChanged())}, {&m_pActionSetZoomModeFitToFrame, ACTION_ID_SET_ZOOM_MODE_FIT_TO_FRAME, true, SLOT(slotZoomModeChanged())}, {&m_pActionSetZoomScaleModeNearest, ACTION_ID_SET_ZOOM_SCALE_MODE_NEAREST, true, SLOT(slotScaleModeChanged())}, {&m_pActionSetZoomScaleModeBilinear, ACTION_ID_SET_ZOOM_SCALE_MODE_BILINEAR, true, SLOT(slotScaleModeChanged())}, {&m_pActionToggleCropPanel, ACTION_ID_TOGGLE_CROP_PANEL, true, SLOT(slotToggleCropPanelVisible(bool))}, {&m_pActionToggleTimeLinePanel, ACTION_ID_TOGGLE_TIMELINE_PANEL, true, SLOT(slotToggleTimeLinePanelVisible(bool))}, {&m_pActionSetTimeLineModeTime, ACTION_ID_SET_TIMELINE_MODE_TIME, true, SLOT(slotTimeLineModeChanged())}, {&m_pActionSetTimeLineModeFrames, ACTION_ID_SET_TIMELINE_MODE_FRAMES, true, SLOT(slotTimeLineModeChanged())}, {&m_pActionTimeStepForward, ACTION_ID_TIME_STEP_FORWARD, false, SLOT(slotTimeStepForward())}, {&m_pActionTimeStepBack, ACTION_ID_TIME_STEP_BACK, false, SLOT(slotTimeStepBack())}, {&m_pActionPasteCropSnippetIntoScript, ACTION_ID_PASTE_CROP_SNIPPET_INTO_SCRIPT, false, SLOT(slotPasteCropSnippetIntoScript())}, {&m_pActionAdvancedSettingsDialog, ACTION_ID_ADVANCED_PREVIEW_SETTINGS, false, SLOT(slotCallAdvancedSettingsDialog())}, {&m_pActionToggleColorPicker, ACTION_ID_TOGGLE_COLOR_PICKER, true, SLOT(slotToggleColorPicker(bool))}, {&m_pActionPlay, ACTION_ID_PLAY, true, SLOT(slotPlay(bool))}, {&m_pActionLoadChapters, ACTION_ID_TIMELINE_LOAD_CHAPTERS, false, SLOT(slotLoadChapters())}, {&m_pActionClearBookmarks, ACTION_ID_TIMELINE_CLEAR_BOOKMARKS, false, SLOT(slotClearBookmarks())}, {&m_pActionBookmarkCurrentFrame, ACTION_ID_TIMELINE_BOOKMARK_CURRENT_FRAME, false, SLOT(slotBookmarkCurrentFrame())}, {&m_pActionUnbookmarkCurrentFrame, ACTION_ID_TIMELINE_UNBOOKMARK_CURRENT_FRAME, false, SLOT(slotUnbookmarkCurrentFrame())}, {&m_pActionGoToPreviousBookmark, ACTION_ID_TIMELINE_GO_TO_PREVIOUS_BOOKMARK, false, SLOT(slotGoToPreviousBookmark())}, {&m_pActionGoToNextBookmark, ACTION_ID_TIMELINE_GO_TO_NEXT_BOOKMARK, false, SLOT(slotGoToNextBookmark())}, {&m_pActionPasteShownFrameNumberIntoScript, ACTION_ID_PASTE_SHOWN_FRAME_NUMBER_INTO_SCRIPT, false, SLOT(slotPasteShownFrameNumberIntoScript())}, {&m_pActionJumpToFrame, ACTION_ID_JUMP_TO_FRAME, false, SLOT(slotJumpToFrame())}, {&m_pActionToggleFramePropsPanel, ACTION_ID_TOGGLE_FRAME_PROPS, false, SLOT(slotToggleFrameProps())}, {&m_pActionSwitchToOutputIndex0, ACTION_ID_SET_OUTPUT_INDEX_0, false, SLOT(slotSwitchOutputIndex0())}, {&m_pActionSwitchToOutputIndex1, ACTION_ID_SET_OUTPUT_INDEX_1, false, SLOT(slotSwitchOutputIndex1())}, {&m_pActionSwitchToOutputIndex2, ACTION_ID_SET_OUTPUT_INDEX_2, false, SLOT(slotSwitchOutputIndex2())}, {&m_pActionSwitchToOutputIndex3, ACTION_ID_SET_OUTPUT_INDEX_3, false, SLOT(slotSwitchOutputIndex3())}, {&m_pActionSwitchToOutputIndex4, ACTION_ID_SET_OUTPUT_INDEX_4, false, SLOT(slotSwitchOutputIndex4())}, {&m_pActionSwitchToOutputIndex5, ACTION_ID_SET_OUTPUT_INDEX_5, false, SLOT(slotSwitchOutputIndex5())}, {&m_pActionSwitchToOutputIndex6, ACTION_ID_SET_OUTPUT_INDEX_6, false, SLOT(slotSwitchOutputIndex6())}, {&m_pActionSwitchToOutputIndex7, ACTION_ID_SET_OUTPUT_INDEX_7, false, SLOT(slotSwitchOutputIndex7())}, {&m_pActionSwitchToOutputIndex8, ACTION_ID_SET_OUTPUT_INDEX_8, false, SLOT(slotSwitchOutputIndex8())}, {&m_pActionSwitchToOutputIndex9, ACTION_ID_SET_OUTPUT_INDEX_9, false, SLOT(slotSwitchOutputIndex9())}, {&m_pActionSwitchToOutputIndex10, ACTION_ID_SET_OUTPUT_INDEX_10, false, SLOT(slotSwitchOutputIndex10())}, {&m_pActionSwitchToOutputIndex11, ACTION_ID_SET_OUTPUT_INDEX_11, false, SLOT(slotSwitchOutputIndex11())}, {&m_pActionSwitchToOutputIndex12, ACTION_ID_SET_OUTPUT_INDEX_12, false, SLOT(slotSwitchOutputIndex12())}, {&m_pActionSwitchToOutputIndex13, ACTION_ID_SET_OUTPUT_INDEX_13, false, SLOT(slotSwitchOutputIndex13())}, {&m_pActionSwitchToOutputIndex14, ACTION_ID_SET_OUTPUT_INDEX_14, false, SLOT(slotSwitchOutputIndex14())}, {&m_pActionSwitchToOutputIndex15, ACTION_ID_SET_OUTPUT_INDEX_15, false, SLOT(slotSwitchOutputIndex15())}, {&m_pActionSwitchToOutputIndex16, ACTION_ID_SET_OUTPUT_INDEX_16, false, SLOT(slotSwitchOutputIndex16())}, {&m_pActionSwitchToOutputIndex17, ACTION_ID_SET_OUTPUT_INDEX_17, false, SLOT(slotSwitchOutputIndex17())}, {&m_pActionSwitchToOutputIndex18, ACTION_ID_SET_OUTPUT_INDEX_18, false, SLOT(slotSwitchOutputIndex18())}, {&m_pActionSwitchToOutputIndex19, ACTION_ID_SET_OUTPUT_INDEX_19, false, SLOT(slotSwitchOutputIndex19())}, {&m_pActionSwitchToPreviousOutputIndex, ACTION_ID_PREVIOUS_OUTPUT_INDEX, false, SLOT(slotSwitchToPreviousOutputIndex())}, {&m_pActionSwitchToNextOutputIndex, ACTION_ID_NEXT_OUTPUT_INDEX, false, SLOT(slotSwitchToNextOutputIndex())}, }; for(ActionToCreate & item : actionsToCreate) { QAction * pAction = m_pSettingsManager->createStandardAction(item.id, this); *item.ppAction = pAction; pAction->setCheckable(item.checkable); m_settableActionsList.push_back(pAction); } //------------------------------------------------------------------------------ m_pPreviewContextMenu = new QMenu(this); vsedit::disableFontKerning(m_pPreviewContextMenu); m_pPreviewContextMenu->addAction(m_pActionJumpToFrame); m_pPreviewContextMenu->addAction(m_pActionFrameToClipboard); m_pPreviewContextMenu->addAction(m_pActionSaveSnapshot); m_pPreviewContextMenu->addAction(m_pActionToggleFramePropsPanel); m_pActionToggleZoomPanel->setChecked( m_pSettingsManager->getZoomPanelVisible()); m_pPreviewContextMenu->addAction(m_pActionToggleZoomPanel); //------------------------------------------------------------------------------ m_pMenuZoomModes = new QMenu(m_pPreviewContextMenu); vsedit::disableFontKerning(m_pMenuZoomModes); m_pMenuZoomModes->setTitle(tr("Zoom mode")); m_pPreviewContextMenu->addMenu(m_pMenuZoomModes); m_pActionGroupZoomModes = new QActionGroup(this); ZoomMode zoomMode = m_pSettingsManager->getZoomMode(); struct ZoomModeAction { QAction * pAction; ZoomMode zoomMode; }; ZoomModeAction zoomModeActions[] = { {m_pActionSetZoomModeNoZoom, ZoomMode::NoZoom}, {m_pActionSetZoomModeFixedRatio, ZoomMode::FixedRatio}, {m_pActionSetZoomModeFitToFrame, ZoomMode::FitToFrame}, }; for(ZoomModeAction & action : zoomModeActions) { QString id = action.pAction->data().toString(); action.pAction->setActionGroup(m_pActionGroupZoomModes); m_pMenuZoomModes->addAction(action.pAction); m_actionIDToZoomModeMap[id] = action.zoomMode; addAction(action.pAction); if(zoomMode == action.zoomMode) action.pAction->setChecked(true); } //------------------------------------------------------------------------------ m_pMenuZoomScaleModes = new QMenu(m_pPreviewContextMenu); vsedit::disableFontKerning(m_pMenuZoomScaleModes); m_pMenuZoomScaleModes->setTitle(tr("Zoom scale mode")); m_pPreviewContextMenu->addMenu(m_pMenuZoomScaleModes); m_pActionGroupZoomScaleModes = new QActionGroup(this); Qt::TransformationMode scaleMode = m_pSettingsManager->getScaleMode(); struct ScaleModeAction { QAction * pAction; Qt::TransformationMode scaleMode; }; ScaleModeAction scaleModeActions[] = { {m_pActionSetZoomScaleModeNearest, Qt::FastTransformation}, {m_pActionSetZoomScaleModeBilinear, Qt::SmoothTransformation}, }; for(ScaleModeAction & action : scaleModeActions) { QString id = action.pAction->data().toString(); action.pAction->setActionGroup(m_pActionGroupZoomScaleModes); m_pMenuZoomScaleModes->addAction(action.pAction); m_actionIDToZoomScaleModeMap[id] = action.scaleMode; addAction(action.pAction); if(scaleMode == action.scaleMode) action.pAction->setChecked(true); } bool noZoom = (zoomMode == ZoomMode::NoZoom); m_pMenuZoomScaleModes->setEnabled(!noZoom); //------------------------------------------------------------------------------ m_pPreviewContextMenu->addAction(m_pActionToggleCropPanel); m_pPreviewContextMenu->addAction(m_pActionToggleTimeLinePanel); m_pActionToggleTimeLinePanel->setChecked( m_pSettingsManager->getTimeLinePanelVisible()); //------------------------------------------------------------------------------ m_pMenuTimeLineModes= new QMenu(m_pPreviewContextMenu); vsedit::disableFontKerning(m_pMenuTimeLineModes); m_pMenuTimeLineModes->setTitle(tr("Timeline display mode")); m_pPreviewContextMenu->addMenu(m_pMenuTimeLineModes); m_pActionGroupTimeLineModes = new QActionGroup(this); TimeLineSlider::DisplayMode timeLineMode = m_pSettingsManager->getTimeLineMode(); struct TimeLineModeAction { QAction * pAction; TimeLineSlider::DisplayMode timeLineMode; }; TimeLineModeAction timeLineModeAction[] = { {m_pActionSetTimeLineModeTime, TimeLineSlider::DisplayMode::Time}, {m_pActionSetTimeLineModeFrames, TimeLineSlider::DisplayMode::Frames}, }; for(TimeLineModeAction & action : timeLineModeAction) { QString id = action.pAction->data().toString(); action.pAction->setActionGroup(m_pActionGroupTimeLineModes); m_pMenuTimeLineModes->addAction(action.pAction); m_actionIDToTimeLineModeMap[id] = action.timeLineMode; addAction(action.pAction); if(timeLineMode == action.timeLineMode) action.pAction->setChecked(true); } //------------------------------------------------------------------------------ addAction(m_pActionTimeStepForward); addAction(m_pActionTimeStepBack); m_pActionToggleColorPicker->setChecked( m_pSettingsManager->getColorPickerVisible()); m_pPreviewContextMenu->addAction(m_pActionToggleColorPicker); m_pActionPlay->setChecked(false); addAction(m_pActionPlay); addAction(m_pActionLoadChapters); addAction(m_pActionClearBookmarks); addAction(m_pActionBookmarkCurrentFrame); addAction(m_pActionUnbookmarkCurrentFrame); addAction(m_pActionGoToPreviousBookmark); addAction(m_pActionGoToNextBookmark); addAction(m_pActionPasteShownFrameNumberIntoScript); m_pPreviewContextMenu->addAction(m_pActionPasteShownFrameNumberIntoScript); addAction(m_pActionJumpToFrame); addAction(m_pActionToggleFramePropsPanel); //------------------------------------------------------------------------------ addAction(m_pActionSwitchToOutputIndex0); addAction(m_pActionSwitchToOutputIndex1); addAction(m_pActionSwitchToOutputIndex2); addAction(m_pActionSwitchToOutputIndex3); addAction(m_pActionSwitchToOutputIndex4); addAction(m_pActionSwitchToOutputIndex5); addAction(m_pActionSwitchToOutputIndex6); addAction(m_pActionSwitchToOutputIndex7); addAction(m_pActionSwitchToOutputIndex8); addAction(m_pActionSwitchToOutputIndex9); addAction(m_pActionSwitchToOutputIndex10); addAction(m_pActionSwitchToOutputIndex11); addAction(m_pActionSwitchToOutputIndex12); addAction(m_pActionSwitchToOutputIndex13); addAction(m_pActionSwitchToOutputIndex14); addAction(m_pActionSwitchToOutputIndex15); addAction(m_pActionSwitchToOutputIndex16); addAction(m_pActionSwitchToOutputIndex17); addAction(m_pActionSwitchToOutputIndex18); addAction(m_pActionSwitchToOutputIndex19); addAction(m_pActionSwitchToPreviousOutputIndex); addAction(m_pActionSwitchToNextOutputIndex); //------------------------------------------------------------------------------ for(ActionToCreate & item : actionsToCreate) { const char * signal = item.checkable ? SIGNAL(toggled(bool)) : SIGNAL(triggered()); connect(*item.ppAction, signal, this, item.slotToConnect); } } // END OF void PreviewDialog::createActionsAndMenus() //============================================================================== void PreviewDialog::setUpZoomPanel() { m_ui.zoomPanel->setVisible(m_pSettingsManager->getZoomPanelVisible()); m_ui.zoomRatioSpinBox->setLocale(QLocale("C")); m_ui.zoomCheckButton->setDefaultAction(m_pActionToggleZoomPanel); m_ui.zoomModeComboBox->addItem(QIcon(":zoom_no_zoom.png"), tr("No zoom"), (int)ZoomMode::NoZoom); m_ui.zoomModeComboBox->addItem(QIcon(":zoom_fixed_ratio.png"), tr("Fixed ratio"), (int)ZoomMode::FixedRatio); m_ui.zoomModeComboBox->addItem(QIcon(":zoom_fit_to_frame.png"), tr("Fit to frame"), (int)ZoomMode::FitToFrame); ZoomMode zoomMode = m_pSettingsManager->getZoomMode(); int comboIndex = m_ui.zoomModeComboBox->findData((int)zoomMode); if(comboIndex != -1) m_ui.zoomModeComboBox->setCurrentIndex(comboIndex); bool fixedRatio(zoomMode == ZoomMode::FixedRatio); m_ui.zoomRatioSpinBox->setEnabled(fixedRatio); double zoomRatio = m_pSettingsManager->getZoomRatio(); m_ui.zoomRatioSpinBox->setValue(zoomRatio); m_ui.scaleModeComboBox->addItem(tr("Nearest"), (int)Qt::FastTransformation); m_ui.scaleModeComboBox->addItem(tr("Bilinear"), (int)Qt::SmoothTransformation); bool noZoom = (zoomMode == ZoomMode::NoZoom); m_ui.scaleModeComboBox->setEnabled(!noZoom); Qt::TransformationMode scaleMode = m_pSettingsManager->getScaleMode(); m_ui.zoomRatioSpinBox->setScaleMode(scaleMode); comboIndex = m_ui.scaleModeComboBox->findData((int)scaleMode); if(comboIndex != -1) m_ui.scaleModeComboBox->setCurrentIndex(comboIndex); connect(m_ui.zoomModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotZoomModeChanged())); connect(m_ui.zoomRatioSpinBox, SIGNAL(valueChanged(double)), this, SLOT(slotZoomRatioChanged(double))); connect(m_ui.scaleModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotScaleModeChanged())); } // END OF void PreviewDialog::setUpZoomPanel() //============================================================================== void PreviewDialog::setUpTimeLinePanel() { m_ui.timeLinePanel->setVisible( m_pSettingsManager->getTimeLinePanelVisible()); m_ui.playButton->setDefaultAction(m_pActionPlay); m_ui.timeLineCheckButton->setDefaultAction(m_pActionToggleTimeLinePanel); m_ui.timeStepForwardButton->setDefaultAction(m_pActionTimeStepForward); m_ui.timeStepBackButton->setDefaultAction(m_pActionTimeStepBack); m_ui.playFpsLimitModeComboBox->addItem(tr("From script"), (int)PlayFPSLimitMode::FromVideo); m_ui.playFpsLimitModeComboBox->addItem(tr("No limit"), (int)PlayFPSLimitMode::NoLimit); m_ui.playFpsLimitModeComboBox->addItem(tr("Custom"), (int)PlayFPSLimitMode::Custom); PlayFPSLimitMode playFpsLimitMode = m_pSettingsManager->getPlayFPSLimitMode(); int comboIndex = m_ui.playFpsLimitModeComboBox->findData( (int)playFpsLimitMode); if(comboIndex != -1) m_ui.playFpsLimitModeComboBox->setCurrentIndex(comboIndex); m_ui.playFpsLimitSpinBox->setLocale(QLocale("C")); double customFPS = m_pSettingsManager->getPlayFPSLimit(); m_ui.playFpsLimitSpinBox->setValue(customFPS); slotSetPlayFPSLimit(); m_ui.loadChaptersButton->setDefaultAction(m_pActionLoadChapters); m_ui.clearBookmarksButton->setDefaultAction(m_pActionClearBookmarks); m_ui.bookmarkCurrentFrameButton->setDefaultAction( m_pActionBookmarkCurrentFrame); m_ui.unbookmarkCurrentFrameButton->setDefaultAction( m_pActionUnbookmarkCurrentFrame); m_ui.goToPreviousBookmarkButton->setDefaultAction( m_pActionGoToPreviousBookmark); m_ui.goToNextBookmarkButton->setDefaultAction( m_pActionGoToNextBookmark); double timeStep = m_pSettingsManager->getTimeStep(); m_ui.timeStepEdit->setTime(vsedit::secondsToQTime(timeStep)); m_ui.timeLineModeComboBox->addItem(QIcon(":timeline.png"), tr("Time"), (int)TimeLineSlider::DisplayMode::Time); m_ui.timeLineModeComboBox->addItem(QIcon(":timeline_frames.png"), tr("Frames"), (int)TimeLineSlider::DisplayMode::Frames); TimeLineSlider::DisplayMode timeLineMode = m_pSettingsManager->getTimeLineMode(); comboIndex = m_ui.timeLineModeComboBox->findData((int)timeLineMode); if(comboIndex != -1) m_ui.timeLineModeComboBox->setCurrentIndex(comboIndex); connect(m_ui.timeStepEdit, SIGNAL(timeChanged(const QTime &)), this, SLOT(slotTimeStepChanged(const QTime &))); connect(m_ui.timeLineModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotTimeLineModeChanged())); connect(m_ui.playFpsLimitModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetPlayFPSLimit())); connect(m_ui.playFpsLimitSpinBox, SIGNAL(valueChanged(double)), this, SLOT(slotSetPlayFPSLimit())); } // END OF void PreviewDialog::setUpTimeLinePanel() //============================================================================== void PreviewDialog::setUpCropPanel() { m_ui.cropCheckButton->setDefaultAction(m_pActionToggleCropPanel); m_ui.cropModeComboBox->addItem(tr("Absolute"), (int)CropMode::Absolute); m_ui.cropModeComboBox->addItem(tr("Relative"), (int)CropMode::Relative); CropMode cropMode = m_pSettingsManager->getCropMode(); int cropModeIndex = m_ui.cropModeComboBox->findData((int)cropMode); m_ui.cropModeComboBox->setCurrentIndex(cropModeIndex); slotCropModeChanged(); m_ui.cropZoomRatioSpinBox->setValue(m_pSettingsManager->getCropZoomRatio()); m_ui.cropPasteToScriptButton->setDefaultAction( m_pActionPasteCropSnippetIntoScript); m_ui.cropPanel->setVisible(false); connect(m_ui.cropModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCropModeChanged())); connect(m_ui.cropLeftSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropLeftValueChanged(int))); connect(m_ui.cropTopSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropTopValueChanged(int))); connect(m_ui.cropWidthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropWidthValueChanged(int))); connect(m_ui.cropHeightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropHeightValueChanged(int))); connect(m_ui.cropRightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropRightValueChanged(int))); connect(m_ui.cropBottomSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropBottomValueChanged(int))); connect(m_ui.cropZoomRatioSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slotCropZoomRatioValueChanged(int))); } // END OF void PreviewDialog::setUpCropPanel() //============================================================================== bool PreviewDialog::requestShowFrame(int a_frameNumber) { if(!m_pVapourSynthScriptProcessor->isInitialized()) return false; if((m_frameShown != -1) && (m_frameShown != m_frameExpected)) return false; m_pVapourSynthScriptProcessor->requestFrameAsync(a_frameNumber, m_outputIndex, true); return true; } // END OF bool PreviewDialog::requestShowFrame(int a_frameNumber) //============================================================================== void PreviewDialog::setPreviewPixmap() { m_devicePixelRatio = window()->devicePixelRatioF(); if(!m_framePixmap.isNull()) m_framePixmap.setDevicePixelRatio(m_devicePixelRatio); if(m_ui.cropPanel->isVisible()) { int cropLeft = m_ui.cropLeftSpinBox->value(); int cropTop = m_ui.cropTopSpinBox->value(); int cropWidth = m_ui.cropWidthSpinBox->value(); int cropHeight = m_ui.cropHeightSpinBox->value(); QPixmap croppedPixmap = m_framePixmap.copy(cropLeft, cropTop, cropWidth, cropHeight); int ratio = m_ui.cropZoomRatioSpinBox->value(); if(abs(ratio * m_devicePixelRatio - 1) < 1e-7) { m_ui.previewArea->setPixmap(croppedPixmap); return; } QPixmap zoomedPixmap = croppedPixmap.scaled( croppedPixmap.width() * ratio, croppedPixmap.height() * ratio, Qt::KeepAspectRatio, Qt::FastTransformation); m_ui.previewArea->setPixmap(zoomedPixmap); return; } ZoomMode zoomMode = (ZoomMode)m_ui.zoomModeComboBox->currentData().toInt(); if(zoomMode == ZoomMode::NoZoom) { m_ui.previewArea->setPixmap(m_framePixmap, true); return; } QPixmap previewPixmap; int frameWidth = 0; int frameHeight = 0; Qt::TransformationMode scaleMode = (Qt::TransformationMode) m_ui.scaleModeComboBox->currentData().toInt(); if(zoomMode == ZoomMode::FixedRatio) { double ratio = m_ui.zoomRatioSpinBox->value(); frameWidth = m_framePixmap.width() * ratio; frameHeight = m_framePixmap.height() * ratio; } else { if(m_framePixmap.isNull()) return; QRect previewRect = m_ui.previewArea->geometry(); int cropSize = m_ui.previewArea->frameWidth() * 2; frameWidth = previewRect.width() * m_devicePixelRatio - cropSize; frameHeight = previewRect.height() * m_devicePixelRatio - cropSize; } previewPixmap = m_framePixmap.scaled(frameWidth, frameHeight, Qt::KeepAspectRatio, scaleMode); m_ui.previewArea->setPixmap(previewPixmap, true); } // END OF bool void PreviewDialog::setPreviewPixmap() //============================================================================== void PreviewDialog::recalculateCropMods() { QSpinBox * cropSpinBoxes[] = {m_ui.cropLeftSpinBox, m_ui.cropTopSpinBox, m_ui.cropWidthSpinBox, m_ui.cropHeightSpinBox, m_ui.cropRightSpinBox, m_ui.cropBottomSpinBox}; for(QSpinBox * pSpinBox : cropSpinBoxes) { int value = pSpinBox->value(); if(value == 0) pSpinBox->setSuffix(""); else { int sizeMod = vsedit::mod(value); pSpinBox->setSuffix(QString(" |%1|").arg(sizeMod)); } } } // END OF void PreviewDialog::recalculateCropMods() //============================================================================== void PreviewDialog::resetCropSpinBoxes() { BEGIN_CROP_VALUES_CHANGE const VSVideoInfo * vi = m_nodeInfo[m_outputIndex].getAsVideo(); if(!vi) { END_CROP_VALUES_CHANGE return; } m_ui.cropLeftSpinBox->setMaximum(vi->width - 1); m_ui.cropLeftSpinBox->setValue(0); m_ui.cropTopSpinBox->setMaximum(vi->height - 1); m_ui.cropTopSpinBox->setValue(0); m_ui.cropWidthSpinBox->setMaximum(vi->width); m_ui.cropWidthSpinBox->setValue(vi->width); m_ui.cropHeightSpinBox->setMaximum(vi->height); m_ui.cropHeightSpinBox->setValue(vi->height); m_ui.cropRightSpinBox->setMaximum(vi->width - 1); m_ui.cropRightSpinBox->setValue(0); m_ui.cropBottomSpinBox->setMaximum(vi->height - 1); m_ui.cropBottomSpinBox->setValue(0); recalculateCropMods(); END_CROP_VALUES_CHANGE } // END OF void PreviewDialog::resetCropSpinBoxes() //============================================================================== void PreviewDialog::setCurrentFrame(const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame) { Q_ASSERT(m_cpVSAPI); m_cpVSAPI->freeFrame(m_cpFrame); m_cpFrame = a_cpOutputFrame; // Copy clip and scene names const VSMap *props = m_cpVSAPI->getFramePropertiesRO(m_cpFrame); int err; bool toChangeTitle = m_toChangeTitle; const char * clipName = m_cpVSAPI->mapGetData( props, "Name", 0, &err); if(m_clipName != clipName) // can be nullptr? { m_clipName = QString(clipName ? clipName : ""); toChangeTitle = true; } const char * sceneName = m_cpVSAPI->mapGetData( props, "SceneName", 0, &err); if(m_sceneName != sceneName) { m_sceneName = QString(sceneName ? sceneName : ""); toChangeTitle = true; } double absoluteTime = m_cpVSAPI->mapGetFloat( props, "_AbsoluteTime", 0, &err); if(!err) { QString absTimeStr = vsedit::timeToString(absoluteTime); if(m_absoluteTime != absTimeStr) { m_absoluteTime = absTimeStr; toChangeTitle = true; } } else { if(!m_absoluteTime.isEmpty()) m_toChangeTitle = true; m_absoluteTime = ""; } if(toChangeTitle) { m_toChangeTitle = false; setTitle(); } if (m_cpPreviewFrame) { m_cpVSAPI->freeFrame(m_cpPreviewFrame); m_cpPreviewFrame = nullptr; } m_framePixmap = pixmapFromRGB(a_cpPreviewFrame); m_cpPreviewFrame = a_cpPreviewFrame; setPreviewPixmap(); QPointF pixelPos = m_ui.previewArea->pixelPosition(); m_ui.previewArea->checkMouseOverPreview(pixelPos); updateFrameProps(false); } // END OF void PreviewDialog::setCurrentFrame( // const VSFrame * a_cpOutputFrame, // const VSFrame * a_cpPreviewFrame) //============================================================================== void PreviewDialog::updateFrameProps(bool a_forced) { if(a_forced || (isVisible() && m_cpFrame)) { QString props = m_pVapourSynthScriptProcessor-> framePropsString(m_cpFrame); QString info = QString("Index %1 | Frame %2 \n\n") .arg(m_outputIndex).arg(m_frameExpected); m_pFramePropsPanel->setText(info + props + QString("\n")); } } // END OF void PreviewDialog::updateFrameProps(bool a_forced) //============================================================================== double PreviewDialog::valueAtPoint(size_t a_x, size_t a_y, int a_plane) { Q_ASSERT(m_cpVSAPI); if(!m_cpFrame) return 0.0; const VSVideoFormat * cpFormat = m_cpVSAPI->getVideoFrameFormat(m_cpFrame); Q_ASSERT((a_plane >= 0) && (a_plane < cpFormat->numPlanes)); const uint8_t * cpPlane = m_cpVSAPI->getReadPtr(m_cpFrame, a_plane); size_t x = a_x; size_t y = a_y; if(a_plane != 0) { x = (a_x >> cpFormat->subSamplingW); y = (a_y >> cpFormat->subSamplingH); } int stride = m_cpVSAPI->getStride(m_cpFrame, a_plane); const uint8_t * cpLine = cpPlane + y * stride; double value = 0.0; if(cpFormat->sampleType == stInteger) { if(cpFormat->bytesPerSample == 1) value = (double)cpLine[x]; else if(cpFormat->bytesPerSample == 2) value = (double)((uint16_t *)cpLine)[x]; else if(cpFormat->bytesPerSample == 4) value = (double)((uint32_t *)cpLine)[x]; } else if(cpFormat->sampleType == stFloat) { if(cpFormat->bytesPerSample == 2) { vsedit::FP16 half; half.u = ((uint16_t *)cpLine)[x]; vsedit::FP32 single = vsedit::halfToSingle(half); value = (double)single.f; } else if(cpFormat->bytesPerSample == 4) value = (double)((float *)cpLine)[x]; } return value; } // END OF double PreviewDialog::valueAtPoint(size_t a_x, size_t a_y, // int a_plane) //============================================================================== void PreviewDialog::previewValueAtPoint(size_t a_x, size_t a_y, int a_ret[]) { // Read RGB values on screen from packed Gray8 if(!m_cpPreviewFrame) return; const VSMap *props = m_cpVSAPI->getFramePropertiesRO(m_cpPreviewFrame); enum p2p_packing packing_fmt = static_cast(m_cpVSAPI->mapGetInt(props, "PackingFormat", 0, nullptr)); bool is_10_bits = (packing_fmt == p2p_rgb30); if (!is_10_bits) { Q_ASSERT(packing_fmt == p2p_argb32); } const uint8_t * cpPlane = m_cpVSAPI->getReadPtr(m_cpPreviewFrame, 0); size_t x = a_x; size_t y = a_y; int stride = m_cpVSAPI->getStride(m_cpPreviewFrame, 0); const uint8_t * cpLoc = cpPlane + y * stride + x * 4; // libp2p will handle endianness p2p_buffer_param p = {}; p.width = 1; p.height = 1; p.packing = packing_fmt; p.src[0] = cpLoc; p.src_stride[0] = 1; if (is_10_bits) { uint16_t unpacked[3]; for (int plane = 0; plane < 3; ++plane) { p.dst[plane] = &unpacked[plane]; p.dst_stride[plane] = 1; } p2p_unpack_frame(&p, 0); for (int plane = 0; plane < 3; ++plane) { a_ret[plane] = static_cast(unpacked[plane]); } } else { uint8_t unpacked[3]; for (int plane = 0; plane < 3; ++plane) { p.dst[plane] = &unpacked[plane]; p.dst_stride[plane] = 1; } p2p_unpack_frame(&p, 0); for (int plane = 0; plane < 3; ++plane) { a_ret[plane] = static_cast(unpacked[plane]); } } } // END OF void PreviewDialog::previewValueAtPoint(size_t a_x, size_t a_y, // int a_ret[]) //============================================================================== QPixmap PreviewDialog::pixmapFromRGB( const VSFrame * a_cpFrame) { if((!m_cpVSAPI) || (!a_cpFrame)) return QPixmap(); const VSVideoFormat * cpFormat = m_cpVSAPI->getVideoFrameFormat(a_cpFrame); Q_ASSERT(cpFormat); int wwidth = m_cpVSAPI->getFrameWidth(a_cpFrame, 0); if((cpFormat->colorFamily != cfGray) || (cpFormat->bitsPerSample != 8) || (wwidth % 4)) { QString errorString = tr("Error forming pixmap from frame. " "Expected format Gray8 with width divisible by 4. "); emit signalWriteLogMessage(mtCritical, errorString); return QPixmap(); } const VSMap *props = m_cpVSAPI->getFramePropertiesRO(a_cpFrame); enum p2p_packing packing_fmt = static_cast( m_cpVSAPI->mapGetInt(props, "PackingFormat", 0, nullptr)); bool is_10_bits; if (packing_fmt == p2p_rgb30) { is_10_bits = true; } else if (packing_fmt == p2p_argb32) { is_10_bits = false; } else { QString errorString = tr("Error forming pixmap from frame. " "Expected frame being packed from RGB24 or RGB30."); emit signalWriteLogMessage(mtCritical, errorString); return QPixmap(); } int width = wwidth / 4; int height = m_cpVSAPI->getFrameHeight(a_cpFrame, 0); int stride = m_cpVSAPI->getStride(a_cpFrame, 0); const uint8_t * pData = m_cpVSAPI->getReadPtr(a_cpFrame, 0); QImage frameImage(reinterpret_cast(pData), width, height, stride, is_10_bits ? QImage::Format_RGB30 : QImage::Format_ARGB32); QPixmap framePixmap = QPixmap::fromImage(frameImage, Qt::NoFormatConversion); return framePixmap; } // END OF QPixmap PreviewDialog::pixmapFromRGB( // const VSFrame * a_cpFrame) //============================================================================== void PreviewDialog::setTitle() { QString l_scriptName = scriptName(); QString scriptNameTitle = l_scriptName.isEmpty() ? tr("(Untitled)") : l_scriptName; if(m_scriptTextChanged) scriptNameTitle = scriptNameTitle + "*"; QString title = tr("Preview - Index %1 | ").arg(m_outputIndex); if(!m_clipName.isEmpty()) title = title + tr("Name: %1 | ").arg(m_clipName); if(!m_sceneName.isEmpty()) title = title + tr("Scene: %1 | ").arg(m_sceneName); if(!m_absoluteTime.isEmpty()) title = title + tr("Abs Time: %1 | ").arg(m_absoluteTime); title = title + scriptNameTitle; setWindowTitle(title); } // END OF void PreviewDialog::setTitle() //============================================================================== qlonglong PreviewDialog::frameToTimestamp(int a_frame) { if(m_fpsDen == 0 || m_fpsNum == 0) return 0; return a_frame * m_fpsDen * 1000 / m_fpsNum; } int PreviewDialog::timestampToFrame(qlonglong a_timestamp) { if(m_fpsDen == 0 || m_fpsNum == 0) return 0; return std::round((double)a_timestamp * m_fpsNum / m_fpsDen / 1000); } void PreviewDialog::setExpectedFrame(int a_frame) { m_frameExpected = a_frame; m_frameTimestampExpected = frameToTimestamp(m_frameExpected); } void PreviewDialog::saveTimelineBookmarks() { QString l_scriptName = scriptName(); if(l_scriptName.isEmpty()) return; QString bookmarksFilePath = l_scriptName + QString(TIMELINE_BOOKMARKS_FILE_SUFFIX); QFile bookmarksFile(bookmarksFilePath); if(!bookmarksFile.open(QIODevice::WriteOnly)) return; std::set bookmarks = m_ui.frameNumberSlider->bookmarks(); QStringList bookmarksStringList; for(int i : bookmarks) bookmarksStringList += QString::number(i); QString bookmarksString = bookmarksStringList.join(", "); bookmarksFile.write(bookmarksString.toUtf8()); bookmarksFile.close(); } // END OF void PreviewDialog::saveTimelineBookmarks() //============================================================================== void PreviewDialog::loadTimelineBookmarks() { std::set bookmarks; QString l_scriptName = scriptName(); if(l_scriptName.isEmpty()) { m_ui.frameNumberSlider->setBookmarks(bookmarks); return; } QString bookmarksFilePath = l_scriptName + QString(TIMELINE_BOOKMARKS_FILE_SUFFIX); QFile bookmarksFile(bookmarksFilePath); if(!bookmarksFile.open(QIODevice::ReadOnly)) { m_ui.frameNumberSlider->setBookmarks(bookmarks); return; } QString bookmarksString = tr(bookmarksFile.readAll().data()); bookmarksFile.close(); QStringList bookmarksStringList = bookmarksString.split(","); for(const QString & string : bookmarksStringList) { bool converted = false; int i = string.simplified().toInt(&converted); if(converted) bookmarks.insert(i); } m_ui.frameNumberSlider->setBookmarks(bookmarks); } // END OF void PreviewDialog::loadTimelineBookmarks() //============================================================================== void PreviewDialog::saveGeometryDelayed() { QApplication::processEvents(); if(!isMaximized()) { m_windowGeometry = saveGeometry(); m_pGeometrySaveTimer->start(); } } // END OF void PreviewDialog::saveGeometryDelayed() //============================================================================== FramePropsPanel::FramePropsPanel(SettingsManager * a_pSettingsManager, PreviewDialog * a_pFakeParent) { m_pFakeParent = a_pFakeParent; setWindowModality(Qt::NonModal); setWindowTitle(QString("Frame Properties")); QFont font = a_pSettingsManager-> getTextFormat(TEXT_FORMAT_ID_COMMON_SCRIPT_TEXT).font(); setFont(font); vsedit::disableFontKerning(this); setAlignment(Qt::AlignmentFlag::AlignTop); setTextInteractionFlags(Qt::TextSelectableByMouse); setCursor(QCursor(Qt::IBeamCursor)); setVisible(false); setHideAction(a_pSettingsManager); } void FramePropsPanel::keyPressEvent(QKeyEvent * a_pEvent) { if(a_pEvent->modifiers() != Qt::NoModifier) QTextEdit::keyPressEvent(a_pEvent); else if(a_pEvent->key() == Qt::Key_Escape) setVisible(false); else m_pFakeParent->keyPressEvent(a_pEvent); } void FramePropsPanel::setHideAction(SettingsManager * a_pSettingsManager) { m_pActionHide = a_pSettingsManager->createStandardAction( ACTION_ID_TOGGLE_FRAME_PROPS, this); m_pActionHide->setCheckable(false); QKeySequence hotkey = a_pSettingsManager-> getHotkey(m_pActionHide->data().toString()); m_pActionHide->setShortcut(hotkey); connect(m_pActionHide, SIGNAL(triggered()), this, SLOT(slotHide())); addAction(m_pActionHide); } void FramePropsPanel::setVisible(bool visible) { if(visible) resize(m_widgetWidth, m_widgetHeight); else { m_widgetWidth = width(); m_widgetHeight = height(); } QWidget::setVisible(visible); } ================================================ FILE: vsedit/src/preview/preview_dialog.h ================================================ #ifndef PREVIEWDIALOG_H_INCLUDED #define PREVIEWDIALOG_H_INCLUDED #include #include "../vapoursynth/vs_script_processor_dialog.h" #include "../../../common-src/settings/settings_definitions.h" #include "../../../common-src/chrono.h" #include #include #include #ifdef Q_OS_WIN // AUDIO #include #include #endif #include #include #include class QEvent; class QMoveEvent; class QResizeEvent; class QKeyEvent; class QMenu; class QActionGroup; class QAction; class QTimer; class SettingsManager; class SettingsDialog; class PreviewAdvancedSettingsDialog; class VSNodeInfo; class FramePropsPanel; extern const char TIMELINE_BOOKMARKS_FILE_SUFFIX[]; class PreviewDialog : public VSScriptProcessorDialog { Q_OBJECT #ifdef Q_OS_WIN // AUDIO struct AudioFrame { int number; int outputIndex; QByteArray data; AudioFrame(); AudioFrame(int a_number, int a_outputIndex, QByteArray a_data); bool valid() const { return !!data.size(); } bool operator==(const AudioFrame & a_other) const; }; #endif public: PreviewDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, bool a_inPreviewer = false, QWidget * a_pParent = nullptr); virtual ~PreviewDialog(); virtual void setScriptName(const QString & a_scriptName) override; void previewScript(const QString& a_script, const QString& a_scriptName); bool busy() const { return VSScriptProcessorDialog::busy(m_outputIndex); } signals: void signalPasteIntoScriptAtNewLine(const QString& a_line); void signalPasteIntoScriptAtCursor(const QString& a_line); public slots: void slotScriptTextChanged(); protected slots: virtual void slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame) override; virtual void slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason) override; void slotShowFrame(int a_frameNumber, bool a_refreshCache = true); void slotSaveSnapshot(); void slotToggleZoomPanelVisible(bool a_zoomPanelVisible); void slotZoomModeChanged(); void slotZoomRatioChanged(double a_zoomRatio); void slotScaleModeChanged(); void slotToggleCropPanelVisible(bool a_cropPanelVisible); void slotCropModeChanged(); void slotCropLeftValueChanged(int a_value); void slotCropTopValueChanged(int a_value); void slotCropWidthValueChanged(int a_value); void slotCropHeightValueChanged(int a_value); void slotCropRightValueChanged(int a_value); void slotCropBottomValueChanged(int a_value); void slotCropZoomRatioValueChanged(int a_cropZoomRatio); void slotPasteCropSnippetIntoScript(); void slotCallAdvancedSettingsDialog(); void slotToggleTimeLinePanelVisible(bool a_timeLinePanelVisible); void slotTimeLineModeChanged(); void slotTimeStepChanged(const QTime & a_time); void slotTimeStepForward(); void slotTimeStepBack(); void slotSettingsChanged(); void slotPreviewAreaSizeChanged(); void slotPreviewAreaCtrlWheel(QPoint a_angleDelta); void slotPreviewAreaMouseMiddleButtonReleased(); void slotPreviewAreaMouseRightButtonReleased(); void slotPreviewAreaMouseOverPoint(double a_pX, double a_pY); void slotFrameToClipboard(); void slotAdvancedSettingsChanged(); void slotToggleColorPicker(bool a_colorPickerVisible); void slotSetPlayFPSLimit(); void slotPlay(bool a_play); void slotProcessPlayQueue(); #ifdef Q_OS_WIN // AUDIO void slotProcessAudioPlayQueue(); #endif void slotLoadChapters(); void slotClearBookmarks(); void slotBookmarkCurrentFrame(); void slotUnbookmarkCurrentFrame(); void slotGoToPreviousBookmark(); void slotGoToNextBookmark(); void slotPasteShownFrameNumberIntoScript(); void slotSaveGeometry(); void slotJumpToFrame(); void slotToggleFrameProps(); void slotSwitchOutputIndex(int a_outputIndex); void slotEnableSwitchOutputIndex(bool a_idle); void setOutputIndex(int a_index); #define SLOT_SWITCH_OUTPUT_INDEX(a) \ void slotSwitchOutputIndex##a() { setOutputIndex(a); } SLOT_SWITCH_OUTPUT_INDEX(0) SLOT_SWITCH_OUTPUT_INDEX(1) SLOT_SWITCH_OUTPUT_INDEX(2) SLOT_SWITCH_OUTPUT_INDEX(3) SLOT_SWITCH_OUTPUT_INDEX(4) SLOT_SWITCH_OUTPUT_INDEX(5) SLOT_SWITCH_OUTPUT_INDEX(6) SLOT_SWITCH_OUTPUT_INDEX(7) SLOT_SWITCH_OUTPUT_INDEX(8) SLOT_SWITCH_OUTPUT_INDEX(9) SLOT_SWITCH_OUTPUT_INDEX(10) SLOT_SWITCH_OUTPUT_INDEX(11) SLOT_SWITCH_OUTPUT_INDEX(12) SLOT_SWITCH_OUTPUT_INDEX(13) SLOT_SWITCH_OUTPUT_INDEX(14) SLOT_SWITCH_OUTPUT_INDEX(15) SLOT_SWITCH_OUTPUT_INDEX(16) SLOT_SWITCH_OUTPUT_INDEX(17) SLOT_SWITCH_OUTPUT_INDEX(18) SLOT_SWITCH_OUTPUT_INDEX(19) #undef SLOT_SWITCH_OUTPUT_INDEX void slotSwitchToPreviousOutputIndex(); void slotSwitchToNextOutputIndex(); protected: friend class FramePropsPanel; virtual void stopAndCleanUp() override; void moveEvent(QMoveEvent * a_pEvent) override; void resizeEvent(QResizeEvent * a_pEvent) override; void changeEvent(QEvent * a_pEvent) override; void closeEvent(QCloseEvent * a_pEvent) override; void keyPressEvent(QKeyEvent * a_pEvent) override; void createActionsAndMenus(); void setUpZoomPanel(); void setUpTimeLinePanel(); void setUpCropPanel(); bool requestShowFrame(int a_frameNumber); void setPreviewPixmap(); void recalculateCropMods(); void resetCropSpinBoxes(); qlonglong frameToTimestamp(int a_frame); int timestampToFrame(qlonglong a_timestamp); void setExpectedFrame(int a_frame); void setCurrentFrame(const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame); void updateFrameProps(bool a_forced); double valueAtPoint(size_t a_x, size_t a_y, int a_plane); void previewValueAtPoint(size_t a_x, size_t a_y, int a_ret[]); QPixmap pixmapFromRGB(const VSFrame * a_cpFrame); void setTitle(); void saveTimelineBookmarks(); void loadTimelineBookmarks(); void saveGeometryDelayed(); QPoint loadLastScrollBarPositions() const; void saveLastScrollBarPositions(); #ifdef Q_OS_WIN // AUDIO void setAudioOutput(); void stopAudioOutput(); void playAudioFrame(); QByteArray readAudioFrame(const VSFrame * a_cpFrame); #endif Ui::PreviewDialog m_ui; PreviewAdvancedSettingsDialog * m_pAdvancedSettingsDialog; int64_t m_fpsNum = 0; int64_t m_fpsDen = 0; int m_frameExpected; qlonglong m_frameTimestampExpected; int m_frameShown; int m_lastFrameRequestedForPlay; int m_bigFrameStep; const VSFrame * m_cpFrame; const VSFrame * m_cpPreviewFrame; QPixmap m_framePixmap; bool m_changingCropValues; QMenu * m_pPreviewContextMenu; QAction * m_pActionFrameToClipboard; QAction * m_pActionSaveSnapshot; QAction * m_pActionToggleZoomPanel; QMenu * m_pMenuZoomModes; QActionGroup * m_pActionGroupZoomModes; QAction * m_pActionSetZoomModeNoZoom; QAction * m_pActionSetZoomModeFixedRatio; QAction * m_pActionSetZoomModeFitToFrame; QMenu * m_pMenuZoomScaleModes; QActionGroup * m_pActionGroupZoomScaleModes; QAction * m_pActionSetZoomScaleModeNearest; QAction * m_pActionSetZoomScaleModeBilinear; QAction * m_pActionToggleCropPanel; QAction * m_pActionToggleTimeLinePanel; QMenu * m_pMenuTimeLineModes; QActionGroup * m_pActionGroupTimeLineModes; QAction * m_pActionSetTimeLineModeTime; QAction * m_pActionSetTimeLineModeFrames; QAction * m_pActionTimeStepForward; QAction * m_pActionTimeStepBack; QAction * m_pActionPasteCropSnippetIntoScript; QAction * m_pActionAdvancedSettingsDialog; QAction * m_pActionToggleColorPicker; QAction * m_pActionPlay; QAction * m_pActionLoadChapters; QAction * m_pActionClearBookmarks; QAction * m_pActionBookmarkCurrentFrame; QAction * m_pActionUnbookmarkCurrentFrame; QAction * m_pActionGoToPreviousBookmark; QAction * m_pActionGoToNextBookmark; QAction * m_pActionPasteShownFrameNumberIntoScript; QAction * m_pActionJumpToFrame; QAction * m_pActionToggleFramePropsPanel; QAction * m_pActionSwitchToOutputIndex0; QAction * m_pActionSwitchToOutputIndex1; QAction * m_pActionSwitchToOutputIndex2; QAction * m_pActionSwitchToOutputIndex3; QAction * m_pActionSwitchToOutputIndex4; QAction * m_pActionSwitchToOutputIndex5; QAction * m_pActionSwitchToOutputIndex6; QAction * m_pActionSwitchToOutputIndex7; QAction * m_pActionSwitchToOutputIndex8; QAction * m_pActionSwitchToOutputIndex9; QAction * m_pActionSwitchToOutputIndex10; QAction * m_pActionSwitchToOutputIndex11; QAction * m_pActionSwitchToOutputIndex12; QAction * m_pActionSwitchToOutputIndex13; QAction * m_pActionSwitchToOutputIndex14; QAction * m_pActionSwitchToOutputIndex15; QAction * m_pActionSwitchToOutputIndex16; QAction * m_pActionSwitchToOutputIndex17; QAction * m_pActionSwitchToOutputIndex18; QAction * m_pActionSwitchToOutputIndex19; QAction * m_pActionSwitchToPreviousOutputIndex; QAction * m_pActionSwitchToNextOutputIndex; std::map m_actionIDToZoomModeMap; std::map m_actionIDToZoomScaleModeMap; std::map m_actionIDToTimeLineModeMap; std::vector m_settableActionsList; bool m_playing; bool m_processingPlayQueue; bool m_nativePlaybackRate; double m_secondsBetweenFrames; hr_time_point m_lastFrameShowTime; QTimer * m_pPlayTimer; QIcon m_iconPlay; QIcon m_iconPause; bool m_alwaysKeepCurrentFrame; QTimer * m_pGeometrySaveTimer; QByteArray m_windowGeometry; qreal m_devicePixelRatio; FramePropsPanel * m_pFramePropsPanel; bool m_toChangeTitle; bool m_scriptTextChanged = false; bool m_inPreviewer; QMetaObject::Connection m_outputIndexComboBoxConnection; #ifdef Q_OS_WIN // AUDIO bool m_currentIsAudio; QAudioSink * m_pAudioSink = nullptr; QIODevice * m_pAudioIODevice = nullptr; std::map m_audioCache; QTimer * m_pAudioPlayTimer = nullptr; double m_audioVolume = 1.0; #endif }; class FramePropsPanel: public QTextEdit { Q_OBJECT public: FramePropsPanel(SettingsManager * a_pSettingsManager, PreviewDialog * a_pFakeParent); void setVisible(bool visible) override; void keyPressEvent(QKeyEvent * a_pEvent) override; public slots: void slotHide() { setVisible(false); } private: PreviewDialog * m_pFakeParent; QAction * m_pActionHide; int m_widgetWidth = 400; int m_widgetHeight = 400; void setHideAction(SettingsManager * a_pSettingsManager); }; #endif // PREVIEWDIALOG_H_INCLUDED ================================================ FILE: vsedit/src/preview/preview_dialog.ui ================================================ PreviewDialog 0 0 933 652 Dialog 0 0 0 0 0 0 0 2 0 0 0 0 Timeline true Index: 0 0 Frame: 0 0 false 999999999 16 0 0 0 Copy frame to clipboard Save snapshot Preview Advanced Settings Crop assistant true Zoom true true 2 0 0 0 0 0 0 false x 0.010000000000000 1024.000000000000000 2.000000000000000 Color picker colorPickerButton frameNumberSlider frameToClipboardButton saveSnapshotButton advancedSettingsButton cropCheckButton zoomCheckButton zoomPanel timeLineCheckButton frameNumberSpinBox frameStatusLabel 2 0 0 0 0 FPS limit mode: 5 100000.000000000000000 23.999990000000000 Qt::Horizontal 571 20 Qt::Horizontal 40 20 Timeline display mode: 2 0 0 Step: 2 H:mm:ss.zzz Step back Step forward 2 0 0 0 0 Qt::Horizontal 40 20 0 0 Crop mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 0 0 Left: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false 0 0 Top: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false 0 0 Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false 1 0 0 Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false 1 0 0 Right: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false 0 0 Bottom: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false 0 0 Zoom: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 false x 1 Paste into script toolBar previewArea cropPanel timeLinePanel PreviewArea QWidget
../../src/preview/preview_area.h
1
TimeLineSlider QWidget
../../../common-src/timeline_slider/timeline_slider.h
1
ZoomRatioSpinBox QDoubleSpinBox
../../src/preview/zoom_ratio_spinbox.h
1
================================================ FILE: vsedit/src/preview/scroll_navigator.cpp ================================================ #include "scroll_navigator.h" #include #include #include //============================================================================== ScrollNavigator::ScrollNavigator(QWidget * a_pParent) : QWidget(a_pParent) , m_contentsHeight(0) , m_contentsWidth(0) , m_viewportX(0) , m_viewportY(0) , m_viewportHeight(0) , m_viewportWidth(0) { setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_TransparentForMouseEvents); setGeometry(0, 0, 100, 100); } // END OF ScrollNavigator::ScrollNavigator(QWidget * a_pParent) //============================================================================== ScrollNavigator::~ScrollNavigator() { } // END OF ScrollNavigator::~ScrollNavigator() //============================================================================== void ScrollNavigator::draw(int a_contentsWidth, int a_contentsHeight, int a_viewportX, int a_viewportY, int a_viewportWidth, int a_viewportHeight) { m_contentsHeight = a_contentsHeight; m_contentsWidth = a_contentsWidth; m_viewportX = a_viewportX; m_viewportY = a_viewportY; m_viewportHeight = a_viewportHeight; m_viewportWidth = a_viewportWidth; repaint(); } // END OF void ScrollNavigator::draw(int a_contentsWidth, int a_contentsHeight, // int a_viewportX, int a_viewportY, int a_viewportWidth, // int a_viewportHeight) //============================================================================== void ScrollNavigator::paintEvent(QPaintEvent * a_pPaintEvent) { if((m_contentsWidth == 0) || (m_contentsHeight == 0) || (m_viewportWidth == 0) || (m_viewportHeight == 0) || (m_viewportX >= m_contentsWidth) || (m_viewportY >= m_contentsHeight)) { a_pPaintEvent->ignore(); return; } int measures[] = {m_contentsWidth, m_contentsHeight, m_viewportWidth, m_viewportHeight}; int maxMeasure = 0; for(int m : measures) if(m > maxMeasure) maxMeasure = m; double dpr = window()->devicePixelRatioF(); int normalizedContentsWidth = int((double)m_contentsWidth * 100.0 / (double)maxMeasure); int normalizedContentsHeight = int((double)m_contentsHeight * 100.0 / (double)maxMeasure); int normalizedVieportX = int(m_viewportX * dpr * 100.0 / (double)maxMeasure); int normalizedViwportY = int(m_viewportY * dpr * 100.0 / (double)maxMeasure); int normalizedViewportWidth = int(m_viewportWidth * dpr * 100.0 / (double)maxMeasure); int normalizedViewportHeight = int(m_viewportHeight * dpr * 100.0 / (double)maxMeasure); int cX1 = 0; int cY1 = 0; int cX2 = normalizedContentsWidth - 1; int cY2 = normalizedContentsHeight - 1; int vX1 = normalizedVieportX; int vY1 = normalizedViwportY; int vX2 = normalizedVieportX + normalizedViewportWidth - 1; int vY2 = normalizedViwportY + normalizedViewportHeight - 1; QPainter painter(this); // Draw contents rectangle. painter.setPen(QColor::fromRgb(255, 0, 255)); painter.drawLine(cX1, cY1, cX2, cY1); painter.drawLine(cX2, cY1, cX2, cY2); painter.drawLine(cX2, cY2, cX1, cY2); painter.drawLine(cX1, cY2, cX1, cY1); // Draw viewport rectangle. painter.setPen(QColor::fromRgb(0, 255, 0)); painter.drawLine(vX1, vY1, vX2, vY1); painter.drawLine(vX2, vY1, vX2, vY2); painter.drawLine(vX2, vY2, vX1, vY2); painter.drawLine(vX1, vY2, vX1, vY1); a_pPaintEvent->accept(); } // END OF void ScrollNavigator::paintEvent(QPaintEvent * a_pPaintEvent) //============================================================================== ================================================ FILE: vsedit/src/preview/scroll_navigator.h ================================================ #ifndef SCROLLNAVIGATOR_H #define SCROLLNAVIGATOR_H #include class QPaintEvent; class ScrollNavigator : public QWidget { public: ScrollNavigator(QWidget * a_pParent = 0); virtual ~ScrollNavigator(); void draw(int a_contentsWidth, int a_contentsHeight, int a_viewportX, int a_viewportY, int a_viewportWidth, int a_viewportHeight); protected: void paintEvent(QPaintEvent * a_pPaintEvent) override; private: int m_contentsHeight; int m_contentsWidth; int m_viewportX; int m_viewportY; int m_viewportHeight; int m_viewportWidth; }; #endif // SCROLLNAVIGATOR_H ================================================ FILE: vsedit/src/preview/zoom_ratio_spinbox.cpp ================================================ #include "zoom_ratio_spinbox.h" #include ZoomRatioSpinBox::ZoomRatioSpinBox(QWidget * a_pWidget): QDoubleSpinBox(a_pWidget) , m_scaleMode{Qt::SmoothTransformation} {} void ZoomRatioSpinBox::setScaleMode(Qt::TransformationMode a_mode) { m_scaleMode = a_mode; if(m_scaleMode == Qt::FastTransformation) { int ival = std::round(value()); if(ival == 0) ival = 1; if(ival > maximum()) ival = std::floor(maximum()); QDoubleSpinBox::setValue(ival); } } void ZoomRatioSpinBox::stepBy(int steps) { if(steps == -1) { if(m_scaleMode == Qt::FastTransformation) { int ival = (int)value() - 1; if(ival <= 0) ival = 1; QDoubleSpinBox::setValue(ival); } else { double val = value(); if(val > 2.0) QDoubleSpinBox::setValue(val - 1.0); else if(val > 1.0) QDoubleSpinBox::setValue(1.0); else if(val > 0.01 + minimum()) QDoubleSpinBox::setValue(val - 0.01); else QDoubleSpinBox::setValue(minimum()); } } else if(steps == 1) { if(m_scaleMode == Qt::FastTransformation) { int ival = (int)value() + 1; if(ival > maximum()) ival = std::floor(maximum()); QDoubleSpinBox::setValue(ival); } else { double val = value(); if(val < 0.99) QDoubleSpinBox::setValue(val + 0.01); else if(val < 1.0) QDoubleSpinBox::setValue(1.0); else if(val + 0.1 < 4.1) QDoubleSpinBox::setValue(val + 0.1); else if(val + 1.0 < maximum()) QDoubleSpinBox::setValue(val + 1.0); else QDoubleSpinBox::setValue(maximum()); } } else if(steps > 0) { stepBy(1); stepBy(steps - 1); } else if(steps < 0) { stepBy(-1); stepBy(steps + 1); } } void ZoomRatioSpinBox::setValue(double val) { if(m_scaleMode == Qt::FastTransformation) { int ival = std::round(val); if(ival == 0) ival = 1; if(ival > maximum()) ival = std::floor(maximum()); QDoubleSpinBox::setValue(ival); } else QDoubleSpinBox::setValue(val); } ================================================ FILE: vsedit/src/preview/zoom_ratio_spinbox.h ================================================ #ifndef ZOOM_RATIO_SPINBOX_H_INCLUDED #define ZOOM_RATIO_SPINBOX_H_INCLUDED #include "../../../common-src/settings/settings_definitions.h" #include class ZoomRatioSpinBox : public QDoubleSpinBox { public: ZoomRatioSpinBox(QWidget * a_pParent = nullptr); void setScaleMode(Qt::TransformationMode a_mode); void stepBy(int steps); public slots: void setValue(double val); private: Qt::TransformationMode m_scaleMode; }; #endif ================================================ FILE: vsedit/src/script_editor/number_matcher.cpp ================================================ #include "number_matcher.h" //============================================================================== NumberMatcher::NumberMatcher() : m_state(Initial), m_lastValidLength(0) { } // END OF NumberMatcher::NumberMatcher() //============================================================================== bool NumberMatcher::beginsWithNumber(const QString & a_string, int a_matchFrom) { m_state = Initial; m_lastValidLength = 0; int stringLength = a_string.length(); for(int i = a_matchFrom; i < stringLength; ++i) { if(m_state == Initial) { if(a_string[i] == '0') { m_state = FirstZero; m_lastValidLength = i - a_matchFrom + 1; } else if(QString("123456789").contains(a_string[i])) { m_state = Integer; m_lastValidLength = i - a_matchFrom + 1; } else if(a_string[i] == '.') m_state = FirstDot; else break; } else if(m_state == FirstZero) { if(a_string[i].toLower() == 'b') m_state = BinLiteral; else if(a_string[i].toLower() == 'x') m_state = HexLiteral; else if(a_string[i].toLower() == 'o') m_state = OctLiteral; else if(QString("0123456789").contains(a_string[i])) { m_state = Integer; m_lastValidLength = i - a_matchFrom + 1; } else if(a_string[i] == '.') { m_state = DotAfterInteger; m_lastValidLength = i - a_matchFrom + 1; } else if(a_string[i].toLower() == 'j') { m_lastValidLength = i - a_matchFrom + 1; return true; } else break; } else if(m_state == BinLiteral) { if(QString("01").contains(a_string[i])) { m_state = BinNumber; m_lastValidLength = i - a_matchFrom + 1; } else break; } else if(m_state == BinNumber) { if(QString("01").contains(a_string[i])) m_lastValidLength = i - a_matchFrom + 1; else break; } else if(m_state == OctLiteral) { if(QString("01234567").contains(a_string[i])) { m_state = OctNumber; m_lastValidLength = i - a_matchFrom + 1; } else break; } else if(m_state == OctNumber) { if(QString("01234567").contains(a_string[i])) m_lastValidLength = i - a_matchFrom + 1; else break; } else if(m_state == HexLiteral) { if(QString("0123456789abcdef").contains(a_string[i].toLower())) { m_state = HexNumber; m_lastValidLength = i - a_matchFrom + 1; } else break; } else if(m_state == HexNumber) { if(QString("0123456789abcdef").contains(a_string[i].toLower())) m_lastValidLength = i - a_matchFrom + 1; else break; } else if(m_state == Integer) { if(QString("0123456789").contains(a_string[i])) m_lastValidLength = i - a_matchFrom + 1; else if(a_string[i] == '.') { m_state = DotAfterInteger; m_lastValidLength = i - a_matchFrom + 1; } else if(a_string[i].toLower() == 'e') m_state = ExpLiteral; else if(a_string[i].toLower() == 'j') { m_lastValidLength = i - a_matchFrom + 1; return true; } else break; } else if(m_state == DotAfterInteger) { if(QString("0123456789").contains(a_string[i])) { m_state = Fraction; m_lastValidLength = i - a_matchFrom + 1; } else if(a_string[i].toLower() == 'e') m_state = ExpLiteral; else if(a_string[i].toLower() == 'j') { m_lastValidLength = i - a_matchFrom + 1; return true; } else break; } else if(m_state == FirstDot) { if(QString("0123456789").contains(a_string[i])) { m_state = Fraction; m_lastValidLength = i - a_matchFrom + 1; } else break; } else if(m_state == Fraction) { if(QString("0123456789").contains(a_string[i])) m_lastValidLength = i - a_matchFrom + 1; else if(a_string[i].toLower() == 'e') m_state = ExpLiteral; else if(a_string[i].toLower() == 'j') { m_lastValidLength = i - a_matchFrom + 1; return true; } else break; } else if(m_state == ExpLiteral) { if(QString("+-").contains(a_string[i])) m_state = ExpSign; else if(QString("0123456789").contains(a_string[i])) { m_state = ExpComplete; m_lastValidLength = i - a_matchFrom + 1; } else break; } else if(m_state == ExpSign) { if(QString("0123456789").contains(a_string[i])) { m_state = ExpComplete; m_lastValidLength = i - a_matchFrom + 1; } else break; } else if(m_state == ExpComplete) { if(QString("0123456789").contains(a_string[i])) m_lastValidLength = i - a_matchFrom + 1; else if(a_string[i].toLower() == 'j') { m_lastValidLength = i - a_matchFrom + 1; return true; } } else break; } return (m_lastValidLength != 0); } // END OF bool NumberMatcher::beginsWithNumber(const QString & a_string, // int a_matchFrom) //============================================================================== int NumberMatcher::matchedLength() const { return m_lastValidLength; } // END OF int NumberMatcher::matchedLength() const //============================================================================== ================================================ FILE: vsedit/src/script_editor/number_matcher.h ================================================ #ifndef NUMBERMATCHER_H_INCLUDED #define NUMBERMATCHER_H_INCLUDED #include /// Finite state machine Python number matcher for QString. class NumberMatcher { public: NumberMatcher(); bool beginsWithNumber(const QString & a_string, int a_matchFrom); int matchedLength() const; private: enum State { Initial, FirstZero, // Valid final state BinLiteral, BinNumber, // Valid final state OctLiteral, OctNumber, // Valid final state HexLiteral, HexNumber, // Valid final state Integer, // Valid final state DotAfterInteger, // Valid final state FirstDot, Fraction, // Valid final state ExpLiteral, ExpSign, ExpComplete, // Valid final state }; State m_state; int m_lastValidLength; }; #endif // NUMBERMATCHER_H_INCLUDED ================================================ FILE: vsedit/src/script_editor/script_completer.cpp ================================================ #include "script_completer.h" //============================================================================== ScriptCompleter::ScriptCompleter(QAbstractItemModel * a_pModel, QObject * a_pParent) : QCompleter(a_pModel, a_pParent) { } //============================================================================== ScriptCompleter::~ScriptCompleter() { } //============================================================================== QString ScriptCompleter::pathFromIndex(const QModelIndex & a_index) const { if(!a_index.isValid()) return QString(); QString path = model()->data(a_index, Qt::EditRole).toString(); QModelIndex index = a_index; while(index.parent().isValid()) { index = index.parent(); path.prepend('.'); path.prepend(model()->data(index, Qt::EditRole).toString()); } return path; } //============================================================================== QStringList ScriptCompleter::splitPath(const QString & a_path) const { return a_path.split('.'); } //============================================================================== ================================================ FILE: vsedit/src/script_editor/script_completer.h ================================================ #ifndef SCRIPTCOMPLETER_H #define SCRIPTCOMPLETER_H #include class ScriptCompleter : public QCompleter { Q_OBJECT public: ScriptCompleter(QAbstractItemModel * a_pModel, QObject * a_pParent = nullptr); virtual ~ScriptCompleter(); QString pathFromIndex(const QModelIndex & a_index) const; QStringList splitPath(const QString & a_path) const; }; #endif // SCRIPTCOMPLETER_H ================================================ FILE: vsedit/src/script_editor/script_completer_model.cpp ================================================ #include "script_completer_model.h" #include //============================================================================== const char DEFAULT_CORE_NAME[] = "core"; //============================================================================== ScriptCompleterModel::ScriptCompleterModel(QObject * a_pParent): QStandardItemModel(a_pParent) { setCoreName(DEFAULT_CORE_NAME); } // END OF ScriptCompleterModel::ScriptCompleterModel(QObject * a_pParent) //============================================================================== ScriptCompleterModel::~ScriptCompleterModel() { } // END OF ScriptCompleterModel::~ScriptCompleterModel() //============================================================================== void ScriptCompleterModel::setPluginsList(const VSPluginsList & a_pluginsList) { if(invisibleRootItem()->rowCount() < 1) setCoreName(DEFAULT_CORE_NAME); QStandardItem * pCoreItem = invisibleRootItem()->child(0, 0); pCoreItem->removeRows(0, pCoreItem->rowCount()); for(const VSData::Plugin & plugin : a_pluginsList) { QStandardItem * pPluginItem = new QStandardItem(plugin.pluginNamespace); pCoreItem->appendRow(pPluginItem); for(const VSData::Function & function : plugin.functions) { QStringList argumentsList; for(const VSData::FunctionArgument & argument : function.arguments) argumentsList << argument.name; QString signature = QString("%1(%2)").arg(function.name) .arg(argumentsList.join(", ")); QStandardItem * pFunctionItem = new QStandardItem(signature); pPluginItem->appendRow(pFunctionItem); } } } // END OF void ScriptCompleterModel::setPluginsList( // const VSPluginsList & a_pluginsList) //============================================================================== void ScriptCompleterModel::setCoreName(const QString & a_coreName) { QStandardItem * pRootItem = invisibleRootItem(); if(pRootItem->rowCount() == 0) { QStandardItem * pCoreItem = new QStandardItem(a_coreName); pRootItem->appendRow(pCoreItem); } else { QStandardItem * pCoreItem = pRootItem->child(0, 0); pCoreItem->setText(a_coreName); } } // END OF void ScriptCompleterModel::setCoreName(const QString & a_coreName) //============================================================================== ================================================ FILE: vsedit/src/script_editor/script_completer_model.h ================================================ #ifndef SCRIPTCOMPLETERMODEL_H #define SCRIPTCOMPLETERMODEL_H #include "../vapoursynth/vs_plugin_data.h" #include class ScriptCompleterModel : public QStandardItemModel { Q_OBJECT public: ScriptCompleterModel(QObject * a_pParent = nullptr); virtual ~ScriptCompleterModel(); void setPluginsList(const VSPluginsList & a_pluginsList); void setCoreName(const QString & a_coreName); }; #endif // SCRIPTCOMPLETERMODEL_H ================================================ FILE: vsedit/src/script_editor/script_editor.cpp ================================================ #include "script_editor.h" #include "script_completer_model.h" #include "script_completer.h" #include "syntax_highlighter.h" #include "../../../common-src/settings/settings_manager.h" #include "../settings/settings_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== const char COMMENT_TOKEN[] = "#"; //============================================================================== ScriptEditor::ScriptEditor(QWidget * a_pParent) : QPlainTextEdit(a_pParent) , m_pSettingsManager(nullptr) , m_pSideBox(nullptr) , m_sideBoxLineWidth(1) , m_sideBoxTextMargin(3) , m_pCompleterModel(nullptr) , m_pCompleter(nullptr) , m_pSyntaxHighlighter(nullptr) , m_typedCharacters(0) , m_charactersTypedToStartCompletion( DEFAULT_CHARACTERS_TYPED_TO_START_COMPLETION) , m_plainText() , m_backgroundColor(Qt::white) , m_activeLineColor(Qt::lightGray) , m_highlightSelectionMatches(DEFAULT_HIGHLIGHT_SELECTION_MATCHES) , m_highlightSelectionMatchesMinLength( DEFAULT_HIGHLIGHT_SELECTION_MATCHES_MIN_LENGTH) , m_commonScriptTextFormat() , m_tabText("\t") , m_spacesInTab(DEFAULT_SPACES_IN_TAB) , m_pContextMenu(nullptr) , m_pActionDuplicateSelection(nullptr) , m_pActionCommentSelection(nullptr) , m_pActionUncommentSelection(nullptr) , m_pActionReplaceTabWithSpaces(nullptr) , m_pActionAutocomplete(nullptr) , m_pActionMoveTextBlockUp(nullptr) , m_pActionMoveTextBlockDown(nullptr) , m_pActionToggleComment(nullptr) { m_pSideBox = new QWidget(this); m_pSideBox->installEventFilter(this); m_pCompleterModel = new ScriptCompleterModel(this); m_pCompleter = new ScriptCompleter(m_pCompleterModel, this); m_pCompleter->setWidget(this); m_pCompleter->setCompletionMode(QCompleter::PopupCompletion); m_pCompleter->setCaseSensitivity(Qt::CaseInsensitive); m_pCompleter->setWrapAround(false); m_pSyntaxHighlighter = new SyntaxHighlighter(document()); fillVariables(); connect(m_pCompleter, SIGNAL(activated(const QString &)), this, SLOT(slotInsertCompletion(const QString &))); connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(slotUpdateSideBoxWidth())); connect(this, SIGNAL(updateRequest(const QRect &, int)), this, SLOT(slotUpdateSideBox(const QRect &, int))); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotHighlightCurrentBlockAndMatches())); connect(this, SIGNAL(selectionChanged()), this, SLOT(slotHighlightCurrentBlockAndMatches())); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotShowCustomMenu(const QPoint &))); setContextMenuPolicy(Qt::CustomContextMenu); } // END OF ScriptEditor::ScriptEditor(QWidget * a_pParent) //============================================================================== ScriptEditor::~ScriptEditor() { } // END OF ScriptEditor::~ScriptEditor() //============================================================================== QString ScriptEditor::text() const { return document()->toPlainText(); } // END OF QString ScriptEditor::text() const //============================================================================== QPoint ScriptEditor::cursorPosition() const { QTextCursor currentCursor = textCursor(); int line = currentCursor.blockNumber(); int index = currentCursor.positionInBlock(); return QPoint(line, index); } // END OF QPoint ScriptEditor::cursorPosition() const //============================================================================== void ScriptEditor::setCursorPosition(const QPoint & a_point) { setCursorPosition(a_point.x(), a_point.y()); } // END OF void ScriptEditor::setCursorPosition(const QPoint & a_point) //============================================================================== void ScriptEditor::setCursorPosition(int a_line, int a_index) { int line = std::max(0, std::min(a_line, blockCount() - 1)); QTextBlock block = document()->findBlockByNumber(line); int index = std::max(0, std::min(a_index, block.length() - 1)); int newCursorPosition = block.position() + index; QTextCursor newCursor = textCursor(); newCursor.setPosition(newCursorPosition); setTextCursor(newCursor); } // END OF void ScriptEditor::setCursorPosition(int a_line, int a_index) //============================================================================== bool ScriptEditor::isModified() const { return document()->isModified(); } // END OF bool ScriptEditor::isModified() const //============================================================================== void ScriptEditor::setModified(bool a_modified) { document()->setModified(a_modified); } // END OF void ScriptEditor::setModified(bool a_modified) //============================================================================== void ScriptEditor::setPluginsList(const VSPluginsList & a_pluginsList) { m_pCompleterModel->setPluginsList(a_pluginsList); m_pSyntaxHighlighter->setPluginsList(a_pluginsList); update(); } // END OF void ScriptEditor::setPluginsList(const VSPluginsList & a_pluginsList) //============================================================================== void ScriptEditor::setSettingsManager(SettingsManager * a_pSettingsManager) { m_pSettingsManager = a_pSettingsManager; m_pSyntaxHighlighter->setSettingsManager(a_pSettingsManager); createActionsAndMenus(); slotLoadSettings(); } // END OF void ScriptEditor::setSettingsManager( // SettingsManager * a_pSettingsManager) //============================================================================== std::vector ScriptEditor::actionsForMenu() const { return {m_pActionDuplicateSelection, m_pActionCommentSelection, m_pActionUncommentSelection, m_pActionReplaceTabWithSpaces, m_pActionMoveTextBlockUp, m_pActionMoveTextBlockDown, m_pActionToggleComment}; } // END OF std::vector ScriptEditor::actionsForMenu() const //============================================================================== std::vector ScriptEditor::variables() const { std::vector cleanVariables = m_variables; for(vsedit::VariableToken & variable : cleanVariables) variable.evaluate = nullptr; return cleanVariables; } // END OF std::vector ScriptEditor::actionsForMenu() const //============================================================================== void ScriptEditor::slotLoadSettings() { if(!m_pSettingsManager) return; setUpdatesEnabled(false); m_pSyntaxHighlighter->slotLoadSettings(); m_charactersTypedToStartCompletion = m_pSettingsManager->getCharactersTypedToStartCompletion(); m_commonScriptTextFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_COMMON_SCRIPT_TEXT); QFont commonScriptTextFont = m_commonScriptTextFormat.font(); document()->setDefaultFont(commonScriptTextFont); m_tabText = m_pSettingsManager->getTabText(); m_spacesInTab = m_pSettingsManager->getSpacesInTab(); QFontMetrics metrics(commonScriptTextFont); setTabStopDistance(metrics.horizontalAdvance(' ') * m_spacesInTab); m_backgroundColor = m_pSettingsManager->getColor(COLOR_ID_TEXT_BACKGROUND); QColor textColor = m_commonScriptTextFormat.foreground().color(); QString sheet = QString("QFrame {color: %1; background-color: %2;}") .arg(textColor.name()).arg(m_backgroundColor.name()); setStyleSheet(sheet); m_activeLineColor = m_pSettingsManager->getColor(COLOR_ID_ACTIVE_LINE); m_selectionMatchesColor = m_pSettingsManager->getColor(COLOR_ID_SELECTION_MATCHES); m_highlightSelectionMatches = m_pSettingsManager->getHighlightSelectionMatches(); m_highlightSelectionMatchesMinLength = m_pSettingsManager->getHighlightSelectionMatchesMinLength(); QKeySequence hotkey; for(QAction * pAction : m_settableActionsList) { hotkey = m_pSettingsManager->getHotkey(pAction->data().toString()); pAction->setShortcut(hotkey); } slotUpdateSideBoxWidth(); slotHighlightCurrentBlockAndMatches(); setUpdatesEnabled(true); } // END OF void ScriptEditor::slotLoadSettings() //============================================================================== void ScriptEditor::slotComplete() { QTextCursor currentCursor = textCursor(); QString lineString = currentCursor.block().text(); int cursorIndex = currentCursor.positionInBlock(); int wordStart = cursorIndex; while((wordStart > 0) && (lineString[wordStart - 1].isLetterOrNumber() || (QString("._").contains(lineString[wordStart - 1])))) wordStart--; m_typedCharacters = cursorIndex - wordStart; QString typedWord = lineString.mid(wordStart, m_typedCharacters); int charactersAfterDot = m_typedCharacters - typedWord.lastIndexOf('.') - 1; QAction * pAction = qobject_cast(sender()); if((charactersAfterDot < m_charactersTypedToStartCompletion) && (pAction == nullptr)) { m_pCompleter->popup()->hide(); return; } m_pCompleter->setCompletionPrefix(typedWord); m_pCompleter->popup()->setCurrentIndex( m_pCompleter->completionModel()->index(0, 0)); QRect cursorRectangle = cursorRect(); cursorRectangle.setWidth(m_pCompleter->popup()->sizeHintForColumn(0) + m_pCompleter->popup()->verticalScrollBar()->sizeHint().width()); m_pCompleter->complete(cursorRectangle); } // END OF void ScriptEditor::slotComplete() //============================================================================== void ScriptEditor::slotInsertCompletion(const QString & a_completionString) { QTextCursor currentCursor = textCursor(); currentCursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, m_typedCharacters); currentCursor.deleteChar(); setTextCursor(currentCursor); insertPlainText(a_completionString); } // END OF void ScriptEditor::slotInsertCompletion( // const QString & a_completionString) //============================================================================== void ScriptEditor::slotDuplicateSelection() { QTextCursor cursor = textCursor(); int l_selectionStart = cursor.selectionStart(); int l_selectionEnd = cursor.selectionEnd(); QString newText = cursor.selectedText(); if(cursor.hasSelection()) { cursor.clearSelection(); } else { newText = cursor.block().text() + QString("\n"); cursor.movePosition(QTextCursor::StartOfBlock); } cursor.insertText(newText); cursor.setPosition(l_selectionStart, QTextCursor::MoveAnchor); cursor.setPosition(l_selectionEnd, QTextCursor::KeepAnchor); setTextCursor(cursor); } // END OF void ScriptEditor::slotDuplicateSelection() //============================================================================== void ScriptEditor::slotCommentSelection() { insertSelectedLinesBegin(COMMENT_TOKEN); } // END OF void ScriptEditor::slotCommentSelection() //============================================================================== void ScriptEditor::slotUncommentSelection() { removeSelectedLinesBegin(COMMENT_TOKEN); } // END OF void ScriptEditor::slotUncommentSelection() //============================================================================== void ScriptEditor::slotReplaceTabWithSpaces() { const QString spaces(m_spacesInTab, ' '); QTextDocument * pDocument = document(); QTextCursor cursor(pDocument); cursor.beginEditBlock(); QTextCursor newCursor = cursor; while(!newCursor.isNull()) { newCursor = pDocument->find("\t", newCursor); if(newCursor.hasSelection()) newCursor.insertText(spaces); } cursor.endEditBlock(); } // END OF void ScriptEditor::slotReplaceTabWithSpaces() //============================================================================== void ScriptEditor::slotTab() { QTextCursor cursor = textCursor(); if(cursor.hasSelection()) insertSelectedLinesBegin(m_tabText); else cursor.insertText(m_tabText); } // END OF void ScriptEditor::slotTab() //============================================================================== void ScriptEditor::slotBackTab() { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); int firstBlockNumber = firstBlock.blockNumber(); int lastBlockNumber = lastBlock.blockNumber(); cursor.beginEditBlock(); for(int i = firstBlockNumber; i <= lastBlockNumber; ++i) { QTextBlock block = pDocument->findBlockByNumber(i); int position = block.position(); cursor.setPosition(position); // If line begins with set tabulation text - remove it. cursor.setPosition(std::min(position + m_tabText.length(), pDocument->characterCount() - 1), QTextCursor::KeepAnchor); if(cursor.selectedText() == m_tabText) { cursor.removeSelectedText(); continue; } // Else remove standard tabulation character. cursor.setPosition(std::min(position + 1, pDocument->characterCount() - 1), QTextCursor::KeepAnchor); if(cursor.selectedText() == "\t") { cursor.removeSelectedText(); continue; } // Else remove set number of space characters used for tabulation. int tokenLength = 0; int spacesInTab = std::max(1, m_spacesInTab); while((tokenLength < spacesInTab) && (pDocument->characterAt(position + tokenLength) == ' ')) tokenLength++; cursor.setPosition(position + tokenLength, QTextCursor::KeepAnchor); cursor.removeSelectedText(); } cursor.endEditBlock(); } // END OF void ScriptEditor::slotBackTab() //============================================================================== void ScriptEditor::slotHome(bool a_select) { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); int blockLength = firstBlock.text().length(); if(blockLength == 0) return; int blockStart = firstBlock.position(); int position = blockStart; int endPosition = cursor.selectionEnd(); for(int i = 0; i < blockLength; ++i) { QChar character = pDocument->characterAt(position); if(!character.isSpace()) break; position++; } if(position == cursor.position()) cursor.setPosition(blockStart); else cursor.setPosition(position); if(a_select) cursor.setPosition(endPosition, QTextCursor::KeepAnchor); setTextCursor(cursor); } // END OF void ScriptEditor::slotHome(bool a_select) //============================================================================== void ScriptEditor::slotInsertTextAtNewLine(const QString & a_text) { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); cursor.setPosition(lastBlock.position() + lastBlock.text().length()); setTextCursor(cursor); insertPlainText(QString("\n") + a_text); } // END OF void ScriptEditor::slotInsertTextAtNewLine(const QString & a_text) //============================================================================== void ScriptEditor::slotMoveTextBlockUp() { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); int firstBlockNumber = firstBlock.blockNumber(); int lastBlockNumber = lastBlock.blockNumber(); if(firstBlockNumber == 0) return; cursor.beginEditBlock(); QTextBlock block = pDocument->findBlockByNumber(firstBlockNumber - 1); QString blockText = block.text(); cursor.setPosition(block.position()); cursor.setPosition(firstBlock.position(), QTextCursor::KeepAnchor); cursor.removeSelectedText(); block = pDocument->findBlockByNumber(lastBlockNumber); if(block.blockNumber() == lastBlockNumber) { cursor.setPosition(block.position()); cursor.insertText(blockText + QString("\n")); } else { cursor.movePosition(QTextCursor::End); cursor.insertText(QString("\n") + blockText); } cursor.endEditBlock(); // Workaround for weird behavior when selection is snapped to the end // of the document cursor = textCursor(); if(cursor.selectionEnd() < pDocument->lastBlock().position()) return; lastBlockNumber--; lastBlock = pDocument->findBlockByNumber(lastBlockNumber); cursor.setPosition(cursor.selectionStart()); cursor.setPosition(lastBlock.position() + lastBlock.length() - 1, QTextCursor::KeepAnchor); setTextCursor(cursor); } // END OF void ScriptEditor::slotMoveTextBlockUp() //============================================================================== void ScriptEditor::slotMoveTextBlockDown() { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); int lastBlockNumber = lastBlock.blockNumber(); if((lastBlockNumber + 1) == pDocument->blockCount()) return; cursor.beginEditBlock(); QTextBlock block = lastBlock.next(); QString blockText = block.text(); if((block.blockNumber() + 1) == pDocument->blockCount()) { cursor.setPosition(lastBlock.position() + lastBlock.length() - 1); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); } else { cursor.setPosition(block.position()); cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); } cursor.removeSelectedText(); cursor.setPosition(firstBlock.position()); cursor.insertText(blockText + QString("\n")); cursor.endEditBlock(); } // END OF void ScriptEditor::slotMoveTextBlockDown() //============================================================================== void ScriptEditor::slotToggleComment() { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); int firstBlockNumber = firstBlock.blockNumber(); int lastBlockNumber = lastBlock.blockNumber(); QString token(COMMENT_TOKEN); int tokenLength = token.length(); bool allCommented = true; cursor.beginEditBlock(); for(int i = firstBlockNumber; i <= lastBlockNumber; ++i) { QTextBlock block = pDocument->findBlockByNumber(i); int position = block.position(); cursor.setPosition(position); cursor.setPosition(std::min(position + tokenLength, pDocument->characterCount() - 1), QTextCursor::KeepAnchor); if(cursor.selectedText() != token) { allCommented = false; break; } } for(int i = firstBlockNumber; i <= lastBlockNumber; ++i) { QTextBlock block = pDocument->findBlockByNumber(i); int position = block.position(); cursor.setPosition(position); cursor.setPosition(std::min(position + tokenLength, pDocument->characterCount() - 1), QTextCursor::KeepAnchor); if(allCommented) cursor.removeSelectedText(); else if(cursor.selectedText() != token) { cursor.setPosition(position); cursor.insertText(token); } } cursor.endEditBlock(); } // END OF void ScriptEditor::slotToggleComment() //============================================================================== bool ScriptEditor::eventFilter(QObject * a_pObject, QEvent * a_pEvent) { if((a_pObject == m_pSideBox) && (a_pEvent->type() == QEvent::Paint)) { QPaintEvent * pPaintEvent = static_cast(a_pEvent); paintSideBox(pPaintEvent); return true; } return QPlainTextEdit::eventFilter(a_pObject, a_pEvent); } // END OF bool ScriptEditor::eventFilter(QObject * a_pObject, QEvent * a_pEvent) //============================================================================== void ScriptEditor::resizeEvent(QResizeEvent * a_pEvent) { QPlainTextEdit::resizeEvent(a_pEvent); QRect cr = contentsRect(); m_pSideBox->setGeometry(QRect(cr.left(), cr.top(), sideBoxWidth(), cr.height())); slotHighlightCurrentBlockAndMatches(); } // END OF void ScriptEditor::resizeEvent(QResizeEvent * a_pEvent) //============================================================================== void ScriptEditor::keyPressEvent(QKeyEvent * a_pEvent) { int key = a_pEvent->key(); Qt::KeyboardModifiers modifiers = a_pEvent->modifiers(); if(m_pCompleter->popup()->isVisible()) { // The following keys are forwarded by the completer to the widget switch(key) { case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Escape: case Qt::Key_Tab: case Qt::Key_Backtab: a_pEvent->ignore(); return; // let the completer do default behavior case Qt::Key_Left: case Qt::Key_Right: m_pCompleter->popup()->hide(); break; default: break; } } if((key == Qt::Key_Tab) && (modifiers == Qt::NoModifier)) { slotTab(); return; } if((key == Qt::Key_Backtab) || ((key == Qt::Key_Tab) && (modifiers == Qt::ShiftModifier))) { slotBackTab(); return; } if((key == Qt::Key_Home) && ((modifiers == Qt::NoModifier) || (modifiers == Qt::ShiftModifier))) { bool select = (modifiers == Qt::ShiftModifier); slotHome(select); return; } QString textBefore = toPlainText(); QPlainTextEdit::keyPressEvent(a_pEvent); if(((key == Qt::Key_Return) && (modifiers == Qt::NoModifier)) || ((key == Qt::Key_Enter) && (modifiers == Qt::KeypadModifier))) indentNewLine(); if(textBefore != toPlainText()) slotComplete(); } // END OF void ScriptEditor::keyPressEvent(QKeyEvent * a_pEvent) //============================================================================== void ScriptEditor::dragEnterEvent(QDragEnterEvent * a_pEvent) { if(!a_pEvent->mimeData()->hasUrls()) { QPlainTextEdit::dragEnterEvent(a_pEvent); return; } QList urls = a_pEvent->mimeData()->urls(); for(const QUrl & url : urls) { if(!url.isLocalFile()) { a_pEvent->ignore(); return; } } a_pEvent->acceptProposedAction(); } // END OF void ScriptEditor::dragEnterEvent(QDragEnterEvent * a_pEvent) //============================================================================== void ScriptEditor::dragMoveEvent(QDragMoveEvent * a_pEvent) { if(!a_pEvent->mimeData()->hasUrls()) { QPlainTextEdit::dragMoveEvent(a_pEvent); return; } QTextCursor cursor = cursorForPosition(a_pEvent->position().toPoint()); setTextCursor(cursor); a_pEvent->acceptProposedAction(); } // END OF void ScriptEditor::dragMoveEvent(QDragMoveEvent * a_pEvent) //============================================================================== void ScriptEditor::dropEvent(QDropEvent * a_pEvent) { if(!a_pEvent->mimeData()->hasUrls()) { QPlainTextEdit::dropEvent(a_pEvent); return; } QList urls = a_pEvent->mimeData()->urls(); Q_ASSERT(urls.size() > 0); if(urls.size() == 1) { QString filePath = urls[0].toLocalFile(); static QRegularExpression re = QRegularExpression("\\.vpy$"); if(re.match(filePath).hasMatch()) { bool handled = false; emit signalScriptFileDropped(filePath, &handled); if(handled) { a_pEvent->acceptProposedAction(); return; } } } QStringList textList; for(m_droppedFileNumber = 0; m_droppedFileNumber < urls.size(); ++m_droppedFileNumber) { m_droppedFilePath = QDir::cleanPath(urls[m_droppedFileNumber].toLocalFile()); m_droppedFilePath = QDir::toNativeSeparators(m_droppedFilePath); QString sourceTemplate = m_pSettingsManager->getDropFileTemplate(m_droppedFilePath); for(const vsedit::VariableToken & variable : m_variables) sourceTemplate = sourceTemplate.replace(variable.token, variable.evaluate()); textList += sourceTemplate; } slotInsertTextAtNewLine(textList.join("\n")); a_pEvent->acceptProposedAction(); } // END OF void ScriptEditor::dropEvent(QDropEvent * a_pEvent) //============================================================================== void ScriptEditor::slotTextChanged() { QString newPlainText = toPlainText(); if(m_plainText == newPlainText) return; m_plainText = newPlainText; QString vsCoreName = getVapourSynthCoreName(); setChildrenCoreName(vsCoreName); } // END OF void ScriptEditor::slotTextChanged() //============================================================================== void ScriptEditor::slotUpdateSideBoxWidth() { setViewportMargins(sideBoxWidth(), 0, 0, 0); } // END OF void ScriptEditor::slotUpdateSideBoxWidth() //============================================================================== void ScriptEditor::slotUpdateSideBox(const QRect & a_rect, int a_dy) { if(a_dy) m_pSideBox->scroll(0, a_dy); else m_pSideBox->update(0, a_rect.y(), m_pSideBox->width(), a_rect.height()); } // END OF void ScriptEditor::slotUpdateSideBox(const QRect & a_rect, int a_dy) //============================================================================== void ScriptEditor::slotHighlightCurrentBlockAndMatches() { QTextDocument * pDocument = document(); QList extraTextSelections; QTextCursor cursor = textCursor(); QString selectedText = cursor.selectedText(); cursor.movePosition(QTextCursor::StartOfBlock); int linesInBlock = cursor.block().lineCount(); for(int i = 0; i < linesInBlock; ++i) { QTextEdit::ExtraSelection selection; selection.format.setBackground(m_activeLineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = cursor; selection.cursor.clearSelection(); extraTextSelections.append(selection); cursor.movePosition(QTextCursor::EndOfLine); cursor.movePosition(QTextCursor::NextCharacter); } if(m_highlightSelectionMatches && (selectedText.length() >= m_highlightSelectionMatchesMinLength)) { cursor = QTextCursor(pDocument); while(true) { cursor = pDocument->find(selectedText, cursor); if(cursor.isNull()) break; QTextEdit::ExtraSelection selection; selection.format.setBackground(m_selectionMatchesColor); selection.cursor = cursor; extraTextSelections.append(selection); } } setExtraSelections(extraTextSelections); } // END OF void ScriptEditor::slotHighlightCurrentBlockAndMatches() //============================================================================== void ScriptEditor::slotShowCustomMenu(const QPoint & a_position) { if(m_pContextMenu) delete m_pContextMenu; m_pContextMenu = createStandardContextMenu(); if(m_pSettingsManager) { m_pContextMenu->addSeparator(); std::vector actionsList = actionsForMenu(); for(QAction * pAction : actionsList) m_pContextMenu->addAction(pAction); } QPoint globalPosition = mapToGlobal(a_position); m_pContextMenu->popup(globalPosition); } // END OF void ScriptEditor::slotShowCustomMenu(const QPoint & a_position) //============================================================================== void ScriptEditor::createActionsAndMenus() { if(!m_pSettingsManager) return; struct ActionToCreate { QAction ** ppAction; const char * id; const char * slotToConnect; }; ActionToCreate actionsToCreate[] = { {&m_pActionDuplicateSelection, ACTION_ID_DUPLICATE_SELECTION, SLOT(slotDuplicateSelection())}, {&m_pActionCommentSelection, ACTION_ID_COMMENT_SELECTION, SLOT(slotCommentSelection())}, {&m_pActionUncommentSelection, ACTION_ID_UNCOMMENT_SELECTION, SLOT(slotUncommentSelection())}, {&m_pActionReplaceTabWithSpaces, ACTION_ID_REPLACE_TAB_WITH_SPACES, SLOT(slotReplaceTabWithSpaces())}, {&m_pActionAutocomplete, ACTION_ID_AUTOCOMPLETE, SLOT(slotComplete())}, {&m_pActionMoveTextBlockUp, ACTION_ID_MOVE_TEXT_BLOCK_UP, SLOT(slotMoveTextBlockUp())}, {&m_pActionMoveTextBlockDown, ACTION_ID_MOVE_TEXT_BLOCK_DOWN, SLOT(slotMoveTextBlockDown())}, {&m_pActionToggleComment, ACTION_ID_TOGGLE_COMMENT, SLOT(slotToggleComment())}, }; for(ActionToCreate & item : actionsToCreate) { if(*item.ppAction) continue; QAction * pAction = m_pSettingsManager->createStandardAction( item.id, this); *item.ppAction = pAction; addAction(pAction); m_settableActionsList.push_back(pAction); connect(pAction, SIGNAL(triggered()), this, item.slotToConnect); } } // END OF void ScriptEditor::createActionsAndMenus() //============================================================================== QString ScriptEditor::getVapourSynthCoreName() const { QString vapourSynthName; QString vsCoreName = "core"; int blocksCount = document()->blockCount(); // Search for VapourSynth object. // Usually looks like: import vapoursynth as vs int vsImportBlock = -1; QString searchString("import vapoursynth"); for(int k = 0; k < blocksCount; ++k) { QString simplifiedText = document()->findBlockByNumber(k).text().simplified(); int i = simplifiedText.indexOf(searchString); if(i < 0) continue; vapourSynthName = "vapoursynth"; i += searchString.length(); if(simplifiedText.mid(i, 4) == " as ") { i += 4; if(!(simplifiedText[i].isLetter() || (simplifiedText[i] == '_'))) continue; int j = i; for(; j < simplifiedText.length(); ++j) { if(!(simplifiedText[j].isLetterOrNumber() || (simplifiedText[j] == '_'))) break; } vapourSynthName = simplifiedText.mid(i, j - i); } vsImportBlock = k; break; } if((vsImportBlock < 0) || (vsImportBlock == (blocksCount - 1))) return vsCoreName; // Search for VapourSynth core creation // Usually looks like: core = vs.get_core() searchString = QString("%1.get_core").arg(vapourSynthName); for(int k = vsImportBlock + 1; k < blocksCount; ++k) { QString simplifiedText = document()->findBlockByNumber(k).text().simplified(); int i = simplifiedText.indexOf(searchString); if(i < 0) continue; i--; if(simplifiedText[i].isSpace()) i--; if(simplifiedText[i] != '=') break; i--; if(simplifiedText[i].isSpace()) i--; int j = i + 1; for(; i >= 0; --i) { if(!(simplifiedText[i].isLetterOrNumber() || (simplifiedText[i] == '_'))) break; } i++; if(simplifiedText[i].isDigit()) break; vsCoreName = simplifiedText.mid(i, j - i); return vsCoreName; } return vsCoreName; } // END OF QString ScriptEditor::getVapourSynthCoreName() const //============================================================================== void ScriptEditor::setChildrenCoreName(const QString & a_coreName) { m_pCompleterModel->setCoreName(a_coreName); m_pSyntaxHighlighter->setCoreName(a_coreName); } // END OF void ScriptEditor::setChildrenCoreName(const QString & a_coreName) // const //============================================================================== int ScriptEditor::sideBoxWidth() const { QString controlString("9"); int max = std::max(1, blockCount()); while(max >= 10) { max /= 10; controlString += "9"; } QFont commonTextFont = m_commonScriptTextFormat.font(); QFontMetrics metrics(commonTextFont); int space = metrics.horizontalAdvance(controlString); space += m_sideBoxTextMargin * 2; space += m_sideBoxLineWidth; return space; } // END OF int ScriptEditor::sideBoxWidth() const //============================================================================== void ScriptEditor::paintSideBox(QPaintEvent * a_pEvent) { QPainter painter(m_pSideBox); painter.fillRect(a_pEvent->rect(), m_backgroundColor); // Draw border line between sidebox and text. QRect borderLineRect = a_pEvent->rect(); borderLineRect.setLeft(borderLineRect.right() - m_sideBoxLineWidth + 1); painter.fillRect(borderLineRect, m_activeLineColor); // Draw visible lines numbers. painter.setPen(m_commonScriptTextFormat.foreground().color()); QFont commonTextFont = m_commonScriptTextFormat.font(); QFontMetrics metrics(commonTextFont); int labelHeight = metrics.height(); painter.setFont(commonTextFont); int lineNumberWidth = m_pSideBox->width() - m_sideBoxLineWidth - m_sideBoxTextMargin * 2; QPointF offset = contentOffset(); int viewportHeight = viewport()->rect().height(); qreal blockTop; for(QTextBlock textBlock = firstVisibleBlock(); textBlock.isValid() && ((blockTop = blockBoundingGeometry(textBlock).translated(offset).top()) < viewportHeight); textBlock = textBlock.next()) { if(!textBlock.isVisible()) continue; QString number = QString::number(textBlock.blockNumber() + 1); painter.drawText(m_sideBoxTextMargin, blockTop, lineNumberWidth, labelHeight, Qt::AlignRight, number); } } // END OF void ScriptEditor::paintSideBox(QPaintEvent * a_pEvent) //============================================================================== void ScriptEditor::indentNewLine() { QTextCursor currentCursor = textCursor(); QTextBlock currentBlock = currentCursor.block(); int blockNumber = currentBlock.blockNumber(); Q_ASSERT(blockNumber != 0); QTextBlock previousBlock = document()->findBlockByNumber(blockNumber - 1); QString blockText = previousBlock.text(); int blockLength = blockText.length(); QString indentation; for(int i = 0; i < blockLength; ++i) { if(!blockText[i].isSpace()) break; indentation += blockText[i]; } currentCursor.insertText(indentation); } // END OF void ScriptEditor::indentNewLine() //============================================================================== void ScriptEditor::insertSelectedLinesBegin(const QString & a_text) { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); int firstBlockNumber = firstBlock.blockNumber(); int lastBlockNumber = lastBlock.blockNumber(); cursor.beginEditBlock(); for(int i = firstBlockNumber; i <= lastBlockNumber; ++i) { QTextBlock block = pDocument->findBlockByNumber(i); int position = block.position(); cursor.setPosition(position); cursor.insertText(a_text); } cursor.endEditBlock(); } // END OF void ScriptEditor::insertSelectedLinesBegin(const QString & a_text) //============================================================================== void ScriptEditor::removeSelectedLinesBegin(const QString & a_text) { QTextCursor cursor = textCursor(); QTextDocument * pDocument = document(); QTextBlock firstBlock = pDocument->findBlock(cursor.selectionStart()); QTextBlock lastBlock = pDocument->findBlock(cursor.selectionEnd()); int firstBlockNumber = firstBlock.blockNumber(); int lastBlockNumber = lastBlock.blockNumber(); int tokenLength = a_text.length(); cursor.beginEditBlock(); for(int i = firstBlockNumber; i <= lastBlockNumber; ++i) { QTextBlock block = pDocument->findBlockByNumber(i); int position = block.position(); cursor.setPosition(position); cursor.setPosition(std::min(position + tokenLength, pDocument->characterCount() - 1), QTextCursor::KeepAnchor); if(cursor.selectedText() == a_text) cursor.removeSelectedText(); } cursor.endEditBlock(); } // END OF void ScriptEditor::removeSelectedLinesBegin(const QString & a_text) //============================================================================== void ScriptEditor::fillVariables() { m_variables = { {"{f}", tr("file path"), [&]() { return m_droppedFilePath; } }, {"{d}", tr("file directory"), [&]() { QFileInfo file(m_droppedFilePath); return QDir::toNativeSeparators(file.path()); } }, {"{n}", tr("file name"), [&]() { QFileInfo file(m_droppedFilePath); return file.completeBaseName(); } }, {"{x}", tr("file extension"), [&]() { QFileInfo file(m_droppedFilePath); return file.suffix(); } }, {"{i}", tr("file number in the list of dropped files"), [&]() { return m_droppedFileNumber > 0 ? QString::number(m_droppedFileNumber + 1) : QString(); } }, }; std::sort(m_variables.begin(), m_variables.end(), [&](const vsedit::VariableToken & a_first, const vsedit::VariableToken & a_second) -> bool { return (a_first.token.length() > a_second.token.length()); }); } // END OF void ScriptEditor::fillVariables() //============================================================================== ================================================ FILE: vsedit/src/script_editor/script_editor.h ================================================ #ifndef SCRIPTEDITOR_H #define SCRIPTEDITOR_H #include "../../../common-src/helpers.h" #include "../vapoursynth/vs_plugin_data.h" #include #include #include class QEvent; class QKeyEvent; class QResizeEvent; class QPaintEvent; class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; class QAction; class ScriptCompleterModel; class ScriptCompleter; class SyntaxHighlighter; class SettingsManager; class SettingsDialog; class ScriptEditor : public QPlainTextEdit { Q_OBJECT public: ScriptEditor(QWidget * a_pParent = nullptr); virtual ~ScriptEditor(); QString text() const; QPoint cursorPosition() const; void setCursorPosition(const QPoint & a_point); void setCursorPosition(int a_line, int a_index); bool isModified() const; void setModified(bool a_modified); void setPluginsList(const VSPluginsList & a_pluginsList); void setSettingsManager(SettingsManager * a_pSettingsManager); std::vector actionsForMenu() const; std::vector variables() const; public slots: void slotLoadSettings(); void slotComplete(); void slotInsertCompletion(const QString & a_completionString); void slotDuplicateSelection(); void slotCommentSelection(); void slotUncommentSelection(); void slotReplaceTabWithSpaces(); void slotTab(); void slotBackTab(); void slotHome(bool a_select = false); void slotInsertTextAtNewLine(const QString & a_text); void slotMoveTextBlockUp(); void slotMoveTextBlockDown(); void slotToggleComment(); signals: void signalScriptFileDropped(const QString & a_filePath, bool * a_pHandled); protected: virtual bool eventFilter(QObject * a_pObject, QEvent * a_pEvent) override; virtual void resizeEvent(QResizeEvent * a_pEvent) override; virtual void keyPressEvent(QKeyEvent * a_pEvent) override; virtual void dragEnterEvent(QDragEnterEvent * a_pEvent) override; virtual void dragMoveEvent(QDragMoveEvent * a_pEvent) override; virtual void dropEvent(QDropEvent * a_pEvent) override; private slots: void slotTextChanged(); void slotUpdateSideBoxWidth(); void slotUpdateSideBox(const QRect & a_rect, int a_dy); void slotHighlightCurrentBlockAndMatches(); void slotShowCustomMenu(const QPoint & a_position); private: void createActionsAndMenus(); QString getVapourSynthCoreName() const; void setChildrenCoreName(const QString & a_coreName); int sideBoxWidth() const; void paintSideBox(QPaintEvent * a_pEvent); void indentNewLine(); void insertSelectedLinesBegin(const QString & a_text); void removeSelectedLinesBegin(const QString & a_text); void fillVariables(); SettingsManager * m_pSettingsManager; QWidget * m_pSideBox; int m_sideBoxLineWidth; int m_sideBoxTextMargin; ScriptCompleterModel * m_pCompleterModel; ScriptCompleter * m_pCompleter; SyntaxHighlighter * m_pSyntaxHighlighter; int m_typedCharacters; int m_charactersTypedToStartCompletion; QString m_plainText; QColor m_backgroundColor; QColor m_activeLineColor; QColor m_selectionMatchesColor; bool m_highlightSelectionMatches; int m_highlightSelectionMatchesMinLength; QTextCharFormat m_commonScriptTextFormat; QString m_tabText; int m_spacesInTab; QMenu * m_pContextMenu; QAction * m_pActionDuplicateSelection; QAction * m_pActionCommentSelection; QAction * m_pActionUncommentSelection; QAction * m_pActionReplaceTabWithSpaces; QAction * m_pActionAutocomplete; QAction * m_pActionMoveTextBlockUp; QAction * m_pActionMoveTextBlockDown; QAction * m_pActionToggleComment; std::vector m_settableActionsList; QString m_droppedFilePath; int m_droppedFileNumber; std::vector m_variables; }; #endif // SCRIPTEDITOR_H ================================================ FILE: vsedit/src/script_editor/syntax_highlighter.cpp ================================================ #include "syntax_highlighter.h" #include "number_matcher.h" #include "../../../common-src/settings/settings_manager.h" #include #include //============================================================================== // Text block states: 0 - 80 are used for indentation value. // Larger values are special cases. enum class BlockState : int { LongStringSingleStart = 81, LongStringSingleMiddle, LongStringDoubleStart, LongStringDoubleMiddle }; enum class TokenType { Undecided, Keyword, Operator, String, Number, Comment, VSCore, VSNamespace, VSFunction, }; struct Token { QString text; int start; int length; TokenType type; Token(const QString & a_text, int a_start, int a_length, TokenType a_type): text(a_text), start(a_start), length(a_length), type(a_type){} }; //============================================================================== SyntaxHighlighter::SyntaxHighlighter(QTextDocument * a_pDocument, VSPluginsList a_pluginsList) : QSyntaxHighlighter(a_pDocument) , m_pSettingsManager(nullptr) , m_coreName("core") , m_pluginsList(a_pluginsList) , m_keywordsList() , m_operatorsList() , m_keywordFormat() , m_operatorFormat() , m_stringFormat() , m_numberFormat() , m_commentFormat() , m_vsCoreFormat() , m_vsNamespaceFormat() , m_vsFunctionFormat() , m_vsArgumentFormat() { m_keywordsList << "False" << "None" << "True" << "and" << "as" << "assert" << "break" << "case" << "class" << "continue" << "def" << "del" << "elif" << "else" << "except" << "finally" << "for" << "from" << "global" << "if" << "import" << "in" << "is" << "lambda" << "match" << "nonlocal" << "not" << "or" << "pass" << "raise" << "return" << "try" << "while" << "with" << "yield"; // MUST be sorted by length in descending order. m_operatorsList << "//=" << ">>=" << "<<=" << "**=" << "**" << "//" << "<<" << ">>" << "<=" << ">=" << "==" << "!=" << "->" << "+=" << "-=" << "*=" << "/=" << "%=" << "&=" << "|=" << "^=" << "+" << "-" << "*" << "/" << "%" << "&" << "|" << "^" << "~" << "<" << ">" << "(" << ")" << "[" << "]" << "{" << "}" << "," << ":" << "." << ";" << "@" << "="; // Don't trust yourself. Sort it. std::sort(m_operatorsList.begin(), m_operatorsList.end(), [&](const QString & a_first, const QString & a_second)->bool { return (a_first.length() > a_second.length()); }); } // END OF SyntaxHighlighter::SyntaxHighlighter(QTextDocument * a_pDocument, // VSPluginsList a_pluginsList) //============================================================================== SyntaxHighlighter::~SyntaxHighlighter() { } // END OF SyntaxHighlighter::~SyntaxHighlighter() //============================================================================== void SyntaxHighlighter::setSettingsManager(SettingsManager * a_pSettingsManager) { m_pSettingsManager = a_pSettingsManager; slotLoadSettings(); } // END OF void SyntaxHighlighter::setSettingsManager( // SettingsManager * a_pSettingsManager) //============================================================================== void SyntaxHighlighter::setCoreName(const QString & a_coreName) { if(m_coreName == a_coreName) return; m_coreName = a_coreName; rehighlight(); } // END OF void SyntaxHighlighter::setCoreName(const QString & a_coreName) //============================================================================== void SyntaxHighlighter::setPluginsList(VSPluginsList a_pluginsList) { m_pluginsList = a_pluginsList; } // END OF void SyntaxHighlighter::setPluginsList(VSPluginsList a_pluginsList) //============================================================================== void SyntaxHighlighter::slotLoadSettings() { m_keywordFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_KEYWORD); m_operatorFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_OPERATOR); m_stringFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_STRING); m_numberFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_NUMBER); m_commentFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_COMMENT); m_vsCoreFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_VS_CORE); m_vsNamespaceFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_VS_NAMESPACE); m_vsFunctionFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_VS_FUNCTION); m_vsArgumentFormat = m_pSettingsManager->getTextFormat( TEXT_FORMAT_ID_VS_ARGUMENT); rehighlight(); } // END OF void SyntaxHighlighter::slotLoadSettings() //============================================================================== void SyntaxHighlighter::highlightBlock(const QString & a_text) { setCurrentBlockState(0); std::vector tokens; int i = 0; int j = 0; int textLength = a_text.length(); bool goToNextToken = false; while(i < textLength) { //------Long string continuation, single quotes--------------------------------- if((i == 0) && ( (previousBlockState() == (int)BlockState::LongStringSingleStart) || (previousBlockState() == (int)BlockState::LongStringSingleMiddle))) { bool foundMatchingQuotes = false; for(j = i; j < textLength - 2; ++j) { if((a_text.mid(j, 3) == "'''") && ((j == 0) || ((j != 0) && (a_text[j - 1] != '\\')))) { foundMatchingQuotes = true; break; } } if(foundMatchingQuotes) { j += 3; Token newToken(a_text.mid(i, j-i), i, j, TokenType::String); tokens.push_back(newToken); i = j; continue; } else { setCurrentBlockState((int)BlockState::LongStringSingleMiddle); setFormat(0, a_text.length(), m_stringFormat); return; } } //------Long string continuation, double quotes--------------------------------- if((i == 0) && ( (previousBlockState() == (int)BlockState::LongStringDoubleStart) || (previousBlockState() == (int)BlockState::LongStringDoubleMiddle))) { bool foundMatchingQuotes = false; for(j = i; j < textLength - 2; ++j) { if((a_text.mid(j, 3) == "\"\"\"") && ((j == 0) || ((j != 0) && (a_text[j - 1] != '\\')))) { foundMatchingQuotes = true; break; } } if(foundMatchingQuotes) { j += 3; Token newToken(a_text.mid(i, j-i), i, j - i, TokenType::String); tokens.push_back(newToken); i = j; continue; } else { setCurrentBlockState((int)BlockState::LongStringDoubleMiddle); setFormat(0, a_text.length(), m_stringFormat); return; } } //------Long string, single quotes.--------------------------------------------- if((((a_text[i].toLower() == 'r') || (a_text[i].toLower() == 'u')) && (a_text.mid(i + 1, 3) == "'''")) || (a_text.mid(i, 3) == "'''")) { if(a_text[i] == '\'') j = i + 3; else j = i + 4; bool foundMatchingQuotes = false; for(; j < textLength - 2; ++j) { if((a_text.mid(j, 3) == "'''") && ((j == 0) || ((j != 0) && (a_text[j - 1] != '\\')))) { foundMatchingQuotes = true; break; } } if(foundMatchingQuotes) { j += 3 - i; Token newToken(a_text.mid(i, j), i, j, TokenType::String); tokens.push_back(newToken); i += j; continue; } else { j = textLength - i; Token newToken(a_text.mid(i, j), i, j, TokenType::String); tokens.push_back(newToken); setCurrentBlockState((int)BlockState::LongStringSingleStart); break; } } //------Long string, double quotes---------------------------------------------- if((((a_text[i].toLower() == 'r') || (a_text[i].toLower() == 'u')) && (a_text.mid(i + 1, 3) == "\"\"\"")) || (a_text.mid(i, 3) == "\"\"\"")) { if(a_text[i] == '\"') j = i + 3; else j = i + 4; bool foundMatchingQuotes = false; for(; j < textLength - 2; ++j) { if((a_text.mid(j, 3) == "\"\"\"") && ((j == 0) || ((j != 0) && (a_text[j - 1] != '\\')))) { foundMatchingQuotes = true; break; } } if(foundMatchingQuotes) { j += 3 - i; Token newToken(a_text.mid(i, j), i, j, TokenType::String); tokens.push_back(newToken); i += j; continue; } else { j = textLength - i; Token newToken(a_text.mid(i, j), i, j, TokenType::String); tokens.push_back(newToken); setCurrentBlockState((int)BlockState::LongStringDoubleStart); break; } } //------Short string, single quotes--------------------------------------------- if((a_text[i] == '\'') || (a_text.mid(i, 2).toLower() == "r'") || (a_text.mid(i, 2).toLower() == "u'")) { if(a_text[i] == '\'') j = i + 1; else j = i + 2; for(; j < textLength; ++j) { if((a_text[j] == '\'') && ((j == 0) || ((j != 0) && (a_text[j - 1] != '\\')))) break; } j += 1 - i; Token newToken(a_text.mid(i, j), i, j, TokenType::String); tokens.push_back(newToken); i += j; continue; } //------Short string, double quotes--------------------------------------------- if((a_text[i] == '\"') || (a_text.mid(i, 2).toLower() == "r\"") || (a_text.mid(i, 2).toLower() == "u\"")) { if(a_text[i] == '\"') j = i + 1; else j = i + 2; for(; j < textLength; ++j) { if((a_text[j] == '\"') && ((j == 0) || ((j != 0) && (a_text[j - 1] != '\\')))) break; } j += 1 - i; Token newToken(a_text.mid(i, j), i, j, TokenType::String); tokens.push_back(newToken); i += j; continue; } //------Comment----------------------------------------------------------------- if(a_text[i] == '#') { j = a_text.length(); Token newToken(a_text.mid(i, j-i), i, j, TokenType::Comment); tokens.push_back(newToken); break; } //------Keyword or unknown word------------------------------------------------- if((a_text[i].isLetter()) || (a_text[i] == '_')) { for(j = i; j < textLength; ++j) { if(!(a_text[j].isLetterOrNumber() || (a_text[j] == '_'))) break; } j = j - i; QString word = a_text.mid(i, j); Token newToken(word, i, j , TokenType::Undecided); for(const QString & keyword : m_keywordsList) { if(word == keyword) { newToken.type = TokenType::Keyword; break; } } tokens.push_back(newToken); i += j; continue; } //------Number------------------------------------------------------------------ NumberMatcher matcher; if(matcher.beginsWithNumber(a_text, i)) { j = matcher.matchedLength(); Token newToken(a_text.mid(i, j), i, j, TokenType::Number); tokens.push_back(newToken); i += j; continue; } //------Operator---------------------------------------------------------------- goToNextToken = false; for(const QString & operatorString : m_operatorsList) { if(goToNextToken) break; j = operatorString.length(); QString substring = a_text.mid(i, j); if(substring == operatorString) { Token newToken(substring, i, j, TokenType::Operator); tokens.push_back(newToken); i += j; goToNextToken = true; } } if(goToNextToken) continue; //------------------------------------------------------------------------------ i++; } size_t tokensNumber = tokens.size(); if(tokensNumber == 0) setCurrentBlockState(previousBlockState()); for(size_t k = 0; k < tokensNumber; ++k) { Token & token = tokens[k]; if(token.text == m_coreName) token.type = TokenType::VSCore; else if((k > 1) && (tokens[k - 1].text == ".") && (tokens[k - 2].type == TokenType::VSCore)) { for(const VSData::Plugin & plugin : m_pluginsList) { if(token.text == plugin.pluginNamespace) { token.type = TokenType::VSNamespace; break; } } } else if((k > 3) && (tokens[k - 1].text == ".") && (tokens[k - 2].type == TokenType::VSNamespace) && (tokens[k - 3].text == ".") && (tokens[k - 4].type == TokenType::VSCore)) { bool foundFunction = false; for(const VSData::Plugin & plugin : m_pluginsList) { foundFunction = false; if(tokens[k - 2].text == plugin.pluginNamespace) { for(const VSData::Function & function : plugin.functions) { if(token.text == function.name) { token.type = TokenType::VSFunction; foundFunction = true; break; } } if(foundFunction) break; } } } if(token.type == TokenType::Keyword) setFormat(token.start, token.length, m_keywordFormat); else if(token.type == TokenType::Operator) setFormat(token.start, token.length, m_operatorFormat); else if(token.type == TokenType::String) setFormat(token.start, token.length, m_stringFormat); else if(token.type == TokenType::Number) setFormat(token.start, token.length, m_numberFormat); else if(token.type == TokenType::Comment) setFormat(token.start, token.length, m_commentFormat); else if(token.type == TokenType::VSCore) setFormat(token.start, token.length, m_vsCoreFormat); else if(token.type == TokenType::VSNamespace) setFormat(token.start, token.length, m_vsNamespaceFormat); else if(token.type == TokenType::VSFunction) setFormat(token.start, token.length, m_vsFunctionFormat); } } // END OF void SyntaxHighlighter::highlightBlock(const QString & a_text) //============================================================================== ================================================ FILE: vsedit/src/script_editor/syntax_highlighter.h ================================================ #ifndef SYNTAXHIGHLIGHTER_H #define SYNTAXHIGHLIGHTER_H #include "../vapoursynth/vs_plugin_data.h" #include #include #include class SettingsManager; class SyntaxHighlighter : public QSyntaxHighlighter { Q_OBJECT public: SyntaxHighlighter(QTextDocument * a_pDocument, VSPluginsList a_pluginsList = VSPluginsList()); virtual ~SyntaxHighlighter(); void setSettingsManager(SettingsManager * a_pSettingsManager); void setCoreName(const QString & a_coreName); void setPluginsList(VSPluginsList a_pluginsList); public slots: void slotLoadSettings(); protected: void highlightBlock(const QString & a_text); private: SettingsManager * m_pSettingsManager; QString m_coreName; VSPluginsList m_pluginsList; QStringList m_keywordsList; QStringList m_operatorsList; QTextCharFormat m_keywordFormat; QTextCharFormat m_operatorFormat; QTextCharFormat m_stringFormat; QTextCharFormat m_numberFormat; QTextCharFormat m_commentFormat; QTextCharFormat m_vsCoreFormat; QTextCharFormat m_vsNamespaceFormat; QTextCharFormat m_vsFunctionFormat; QTextCharFormat m_vsArgumentFormat; }; #endif // SYNTAXHIGHLIGHTER_H ================================================ FILE: vsedit/src/script_status_bar_widget/script_status_bar_widget.cpp ================================================ #include "script_status_bar_widget.h" #include "../../../common-src/helpers.h" #include //============================================================================== ScriptStatusBarWidget::ScriptStatusBarWidget(QWidget * a_pParent) : QWidget(a_pParent) , m_readyPixmap(":tick.png") , m_busyPixmap(":busy.png") { vsedit::disableFontKerning(this); m_ui.setupUi(this); m_ui.colorPickerWidget->setVisible(false); m_ui.colorPickerIconLabel->setPixmap(QPixmap(":color_picker.png")); m_ui.colorPickerLabel->clear(); m_ui.scriptProcessorQueueLabel->clear(); m_ui.videoInfoLabel->clear(); m_ui.scriptProcessorQueueIconLabel->setPixmap(m_readyPixmap); setQueueState(0, 0, 0, 0.0); } // END OF ScriptStatusBarWidget::ScriptStatusBarWidget(QWidget * a_pParent) //============================================================================== ScriptStatusBarWidget::~ScriptStatusBarWidget() { } // END OF ScriptStatusBarWidget::~ScriptStatusBarWidget() //============================================================================== bool ScriptStatusBarWidget::colorPickerVisible() const { return m_ui.colorPickerWidget->isVisible(); } // END OF bool ScriptStatusBarWidget::colorPickerVisible() const //============================================================================== void ScriptStatusBarWidget::setColorPickerVisible(bool a_visible) { m_ui.colorPickerWidget->setVisible(a_visible); } // END OF void ScriptStatusBarWidget::setColorPickerVisible(bool a_visible) //============================================================================== void ScriptStatusBarWidget::setColorPickerString(const QString & a_string) { m_ui.colorPickerLabel->setText(a_string); } // END OF void ScriptStatusBarWidget::setColorPickerString( // const QString & a_string) //============================================================================== void ScriptStatusBarWidget::setQueueState(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio) { if((a_inProcess + a_inQueue) > 0) m_ui.scriptProcessorQueueIconLabel->setPixmap(m_busyPixmap); else m_ui.scriptProcessorQueueIconLabel->setPixmap(m_readyPixmap); int percentage_int = (int)(a_usedCacheRatio * 100); int percentage_dec = (int)(a_usedCacheRatio * 1000) - 10 * percentage_int; m_ui.scriptProcessorQueueLabel->setText( tr("Script processor queue: %1:%2(%3) | Core cache used: %4.%5%") .arg(a_inQueue).arg(a_inProcess).arg(a_maxThreads) .arg(percentage_int).arg(percentage_dec)); } // END OF void ScriptStatusBarWidget::setQueueState(size_t a_inQueue, // size_t a_inProcess, size_t a_maxThreads) //============================================================================== void ScriptStatusBarWidget::setNodeInfo(const VSNodeInfo & a_nodeInfo, const VSAPI * a_cpVSAPI) { QString infoString = vsedit::nodeInfoString(a_nodeInfo, a_cpVSAPI); m_ui.videoInfoLabel->setText(infoString); } // END OF void ScriptStatusBarWidget::setVideoInfo( // const VSVideoInfo * a_cpVideoInfo) //============================================================================== ================================================ FILE: vsedit/src/script_status_bar_widget/script_status_bar_widget.h ================================================ #ifndef SCRIPT_STATUS_BAR_WIDGET_H_INCLUDED #define SCRIPT_STATUS_BAR_WIDGET_H_INCLUDED #include #include #include class VSNodeInfo; class ScriptStatusBarWidget: public QWidget { Q_OBJECT public: ScriptStatusBarWidget(QWidget * a_pParent = nullptr); virtual ~ScriptStatusBarWidget(); virtual bool colorPickerVisible() const; public slots: virtual void setColorPickerVisible(bool a_visible = true); virtual void setColorPickerString(const QString & a_string); virtual void setQueueState(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio); virtual void setNodeInfo(const VSNodeInfo & a_nodeInfo, const VSAPI * a_cpVSAPI); protected: Ui::ScriptStatusBarWidget m_ui; QPixmap m_readyPixmap; QPixmap m_busyPixmap; }; #endif // SCRIPT_STATUS_BAR_WIDGET_H_INCLUDED ================================================ FILE: vsedit/src/script_status_bar_widget/script_status_bar_widget.ui ================================================ ScriptStatusBarWidget 0 0 674 67 0 0 Form 2 2 2 2 2 4 0 0 0 0 colorPickerIconLabel colorPickerLabel Qt::Horizontal 486 20 4 0 0 0 0 scriptProcessorQueueIconLabel scriptProcessorQueueLabel Qt::Horizontal 268 20 videoInfoLabel Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse ================================================ FILE: vsedit/src/script_templates/drop_file_category_model.cpp ================================================ #include "drop_file_category_model.h" #include #include //============================================================================== const int COLUMNS_NUMBER = 2; const int NAME_COLUMN = 0; const int MASK_LIST_COLUMN = 1; const char MASK_LIST_SPLITTER = ';'; //============================================================================== DropFileCategoryModel::DropFileCategoryModel(QObject * a_pParent) : QAbstractItemModel(a_pParent) { } // END OF DropFileCategoryModel::DropFileCategoryModel(QObject * a_pParent) : // QAbstractItemModel(a_pParent) //============================================================================== DropFileCategoryModel::~DropFileCategoryModel() { } // END OF DropFileCategoryModel::~DropFileCategoryModel() //============================================================================== QModelIndex DropFileCategoryModel::index(int a_row, int a_column, const QModelIndex & a_parent) const { (void)a_parent; return createIndex(a_row, a_column); } // END OF QModelIndex DropFileCategoryModel::index(int a_row, int a_column, // const QModelIndex & a_parent) const //============================================================================== QModelIndex DropFileCategoryModel::parent(const QModelIndex & a_child) const { (void)a_child; return QModelIndex(); } // END OF QModelIndex DropFileCategoryModel::parent( // const QModelIndex & a_child) const //============================================================================== Qt::ItemFlags DropFileCategoryModel::flags(const QModelIndex & a_index) const { if (!a_index.isValid()) { return Qt::NoItemFlags; } Qt::ItemFlags cellFlags = Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemNeverHasChildren ; return cellFlags; } // END OF Qt::ItemFlags DropFileCategoryModel::flags( // const QModelIndex & a_index) const //============================================================================== QVariant DropFileCategoryModel::data(const QModelIndex & a_index, int a_role) const { if(!a_index.isValid()) return QVariant(); if((a_index.row() >= (int)m_categories.size()) || (a_index.column() >= COLUMNS_NUMBER)) return QVariant(); if((a_index.column() == NAME_COLUMN) && ((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole) || (a_role == Qt::EditRole))) return m_categories[a_index.row()].name; else if((a_index.column() == MASK_LIST_COLUMN) && ((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole) || (a_role == Qt::EditRole))) return m_categories[a_index.row()].maskList.join(MASK_LIST_SPLITTER); return QVariant(); } // END OF QVariant DropFileCategoryModel::data(const QModelIndex & a_index, // int a_role) //============================================================================== QVariant DropFileCategoryModel::headerData(int a_section, Qt::Orientation a_orientation, int a_role) const { if(a_orientation != Qt::Horizontal) return QVariant(); if(a_role != Qt::DisplayRole) return QVariant(); if(a_section == NAME_COLUMN) return tr("Category"); if(a_section == MASK_LIST_COLUMN) return tr("File name mask list"); return QVariant(); } // END OF QVariant DropFileCategoryModel::headerData(int a_section, // Qt::Orientation a_orientation, int a_role) const //============================================================================== int DropFileCategoryModel::rowCount(const QModelIndex & a_parent) const { (void)a_parent; return (int)m_categories.size(); } // END OF int DropFileCategoryModel::rowCount(const QModelIndex & a_parent) // const //============================================================================== int DropFileCategoryModel::columnCount(const QModelIndex & a_parent) const { (void)a_parent; return COLUMNS_NUMBER; } // END OF int DropFileCategoryModel::columnCount(const QModelIndex & a_parent) // const //============================================================================== bool DropFileCategoryModel::setData(const QModelIndex & a_index, const QVariant & a_value, int a_role) { if(a_role != Qt::EditRole) return false; if((a_index.row() >= (int)m_categories.size()) || (a_index.column() >= COLUMNS_NUMBER)) return false; if(a_index.column() == NAME_COLUMN) { for(int i = 0; i < (int)m_categories.size(); ++i) { if((i != a_index.row()) && (m_categories[i].name == a_value.toString())) return false; } m_categories[a_index.row()].name = a_value.toString(); return true; } if(a_index.column() == MASK_LIST_COLUMN) { QStringList maskList = a_value.toString().split(MASK_LIST_SPLITTER); for(int i = 0; i < (int)m_categories.size(); ++i) { if(i == a_index.row()) continue; QSet set_curr(maskList.begin(), maskList.end()); QSet set_i(m_categories[i].maskList.begin(), m_categories[i].maskList.end()); QSet intersection = set_curr.intersect(set_i); if(intersection.size() > 0) return false; } for(const QString & mask : maskList) { auto re = QRegularExpression::fromWildcard(mask, Qt::CaseInsensitive, QRegularExpression::UnanchoredWildcardConversion); if(!re.isValid()) return false; } m_categories[a_index.row()].maskList = maskList; return true; } return false; } // END OF bool DropFileCategoryModel::setData(const QModelIndex & a_index, // const QVariant & a_value, int a_role) //============================================================================== std::vector DropFileCategoryModel::getCategories() const { return m_categories; } // END OF std::vector DropFileCategoryModel::getCategories() // const //============================================================================== void DropFileCategoryModel::setCategories( const std::vector & a_categories) { m_categories = a_categories; emit layoutChanged(); } // END OF void DropFileCategoryModel::setCategories( // const std::vector & a_categories) //============================================================================== void DropFileCategoryModel::addCategory() { beginInsertRows(QModelIndex(), (int)m_categories.size(), (int)m_categories.size()); m_categories.emplace_back(); endInsertRows(); } // END OF void DropFileCategoryModel::addCategory() //============================================================================== void DropFileCategoryModel::deleteCategory(int a_index) { if(a_index >= (int)m_categories.size() || a_index < 0) return; beginRemoveRows(QModelIndex(), a_index, a_index); m_categories.erase(m_categories.begin() + a_index); endRemoveRows(); } // END OF void DropFileCategoryModel::deleteCategory(int a_index) //============================================================================== QString DropFileCategoryModel::sourceTemplate(int a_index) const { if(a_index >= (int)m_categories.size() || a_index < 0) return QString(); return m_categories[a_index].sourceTemplate; } // END OF QString DropFileCategoryModel::sourceTemplate(int a_index) const //============================================================================== void DropFileCategoryModel::setSourceTemplate(int a_index, const QString & a_text) { if(a_index >= (int)m_categories.size() || a_index < 0) return; m_categories[a_index].sourceTemplate = a_text; } // END OF void DropFileCategoryModel::setSourceTemplate(int a_index, // const QString & a_text) //============================================================================== ================================================ FILE: vsedit/src/script_templates/drop_file_category_model.h ================================================ #ifndef DROP_FILE_CATEGORY_MODEL_H_INCLUDED #define DROP_FILE_CATEGORY_MODEL_H_INCLUDED #include "../../../common-src/settings/settings_definitions.h" #include class DropFileCategoryModel : public QAbstractItemModel { Q_OBJECT public: DropFileCategoryModel(QObject * a_pParent = nullptr); virtual ~DropFileCategoryModel(); virtual QModelIndex index(int a_row, int a_column, const QModelIndex & a_parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex & a_child) const override; virtual Qt::ItemFlags flags(const QModelIndex & a_index) const override; virtual QVariant data(const QModelIndex & a_index, int a_role = Qt::DisplayRole) const override; virtual QVariant headerData(int a_section, Qt::Orientation a_orientation, int a_role = Qt::DisplayRole) const override; virtual int rowCount(const QModelIndex & a_parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex & a_parent = QModelIndex()) const override; virtual bool setData(const QModelIndex & a_index, const QVariant & a_value, int a_role = Qt::EditRole) override; std::vector getCategories() const; void setCategories(const std::vector & a_categories); void addCategory(); void deleteCategory(int a_index); QString sourceTemplate(int a_index) const; void setSourceTemplate(int a_index, const QString & a_text); private: std::vector m_categories; }; #endif // DROP_FILE_CATEGORY_MODEL_H_INCLUDED ================================================ FILE: vsedit/src/script_templates/templates_dialog.cpp ================================================ #include "templates_dialog.h" #include "drop_file_category_model.h" #include "../../../common-src/settings/settings_manager.h" #include #include //============================================================================== TemplatesDialog::TemplatesDialog(SettingsManager * a_pSettingsManager, QWidget * a_pParent, Qt::WindowFlags a_flags) : QDialog(a_pParent, a_flags) , m_pSettingsManager(a_pSettingsManager) , m_pDropFileCategoryModel(nullptr) , m_pSaveAction(nullptr) { Q_ASSERT(m_pSettingsManager); vsedit::disableFontKerning(this); m_ui.setupUi(this); m_scriptEditors = {m_ui.snippetEdit, m_ui.newScriptTemplateEdit, m_ui.dropFileCategoryTemplateEdit}; for(ScriptEditor * pEdit : m_scriptEditors) pEdit->setSettingsManager(m_pSettingsManager); m_pDropFileCategoryModel = new DropFileCategoryModel(this); m_ui.dropFileCategoryView->setModel(m_pDropFileCategoryModel); QString fileDropTemplatesInfo = tr("Write file name mask list " "as a list of wildcards, separated by semicolons without spaces.\n" "In the template below use tokens: "); QStringList tokenInfoList; std::vector variables = m_ui.dropFileCategoryTemplateEdit->variables(); for(const vsedit::VariableToken & variable : variables) tokenInfoList += QString("%1 - %2").arg(variable.token) .arg(variable.description); fileDropTemplatesInfo += tokenInfoList.join("; "); fileDropTemplatesInfo += tr("."); m_ui.fileDropTemplatesInfoLabel->setText(fileDropTemplatesInfo); m_pSaveAction = m_pSettingsManager->createStandardAction( ACTION_ID_SAVE_SCRIPT, this); addAction(m_pSaveAction); connect(m_pSaveAction, SIGNAL(triggered()), this, SLOT(slotSaveActionTriggered())); connect(m_ui.snippetPasteIntoScriptButton, SIGNAL(clicked()), this, SLOT(slotSnippetPasteIntoScriptButtonClicked())); connect(m_ui.snippetSaveButton, SIGNAL(clicked()), this, SLOT(slotSnippetSaveButtonClicked())); connect(m_ui.snippetDeleteButton, SIGNAL(clicked()), this, SLOT(slotSnippetDeleteButtonClicked())); connect(m_ui.snippetNameComboBox, SIGNAL(textActivated(const QString &)), this, SLOT(slotSnippetNameComboBoxActivated(const QString &))); connect(m_ui.newScriptTemplateRevertButton, SIGNAL(clicked()), this, SLOT(slotNewScriptTemplateRevertButtonClicked())); connect(m_ui.newScriptTemplateLoadDefaultButton, SIGNAL(clicked()), this, SLOT(slotNewScriptTemplateLoadDefaultButtonClicked())); connect(m_ui.newScriptTemplateSaveButton, SIGNAL(clicked()), this, SLOT(slotNewScriptTemplateSaveButtonClicked())); connect(m_ui.saveDropFileCategoriesButton, SIGNAL(clicked()), this, SLOT(slotSaveDropFileCategoriesButtonClicked())); connect(m_ui.revertDropFileCategoriesButton, SIGNAL(clicked()), this, SLOT(slotRevertDropFileCategoriesButtonClicked())); connect(m_ui.addDropFileCategoryButton, SIGNAL(clicked()), this, SLOT(slotAddDropFileCategoryButtonClicked())); connect(m_ui.deleteSelectedDropFileCategoryButton, SIGNAL(clicked()), this, SLOT(slotDeleteSelectedDropFileCategoryButtonClicked())); connect(m_ui.dropFileCategoryView, SIGNAL(pressed(const QModelIndex &)), this, SLOT(slotDropFileCategoryViewPressed(const QModelIndex &))); connect(m_ui.dropFileCategoryTemplateEdit, SIGNAL(textChanged()), this, SLOT(slotUpdateDropFileCategories())); slotLoadSettings(); } // END OF TemplatesDialog::TemplatesDialog(SettingsManager * a_pSettingsManager, // QWidget * a_pParent, Qt::WindowFlags a_flags) //============================================================================== TemplatesDialog::~TemplatesDialog() { } // END OF TemplatesDialog::~TemplatesDialog() //============================================================================== void TemplatesDialog::setPluginsList(const VSPluginsList & a_pluginsList) { for(ScriptEditor * pEdit : m_scriptEditors) pEdit->setPluginsList(a_pluginsList); } // END OF void TemplatesDialog::setPluginsList( // const VSPluginsList & a_pluginsList) //============================================================================== void TemplatesDialog::call() { slotLoadSettings(); show(); } // END OF void TemplatesDialog::call() //============================================================================== void TemplatesDialog::slotLoadSettings() { for(ScriptEditor * pEdit : m_scriptEditors) pEdit->slotLoadSettings(); m_pSaveAction->setShortcut(m_pSettingsManager->getHotkey( m_pSaveAction->data().toString())); m_ui.snippetNameComboBox->clear(); m_codeSnippets = m_pSettingsManager->getAllCodeSnippets(); for(const CodeSnippet & snippet : m_codeSnippets) m_ui.snippetNameComboBox->addItem(snippet.name); m_ui.snippetNameComboBox->setCurrentIndex(0); slotSnippetNameComboBoxActivated(m_ui.snippetNameComboBox->currentText()); QString newScriptTemplate = m_pSettingsManager->getNewScriptTemplate(); m_ui.newScriptTemplateEdit->setPlainText(newScriptTemplate); slotRevertDropFileCategoriesButtonClicked(); } // END OF void TemplatesDialog::slotLoadSettings() //============================================================================== void TemplatesDialog::slotSnippetPasteIntoScriptButtonClicked() { QString text = m_ui.snippetEdit->text(); if(!text.isEmpty()) emit signalPasteCodeSnippet(text); } // END OF void TemplatesDialog::slotSnippetPasteIntoScriptButtonClicked() //============================================================================== void TemplatesDialog::slotSnippetSaveButtonClicked() { CodeSnippet snippet(m_ui.snippetNameComboBox->currentText(), m_ui.snippetEdit->text()); bool success = m_pSettingsManager->saveCodeSnippet(snippet); if(!success) return; std::vector::iterator it = std::find( m_codeSnippets.begin(), m_codeSnippets.end(), snippet); if(it == m_codeSnippets.end()) { Q_ASSERT(m_ui.snippetNameComboBox->findText(snippet.name) == -1); m_codeSnippets.push_back(snippet); m_ui.snippetNameComboBox->addItem(snippet.name); m_ui.snippetNameComboBox->model()->sort(0); } else { Q_ASSERT(m_ui.snippetNameComboBox->findText(snippet.name) != -1); *it = snippet; } } // END OF void TemplatesDialog::slotSnippetSaveButtonClicked() //============================================================================== void TemplatesDialog::slotSnippetDeleteButtonClicked() { CodeSnippet snippet(m_ui.snippetNameComboBox->currentText()); if(snippet.name.isEmpty()) return; QMessageBox quesBox(this); vsedit::disableFontKerning(&quesBox); quesBox.setWindowTitle(tr("Delete snippet")); quesBox.setText(tr("Do you really want to delete " "snippet \'%1\'?").arg(snippet.name)); quesBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); quesBox.setDefaultButton(QMessageBox::No); int result = quesBox.exec(); if(result == QMessageBox::No) return; std::vector::iterator it = std::find( m_codeSnippets.begin(), m_codeSnippets.end(), snippet); if(it == m_codeSnippets.end()) { Q_ASSERT(m_ui.snippetNameComboBox->findText(snippet.name) == -1); return; } int index = m_ui.snippetNameComboBox->findText(snippet.name); Q_ASSERT(index != -1); m_ui.snippetNameComboBox->removeItem(index); m_codeSnippets.erase(it); m_ui.snippetNameComboBox->setCurrentIndex(0); slotSnippetNameComboBoxActivated(m_ui.snippetNameComboBox->currentText()); m_pSettingsManager->deleteCodeSnippet(snippet.name); } // END OF void TemplatesDialog::slotSnippetDeleteButtonClicked() //============================================================================== void TemplatesDialog::slotSnippetNameComboBoxActivated(const QString & a_text) { if(a_text.isEmpty()) { m_ui.snippetEdit->clear(); return; } CodeSnippet snippet(a_text); std::vector::iterator it = std::find( m_codeSnippets.begin(), m_codeSnippets.end(), snippet); if(it == m_codeSnippets.end()) return; snippet = *it; m_ui.snippetEdit->setPlainText(snippet.text); } // END OF void TemplatesDialog::slotSnippetNameComboBoxActivated( // const QString & a_text) //============================================================================== void TemplatesDialog::slotNewScriptTemplateRevertButtonClicked() { QString newScriptTemplate = m_pSettingsManager->getNewScriptTemplate(); m_ui.newScriptTemplateEdit->setPlainText(newScriptTemplate); } // END OF void TemplatesDialog::slotNewScriptTemplateRevertButtonClicked() //============================================================================== void TemplatesDialog::slotNewScriptTemplateLoadDefaultButtonClicked() { QString defaultNewScriptTemplate = m_pSettingsManager->getDefaultNewScriptTemplate(); m_ui.newScriptTemplateEdit->setPlainText(defaultNewScriptTemplate); } // END OF void TemplatesDialog::slotNewScriptTemplateLoadDefaultButtonClicked() //============================================================================== void TemplatesDialog::slotNewScriptTemplateSaveButtonClicked() { QString newScriptTemplate = m_ui.newScriptTemplateEdit->toPlainText(); m_pSettingsManager->setNewScriptTemplate(newScriptTemplate); } // END OF void TemplatesDialog::slotNewScriptTemplateSaveButtonClicked() //============================================================================== void TemplatesDialog::slotSaveDropFileCategoriesButtonClicked() { int index = m_ui.dropFileCategoryView->currentIndex().row(); m_pDropFileCategoryModel->setSourceTemplate(index, m_ui.dropFileCategoryTemplateEdit->text()); std::vector categories = m_pDropFileCategoryModel->getCategories(); m_pSettingsManager->setDropFileTemplates(categories); } // END OF void TemplatesDialog::slotSaveDropFileCategoriesButtonClicked() //============================================================================== void TemplatesDialog::slotRevertDropFileCategoriesButtonClicked() { std::vector categories = m_pSettingsManager->getAllDropFileTemplates(); m_pDropFileCategoryModel->setCategories(categories); m_ui.dropFileCategoryView->resizeColumnToContents(0); m_ui.dropFileCategoryView->setCurrentIndex( m_pDropFileCategoryModel->index(0, 0)); slotDisplayCurrentDropFileCategoryTemplate(); } // END OF void TemplatesDialog::slotRevertDropFileCategoriesButtonClicked() //============================================================================== void TemplatesDialog::slotAddDropFileCategoryButtonClicked() { m_pDropFileCategoryModel->addCategory(); QModelIndex index = m_pDropFileCategoryModel->index( m_pDropFileCategoryModel->rowCount() - 1, 0); m_ui.dropFileCategoryView->setCurrentIndex(index); slotDisplayCurrentDropFileCategoryTemplate(); m_ui.dropFileCategoryView->edit(index); } // END OF void TemplatesDialog::slotAddDropFileCategoryButtonClicked() //============================================================================== void TemplatesDialog::slotDeleteSelectedDropFileCategoryButtonClicked() { QModelIndex index = m_ui.dropFileCategoryView->currentIndex(); if(!index.isValid()) return; int row = index.row(); std::vector categories = m_pDropFileCategoryModel->getCategories(); if(categories.empty()) return; DropFileCategory category = categories[row]; QMessageBox quesBox(this); vsedit::disableFontKerning(&quesBox); quesBox.setWindowTitle(tr("Delete category")); quesBox.setText(tr("Do you really want to delete " "category \'%1\'?").arg(category.name)); quesBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); quesBox.setDefaultButton(QMessageBox::No); int result = quesBox.exec(); if(result == QMessageBox::No) return; m_pDropFileCategoryModel->deleteCategory(row); slotDisplayCurrentDropFileCategoryTemplate(); } // END OF void TemplatesDialog:: // slotDeleteSelectedDropFileCategoryButtonClicked() //============================================================================== void TemplatesDialog::slotDropFileCategoryViewPressed( const QModelIndex & a_index) { (void)a_index; slotDisplayCurrentDropFileCategoryTemplate(); } // END OF void TemplatesDialog::slotDropFileCategoryViewPressed( // const QModelIndex & a_index) //============================================================================== void TemplatesDialog::slotDisplayCurrentDropFileCategoryTemplate() { QModelIndex index = m_ui.dropFileCategoryView->currentIndex(); if(!index.isValid()) return; QString sourceTemplate = m_pDropFileCategoryModel->sourceTemplate(index.row()); m_ui.dropFileCategoryTemplateEdit->setPlainText(sourceTemplate); } // END OF void TemplatesDialog::slotDisplayCurrentDropFileCategoryTemplate() //============================================================================== void TemplatesDialog::slotUpdateDropFileCategories() { QModelIndex index = m_ui.dropFileCategoryView->currentIndex(); if(!index.isValid()) return; QString sourceTemplate = m_ui.dropFileCategoryTemplateEdit->text(); m_pDropFileCategoryModel->setSourceTemplate(index.row(), sourceTemplate); } // END OF void TemplatesDialog::slotUpdateDropFileCategories() //============================================================================== void TemplatesDialog::slotSaveActionTriggered() { QWidget * pCurrentWidget = m_ui.templatesTabWidget->currentWidget(); if(pCurrentWidget == m_ui.codeSnippetsTab) slotSnippetSaveButtonClicked(); else if(pCurrentWidget == m_ui.newScriptTemplateTab) slotNewScriptTemplateSaveButtonClicked(); else if(pCurrentWidget == m_ui.fileDropTemplatesTab) slotSaveDropFileCategoriesButtonClicked(); } // END OF void TemplatesDialog::slotSaveActionTriggered() //============================================================================== ================================================ FILE: vsedit/src/script_templates/templates_dialog.h ================================================ #ifndef TEMPLATES_DIALOG_H_INCLUDED #define TEMPLATES_DIALOG_H_INCLUDED #include #include "../../../common-src/settings/settings_definitions.h" #include class DropFileCategoryModel; class SettingsManager; class QAction; class TemplatesDialog : public QDialog { Q_OBJECT public: TemplatesDialog(SettingsManager * a_pSettingsManager, QWidget * a_pParent = nullptr, Qt::WindowFlags a_flags = Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); virtual ~TemplatesDialog(); void setPluginsList(const VSPluginsList & a_pluginsList); public slots: void call(); void slotLoadSettings(); signals: void signalPasteCodeSnippet(const QString & a_text); private slots: void slotSnippetPasteIntoScriptButtonClicked(); void slotSnippetSaveButtonClicked(); void slotSnippetDeleteButtonClicked(); void slotSnippetNameComboBoxActivated(const QString & a_text); void slotNewScriptTemplateRevertButtonClicked(); void slotNewScriptTemplateLoadDefaultButtonClicked(); void slotNewScriptTemplateSaveButtonClicked(); void slotSaveDropFileCategoriesButtonClicked(); void slotRevertDropFileCategoriesButtonClicked(); void slotAddDropFileCategoryButtonClicked(); void slotDeleteSelectedDropFileCategoryButtonClicked(); void slotDropFileCategoryViewPressed(const QModelIndex & a_index); void slotDisplayCurrentDropFileCategoryTemplate(); void slotUpdateDropFileCategories(); void slotSaveActionTriggered(); private: Ui::TemplatesDialog m_ui; SettingsManager * m_pSettingsManager; std::vector m_codeSnippets; DropFileCategoryModel * m_pDropFileCategoryModel; QAction * m_pSaveAction; std::vector m_scriptEditors; }; #endif // TEMPLATES_DIALOG_H_INCLUDED ================================================ FILE: vsedit/src/script_templates/templates_dialog.ui ================================================ TemplatesDialog 0 0 770 578 Script templates 0 0 0 0 0 0 Code snippets 4 0 0 0 0 4 0 0 true QComboBox::NoInsert Paste into script true Save Delete New script template 4 0 0 0 0 4 Qt::Horizontal 318 20 Revert Load default Save File drop templates 4 0 0 0 0 4 true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows Qt::ElideNone QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel false true false 4 Save all Revert Add Delete Qt::Vertical 20 40 Write file name mask list as a list of wildcards, separated by semicolons without spaces. In the template below use tokens: true ScriptEditor QPlainTextEdit
../../src/script_editor/script_editor.h
================================================ FILE: vsedit/src/settings/actions_hotkey_edit_model.cpp ================================================ #include "actions_hotkey_edit_model.h" //============================================================================== const int COLUMNS_NUMBER = 3; const int ICON_COLUMN = 0; const int TITLE_COLUMN = 1; const int HOTKEY_COLUMN = 2; //============================================================================== ActionsHotkeyEditModel::ActionsHotkeyEditModel( SettingsManager * a_pSettingsManager, QObject * a_pParent) : QAbstractItemModel(a_pParent) , m_pSettingsManager(a_pSettingsManager) { Q_ASSERT(m_pSettingsManager); m_actions = m_pSettingsManager->getStandardActions(); reloadHotkeysSettings(); } // END OF ActionsHotkeyEditModel::ActionsHotkeyEditModel( // SettingsManager * a_pSettingsManager, QObject * a_pParent) //============================================================================== ActionsHotkeyEditModel::~ActionsHotkeyEditModel() { } // END OF ActionsHotkeyEditModel::~ActionsHotkeyEditModel() //============================================================================== QModelIndex ActionsHotkeyEditModel::index(int a_row, int a_column, const QModelIndex & a_parent) const { (void)a_parent; return createIndex(a_row, a_column); } // END OF QModelIndex ActionsHotkeyEditModel::index(int a_row, int a_column, // const QModelIndex & a_parent) const //============================================================================== QModelIndex ActionsHotkeyEditModel::parent(const QModelIndex & a_child) const { (void)a_child; return QModelIndex(); } // END OF QModelIndex ActionsHotkeyEditModel::parent( // const QModelIndex & a_child) const //============================================================================== Qt::ItemFlags ActionsHotkeyEditModel::flags(const QModelIndex & a_index) const { if (!a_index.isValid()) { return Qt::NoItemFlags; } Qt::ItemFlags cellFlags = Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable ; if(a_index.column() == HOTKEY_COLUMN) cellFlags |= Qt::ItemIsEditable; return cellFlags; } // END OF Qt::ItemFlags ActionsHotkeyEditModel::flags( // const QModelIndex & a_index) const //============================================================================== QVariant ActionsHotkeyEditModel::data(const QModelIndex & a_index, int a_role) const { if(!a_index.isValid()) return QVariant(); if((a_index.row() >= (int)m_actions.size()) || (a_index.column() >= COLUMNS_NUMBER)) return QVariant(); if((a_index.column() == ICON_COLUMN) && (a_role == Qt::DecorationRole)) return m_actions[a_index.row()].icon; else if((a_index.column() == TITLE_COLUMN) && ((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole))) return m_actions[a_index.row()].title; else if((a_index.column() == HOTKEY_COLUMN) && ((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole) || (a_role == Qt::EditRole))) return m_actions[a_index.row()].hotkey; return QVariant(); } // END OF QVariant ActionsHotkeyEditModel::data(const QModelIndex & a_index, // int a_role) const //============================================================================== int ActionsHotkeyEditModel::rowCount(const QModelIndex & a_parent) const { (void)a_parent; return (int)m_actions.size(); } // END OF int ActionsHotkeyEditModel::rowCount(const QModelIndex & a_parent) // const //============================================================================== int ActionsHotkeyEditModel::columnCount(const QModelIndex & a_parent) const { (void)a_parent; return COLUMNS_NUMBER; } // END OF int ActionsHotkeyEditModel::columnCount(const QModelIndex & a_parent) // const //============================================================================== bool ActionsHotkeyEditModel::setData(const QModelIndex & a_index, const QVariant & a_value, int a_role) { if((a_index.column() != HOTKEY_COLUMN) || (a_role != Qt::EditRole)) return false; m_actions[a_index.row()].hotkey = a_value.value(); return true; } // END OF bool ActionsHotkeyEditModel::setData(const QModelIndex & a_index, // const QVariant & a_value, int a_role) //============================================================================== void ActionsHotkeyEditModel::reloadHotkeysSettings() { for(StandardAction & action : m_actions) action.hotkey = m_pSettingsManager->getHotkey(action.id); emit dataChanged(createIndex(HOTKEY_COLUMN, 0), createIndex(HOTKEY_COLUMN, (int)m_actions.size() - 1)); } // END OF void ActionsHotkeyEditModel::reloadHotkeysSettings() //============================================================================== void ActionsHotkeyEditModel::slotSaveActionsHotkeys() { for(const StandardAction & action : m_actions) m_pSettingsManager->setHotkey(action.id, action.hotkey); } // END OF void ActionsHotkeyEditModel::slotSaveActionsHotkeys() //============================================================================== ================================================ FILE: vsedit/src/settings/actions_hotkey_edit_model.h ================================================ #ifndef ACTIONSHOTKEYEDITMODEL_H #define ACTIONSHOTKEYEDITMODEL_H #include "../../../common-src/settings/settings_manager.h" #include class ActionsHotkeyEditModel : public QAbstractItemModel { Q_OBJECT public: ActionsHotkeyEditModel(SettingsManager * a_pSettingsManager, QObject * a_pParent = nullptr); virtual ~ActionsHotkeyEditModel(); QModelIndex index(int a_row, int a_column, const QModelIndex & a_parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex & a_child) const override; Qt::ItemFlags flags(const QModelIndex & a_index) const override; QVariant data(const QModelIndex & a_index, int a_role = Qt::DisplayRole) const override; int rowCount(const QModelIndex & a_parent = QModelIndex()) const override; int columnCount(const QModelIndex & a_parent = QModelIndex()) const override; bool setData(const QModelIndex & a_index, const QVariant & a_value, int a_role = Qt::EditRole) override; void reloadHotkeysSettings(); public slots: void slotSaveActionsHotkeys(); private: std::vector m_actions; SettingsManager * m_pSettingsManager; }; #endif // ACTIONSHOTKEYEDITMODEL_H ================================================ FILE: vsedit/src/settings/clearable_key_sequence_editor.cpp ================================================ #include "clearable_key_sequence_editor.h" #include #include #include //============================================================================== ClearableKeySequenceEditor::ClearableKeySequenceEditor(QWidget * a_pParent): QWidget(a_pParent) , m_pKeySequenceEdit(nullptr) , m_pClearKeySequenceButton(nullptr) { QHBoxLayout * pLayout = new QHBoxLayout(this); pLayout->setContentsMargins(0, 0, 0, 0); pLayout->setSpacing(0); setLayout(pLayout); m_pKeySequenceEdit = new QKeySequenceEdit(this); pLayout->addWidget(m_pKeySequenceEdit); connect(m_pKeySequenceEdit, SIGNAL(editingFinished()), this, SIGNAL(editingFinished())); m_pClearKeySequenceButton = new QToolButton(this); m_pClearKeySequenceButton->setToolTip(tr("Erase hotkey")); m_pClearKeySequenceButton->setIcon(QIcon(":erase.png")); pLayout->addWidget(m_pClearKeySequenceButton); connect(m_pClearKeySequenceButton, SIGNAL(clicked()), m_pKeySequenceEdit, SLOT(clear())); } //============================================================================== ClearableKeySequenceEditor::~ClearableKeySequenceEditor() { } //============================================================================== QKeySequence ClearableKeySequenceEditor::keySequence() const { return m_pKeySequenceEdit->keySequence(); } //============================================================================== void ClearableKeySequenceEditor::slotSetKeySequence( const QKeySequence & a_keySequence) { m_pKeySequenceEdit->setKeySequence(a_keySequence); } //============================================================================== ================================================ FILE: vsedit/src/settings/clearable_key_sequence_editor.h ================================================ #ifndef CLEARABLE_KEY_SEQUENCE_EDITOR_H_INCLUDED #define CLEARABLE_KEY_SEQUENCE_EDITOR_H_INCLUDED #include #include class QKeySequenceEdit; class QToolButton; class ClearableKeySequenceEditor : public QWidget { Q_OBJECT public: ClearableKeySequenceEditor(QWidget * a_pParent); virtual ~ClearableKeySequenceEditor(); QKeySequence keySequence() const; public slots: void slotSetKeySequence(const QKeySequence & a_keySequence); signals: void editingFinished(); private: QKeySequenceEdit * m_pKeySequenceEdit; QToolButton * m_pClearKeySequenceButton; }; #endif // CLEARABLE_KEY_SEQUENCE_EDITOR_H_INCLUDED ================================================ FILE: vsedit/src/settings/item_delegate_for_hotkey.cpp ================================================ #include "item_delegate_for_hotkey.h" #include "clearable_key_sequence_editor.h" //============================================================================== ItemDelegateForHotkey::ItemDelegateForHotkey(QObject * a_pParent): QStyledItemDelegate(a_pParent) { } //============================================================================== ItemDelegateForHotkey::~ItemDelegateForHotkey() { } //============================================================================== QWidget * ItemDelegateForHotkey::createEditor(QWidget * a_pParent, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const { (void)a_option; (void)a_index; ClearableKeySequenceEditor * pEditor = new ClearableKeySequenceEditor(a_pParent); connect(pEditor, SIGNAL(editingFinished()), this, SLOT(slotCommitAndCloseEditor())); return pEditor; } //============================================================================== void ItemDelegateForHotkey::setEditorData(QWidget * a_pEditor, const QModelIndex & a_index) const { ClearableKeySequenceEditor * pEditor = static_cast(a_pEditor); QVariant newValue = a_index.model()->data(a_index, Qt::EditRole); pEditor->slotSetKeySequence(newValue.value()); } //============================================================================== void ItemDelegateForHotkey::updateEditorGeometry(QWidget * a_pEditor, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const { (void)a_option; (void)a_index; a_pEditor->setGeometry(a_option.rect); return; } //============================================================================== void ItemDelegateForHotkey::setModelData(QWidget * a_pEditor, QAbstractItemModel * a_model, const QModelIndex & a_index) const { ClearableKeySequenceEditor * pEditor = static_cast(a_pEditor); a_model->setData(a_index, pEditor->keySequence(), Qt::EditRole); return; } //============================================================================== void ItemDelegateForHotkey::slotCommitAndCloseEditor() { ClearableKeySequenceEditor * pEditor = qobject_cast(sender()); emit commitData(pEditor); emit closeEditor(pEditor); } //============================================================================== ================================================ FILE: vsedit/src/settings/item_delegate_for_hotkey.h ================================================ #ifndef ITEMDELEGATEFORHOTKEY_H #define ITEMDELEGATEFORHOTKEY_H #include class ItemDelegateForHotkey : public QStyledItemDelegate { Q_OBJECT public: ItemDelegateForHotkey(QObject * a_pParent = nullptr); virtual ~ItemDelegateForHotkey(); QWidget * createEditor(QWidget * a_pParent, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const override; void setEditorData(QWidget * a_pEditor, const QModelIndex & a_index) const override; void updateEditorGeometry(QWidget * a_pEditor, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const override; void setModelData(QWidget * a_pEditor, QAbstractItemModel * a_model, const QModelIndex & a_index) const override; private slots: void slotCommitAndCloseEditor(); }; #endif // ITEMDELEGATEFORHOTKEY_H ================================================ FILE: vsedit/src/settings/settings_dialog.cpp ================================================ #include "settings_dialog.h" #include "../../../common-src/settings/settings_manager.h" #include "../../../common-src/helpers.h" #include "item_delegate_for_hotkey.h" #include "theme_elements_model.h" #include #include #include #include //============================================================================== SettingsDialog::SettingsDialog(SettingsManager * a_pSettingsManager, QWidget * a_pParent) : QDialog(a_pParent, Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint) , m_pSettingsManager(a_pSettingsManager) , m_pActionsHotkeyEditModel(nullptr) , m_pItemDelegateForHotkey(nullptr) , m_pThemeElementsModel(nullptr) { vsedit::disableFontKerning(this); m_ui.setupUi(this); vsedit::disableFontKerning(m_ui.hotkeysTable); vsedit::disableFontKerning(m_ui.themeElementsList); setWindowIcon(QIcon(":settings.png")); m_ui.addVSLibraryPathButton->setIcon(QIcon(":folder_add.png")); m_ui.removeVSLibraryPathButton->setIcon(QIcon(":folder_remove.png")); m_ui.selectVSLibraryPathButton->setIcon(QIcon(":folder.png")); m_pActionsHotkeyEditModel = new ActionsHotkeyEditModel(m_pSettingsManager, this); m_ui.hotkeysTable->setModel(m_pActionsHotkeyEditModel); m_pItemDelegateForHotkey = new ItemDelegateForHotkey(this); m_ui.hotkeysTable->setItemDelegateForColumn(2, m_pItemDelegateForHotkey); m_ui.hotkeysTable->resizeColumnsToContents(); m_pThemeElementsModel = new ThemeElementsModel(m_pSettingsManager, this); m_ui.themeElementsList->setModel(m_pThemeElementsModel); addThemeElements(); connect(m_ui.okButton, SIGNAL(clicked()), this, SLOT(slotOk())); connect(m_ui.applyButton, SIGNAL(clicked()), this, SLOT(slotApply())); connect(m_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_ui.addVSLibraryPathButton, SIGNAL(clicked()), this, SLOT(slotAddVSLibraryPath())); connect(m_ui.removeVSLibraryPathButton, SIGNAL(clicked()), this, SLOT(slotRemoveVSLibraryPath())); connect(m_ui.selectVSLibraryPathButton, SIGNAL(clicked()), this, SLOT(slotSelectVSLibraryPath())); connect(m_ui.themeElementsList, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotThemeElementSelected(const QModelIndex &))); connect(m_ui.fontButton, SIGNAL(clicked()), this, SLOT(slotFontButtonClicked())); connect(m_ui.colourButton, SIGNAL(clicked()), this, SLOT(slotColourButtonClicked())); } // END OF SettingsDialog::SettingsDialog(SettingsManager * a_pSettingsManager, // QWidget * a_pParent) //============================================================================== SettingsDialog::~SettingsDialog() { } // END OF SettingsDialog::~SettingsDialog() //============================================================================== void SettingsDialog::slotCall() { m_ui.autoLoadLastScriptCheckBox->setChecked( m_pSettingsManager->getAutoLoadLastScript()); m_ui.reloadFromDiskCheckBox->setChecked( m_pSettingsManager->getReloadScriptFromDisk()); m_ui.promptToSaveChangesCheckBox->setChecked( m_pSettingsManager->getPromptToSaveChanges()); m_ui.portableModeCheckBox->setChecked( m_pSettingsManager->getPortableMode()); m_ui.maxRecentFilesSpinBox->setValue( m_pSettingsManager->getMaxRecentFilesNumber()); m_ui.charactersTypedToStartCompletionSpinBox->setValue( m_pSettingsManager->getCharactersTypedToStartCompletion()); m_ui.useSpacesAsTabCheckBox->setChecked( m_pSettingsManager->getUseSpacesAsTab()); m_ui.spacesInTabSpinBox->setValue( m_pSettingsManager->getSpacesInTab()); m_ui.highlightSelectionMatchesCheckBox->setChecked( m_pSettingsManager->getHighlightSelectionMatches()); m_ui.highlightSelectionMatchesMinLengthSpinBox->setValue( m_pSettingsManager->getHighlightSelectionMatchesMinLength()); m_ui.rememberLastPreviewFrameCheckBox->setChecked( m_pSettingsManager->getRememberLastPreviewFrame()); m_ui.alwaysKeepCurrentFrameCheckBox->setChecked( m_pSettingsManager->getAlwaysKeepCurrentFrame()); m_ui.debugMsgCheckBox->setChecked( m_pSettingsManager->getShowDebugMessages()); m_ui.snapshotCompressionLevelSpinBox->setValue( m_pSettingsManager->getPNGSnapshotCompressionLevel()); m_ui.preferLibraryFromListCheckBox->setChecked( m_pSettingsManager->getPreferVSLibrariesFromList()); m_ui.darkModeCheckBox->setChecked(m_pSettingsManager->getDarkMode()); m_ui.vsLibraryPathsListWidget->clear(); m_ui.vsLibraryPathsListWidget->addItems( m_pSettingsManager->getVapourSynthLibraryPaths()); m_ui.vsLibraryPathEdit->clear(); m_ui.settingsTabWidget->setCurrentIndex(0); m_pActionsHotkeyEditModel->reloadHotkeysSettings(); m_pThemeElementsModel->reloadThemeSettings(); QModelIndex firstElement = m_pActionsHotkeyEditModel->index(0, 0); m_ui.themeElementsList->setCurrentIndex(firstElement); show(); } // END OF void SettingsDialog::slotCall() //============================================================================== void SettingsDialog::addThemeElements() { m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_COMMON_SCRIPT_TEXT, tr("Common script text")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_KEYWORD, tr("Keyword")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_OPERATOR, tr("Operator")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_STRING, tr("String")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_NUMBER, tr("Number")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_COMMENT, tr("Comment")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_VS_CORE, tr("VapourSynth core")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_VS_NAMESPACE, tr("VapourSynth namespace")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_VS_FUNCTION, tr("VapourSynth function")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_VS_ARGUMENT, tr("VapourSynth argument")); m_pThemeElementsModel->addTextCharFormat(TEXT_FORMAT_ID_TIMELINE, tr("Timeline labels")); m_pThemeElementsModel->addColor(COLOR_ID_TEXT_BACKGROUND, tr("Text background color")); m_pThemeElementsModel->addColor(COLOR_ID_ACTIVE_LINE, tr("Active line color")); m_pThemeElementsModel->addColor(COLOR_ID_SELECTION_MATCHES, tr("Selection matches color")); m_pThemeElementsModel->addColor(COLOR_ID_TIMELINE_BOOKMARKS, tr("Timeline bookmarks color")); } // END OF void SettingsDialog::addThemeElements() //============================================================================== void SettingsDialog::slotOk() { slotApply(); accept(); } // END OF void SettingsDialog::slotOk() //============================================================================== void SettingsDialog::slotApply() { m_pSettingsManager->setAutoLoadLastScript( m_ui.autoLoadLastScriptCheckBox->isChecked()); m_pSettingsManager->setReloadScriptFromDisk( m_ui.reloadFromDiskCheckBox->isChecked()); m_pSettingsManager->setPromptToSaveChanges( m_ui.promptToSaveChangesCheckBox->isChecked()); m_pSettingsManager->setPortableMode( m_ui.portableModeCheckBox->isChecked()); m_pSettingsManager->setMaxRecentFilesNumber( m_ui.maxRecentFilesSpinBox->value()); m_pSettingsManager->setCharactersTypedToStartCompletion( m_ui.charactersTypedToStartCompletionSpinBox->value()); m_pSettingsManager->setUseSpacesAsTab( m_ui.useSpacesAsTabCheckBox->isChecked()); m_pSettingsManager->setSpacesInTab( m_ui.spacesInTabSpinBox->value()); m_pSettingsManager->setHighlightSelectionMatches( m_ui.highlightSelectionMatchesCheckBox->isChecked()); m_pSettingsManager->setHighlightSelectionMatchesMinLength( m_ui.highlightSelectionMatchesMinLengthSpinBox->value()); m_pSettingsManager->setRememberLastPreviewFrame( m_ui.rememberLastPreviewFrameCheckBox->isChecked()); m_pSettingsManager->setAlwaysKeepCurrentFrame( m_ui.alwaysKeepCurrentFrameCheckBox->isChecked()); m_pSettingsManager->setShowDebugMessages( m_ui.debugMsgCheckBox->isChecked()); m_pSettingsManager->setPNGSnapshotCompressionLevel( m_ui.snapshotCompressionLevelSpinBox->value()); m_pSettingsManager->setPreferVSLibrariesFromList( m_ui.preferLibraryFromListCheckBox->isChecked()); QStringList vapourSynthLibraryPaths; int vsLibraryPathsNumber = m_ui.vsLibraryPathsListWidget->count(); for(int i = 0; i < vsLibraryPathsNumber; ++i) { QString path = m_ui.vsLibraryPathsListWidget->item(i)->text(); vapourSynthLibraryPaths.append(path); } vapourSynthLibraryPaths.removeDuplicates(); m_pSettingsManager->setVapourSynthLibraryPaths(vapourSynthLibraryPaths); m_pActionsHotkeyEditModel->slotSaveActionsHotkeys(); m_pThemeElementsModel->slotSaveThemeSettings(); m_pSettingsManager->setDarkMode(m_ui.darkModeCheckBox->isChecked()); emit signalSettingsChanged(); } // END OF void SettingsDialog::slotApply() //============================================================================== void SettingsDialog::slotAddVSLibraryPath() { QString newPath = m_ui.vsLibraryPathEdit->text(); if(newPath.isEmpty()) return; int pathsNumber = m_ui.vsLibraryPathsListWidget->count(); for(int i = 0; i < pathsNumber; ++i) { QString path = m_ui.vsLibraryPathsListWidget->item(i)->text(); if(path == newPath) return; } QListWidgetItem * pListItem = new QListWidgetItem(newPath, m_ui.vsLibraryPathsListWidget); pListItem->setToolTip(newPath); } // END OF void SettingsDialog::slotAddVSLibraryPath() //============================================================================== void SettingsDialog::slotRemoveVSLibraryPath() { QListWidgetItem * pCurrentItem = m_ui.vsLibraryPathsListWidget->currentItem(); if(pCurrentItem) delete pCurrentItem; } void SettingsDialog::slotSelectVSLibraryPath() { QString path = QFileDialog::getExistingDirectory(this, tr("Select VapourSynth library search path"), m_ui.vsLibraryPathEdit->text()); if(!path.isEmpty()) m_ui.vsLibraryPathEdit->setText(path); } // END OF void SettingsDialog::slotSelectVSLibraryPath() //============================================================================== void SettingsDialog::slotThemeElementSelected(const QModelIndex & a_index) { if(!a_index.isValid()) return; QString themeElementId = m_pThemeElementsModel->data( a_index, Qt::UserRole).toString(); ThemeElementData themeElementData = m_pThemeElementsModel->getThemeElementData(themeElementId); if(themeElementData.id.isEmpty()) return; if(themeElementData.type == ThemeElementType::TextCharFormat) { m_ui.fontButton->setEnabled(true); m_ui.colourButton->setEnabled(true); m_ui.fontLabel->setText( themeElementData.textCharFormat.font().family()); m_ui.fontLabel->setFont(themeElementData.textCharFormat.font()); QPalette newPalette = m_ui.fontLabel->palette(); newPalette.setColor(QPalette::WindowText, themeElementData.textCharFormat.foreground().color()); m_ui.fontLabel->setPalette(newPalette); m_ui.fontLabel->update(); newPalette = m_ui.colourFrame->palette(); newPalette.setColor(QPalette::Window, themeElementData.textCharFormat.foreground().color()); m_ui.colourFrame->setPalette(newPalette); m_ui.colourFrame->update(); } else if(themeElementData.type == ThemeElementType::Color) { m_ui.fontButton->setEnabled(false); m_ui.colourButton->setEnabled(true); m_ui.fontLabel->setText(QString()); m_ui.fontLabel->setFont(QFont()); m_ui.fontLabel->setPalette(QPalette()); m_ui.fontLabel->update(); QPalette newPalette = m_ui.colourFrame->palette(); newPalette.setColor(QPalette::Window, themeElementData.color); m_ui.colourFrame->setPalette(newPalette); m_ui.colourFrame->update(); } } // END OF void SettingsDialog::slotThemeElementSelected( // const QModelIndex & a_index) //============================================================================== void SettingsDialog::slotFontButtonClicked() { QModelIndex index = m_ui.themeElementsList->currentIndex(); QString id = m_pThemeElementsModel->data(index, Qt::UserRole).toString(); ThemeElementData themeElementData = m_pThemeElementsModel->getThemeElementData(id); QFontDialog fontDialog; fontDialog.setCurrentFont(themeElementData.textCharFormat.font()); int returnCode = fontDialog.exec(); if(returnCode == QDialog::Rejected) return; QFont newFont = fontDialog.selectedFont(); themeElementData.textCharFormat.setFont(newFont); m_pThemeElementsModel->saveThemeElementData(themeElementData); m_ui.fontLabel->setText(newFont.family()); m_ui.fontLabel->setFont(newFont); } // END OF void SettingsDialog::slotFontButtonClicked() //============================================================================== void SettingsDialog::slotColourButtonClicked() { QModelIndex index = m_ui.themeElementsList->currentIndex(); QString id = m_pThemeElementsModel->data(index, Qt::UserRole).toString(); ThemeElementData themeElementData = m_pThemeElementsModel->getThemeElementData(id); QColorDialog colorDialog; if(themeElementData.type == ThemeElementType::TextCharFormat) { colorDialog.setCurrentColor( themeElementData.textCharFormat.foreground().color()); } else if(themeElementData.type == ThemeElementType::Color) colorDialog.setCurrentColor(themeElementData.color); int returnCode = colorDialog.exec(); if(returnCode == QDialog::Rejected) return; QColor newColor = colorDialog.selectedColor(); if(themeElementData.type == ThemeElementType::TextCharFormat) { QBrush brush = themeElementData.textCharFormat.foreground(); brush.setColor(newColor); themeElementData.textCharFormat.setForeground(brush); QPalette newPalette = m_ui.fontLabel->palette(); newPalette.setColor(QPalette::WindowText, themeElementData.textCharFormat.foreground().color()); m_ui.fontLabel->setPalette(newPalette); m_ui.fontLabel->update(); newPalette = m_ui.colourFrame->palette(); newPalette.setColor(QPalette::Window, themeElementData.textCharFormat.foreground().color()); m_ui.colourFrame->setPalette(newPalette); m_ui.colourFrame->update(); } else if(themeElementData.type == ThemeElementType::Color) { themeElementData.color = newColor; QPalette newPalette = m_ui.colourFrame->palette(); newPalette.setColor(QPalette::Window, themeElementData.color); m_ui.colourFrame->setPalette(newPalette); m_ui.colourFrame->update(); } m_pThemeElementsModel->saveThemeElementData(themeElementData); } // END OF void SettingsDialog::slotColourButtonClicked() //============================================================================== ================================================ FILE: vsedit/src/settings/settings_dialog.h ================================================ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H #include #include "actions_hotkey_edit_model.h" class SettingsManager; class ItemDelegateForHotkey; class ThemeElementsModel; class SettingsDialog : public QDialog { Q_OBJECT public: SettingsDialog(SettingsManager * a_pSettingsManager, QWidget * a_pParent = nullptr); virtual ~SettingsDialog(); public slots: void slotCall(); protected: signals: void signalSettingsChanged(); private: void addThemeElements(); Ui::SettingsDialog m_ui; SettingsManager * m_pSettingsManager; ActionsHotkeyEditModel * m_pActionsHotkeyEditModel; ItemDelegateForHotkey * m_pItemDelegateForHotkey; ThemeElementsModel * m_pThemeElementsModel; private slots: void slotOk(); void slotApply(); void slotAddVSLibraryPath(); void slotRemoveVSLibraryPath(); void slotSelectVSLibraryPath(); void slotThemeElementSelected(const QModelIndex & a_index); void slotFontButtonClicked(); void slotColourButtonClicked(); }; #endif // SETTINGSDIALOG_H ================================================ FILE: vsedit/src/settings/settings_dialog.ui ================================================ SettingsDialog 0 0 512 665 Settings - VapourSynth Editor 0 0 0 0 0 0 Common 4 6 6 6 6 Automatically load the last script true Periodically reload script text from disk (to sync with external changes) false Prompt to save changes when closing script file true Limit the number of recent scripts in history to 1 10 Qt::Horizontal 40 20 Characters typed to start autocompletion 0 10 2 Qt::Horizontal 40 20 Use spaces as Tab Space characters in Tab 1 80 4 Qt::Horizontal 40 20 Highlight selection matches Minimum legth to highlight 80 3 Qt::Horizontal 40 20 Remember last previewed frame on exit Keep current frame when previewing different script PNG snapshot compression level (0-100 for size factor) 100 0 0 Qt::Horizontal 40 20 Show debug messages Dark theme (overriding OS theme; relaunching required) QFrame::HLine QFrame::Sunken Portable mode means that editor stores its settings in the same folder with its executable. Editors determines if it is running in portable mode by looking for the configuration file in its folder. Please make sure you have the rights to write files in the editor's folder before you switch to this mode. true Portable mode enabled Qt::Vertical 20 31 Paths 2 2 2 2 2 VapourSynth library (VSScript) search paths 0 0 0 0 2 Qt::ScrollBarAlwaysOff true Qt::ElideNone Remove VapourSynth library search path Qt::Vertical 20 124 Add VapourSynth library search path Select path Libraries from this list are loaded prior to environment configs Hotkeys 0 0 0 0 0 QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows Qt::ElideNone QAbstractItemView::ScrollPerPixel false true false Qt::PreventContextMenu Theme 4 6 6 6 6 Theme elements QAbstractItemView::NoEditTriggers QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel QListView::Adjust Font 0 0 Colour true QFrame::Box QFrame::Plain 2 Qt::Horizontal 40 20 OK Apply Cancel ================================================ FILE: vsedit/src/settings/theme_elements_model.cpp ================================================ #include "theme_elements_model.h" #include "../../../common-src/settings/settings_manager.h" //============================================================================== ThemeElementsModel::ThemeElementsModel(SettingsManager * a_pSettingsManager, QObject * a_pParent) : QAbstractItemModel(a_pParent) , m_pSettingsManager(a_pSettingsManager) { } // END OF ThemeElementsModel::ThemeElementsModel( // SettingsManager * a_pSettingsManager, QObject * a_pParent) //============================================================================== ThemeElementsModel::~ThemeElementsModel() { } // END OF ThemeElementsModel::~ThemeElementsModel() //============================================================================== QModelIndex ThemeElementsModel::index(int a_row, int a_column, const QModelIndex & a_parent) const { (void)a_parent; return createIndex(a_row, a_column); } // END OF QModelIndex ThemeElementsModel::index(int a_row, int a_column, // const QModelIndex & a_parent) const //============================================================================== QModelIndex ThemeElementsModel::parent(const QModelIndex & a_child) const { (void)a_child; return QModelIndex(); } // END OF QModelIndex ThemeElementsModel::parent(const QModelIndex & a_child) // const //============================================================================== Qt::ItemFlags ThemeElementsModel::flags(const QModelIndex & a_index) const { if (!a_index.isValid()) { return Qt::NoItemFlags; } Qt::ItemFlags cellFlags = Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable ; return cellFlags; } // END OF Qt::ItemFlags ThemeElementsModel::flags(const QModelIndex & a_index) // const //============================================================================== QVariant ThemeElementsModel::data(const QModelIndex & a_index, int a_role) const { if(!a_index.isValid()) return QVariant(); if((a_index.row() >= (int)m_themeElementsList.size()) || (a_index.column() >= 1)) return QVariant(); if(a_role == Qt::DecorationRole) return m_themeElementsList[a_index.row()].icon; else if((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole)) return m_themeElementsList[a_index.row()].text; else if(a_role == Qt::UserRole) return m_themeElementsList[a_index.row()].id; return QVariant(); } // END OF QVariant ThemeElementsModel::data(const QModelIndex & a_index, // int a_role) const //============================================================================== int ThemeElementsModel::rowCount(const QModelIndex & a_parent) const { (void)a_parent; return (int)m_themeElementsList.size(); } // END OF int ThemeElementsModel::rowCount(const QModelIndex & a_parent) const //============================================================================== int ThemeElementsModel::columnCount(const QModelIndex & a_parent) const { (void)a_parent; return 2; } // END OF int ThemeElementsModel::columnCount(const QModelIndex & a_parent) // const //============================================================================== bool ThemeElementsModel::setData(const QModelIndex & a_index, const QVariant & a_value, int a_role) { (void)a_index; (void)a_value; (void)a_role; return false; } // END OF bool ThemeElementsModel::setData(const QModelIndex & a_index, // const QVariant & a_value, int a_role) //============================================================================== void ThemeElementsModel::addThemeElement( const ThemeElementData & a_themeElementData) { for(size_t i = 0; i < m_themeElementsList.size(); ++i) { if(m_themeElementsList[i].id == a_themeElementData.id) return; } m_themeElementsList.push_back(a_themeElementData); emit layoutChanged(); } // END OF void ThemeElementsModel::addThemeElement( // const ThemeElementData & a_themeElementData) //============================================================================== void ThemeElementsModel::addTextCharFormat(const QString & a_id, const QString & a_text) { ThemeElementData newThemeElementData; newThemeElementData.type = ThemeElementType::TextCharFormat; newThemeElementData.id = a_id; newThemeElementData.text = a_text; newThemeElementData.icon = QIcon(QString(":font.png")); newThemeElementData.textCharFormat = m_pSettingsManager->getTextFormat(a_id); addThemeElement(newThemeElementData); } // END OF void ThemeElementsModel::addTextCharFormat(const QString & a_id, // const QString & a_text) //============================================================================== void ThemeElementsModel::addColor(const QString & a_id, const QString & a_text) { ThemeElementData newThemeElementData; newThemeElementData.type = ThemeElementType::Color; newThemeElementData.id = a_id; newThemeElementData.text = a_text; newThemeElementData.icon = QIcon(QString(":color_swatch.png")); newThemeElementData.color = m_pSettingsManager->getColor(a_id); addThemeElement(newThemeElementData); } // END OF void ThemeElementsModel::addColor(const QString & a_id, // const QString & a_text) //============================================================================== void ThemeElementsModel::reloadThemeSettings() { for(size_t i = 0; i < m_themeElementsList.size(); ++i) { if(m_themeElementsList[i].type == ThemeElementType::TextCharFormat) { m_themeElementsList[i].textCharFormat = m_pSettingsManager->getTextFormat(m_themeElementsList[i].id); } else if(m_themeElementsList[i].type == ThemeElementType::Color) { m_themeElementsList[i].color = m_pSettingsManager->getColor(m_themeElementsList[i].id); } } } // END OF void ThemeElementsModel::reloadThemeSettings() //============================================================================== ThemeElementData ThemeElementsModel::getThemeElementData(const QString & a_id) { for(size_t i = 0; i < m_themeElementsList.size(); ++i) { if(m_themeElementsList[i].id == a_id) return m_themeElementsList[i]; } return ThemeElementData(); } // END OF ThemeElementData ThemeElementsModel::getThemeElementData( // const QString & a_id) //============================================================================== bool ThemeElementsModel::saveThemeElementData( const ThemeElementData & a_themeElementData) { for(size_t i = 0; i < m_themeElementsList.size(); ++i) { if(m_themeElementsList[i].id != a_themeElementData.id) continue; m_themeElementsList[i].textCharFormat = a_themeElementData.textCharFormat; m_themeElementsList[i].color = a_themeElementData.color; return true; } return false; } // END OF bool ThemeElementsModel::saveThemeElementData( // const ThemeElementData & a_themeElementData) //============================================================================== void ThemeElementsModel::slotSaveThemeSettings() { for(size_t i = 0; i < m_themeElementsList.size(); ++i) { if(m_themeElementsList[i].type == ThemeElementType::TextCharFormat) { m_pSettingsManager->setTextFormat(m_themeElementsList[i].id, m_themeElementsList[i].textCharFormat); } else if(m_themeElementsList[i].type == ThemeElementType::Color) { m_pSettingsManager->setColor(m_themeElementsList[i].id, m_themeElementsList[i].color); } } } // END OF void ThemeElementsModel::slotSaveThemeSettings() //============================================================================== ================================================ FILE: vsedit/src/settings/theme_elements_model.h ================================================ #ifndef THEME_ELEMENTS_MODEL_H_INCLUDED #define THEME_ELEMENTS_MODEL_H_INCLUDED #include "../../../common-src/settings/settings_definitions.h" #include #include #include class SettingsManager; enum class ThemeElementType { TextCharFormat, Color }; struct ThemeElementData { ThemeElementType type; QString id; QString text; QIcon icon; QTextCharFormat textCharFormat; QColor color; }; typedef std::vector ThemeElementsList; class ThemeElementsModel : public QAbstractItemModel { Q_OBJECT public: ThemeElementsModel(SettingsManager * a_pSettingsManager, QObject * a_pParent = nullptr); virtual ~ThemeElementsModel(); QModelIndex index(int a_row, int a_column, const QModelIndex & a_parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex & a_child) const override; Qt::ItemFlags flags(const QModelIndex & a_index) const override; QVariant data(const QModelIndex & a_index, int a_role = Qt::DisplayRole) const override; int rowCount(const QModelIndex & a_parent = QModelIndex()) const override; int columnCount(const QModelIndex & a_parent = QModelIndex()) const override; bool setData(const QModelIndex & a_index, const QVariant & a_value, int a_role = Qt::EditRole) override; void addThemeElement(const ThemeElementData & a_themeElementData); void addTextCharFormat(const QString & a_id, const QString & a_text); void addColor(const QString & a_id, const QString & a_text); void reloadThemeSettings(); ThemeElementData getThemeElementData(const QString & a_id); bool saveThemeElementData(const ThemeElementData & a_themeElementData); public slots: void slotSaveThemeSettings(); private: ThemeElementsList m_themeElementsList; SettingsManager * m_pSettingsManager; }; #endif // THEME_ELEMENTS_MODEL_H_INCLUDED ================================================ FILE: vsedit/src/vapoursynth/vapoursynth_plugins_manager.cpp ================================================ #include "vapoursynth_plugins_manager.h" #include "../../../common-src/helpers.h" #include "../../../common-src/settings/settings_manager.h" #include #include #include #include #include #include //============================================================================== const char CORE_PLUGINS_FILEPATH[] = "core"; //============================================================================== VapourSynthPluginsManager::VapourSynthPluginsManager( SettingsManager * a_pSettingsManager, const VSAPI * a_cpVSAPI, QObject * a_pParent): QObject(a_pParent) , m_pluginsList() , m_currentPluginPath() , m_pluginAlreadyLoaded(false) , m_pSettingsManager(a_pSettingsManager) { if(a_pParent) { connect(this, SIGNAL(signalWriteLogMessage(int, const QString &)), a_pParent, SLOT(slotWriteLogMessage(int, const QString &))); } slotRefill(a_cpVSAPI); } // END OF VapourSynthPluginsManager::VapourSynthPluginsManager( // SettingsManager * a_pSettingsManager, QObject * a_pParent) //============================================================================== VapourSynthPluginsManager::~VapourSynthPluginsManager() { slotClear(); } // END OF VapourSynthPluginsManager::~VapourSynthPluginsManager() //============================================================================== void VapourSynthPluginsManager::getCorePlugins(const VSAPI * a_cpVSAPI) { if(!a_cpVSAPI) return; VSCore * pCore = a_cpVSAPI->createCore(0); if(!pCore) { emit signalWriteLogMessage(mtCritical, "VapourSynth plugins manager: " "Failed to create VapourSynth core!"); return; } VSPlugin * pPlugin = a_cpVSAPI->getNextPlugin(nullptr, pCore); while(pPlugin) { m_pluginsList.emplace_back(); VSData::Plugin & pluginData = m_pluginsList.back(); pluginData.filepath = a_cpVSAPI->getPluginPath(pPlugin); pluginData.id = a_cpVSAPI->getPluginID(pPlugin); pluginData.pluginNamespace = a_cpVSAPI->getPluginNamespace(pPlugin); pluginData.name = a_cpVSAPI->getPluginName(pPlugin); VSPluginFunction * pPluginFunction = a_cpVSAPI->getNextPluginFunction(nullptr, pPlugin); while(pPluginFunction) { const char * pluginFunctionName = a_cpVSAPI->getPluginFunctionName(pPluginFunction); const char * pluginFunctionArgs = a_cpVSAPI->getPluginFunctionArguments(pPluginFunction); VSData::Function function = parseFunctionSignature(pluginFunctionName, pluginFunctionArgs); pluginData.functions.push_back(function); pPluginFunction = a_cpVSAPI->getNextPluginFunction(pPluginFunction, pPlugin); } pPlugin = a_cpVSAPI->getNextPlugin(pPlugin, pCore); } a_cpVSAPI->freeCore(pCore); } // END OF void VapourSynthPluginsManager::getCorePlugins(const VSAPI * a_cpVSAPI) //============================================================================== QStringList VapourSynthPluginsManager::functions() const { QStringList functionsList; for(const VSData::Plugin & plugin : m_pluginsList) { QString functionNamespace = plugin.pluginNamespace + "."; for(const VSData::Function & function : plugin.functions) functionsList << functionNamespace + function.toString(); } return functionsList; } // END OF QStringList VapourSynthPluginsManager::functions() const //============================================================================== VSPluginsList VapourSynthPluginsManager::pluginsList() const { return m_pluginsList; } // END OF VSPluginsList VapourSynthPluginsManager::pluginsList() const //============================================================================== VSData::Function VapourSynthPluginsManager::parseFunctionSignature( const QString & a_name, const QString & a_arguments) { VSData::Function function; function.name = a_name; QStringList argumentsList = a_arguments.split(';', Qt::SkipEmptyParts); if(argumentsList.size() == 0) return function; // This is true for arguments lists returned by VSAPI. if(argumentsList[0] == a_name) argumentsList.removeFirst(); for(const QString& argumentString : argumentsList) { QStringList argumentParts = argumentString.split(':'); int partsNumber = argumentParts.size(); if(partsNumber < 2) continue; function.arguments.emplace_back(); VSData::FunctionArgument & argument = function.arguments.back(); argument.name = argumentParts[0]; argument.type = argumentParts[1]; for(int i = 2; i < partsNumber; ++i) { if(argumentParts[i] == "opt") argument.optional = true; else if(argumentParts[i] == "empty") argument.empty = true; } } return function; } // END OF VSData::Function VapourSynthPluginsManager::parseFunctionSignature( // const QString & a_name, const QString & a_arguments) //============================================================================== void VapourSynthPluginsManager::slotClear() { m_pluginsList.clear(); } // END OF void VapourSynthPluginsManager::slotClear() //============================================================================== void VapourSynthPluginsManager::slotSort() { std::stable_sort(m_pluginsList.begin(), m_pluginsList.end()); for(VSData::Plugin & plugin : m_pluginsList) std::stable_sort(plugin.functions.begin(), plugin.functions.end()); } // END OF void VapourSynthPluginsManager::slotSort() //============================================================================== void VapourSynthPluginsManager::slotRefill(const VSAPI * a_cpVSAPI) { slotClear(); getCorePlugins(a_cpVSAPI); slotSort(); } // END OF void VapourSynthPluginsManager::slotRefill(const VSAPI * a_cpVSAPI) //============================================================================== ================================================ FILE: vsedit/src/vapoursynth/vapoursynth_plugins_manager.h ================================================ #ifndef VAPOURSYNTHPLUGINSMANAGER_H #define VAPOURSYNTHPLUGINSMANAGER_H #include "vs_plugin_data.h" #include #include #include class SettingsManager; class VapourSynthPluginsManager : public QObject { Q_OBJECT public: VapourSynthPluginsManager(SettingsManager * a_pSettingsManager, const VSAPI * a_cpVSAPI, QObject * a_pParent = nullptr); virtual ~VapourSynthPluginsManager(); void getCorePlugins(const VSAPI * a_cpVSAPI = nullptr); QStringList functions() const; VSPluginsList pluginsList() const; static VSData::Function parseFunctionSignature(const QString & a_name, const QString & a_arguments); public slots: void slotClear(); void slotSort(); void slotRefill(const VSAPI * a_cpVSAPI); signals: void signalWriteLogMessage(int a_messageType, const QString & a_message); private: VSPluginsList m_pluginsList; QString m_currentPluginPath; bool m_pluginAlreadyLoaded; SettingsManager * m_pSettingsManager; VSPLUGINAPI m_VSPAPI; }; #endif // VAPOURSYNTHPLUGINSMANAGER_H ================================================ FILE: vsedit/src/vapoursynth/vs_plugin_data.cpp ================================================ #include "vs_plugin_data.h" #include //============================================================================== VSData::FunctionArgument::FunctionArgument(): name() , type() , optional(false) , empty(false) { } VSData::FunctionArgument::FunctionArgument( const VSData::FunctionArgument & a_other): name(a_other.name) , type(a_other.type) , optional(a_other.optional) , empty(a_other.empty) { } VSData::FunctionArgument::FunctionArgument( VSData::FunctionArgument && a_other): name(std::move(a_other.name)) , type(std::move(a_other.type)) , optional(std::move(a_other.optional)) , empty(std::move(a_other.empty)) { } VSData::FunctionArgument & VSData::FunctionArgument::operator=( VSData::FunctionArgument a_other) { std::swap(name, a_other.name); std::swap(type, a_other.type); std::swap(optional, a_other.optional); std::swap(empty, a_other.empty); return *this; } QString VSData::FunctionArgument::toString() const { return QString("%1 %2").arg(type).arg(name); } bool VSData::FunctionArgument::operator<( const VSData::FunctionArgument & a_other) const { return (!optional && a_other.optional); } //============================================================================== VSData::Function::Function(): name() , arguments() { } VSData::Function::Function(const VSData::Function & a_other): name(a_other.name) , arguments(a_other.arguments) { } VSData::Function::Function(VSData::Function && a_other): name(std::move(a_other.name)) , arguments(std::move(a_other.arguments)) { } VSData::Function & VSData::Function::operator=(VSData::Function a_other) { if(&a_other == this) return *this; std::swap(name, a_other.name); std::swap(arguments, a_other.arguments); return *this; } QString VSData::Function::toString() const { std::vector argumentsCopy = arguments; std::sort(argumentsCopy.begin(), argumentsCopy.end()); size_t argumentsNumber = argumentsCopy.size(); size_t firstOptional; for(firstOptional = 0; (firstOptional < argumentsNumber) && (!argumentsCopy[firstOptional].optional); ++firstOptional){}; QStringList mandatoryArguments; for(size_t i = 0; i < firstOptional; ++i) mandatoryArguments << argumentsCopy[i].toString(); QStringList optionalArguments; for(size_t i = firstOptional; i < argumentsNumber; ++i) optionalArguments << argumentsCopy[i].toString(); QString argumentsString = mandatoryArguments.join(", "); if(!optionalArguments.isEmpty()) argumentsString += QString("[, %1]").arg(optionalArguments.join(", ")); return QString("%1(%2)").arg(name).arg(argumentsString); } bool VSData::Function::operator==(const VSData::Function & a_other) const { return (name == a_other.name); } bool VSData::Function::operator<(const VSData::Function & a_other) const { return (name.compare(a_other.name, Qt::CaseInsensitive) < 0); } //============================================================================== VSData::Plugin::Plugin(): filepath() , id() , pluginNamespace() , name() , functions() { } VSData::Plugin::Plugin(const VSData::Plugin & a_other): filepath(a_other.filepath) , id(a_other.id) , pluginNamespace(a_other.pluginNamespace) , name(a_other.name) , functions(a_other.functions) { } VSData::Plugin::Plugin(VSData::Plugin && a_other): filepath(std::move(a_other.filepath)) , id(std::move(a_other.id)) , pluginNamespace(std::move(a_other.pluginNamespace)) , name(std::move(a_other.name)) , functions(std::move(a_other.functions)) { } VSData::Plugin & VSData::Plugin::operator=(VSData::Plugin a_other) { if(&a_other == this) return *this; std::swap(filepath, a_other.filepath); std::swap(id, a_other.id); std::swap(pluginNamespace, a_other.pluginNamespace); std::swap(name, a_other.name); std::swap(functions, a_other.functions); return *this; } bool VSData::Plugin::operator==(const VSData::Plugin & a_other) const { return (id == a_other.id); } bool VSData::Plugin::operator<(const VSData::Plugin & a_other) const { return (pluginNamespace.compare(a_other.pluginNamespace, Qt::CaseInsensitive) < 0); } //============================================================================== ================================================ FILE: vsedit/src/vapoursynth/vs_plugin_data.h ================================================ #ifndef VSPLUGINDATA_H_INCLUDED #define VSPLUGINDATA_H_INCLUDED #include #include /// Data, gathered from VS core and plugins for syntax highlighting, /// autocompletion and reference. namespace VSData { struct FunctionArgument { QString name; QString type; bool optional; bool empty; FunctionArgument(); FunctionArgument(const VSData::FunctionArgument & a_other); FunctionArgument(VSData::FunctionArgument && a_other); VSData::FunctionArgument & operator=(VSData::FunctionArgument a_other); QString toString() const; bool operator<(const VSData::FunctionArgument & a_other) const; }; struct Function { QString name; std::vector arguments; Function(); Function(const VSData::Function & a_other); Function(VSData::Function && a_other); VSData::Function & operator=(VSData::Function a_other); QString toString() const; bool operator==(const VSData::Function & a_other) const; bool operator<(const VSData::Function & a_other) const; }; struct Plugin { QString filepath; QString id; QString pluginNamespace; QString name; std::vector functions; Plugin(); Plugin(const VSData::Plugin & a_other); Plugin(VSData::Plugin && a_other); VSData::Plugin & operator=(VSData::Plugin a_other); bool operator==(const VSData::Plugin & a_other) const; bool operator<(const VSData::Plugin & a_other) const; }; } typedef std::vector VSPluginsList; #endif // VSPLUGINDATA_H_INCLUDED ================================================ FILE: vsedit/src/vapoursynth/vs_script_processor_dialog.cpp ================================================ #include "vs_script_processor_dialog.h" #include "../../../common-src/helpers.h" #include "../../../common-src/settings/settings_manager.h" #include "../../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../../../common-src/vapoursynth/vs_script_library.h" #include #include #include #include #include //============================================================================== VSScriptProcessorDialog::VSScriptProcessorDialog( SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent, Qt::WindowFlags a_flags): QDialog(a_pParent, a_flags) , m_pSettingsManager(a_pSettingsManager) , m_pVSScriptLibrary(a_pVSScriptLibrary) , m_pVapourSynthScriptProcessor(nullptr) , m_cpVSAPI(nullptr) , m_framesInQueue() , m_framesInProcess() , m_maxThreads(0) , m_usedCacheRatio(0.0) , m_outputIndex(0) , m_wantToFinalize(false) , m_wantToClose(false) , m_pStatusBar(nullptr) , m_pStatusBarWidget(nullptr) , m_readyPixmap(":tick.png") , m_busyPixmap(":busy.png") , m_errorPixmap(":cross.png") , m_cachedFramesLimit(100) , m_clipName("") , m_sceneName("") , m_absoluteTime("") { Q_ASSERT(m_pSettingsManager); Q_ASSERT(m_pVSScriptLibrary); //connect(m_pVSScriptLibrary, // SIGNAL(signalWriteLogMessage(int, const QString &)), // this, SLOT(slotWriteLogMessage(int, const QString &))); m_pVapourSynthScriptProcessor = new VapourSynthScriptProcessor( m_pSettingsManager, m_pVSScriptLibrary, this); m_pStatusBarWidget = new ScriptStatusBarWidget(); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotWriteLogMessage(int, const QString &))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFrameQueueStateChanged(size_t, size_t, size_t, double)), this, SLOT(slotFrameQueueStateChanged(size_t, size_t, size_t, double))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFinalized()), this, SLOT(slotScriptProcessorFinalized())); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalDistributeFrame(int, int, const VSFrame *, const VSFrame *)), this, SLOT(slotReceiveFrame(int, int, const VSFrame *, const VSFrame *))); connect(m_pVapourSynthScriptProcessor, SIGNAL(signalFrameRequestDiscarded(int, int, const QString &)), this, SLOT(slotFrameRequestDiscarded(int, int, const QString &))); } // END OF VSScriptProcessorDialog::VSScriptProcessorDialog( // SettingsManager * a_pSettingsManager, // VSScriptLibrary * a_pVSScriptLibrary, // QWidget * a_pParent, Qt::WindowFlags a_flags) //============================================================================== VSScriptProcessorDialog::~VSScriptProcessorDialog() { stopAndCleanUp(); m_pVapourSynthScriptProcessor->finalize(); } // END OF VSScriptProcessorDialog::~VSScriptProcessorDialog() //============================================================================== bool VSScriptProcessorDialog::initialize(const QString & a_script, const QString & a_scriptName, ProcessReason a_reason) { Q_ASSERT(m_pVapourSynthScriptProcessor); m_cpVSAPI = m_pVSScriptLibrary->getVSAPI(); if(!m_cpVSAPI) return false; if(m_pVapourSynthScriptProcessor->isInitialized()) { stopAndCleanUp(); bool finalized = m_pVapourSynthScriptProcessor->finalize(); if(!finalized) { m_wantToFinalize = true; return false; } } bool initialized = m_pVapourSynthScriptProcessor->initialize(a_script, a_scriptName, m_outputIndex, a_reason); if(!initialized) { if(isVisible()) hide(); return false; } m_nodeInfo[m_outputIndex] = m_pVapourSynthScriptProcessor->nodeInfo(m_outputIndex); Q_ASSERT(!m_nodeInfo[m_outputIndex].isInvalid()); m_pStatusBarWidget->setNodeInfo(m_nodeInfo[m_outputIndex], m_cpVSAPI); return true; } // END OF bool VSScriptProcessorDialog::initialize(const QString & a_script, // const QString & a_scriptName) //============================================================================== bool VSScriptProcessorDialog::busy(int a_outputIndex) const { if(m_framesInProcess.find(a_outputIndex) == m_framesInProcess.end()) return false; return ((m_framesInProcess.at(a_outputIndex) + m_framesInQueue.at(a_outputIndex)) != 0); } // END OF bool VSScriptProcessorDialog::busy(int a_outputIndex) //============================================================================== const QString & VSScriptProcessorDialog::script() const { return m_pVapourSynthScriptProcessor->script(); } // END OF const QString & VSScriptProcessorDialog::script() const //============================================================================== const QString & VSScriptProcessorDialog::scriptName() const { return m_pVapourSynthScriptProcessor->scriptName(); } // END OF const QString & VSScriptProcessorDialog::scriptName() const //============================================================================== void VSScriptProcessorDialog::setScriptName(const QString & a_scriptName) { m_pVapourSynthScriptProcessor->setScriptName(a_scriptName); } // END OF void VSScriptProcessorDialog::setScriptName( // const QString & a_scriptName) //============================================================================== void VSScriptProcessorDialog::slotWriteLogMessage(int a_messageType, const QString & a_message) { emit signalWriteLogMessage(a_messageType, a_message); } // END OF void VSScriptProcessorDialog::slotWriteLogMessage(int a_messageType, // const QString & a_message) //============================================================================== void VSScriptProcessorDialog::slotFrameQueueStateChanged(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio) { m_framesInQueue[m_outputIndex] = a_inQueue; m_framesInProcess[m_outputIndex] = a_inProcess; m_maxThreads = a_maxThreads; m_usedCacheRatio = a_usedCacheRatio; m_pStatusBarWidget->setQueueState(m_framesInQueue[m_outputIndex], m_framesInProcess[m_outputIndex], m_maxThreads, m_usedCacheRatio); emit signalProcessorIdle(!busy(m_outputIndex)); } // END OF void VSScriptProcessorDialog::slotFrameQueueStateChanged( // size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, // double a_usedCacheRatio) //============================================================================== void VSScriptProcessorDialog::slotScriptProcessorFinalized() { m_wantToFinalize = false; if(m_wantToClose) { m_wantToClose = false; close(); } } // END OF void VSScriptProcessorDialog::slotScriptProcessofFinalized() //============================================================================== void VSScriptProcessorDialog::closeEvent(QCloseEvent * a_pEvent) { if(m_wantToClose) return; m_wantToClose = true; stopAndCleanUp(); bool finalized = m_pVapourSynthScriptProcessor->finalize(); if(!finalized) { m_wantToFinalize = true; emit signalWriteLogMessage(mtWarning, tr("Script processor " "is busy. Dialog will close when it is finalized.")); a_pEvent->ignore(); return; } QDialog::closeEvent(a_pEvent); m_wantToClose = false; } // END OF void VSScriptProcessorDialog::closeEvent(QCloseEvent * a_pEvent) //============================================================================== void VSScriptProcessorDialog::stopAndCleanUp() { clearFramesCache(); for(auto & pair : m_nodeInfo) pair.second.setNull(); m_outputIndex = 0; } // END OF void VSScriptProcessorDialog::stopAndCleanUp() //============================================================================== void VSScriptProcessorDialog::clearFramesCache() { for(auto & pair : m_framesCache) { if(!pair.second.empty()) { for(Frame & frame : pair.second) { m_cpVSAPI->freeFrame(frame.cpOutputFrame); m_cpVSAPI->freeFrame(frame.cpPreviewFrame); } pair.second.clear(); } } } // END OF void VSScriptProcessorDialog::stopAndCleanUp() //============================================================================== void VSScriptProcessorDialog::createStatusBar() { QLayout * pLayout = layout(); Q_ASSERT(pLayout); if(!pLayout) return; m_pStatusBar = new QStatusBar(this); pLayout->addWidget(m_pStatusBar); m_pStatusBar->addPermanentWidget(m_pStatusBarWidget, 1); } // END OF void VSScriptProcessorDialog::createStatusBar() //============================================================================== ================================================ FILE: vsedit/src/vapoursynth/vs_script_processor_dialog.h ================================================ #ifndef VS_SCRIPT_PROCESSOR_DIALOG_H_INCLUDED #define VS_SCRIPT_PROCESSOR_DIALOG_H_INCLUDED #include "../../../common-src/helpers.h" #include "../../../common-src/vapoursynth/vs_script_processor_structures.h" #include "../../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../script_status_bar_widget/script_status_bar_widget.h" #include #include #include #include #include #include class QCloseEvent; class QStatusBar; class QLabel; class SettingsManager; class VSScriptLibrary; class VapourSynthScriptProcessor; struct VSAPI; struct VSVideoInfo; struct VSFrame; class VSScriptProcessorDialog : public QDialog { Q_OBJECT public: VSScriptProcessorDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent = nullptr, Qt::WindowFlags a_flags = Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); virtual ~VSScriptProcessorDialog(); virtual bool initialize(const QString & a_script, const QString & a_scriptName, ProcessReason a_reason); virtual bool busy(int a_outputIndex = 0) const; virtual const QString & script() const; virtual const QString & scriptName() const; virtual void setScriptName(const QString & a_scriptName); protected slots: virtual void slotWriteLogMessage(int a_messageType, const QString & a_message); virtual void slotFrameQueueStateChanged(size_t a_inQueue, size_t a_inProcess, size_t a_maxThreads, double a_usedCacheRatio); virtual void slotScriptProcessorFinalized(); virtual void slotReceiveFrame(int a_frameNumber, int a_outputIndex, const VSFrame * a_cpOutputFrame, const VSFrame * a_cpPreviewFrame) = 0; virtual void slotFrameRequestDiscarded(int a_frameNumber, int a_outputIndex, const QString & a_reason) = 0; signals: void signalWriteLogMessage(int a_messageType, const QString & a_message); void signalProcessorIdle(bool a_idle); protected: virtual void closeEvent(QCloseEvent * a_pEvent) override; virtual void stopAndCleanUp(); virtual void clearFramesCache(); /// Adds status bar to the dialog. /// Relies on dialog having a layout. /// Call in derived class after GUI is created. virtual void createStatusBar(); SettingsManager * m_pSettingsManager; VSScriptLibrary * m_pVSScriptLibrary; VapourSynthScriptProcessor * m_pVapourSynthScriptProcessor; const VSAPI * m_cpVSAPI; std::map m_nodeInfo; std::map m_framesInQueue; std::map m_framesInProcess; size_t m_maxThreads; double m_usedCacheRatio; int m_outputIndex; std::vector m_outputIndices; bool m_wantToFinalize; bool m_wantToClose; QStatusBar * m_pStatusBar; ScriptStatusBarWidget * m_pStatusBarWidget; QPixmap m_readyPixmap; QPixmap m_busyPixmap; QPixmap m_errorPixmap; std::map> m_framesCache; size_t m_cachedFramesLimit; QString m_clipName; QString m_sceneName; QString m_absoluteTime; }; #endif // VS_SCRIPT_PROCESSOR_DIALOG_H_INCLUDED ================================================ FILE: vsedit/src/vsedit_encode_main.cpp ================================================ #include "../../common-src/settings/settings_manager.h" #include "../../common-src/vapoursynth/vs_script_library.h" #include "../../common-src/log/vs_editor_log_definitions.h" #include "../../common-src/helpers.h" #include "frame_consumers/encode_dialog.h" #include #include #include #include #include Q_DECLARE_OPAQUE_POINTER(const VSFrame *) Q_DECLARE_OPAQUE_POINTER(VSNode *) SettingsManager * pSettings = nullptr; VSScriptLibrary * pVSSLibrary = nullptr; EncodeDialog * pEncodeDialog = nullptr; int exitCode = 0; void writeLogMessageByTypename(const QString & a_msg, const QString & a_style) { QString debugTypes[] = { LOG_STYLE_DEBUG, LOG_STYLE_QT_DEBUG, LOG_STYLE_VS_DEBUG, }; if(pSettings->getShowDebugMessages() || !vsedit::contains(debugTypes, a_style)) { std::cerr << "[" << a_style.toUpper().toStdString() << "] " << std::string(a_msg.toLocal8Bit()) << std::endl; } QString breakingTypes[] = { LOG_STYLE_VS_CRITICAL, LOG_STYLE_QT_CRITICAL, LOG_STYLE_ERROR, LOG_STYLE_VS_FATAL, LOG_STYLE_QT_FATAL, }; if(vsedit::contains(breakingTypes, a_style)) { if(pEncodeDialog) { pEncodeDialog->show(); QMessageBox * msgBox = new QMessageBox(pEncodeDialog); msgBox->setText(a_msg); msgBox->setWindowTitle(a_style.toUpper()); vsedit::disableFontKerning(msgBox); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse); msgBox->exec(); } // Handle fatal errors and save to current dir QString fatalTypes[] = {LOG_STYLE_VS_FATAL, LOG_STYLE_QT_FATAL}; if(vsedit::contains(fatalTypes, a_style)) { QDateTime now = QDateTime::currentDateTime(); QString timeString = now.toString("hh:mm:ss.zzz"); QString dateString = now.toString("yyyy-MM-dd"); QString caption = QObject::tr("vsedit-encode fatal error!"); QString fullMessage = dateString + QString(" ") + timeString + QString("\n") + caption + QString("\n") + a_msg; QString applicationDir = QCoreApplication::applicationDirPath(); QString errorLogFilePath = applicationDir + QString("/") + QString("vsedit-encode-crashlog-") + dateString + QString("-") + timeString.replace(':', '-') + QString(".txt"); QFile errorLogFile(errorLogFilePath); if(errorLogFile.open(QIODevice::WriteOnly)) { errorLogFile.write(fullMessage.toUtf8()); errorLogFile.close(); } } qApp->exit(-1); exitCode = -1; } } void writeLogMessage(int a_msgType, const QString & a_msg) { QString style = vsMessageTypeToStyleName(a_msgType); writeLogMessageByTypename(a_msg, style); } void handleQtMessage(QtMsgType a_type, const QMessageLogContext & a_context, const QString & a_message) { QString style = LOG_STYLE_DEFAULT; switch(a_type) { case QtDebugMsg: style = LOG_STYLE_QT_DEBUG; break; case QtInfoMsg: style = LOG_STYLE_QT_INFO; break; case QtWarningMsg: style = LOG_STYLE_QT_WARNING; break; case QtCriticalMsg: style = LOG_STYLE_QT_CRITICAL; break; case QtFatalMsg: style = LOG_STYLE_QT_FATAL; break; default: Q_ASSERT(false); } QString fullMessage = QString("%1: %2").arg(style.toUpper()).arg(a_message); QString fileString(a_context.file); QString lineString = QString::number(a_context.line); QString functionString(a_context.function); QString lineInfo = QString("\n(%1:%2").arg(fileString).arg(lineString); if(!functionString.isEmpty()) lineInfo += QString(", %1").arg(functionString); lineInfo += QString(")"); if(!fileString.isEmpty()) fullMessage += lineInfo; writeLogMessageByTypename(fullMessage, style); } int main(int argc, char *argv[]) { QString scriptFilePath; if(argc > 1) { scriptFilePath = QString::fromLocal8Bit(argv[1], -1); } else { std::cerr << "vsedit-encode: Please provide the path to your script." << std::endl; return -2; } QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); QGuiApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::Floor); QApplication application(argc, argv); qInstallMessageHandler(handleQtMessage); pSettings = new SettingsManager(qApp); vsedit::disableFontKerning(qApp); // Make text in message box selectable application.setStyleSheet( "QToolTip { font-kerning: none; }" "QMessageBox { messagebox-text-interaction-flags: 5; }" "QLabel { padding: 0px; }"); qRegisterMetaType("const VSFrame *"); qRegisterMetaType("VSNode *"); QResource digitalMiniFontResource(":/fonts/DigitalMini.ttf"); QByteArray digitalMiniFontData( (const char *)digitalMiniFontResource.data(), digitalMiniFontResource.size()); QFontDatabase::addApplicationFontFromData(digitalMiniFontData); if(pSettings->inDarkMode()) { // Load qDarkStyle colors QFile styleSheetDark(":/dark/style.qss"); if(!styleSheetDark.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::critical(nullptr, QString::fromUtf8("File open error"), QString::fromUtf8("Failed to open stylesheet file ") + styleSheetDark.errorString()); } qApp->setStyleSheet(styleSheetDark.readAll()); QPalette newPal(qApp->palette()); newPal.setColor(QPalette::Base, QColor(0, 0, 0)); newPal.setColor(QPalette::Highlight, QColor(128, 128, 128)); newPal.setColor(QPalette::Dark, QColor(192, 192, 192)); newPal.setColor(QPalette::Text, QColor(64, 192, 0)); qApp->setPalette(newPal); } #ifdef Q_OS_WIN else qApp->setStyle("fusion"); #endif QFileInfo fileInfo(scriptFilePath); QString scriptFileFullPath = fileInfo.absoluteFilePath(); QFile scriptFile(scriptFileFullPath); bool loaded = scriptFile.open(QIODevice::ReadOnly | QIODevice::Text); if(!loaded) { QString errorMsg = QString("Failed to open script [%1]!") .arg(scriptFilePath); writeLogMessageByTypename(errorMsg, LOG_STYLE_ERROR); delete pSettings; return -1; } pVSSLibrary = new VSScriptLibrary(pSettings, qApp); QObject::connect(pVSSLibrary, &VSScriptLibrary::signalWriteLogMessage, writeLogMessage); pEncodeDialog = new EncodeDialog(pSettings, pVSSLibrary, nullptr); QObject::connect(pEncodeDialog, &EncodeDialog::signalWriteLogMessage, writeLogMessageByTypename); if(exitCode != 0) return exitCode; QString scriptText = QString::fromUtf8(scriptFile.readAll()); pSettings->setLastUsedPath(scriptFileFullPath); if(pVSSLibrary->initialize()) { if(pEncodeDialog->initialize(scriptText, scriptFilePath)) { pEncodeDialog->showActive(); exitCode = pEncodeDialog->exec(); } } return exitCode; } ================================================ FILE: vsedit/src/vsedit_previewer_main.cpp ================================================ #include "../../common-src/settings/settings_manager.h" #include "../../common-src/vapoursynth/vs_script_library.h" #include "../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../../common-src/log/vs_editor_log_definitions.h" #include "../../common-src/helpers.h" #include "../../common-src/version_info.h" #include "preview/preview_dialog.h" #include #include #include #include #include #include Q_DECLARE_OPAQUE_POINTER(const VSFrame *) Q_DECLARE_OPAQUE_POINTER(VSNode *) SettingsManager * pSettings = nullptr; VSScriptLibrary * pVSSLibrary = nullptr; PreviewDialog * pPreviewDialog = nullptr; int exitCode = 0; void writeLogMessageByTypename(const QString & a_msg, const QString & a_style) { QString debugTypes[] = { LOG_STYLE_DEBUG, LOG_STYLE_QT_DEBUG, LOG_STYLE_VS_DEBUG, }; if(pSettings->getShowDebugMessages() || !vsedit::contains(debugTypes, a_style)) { std::cerr << "[" << a_style.toUpper().toStdString() << "] " << std::string(a_msg.toLocal8Bit()) << std::endl; } QString breakingTypes[] = { LOG_STYLE_VS_CRITICAL, LOG_STYLE_QT_CRITICAL, LOG_STYLE_ERROR, LOG_STYLE_VS_FATAL, LOG_STYLE_QT_FATAL, }; if(vsedit::contains(breakingTypes, a_style)) { if(pPreviewDialog) { pPreviewDialog->show(); QMessageBox * msgBox = new QMessageBox(pPreviewDialog); msgBox->setText(a_msg); msgBox->setWindowTitle(a_style.toUpper()); vsedit::disableFontKerning(msgBox); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse); msgBox->exec(); } // Handle fatal errors and save to current dir QString fatalTypes[] = {LOG_STYLE_VS_FATAL, LOG_STYLE_QT_FATAL}; if(vsedit::contains(fatalTypes, a_style)) { QDateTime now = QDateTime::currentDateTime(); QString timeString = now.toString("hh:mm:ss.zzz"); QString dateString = now.toString("yyyy-MM-dd"); QString caption = QObject::tr("VSE-Previewer fatal error!"); QString fullMessage = dateString + QString(" ") + timeString + QString("\n") + caption + QString("\n") + a_msg; QString applicationDir = QCoreApplication::applicationDirPath(); QString errorLogFilePath = applicationDir + QString("/") + QString("VSE-Previwer-crashlog-") + dateString + QString("-") + timeString.replace(':', '-') + QString(".txt"); QFile errorLogFile(errorLogFilePath); if(errorLogFile.open(QIODevice::WriteOnly)) { errorLogFile.write(fullMessage.toUtf8()); errorLogFile.close(); } } qApp->exit(-1); exitCode = -1; } } void writeLogMessage(int a_msgType, const QString & a_msg) { QString style = vsMessageTypeToStyleName(a_msgType); writeLogMessageByTypename(a_msg, style); } void handleQtMessage(QtMsgType a_type, const QMessageLogContext & a_context, const QString & a_message) { QString style = LOG_STYLE_DEFAULT; switch(a_type) { case QtDebugMsg: style = LOG_STYLE_QT_DEBUG; break; case QtInfoMsg: style = LOG_STYLE_QT_INFO; break; case QtWarningMsg: style = LOG_STYLE_QT_WARNING; break; case QtCriticalMsg: style = LOG_STYLE_QT_CRITICAL; break; case QtFatalMsg: style = LOG_STYLE_QT_FATAL; break; default: Q_ASSERT(false); } QString fullMessage = QString("%1: %2").arg(style.toUpper()).arg(a_message); QString fileString(a_context.file); QString lineString = QString::number(a_context.line); QString functionString(a_context.function); QString lineInfo = QString("\n(%1:%2").arg(fileString).arg(lineString); if(!functionString.isEmpty()) lineInfo += QString(", %1").arg(functionString); lineInfo += QString(")"); if(!fileString.isEmpty()) fullMessage += lineInfo; writeLogMessageByTypename(fullMessage, style); } int main(int argc, char *argv[]) { QString scriptFilePath; if(argc > 1) { if(strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { print_version(); return 0; } else scriptFilePath = QString::fromLocal8Bit(argv[1], -1); } else { std::cerr << "vsedit-previewer: Please provide the path to your script." << std::endl; return -2; } print_version(); QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); QGuiApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::Floor); QApplication application(argc, argv); qInstallMessageHandler(handleQtMessage); pSettings = new SettingsManager(qApp); vsedit::disableFontKerning(qApp); // Make text in message box selectable application.setStyleSheet( "QToolTip { font-kerning: none; }" "QMessageBox { messagebox-text-interaction-flags: 5; }" "QLabel { padding: 0px; }"); qRegisterMetaType("const VSFrame *"); qRegisterMetaType("VSNode *"); QResource digitalMiniFontResource(":/fonts/DigitalMini.ttf"); QByteArray digitalMiniFontData( (const char *)digitalMiniFontResource.data(), digitalMiniFontResource.size()); QFontDatabase::addApplicationFontFromData(digitalMiniFontData); if(pSettings->inDarkMode()) { // Load qDarkStyle colors QFile styleSheetDark(":/dark/style.qss"); if(!styleSheetDark.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::critical(nullptr, QString::fromUtf8("File open error"), QString::fromUtf8("Failed to open stylesheet file ") + styleSheetDark.errorString()); } qApp->setStyleSheet(styleSheetDark.readAll()); QPalette newPal(qApp->palette()); newPal.setColor(QPalette::Base, QColor(0, 0, 0)); newPal.setColor(QPalette::Highlight, QColor(128, 128, 128)); newPal.setColor(QPalette::Dark, QColor(192, 192, 192)); newPal.setColor(QPalette::Text, QColor(64, 192, 0)); qApp->setPalette(newPal); } #ifdef Q_OS_WIN else qApp->setStyle("fusion"); #endif QFileInfo fileInfo(scriptFilePath); QString scriptFileFullPath = fileInfo.absoluteFilePath(); QFile scriptFile(scriptFileFullPath); bool loaded = scriptFile.open(QIODevice::ReadOnly | QIODevice::Text); if(!loaded) { QString errorMsg = QString("Failed to open script [%1]!") .arg(scriptFilePath); writeLogMessageByTypename(errorMsg, LOG_STYLE_ERROR); delete pSettings; return -1; } pVSSLibrary = new VSScriptLibrary(pSettings, qApp); QObject::connect(pVSSLibrary, &VSScriptLibrary::signalWriteLogMessage, writeLogMessage); pPreviewDialog = new PreviewDialog(pSettings, pVSSLibrary, true); QObject::connect(pPreviewDialog, &PreviewDialog::signalWriteLogMessage, writeLogMessage); if(exitCode != 0) return exitCode; QString scriptText = QString::fromUtf8(scriptFile.readAll()); pSettings->setLastUsedPath(scriptFileFullPath); if(pVSSLibrary->initialize()) { pPreviewDialog->previewScript(scriptText, scriptFileFullPath); exitCode = pPreviewDialog->exec(); } return exitCode; } ================================================ FILE: vsedit-job-server/src/job_server.cpp ================================================ #include "job_server.h" #include "../../common-src/ipc_defines.h" #include "../../common-src/helpers.h" #include "jobs/jobs_manager.h" #include #include //============================================================================== JobServer::JobServer(QObject * a_pParent) : QObject(a_pParent) , m_pSettingsManager(nullptr) , m_pJobsManager(nullptr) , m_pWebSocketServer(nullptr) { m_pSettingsManager = new SettingsManagerCore(this); m_trustedClientsAddresses = m_pSettingsManager->getTrustedClientsAddresses(); m_pJobsManager = new JobsManager(m_pSettingsManager, this); m_pJobsManager->loadJobs(); connect(m_pJobsManager, &JobsManager::signalLogMessage, this, &JobServer::slotLogMessage); connect(m_pJobsManager, &JobsManager::signalJobCreated, this, &JobServer::slotJobCreated); connect(m_pJobsManager, &JobsManager::signalJobChanged, this, &JobServer::slotJobChanged); connect(m_pJobsManager, &JobsManager::signalJobStateChanged, this, &JobServer::slotJobStateChanged); connect(m_pJobsManager, &JobsManager::signalJobProgressChanged, this, &JobServer::slotJobProgressChanged); connect(m_pJobsManager, &JobsManager::signalJobStartTimeChanged, this, &JobServer::slotJobStartTimeChanged); connect(m_pJobsManager, &JobsManager::signalJobEndTimeChanged, this, &JobServer::slotJobEndTimeChanged); connect(m_pJobsManager, &JobsManager::signalJobDependenciesChanged, this, &JobServer::slotJobDependenciesChanged); connect(m_pJobsManager, &JobsManager::signalJobsSwapped, this, &JobServer::slotJobsSwapped); connect(m_pJobsManager, &JobsManager::signalJobsDeleted, this, &JobServer::slotJobsDeleted); m_pWebSocketServer = new QWebSocketServer(JOB_SERVER_NAME, QWebSocketServer::NonSecureMode, this); connect(m_pWebSocketServer, &QWebSocketServer::newConnection, this, &JobServer::slotNewConnection); } // END OF JobServer::JobServer(QObject * a_pParent) //============================================================================== JobServer::~JobServer() { for(QWebSocket * pClient : m_clients) { disconnect(pClient, &QWebSocket::disconnected, this, &JobServer::slotSocketDisconnected); delete pClient; } m_clients.clear(); m_subscribers.clear(); m_pWebSocketServer->close(); m_pJobsManager->saveJobs(); } // END OF JobServer::~JobServer() //============================================================================== bool JobServer::start() { Q_ASSERT(m_pWebSocketServer); return m_pWebSocketServer->listen(QHostAddress::Any, JOB_SERVER_PORT); } // END OF bool JobServer::start() //============================================================================== void JobServer::slotNewConnection() { QWebSocket * pSocket = m_pWebSocketServer->nextPendingConnection(); m_clients.push_back(pSocket); connect(pSocket, &QWebSocket::binaryMessageReceived, this, &JobServer::slotBinaryMessageReceived); connect(pSocket, &QWebSocket::textMessageReceived, this, &JobServer::slotTextMessageReceived); connect(pSocket, &QWebSocket::disconnected, this, &JobServer::slotSocketDisconnected); if(trustedClientAddress(pSocket->peerAddress())) { QByteArray message = vsedit::jsonMessage(SMSG_TRUSTED_CLIENTS_INFO, QJsonArray::fromStringList(m_trustedClientsAddresses)); pSocket->sendBinaryMessage(message); } } // END OF void JobServer::slotNewConnection() //============================================================================== void JobServer::slotBinaryMessageReceived( const QByteArray & a_message) { QWebSocket * pClient = qobject_cast(sender()); if(!pClient) return; QString messageString = tr(a_message); processMessage(pClient, messageString); } // END OF void JobServer::slotBinaryMessageReceived( // const QByteArray & a_message) //============================================================================== void JobServer::slotTextMessageReceived(const QString & a_message) { QWebSocket * pClient = qobject_cast(sender()); if(!pClient) return; processMessage(pClient, a_message); } // END OF void JobServer::slotTextMessageReceived(const QString & a_message) //============================================================================== void JobServer::slotSocketDisconnected() { QWebSocket * pClient = qobject_cast(sender()); if(!pClient) return; m_clients.remove(pClient); m_subscribers.remove(pClient); pClient->deleteLater(); } // END OF void JobServer::slotSocketDisconnected() //============================================================================== void JobServer::slotLogMessage(const QString & a_message, const QString & a_style) { LogEntry entry(a_message, a_style); m_logEntries.push_back(entry); broadcastMessage(vsedit::jsonMessage(SMSG_LOG_MESSAGE, entry.toJson())); } // END OF void JobServer::slotLogMessage(const QString & a_message, // const QString & a_style) //============================================================================== void JobServer::slotJobCreated(const JobProperties & a_properties) { broadcastMessage(vsedit::jsonMessage(SMSG_JOB_CREATED, a_properties.toJson())); } // END OF void JobServer::slotJobCreated(const JobProperties & a_properties) //============================================================================== void JobServer::slotJobChanged(const JobProperties & a_properties) { broadcastMessage(vsedit::jsonMessage(SMSG_JOB_UPDATE, a_properties.toJson())); } // END OF void JobServer::slotJobChanged(const JobProperties & a_properties) //============================================================================== void JobServer::slotJobStateChanged(const QUuid & a_jobID, JobState a_state) { QJsonObject jsJob; jsJob[JP_ID] = a_jobID.toString(); jsJob[JP_JOB_STATE] = (int)a_state; broadcastMessage(vsedit::jsonMessage(SMSG_JOB_STATE_UPDATE, jsJob)); } // END OF void JobServer::slotJobStateChanged(const QUuid & a_jobID, // JobState a_state) //============================================================================== void JobServer::slotJobProgressChanged(const QUuid & a_jobID, int a_progress, double a_fps) { QJsonObject jsJob; jsJob[JP_ID] = a_jobID.toString(); jsJob[JP_FRAMES_PROCESSED] = a_progress; jsJob[JP_FPS] = a_fps; broadcastMessage(vsedit::jsonMessage(SMSG_JOB_PROGRESS_UPDATE, jsJob)); } // END OF void JobServer::slotJobProgressChanged(const QUuid & a_jobID, // int a_progress, double a_fps) //============================================================================== void JobServer::slotJobStartTimeChanged(const QUuid & a_jobID, const QDateTime & a_time) { QJsonObject jsJob; jsJob[JP_ID] = a_jobID.toString(); jsJob[JP_TIME_STARTED] = a_time.toMSecsSinceEpoch(); broadcastMessage(vsedit::jsonMessage(SMSG_JOB_START_TIME_UPDATE, jsJob)); } // END OF void JobServer::slotJobStartTimeChanged(const QUuid & a_jobID, // const QDateTime & a_time) //============================================================================== void JobServer::slotJobEndTimeChanged(const QUuid & a_jobID, const QDateTime & a_time) { QJsonObject jsJob; jsJob[JP_ID] = a_jobID.toString(); jsJob[JP_TIME_ENDED] = a_time.toMSecsSinceEpoch(); broadcastMessage(vsedit::jsonMessage(SMSG_JOB_END_TIME_UPDATE, jsJob)); } // END OF void JobServer::slotJobEndTimeChanged(const QUuid & a_jobID, // const QDateTime & a_time) //============================================================================== void JobServer::slotJobDependenciesChanged(const QUuid & a_jobID, const std::vector & a_dependencies) { QJsonObject jsJob; jsJob[JP_ID] = a_jobID.toString(); QJsonArray jsDependencies; for(const QUuid & id : a_dependencies) jsDependencies << id.toString(); jsJob[JP_DEPENDS_ON_JOB_IDS] = jsDependencies; broadcastMessage(vsedit::jsonMessage(SMSG_JOB_DEPENDENCIES_UPDATE, jsJob)); } // END OF void JobServer::slotJobDependenciesChanged(const QUuid & a_jobID, // const std::vector & a_dependencies) //============================================================================== void JobServer::slotJobsSwapped(const QUuid & a_jobID1, const QUuid & a_jobID2) { QJsonArray jsSwap; jsSwap << a_jobID1.toString(); jsSwap << a_jobID2.toString(); broadcastMessage(vsedit::jsonMessage(SMSG_JOBS_SWAPPED, jsSwap)); } // END OF void JobServer::slotJobsSwapped(const QUuid & a_jobID1, // const QUuid & a_jobID2) //============================================================================== void JobServer::slotJobsDeleted(const std::vector & a_ids) { QJsonArray jsIdsArray; for(const QUuid & id : a_ids) jsIdsArray.push_back(id.toString()); broadcastMessage(vsedit::jsonMessage(SMSG_JOBS_DELETED, jsIdsArray)); } // END OF void JobServer::slotJobsDeleted(const std::vector & a_ids) //============================================================================== void JobServer::processMessage(QWebSocket * a_pClient, const QString & a_message) { bool trustedClient = trustedClientAddress(a_pClient->peerAddress()); QString command = a_message; QString arguments; int spaceIndex = a_message.indexOf(' '); if(spaceIndex >= 0) { command = a_message.left(spaceIndex); arguments = a_message.mid(spaceIndex + 1); } QString trustedOnlyCommands[] = {MSG_CLOSE_SERVER, MSG_CREATE_JOB, MSG_CHANGE_JOB, MSG_SWAP_JOBS, MSG_RESET_JOBS, MSG_DELETE_JOBS, MSG_START_ALL_WAITING_JOBS, MSG_PAUSE_ACTIVE_JOBS, MSG_RESUME_PAUSED_JOBS, MSG_ABORT_ACTIVE_JOBS, MSG_GET_TRUSTED_CLIENTS, MSG_SET_TRUSTED_CLIENTS}; if(vsedit::contains(trustedOnlyCommands, command) && (!trustedClient)) { a_pClient->sendBinaryMessage("You're naughty! This command can not " "be executed remotely."); return; } QJsonDocument jsArguments = QJsonDocument::fromJson(arguments.toUtf8()); if(command == QString(MSG_GET_JOBS_INFO)) { a_pClient->sendBinaryMessage(jobsInfoMessage()); return; } if(command == QString(MSG_GET_LOG)) { a_pClient->sendBinaryMessage(completeLogMessage()); return; } if(command == QString(MSG_SUBSCRIBE)) { m_subscribers.push_back(a_pClient); a_pClient->sendBinaryMessage("Subscribed to jobs updates."); return; } if(command == QString(MSG_UNSUBSCRIBE)) { m_subscribers.remove(a_pClient); a_pClient->sendBinaryMessage("Unsubscribed from jobs updates."); return; } if(command == QString(MSG_CLOSE_SERVER)) { broadcastMessage(SMSG_CLOSING_SERVER, true); emit finish(); return; } if(command == QString(MSG_GET_TRUSTED_CLIENTS)) { QByteArray message = vsedit::jsonMessage(SMSG_TRUSTED_CLIENTS_INFO, QJsonArray::fromStringList(m_trustedClientsAddresses)); a_pClient->sendBinaryMessage(message); return; } if(command == QString(MSG_SET_TRUSTED_CLIENTS)) { QStringList trustedClientsAddresses; for(QJsonValue value : jsArguments.array()) { QString address = value.toString(); QHostAddress hostAddress(address); if(hostAddress.isLoopback()) continue; trustedClientsAddresses << address; } trustedClientsAddresses.removeDuplicates(); m_trustedClientsAddresses = trustedClientsAddresses; m_pSettingsManager->setTrustedClientsAddresses( m_trustedClientsAddresses); QByteArray message = vsedit::jsonMessage(SMSG_TRUSTED_CLIENTS_INFO, QJsonArray::fromStringList(m_trustedClientsAddresses)); broadcastMessage(message, true, true); return; } if(command == QString(MSG_CREATE_JOB)) { JobProperties properties = JobProperties::fromJson(jsArguments.object()); m_pJobsManager->createJob(properties); return; } if(command == QString(MSG_CHANGE_JOB)) { JobProperties properties = JobProperties::fromJson(jsArguments.object()); m_pJobsManager->changeJob(properties); return; } if(command == QString(MSG_SET_JOB_DEPENDENCIES)) { QJsonObject jsJob = jsArguments.object(); if(!jsJob.contains(JP_ID)) return; QUuid id(jsJob[JP_ID].toString()); if(!jsJob.contains(JP_DEPENDS_ON_JOB_IDS)) return; QJsonArray jsDependencies = jsJob[JP_DEPENDS_ON_JOB_IDS].toArray(); std::vector dependencies; for(int i = 0; i < jsDependencies.count(); ++i) dependencies.push_back(QUuid(jsDependencies[i].toString())); m_pJobsManager->setJobDependsOnIds(id, dependencies); return; } if(command == QString(MSG_SWAP_JOBS)) { QJsonArray jsIDs = jsArguments.array(); if(jsIDs.count() != 2) return; m_pJobsManager->swapJobs(QUuid(jsIDs[0].toString()), QUuid(jsIDs[1].toString())); return; } if(command == QString(MSG_RESET_JOBS)) { QJsonArray jsIDs = jsArguments.array(); std::vector ids; for(int i = 0; i < jsIDs.count(); ++i) ids.push_back(QUuid(jsIDs[i].toString())); m_pJobsManager->resetJobs(ids); return; } if(command == QString(MSG_DELETE_JOBS)) { QJsonArray jsIDs = jsArguments.array(); std::vector ids; for(int i = 0; i < jsIDs.count(); ++i) ids.push_back(QUuid(jsIDs[i].toString())); m_pJobsManager->deleteJobs(ids); return; } if(command == QString(MSG_START_ALL_WAITING_JOBS)) { m_pJobsManager->startWaitingJobs(); return; } if(command == QString(MSG_PAUSE_ACTIVE_JOBS)) { m_pJobsManager->pauseActiveJobs(); return; } if(command == QString(MSG_RESUME_PAUSED_JOBS)) { m_pJobsManager->resumePausedJobs(); return; } if(command == QString(MSG_ABORT_ACTIVE_JOBS)) { m_pJobsManager->abortActiveJobs(); return; } a_pClient->sendBinaryMessage(QString("Received an unknown command: %1") .arg(a_message).toUtf8()); } // END OF void JobServer::processMessage(QWebSocket * a_pClient, // const QString & a_message) //============================================================================== QByteArray JobServer::jobsInfoMessage() const { QJsonArray jsJobs; for(const JobProperties & properties : m_pJobsManager->jobsProperties()) jsJobs.push_back(properties.toJson()); QByteArray message = vsedit::jsonMessage(SMSG_JOBS_INFO, jsJobs); return message; } // END OF QByteArray JobServer::jobsInfoMessage() const //============================================================================== QByteArray JobServer::completeLogMessage() const { QJsonArray jsEntries; for(const LogEntry & entry : m_logEntries) jsEntries.push_back(entry.toJson()); QByteArray message = vsedit::jsonMessage(SMSG_COMPLETE_LOG, jsEntries); return message; } // END OF QByteArray JobServer::completeLogMessage() const //============================================================================== void JobServer::broadcastMessage(const QString & a_message, bool a_includeNonSubscribers, bool a_trustedOnly) { broadcastMessage(a_message.toUtf8(), a_includeNonSubscribers, a_trustedOnly); } // END OF void JobServer::broadcastMessage(const QString & a_message, // bool a_includeNonSubscribers, bool a_trustedOnly) //============================================================================== void JobServer::broadcastMessage(const char * a_message, bool a_includeNonSubscribers, bool a_trustedOnly) { broadcastMessage(QByteArray(a_message), a_includeNonSubscribers, a_trustedOnly); } // END OF void JobServer::broadcastMessage(const char * a_message, // bool a_includeNonSubscribers, bool a_trustedOnly) //============================================================================== void JobServer::broadcastMessage(const QByteArray & a_message, bool a_includeNonSubscribers, bool a_trustedOnly) { std::list & clients = a_includeNonSubscribers ? m_clients : m_subscribers; for(QWebSocket * pClient : clients) { if((!a_trustedOnly) || trustedClientAddress(pClient->peerAddress())) pClient->sendBinaryMessage(a_message); } } // END OF void JobServer::broadcastMessage(const QByteArray & a_message, // bool a_includeNonSubscribers, bool a_trustedOnly) //============================================================================== bool JobServer::trustedClientAddress(const QHostAddress & a_address) { return (a_address.isLoopback() || m_trustedClientsAddresses.contains(a_address.toString()) || m_trustedClientsAddresses.contains( a_address.toString().remove("::ffff:"))); } // END OF bool JobServer::trustedClientAddress(const QHostAddress & a_address) //============================================================================== ================================================ FILE: vsedit-job-server/src/job_server.h ================================================ #ifndef WEB_SOCKET_JOB_SERVER_H_INCLUDED #define WEB_SOCKET_JOB_SERVER_H_INCLUDED #include "../../common-src/settings/settings_manager_core.h" #include "../../common-src/log/styled_log_view_core.h" #include #include #include #include #include #include class SettingsManagerCore; class JobsManager; class QWebSocketServer; class QWebSocket; class QHostAddress; class JobServer : public QObject { Q_OBJECT public: JobServer(QObject * a_pParent = nullptr); virtual ~JobServer(); bool start(); signals: void finish(); private slots: void slotNewConnection(); void slotBinaryMessageReceived(const QByteArray & a_message); void slotTextMessageReceived(const QString & a_message); void slotSocketDisconnected(); void slotLogMessage(const QString & a_message, const QString & a_style); void slotJobCreated(const JobProperties & a_properties); void slotJobChanged(const JobProperties & a_properties); void slotJobStateChanged(const QUuid & a_jobID, JobState a_state); void slotJobProgressChanged(const QUuid & a_jobID, int a_progress, double a_fps); void slotJobStartTimeChanged(const QUuid & a_jobID, const QDateTime & a_time); void slotJobEndTimeChanged(const QUuid & a_jobID, const QDateTime & a_time); void slotJobDependenciesChanged(const QUuid & a_jobID, const std::vector & a_dependencies); void slotJobsSwapped(const QUuid & a_jobID1, const QUuid & a_jobID2); void slotJobsDeleted(const std::vector & a_ids); private: void processMessage(QWebSocket * a_pClient, const QString & a_message); QByteArray jobsInfoMessage() const; QByteArray completeLogMessage() const; void broadcastMessage(const QString & a_message, bool a_includeNonSubscribers = false, bool a_trustedOnly = false); void broadcastMessage(const char * a_message, bool a_includeNonSubscribers = false, bool a_trustedOnly = false); void broadcastMessage(const QByteArray & a_message, bool a_includeNonSubscribers = false, bool a_trustedOnly = false); bool trustedClientAddress(const QHostAddress & a_address); SettingsManagerCore * m_pSettingsManager; JobsManager * m_pJobsManager; QWebSocketServer * m_pWebSocketServer; std::vector m_logEntries; std::list m_clients; std::list m_subscribers; QStringList m_trustedClientsAddresses; }; #endif // WEB_SOCKET_JOB_SERVER_H_INCLUDED ================================================ FILE: vsedit-job-server/src/jobs/job_definitions.h ================================================ #ifndef JOB_DEFINITIONS_H_INCLUDED #define JOB_DEFINITIONS_H_INCLUDED #include "../../../common-src/jobs/job.h" enum class JobWantTo { Nothing, RunNext, }; struct JobTicket { vsedit::Job * pJob; JobWantTo whenDone; }; #endif // JOB_DEFINITIONS_H_INCLUDED ================================================ FILE: vsedit-job-server/src/jobs/jobs_manager.cpp ================================================ #include "jobs_manager.h" #include "../../../common-src/settings/settings_manager_core.h" #include "../../../common-src/vapoursynth/vs_script_library.h" //============================================================================== JobsManager::JobsManager(SettingsManagerCore * a_pSettingsManager, QObject * a_pParent) : QObject(a_pParent) , m_pSettingsManager(a_pSettingsManager) , m_pVSScriptLibrary(nullptr) { Q_ASSERT(m_pSettingsManager); m_pVSScriptLibrary = new VSScriptLibrary(m_pSettingsManager, this); connect(m_pVSScriptLibrary, SIGNAL(signalWriteLogMessage(int, const QString &)), this, SLOT(slotLogMessage(int, const QString &))); } // END OF //============================================================================== JobsManager::~JobsManager() { } // END OF //============================================================================== std::vector JobsManager::jobsProperties() const { std::vector properties; for(const JobTicket & ticket : m_tickets) properties.push_back(ticket.pJob->properties()); return properties; } // END OF //============================================================================== int JobsManager::createJob(const JobProperties & a_jobProperties) { vsedit::Job * pJob = new vsedit::Job(a_jobProperties, m_pSettingsManager, m_pVSScriptLibrary, this); connectJob(pJob); JobTicket ticket = {pJob, JobWantTo::Nothing}; m_tickets.push_back(ticket); int newRow = (int)m_tickets.size(); saveJobs(); emit signalJobCreated(a_jobProperties); return newRow; } // END OF //============================================================================== bool JobsManager::swapJobs(const QUuid & a_jobID1, const QUuid & a_jobID2) { int lowerIndex = indexOfJob(a_jobID1); if(lowerIndex < 0) return false; int higherIndex = indexOfJob(a_jobID2); if(higherIndex < 0) return false; if(higherIndex < lowerIndex) std::swap(lowerIndex, higherIndex); for(int i = lowerIndex; i < higherIndex; ++i) { if(vsedit::contains(m_tickets[higherIndex].pJob->dependsOnJobIds(), m_tickets[i].pJob->id())) return false; } for(int i = lowerIndex + 1; i < higherIndex; ++i) { if(vsedit::contains(m_tickets[i].pJob->dependsOnJobIds(), m_tickets[lowerIndex].pJob->id())) return false; } std::swap(m_tickets[lowerIndex], m_tickets[higherIndex]); saveJobs(); emit signalJobsSwapped(a_jobID1, a_jobID2); return true; } // END OF //============================================================================== bool JobsManager::setJobState(const QUuid & a_jobID, JobState a_state) { int index = indexOfJob(a_jobID); if(!checkCanModifyJobAndNotify(index)) return false; bool result = m_tickets[index].pJob->setState(a_state); if(result) saveJobs(); return result; } // END OF //============================================================================== bool JobsManager::setJobDependsOnIds(const QUuid & a_jobID, const std::vector & a_dependencies) { int index = indexOfJob(a_jobID); if(!checkCanModifyJobAndNotify(index)) return false; for(const QUuid & id : a_dependencies) { int dependencyIndex = indexOfJob(id); if((dependencyIndex < 0) || (dependencyIndex >= index)) return false; } bool result = m_tickets[index].pJob->setDependsOnJobIds(a_dependencies); if(!result) return false; saveJobs(); emit signalJobDependenciesChanged(a_jobID, a_dependencies); return true; } // END OF //============================================================================== bool JobsManager::changeJob(const JobProperties & a_jobProperties) { int index = indexOfJob(a_jobProperties.id); if(!checkCanModifyJobAndNotify(index)) return false; vsedit::Job * pJob = m_tickets[index].pJob; bool result = pJob->setProperties(a_jobProperties); if(result) result = pJob->setState(JobState::Waiting); saveJobs(); emit signalJobChanged(pJob->properties()); return result; } // END OF //============================================================================== bool JobsManager::loadJobs() { if(hasActiveJobs()) { emit signalLogMessage(tr("Can not load jobs. " "Some of current jobs are still active.", LOG_STYLE_WARNING)); return false; } if((!m_pSettingsManager) || (!m_pVSScriptLibrary)) { emit signalLogMessage(tr("Can not load jobs. " "Model is not initialized correctly.", LOG_STYLE_ERROR)); return false; } clearJobs(); std::vector jobPropertiesList = m_pSettingsManager->getJobs(); for(const JobProperties & properties : jobPropertiesList) { vsedit::Job * pJob = new vsedit::Job(properties, m_pSettingsManager, m_pVSScriptLibrary); if(vsedit::contains(ACTIVE_JOB_STATES, pJob->state())) pJob->setState(JobState::Aborted); connectJob(pJob); JobTicket ticket = {pJob, JobWantTo::Nothing}; m_tickets.push_back(ticket); } return true; } // END OF //============================================================================== bool JobsManager::saveJobs() { if(!m_pSettingsManager) { emit signalLogMessage(tr("Can not save jobs. " "Model is not initialized correctly.", LOG_STYLE_ERROR)); return false; } std::vector jobPropertiesList; for(const JobTicket & ticket : m_tickets) jobPropertiesList.push_back(ticket.pJob->properties()); bool result = m_pSettingsManager->setJobs(jobPropertiesList); if(!result) emit signalLogMessage(tr("Failed to save jobs.", LOG_STYLE_ERROR)); return result; } // END OF //============================================================================== bool JobsManager::hasActiveJobs() { for(const JobTicket & ticket : m_tickets) { if(vsedit::contains(ACTIVE_JOB_STATES, ticket.pJob->state())) return true; } return false; } // END OF //============================================================================== void JobsManager::startWaitingJobs() { startFirstReadyJob(); } // END OF //============================================================================== void JobsManager::abortActiveJobs() { for(JobTicket & ticket : m_tickets) { if(!vsedit::contains(ACTIVE_JOB_STATES, ticket.pJob->state())) continue; ticket.whenDone = JobWantTo::Nothing; ticket.pJob->abort(); } } // END OF //============================================================================== void JobsManager::pauseActiveJobs() { for(JobTicket & ticket : m_tickets) { if(ticket.pJob->state() != JobState::Running) continue; ticket.pJob->pause(); } } // END OF //============================================================================== void JobsManager::resumePausedJobs() { for(JobTicket & ticket : m_tickets) { if(ticket.pJob->state() != JobState::Paused) continue; ticket.pJob->start(); } } // END OF //============================================================================== void JobsManager::resetJobs(const std::vector & a_ids) { for(const QUuid & id : a_ids) setJobState(id, JobState::Waiting); } // END OF //============================================================================== void JobsManager::deleteJobs(const std::vector & a_ids) { std::vector deletedJobs; for(const QUuid & id : a_ids) { int index = indexOfJob(id); if(index < 0) continue; vsedit::Job * pJob = m_tickets[index].pJob; if(vsedit::contains(ACTIVE_JOB_STATES, pJob->state())) { emit signalLogMessage(tr("Can not delete an active job."), LOG_STYLE_WARNING); return; } for(const JobTicket & ticket : m_tickets) { if(vsedit::contains(ticket.pJob->dependsOnJobIds(), pJob->id())) { emit signalLogMessage(tr("Can not delete a job while " "other jobs depend on it."), LOG_STYLE_WARNING); return; } } delete pJob; m_tickets.erase(m_tickets.begin() + index); deletedJobs.push_back(id); } saveJobs(); emit signalJobsDeleted(deletedJobs); } // END OF //============================================================================== void JobsManager::slotLogMessage(const QString & a_message, const QString & a_style) { QString message = a_message; const vsedit::Job * cpJob = qobject_cast(sender()); if(cpJob) { ptrdiff_t index = indexOfJob(cpJob->id()); if(index >= 0) message = QString("Job %1: %2").arg(index + 1).arg(a_message); } emit signalLogMessage(message, a_style); } // END OF //============================================================================== void JobsManager::slotLogMessage(int a_type, const QString & a_message) { QString style = vsMessageTypeToStyleName(a_type); slotLogMessage(a_message, style); } // END OF //============================================================================== void JobsManager::slotJobPropertiesChanged() { vsedit::Job * pJob = qobject_cast(sender()); if(!pJob) return; emit signalJobChanged(pJob->properties()); } // END OF //============================================================================== void JobsManager::slotJobStateChanged(JobState a_newState, JobState a_oldState) { if(a_oldState == a_newState) return; vsedit::Job * pJob = qobject_cast(sender()); if(!pJob) return; saveJobs(); emit signalJobStateChanged(pJob->id(), a_newState); if(vsedit::contains(ACTIVE_JOB_STATES, a_newState)) return; // Recursion if(a_newState == JobState::DependencyNotMet) return; // State reset if(a_newState == JobState::Waiting) return; int jobIndex = indexOfJob(pJob->id()); if(m_tickets[jobIndex].whenDone == JobWantTo::RunNext) startFirstReadyJob(jobIndex + 1); m_tickets[jobIndex].whenDone = JobWantTo::Nothing; } // END OF //============================================================================== void JobsManager::slotJobProgressChanged() { vsedit::Job * pJob = qobject_cast(sender()); if(!pJob) return; emit signalJobProgressChanged(pJob->id(), pJob->framesProcessed(), pJob->fps()); } // END OF //============================================================================== void JobsManager::slotJobStartTimeChanged() { vsedit::Job * pJob = qobject_cast(sender()); if(!pJob) return; emit signalJobStartTimeChanged(pJob->id(), pJob->properties().timeStarted); } // END OF //============================================================================== void JobsManager::slotJobEndTimeChanged() { vsedit::Job * pJob = qobject_cast(sender()); if(!pJob) return; emit signalJobEndTimeChanged(pJob->id(), pJob->properties().timeEnded); } // END OF //============================================================================== bool JobsManager::canModifyJob(int a_index) const { if((a_index < 0) || ((size_t)a_index >= m_tickets.size())) return false; vsedit::Job * pJob = m_tickets[a_index].pJob; Q_ASSERT(pJob); if(!pJob) return false; if(vsedit::contains(ACTIVE_JOB_STATES, pJob->state())) return false; return true; } // END OF //============================================================================== int JobsManager::indexOfJob(const QUuid & a_uuid) const { std::vector::const_iterator it = std::find_if(m_tickets.cbegin(), m_tickets.cend(), [&](const JobTicket & a_ticket)->bool { return (a_ticket.pJob->id() == a_uuid); }); return (it == m_tickets.cend()) ? -1 : std::distance(m_tickets.cbegin(), it); } // END OF //============================================================================== void JobsManager::clearJobs() { for(JobTicket & ticket : m_tickets) delete ticket.pJob; m_tickets.clear(); } // END OF //============================================================================== bool JobsManager::checkCanModifyJobAndNotify(int a_index) { bool result = canModifyJob(a_index); if(!result) { emit signalLogMessage(tr("Can not modify an active job."), LOG_STYLE_WARNING); } return result; } // END OF //============================================================================== JobsManager::DependenciesState JobsManager::dependenciesState(int a_index) { if((a_index < 0) || (a_index >= (int)m_tickets.size())) return DependenciesState::Failed; JobState failStates[] = {JobState::Aborted, JobState::Aborting, JobState::DependencyNotMet, JobState::Failed, JobState::FailedCleanUp}; bool incomplete = false; for(const QUuid & id : m_tickets[a_index].pJob->dependsOnJobIds()) { int dependencyIndex = indexOfJob(id); if((dependencyIndex < 0) || (dependencyIndex >= (int)m_tickets.size())) return DependenciesState::Failed; const vsedit::Job * pDependentJob = m_tickets[dependencyIndex].pJob; if(pDependentJob->state() != JobState::Completed) incomplete = true; if(vsedit::contains(failStates, pDependentJob->state())) return DependenciesState::Failed; } if(incomplete) return DependenciesState::Incomplete; return DependenciesState::Complete; } // END OF //============================================================================== void JobsManager::connectJob(vsedit::Job * a_pJob) { connect(a_pJob, SIGNAL(signalPropertiesChanged()), this, SLOT(slotJobPropertiesChanged())); connect(a_pJob, SIGNAL(signalStateChanged(JobState, JobState)), this, SLOT(slotJobStateChanged(JobState, JobState))); connect(a_pJob, SIGNAL(signalProgressChanged()), this, SLOT(slotJobProgressChanged())); connect(a_pJob, SIGNAL(signalStartTimeChanged()), this, SLOT(slotJobStartTimeChanged())); connect(a_pJob, SIGNAL(signalEndTimeChanged()), this, SLOT(slotJobEndTimeChanged())); connect(a_pJob, SIGNAL(signalLogMessage(const QString &, const QString &)), this, SLOT(slotLogMessage(const QString &, const QString &))); } // END OF //============================================================================== void JobsManager::startFirstReadyJob(int a_fromIndex) { if((a_fromIndex < 0) || (a_fromIndex >= (int)m_tickets.size())) return; JobState validStates[] = {JobState::Waiting, JobState::Paused}; for(int i = a_fromIndex; i < (a_fromIndex + (int)m_tickets.size()); ++i) { int nextIndex = i % m_tickets.size(); vsedit::Job * pNextJob = m_tickets[nextIndex].pJob; if(pNextJob->isActive()) return; if(!vsedit::contains(validStates, pNextJob->state())) continue; DependenciesState jobDependenciesState = dependenciesState(i); if(jobDependenciesState == DependenciesState::Failed) pNextJob->setState(JobState::DependencyNotMet); if(jobDependenciesState != DependenciesState::Complete) continue; m_tickets[nextIndex].whenDone = JobWantTo::RunNext; pNextJob->start(); return; } } // END OF //============================================================================== ================================================ FILE: vsedit-job-server/src/jobs/jobs_manager.h ================================================ #ifndef JOBS_MANAGER_H_INCLUDED #define JOBS_MANAGER_H_INCLUDED #include "../../../common-src/jobs/job.h" #include "job_definitions.h" #include "../../../common-src/log/vs_editor_log_definitions.h" #include #include class SettingsManagerCore; class VSScriptLibrary; class JobsManager : public QObject { Q_OBJECT public: JobsManager(SettingsManagerCore * a_pSettingsManager, QObject * a_pParent = nullptr); virtual ~JobsManager(); std::vector jobsProperties() const; int createJob(const JobProperties & a_jobProperties = JobProperties()); bool swapJobs(const QUuid & a_jobID1, const QUuid & a_jobID2); bool setJobState(const QUuid & a_jobID, JobState a_state); bool setJobDependsOnIds(const QUuid & a_jobID, const std::vector & a_dependencies); bool changeJob(const JobProperties & a_jobProperties); bool loadJobs(); bool saveJobs(); bool hasActiveJobs(); void startWaitingJobs(); void abortActiveJobs(); void pauseActiveJobs(); void resumePausedJobs(); void resetJobs(const std::vector & a_ids); void deleteJobs(const std::vector & a_ids); signals: void signalLogMessage(const QString & a_message, const QString & a_style = LOG_STYLE_DEFAULT); void signalJobCreated(const JobProperties & a_properties); void signalJobChanged(const JobProperties & a_properties); void signalJobStateChanged(const QUuid & a_jobID, JobState a_state); void signalJobProgressChanged(const QUuid & a_jobID, int a_progress, double a_fps); void signalJobStartTimeChanged(const QUuid & a_jobID, const QDateTime & a_time); void signalJobEndTimeChanged(const QUuid & a_jobID, const QDateTime & a_time); void signalJobDependenciesChanged(const QUuid & a_jobID, const std::vector & a_dependencies); void signalJobsSwapped(const QUuid & a_jobID1, const QUuid & a_jobID2); void signalJobsDeleted(const std::vector & a_ids); private slots: void slotLogMessage(const QString & a_message, const QString & a_style = LOG_STYLE_DEFAULT); void slotLogMessage(int a_type, const QString & a_message); void slotJobPropertiesChanged(); void slotJobStateChanged(JobState a_newState, JobState a_oldState); void slotJobProgressChanged(); void slotJobStartTimeChanged(); void slotJobEndTimeChanged(); private: enum class DependenciesState { Incomplete, Complete, Failed, }; bool canModifyJob(int a_index) const; int indexOfJob(const QUuid & a_uuid) const; void clearJobs(); bool checkCanModifyJobAndNotify(int a_index); DependenciesState dependenciesState(int a_index); void connectJob(vsedit::Job * a_pJob); void startFirstReadyJob(int a_fromIndex = 0); std::vector m_tickets; SettingsManagerCore * m_pSettingsManager; VSScriptLibrary * m_pVSScriptLibrary; }; #endif // JOBS_MANAGER_H_INCLUDED ================================================ FILE: vsedit-job-server/src/main.cpp ================================================ #include "job_server.h" #include "../../common-src/application_instance_file_guard/application_instance_file_guard.h" #include "../../common-src/ipc_defines.h" #include "../../common-src/version_info.h" #include #include Q_DECLARE_OPAQUE_POINTER(const VSFrame *) Q_DECLARE_OPAQUE_POINTER(VSNode *) int main(int argc, char *argv[]) { if(argc > 1) { if(strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { print_version(); return 0; } } QCoreApplication application(argc, argv); qRegisterMetaType("const VSFrame *"); qRegisterMetaType("VSNode *"); ApplicationInstanceFileGuard guard("vsedit_job_server_running"); if(!guard.isLocked()) { qCritical("Couldn't start the server. " "Another instance is probably already running."); return 1; } JobServer jobServer; application.connect(&jobServer, &JobServer::finish, &application, &QCoreApplication::quit); bool started = jobServer.start(); if(!started) { qCritical("Couldn't start the server."); return 1; } int exitCode = application.exec(); if(!guard.unlock()) qCritical("%s", guard.error().toLocal8Bit().data()); return exitCode; } ================================================ FILE: vsedit-job-server-watcher/src/connect_to_server_dialog.cpp ================================================ #include "connect_to_server_dialog.h" #include "../../common-src/settings/settings_manager.h" #include ConnectToServerDialog::ConnectToServerDialog( SettingsManager * a_pSettingsManager, QWidget * a_pParent) : QDialog(a_pParent) , m_pSettingsManager(a_pSettingsManager) { Q_ASSERT(m_pSettingsManager); m_ui.setupUi(this); connect(m_ui.connectButton, SIGNAL(clicked()), this, SLOT(slotConnectButtonClicked())); connect(m_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); } ConnectToServerDialog::~ConnectToServerDialog() { } int ConnectToServerDialog::call(const QHostAddress & a_address) { m_ui.serverAddressComboBox->clear(); QStringList recentServers = m_pSettingsManager->getRecentJobServers(); m_ui.serverAddressComboBox->addItems(recentServers); m_ui.serverAddressComboBox->setCurrentText(a_address.toString()); return exec(); } void ConnectToServerDialog::slotConnectButtonClicked() { QString address = m_ui.serverAddressComboBox->currentText(); QHostAddress host(address); if(host.isNull()) return; int index = -1; while((index = m_ui.serverAddressComboBox->findText(address)) >= 0) { m_ui.serverAddressComboBox->removeItem(index); } m_ui.serverAddressComboBox->insertItem(0, address); saveServersList(); accept(); emit signalConnectToServer(host); } void ConnectToServerDialog::saveServersList() { QStringList recentServers; for(int i = 0; i < m_ui.serverAddressComboBox->count(); ++i) { QString address = m_ui.serverAddressComboBox->itemText(i); if(QHostAddress(address).isNull()) continue; recentServers << address; } m_pSettingsManager->setRecentJobServers(recentServers); } ================================================ FILE: vsedit-job-server-watcher/src/connect_to_server_dialog.h ================================================ #ifndef CONNECT_TO_SERVER_DIALOG_H_INCLUDED #define CONNECT_TO_SERVER_DIALOG_H_INCLUDED #include #include class SettingsManager; class ConnectToServerDialog : public QDialog { Q_OBJECT public: ConnectToServerDialog(SettingsManager * a_pSettingsManager, QWidget * a_pParent = nullptr); virtual ~ConnectToServerDialog(); int call(const QHostAddress & a_address); signals: void signalConnectToServer(const QHostAddress & a_address); private slots: void slotConnectButtonClicked(); private: void saveServersList(); Ui::ConnectToServerDialog m_ui; SettingsManager * m_pSettingsManager; }; #endif // CONNECT_TO_SERVER_DIALOG_H_INCLUDED ================================================ FILE: vsedit-job-server-watcher/src/connect_to_server_dialog.ui ================================================ ConnectToServerDialog 0 0 462 67 Connect to server :/link.png:/link.png 4 4 4 4 4 Server address: 0 0 Connect true QComboBox::InsertAtTop 0 0 Cancel true ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_dependencies_delegate.cpp ================================================ #include "job_dependencies_delegate.h" #include "jobs_model.h" #include "../../../common-src/helpers.h" #include #include #include //============================================================================== JobDependenciesDelegate::JobDependenciesDelegate(QObject * a_pParent) : QStyledItemDelegate(a_pParent) { } // END OF JobDependenciesDelegate::JobDependenciesDelegate(QObject * a_pParent) //============================================================================== JobDependenciesDelegate::~JobDependenciesDelegate() { } // END OF JobDependenciesDelegate::~JobDependenciesDelegate() //============================================================================== QWidget * JobDependenciesDelegate::createEditor(QWidget * a_pParent, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const { (void)a_pParent; (void)a_option; (void)a_index; QListWidget * pListWidget = new QListWidget(); pListWidget->setWindowFlags(Qt::FramelessWindowHint); return pListWidget; } // END OF QWidget * JobDependenciesDelegate::createEditor(QWidget * a_pParent, // const QStyleOptionViewItem & a_option, // const QModelIndex & a_index) const //============================================================================== void JobDependenciesDelegate::setEditorData(QWidget * a_pEditor, const QModelIndex & a_index) const { QListWidget * pListWidget = qobject_cast(a_pEditor); if(!pListWidget) return; const JobsModel * cpModel = qobject_cast(a_index.model()); if(!cpModel) return; JobProperties properties = cpModel->jobProperties(a_index.row()); std::vector dependencyIds = properties.dependsOnJobIds; pListWidget->clear(); for(int i = 0; i < a_index.row(); ++i) { QString name = tr("Job %1").arg(i + 1); QListWidgetItem * pItem = new QListWidgetItem(name, pListWidget); QUuid id = cpModel->jobProperties(i).id; pItem->setData(Qt::UserRole, id); pItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable); bool depends = vsedit::contains(dependencyIds, id); pItem->setCheckState(depends ? Qt::Checked : Qt::Unchecked); } } // END OF void JobDependenciesDelegate::setEditorData(QWidget * a_pEditor, // const QModelIndex & a_index) const //============================================================================== void JobDependenciesDelegate::setModelData(QWidget * a_pEditor, QAbstractItemModel * a_pModel, const QModelIndex & a_index) const { QListWidget * pListWidget = qobject_cast(a_pEditor); if(!pListWidget) return; JobsModel * pModel = qobject_cast(a_pModel); if(!pModel) return; JobProperties properties = pModel->jobProperties(a_index.row()); std::vector dependencyIds; for(int i = 0; i < pListWidget->count(); ++i) { QListWidgetItem * pItem = pListWidget->item(i); if(pItem->checkState() != Qt::Checked) continue; QUuid id = pItem->data(Qt::UserRole).toUuid(); dependencyIds.push_back(id); } pModel->requestJobDependsOnIds(properties.id, dependencyIds); } // END OF void JobDependenciesDelegate::setModelData(QWidget * a_pEditor, // QAbstractItemModel * a_pModel, const QModelIndex & a_index) const //============================================================================== void JobDependenciesDelegate::updateEditorGeometry(QWidget * a_pEditor, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const { (void)a_index; QListWidget * pListWidget = qobject_cast(a_pEditor); if(!pListWidget) return; QPoint origin = a_option.widget->mapToGlobal(a_option.rect.topLeft()); const QTableView * cpTableView = qobject_cast(a_option.widget); if(cpTableView) { int headerWidth = 0; if(cpTableView->verticalHeader()->isVisible()) headerWidth = cpTableView->verticalHeader()->width(); int headerHeight = 0; if(cpTableView->horizontalHeader()->isVisible()) headerHeight = cpTableView->horizontalHeader()->height(); origin += QPoint(headerWidth, headerHeight); } pListWidget->move(origin); } // END OF void JobDependenciesDelegate::updateEditorGeometry( // QWidget * a_pEditor, const QStyleOptionViewItem & a_option, // const QModelIndex & a_index) const //============================================================================== ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_dependencies_delegate.h ================================================ #ifndef JOB_DEPENDENCIES_DELEGATE_H_INCLUDED #define JOB_DEPENDENCIES_DELEGATE_H_INCLUDED #include class JobDependenciesDelegate : public QStyledItemDelegate { Q_OBJECT public: JobDependenciesDelegate(QObject * a_pParent = nullptr); virtual ~JobDependenciesDelegate(); virtual QWidget * createEditor(QWidget * a_pParent, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const override; virtual void setEditorData(QWidget * a_pEditor, const QModelIndex & a_index) const override; virtual void setModelData(QWidget * a_pEditor, QAbstractItemModel * a_pModel, const QModelIndex & a_index) const override; virtual void updateEditorGeometry(QWidget * a_pEditor, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const override; }; #endif // JOB_DEPENDENCIES_DELEGATE_H_INCLUDED ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_edit_dialog.cpp ================================================ #include "job_edit_dialog.h" #include "../../../common-src/settings/settings_manager.h" #include "../../../common-src/vapoursynth/vapoursynth_script_processor.h" #include "../../../common-src/jobs/job_variables.h" #include "../../../common-src/helpers.h" #include #include #include #include #include //============================================================================== JobEditDialog::JobEditDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent) : QDialog(a_pParent, Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint) , m_pSettingsManager(a_pSettingsManager) , m_pVSScriptLibrary(a_pVSScriptLibrary) { vsedit::disableFontKerning(this); m_ui.setupUi(this); JobType jobTypes[] = {JobType::EncodeScriptCLI, JobType::RunProcess, JobType::RunShellCommand}; for(const JobType & jobType : jobTypes) m_ui.jobTypeComboBox->addItem(JobProperties::typeName(jobType), (int)jobType); m_ui.jobTypeComboBox->setCurrentIndex(0); slotJobTypeChanged(m_ui.jobTypeComboBox->currentIndex()); m_ui.encodingHeaderTypeComboBox->addItem(tr("No header"), (int)EncodingHeaderType::NoHeader); m_ui.encodingHeaderTypeComboBox->addItem(tr("Y4M"), (int)EncodingHeaderType::Y4M); m_ui.encodingFirstFrameSpinBox->setMaximum(std::numeric_limits::max()); m_ui.encodingLastFrameSpinBox->setMaximum(std::numeric_limits::max()); setUpEncodingPresets(); connect(m_ui.jobTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotJobTypeChanged(int))); connect(m_ui.encodingScriptBrowseButton, SIGNAL(clicked()), this, SLOT(slotEncodingScriptBrowseButtonClicked())); connect(m_ui.encodingPresetComboBox, SIGNAL(textActivated(const QString &)), this, SLOT(slotEncodingPresetComboBoxActivated(const QString &))); connect(m_ui.encodingPresetSaveButton, SIGNAL(clicked()), this, SLOT(slotEncodingPresetSaveButtonClicked())); connect(m_ui.encodingPresetDeleteButton, SIGNAL(clicked()), this, SLOT(slotEncodingPresetDeleteButton())); connect(m_ui.encodingExecutableBrowseButton, SIGNAL(clicked()), this, SLOT(slotEncodingExecutableBrowseButtonClicked())); connect(m_ui.encodingArgumentsHelpButton, SIGNAL(clicked()), this, SLOT(slotEncodingArgumentsHelpButtonClicked())); connect(m_ui.encodingFramesFromVideoButton, SIGNAL(clicked()), this, SLOT(slotEncodingFramesFromVideoButtonClicked())); connect(m_ui.processExecutableBrowseButton, SIGNAL(clicked()), this, SLOT(slotProcessExecutableBrowseButtonClicked())); connect(m_ui.jobSaveButton, SIGNAL(clicked()), this, SLOT(accept())); connect(m_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); } // END OF JobEditDialog::JobEditDialog(SettingsManager * a_pSettingsManager, // QWidget * a_pParent) //============================================================================== JobEditDialog::~JobEditDialog() { } // END OF JobEditDialog::~JobEditDialog() //============================================================================== JobProperties JobEditDialog::jobProperties() const { JobProperties newProperties; newProperties.type = (JobType)m_ui.jobTypeComboBox->currentData().toInt(); newProperties.scriptName = m_ui.encodingScriptPathEdit->text(); newProperties.encodingHeaderType = (EncodingHeaderType)m_ui .encodingHeaderTypeComboBox->currentData().toInt(); if(newProperties.type == JobType::EncodeScriptCLI) { newProperties.executablePath = m_ui.encodingExecutablePathEdit->text(); newProperties.arguments = m_ui.encodingArgumentsTextEdit->toPlainText(); } else if(newProperties.type == JobType::RunProcess) { newProperties.executablePath = m_ui.processExecutablePathEdit->text(); newProperties.arguments = m_ui.processArgumentsTextEdit->toPlainText(); } newProperties.shellCommand = m_ui.shellCommandTextEdit->toPlainText(); newProperties.firstFrame = m_ui.encodingFirstFrameSpinBox->value(); newProperties.lastFrame = m_ui.encodingLastFrameSpinBox->value(); return newProperties; } // END OF JobProperties JobEditDialog::jobProperties() const //============================================================================== int JobEditDialog::call(const QString & a_title, const JobProperties & a_jobProperties) { setUpEncodingPresets(); setWindowTitle(a_title); int index = m_ui.jobTypeComboBox->findData((int)a_jobProperties.type); m_ui.jobTypeComboBox->setCurrentIndex(index); m_ui.encodingScriptPathEdit->setText(a_jobProperties.scriptName); m_ui.encodingPresetComboBox->clearEditText(); index = m_ui.encodingHeaderTypeComboBox->findData( (int)a_jobProperties.encodingHeaderType); m_ui.encodingHeaderTypeComboBox->setCurrentIndex(index); m_ui.encodingExecutablePathEdit->setText(a_jobProperties.executablePath); m_ui.encodingArgumentsTextEdit->setPlainText(a_jobProperties.arguments); m_ui.processExecutablePathEdit->setText(a_jobProperties.executablePath); m_ui.processArgumentsTextEdit->setPlainText(a_jobProperties.arguments); m_ui.shellCommandTextEdit->setPlainText(a_jobProperties.shellCommand); m_ui.encodingFirstFrameSpinBox->setValue(a_jobProperties.firstFrame); m_ui.encodingLastFrameSpinBox->setValue(a_jobProperties.lastFrame); return exec(); } // END OF int JobEditDialog::call(const QString & a_title, // const JobProperties & a_jobProperties) //============================================================================== void JobEditDialog::slotJobTypeChanged(int a_index) { std::map panels = { {JobType::EncodeScriptCLI, m_ui.encodingPanel}, {JobType::RunProcess, m_ui.processPanel}, {JobType::RunShellCommand, m_ui.shellCommandPanel}, }; JobType jobType = (JobType)m_ui.jobTypeComboBox->itemData(a_index).toInt(); for(const std::pair & pair : panels) { if(pair.first == jobType) pair.second->setVisible(true); else pair.second->setVisible(false); } } // END OF void JobEditDialog::slotJobTypeChanged(int a_index) //============================================================================== void JobEditDialog::slotEncodingScriptBrowseButtonClicked() { QFileDialog fileDialog(this); fileDialog.setWindowTitle(tr("Open VapourSynth script")); fileDialog.setNameFilter( tr("VapourSynth script (*.vpy);;All files (*)")); QString path = m_ui.encodingScriptPathEdit->text(); if(path.isEmpty()) path = m_pSettingsManager->getLastUsedPath(); QFileInfo fileInfo(path); QString dirPath = fileInfo.absoluteDir().path(); fileDialog.setDirectory(dirPath); fileDialog.selectFile(fileInfo.fileName()); if(!fileDialog.exec()) return; QStringList filesList = fileDialog.selectedFiles(); m_ui.encodingScriptPathEdit->setText(filesList[0]); m_pSettingsManager->setLastUsedPath(filesList[0]); } // END OF void JobEditDialog::slotEncodingScriptBrowseButtonClicked() //============================================================================== void JobEditDialog::slotEncodingPresetComboBoxActivated(const QString & a_text) { if(a_text.isEmpty()) { m_ui.encodingExecutablePathEdit->clear(); m_ui.encodingArgumentsTextEdit->clear(); return; } EncodingPreset preset(a_text); std::vector::iterator it = std::find( m_encodingPresets.begin(), m_encodingPresets.end(), preset); if(it == m_encodingPresets.end()) return; preset = *it; m_ui.encodingExecutablePathEdit->setText(preset.executablePath); m_ui.encodingArgumentsTextEdit->setPlainText(preset.arguments); int headerTypeIndex = m_ui.encodingHeaderTypeComboBox->findData((int)preset.headerType); if(headerTypeIndex < 0) headerTypeIndex = 0; m_ui.encodingHeaderTypeComboBox->setCurrentIndex(headerTypeIndex); } // END OF void JobEditDialog::slotEncodingPresetComboBoxActivated( // const QString & a_text) //============================================================================== void JobEditDialog::slotEncodingPresetSaveButtonClicked() { EncodingPreset preset(m_ui.encodingPresetComboBox->currentText()); if(preset.name.isEmpty()) { QMessageBox::warning(this, tr("Preset save error."), tr("Preset name must not be empty.")); return; } if(preset.type == EncodingType::CLI) { preset.executablePath = m_ui.encodingExecutablePathEdit->text(); if(preset.executablePath.isEmpty()) { QMessageBox::warning(this, tr("Preset save error."), tr("Executable path must not be empty.")); return; } preset.arguments = m_ui.encodingArgumentsTextEdit->toPlainText(); } preset.headerType = (EncodingHeaderType) m_ui.encodingHeaderTypeComboBox->currentData().toInt(); bool success = m_pSettingsManager->saveEncodingPreset(preset); if(!success) { QMessageBox::critical(this, tr("Preset save error."), tr("Error saving encoding preset.")); return; } std::vector::iterator it = std::find( m_encodingPresets.begin(), m_encodingPresets.end(), preset); if(it == m_encodingPresets.end()) { Q_ASSERT(m_ui.encodingPresetComboBox->findText(preset.name) == -1); m_encodingPresets.push_back(preset); m_ui.encodingPresetComboBox->addItem(preset.name); m_ui.encodingPresetComboBox->model()->sort(0); } else { Q_ASSERT(m_ui.encodingPresetComboBox->findText(preset.name) != -1); *it = preset; } } // END OF void JobEditDialog::slotEncodingPresetSaveButtonClicked() //============================================================================== void JobEditDialog::slotEncodingPresetDeleteButton() { EncodingPreset preset(m_ui.encodingPresetComboBox->currentText()); if(preset.name.isEmpty()) return; QMessageBox quesBox(this); vsedit::disableFontKerning(&quesBox); quesBox.setWindowTitle(tr("Delete preset")); quesBox.setText(tr("Do you really want to delete " "preset \'%1\'?").arg(preset.name)); quesBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); quesBox.setDefaultButton(QMessageBox::No); int result = quesBox.exec(); if(result == QMessageBox::No) return; std::vector::iterator it = std::find( m_encodingPresets.begin(), m_encodingPresets.end(), preset); if(it == m_encodingPresets.end()) { Q_ASSERT(m_ui.encodingPresetComboBox->findText(preset.name) == -1); QMessageBox::critical(this, tr("Preset delete error."), tr("Error deleting preset. Preset was never saved.")); return; } int index = m_ui.encodingPresetComboBox->findText(preset.name); Q_ASSERT(index != -1); m_ui.encodingPresetComboBox->removeItem(index); m_encodingPresets.erase(it); m_ui.encodingPresetComboBox->setCurrentIndex(0); slotEncodingPresetComboBoxActivated( m_ui.encodingPresetComboBox->currentText()); bool success = m_pSettingsManager->deleteEncodingPreset(preset.name); if(!success) { QMessageBox::critical(this, tr("Preset delete error."), tr("Error deleting preset \'%1\'.").arg(preset.name)); return; } } // END OF void JobEditDialog::slotEncodingPresetDeleteButton() //============================================================================== void JobEditDialog::slotEncodingExecutableBrowseButtonClicked() { QString executablePath = chooseExecutable( tr("Choose encoder executable"), m_ui.encodingExecutablePathEdit->text()); if(!executablePath.isEmpty()) m_ui.encodingExecutablePathEdit->setText(executablePath); } // END OF void JobEditDialog::slotEncodingExecutableBrowseButtonClicked() //============================================================================== void JobEditDialog::slotEncodingFramesFromVideoButtonClicked() { QString scriptName = m_ui.encodingScriptPathEdit->text(); QString absoluteScriptPath = vsedit::resolvePathFromApplication(scriptName); QFile scriptFile(absoluteScriptPath); bool opened = scriptFile.open(QIODevice::ReadOnly); if(!opened) return; QString script = QString::fromUtf8(scriptFile.readAll()); scriptFile.close(); VapourSynthScriptProcessor processor(m_pSettingsManager, m_pVSScriptLibrary); bool initialized = processor.initialize(script, scriptName, 0, ProcessReason::Encode); if(!initialized) return; const VSVideoInfo * cpVideoInfo = processor.nodeInfo().getAsVideo(); m_ui.encodingFirstFrameSpinBox->setValue(0); m_ui.encodingLastFrameSpinBox->setValue(cpVideoInfo->numFrames - 1); } // END OF void JobEditDialog::slotEncodingFramesFromVideoButtonClicked() //============================================================================== void JobEditDialog::slotEncodingArgumentsHelpButtonClicked() { JobVariables variables; QString argumentsHelpString = tr("Use following placeholders:"); for(const vsedit::VariableToken & variable : variables.variables()) { argumentsHelpString += QString("\n%1 - %2") .arg(variable.token).arg(variable.description); } QString title = tr("Encoder arguments"); QMessageBox msgBox(this); msgBox.setWindowTitle(title); msgBox.setText(argumentsHelpString); vsedit::disableFontKerning(&msgBox); msgBox.exec(); } // END OF void JobEditDialog::slotEncodingArgumentsHelpButtonClicked() //============================================================================== void JobEditDialog::slotProcessExecutableBrowseButtonClicked() { QString executablePath = chooseExecutable( tr("Choose process executable"), m_ui.processExecutablePathEdit->text()); if(!executablePath.isEmpty()) m_ui.processExecutablePathEdit->setText(executablePath); } // END OF void JobEditDialog::slotProcessExecutableBrowseButtonClicked() //============================================================================== void JobEditDialog::setUpEncodingPresets() { m_ui.encodingPresetComboBox->clear(); m_encodingPresets = m_pSettingsManager->getAllEncodingPresets(); for(const EncodingPreset & preset : m_encodingPresets) m_ui.encodingPresetComboBox->addItem(preset.name); m_ui.encodingPresetComboBox->setCurrentIndex(0); slotEncodingPresetComboBoxActivated( m_ui.encodingPresetComboBox->currentText()); } // END OF void JobEditDialog::setUpEncodingPresets() //============================================================================== QString JobEditDialog::chooseExecutable(const QString & a_dialogTitle, const QString & a_initialPath) { QString applicationPath = QCoreApplication::applicationDirPath(); QFileDialog fileDialog; fileDialog.setWindowTitle(a_dialogTitle); if(a_initialPath.isEmpty()) fileDialog.setDirectory(applicationPath); else fileDialog.selectFile(a_initialPath); #ifdef Q_OS_WIN fileDialog.setNameFilter("*.exe"); #endif if(!fileDialog.exec()) return QString(); QStringList filesList = fileDialog.selectedFiles(); return filesList[0]; } // END OF QString JobEditDialog::chooseExecutable(const QString & a_dialogTitle, // const QString & a_initialPath) //============================================================================== ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_edit_dialog.h ================================================ #ifndef JOB_EDIT_DIALOG_H_INCLUDED #define JOB_EDIT_DIALOG_H_INCLUDED #include #include "../../../common-src/settings/settings_definitions_core.h" class SettingsManager; class VSScriptLibrary; class JobEditDialog : public QDialog { Q_OBJECT public: JobEditDialog(SettingsManager * a_pSettingsManager, VSScriptLibrary * a_pVSScriptLibrary, QWidget * a_pParent = nullptr); virtual ~JobEditDialog(); JobProperties jobProperties() const; public slots: int call(const QString & a_title, const JobProperties & a_jobProperties); private slots: void slotJobTypeChanged(int a_index); void slotEncodingScriptBrowseButtonClicked(); void slotEncodingPresetComboBoxActivated(const QString & a_text); void slotEncodingPresetSaveButtonClicked(); void slotEncodingPresetDeleteButton(); void slotEncodingExecutableBrowseButtonClicked(); void slotEncodingFramesFromVideoButtonClicked(); void slotEncodingArgumentsHelpButtonClicked(); void slotProcessExecutableBrowseButtonClicked(); private: void setUpEncodingPresets(); QString chooseExecutable(const QString & a_dialogTitle, const QString & a_initialPath = QString()); Ui::JobEditDialog m_ui; SettingsManager * m_pSettingsManager; VSScriptLibrary * m_pVSScriptLibrary; std::vector m_encodingPresets; }; #endif // JOB_EDIT_DIALOG_H_INCLUDED ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_edit_dialog.ui ================================================ JobEditDialog 0 0 566 466 :/jobs.png:/jobs.png 4 4 4 4 4 4 Job type: Qt::Horizontal 40 20 2 0 0 0 0 4 Script file: :/folder.png:/folder.png 4 Preset: 0 0 true QComboBox::NoInsert Save Delete 4 Header: QComboBox::AdjustToContents Executable: :/folder.png:/folder.png 4 Arguments (newlines will be replaced with spaces): :/information.png:/information.png Qt::Horizontal 40 20 4 Frames from Min -1 to Max -1 From video Qt::Horizontal 40 20 4 0 0 0 0 4 Executable: :/folder.png:/folder.png Arguments (newlines will be replaced with spaces): 4 0 0 0 0 Shell command (newlines will be replaced with spaces): 4 Qt::Horizontal 40 20 Save Cancel true ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_state_delegate.cpp ================================================ #include "job_state_delegate.h" #include "jobs_model.h" #include "../../../common-src/helpers.h" #include JobStateDelegate::JobStateDelegate(QObject * a_pParent) : QStyledItemDelegate(a_pParent) { } JobStateDelegate::~JobStateDelegate() { } void JobStateDelegate::paint(QPainter * a_pPainter, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const { a_pPainter->save(); const JobsModel * cpModel = qobject_cast(a_index.model()); Q_ASSERT(cpModel); Q_ASSERT(a_index.column() == JobsModel::STATE_COLUMN); JobProperties properties = cpModel->jobProperties(a_index.row()); JobState state = properties.jobState; QColor cellColor = jobStateColor(state, a_option); JobState noProgressBarStates[] = {JobState::Waiting, JobState::DependencyNotMet}; bool drawProgress = (properties.type == JobType::EncodeScriptCLI) && (!vsedit::contains(noProgressBarStates, state)); a_pPainter->setPen(cellColor); a_pPainter->setBrush(a_option.palette.color(QPalette::Base)); QRect innerRect = a_option.rect; innerRect.setWidth(a_option.rect.width() - 1); innerRect.setHeight(a_option.rect.height() - 1); if(drawProgress) { int progressWidth = properties.framesProcessed * innerRect.width() / properties.framesTotal(); QRect progressRect = innerRect; progressRect.setWidth(progressWidth); a_pPainter->drawRect(progressRect); QRect blankRect = innerRect; blankRect.setWidth(innerRect.width() - progressWidth); blankRect.translate(progressWidth, 0); a_pPainter->setBrush(a_option.palette.color(QPalette::Base)); a_pPainter->drawRect(blankRect); } else a_pPainter->drawRect(innerRect); QString stateText = cpModel->data(a_index).toString(); if(drawProgress) { stateText += QString(" %1 / %2").arg(properties.framesProcessed) .arg(properties.framesTotal()); } a_pPainter->setPen(a_option.palette.color(QPalette::Text)); a_pPainter->setFont(a_option.font); a_pPainter->drawText(innerRect, Qt::AlignCenter | Qt::TextWordWrap, stateText); a_pPainter->restore(); } QColor JobStateDelegate::jobStateColor(JobState a_state, const QStyleOptionViewItem & a_option) const { switch(a_state) { case JobState::Aborted: case JobState::Failed: case JobState::DependencyNotMet: return QColor("#ffcccc"); case JobState::Aborting: case JobState::FailedCleanUp: return QColor("#ffeeee"); case JobState::Pausing: case JobState::Paused: return QColor("#fffddd"); case JobState::CompletedCleanUp: case JobState::Completed: return QColor("#ddffdd"); case JobState::Running: return QColor("#ddeeff"); default: return a_option.palette.color(QPalette::Base); } } ================================================ FILE: vsedit-job-server-watcher/src/jobs/job_state_delegate.h ================================================ #ifndef JOB_STATE_DELEGATE_H_INCLUDED #define JOB_STATE_DELEGATE_H_INCLUDED #include "../../../common-src/settings/settings_definitions_core.h" #include class JobStateDelegate : public QStyledItemDelegate { Q_OBJECT public: JobStateDelegate(QObject * a_pParent = nullptr); virtual ~JobStateDelegate(); virtual void paint(QPainter * a_pPainter, const QStyleOptionViewItem & a_option, const QModelIndex & a_index) const override; protected: virtual QColor jobStateColor(JobState a_state, const QStyleOptionViewItem & a_option) const; }; #endif // JOB_STATE_DELEGATE_H_INCLUDED ================================================ FILE: vsedit-job-server-watcher/src/jobs/jobs_model.cpp ================================================ #include "jobs_model.h" #include "../../../common-src/settings/settings_manager.h" #include "../../../common-src/helpers.h" #include //============================================================================== const int JobsModel::NAME_COLUMN = 0; const int JobsModel::TYPE_COLUMN = 1; const int JobsModel::SUBJECT_COLUMN = 2; const int JobsModel::STATE_COLUMN = 3; const int JobsModel::DEPENDS_ON_COLUMN = 4; const int JobsModel::TIME_START_COLUMN = 5; const int JobsModel::TIME_END_COLUMN = 6; const int JobsModel::FPS_COLUMN = 7; const int JobsModel::COLUMNS_NUMBER = 8; //============================================================================== JobsModel::JobsModel(SettingsManager * a_pSettingsManager, QObject * a_pParent) : QAbstractItemModel(a_pParent) , m_pSettingsManager(a_pSettingsManager) , m_fpsDisplayPrecision(DEFAULT_FPS_DISPLAY_PRECISION) { Q_ASSERT(m_pSettingsManager); } // END OF JobsModel::JobsModel(SettingsManager * a_pSettingsManager, // QObject * a_pParent) //============================================================================== JobsModel::~JobsModel() { } // END OF JobsModel::~JobsModel() //============================================================================== QModelIndex JobsModel::index(int a_row, int a_column, const QModelIndex & a_parent) const { (void)a_parent; return createIndex(a_row, a_column); } // END OF QModelIndex JobsModel::index(int a_row, int a_column, // const QModelIndex & a_parent) const //============================================================================== QModelIndex JobsModel::parent(const QModelIndex & a_child) const { (void)a_child; return QModelIndex(); } // END OF QModelIndex JobsModel::parent(const QModelIndex & a_child) const //============================================================================== Qt::ItemFlags JobsModel::flags(const QModelIndex & a_index) const { if (!a_index.isValid()) return Qt::NoItemFlags; int row = a_index.row(); int column = a_index.column(); if((row >= (int)m_jobs.size()) || (column >= COLUMNS_NUMBER)) return Qt::NoItemFlags; Qt::ItemFlags cellFlags = Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable ; bool modifiable = canModifyJob(row); if((a_index.column() == DEPENDS_ON_COLUMN) && modifiable && (row > 0)) cellFlags |= Qt::ItemIsEditable; return cellFlags; } // END OF Qt::ItemFlags JobsModel::flags(const QModelIndex & a_index) const //============================================================================== QVariant JobsModel::headerData(int a_section, Qt::Orientation a_orientation, int a_role) const { if(a_orientation != Qt::Horizontal) return QVariant(); if((a_role != Qt::DisplayRole) && (a_role != Qt::ToolTipRole)) return QVariant(); switch(a_section) { case NAME_COLUMN: return tr("Name"); case TYPE_COLUMN: return tr("Type"); case SUBJECT_COLUMN: return tr("Subject"); case STATE_COLUMN: return tr("State"); case DEPENDS_ON_COLUMN: return tr("Depends on jobs"); case TIME_START_COLUMN: return tr("Started"); case TIME_END_COLUMN: return tr("Ended"); case FPS_COLUMN: return tr("FPS"); default: return QVariant(); } return QVariant(); } // END OF QVariant JobsModel::headerData(int a_section, // Qt::Orientation a_orientation, int a_role) const //============================================================================== QVariant JobsModel::data(const QModelIndex & a_index, int a_role) const { if(!a_index.isValid()) return QVariant(); int row = a_index.row(); int column = a_index.column(); if((a_index.row() >= (int)m_jobs.size()) || (a_index.column() >= COLUMNS_NUMBER)) return QVariant(); const QString dateTimeFormat = "yyyy-MM-dd\nhh:mm:ss.z"; if((a_role == Qt::DisplayRole) || (a_role == Qt::ToolTipRole)) { if(column == NAME_COLUMN) return tr("Job %1").arg(row + 1); else if(column == TYPE_COLUMN) return JobProperties::typeName(m_jobs[row].type); else if(column == SUBJECT_COLUMN) return m_jobs[row].subject(); else if(column == STATE_COLUMN) return JobProperties::stateName(m_jobs[row].jobState); else if(column == DEPENDS_ON_COLUMN) { QStringList dependsList; for(const QUuid & id : m_jobs[row].dependsOnJobIds) { ptrdiff_t index = indexOfJob(id); if(index < 0) dependsList << tr(""); else dependsList << tr("Job %1").arg(index + 1); } return dependsList.join(", "); } else if(column == TIME_START_COLUMN) { QDateTime timeStarted = m_jobs[row].timeStarted; if(timeStarted != QDateTime()) return timeStarted.toLocalTime().toString(dateTimeFormat); } else if(column == TIME_END_COLUMN) { QDateTime timeStarted = m_jobs[row].timeEnded; if(timeStarted != QDateTime()) return timeStarted.toLocalTime().toString(dateTimeFormat); } else if((column == FPS_COLUMN) && (m_jobs[row].type == JobType::EncodeScriptCLI) && (m_jobs[row].framesProcessed > 0)) { QString fps = QString::number(m_jobs[row].fps, 'f', m_fpsDisplayPrecision); int framesTotal = m_jobs[row].framesTotal(); if(vsedit::contains(ACTIVE_JOB_STATES, m_jobs[row].jobState) && (m_jobs[row].framesProcessed < framesTotal)) { int framesLeft = framesTotal - m_jobs[row].framesProcessed; double secondsToFinish = (double)framesLeft / m_jobs[row].fps; fps += "\n"; fps += vsedit::timeToString(secondsToFinish); } return fps; } } else if(a_role == Qt::TextAlignmentRole) { const int centeredColumns[] = {STATE_COLUMN, TIME_START_COLUMN, TIME_END_COLUMN}; if(vsedit::contains(centeredColumns, column)) return Qt::AlignCenter; } return QVariant(); } // END OF QVariant JobsModel::data(const QModelIndex & a_index, int a_role) // const //============================================================================== int JobsModel::rowCount(const QModelIndex & a_parent) const { (void)a_parent; return (int)m_jobs.size(); } // END OF int JobsModel::rowCount(const QModelIndex & a_parent) const //============================================================================== int JobsModel::columnCount(const QModelIndex & a_parent) const { (void)a_parent; return COLUMNS_NUMBER; } // END OF int JobsModel::columnCount(const QModelIndex & a_parent) const //============================================================================== bool JobsModel::setData(const QModelIndex & a_index, const QVariant & a_value, int a_role) { (void)a_role; if(!a_index.isValid()) return false; int row = a_index.row(); int column = a_index.column(); if((row >= (int)m_jobs.size()) || (column != DEPENDS_ON_COLUMN)) return false; if(!a_value.canConvert()) return false; QVariantList variantList = a_value.toList(); std::vector ids; for(const QVariant & variant : variantList) { if(!variant.canConvert()) return false; QUuid id = variant.toUuid(); int index = indexOfJob(id); if((index < 0) || (index > row)) return false; ids.push_back(id); } emit signalSetDependencies(m_jobs[row].id, ids); return true; } // END OF bool JobsModel::setData(const QModelIndex & a_index, // const QVariant & a_value, int a_role) //============================================================================== void JobsModel::clear() { if(m_jobs.empty()) return; beginRemoveRows(QModelIndex(), 0, (int)m_jobs.size() - 1); m_jobs.clear(); endRemoveRows(); } // END OF void JobsModel::clear() //============================================================================== std::vector JobsModel::jobs() const { return m_jobs; } // END OF std::vector JobsModel::jobs() const //============================================================================== bool JobsModel::setJobs(const std::vector & a_jobs) { beginResetModel(); m_jobs = a_jobs; endResetModel(); return true; } // END OF bool JobsModel::setJobs(const std::vector & a_jobs) //============================================================================== JobProperties JobsModel::jobProperties(int a_index) const { if((a_index < 0) || ((size_t)a_index >= m_jobs.size())) return JobProperties(); return m_jobs[a_index]; } // END OF JobProperties JobsModel::jobProperties(int a_index) const //============================================================================== int JobsModel::createJob(const JobProperties & a_jobProperties) { int newRow = (int)m_jobs.size(); beginInsertRows(QModelIndex(), newRow, newRow); m_jobs.push_back(a_jobProperties); endInsertRows(); return newRow; } // END OF int JobsModel::createJob(const JobProperties & a_jobProperties) //============================================================================== bool JobsModel::swapJobs(const QUuid & a_id1, const QUuid & a_id2) { int index1 = indexOfJob(a_id1); if(index1 < 0) return false; int index2 = indexOfJob(a_id2); if(index2 < 0) return false; std::swap(m_jobs[index1], m_jobs[index2]); notifyJobUpdated(index1); notifyJobUpdated(index2); return true; } // END OF bool JobsModel::swapJobs(const QUuid & a_id1, const QUuid & a_id2) //============================================================================== bool JobsModel::deleteJobs(std::vector a_ids) { for(const QUuid & id : a_ids) { int index = indexOfJob(id); if(index < 0) continue; beginRemoveRows(QModelIndex(), index, index); m_jobs.erase(m_jobs.begin() + index); endRemoveRows(); } return true; } // END OF bool JobsModel::deleteJobs(std::vector a_ids) //============================================================================== bool JobsModel::updateJobProperties(const JobProperties & a_jobProperties) { int index = indexOfJob(a_jobProperties.id); if(index < 0) return false; m_jobs[index] = a_jobProperties; notifyJobUpdated(index); return true; } // END OF bool JobsModel::updateJobProperties( // const JobProperties & a_jobProperties) //============================================================================== bool JobsModel::setJobDependsOnIds(const QUuid & a_id, const std::vector & a_dependencies) { int index = indexOfJob(a_id); if(index < 0) return false; m_jobs[index].dependsOnJobIds = a_dependencies; notifyJobUpdated(index, DEPENDS_ON_COLUMN); return true; } // END OF bool JobsModel::setJobDependsOnIds(const QUuid & a_id, // const std::vector & a_dependencies) //============================================================================== void JobsModel::requestJobDependsOnIds(const QUuid & a_id, const std::vector & a_dependencies) { emit signalSetDependencies(a_id, a_dependencies); } // END OF void JobsModel::requestJobDependsOnIds(const QUuid & a_id, // const std::vector & a_dependencies) //============================================================================== bool JobsModel::setJobProgress(const QUuid & a_id, int a_progress, double a_fps) { int index = indexOfJob(a_id); if(index < 0) return false; m_jobs[index].framesProcessed = a_progress; m_jobs[index].fps = a_fps; notifyJobUpdated(index, STATE_COLUMN); notifyJobUpdated(index, FPS_COLUMN); emit signalProgressChanged(index, a_progress, m_jobs[index].framesTotal()); return true; } // END OF bool JobsModel::setJobProgress(const QUuid & a_id, int a_progress, // double a_fps) //============================================================================== bool JobsModel::setJobState(const QUuid & a_id, JobState a_state) { int index = indexOfJob(a_id); if(index < 0) return false; m_jobs[index].jobState = a_state; notifyJobUpdated(index, STATE_COLUMN); emit signalStateChanged(index, a_state); return true; } // END OF bool JobsModel::setJobState(const QUuid & a_id, JobState a_state) //============================================================================== bool JobsModel::setJobStartTime(const QUuid & a_id, const QDateTime & a_time) { int index = indexOfJob(a_id); if(index < 0) return false; m_jobs[index].timeStarted = a_time; notifyJobUpdated(index, TIME_START_COLUMN); return true; } // END OF bool JobsModel::setJobStartTime(const QUuid & a_id, // const QDateTime & a_time) //============================================================================== bool JobsModel::setJobEndTime(const QUuid & a_id, const QDateTime & a_time) { int index = indexOfJob(a_id); if(index < 0) return false; m_jobs[index].timeEnded = a_time; notifyJobUpdated(index, TIME_END_COLUMN); return true; } // END OF bool JobsModel::setJobEndTime(const QUuid & a_id, // const QDateTime & a_time) //============================================================================== bool JobsModel::canModifyJob(int a_index) const { if((a_index < 0) || ((size_t)a_index >= m_jobs.size())) return false; JobState forbiddenStates[] = {JobState::Running, JobState::Paused, JobState::Aborting}; if(vsedit::contains(forbiddenStates, m_jobs[a_index].jobState)) return false; return true; } // END OF bool JobsModel::canModifyJob(int a_index) const //============================================================================== bool JobsModel::hasActiveJobs() { for(const JobProperties & properties : m_jobs) { if(vsedit::contains(ACTIVE_JOB_STATES, properties.jobState)) return true; } return false; } // END OF bool JobsModel::hasActiveJobs() //============================================================================== bool JobsModel::hasWaitingJobs() { for(const JobProperties & properties : m_jobs) { if(properties.jobState == JobState::Waiting) return true; } return false; } // END OF bool JobsModel::hasActiveJobs() //============================================================================== std::vector JobsModel::indexesFromSelection( const QItemSelection & a_selection) { std::set indexesSet; QModelIndexList modelIndexList = a_selection.indexes(); for(const QModelIndex & jobIndex : modelIndexList) indexesSet.insert(jobIndex.row()); std::vector indexesVector; std::copy(indexesSet.begin(), indexesSet.end(), std::back_inserter(indexesVector)); return indexesVector; } // END OF std::vector JobsModel::indexesFromSelection( // const QItemSelection & a_selection) //============================================================================== std::vector JobsModel::idsFromSelection( const QItemSelection & a_selection) { std::vector indexesVector = indexesFromSelection(a_selection); std::vector idsVector; for(int jobIndex : indexesVector) idsVector.push_back(m_jobs[jobIndex].id); return idsVector; } // END OF std::vector JobsModel::idsFromSelection( // const QItemSelection & a_selection) //============================================================================== int JobsModel::indexOfJob(const QUuid & a_uuid) const { std::vector::const_iterator it = std::find_if(m_jobs.cbegin(), m_jobs.cend(), [&](const JobProperties & a_properties)->bool { return (a_properties.id == a_uuid); }); return (it == m_jobs.cend()) ? -1 : std::distance(m_jobs.cbegin(), it); } // END OF int JobsModel::indexOfJob(const QUuid & a_uuid) const //============================================================================== void JobsModel::notifyJobUpdated(int a_index, int a_column) { QModelIndex first; QModelIndex last; if(a_column < 0) { first = createIndex(a_index, 0); last = createIndex(a_index, COLUMNS_NUMBER - 1); } else { first = createIndex(a_index, a_column); last = createIndex(a_index, a_column); } emit dataChanged(first, last); } // END OF void JobsModel::noifyJobUpdated(int a_index) //============================================================================== ================================================ FILE: vsedit-job-server-watcher/src/jobs/jobs_model.h ================================================ #ifndef JOBS_MODEL_H_INCLUDED #define JOBS_MODEL_H_INCLUDED #include "../../../common-src/settings/settings_definitions_core.h" #include "../../../common-src/log/styled_log_view_core.h" #include #include #include class SettingsManager; class JobsModel : public QAbstractItemModel { Q_OBJECT public: static const int NAME_COLUMN; static const int TYPE_COLUMN; static const int SUBJECT_COLUMN; static const int STATE_COLUMN; static const int DEPENDS_ON_COLUMN; static const int TIME_START_COLUMN; static const int TIME_END_COLUMN; static const int FPS_COLUMN; static const int COLUMNS_NUMBER; JobsModel(SettingsManager * a_pSettingsManager, QObject * a_pParent = nullptr); virtual ~JobsModel(); virtual QModelIndex index(int a_row, int a_column, const QModelIndex & a_parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex & a_child) const override; virtual Qt::ItemFlags flags(const QModelIndex & a_index) const override; virtual QVariant headerData(int a_section, Qt::Orientation a_orientation, int a_role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex & a_index, int a_role = Qt::DisplayRole) const override; virtual int rowCount(const QModelIndex & a_parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex & a_parent = QModelIndex()) const override; virtual bool setData(const QModelIndex & a_index, const QVariant & a_value, int a_role = Qt::EditRole) override; void clear(); std::vector jobs() const; bool setJobs(const std::vector & a_jobs); JobProperties jobProperties(int a_index) const; int createJob(const JobProperties & a_jobProperties); bool swapJobs(const QUuid & a_id1, const QUuid & a_id2); bool deleteJobs(std::vector a_ids); bool updateJobProperties(const JobProperties & a_jobProperties); bool setJobDependsOnIds(const QUuid & a_id, const std::vector & a_dependencies); void requestJobDependsOnIds(const QUuid & a_id, const std::vector & a_dependencies); bool setJobProgress(const QUuid & a_id, int a_progress, double a_fps); bool setJobState(const QUuid & a_id, JobState a_state); bool setJobStartTime(const QUuid & a_id, const QDateTime & a_time); bool setJobEndTime(const QUuid & a_id, const QDateTime & a_time); bool canModifyJob(int a_index) const; bool hasActiveJobs(); bool hasWaitingJobs(); std::vector indexesFromSelection(const QItemSelection & a_selection); std::vector idsFromSelection(const QItemSelection & a_selection); signals: void signalLogMessage(const QString & a_message, const QString & a_style = LOG_STYLE_DEFAULT); void signalStateChanged(int a_job, JobState a_state); void signalProgressChanged(int a_job, int a_progress, int a_progressMax); void signalSetDependencies(const QUuid & a_id, std::vector a_dependencies); private: int indexOfJob(const QUuid & a_id) const; void notifyJobUpdated(int a_index, int a_column = -1); std::vector m_jobs; SettingsManager * m_pSettingsManager; int m_fpsDisplayPrecision; }; #endif // JOBS_MODEL_H_INCLUDED ================================================ FILE: vsedit-job-server-watcher/src/main.cpp ================================================ #include "main_window.h" #include "../../common-src/settings/settings_manager.h" #include "../../common-src/log/vs_editor_log.h" #include "../../common-src/application_instance_file_guard/application_instance_file_guard.h" #include "../../common-src/ipc_defines.h" #include "../../common-src/helpers.h" #include "../../common-src/version_info.h" #include #include MainWindow * pMainWindow = nullptr; void handleQtMessage(QtMsgType a_type, const QMessageLogContext & a_context, const QString & a_message) { QString prefix = "Qt debug"; QString style = LOG_STYLE_DEFAULT; switch(a_type) { case QtDebugMsg: prefix = "Qt debug"; style = LOG_STYLE_QT_DEBUG; break; case QtInfoMsg: prefix = "Qt info"; style = LOG_STYLE_QT_INFO; break; case QtWarningMsg: prefix = "Qt warning"; style = LOG_STYLE_QT_WARNING; break; case QtCriticalMsg: prefix = "Qt critical"; style = LOG_STYLE_QT_CRITICAL; break; case QtFatalMsg: prefix = "Qt fatal"; style = LOG_STYLE_QT_FATAL; break; default: Q_ASSERT(false); } QString fullMessage = QString("%1: %2").arg(prefix).arg(a_message); QString fileString(a_context.file); QString lineString = QString::number(a_context.line); QString functionString(a_context.function); QString lineInfo = QString("\n(%1:%2").arg(fileString).arg(lineString); if(!functionString.isEmpty()) lineInfo += QString(", %1").arg(functionString); lineInfo += QString(")"); if(!fileString.isEmpty()) fullMessage += lineInfo; pMainWindow->slotWriteLogMessage(fullMessage, style); if(a_type == QtFatalMsg) abort(); } int main(int argc, char *argv[]) { if(argc > 1) { if(strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { print_version(); return 0; } } QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); QApplication application(argc, argv); SettingsManager *settings = new SettingsManager(qApp); vsedit::disableFontKerning(qApp); application.setQuitOnLastWindowClosed(false); ApplicationInstanceFileGuard guard("vsedit_job_server_watcher_running"); if(!guard.isLocked()) { QLocalSocket socket; socket.connectToServer(JOB_SERVER_WATCHER_LOCAL_SERVER_NAME, QIODevice::WriteOnly); bool connected = socket.waitForConnected(5000); if(connected) { socket.write(WMSG_SHOW_WINDOW); socket.waitForBytesWritten(5000); } else qCritical("Couldn't start the server watcher."); return 1; } // Make text in message box selectable application.setStyleSheet( "QToolTip { font-kerning: none; }" "QMessageBox { messagebox-text-interaction-flags: 5; }"); pMainWindow = new MainWindow(settings); qInstallMessageHandler(handleQtMessage); pMainWindow->showAndConnect(); int exitCode = application.exec(); delete pMainWindow; delete settings; if(!guard.unlock()) qCritical("%s", guard.error().toLocal8Bit().data()); return exitCode; } ================================================ FILE: vsedit-job-server-watcher/src/main_window.cpp ================================================ #include "main_window.h" #include "jobs/jobs_model.h" #include "jobs/job_state_delegate.h" #include "jobs/job_dependencies_delegate.h" #include "jobs/job_edit_dialog.h" #include "connect_to_server_dialog.h" #include "trusted_clients_addresses_dialog.h" #include "../../common-src/helpers.h" #include "../../common-src/ipc_defines.h" #include "../../common-src/settings/settings_definitions.h" #include "../../common-src/settings/settings_manager.h" #include "../../common-src/vapoursynth/vs_script_library.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== const char MainWindow::WINDOW_TITLE[] = "VapourSynth jobs server watcher"; //============================================================================== MainWindow::MainWindow(SettingsManager *settings) : QMainWindow() , m_pSettingsManager(settings) , m_pJobsModel(nullptr) , m_pJobStateDelegate(nullptr) , m_pJobDependenciesDelegate(nullptr) , m_pVSScriptLibrary(nullptr) , m_pJobEditDialog(nullptr) , m_pServerSocket(nullptr) , m_connectionAttempts(0) , m_maxConnectionAttempts(DEFAULT_MAX_WATCHER_CONNECTION_ATTEMPTS) , m_state(WatcherState::NotConnected) , m_pTrayIcon(nullptr) , m_pTrayMenu(nullptr) , m_pActionSetTrustedClientsAddresses(nullptr) , m_pActionExit(nullptr) , m_pActionShutdownServerAndExit(nullptr) , m_pConnectToServerDialog(nullptr) , m_nextServerAddress(QHostAddress::LocalHost) , m_pTaskServer(nullptr) , m_pGeometrySaveTimer(nullptr) { vsedit::disableFontKerning(this); m_ui.setupUi(this); setWindowTitle(tr(WINDOW_TITLE)); if(m_pSettingsManager->inDarkMode()) { // Load qDarkStyle colors QFile styleSheetDark(":/dark/style.qss"); if(!styleSheetDark.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::critical(this, QString::fromUtf8("File open error"), QString::fromUtf8("Failed to open stylesheet file ") + styleSheetDark.errorString()); } qApp->setStyleSheet(styleSheetDark.readAll()); // With the current impl of the timeline slider // we have to relaunch anyway QPalette newPal(qApp->palette()); newPal.setColor(QPalette::Base, QColor(0, 0, 0)); newPal.setColor(QPalette::Highlight, QColor(128, 128, 128)); newPal.setColor(QPalette::Dark, QColor(192, 192, 192)); newPal.setColor(QPalette::Text, QColor(64, 192, 0)); qApp->setPalette(newPal); } #ifdef Q_OS_WIN else qApp->setStyle("fusion"); #endif m_pVSScriptLibrary = new VSScriptLibrary(m_pSettingsManager, this); m_pJobEditDialog = new JobEditDialog(m_pSettingsManager, m_pVSScriptLibrary, this); m_pJobsModel = new JobsModel(m_pSettingsManager, this); m_ui.jobsTableView->setModel(m_pJobsModel); m_pJobStateDelegate = new JobStateDelegate(this); m_ui.jobsTableView->setItemDelegateForColumn( JobsModel::STATE_COLUMN, m_pJobStateDelegate); m_pJobDependenciesDelegate = new JobDependenciesDelegate(this); m_ui.jobsTableView->setItemDelegateForColumn( JobsModel::DEPENDS_ON_COLUMN, m_pJobDependenciesDelegate); QHeaderView * pHorizontalHeader = m_ui.jobsTableView->horizontalHeader(); pHorizontalHeader->setSectionsMovable(true); QHeaderView * pVerticalHeader = m_ui.jobsTableView->verticalHeader(); pVerticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); m_ui.logView->setName("job_server_watcher_main_log"); m_ui.logView->setSettingsManager(m_pSettingsManager); m_ui.logView->loadSettings(); m_pConnectToServerDialog = new ConnectToServerDialog(m_pSettingsManager, this); m_pServerSocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this); m_pGeometrySaveTimer = new QTimer(this); m_pGeometrySaveTimer->setInterval(DEFAULT_WINDOW_GEOMETRY_SAVE_DELAY); connect(m_pGeometrySaveTimer, &QTimer::timeout, this, &MainWindow::slotSaveGeometry); m_windowGeometry = m_pSettingsManager->getJobServerWatcherGeometry(); if(!m_windowGeometry.isEmpty()) restoreGeometry(m_windowGeometry); QByteArray headerState = m_pSettingsManager->getJobsHeaderState(); if(!headerState.isEmpty()) pHorizontalHeader->restoreState(headerState); pHorizontalHeader->setContextMenuPolicy(Qt::CustomContextMenu); m_pJobsHeaderMenu = new QMenu(pHorizontalHeader); vsedit::disableFontKerning(m_pJobsHeaderMenu); for(int i = 0; i < m_pJobsModel->columnCount(); ++i) { QAction * pAction = new QAction(m_pJobsHeaderMenu); pAction->setText( m_pJobsModel->headerData(i, Qt::Horizontal).toString()); pAction->setData(i); pAction->setCheckable(true); pAction->setChecked(!pHorizontalHeader->isSectionHidden(i)); vsedit::disableFontKerning(pAction); m_pJobsHeaderMenu->addAction(pAction); connect(pAction, SIGNAL(toggled(bool)), this, SLOT(slotShowJobsHeaderSection(bool))); } if(QSystemTrayIcon::isSystemTrayAvailable()) { m_pTrayIcon = new QSystemTrayIcon(QIcon(":watcher.ico"), this); m_pTrayIcon->setToolTip(WINDOW_TITLE); connect(m_pTrayIcon, &QSystemTrayIcon::activated, this, &MainWindow::slotTrayIconActivated); m_pTrayIcon->show(); } m_pTaskServer = new QLocalServer(this); connect(m_ui.jobNewButton, SIGNAL(clicked()), this, SLOT(slotJobNewButtonClicked())); connect(m_ui.jobEditButton, SIGNAL(clicked()), this, SLOT(slotJobEditButtonClicked())); connect(m_ui.jobMoveUpButton, SIGNAL(clicked()), this, SLOT(slotJobMoveUpButtonClicked())); connect(m_ui.jobMoveDownButton, SIGNAL(clicked()), this, SLOT(slotJobMoveDownButtonClicked())); connect(m_ui.jobDeleteButton, SIGNAL(clicked()), this, SLOT(slotJobDeleteButtonClicked())); connect(m_ui.jobResetStateButton, SIGNAL(clicked()), this, SLOT(slotJobResetStateButtonClicked())); connect(m_ui.startButton, SIGNAL(clicked()), this, SLOT(slotStartButtonClicked())); connect(m_ui.pauseButton, SIGNAL(clicked()), this, SLOT(slotPauseButtonClicked())); connect(m_ui.resumeButton, SIGNAL(clicked()), this, SLOT(slotResumeButtonClicked())); connect(m_ui.abortButton, SIGNAL(clicked()), this, SLOT(slotAbortButtonClicked())); connect(m_ui.startServerButton, SIGNAL(clicked()), this, SLOT(slotStartLocalServer())); connect(m_ui.connectToServerButton, SIGNAL(clicked()), this, SLOT(slotConnectToServerDialog())); connect(m_ui.shutdownServerButton, SIGNAL(clicked()), this, SLOT(slotShutdownServer())); connect(m_ui.jobsTableView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(slotJobDoubleClicked(const QModelIndex &))); connect(m_ui.jobsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::slotSelectionChanged); connect(pHorizontalHeader, SIGNAL(sectionResized(int, int, int)), this, SLOT(slotSaveHeaderState())); connect(pHorizontalHeader, SIGNAL(sectionMoved(int, int, int)), this, SLOT(slotSaveHeaderState())); connect(pHorizontalHeader, SIGNAL(sectionCountChanged(int, int)), this, SLOT(slotSaveHeaderState())); connect(pHorizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(slotSaveHeaderState())); connect(pHorizontalHeader, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotJobsHeaderContextMenu(const QPoint &))); connect(m_pConnectToServerDialog, SIGNAL(signalConnectToServer(const QHostAddress &)), this, SLOT(slotConnectToServer(const QHostAddress &))); connect(m_pServerSocket, &QWebSocket::connected, this, &MainWindow::slotServerConnected); connect(m_pServerSocket, &QWebSocket::disconnected, this, &MainWindow::slotServerDisconnected); connect(m_pServerSocket, &QWebSocket::binaryMessageReceived, this, &MainWindow::slotBinaryMessageReceived); connect(m_pServerSocket, &QWebSocket::textMessageReceived, this, &MainWindow::slotTextMessageReceived); connect(m_pServerSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotServerError(QAbstractSocket::SocketError))); connect(m_pJobsModel, SIGNAL(signalLogMessage(const QString &, const QString &)), m_ui.logView, SLOT(addEntry(const QString &, const QString &))); connect(m_pJobsModel, &JobsModel::signalStateChanged, this, &MainWindow::slotJobStateChanged); connect(m_pJobsModel, &JobsModel::signalProgressChanged, this, &MainWindow::slotJobProgressChanged); connect(m_pJobsModel, &JobsModel::signalSetDependencies, this, &MainWindow::slotSetJobDependencies); connect(m_pTaskServer, &QLocalServer::newConnection, this, &MainWindow::slotTaskServerNewConnection); createActionsAndMenus(); setUiEnabled(); m_pTaskServer->setSocketOptions(QLocalServer::WorldAccessOption); bool taskServerStarted = m_pTaskServer->listen(JOB_SERVER_WATCHER_LOCAL_SERVER_NAME); if(!taskServerStarted) { m_ui.logView->addEntry(tr("Couldn't start task server."), LOG_STYLE_ERROR); } } // END OF MainWindow::MainWindow() //============================================================================== MainWindow::~MainWindow() { if(m_pGeometrySaveTimer->isActive()) { m_pGeometrySaveTimer->stop(); slotSaveGeometry(); } m_pServerSocket->close(QWebSocketProtocol::CloseCodeNormal, tr("Closing watcher.")); for(QLocalSocket * pClient : m_taskClients) { disconnect(pClient, &QLocalSocket::disconnected, this, &MainWindow::slotTaskClientDisconnected); delete pClient; } qInstallMessageHandler(0); } // END OF MainWindow::~MainWindow() //============================================================================== void MainWindow::showAndConnect() { show(); if(m_state != WatcherState::NotConnected) return; slotWriteLogMessage(tr("Connecting to local server.")); changeState(WatcherState::ProbingLocal); slotConnectToLocalServer(); } // END OF MainWindow::showAndConnect() //============================================================================== void MainWindow::show() { if(m_pSettingsManager->getJobServerWatcherMaximized()) showMaximized(); else showNormal(); activateWindow(); } // END OF MainWindow::show() //============================================================================== void MainWindow::close() { changeState(WatcherState::ShuttingDown); QMainWindow::close(); } // END OF void MainWindow::close() //============================================================================== void MainWindow::slotWriteLogMessage(int a_messageType, const QString & a_message) { QString style = vsMessageTypeToStyleName(a_messageType); slotWriteLogMessage(a_message, style); } // END OF void MainWindow::slotWriteLogMessage(int a_messageType, // const QString & a_message) //============================================================================== void MainWindow::slotWriteLogMessage(const QString & a_message, const QString & a_style) { QString debugTypes[] = { LOG_STYLE_DEBUG, LOG_STYLE_QT_DEBUG, LOG_STYLE_VS_DEBUG, }; if(m_pSettingsManager->getShowDebugMessages() || !vsedit::contains(debugTypes, a_style)) { m_ui.logView->addEntry(a_message, a_style); } QString fatalTypes[] = {LOG_STYLE_VS_FATAL, LOG_STYLE_QT_FATAL}; if(!vsedit::contains(fatalTypes, a_style)) return; QDateTime now = QDateTime::currentDateTime(); QString timeString = now.toString("hh:mm:ss.zzz"); QString dateString = now.toString("yyyy-MM-dd"); QString caption = QObject::tr("VapourSynth Editor Job Server " "fatal error!"); QString fullMessage = dateString + QString(" ") + timeString + QString("\n") + caption + QString("\n") + a_message; QString tempPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation); if(tempPath.isEmpty()) { QMessageBox::critical(nullptr, caption, fullMessage); return; } QString filePath = tempPath + QString("/") + QString("VapourSynth-Editor-Job-Server-Watcher-crashlog-") + dateString + QString("-") + timeString.replace(':', '-') + QString(".html"); bool saved = m_ui.logView->saveHtml(filePath); if(!saved) { QMessageBox::critical(nullptr, caption, fullMessage); return; } QUrl fileUrl = QUrl::fromLocalFile(filePath); QDesktopServices::openUrl(fileUrl); } // END OF void MainWindow::slotWriteLogMessage(const QString & a_message, // const QString & a_style); //============================================================================== void MainWindow::moveEvent(QMoveEvent * a_pEvent) { QMainWindow::moveEvent(a_pEvent); saveGeometryDelayed(); } // END OF void MainWindow::moveEvent(QMoveEvent * a_pEvent) //============================================================================== void MainWindow::resizeEvent(QResizeEvent * a_pEvent) { QMainWindow::resizeEvent(a_pEvent); saveGeometryDelayed(); } // END OF void MainWindow::resizeEvent(QResizeEvent * a_pEvent) //============================================================================== void MainWindow::changeEvent(QEvent * a_pEvent) { if(a_pEvent->type() == QEvent::WindowStateChange) { if(isMaximized()) m_pSettingsManager->setJobServerWatcherMaximized(true); else m_pSettingsManager->setJobServerWatcherMaximized(false); } QMainWindow::changeEvent(a_pEvent); } // END OF void MainWindow::changeEvent(QEvent * a_pEvent) //============================================================================== void MainWindow::showEvent(QShowEvent * a_pEvent) { QMainWindow::showEvent(a_pEvent); } // END OF void MainWindow::showEvent(QShowEvent * a_pEvent) //============================================================================== void MainWindow::closeEvent(QCloseEvent * a_pEvent) { WatcherState statesToClose[] = {WatcherState::ShuttingDown, WatcherState::ClosingServerShuttingDown}; bool closeToTray = QSystemTrayIcon::isSystemTrayAvailable() && (!vsedit::contains(statesToClose, m_state)); if(closeToTray) { a_pEvent->ignore(); hide(); } else { QMainWindow::closeEvent(a_pEvent); QCoreApplication::quit(); } } // END OF void MainWindow::closeEvent(QCloseEvent * a_pEvent) //============================================================================== void MainWindow::slotTrayIconActivated( QSystemTrayIcon::ActivationReason a_reason) { if(a_reason == QSystemTrayIcon::DoubleClick) show(); } // END OF void MainWindow::slotTrayIconActivated( // QSystemTrayIcon::ActivationReason a_reason) //============================================================================== void MainWindow::slotJobNewButtonClicked() { int result = m_pJobEditDialog->call(tr("New job"), JobProperties()); if(result == QDialog::Accepted) { JobProperties newJobProperties = m_pJobEditDialog->jobProperties(); m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_CREATE_JOB, newJobProperties.toJson())); } processTaskList(); } // END OF void MainWindow::slotJobNewButtonClicked() //============================================================================== void MainWindow::slotJobEditButtonClicked() { QItemSelectionModel * pSelectionModel = m_ui.jobsTableView->selectionModel(); QModelIndexList selection = pSelectionModel->selectedRows(); if(selection.size() != 1) return; editJob(selection[0]); } // END OF void MainWindow::slotJobEditButtonClicked() //============================================================================== void MainWindow::slotJobMoveUpButtonClicked() { std::vector selection = selectedIndexes(); if(selection.size() != 1) return; if(selection[0] == 0) return; QJsonArray jsSwap; jsSwap << m_pJobsModel->jobProperties(selection[0] - 1).id.toString(); jsSwap << m_pJobsModel->jobProperties(selection[0]).id.toString(); m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_SWAP_JOBS, jsSwap)); } // END OF void MainWindow::slotJobMoveUpButtonClicked() //============================================================================== void MainWindow::slotJobMoveDownButtonClicked() { std::vector selection = selectedIndexes(); if(selection.size() != 1) return; if(selection[0] >= (int)m_pJobsModel->jobs().size()) return; QJsonArray jsSwap; jsSwap << m_pJobsModel->jobProperties(selection[0]).id.toString(); jsSwap << m_pJobsModel->jobProperties(selection[0] + 1).id.toString(); m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_SWAP_JOBS, jsSwap)); } // END OF void MainWindow::slotJobMoveDownButtonClicked() //============================================================================== void MainWindow::slotJobDeleteButtonClicked() { std::vector selection = selectedIndexes(); if(selection.empty()) return; QJsonArray jsIds; for(int index : selection) jsIds << m_pJobsModel->jobProperties(index).id.toString(); m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_DELETE_JOBS, jsIds)); } // END OF void MainWindow::slotJobDeleteButtonClicked() //============================================================================== void MainWindow::slotJobResetStateButtonClicked() { std::vector selection = selectedIndexes(); if(selection.empty()) return; QJsonArray jsIds; for(int index : selection) jsIds << m_pJobsModel->jobProperties(index).id.toString(); m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_RESET_JOBS, jsIds)); } // END OF void MainWindow::slotJobResetStateButtonClicked() //============================================================================== void MainWindow::slotStartButtonClicked() { m_pServerSocket->sendBinaryMessage(MSG_START_ALL_WAITING_JOBS); } // END OF void MainWindow::slotStartButtonClicked() //============================================================================== void MainWindow::slotPauseButtonClicked() { m_pServerSocket->sendBinaryMessage(MSG_PAUSE_ACTIVE_JOBS); } // END OF void MainWindow::slotPauseButtonClicked() //============================================================================== void MainWindow::slotResumeButtonClicked() { m_pServerSocket->sendBinaryMessage(MSG_RESUME_PAUSED_JOBS); } // END OF void MainWindow::slotResumeButtonClicked() //============================================================================== void MainWindow::slotAbortButtonClicked() { m_pServerSocket->sendBinaryMessage(MSG_ABORT_ACTIVE_JOBS); } // END OF void MainWindow::slotAbortButtonClicked() //============================================================================== void MainWindow::slotJobDoubleClicked(const QModelIndex & a_index) { if(a_index.column() == JobsModel::DEPENDS_ON_COLUMN) return; editJob(a_index); } // END OF void MainWindow::slotJobDoubleClicked(const QModelIndex & a_index) //============================================================================== void MainWindow::slotSelectionChanged() { setUiEnabled(); } // END OF void MainWindow::slotSelectionChanged() //============================================================================== void MainWindow::slotSaveHeaderState() { QHeaderView * pHeader = m_ui.jobsTableView->horizontalHeader(); m_pSettingsManager->setJobsHeaderState(pHeader->saveState()); } // END OF void MainWindow::slotSaveHeaderState() //============================================================================== void MainWindow::slotJobsHeaderContextMenu(const QPoint & a_point) { (void)a_point; m_pJobsHeaderMenu->exec(QCursor::pos()); } // END OF void MainWindow::slotJobsHeaderContextMenu(const QPoint & a_point) //============================================================================== void MainWindow::slotShowJobsHeaderSection(bool a_show) { QAction * pAction = qobject_cast(sender()); if(!pAction) return; int section = pAction->data().toInt(); QHeaderView * pHeader = m_ui.jobsTableView->horizontalHeader(); pHeader->setSectionHidden(section, !a_show); } // END OF void MainWindow::slotShowJobsHeaderSection(bool a_show) //============================================================================== void MainWindow::slotJobStateChanged(int a_job, JobState a_state) { setUiEnabled(); resetWindowTitle(a_job); // Tray JobState finalStates[] = {JobState::Completed, JobState::Aborted, JobState::Failed}; if(QSystemTrayIcon::isSystemTrayAvailable() && vsedit::contains(finalStates, a_state)) { Q_ASSERT(m_pTrayIcon); QString message; QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::NoIcon; if(m_pJobsModel->hasActiveJobs() || m_pJobsModel->hasWaitingJobs()) { if(a_state == JobState::Completed) message = tr("Job%1 has finished successfully"); else if(a_state == JobState::Aborted) { message = tr("Job%1 was aborted"); icon = QSystemTrayIcon::Warning; } else if(a_state == JobState::Failed) { message = tr("Job%1 has failed"); icon = QSystemTrayIcon::Critical; } message = message.arg(a_job + 1); } else message = tr("All jobs are finished."); m_pTrayIcon->showMessage(WINDOW_TITLE, message, icon); } } // END OF void MainWindow::slotJobStateChanged(int a_job, JobState a_state) //============================================================================== void MainWindow::slotJobProgressChanged(int a_job, int a_progress, int a_progressMax) { (void)a_progress; (void)a_progressMax; resetWindowTitle(a_job); JobProperties properties = m_pJobsModel->jobProperties(a_job); } // END OF void MainWindow::slotJobProgressChanged(int a_job, int a_progress, // int a_progressMax) //============================================================================== void MainWindow::slotSetJobDependencies(const QUuid & a_id, std::vector a_dependencies) { QJsonObject jsJob; jsJob[JP_ID] = a_id.toString(); QJsonArray jsDependencies; for(QUuid id : a_dependencies) jsDependencies << id.toString(); jsJob[JP_DEPENDS_ON_JOB_IDS] = jsDependencies; m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_SET_JOB_DEPENDENCIES, jsJob)); } // END OF void MainWindow::slotSetJobDependencies(const QUuid & a_id, // std::vector a_dependencies) //============================================================================== void MainWindow::slotServerConnected() { changeState(WatcherState::Connected); m_connectionAttempts = 0; m_pServerSocket->sendBinaryMessage(MSG_GET_JOBS_INFO); m_pServerSocket->sendBinaryMessage(MSG_GET_LOG); m_pServerSocket->sendBinaryMessage(MSG_SUBSCRIBE); processTaskList(); } // END OF void MainWindow::slotServerConnected() //============================================================================== void MainWindow::slotServerDisconnected() { m_pJobsModel->clear(); m_trustedClientsAddresses.clear(); m_pActionSetTrustedClientsAddresses->setEnabled(false); if(m_state == WatcherState::ProbingLocal) { changeState(WatcherState::StartingLocal); QString serverPath = vsedit::resolvePathFromApplication( "./vsedit-job-server"); QString thisDir = vsedit::resolvePathFromApplication("."); QProcess serverProcess; bool started = serverProcess.startDetached(serverPath, QStringList(), thisDir); if(!started) { changeState(WatcherState::NotConnected); m_ui.logView->addEntry(tr("Could not start server."), LOG_STYLE_ERROR); return; } changeState(WatcherState::Connecting); QTimer::singleShot(1000, Qt::PreciseTimer, this, &MainWindow::slotConnectToLocalServer); } else if(m_state == WatcherState::Connecting) { m_connectionAttempts++; if(m_connectionAttempts >= m_maxConnectionAttempts) { changeState(WatcherState::NotConnected); m_connectionAttempts = 0; m_ui.logView->addEntry(tr("Could not connect to server."), LOG_STYLE_ERROR); return; } QTimer::singleShot(500, Qt::PreciseTimer, this, &MainWindow::slotReconnectToServer); } else if(m_state == WatcherState::SwitchingServer) { changeState(WatcherState::Connecting); slotReconnectToServer(); return; } else if((m_state == WatcherState::Disconnecting) || (m_state == WatcherState::ShuttingDown)) { changeState(WatcherState::NotConnected); m_ui.logView->addEntry(tr("Disconnected from server")); } else if(m_state == WatcherState::Connected) { m_ui.logView->addEntry(tr("Disconnected from server. " "Reconnecting"), LOG_STYLE_ERROR); changeState(WatcherState::Connecting); slotReconnectToServer(); } else if(m_state == WatcherState::ClosingServerShuttingDown) close(); } // END OF void MainWindow::slotServerDisconnected() //============================================================================== void MainWindow::slotBinaryMessageReceived(const QByteArray & a_message) { slotTextMessageReceived(QString::fromUtf8(a_message)); } // END OF void MainWindow::slotBinaryMessageReceived( // const QByteArray & a_message) //============================================================================== void MainWindow::slotTextMessageReceived(const QString & a_message) { QString command = a_message; QString arguments; int spaceIndex = a_message.indexOf(' '); if(spaceIndex >= 0) { command = a_message.left(spaceIndex); arguments = a_message.mid(spaceIndex + 1); } QJsonDocument jsArguments = QJsonDocument::fromJson(arguments.toUtf8()); if(command == QString(SMSG_JOBS_INFO)) { processSMsgJobInfo(arguments); return; } if(command == QString(SMSG_COMPLETE_LOG)) { QJsonArray jsEntries = jsArguments.array(); for(int i = 0; i < jsEntries.size(); ++i) { LogEntry entry = LogEntry::fromJson(jsEntries[i].toObject()); m_ui.logView->addEntry(entry); } return; } if(command == QString(SMSG_LOG_MESSAGE)) { LogEntry entry = LogEntry::fromJson(jsArguments.object()); m_ui.logView->addEntry(entry); return; } if(command == QString(SMSG_JOB_CREATED)) { QJsonObject jsJobProperties = jsArguments.object(); m_pJobsModel->createJob(JobProperties::fromJson(jsJobProperties)); return; } if(command == QString(SMSG_JOB_UPDATE)) { QJsonObject jsJobProperties = jsArguments.object(); JobProperties properties = JobProperties::fromJson(jsJobProperties); m_pJobsModel->updateJobProperties(properties); return; } if(command == QString(SMSG_JOB_STATE_UPDATE)) { QJsonObject jsJob = jsArguments.object(); if(!jsJob.contains(JP_ID)) return; QUuid id(jsJob[JP_ID].toString()); if(!jsJob.contains(JP_JOB_STATE)) return; JobState state = (JobState)jsJob[JP_JOB_STATE].toInt(); m_pJobsModel->setJobState(id, state); return; } if(command == QString(SMSG_JOB_PROGRESS_UPDATE)) { QJsonObject jsJob = jsArguments.object(); if(!jsJob.contains(JP_ID)) return; QUuid id(jsJob[JP_ID].toString()); if(!jsJob.contains(JP_FRAMES_PROCESSED)) return; int progress = jsJob[JP_FRAMES_PROCESSED].toInt(); if(!jsJob.contains(JP_FPS)) return; double fps = jsJob[JP_FPS].toDouble(); m_pJobsModel->setJobProgress(id, progress, fps); return; } if(command == QString(SMSG_JOB_START_TIME_UPDATE)) { QJsonObject jsJob = jsArguments.object(); if(!jsJob.contains(JP_ID)) return; QUuid id(jsJob[JP_ID].toString()); if(!jsJob.contains(JP_TIME_STARTED)) return; QDateTime time = QDateTime::fromMSecsSinceEpoch( jsJob[JP_TIME_STARTED].toVariant().toLongLong()); m_pJobsModel->setJobStartTime(id, time); return; } if(command == QString(SMSG_JOB_END_TIME_UPDATE)) { QJsonObject jsJob = jsArguments.object(); if(!jsJob.contains(JP_ID)) return; QUuid id(jsJob[JP_ID].toString()); if(!jsJob.contains(JP_TIME_ENDED)) return; QDateTime time = QDateTime::fromMSecsSinceEpoch( jsJob[JP_TIME_ENDED].toVariant().toLongLong()); m_pJobsModel->setJobEndTime(id, time); return; } if(command == QString(SMSG_JOB_DEPENDENCIES_UPDATE)) { QJsonObject jsJob = jsArguments.object(); if(!jsJob.contains(JP_ID)) return; QUuid id(jsJob[JP_ID].toString()); if(!jsJob.contains(JP_DEPENDS_ON_JOB_IDS)) return; QJsonArray jsDependencies = jsJob[JP_DEPENDS_ON_JOB_IDS].toArray(); std::vector dependencies; for(int i = 0; i < jsDependencies.count(); ++i) dependencies.push_back(QUuid(jsDependencies[i].toString())); m_pJobsModel->setJobDependsOnIds(id, dependencies); return; } if(command == QString(SMSG_JOBS_SWAPPED)) { QJsonArray jsSwap = jsArguments.array(); if(jsSwap.size() != 2) return; QUuid id1(jsSwap[0].toString()); QUuid id2(jsSwap[1].toString()); m_pJobsModel->swapJobs(id1, id2); return; } if(command == QString(SMSG_JOBS_DELETED)) { QJsonArray jsIds = jsArguments.array(); std::vector ids; for(int i = 0; i < jsIds.count(); ++i) ids.push_back(QUuid(jsIds[i].toString())); m_pJobsModel->deleteJobs(ids); return; } if(command == QString(SMSG_REFUSE)) { return; } if(command == QString(SMSG_CLOSING_SERVER)) { m_ui.logView->addEntry(tr("Server is shutting down.")); return; } if(command == QString(SMSG_TRUSTED_CLIENTS_INFO)) { QStringList trustedClientsAddresses; QVariantList values = jsArguments.array().toVariantList(); for(const QVariant & value : values) trustedClientsAddresses << value.toString(); m_trustedClientsAddresses = trustedClientsAddresses; m_pActionSetTrustedClientsAddresses->setEnabled(true); return; } m_ui.logView->addEntry(a_message); } // END OF void MainWindow::slotTextMessageReceived(const QString & a_message) //============================================================================== void MainWindow::slotServerError(QAbstractSocket::SocketError a_error) { if(a_error == QAbstractSocket::ConnectionRefusedError) return; // Handled by slotServerDisconnected() m_ui.logView->addEntry(m_pServerSocket->errorString(), LOG_STYLE_ERROR); } // END OF void MainWindow::slotServerError(QAbstractSocket::SocketError a_error) //============================================================================== void MainWindow::slotStartLocalServer() { if(m_state != WatcherState::NotConnected) return; slotWriteLogMessage(tr("Starting local server.")); changeState(WatcherState::ProbingLocal); slotConnectToLocalServer(); } // END OF void MainWindow::slotStartLocalServer() //============================================================================== void MainWindow::slotShutdownServer() { if(m_state != WatcherState::Connected) return; if(!m_pServerSocket->peerAddress().isLoopback()) return; changeState(WatcherState::Disconnecting); m_pServerSocket->sendBinaryMessage(MSG_CLOSE_SERVER); } // END OF void MainWindow::slotShutdownServer() //============================================================================== void MainWindow::slotConnectToServerDialog() { m_pConnectToServerDialog->call(m_pServerSocket->peerAddress()); } // END OF void MainWindow::slotConnectToServerDialog() //============================================================================== void MainWindow::slotReconnectToServer() { slotConnectToServer(m_nextServerAddress); } // END OF void MainWindow::slotReconnectToServer() //============================================================================== void MainWindow::slotConnectToServer(const QHostAddress & a_address) { if(a_address.isNull()) return; m_nextServerAddress = a_address; QString addressString = a_address.toString(); QString connectionURL = QString("ws://%1:%2").arg(addressString) .arg(JOB_SERVER_PORT); if(m_state == WatcherState::Connected) { // if(m_nextServerAddress == m_pServerSocket->peerAddress()) // return; changeState(WatcherState::SwitchingServer); m_pServerSocket->close(); } else if((m_state == WatcherState::NotConnected) || (m_state == WatcherState::Connecting)) { changeState(WatcherState::Connecting); slotWriteLogMessage(tr("Connecting to server %1. Try %2.") .arg(addressString).arg(m_connectionAttempts + 1)); m_pServerSocket->open(connectionURL); } else if(m_state == WatcherState::ProbingLocal) { m_pServerSocket->open(connectionURL); } } // END OF void MainWindow::slotConnectToServer(const QHostAddress & a_address) //============================================================================== void MainWindow::slotConnectToLocalServer() { slotConnectToServer(QHostAddress::LocalHost); } // END OF void MainWindow::slotConnectToLocalServer() //============================================================================== void MainWindow::slotShutdownServerAndExit() { if(m_state != WatcherState::Connected) close(); if(!m_pServerSocket->peerAddress().isLoopback()) { m_ui.logView->addEntry( tr("Not allowed to shut down remote server."), LOG_STYLE_ERROR); return; } changeState(WatcherState::ClosingServerShuttingDown); m_pServerSocket->sendBinaryMessage(MSG_CLOSE_SERVER); } // END OF void MainWindow::slotShutdownServerAndExit() //============================================================================== void MainWindow::slotTaskServerNewConnection() { QLocalSocket * pSocket = m_pTaskServer->nextPendingConnection(); if(!pSocket) return; connect(pSocket, &QLocalSocket::disconnected, this, &MainWindow::slotTaskClientDisconnected); connect(pSocket, &QLocalSocket::readyRead, this, &MainWindow::slotTaskClientReadyRead); m_taskClients.push_back(pSocket); } // END OF void MainWindow::slotTaskServerNewConnection() //============================================================================== void MainWindow::slotTaskClientReadyRead() { QLocalSocket * pSocket = qobject_cast(sender()); if(!pSocket) return; QByteArray message = pSocket->readAll(); QString command = QString::fromUtf8(message); QByteArray arguments; int spaceIndex = message.indexOf(' '); if(spaceIndex >= 0) { command = QString::fromUtf8(message.left(spaceIndex)); arguments = message.mid(spaceIndex + 1); } if(command == QString(WMSG_SHOW_WINDOW)) { show(); } else if(command == QString(WMSG_CLI_ENCODE_JOB)) { show(); QJsonDocument jsArguments = QJsonDocument::fromJson(arguments); JobProperties properties = JobProperties::fromJson(jsArguments.object()); m_taskList.push_back(properties); if(m_state != WatcherState::Connected) { m_ui.logView->addEntry(tr("New job task is put on hold " "until watcher connects to server."), LOG_STYLE_WARNING); return; } if(m_pJobEditDialog->isVisible()) return; processTaskList(); } } // END OF void MainWindow::slotTaskClientReadyRead() //============================================================================== void MainWindow::slotTaskClientDisconnected() { QLocalSocket * pClient = qobject_cast(sender()); if(!pClient) return; m_taskClients.remove(pClient); pClient->deleteLater(); } // END OF void MainWindow::slotTaskClientDisconnected() //============================================================================== void MainWindow::slotSetTrustedClientsAddresses() { TrustedClientsAddressesDialog dialog(this); int result = dialog.call(m_trustedClientsAddresses); if(result == QDialog::Rejected) return; QByteArray message = vsedit::jsonMessage(MSG_SET_TRUSTED_CLIENTS, QJsonArray::fromStringList(dialog.addresses())); m_pServerSocket->sendBinaryMessage(message); } // END OF void MainWindow::slotSetTrustedClientsAddresses() //============================================================================== void MainWindow::slotSaveGeometry() { m_pGeometrySaveTimer->stop(); m_pSettingsManager->setJobServerWatcherGeometry(m_windowGeometry); } // END OF void MainWindow::slotSaveGeometry() //============================================================================== void MainWindow::createActionsAndMenus() { struct ActionToCreate { QAction ** ppAction; const char * id; QObject * pObjectToConnect; const char * slotToConnect; }; ActionToCreate actionsToCreate[] = { {&m_pActionSetTrustedClientsAddresses, ACTION_ID_SET_TRUSTED_CLIENTS_ADDRESSES, this, SLOT(slotSetTrustedClientsAddresses())}, {&m_pActionExit, ACTION_ID_EXIT, this, SLOT(close())}, {&m_pActionShutdownServerAndExit, ACTION_ID_SHUTDOWN_SERVER_AND_EXIT, this, SLOT(slotShutdownServerAndExit())}, }; for(ActionToCreate & item : actionsToCreate) { QAction * pAction = m_pSettingsManager->createStandardAction( item.id, this); *item.ppAction = pAction; //m_settableActionsList.push_back(pAction); connect(pAction, SIGNAL(triggered()), item.pObjectToConnect, item.slotToConnect); } m_pActionSetTrustedClientsAddresses->setEnabled(false); QMenu * pMainMenu = m_ui.menuBar->addMenu(tr("Main")); vsedit::disableFontKerning(pMainMenu); pMainMenu->addAction(m_pActionSetTrustedClientsAddresses); pMainMenu->addAction(m_pActionExit); pMainMenu->addAction(m_pActionShutdownServerAndExit); if(QSystemTrayIcon::isSystemTrayAvailable()) { Q_ASSERT(m_pTrayIcon); m_pTrayMenu = new QMenu(this); vsedit::disableFontKerning(m_pTrayMenu); m_pTrayMenu->addAction(m_pActionExit); m_pTrayMenu->addAction(m_pActionShutdownServerAndExit); m_pTrayIcon->setContextMenu(m_pTrayMenu); } } // END OF void MainWindow::createActionsAndMenus() //============================================================================== void MainWindow::editJob(const QModelIndex & a_index) { JobProperties properties = m_pJobsModel->jobProperties(a_index.row()); if(vsedit::contains(ACTIVE_JOB_STATES, properties.jobState)) { m_ui.logView->addEntry(tr("Can not edit active job."), LOG_STYLE_WARNING); return; } int result = m_pJobEditDialog->call(tr("Edit Job %1") .arg(a_index.row() + 1), properties); if(result == QDialog::Accepted) { QUuid id = properties.id; properties = m_pJobEditDialog->jobProperties(); properties.id = id; m_pServerSocket->sendBinaryMessage(vsedit::jsonMessage(MSG_CHANGE_JOB, properties.toJson())); } processTaskList(); } // END OF void MainWindow::editJob(const QModelIndex & a_index) //============================================================================== void MainWindow::processSMsgJobInfo(const QString & a_message) { if(a_message.isEmpty()) return; QJsonDocument doc = QJsonDocument::fromJson(a_message.toUtf8()); if(!doc.isArray()) return; std::vector propertiesVector; for(const QJsonValue & value : doc.array()) { if(!value.isObject()) continue; JobProperties properties = JobProperties::fromJson(value.toObject()); propertiesVector.push_back(properties); } m_pJobsModel->setJobs(propertiesVector); } // END OF void MainWindow::processSMsgJobInfo(const QString & a_message) //============================================================================== std::vector MainWindow::selectedIndexes() { std::vector indexes; QItemSelectionModel * pSelectionModel = m_ui.jobsTableView->selectionModel(); QModelIndexList selection = pSelectionModel->selectedRows(); for(const QModelIndex & index : selection) indexes.push_back(index.row()); return indexes; } // END OF std::vector MainWindow::selectedIndexes() //============================================================================== void MainWindow::setUiEnabled() { std::map buttonsToEnable; buttonsToEnable[m_ui.jobNewButton] = false; buttonsToEnable[m_ui.jobEditButton] = false; buttonsToEnable[m_ui.jobMoveUpButton] = false; buttonsToEnable[m_ui.jobMoveDownButton] = false; buttonsToEnable[m_ui.jobDeleteButton] = false; buttonsToEnable[m_ui.jobResetStateButton] = false; buttonsToEnable[m_ui.startButton] = false; buttonsToEnable[m_ui.pauseButton] = false; buttonsToEnable[m_ui.resumeButton] = false; buttonsToEnable[m_ui.abortButton] = false; buttonsToEnable[m_ui.startServerButton] = false; buttonsToEnable[m_ui.connectToServerButton] = false; buttonsToEnable[m_ui.shutdownServerButton] = false; if(m_state == WatcherState::NotConnected) { buttonsToEnable[m_ui.startServerButton] = true; } if((m_state == WatcherState::NotConnected) || (m_state == WatcherState::Connected)) { buttonsToEnable[m_ui.connectToServerButton] = true; } if(m_state == WatcherState::Connected) { buttonsToEnable[m_ui.jobNewButton] = true; buttonsToEnable[m_ui.shutdownServerButton] = true; std::vector l_selectedIndexes = selectedIndexes(); if(l_selectedIndexes.size() == 1) { JobProperties jobProperties = m_pJobsModel->jobProperties(l_selectedIndexes[0]); if(!vsedit::contains(ACTIVE_JOB_STATES, jobProperties.jobState)) { buttonsToEnable[m_ui.jobEditButton] = true; if(l_selectedIndexes[0] > 0) buttonsToEnable[m_ui.jobMoveUpButton] = true; if(l_selectedIndexes[0] < (m_pJobsModel->rowCount() - 1)) buttonsToEnable[m_ui.jobMoveDownButton] = true; } } bool allInactive = !l_selectedIndexes.empty(); for(size_t i = 0; i < l_selectedIndexes.size(); ++i) { JobProperties jobProperties = m_pJobsModel->jobProperties(l_selectedIndexes[i]); if(vsedit::contains(ACTIVE_JOB_STATES, jobProperties.jobState)) allInactive = false; } buttonsToEnable[m_ui.jobResetStateButton] = allInactive; buttonsToEnable[m_ui.jobDeleteButton] = allInactive; buttonsToEnable[m_ui.startButton] = true; buttonsToEnable[m_ui.pauseButton] = true; buttonsToEnable[m_ui.resumeButton] = true; buttonsToEnable[m_ui.abortButton] = true; } for(std::pair buttonToEnable : buttonsToEnable) { if(buttonToEnable.first->isEnabled() != buttonToEnable.second) buttonToEnable.first->setEnabled(buttonToEnable.second); } } // END OF void MainWindow::setUiEnabled() //============================================================================== void MainWindow::resetWindowTitle(int a_jobIndex) { QString title = WINDOW_TITLE; JobProperties properties = m_pJobsModel->jobProperties(a_jobIndex); if(m_pJobsModel->hasActiveJobs()) { QString progress; if(properties.type == JobType::EncodeScriptCLI) { progress = QString("%1% ").arg(properties.framesProcessed * 100 / properties.framesTotal()); } title = QString("%1%2/%3 %4").arg(progress).arg(a_jobIndex + 1) .arg(m_pJobsModel->jobs().size()).arg(WINDOW_TITLE); } setWindowTitle(title); if(QSystemTrayIcon::isSystemTrayAvailable()) { Q_ASSERT(m_pTrayIcon); m_pTrayIcon->setToolTip(title); } } // END OF void MainWindow::resetWindowTitle(int a_jobIndex) //============================================================================== void MainWindow::changeState(WatcherState a_newState) { if(m_state == a_newState) return; m_state = a_newState; setUiEnabled(); } // END OF void MainWindow::changeState(WatcherState a_newState) //============================================================================== void MainWindow::processTaskList() { while(!m_taskList.empty()) { int result = m_pJobEditDialog->call(tr("New job"), m_taskList.front()); if(result == QDialog::Accepted) { JobProperties newJobProperties = m_pJobEditDialog->jobProperties(); m_pServerSocket->sendBinaryMessage( vsedit::jsonMessage(MSG_CREATE_JOB, newJobProperties.toJson())); } m_taskList.pop_front(); } } // END OF void MainWindow::processTaskList() //============================================================================== void MainWindow::saveGeometryDelayed() { QApplication::processEvents(); if(!isMaximized()) { m_windowGeometry = saveGeometry(); m_pGeometrySaveTimer->start(); } } // END OF void MainWindow::saveGeometryDelayed() //============================================================================== ================================================ FILE: vsedit-job-server-watcher/src/main_window.h ================================================ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "../../common-src/settings/settings_definitions_core.h" #include "../../common-src/settings/settings_definitions.h" #include #include #include #include #include class SettingsManager; class JobsModel; class JobEditDialog; class JobStateDelegate; class JobDependenciesDelegate; class VSScriptLibrary; class QMenu; class ConnectToServerDialog; class QLocalServer; class QLocalSocket; class TrustedClientsAddressesDialog; class QTimer; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(SettingsManager *settings); virtual ~MainWindow(); void showAndConnect(); public slots: void show(); void close(); void slotWriteLogMessage(int a_messageType, const QString & a_message); void slotWriteLogMessage(const QString & a_message, const QString & a_style = LOG_STYLE_DEFAULT); protected: virtual void moveEvent(QMoveEvent * a_pEvent) override; virtual void resizeEvent(QResizeEvent * a_pEvent) override; virtual void changeEvent(QEvent * a_pEvent) override; virtual void showEvent(QShowEvent * a_pEvent) override; virtual void closeEvent(QCloseEvent * a_pEvent) override; private slots: void slotTrayIconActivated(QSystemTrayIcon::ActivationReason a_reason); void slotJobNewButtonClicked(); void slotJobEditButtonClicked(); void slotJobMoveUpButtonClicked(); void slotJobMoveDownButtonClicked(); void slotJobDeleteButtonClicked(); void slotJobResetStateButtonClicked(); void slotStartButtonClicked(); void slotPauseButtonClicked(); void slotResumeButtonClicked(); void slotAbortButtonClicked(); void slotJobDoubleClicked(const QModelIndex & a_index); void slotSelectionChanged(); void slotSaveHeaderState(); void slotJobsHeaderContextMenu(const QPoint & a_point); void slotShowJobsHeaderSection(bool a_show); void slotJobStateChanged(int a_job, JobState a_state); void slotJobProgressChanged(int a_job, int a_progress, int a_progressMax); void slotSetJobDependencies(const QUuid & a_id, std::vector a_dependencies); void slotServerConnected(); void slotServerDisconnected(); void slotBinaryMessageReceived(const QByteArray & a_message); void slotTextMessageReceived(const QString & a_message); void slotServerError(QAbstractSocket::SocketError a_error); void slotStartLocalServer(); void slotShutdownServer(); void slotConnectToServerDialog(); void slotReconnectToServer(); void slotConnectToServer(const QHostAddress & a_address); void slotConnectToLocalServer(); void slotShutdownServerAndExit(); void slotTaskServerNewConnection(); void slotTaskClientReadyRead(); void slotTaskClientDisconnected(); void slotSetTrustedClientsAddresses(); void slotSaveGeometry(); private: enum class WatcherState { NotConnected, ProbingLocal, StartingLocal, Connecting, Connected, Disconnecting, SwitchingServer, ShuttingDown, ClosingServerShuttingDown, }; void createActionsAndMenus(); void editJob(const QModelIndex & a_index); void processSMsgJobInfo(const QString & a_message); std::vector selectedIndexes(); void setUiEnabled(); void resetWindowTitle(int a_jobIndex); void changeState(WatcherState a_newState); void processTaskList(); void saveGeometryDelayed(); static const char WINDOW_TITLE[]; Ui::MainWindow m_ui; SettingsManager * m_pSettingsManager; JobsModel * m_pJobsModel; JobStateDelegate * m_pJobStateDelegate; JobDependenciesDelegate * m_pJobDependenciesDelegate; VSScriptLibrary * m_pVSScriptLibrary; JobEditDialog * m_pJobEditDialog; QMenu * m_pJobsHeaderMenu; QWebSocket * m_pServerSocket; int m_connectionAttempts; int m_maxConnectionAttempts; WatcherState m_state; QSystemTrayIcon * m_pTrayIcon; QMenu * m_pTrayMenu; QAction * m_pActionSetTrustedClientsAddresses; QAction * m_pActionExit; QAction * m_pActionShutdownServerAndExit; ConnectToServerDialog * m_pConnectToServerDialog; QHostAddress m_nextServerAddress; QLocalServer * m_pTaskServer; std::list m_taskClients; std::list m_taskList; QStringList m_trustedClientsAddresses; QTimer * m_pGeometrySaveTimer; QByteArray m_windowGeometry; }; #endif // MAINWINDOW_H ================================================ FILE: vsedit-job-server-watcher/src/main_window.ui ================================================ MainWindow 0 0 1015 756 :/watcher.ico:/watcher.ico 2 0 0 0 0 false QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel false false 4 0 2 2 2 New Edit Up Down Delete Reset state Qt::Vertical QSizePolicy::Preferred 20 16 Start Pause Resume Abort Qt::Vertical QSizePolicy::Preferred 20 16 Start server Connect to server Shutdown server Qt::Vertical 20 40 0 0 1015 21 QDockWidget::NoDockWidgetFeatures Qt::BottomDockWidgetArea Log 8 0 0 0 0 0 Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse VSEditorLog QTextEdit
../../common-src/log/vs_editor_log.h
================================================ FILE: vsedit-job-server-watcher/src/trusted_clients_addresses_dialog.cpp ================================================ #include "trusted_clients_addresses_dialog.h" #include //============================================================================== TrustedClientsAddressesDialog::TrustedClientsAddressesDialog( QWidget * a_pParent) : QDialog(a_pParent) { m_ui.setupUi(this); connect(m_ui.addButton, &QToolButton::clicked, this, &TrustedClientsAddressesDialog::slotAddButtonClicked); connect(m_ui.removeButton, &QToolButton::clicked, this, &TrustedClientsAddressesDialog::slotRemoveButtonClicked); connect(m_ui.okButton, &QPushButton::clicked, this, &TrustedClientsAddressesDialog::accept); connect(m_ui.cancelButton, &QPushButton::clicked, this, &TrustedClientsAddressesDialog::reject); } // END OF TrustedClientsAddressesDialog::TrustedClientsAddressesDialog( // QWidget * a_pParent) //============================================================================== TrustedClientsAddressesDialog::~TrustedClientsAddressesDialog() { } // END OF TrustedClientsAddressesDialog::~TrustedClientsAddressesDialog() //============================================================================== int TrustedClientsAddressesDialog::call(const QStringList & a_addresses) { m_ui.addressesList->clear(); QStringList clientAddresses = a_addresses; clientAddresses.removeDuplicates(); for(const QString & address : clientAddresses) checkAndAddAddress(address); return exec(); } // END OF int TrustedClientsAddressesDialog::call( // const QStringList & a_addresses) //============================================================================== QStringList TrustedClientsAddressesDialog::addresses() const { QStringList clientAddresses; for(int i = 0; i < m_ui.addressesList->count(); ++i) clientAddresses << m_ui.addressesList->item(i)->text(); return clientAddresses; } // END OF QStringList TrustedClientsAddressesDialog::addresses() const //============================================================================== void TrustedClientsAddressesDialog::slotAddButtonClicked() { checkAndAddAddress(m_ui.addressEdit->text()); } // END OF void TrustedClientsAddressesDialog::slotAddButtonClicked() //============================================================================== void TrustedClientsAddressesDialog::slotRemoveButtonClicked() { QList items = m_ui.addressesList->selectedItems(); for(QListWidgetItem * pItem : items) delete pItem; } // END OF void TrustedClientsAddressesDialog::slotRemoveButtonClicked() //============================================================================== void TrustedClientsAddressesDialog::checkAndAddAddress( const QString & a_address) { QHostAddress hostAddress(a_address); if(hostAddress.isLoopback() || (hostAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol)) return; m_ui.addressesList->addItem(a_address); m_ui.addressesList->sortItems(); } // END OF void TrustedClientsAddressesDialog::checkAndAddAddress( // const QString & a_address) //============================================================================== ================================================ FILE: vsedit-job-server-watcher/src/trusted_clients_addresses_dialog.h ================================================ #ifndef TRUSTED_CLIENTS_ADDRESSES_DIALOG_H_INCLUDED #define TRUSTED_CLIENTS_ADDRESSES_DIALOG_H_INCLUDED #include #include class TrustedClientsAddressesDialog : public QDialog { Q_OBJECT public: TrustedClientsAddressesDialog(QWidget * a_pParent = nullptr); virtual ~TrustedClientsAddressesDialog(); int call(const QStringList & a_addresses); QStringList addresses() const; private slots: void slotAddButtonClicked(); void slotRemoveButtonClicked(); private: void checkAndAddAddress(const QString & a_address); Ui::TrustedClientsAddressesDialog m_ui; }; #endif // TRUSTED_CLIENTS_ADDRESSES_DIALOG_H_INCLUDED ================================================ FILE: vsedit-job-server-watcher/src/trusted_clients_addresses_dialog.ui ================================================ TrustedClientsAddressesDialog 0 0 525 484 Trusted clients addresses 4 4 4 4 4 :/delete.png:/delete.png :/add.png:/add.png background-color: rgb(0, 0, 0); color: rgb(255, 0, 0); font-size: 24px; font-weight: bold; WARNING! Adding any address to this list opens your PC for a full control and makes it vulnerable for potential hacker attack! true 4 Qt::Horizontal 308 20 OK Cancel Qt::Vertical 20 40