diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..4647a9147 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,541 @@ +cmake_minimum_required(VERSION 3.14) +cmake_policy(SET CMP0074 NEW) + +project(Aegisub) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + +include_directories("build") +include_directories("libaegisub/include") +include_directories("vendor/luajit/include") + +add_library(libaegisub STATIC + libaegisub/common/parser.cpp + libaegisub/ass/dialogue_parser.cpp + libaegisub/ass/time.cpp + libaegisub/ass/uuencode.cpp + libaegisub/audio/provider.cpp + libaegisub/audio/provider_convert.cpp + libaegisub/audio/provider_dummy.cpp + libaegisub/audio/provider_hd.cpp + libaegisub/audio/provider_lock.cpp + libaegisub/audio/provider_pcm.cpp + libaegisub/audio/provider_ram.cpp + libaegisub/common/cajun/elements.cpp + libaegisub/common/cajun/reader.cpp + libaegisub/common/cajun/writer.cpp + libaegisub/lua/modules/lfs.cpp + libaegisub/lua/modules/re.cpp + libaegisub/lua/modules/unicode.cpp + libaegisub/lua/modules/lpeg.c + libaegisub/lua/modules.cpp + libaegisub/lua/script_reader.cpp + libaegisub/lua/utils.cpp + libaegisub/common/calltip_provider.cpp + libaegisub/common/character_count.cpp + libaegisub/common/charset.cpp + libaegisub/common/charset_6937.cpp + libaegisub/common/charset_conv.cpp + libaegisub/common/color.cpp + libaegisub/common/file_mapping.cpp + libaegisub/common/format.cpp + libaegisub/common/fs.cpp + libaegisub/common/hotkey.cpp + libaegisub/common/io.cpp + libaegisub/common/json.cpp + libaegisub/common/kana_table.cpp + libaegisub/common/karaoke_matcher.cpp + libaegisub/common/keyframe.cpp + libaegisub/common/line_iterator.cpp + libaegisub/common/log.cpp + libaegisub/common/mru.cpp + libaegisub/common/option.cpp + libaegisub/common/option_value.cpp + libaegisub/common/path.cpp + libaegisub/common/thesaurus.cpp + libaegisub/common/util.cpp + libaegisub/common/vfr.cpp + libaegisub/common/ycbcr_conv.cpp + libaegisub/common/dispatch.cpp +) +if (UNIX) + target_sources(libaegisub PRIVATE + libaegisub/unix/access.cpp + libaegisub/unix/fs.cpp + libaegisub/unix/log.cpp + libaegisub/unix/path.cpp + libaegisub/unix/util.cpp + ) +elseif(WIN32) + target_sources(libaegisub PRIVATE + libaegisub/windows/access.cpp + libaegisub/windows/charset_conv_win.cpp + libaegisub/windows/fs.cpp + libaegisub/windows/lagi_pre.cpp + libaegisub/windows/log_win.cpp + libaegisub/windows/path_win.cpp + libaegisub/windows/util_win.cpp + ) +endif(UNIX) +SET_TARGET_PROPERTIES(libaegisub PROPERTIES PREFIX "") + +add_library(luabins STATIC + vendor/luabins/src/fwrite.c + vendor/luabins/src/load.c + vendor/luabins/src/luabins.c + vendor/luabins/src/luainternals.c + vendor/luabins/src/save.c + vendor/luabins/src/savebuffer.c + vendor/luabins/src/write.c +) + +add_executable(luajit-minilua vendor/luajit/src/host/minilua.c) +if (NOT MSVC) +target_link_libraries(luajit-minilua m) +endif(NOT MSVC) +add_custom_command(TARGET luajit-minilua POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/vendor/luajit/src/gen + COMMAND luajit-minilua ../dynasm/dynasm.lua -D P64 -D JIT -D FFI -D FPU -D HFABI -D VER= -o gen/buildvm_arch.h vm_x86.dasc + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src +) +add_custom_command(TARGET luajit-minilua POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/src/libresrc/default_config_win.json ${PROJECT_SOURCE_DIR}/src/libresrc/default_config_platform.json + COMMAND luajit-minilua ../../tools/respack.lua manifest.respack default_config.cpp default_config.h + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/libresrc + BYPRODUCTS ${PROJECT_SOURCE_DIR}/src/libresrc/default_config.cpp ${PROJECT_SOURCE_DIR}/src/libresrc/default_config.h +) +add_custom_command(TARGET luajit-minilua POST_BUILD + COMMAND luajit-minilua ../../tools/respack.lua manifest.respack ../libresrc/bitmap.cpp ../libresrc/bitmap.h + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/bitmaps + BYPRODUCTS ${PROJECT_SOURCE_DIR}/src/libresrc/bitmap.cpp ${PROJECT_SOURCE_DIR}/src/libresrc/bitmap.h +) + +add_executable(luajit-buildvm + vendor/luajit/src/host/buildvm.c + vendor/luajit/src/host/buildvm_asm.c + vendor/luajit/src/host/buildvm_peobj.c + vendor/luajit/src/host/buildvm_lib.c + vendor/luajit/src/host/buildvm_fold.c +) +target_include_directories(luajit-buildvm PRIVATE vendor/luajit/src vendor/luajit/src/gen) +add_dependencies(luajit-buildvm luajit-minilua) +if(UNIX) + add_custom_command(TARGET luajit-buildvm POST_BUILD + COMMAND luajit-buildvm -m elfasm -o lj_vm.s + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src + BYPRODUCTS ${PROJECT_SOURCE_DIR}/vendor/luajit/src/lj_vm.s + ) + set_property(SOURCE vendor/luajit/src/lj_vm.s PROPERTY LANGUAGE C) +elseif(MSVC) + add_custom_command(TARGET luajit-buildvm POST_BUILD + COMMAND luajit-buildvm -m peobj -o lj_vm.obj + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src + BYPRODUCTS ${PROJECT_SOURCE_DIR}/vendor/luajit/src/lj_vm.obj + ) +endif(UNIX) +add_custom_command(TARGET luajit-buildvm POST_BUILD + COMMAND luajit-buildvm -m ffdef -o gen/lj_ffdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + COMMAND luajit-buildvm -m bcdef -o gen/lj_bcdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + COMMAND luajit-buildvm -m folddef -o gen/lj_folddef.h lj_opt_fold.c + COMMAND luajit-buildvm -m recdef -o gen/lj_recdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + COMMAND luajit-buildvm -m libdef -o gen/lj_libdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + COMMAND luajit-buildvm -m vmdef -o jit/vmdef.lua lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src +) + + +add_library(luajit STATIC + vendor/luajit/src/lj_gc.c + vendor/luajit/src/lj_err.c + vendor/luajit/src/lj_char.c + vendor/luajit/src/lj_bc.c + vendor/luajit/src/lj_obj.c + vendor/luajit/src/lj_str.c + vendor/luajit/src/lj_tab.c + vendor/luajit/src/lj_func.c + vendor/luajit/src/lj_udata.c + vendor/luajit/src/lj_meta.c + vendor/luajit/src/lj_debug.c + vendor/luajit/src/lj_state.c + vendor/luajit/src/lj_dispatch.c + vendor/luajit/src/lj_vmevent.c + vendor/luajit/src/lj_vmmath.c + vendor/luajit/src/lj_strscan.c + vendor/luajit/src/lj_api.c + vendor/luajit/src/lj_lex.c + vendor/luajit/src/lj_parse.c + vendor/luajit/src/lj_bcread.c + vendor/luajit/src/lj_bcwrite.c + vendor/luajit/src/lj_load.c + vendor/luajit/src/lj_ir.c + vendor/luajit/src/lj_opt_mem.c + vendor/luajit/src/lj_opt_fold.c + vendor/luajit/src/lj_opt_narrow.c + vendor/luajit/src/lj_opt_dce.c + vendor/luajit/src/lj_opt_loop.c + vendor/luajit/src/lj_opt_split.c + vendor/luajit/src/lj_opt_sink.c + vendor/luajit/src/lj_mcode.c + vendor/luajit/src/lj_snap.c + vendor/luajit/src/lj_record.c + vendor/luajit/src/lj_crecord.c + vendor/luajit/src/lj_ffrecord.c + vendor/luajit/src/lj_asm.c + vendor/luajit/src/lj_trace.c + vendor/luajit/src/lj_gdbjit.c + vendor/luajit/src/lj_ctype.c + vendor/luajit/src/lj_cdata.c + vendor/luajit/src/lj_cconv.c + vendor/luajit/src/lj_ccall.c + vendor/luajit/src/lj_ccallback.c + vendor/luajit/src/lj_carith.c + vendor/luajit/src/lj_clib.c + vendor/luajit/src/lj_cparse.c + vendor/luajit/src/lj_lib.c + vendor/luajit/src/lj_alloc.c + vendor/luajit/src/lib_aux.c + vendor/luajit/src/lib_base.c + vendor/luajit/src/lib_math.c + vendor/luajit/src/lib_bit.c + vendor/luajit/src/lib_string.c + vendor/luajit/src/lib_table.c + vendor/luajit/src/lib_io.c + vendor/luajit/src/lib_os.c + vendor/luajit/src/lib_package.c + vendor/luajit/src/lib_debug.c + vendor/luajit/src/lib_jit.c + vendor/luajit/src/lib_ffi.c + vendor/luajit/src/lib_init.c +) +if(MSVC) + target_sources(luajit PRIVATE vendor/luajit/src/lj_vm.obj) +else(MSVC) + target_sources(luajit PRIVATE vendor/luajit/src/lj_vm.s) + set_property(SOURCE vendor/luajit/src/lj_vm.s PROPERTY LANGUAGE C) + target_link_libraries(luajit dl) +endif(MSVC) +target_include_directories(luajit PRIVATE vendor/luajit/src/gen) +add_dependencies(luajit luajit-buildvm) +target_compile_definitions(luajit PRIVATE LUAJIT_ENABLE_LUA52COMPAT) + +add_library(resrc STATIC + src/libresrc/bitmap.cpp + src/libresrc/default_config.cpp + src/libresrc/libresrc.cpp +) +add_dependencies(resrc luajit-minilua) + +add_library(csri STATIC + vendor/csri/lib/list.c + vendor/csri/lib/wrap.c + vendor/csri/subhelp/logging.c +) +target_include_directories(csri PRIVATE "vendor/csri/include") +IF (WIN32) + target_include_directories(csri PRIVATE "vendor/csri/lib/win32") + target_sources(csri PRIVATE vendor/csri/lib/win32/enumerate.c) +ELSE() + target_include_directories(csri PRIVATE "vendor/csri/lib/posix") + target_sources(csri PRIVATE vendor/csri/lib/posix/enumerate.c) +ENDIF() + +add_executable(Aegisub WIN32 + src/command/app.cpp + src/command/audio.cpp + src/command/automation.cpp + src/command/command.cpp + src/command/edit.cpp + src/command/grid.cpp + src/command/help.cpp + src/command/keyframe.cpp + src/command/recent.cpp + src/command/subtitle.cpp + src/command/time.cpp + src/command/timecode.cpp + src/command/tool.cpp + src/command/video.cpp + src/command/vis_tool.cpp + src/dialog_about.cpp + src/dialog_align.cpp + src/dialog_attachments.cpp + src/dialog_automation.cpp + src/dialog_autosave.cpp + src/dialog_colorpicker.cpp + src/dialog_detached_video.cpp + src/dialog_dummy_video.cpp + src/dialog_export.cpp + src/dialog_export_ebu3264.cpp + src/dialog_fonts_collector.cpp + src/dialog_jumpto.cpp + src/dialog_kara_timing_copy.cpp + src/dialog_log.cpp + src/dialog_paste_over.cpp + src/dialog_progress.cpp + src/dialog_properties.cpp + src/dialog_resample.cpp + src/dialog_search_replace.cpp + src/dialog_selected_choices.cpp + src/dialog_selection.cpp + src/dialog_shift_times.cpp + src/dialog_spellchecker.cpp + src/dialog_style_editor.cpp + src/dialog_style_manager.cpp + src/dialog_styling_assistant.cpp + src/dialog_text_import.cpp + src/dialog_timing_processor.cpp + src/dialog_translation.cpp + src/dialog_version_check.cpp + src/dialog_video_details.cpp + src/dialog_video_properties.cpp + src/subtitle_format.cpp + src/subtitle_format_ass.cpp + src/subtitle_format_ebu3264.cpp + src/subtitle_format_encore.cpp + src/subtitle_format_microdvd.cpp + src/subtitle_format_mkv.cpp + src/subtitle_format_srt.cpp + src/subtitle_format_ssa.cpp + src/subtitle_format_transtation.cpp + src/subtitle_format_ttxt.cpp + src/subtitle_format_txt.cpp + src/visual_tool.cpp + src/visual_tool_clip.cpp + src/visual_tool_cross.cpp + src/visual_tool_drag.cpp + src/visual_tool_rotatexy.cpp + src/visual_tool_rotatez.cpp + src/visual_tool_scale.cpp + src/visual_tool_vector_clip.cpp + src/MatroskaParser.c + src/aegisublocale.cpp + src/ass_attachment.cpp + src/ass_dialogue.cpp + src/ass_entry.cpp + src/ass_export_filter.cpp + src/ass_exporter.cpp + src/ass_file.cpp + src/ass_karaoke.cpp + src/ass_override.cpp + src/ass_parser.cpp + src/ass_style.cpp + src/ass_style_storage.cpp + src/async_video_provider.cpp + src/audio_box.cpp + src/audio_colorscheme.cpp + src/audio_controller.cpp + src/audio_display.cpp + src/audio_karaoke.cpp + src/audio_marker.cpp + src/audio_player.cpp + src/audio_provider_factory.cpp + src/audio_renderer.cpp + src/audio_renderer_spectrum.cpp + src/audio_renderer_waveform.cpp + src/audio_timing_dialogue.cpp + src/audio_timing_karaoke.cpp + src/auto4_base.cpp + src/auto4_lua.cpp + src/auto4_lua_assfile.cpp + src/auto4_lua_dialog.cpp + src/auto4_lua_progresssink.cpp + src/base_grid.cpp + src/charset_detect.cpp + src/colorspace.cpp + src/colour_button.cpp + src/compat.cpp + src/context.cpp + src/export_fixstyle.cpp + src/export_framerate.cpp + src/fft.cpp + src/font_file_lister.cpp + src/frame_main.cpp + src/gl_text.cpp + src/gl_wrap.cpp + src/grid_column.cpp + src/help_button.cpp + src/hotkey.cpp + src/hotkey_data_view_model.cpp + src/image_position_picker.cpp + src/initial_line_state.cpp + src/main.cpp + src/menu.cpp + src/mkv_wrap.cpp + src/pen.cpp + src/persist_location.cpp + src/preferences.cpp + src/preferences_base.cpp + src/project.cpp + src/resolution_resampler.cpp + src/search_replace_engine.cpp + src/selection_controller.cpp + src/spellchecker.cpp + src/spline.cpp + src/spline_curve.cpp + src/string_codec.cpp + src/subs_controller.cpp + src/subs_edit_box.cpp + src/subs_edit_ctrl.cpp + src/subs_preview.cpp + src/subtitles_provider.cpp + src/subtitles_provider_libass.cpp + src/text_file_reader.cpp + src/text_file_writer.cpp + src/text_selection_controller.cpp + src/thesaurus.cpp + src/timeedit_ctrl.cpp + src/toggle_bitmap.cpp + src/toolbar.cpp + src/tooltip_manager.cpp + src/utils.cpp + src/validators.cpp + src/vector2d.cpp + src/version.cpp + src/video_box.cpp + src/video_controller.cpp + src/video_display.cpp + src/video_frame.cpp + src/video_out_gl.cpp + src/video_provider_cache.cpp + src/video_provider_dummy.cpp + src/video_provider_manager.cpp + src/video_provider_yuv4mpeg.cpp + src/video_slider.cpp + src/visual_feature.cpp +) +target_link_libraries(Aegisub ${CMAKE_DL_LIBS} libaegisub luabins luajit resrc csri) + +if (MSVC) + set_target_properties(libaegisub PROPERTIES COMPILE_FLAGS "/Yu${PROJECT_SOURCE_DIR}/libaegisub/lagi_pre.h" COMPILE_FLAGS "/FI${PROJECT_SOURCE_DIR}/libaegisub/lagi_pre.h") +else(MSVC) + target_compile_options(libaegisub PRIVATE -include "${PROJECT_SOURCE_DIR}/libaegisub/lagi_pre.h") +endif(MSVC) + +set_property( + SOURCE libaegisub/unix/path.cpp + PROPERTY COMPILE_DEFINITIONS + P_DATA="${CMAKE_INSTALL_PREFIX}/share/aegisub/" +) + +if (MSVC) + add_definitions("-DNOMINMAX -MP -DINITGUID") + set_target_properties(Aegisub PROPERTIES COMPILE_FLAGS "/Yu${PROJECT_SOURCE_DIR}/src/agi_pre.h" COMPILE_FLAGS "/FI${PROJECT_SOURCE_DIR}/src/agi_pre.h") + target_link_libraries (Aegisub Usp10) + #target_sources(Aegisub PRIVATE src/res/res.rc src/res/strings.rc src/crash_writer_minidump.cpp) + target_sources(Aegisub PRIVATE src/res/res.rc src/res/strings.rc src/crash_writer.cpp src/dpi_aware.manifest) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Aegisub) +else(MSVC) + target_sources(Aegisub PRIVATE src/crash_writer.cpp) + target_compile_options(Aegisub PRIVATE -include "${PROJECT_SOURCE_DIR}/src/agi_pre.h") +endif(MSVC) + +if (WIN32) + target_sources(Aegisub PRIVATE src/font_file_lister_gdi.cpp) +else (WIN32) + find_package(Fontconfig REQUIRED) + target_link_libraries (Aegisub ${Fontconfig_LIBRARIES}) + target_sources(Aegisub PRIVATE src/font_file_lister_fontconfig.cpp) + set_property(SOURCE src/font_file_lister_fontconfig.cpp PROPERTY INCLUDE_DIRECTORIES "${Fontconfig_INCLUDE_DIRS}") +endif (WIN32) + +find_package(ass REQUIRED) +include_directories(${ass_INCLUDE_DIRS}) +target_link_libraries (Aegisub ${ass_LIBRARIES}) + +find_package(Boost REQUIRED chrono filesystem locale regex system thread) +include_directories(${Boost_INCLUDE_DIRS}) +target_link_directories(Aegisub PRIVATE ${Boost_LIBRARY_DIRS}) +target_link_libraries(Aegisub ${Boost_LIBRARIES}) + +find_package(OpenGL REQUIRED) +include_directories(${OPENGL_INCLUDE_DIR}) +target_link_libraries (Aegisub ${OPENGL_LIBRARIES}) + +find_package(Hunspell REQUIRED) +include_directories(${HUNSPELL_INCLUDE_DIR}) +target_link_libraries (Aegisub ${HUNSPELL_LIBRARIES}) +add_definitions("-DWITH_HUNSPELL") +target_sources(Aegisub PRIVATE src/spellchecker_hunspell.cpp) + +find_package(Iconv REQUIRED) +include_directories(${Iconv_INCLUDE_DIRS}) +target_link_libraries (Aegisub ${Iconv_LIBRARIES}) +add_definitions("-DHAVE_ICONV") +if (NOT Iconv_IS_BUILT_IN) +set_property( + SOURCE libaegisub/common/charset_conv.cpp + PROPERTY COMPILE_DEFINITIONS AGI_ICONV_CONST +) +endif (NOT Iconv_IS_BUILT_IN) + +find_package(ICU REQUIRED uc dt in) +include_directories(${ICU_INCLUDE_DIRS}) +target_link_libraries (Aegisub ${ICU_LIBRARIES}) + +find_package(wxWidgets REQUIRED adv base core gl stc xml) +include(${wxWidgets_USE_FILE}) +target_link_libraries(Aegisub ${wxWidgets_LIBRARIES}) + +find_package(ZLIB REQUIRED) +include_directories(${ZLIB_INCLUDE_DIRS}) +target_link_libraries (Aegisub ${ZLIB_LIBRARIES}) + +find_package(ALSA) +if (ALSA_FOUND) + include_directories(${ALSA_INCLUDE_DIRS}) + target_link_libraries (Aegisub ${ALSA_LIBRARIES}) + add_definitions("-DWITH_ALSA") + target_sources(Aegisub PRIVATE src/audio_player_alsa.cpp) +endif(ALSA_FOUND) + +# target_compile_definitions(Aegisub PRIVATE "WITH_AVISYNTH") +# target_sources(Aegisub PRIVATE src/audio_provider_avs.cpp src/avisynth_wrap.cpp src/video_provider_avs.cpp) + +target_compile_definitions(Aegisub PRIVATE "WITH_CSRI") +target_sources(Aegisub PRIVATE src/subtitles_provider_csri.cpp) +set_property(SOURCE src/subtitles_provider_csri.cpp PROPERTY INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/vendor/csri/include") + +if(MSVC) + target_link_libraries (Aegisub dsound) + add_definitions("-DWITH_DIRECTSOUND") + target_sources(Aegisub PRIVATE src/audio_player_dsound.cpp src/audio_player_dsound2.cpp) +endif(MSVC) + +find_package(FFMS2) +if (FFMS2_FOUND) + include_directories(${FFMS2_INCLUDE_DIRS}) + target_link_libraries (Aegisub ${FFMS2_LIBRARIES}) + add_definitions("-DWITH_FFMS2") + target_sources(Aegisub PRIVATE src/audio_provider_ffmpegsource.cpp src/ffmpegsource_common.cpp src/video_provider_ffmpegsource.cpp) +endif(FFMS2_FOUND) + +find_package(FFTW) +if (FFTW_FOUND) + include_directories(${FFTW_INCLUDES}) + target_link_libraries (Aegisub ${FFTW_LIBRARIES}) + add_definitions("-DWITH_FFTW3") +endif(FFTW_FOUND) + +#ifdef WITH_LIBPULSE +#add_definitions("-DWITH_LIBPULSE") +#target_sources(Aegisub PRIVATE src/audio_player_pulse.cpp) + +find_package(OpenAL) +if (OPENAL_FOUND) + include_directories(${OPENAL_INCLUDE_DIR}) + target_link_libraries (Aegisub ${OPENAL_LIBRARY}) + add_definitions("-DWITH_OPENAL") + target_sources(Aegisub PRIVATE src/audio_player_openal.cpp) +endif(OPENAL_FOUND) + +#ifdef WITH_OSS +#ifdef WITH_PORTAUDIO +#ifdef WITH_STARTUPLOG + +find_package(uchardet) +if (uchardet_FOUND) + include_directories(${uchardet_INCLUDE_DIRS}) + target_link_libraries (Aegisub ${uchardet_LIBRARIES}) + add_definitions("-DWITH_UCHARDET") +endif(uchardet_FOUND) + +#ifdef WITH_UPDATE_CHECKER diff --git a/README.md b/README.md index 618fe9867..478ee14b8 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,15 @@ The `cibuilds` branch makes some CI builds of snapshots of `feature` at relevant ### Branch/Feature list - [`folding`](https://github.com/arch1t3cht/Aegisub/tree/folding): Add the ability to visually group and collapse lines in the subtitle grid +- [`lua_api`](https://github.com/arch1t3cht/Aegisub/tree/lua_api): Add new functions to the Lua automation API, like controlling the selection or cursor in the text edit box - [`vector_clip_actions`](https://github.com/arch1t3cht/Aegisub/tree/vector_clip_actions): Make the different modes of the vector clip tool (lines, bezier curves, adding points, etc) bindable to hotkeys - [`color_picker_fix2`](https://github.com/arch1t3cht/Aegisub/tree/color_picker_fix2): Add an option (under "Interface") to restrict the color picker to the window, which fixes the color picker on Linux in a lot of cases. - [`avisynth`](https://github.com/arch1t3cht/Aegisub/tree/avisynth): Reenable Avisynth support on Windows (Still occasionally crashes) - [`bugfixes`](https://github.com/arch1t3cht/Aegisub/tree/bugfixes): Various fixes, mostly relevant for compilation - [`misc_dc`](https://github.com/arch1t3cht/Aegisub/tree/misc_dc): Miscellaneous changes taken from AegisubDC -- [`video_panning_feature`](https://github.com/arch1t3cht/Aegisub/tree/video_panning_feature): Merge [moex3's video zoom and panning](https://github.com/TypesettingTools/Aegisub/pull/150), with an OSX fix and an option to toggle the behavior of Ctrl+Zoom +- [`video_panning_feature`](https://github.com/arch1t3cht/Aegisub/tree/video_panning_feature): Merge [moex3's video zoom and panning](https://github.com/TypesettingTools/Aegisub/pull/150), with an OSX fix and more options to control zoom behavior - [`spectrum-frequency-mapping`](https://github.com/arch1t3cht/Aegisub/tree/spectrum-frequency-mapping): Merge EleonoreMizo's [spectrum display improvements](https://github.com/TypesettingTools/Aegisub/pull/94), and also make Shift+Scroll vertically zoom the audio display +- [`wangqr_time_video`](https://github.com/arch1t3cht/Aegisub/tree/wangqr_time_video): Merge wangqr's feature adding a tool for timing subtitles to changes in the video ### Troubleshooting #### Building fails with a "CMake sandbox violation" @@ -43,6 +45,9 @@ The changes to `default_config.json` or similar files weren't detected by meson #### The video is desynced / Frames don't appear at the right time This is probably due to the ffms2 seeking bug ([#394](https://github.com/FFMS/ffms2/issues/394)). On Windows, this shouldn't happen anymore. On Linux, you need to install the latest git version of ffms2 - for example the [`ffms2-git`](https://aur.archlinux.org/packages/ffms2-git) AUR package on Arch linux, or just compile it yourself. +#### On Windows: Aegisub crashes whenever I open a video +If you're compiling yourself, try adding `--force-fallback-for=zlib` to the meson options. + # Aegisub diff --git a/automation/v4-docs/gui.txt b/automation/v4-docs/gui.txt new file mode 100644 index 000000000..1ec617805 --- /dev/null +++ b/automation/v4-docs/gui.txt @@ -0,0 +1,57 @@ +Automation 4 Gui Functions + +This document describes the available Automation 4 functions for +controlling the editor's graphical interface. These all reside in the +table aegisub.gui . + +--- + +Getting and setting the selection and cursor in the text edit box + +This set of functions controls the selection in the text edit box. +All indices are counted starting from 1, following Lua conventions. +The setter functions are applied after all subtitle changes have been +applied. Only the latest update is applied. +The getter functions return the state after the latest update by +the setter functions, or the original state if there were none. + + +function aegisub.gui.get_cursor() + +Returns: 1 number + 1. The position of the cursor in the text edit field. + +--- + +function aegisub.get_selection() + +Returns: 2 values, all numbers. + 1. Starting position of the selection. + 2. Ending position of the selection, always larger or equal + than the stating position. + +--- + +function aegisub.gui.set_cursor(position) + +@position (number) + The new position of the cursor. + +Returns: 0 values + +--- + +function aegisub.gui.set_selection(start, end) + +@start (number) + The new start of the selection. + +@end (number) + The new end of the selection, i.e. where the cursor will be. + Can be smaller than the start, in which case the cursor will + be on the left side of the selection. + +Returns: 0 values + +--- + diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index 6c131902e..bba6e6aeb 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -51,6 +51,7 @@ #include "selection_controller.h" #include "subs_controller.h" #include "video_controller.h" +#include "text_selection_controller.h" #include "utils.h" #include @@ -285,6 +286,39 @@ namespace { return 0; } + int lua_get_text_cursor(lua_State *L) + { + push_value(L, get_context(L)->textSelectionController->GetStagedInsertionPoint() + 1); + return 1; + } + + int lua_set_text_cursor(lua_State *L) + { + int point = lua_tointeger(L, -1) - 1; + lua_pop(L, 1); + get_context(L)->textSelectionController->StageSetInsertionPoint(point); + return 0; + } + + int lua_get_text_selection(lua_State *L) + { + const agi::Context *c = get_context(L); + int start = c->textSelectionController->GetStagedSelectionStart() + 1; + int end = c->textSelectionController->GetStagedSelectionEnd() + 1; + push_value(L, start <= end ? start : end); + push_value(L, start <= end ? end : start); + return 2; + } + + int lua_set_text_selection(lua_State *L) + { + int start = lua_tointeger(L, -2) - 1; + int end = lua_tointeger(L, -1) - 1; + lua_pop(L, 2); + get_context(L)->textSelectionController->StageSetSelection(start, end); + return 0; + } + int project_properties(lua_State *L) { const agi::Context *c = get_context(L); @@ -489,6 +523,12 @@ namespace { set_field(L, "project_properties"); set_field(L, "get_audio_selection"); set_field(L, "set_status_text"); + lua_createtable(L, 0, 4); + set_field(L, "get_cursor"); + set_field(L, "set_cursor"); + set_field(L, "get_selection"); + set_field(L, "set_selection"); + lua_setfield(L, -2, "gui"); // store aegisub table to globals lua_settable(L, LUA_GLOBALSINDEX); @@ -786,6 +826,7 @@ namespace { void LuaCommand::operator()(agi::Context *c) { + c->textSelectionController->DropStagedChanges(); LuaStackcheck stackcheck(L); set_context(L, c); stackcheck.check_stack(0); @@ -883,6 +924,7 @@ namespace { new_active = *new_sel.begin(); c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active); } + c->textSelectionController->CommitStagedChanges(); stackcheck.check_stack(0); } diff --git a/src/auto4_lua_assfile.cpp b/src/auto4_lua_assfile.cpp index f98e0a35d..c3e3902e6 100644 --- a/src/auto4_lua_assfile.cpp +++ b/src/auto4_lua_assfile.cpp @@ -102,13 +102,19 @@ namespace { } template - void get_userdata_field(lua_State *L, const char *name, const char *line_class, T *target) + bool get_userdata_field(lua_State *L, const char *name, const char *line_class, T *target, bool required) { lua_getfield(L, -1, name); - if (!lua_isuserdata(L, -1)) + if (!lua_isuserdata(L, -1)) { + if (!required) { + lua_pop(L, 1); + return false; + } throw bad_field("userdata", name, line_class); + } *target = *static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); + return true; } using namespace Automation4; @@ -316,7 +322,9 @@ namespace Automation4 { dia->Margin[2] = get_int_field(L, "margin_t", "dialogue"); dia->Effect = get_string_field(L, "effect", "dialogue"); dia->Text = get_string_field(L, "text", "dialogue"); - get_userdata_field(L, "_foldinfo", "dialogue", &dia->Fold); + if (!get_userdata_field(L, "_foldinfo", "dialogue", &dia->Fold, false)) { + dia->Fold = FoldInfo(); + } std::vector new_ids; diff --git a/src/bitmaps/button/button_align_16.png b/src/bitmaps/button/button_align_16.png new file mode 100644 index 000000000..07ce86c1b Binary files /dev/null and b/src/bitmaps/button/button_align_16.png differ diff --git a/src/bitmaps/button/button_align_24.png b/src/bitmaps/button/button_align_24.png new file mode 100644 index 000000000..8e98fee9d Binary files /dev/null and b/src/bitmaps/button/button_align_24.png differ diff --git a/src/bitmaps/button/button_align_32.png b/src/bitmaps/button/button_align_32.png new file mode 100644 index 000000000..958f4b34e Binary files /dev/null and b/src/bitmaps/button/button_align_32.png differ diff --git a/src/bitmaps/button/button_align_48.png b/src/bitmaps/button/button_align_48.png new file mode 100644 index 000000000..9d28cffad Binary files /dev/null and b/src/bitmaps/button/button_align_48.png differ diff --git a/src/bitmaps/button/button_align_64.png b/src/bitmaps/button/button_align_64.png new file mode 100644 index 000000000..0d6039c0f Binary files /dev/null and b/src/bitmaps/button/button_align_64.png differ diff --git a/src/bitmaps/manifest.respack b/src/bitmaps/manifest.respack index 2dc110d3a..55c731bd9 100644 --- a/src/bitmaps/manifest.respack +++ b/src/bitmaps/manifest.respack @@ -51,6 +51,11 @@ button/bugtracker_button_24.png button/bugtracker_button_32.png button/bugtracker_button_48.png button/bugtracker_button_64.png +button/button_align_16.png +button/button_align_24.png +button/button_align_32.png +button/button_align_48.png +button/button_align_64.png button/button_audio_commit_16.png button/button_audio_commit_24.png button/button_audio_commit_32.png diff --git a/src/colour_button.h b/src/colour_button.h index b2e0f9335..6234dc9fc 100644 --- a/src/colour_button.h +++ b/src/colour_button.h @@ -43,6 +43,12 @@ public: /// Get the currently selected color agi::Color GetColor() { return colour; } + + void SetColor(agi::Color color) + { + colour = color; + UpdateBitmap(); + } }; struct ColorValidator final : public wxValidator { diff --git a/src/command/time.cpp b/src/command/time.cpp index d703ebdc0..c44216db2 100644 --- a/src/command/time.cpp +++ b/src/command/time.cpp @@ -235,6 +235,18 @@ struct time_snap_scene final : public validate_video_loaded { } }; +struct time_align_subtitle_to_point final : public validate_video_loaded { + CMD_NAME("time/align") + CMD_ICON(button_align) + STR_MENU("Align subtitle to video") + STR_DISP("Align subtitle to video") + STR_HELP("Align subtitle to video by key points") + void operator()(agi::Context* c) override { + c->videoController->Stop(); + ShowAlignToVideoDialog(c); + } +}; + struct time_add_lead_both final : public Command { CMD_NAME("time/lead/both") STR_MENU("Add lead in and out") @@ -393,6 +405,7 @@ namespace cmd { reg(agi::make_unique()); reg(agi::make_unique()); reg(agi::make_unique()); + reg(agi::make_unique()); reg(agi::make_unique()); reg(agi::make_unique()); } diff --git a/src/dialog_align.cpp b/src/dialog_align.cpp new file mode 100644 index 000000000..93e506636 --- /dev/null +++ b/src/dialog_align.cpp @@ -0,0 +1,375 @@ +// Copyright (c) 2019, Charlie Jiang +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Aegisub Group nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Aegisub Project http://www.aegisub.org/ + +#include "ass_dialogue.h" +#include "ass_file.h" +#include "compat.h" +#include "dialog_manager.h" +#include "format.h" +#include "include/aegisub/context.h" +#include "video_frame.h" +#include "libresrc/libresrc.h" +#include "options.h" +#include "project.h" +#include "selection_controller.h" +#include "video_controller.h" +#include "async_video_provider.h" +#include "colour_button.h" +#include "image_position_picker.h" + +#include + +#include +#include + +#include +#include +#include +#if BOOST_VERSION >= 106900 +#include +#else +#include +#endif + +namespace { + class DialogAlignToVideo final : public wxDialog { + agi::Context* context; + AsyncVideoProvider* provider; + + wxImage preview_image; + VideoFrame current_frame; + int current_n_frame; + + ImagePositionPicker* preview_frame; + ColourButton* selected_color; + wxTextCtrl* selected_x; + wxTextCtrl* selected_y; + wxTextCtrl* selected_tolerance; + + void update_from_textbox(); + void update_from_textbox(wxCommandEvent&); + + bool check_exists(int pos, int x, int y, int* lrud, double* orig, unsigned char tolerance); + void process(wxEvent&); + public: + DialogAlignToVideo(agi::Context* context); + ~DialogAlignToVideo(); + }; + + DialogAlignToVideo::DialogAlignToVideo(agi::Context* context) + : wxDialog(context->parent, -1, _("Align subtitle to video by key point"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxRESIZE_BORDER) + , context(context), provider(context->project->VideoProvider()) + { + auto add_with_label = [&](wxSizer * sizer, wxString const& label, wxWindow * ctrl) { + sizer->Add(new wxStaticText(this, -1, label), 0, wxLEFT | wxRIGHT | wxCENTER, 3); + sizer->Add(ctrl, 1, wxLEFT); + }; + + auto tolerance = OPT_GET("Tool/Align to Video/Tolerance")->GetInt(); + auto maximized = OPT_GET("Tool/Align to Video/Maximized")->GetBool(); + + current_n_frame = context->videoController->GetFrameN(); + current_frame = *context->project->VideoProvider()->GetFrame(current_n_frame, 0, true); + preview_image = GetImage(current_frame); + + preview_frame = new ImagePositionPicker(this, preview_image, [&](int x, int y, unsigned char r, unsigned char g, unsigned char b) -> void { + selected_x->ChangeValue(wxString::Format(wxT("%i"), x)); + selected_y->ChangeValue(wxString::Format(wxT("%i"), y)); + + selected_color->SetColor(agi::Color(r, g, b)); + }); + selected_color = new ColourButton(this, wxSize(55, 16), true, agi::Color("FFFFFF")); + selected_color->SetToolTip(_("The key color to be followed")); + selected_x = new wxTextCtrl(this, -1, "0"); + selected_x->SetToolTip(_("The x coord of the key point")); + selected_y = new wxTextCtrl(this, -1, "0"); + selected_y->SetToolTip(_("The y coord of the key point")); + selected_tolerance = new wxTextCtrl(this, -1, wxString::Format(wxT("%i"), int(tolerance))); + selected_tolerance->SetToolTip(_("Max tolerance of the color")); + + selected_x->Bind(wxEVT_TEXT, &DialogAlignToVideo::update_from_textbox, this); + selected_y->Bind(wxEVT_TEXT, &DialogAlignToVideo::update_from_textbox, this); + update_from_textbox(); + + wxFlexGridSizer* right_sizer = new wxFlexGridSizer(4, 2, 5, 5); + add_with_label(right_sizer, _("X"), selected_x); + add_with_label(right_sizer, _("Y"), selected_y); + add_with_label(right_sizer, _("Color"), selected_color); + add_with_label(right_sizer, _("Tolerance"), selected_tolerance); + right_sizer->AddGrowableCol(1, 1); + + wxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL); + + main_sizer->Add(preview_frame, 1, (wxALL & ~wxRIGHT) | wxEXPAND, 5); + main_sizer->Add(right_sizer, 0, wxALIGN_LEFT, 5); + + wxSizer* dialog_sizer = new wxBoxSizer(wxVERTICAL); + dialog_sizer->Add(main_sizer, wxSizerFlags(1).Border(wxALL & ~wxBOTTOM).Expand()); + dialog_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Right().Border()); + SetSizerAndFit(dialog_sizer); + SetSize(1024, 700); + CenterOnParent(); + + Bind(wxEVT_BUTTON, &DialogAlignToVideo::process, this, wxID_OK); + Bind(wxEVT_LEFT_DCLICK, &DialogAlignToVideo::process, this, preview_frame->GetId()); + SetIcon(GETICON(button_align_16)); + if (maximized) + wxDialog::Maximize(true); + } + + DialogAlignToVideo::~DialogAlignToVideo() + { + long lt; + if (!selected_tolerance->GetValue().ToLong(<)) + return; + if (lt < 0 || lt > 255) + return; + + OPT_SET("Tool/Align to Video/Tolerance")->SetInt(lt); + } + + void rgb2lab(unsigned char r, unsigned char g, unsigned char b, double* lab) + { + double X = (0.412453 * r + 0.357580 * g + 0.180423 * b) / 255.0; + double Y = (0.212671 * r + 0.715160 * g + 0.072169 * b) / 255.0; + double Z = (0.019334 * r + 0.119193 * g + 0.950227 * b) / 255.0; + double xr = X / 0.950456, yr = Y / 1.000, zr = Z / 1.088854; + + if (yr > 0.008856) { + lab[0] = 116.0 * pow(yr, 1.0 / 3.0) - 16.0; + } + else { + lab[0] = 903.3 * yr; + } + + double fxr, fyr, fzr; + if (xr > 0.008856) + fxr = pow(xr, 1.0 / 3.0); + else + fxr = 7.787 * xr + 16.0 / 116.0; + + if (yr > 0.008856) + fyr = pow(yr, 1.0 / 3.0); + else + fyr = 7.787 * yr + 16.0 / 116.0; + + if (zr > 0.008856) + fzr = pow(zr, 1.0 / 3.0); + else + fzr = 7.787 * zr + 16.0 / 116.0; + + lab[1] = 500.0 * (fxr - fyr); + lab[2] = 200.0 * (fyr - fzr); + } + + template + bool check_point(boost::gil::pixel & pixel, double orig[3], unsigned char tolerance) + { + double lab[3]; + // in pixel: B,G,R + rgb2lab(pixel[2], pixel[1], pixel[0], lab); + auto diff = sqrt(pow(lab[0] - orig[0], 2) + pow(lab[1] - orig[1], 2) + pow(lab[2] - orig[2], 2)); + return diff <= tolerance; + } + + template + bool calculate_point(boost::gil::image_view view, int x, int y, double orig[3], unsigned char tolerance, int* ret) + { + auto origin = *view.at(x, y); + if (!check_point(origin, orig, tolerance)) + return false; + auto w = view.width(); + auto h = view.height(); + int l = x, r = x, u = y, d = y; + for (int i = x + 1; i < w; i++) + { + auto p = *view.at(i, y); + if (!check_point(p, orig, tolerance)) + { + r = i; + break; + } + } + + for (int i = x - 1; i >= 0; i--) + { + auto p = *view.at(i, y); + if (!check_point(p, orig, tolerance)) + { + l = i; + break; + } + } + + for (int i = y + 1; i < h; i++) + { + auto p = *view.at(x, i); + if (!check_point(p, orig, tolerance)) + { + d = i; + break; + } + } + + for (int i = y - 1; i >= 0; i--) + { + auto p = *view.at(x, i); + if (!check_point(p, orig, tolerance)) + { + u = i; + break; + } + } + ret[0] = l; + ret[1] = r; + ret[2] = u; + ret[3] = d; + return true; + } + + void DialogAlignToVideo::process(wxEvent &) + { + auto n_frames = provider->GetFrameCount(); + auto w = provider->GetWidth(); + auto h = provider->GetHeight(); + + long lx, ly, lt; + if (!selected_x->GetValue().ToLong(&lx) || !selected_y->GetValue().ToLong(&ly) || !selected_tolerance->GetValue().ToLong(<)) + { + wxMessageBox(_("Bad x or y position or tolerance value!")); + return; + } + if (lx < 0 || ly < 0 || lx >= w || ly >= h) + { + wxMessageBox(wxString::Format(_("Bad x or y position! Require: 0 <= x < %i, 0 <= y < %i"), w, h)); + return; + } + if (lt < 0 || lt > 255) + { + wxMessageBox(_("Bad tolerance value! Require: 0 <= torlerance <= 255")); + return; + } + int x = int(lx), y = int(ly); + unsigned char tolerance = (unsigned char)(lt); + + auto color = selected_color->GetColor(); + auto r = color.r; + auto b = color.b; + auto g = color.g; + double lab[3]; + rgb2lab(r, g, b, lab); + + int pos = current_n_frame; + auto frame = provider->GetFrame(pos, -1, true); + auto view = interleaved_view(frame->width, frame->height, reinterpret_cast(frame->data.data()), frame->pitch); + if (frame->flipped) + y = frame->height - y; + + // Ensure selected color and position match + if(!check_point(*view.at(x,y), lab, tolerance)) + { + wxMessageBox(_("Selected position and color are not within tolerance!")); + return; + } + + int lrud[4]; + calculate_point(view, x, y, lab, tolerance, lrud); + + // find forward +#define CHECK_EXISTS_POS check_exists(pos, x, y, lrud, lab, tolerance) + do { + pos -= 2; + } while (pos >= 0 && CHECK_EXISTS_POS); + pos++; + pos = std::max(0, pos); + auto left = CHECK_EXISTS_POS ? pos : pos + 1; + + pos = current_n_frame; + do { + pos += 2; + } while (pos < n_frames && CHECK_EXISTS_POS); + pos--; + pos = std::min(pos, n_frames - 1); + auto right = CHECK_EXISTS_POS ? pos : pos - 1; + + auto timecode = context->project->Timecodes(); + auto line = context->selectionController->GetActiveLine(); + line->Start = timecode.TimeAtFrame(left, agi::vfr::Time::START); + line->End = timecode.TimeAtFrame(right, agi::vfr::Time::END); // exclusive + context->ass->Commit(_("Align to video by key point"), AssFile::COMMIT_DIAG_TIME); + Close(); + } + + + + bool DialogAlignToVideo::check_exists(int pos, int x, int y, int* lrud, double* orig, unsigned char tolerance) + { + auto frame = provider->GetFrame(pos, -1, true); + auto view = interleaved_view(frame->width, frame->height, reinterpret_cast(frame->data.data()), frame->pitch); + if (frame->flipped) + y = frame->height - y; + int actual[4]; + if (!calculate_point(view, x, y, orig, tolerance, actual)) return false; + int dl = abs(actual[0] - lrud[0]); + int dr = abs(actual[1] - lrud[1]); + int du = abs(actual[2] - lrud[2]); + int dd = abs(actual[3] - lrud[3]); + + return dl <= 5 && dr <= 5 && du <= 5 && dd <= 5; + } + + void DialogAlignToVideo::update_from_textbox() + { + long lx, ly; + int w = preview_image.GetWidth(), h = preview_image.GetHeight(); + if (!selected_x->GetValue().ToLong(&lx) || !selected_y->GetValue().ToLong(&ly)) + return; + + if (lx < 0 || ly < 0 || lx >= w || ly >= h) + return; + int x = int(lx); + int y = int(ly); + auto r = preview_image.GetRed(x, y); + auto g = preview_image.GetGreen(x, y); + auto b = preview_image.GetBlue(x, y); + selected_color->SetColor(agi::Color(r, g, b)); + } + + void DialogAlignToVideo::update_from_textbox(wxCommandEvent & evt) + { + update_from_textbox(); + } + +} + + +void ShowAlignToVideoDialog(agi::Context * c) +{ + c->dialog->Show(c); +} diff --git a/src/dialogs.h b/src/dialogs.h index 2e03d09e9..d781c6614 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -74,3 +74,4 @@ void ShowSpellcheckerDialog(agi::Context *c); void ShowStyleManagerDialog(agi::Context *c); void ShowTimingProcessorDialog(agi::Context *c); void ShowVideoDetailsDialog(agi::Context *c); +void ShowAlignToVideoDialog(agi::Context* c); diff --git a/src/hotkey.cpp b/src/hotkey.cpp index 2ec8f50e4..deb624628 100644 --- a/src/hotkey.cpp +++ b/src/hotkey.cpp @@ -29,6 +29,11 @@ #include namespace { + const char* added_hotkeys_cj[][3] = { + {"time/align", "Video", "KP_TAB"}, + {nullptr} + }; + const char *added_hotkeys_7035[][3] = { {"audio/play/line", "Audio", "R"}, {nullptr} @@ -81,6 +86,11 @@ void init() { auto migrations = OPT_GET("App/Hotkey Migrations")->GetListString(); + if (boost::find(migrations, "cj") == end(migrations)) { + migrate_hotkeys(added_hotkeys_cj); + migrations.emplace_back("cj"); + } + if (boost::find(migrations, "7035") == end(migrations)) { migrate_hotkeys(added_hotkeys_7035); migrations.emplace_back("7035"); diff --git a/src/image_position_picker.cpp b/src/image_position_picker.cpp new file mode 100644 index 000000000..5912b4fc3 --- /dev/null +++ b/src/image_position_picker.cpp @@ -0,0 +1,132 @@ +#include "image_position_picker.h" +BEGIN_EVENT_TABLE(ImagePositionPicker, wxPanel) + // some useful events + /* + EVT_MOTION(ImagePositionPicker::mouseMoved) + EVT_LEFT_DOWN(ImagePositionPicker::mouseDown) + EVT_LEFT_UP(ImagePositionPicker::mouseReleased) + EVT_RIGHT_DOWN(ImagePositionPicker::rightClick) + EVT_LEAVE_WINDOW(ImagePositionPicker::mouseLeftWindow) + EVT_KEY_DOWN(ImagePositionPicker::keyPressed) + EVT_KEY_UP(ImagePositionPicker::keyReleased) + EVT_MOUSEWHEEL(ImagePositionPicker::mouseWheelMoved) + */ + + // catch paint events + EVT_PAINT(ImagePositionPicker::paintEvent) + //Size event + EVT_SIZE(ImagePositionPicker::OnSize) + EVT_MOUSE_EVENTS(ImagePositionPicker::OnMouseEvent) +END_EVENT_TABLE() + + +// some useful events +/* + void ImagePositionPicker::mouseMoved(wxMouseEvent& event) {} + void ImagePositionPicker::mouseDown(wxMouseEvent& event) {} + void ImagePositionPicker::mouseWheelMoved(wxMouseEvent& event) {} + void ImagePositionPicker::mouseReleased(wxMouseEvent& event) {} + void ImagePositionPicker::rightClick(wxMouseEvent& event) {} + void ImagePositionPicker::mouseLeftWindow(wxMouseEvent& event) {} + void ImagePositionPicker::keyPressed(wxKeyEvent& event) {} + void ImagePositionPicker::keyReleased(wxKeyEvent& event) {} + */ + +ImagePositionPicker::ImagePositionPicker(wxWindow* parent, wxImage i, updator upd) : wxPanel(parent) +{ + image = i; + prevW = -1; + prevH = -1; + w = image.GetWidth(); + h = image.GetHeight(); + update = upd; +} + +/* + * Called by the system of by wxWidgets when the panel needs + * to be redrawn. You can also trigger this call by + * calling Refresh()/Update(). + */ + +void ImagePositionPicker::paintEvent(wxPaintEvent& evt) +{ + // depending on your system you may need to look at double-buffered dcs + wxPaintDC dc(this); + render(dc); +} + +/* + * Alternatively, you can use a clientDC to paint on the panel + * at any time. Using this generally does not free you from + * catching paint events, since it is possible that e.g. the window + * manager throws away your drawing when the window comes to the + * background, and expects you will redraw it when the window comes + * back (by sending a paint event). + */ +void ImagePositionPicker::paintNow() +{ + // depending on your system you may need to look at double-buffered dcs + wxClientDC dc(this); + render(dc); +} + +/* + * Here we do the actual rendering. I put it in a separate + * method so that it can work no matter what type of DC + * (e.g. wxPaintDC or wxClientDC) is used. + */ +void ImagePositionPicker::render(wxDC& dc) +{ + int neww, newh; + dc.GetSize(&neww, &newh); + + if (neww != prevW || newh != prevH) + { + // keep the image proportionate + int ww, hh; + if (double(neww) / w >= double(newh) / h) // too long + { + ww = newh * w / h; + hh = newh; + } + else + { + ww = neww; + hh = neww * h / w; + } + resized = wxBitmap(image.Scale(ww, hh /*, wxIMAGE_QUALITY_HIGH*/)); + prevW = ww; + prevH = hh; + dc.DrawBitmap(resized, 0, 0, false); + } + else { + dc.DrawBitmap(resized, 0, 0, false); + } +} + +/* + * Here we call refresh to tell the panel to draw itself again. + * So when the user resizes the image panel the image should be resized too. + */ +void ImagePositionPicker::OnSize(wxSizeEvent& event) { + Refresh(); + //skip the event. + event.Skip(); +} + +void ImagePositionPicker::OnMouseEvent(wxMouseEvent& evt) +{ + wxPoint pos = evt.GetPosition(); + if (evt.Dragging() || evt.LeftDown() || evt.LeftUp()) + { + int x = pos.x * w / prevW; + int y = pos.y * h / prevH; + if (x >= 0 && x < w && y >= 0 && y < h) + update(x, y, image.GetRed(x, y), image.GetGreen(x, y), image.GetBlue(x, y)); + } + else if (evt.LeftDClick()) { + // Propagate the double click event to submit + evt.ResumePropagation(wxEVENT_PROPAGATE_MAX); + evt.Skip(); + } +} diff --git a/src/image_position_picker.h b/src/image_position_picker.h new file mode 100644 index 000000000..1f808db39 --- /dev/null +++ b/src/image_position_picker.h @@ -0,0 +1,37 @@ +#include +#include +#include "gl_wrap.h" + +typedef std::function updator; + +class ImagePositionPicker : public wxPanel +{ + wxImage image; + wxBitmap resized; + int prevW, prevH, w, h; + + updator update; + +public: + ImagePositionPicker(wxWindow* parent, wxImage i, updator upd); + + void paintEvent(wxPaintEvent & evt); + void paintNow(); + void OnSize(wxSizeEvent& event); + void OnMouseEvent(wxMouseEvent& evt); + void render(wxDC& dc); + + // some useful events + /* + void mouseMoved(wxMouseEvent& event); + void mouseDown(wxMouseEvent& event); + void mouseWheelMoved(wxMouseEvent& event); + void mouseReleased(wxMouseEvent& event); + void rightClick(wxMouseEvent& event); + void mouseLeftWindow(wxMouseEvent& event); + void keyPressed(wxKeyEvent& event); + void keyReleased(wxKeyEvent& event); + */ + + DECLARE_EVENT_TABLE() +}; diff --git a/src/libresrc/default_config.json b/src/libresrc/default_config.json index ce27a3fa6..7989b0fb1 100644 --- a/src/libresrc/default_config.json +++ b/src/libresrc/default_config.json @@ -581,6 +581,10 @@ }, "Visual" : { "Autohide": false + }, + "Align to Video" : { + "Tolerance" : 20, + "Maximized" : true } }, @@ -590,6 +594,8 @@ }, "Video" : { + "Disable Scroll Zoom" : false, + "Reverse Zoom" : false, "Default Zoom" : 7, "Detached" : { "Enabled" : false, @@ -619,6 +625,6 @@ }, "Subtitle Sync" : true, "Video Pan": false, - "Default to UI Zoom": false + "Default to Video Zoom": false } } diff --git a/src/libresrc/default_hotkey.json b/src/libresrc/default_hotkey.json index 34e0ef486..6f4a2c738 100644 --- a/src/libresrc/default_hotkey.json +++ b/src/libresrc/default_hotkey.json @@ -263,7 +263,7 @@ "subtitle/select/all" : [ "Ctrl-A" ], - "grid/toggle" : [ + "grid/fold/toggle" : [ "Enter" ], "video/frame/next" : [ @@ -289,6 +289,9 @@ ], "video/frame/prev/large" : [ "Alt-Left" + ], + "time/align" : [ + "KP_TAB" ] }, "Translation Assistant" : { diff --git a/src/libresrc/default_toolbar.json b/src/libresrc/default_toolbar.json index 07afb90b0..7b85ee600 100644 --- a/src/libresrc/default_toolbar.json +++ b/src/libresrc/default_toolbar.json @@ -42,6 +42,7 @@ "subtitle/select/visible", "time/snap/scene", "time/frame/current", + "time/align", "", "tool/style/manager", "subtitle/properties", diff --git a/src/libresrc/osx/default_config.json b/src/libresrc/osx/default_config.json index 2e24ccbec..ceee6db4e 100644 --- a/src/libresrc/osx/default_config.json +++ b/src/libresrc/osx/default_config.json @@ -580,6 +580,10 @@ }, "Visual" : { "Autohide": false + }, + "Align to Video" : { + "Tolerance" : 20, + "Maximized" : true } }, @@ -589,6 +593,8 @@ }, "Video" : { + "Disable Scroll Zoom" : false, + "Reverse Zoom" : false, "Default Zoom" : 7, "Detached" : { "Enabled" : false, @@ -618,6 +624,6 @@ }, "Subtitle Sync" : true, "Video Pan": false, - "Default to UI Zoom": false + "Default to Video Zoom": false } } diff --git a/src/libresrc/osx/default_hotkey.json b/src/libresrc/osx/default_hotkey.json index 136f5bc48..64d16bd85 100644 --- a/src/libresrc/osx/default_hotkey.json +++ b/src/libresrc/osx/default_hotkey.json @@ -273,7 +273,7 @@ "subtitle/select/all" : [ "Ctrl-A" ], - "grid/toggle" : [ + "grid/fold/toggle" : [ "Enter" ], "video/frame/next" : [ diff --git a/src/meson.build b/src/meson.build index 64f0bb50a..5287fb198 100644 --- a/src/meson.build +++ b/src/meson.build @@ -56,6 +56,7 @@ aegisub_src = files( 'context.cpp', 'crash_writer.cpp', 'dialog_about.cpp', + 'dialog_align.cpp', 'dialog_attachments.cpp', 'dialog_automation.cpp', 'dialog_autosave.cpp', @@ -98,6 +99,7 @@ aegisub_src = files( 'help_button.cpp', 'hotkey.cpp', 'hotkey_data_view_model.cpp', + 'image_position_picker.cpp', 'initial_line_state.cpp', 'main.cpp', 'menu.cpp', diff --git a/src/preferences.cpp b/src/preferences.cpp index c9119f9c2..2a212cf69 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -174,6 +174,9 @@ void Video(wxTreebook *book, Preferences *parent) { p->CellSkip(general); p->OptionAdd(general, _("Automatically open audio when opening video"), "Video/Open Audio"); p->CellSkip(general); + p->OptionAdd(general, _("Disable zooming with scroll bar"), "Video/Disable Scroll Zoom") + ->SetToolTip("Makes the scroll bar not zoom the video. Useful when using a track pad that often scrolls accidentally."); + p->OptionAdd(general, _("Reverse zoom direction"), "Video/Reverse Zoom"); const wxString czoom_arr[24] = { "12.5%", "25%", "37.5%", "50%", "62.5%", "75%", "87.5%", "100%", "112.5%", "125%", "137.5%", "150%", "162.5%", "175%", "187.5%", "200%", "212.5%", "225%", "237.5%", "250%", "262.5%", "275%", "287.5%", "300%" }; wxArrayString choice_zoom(24, czoom_arr); @@ -446,7 +449,8 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) { p->OptionChoice(expert, _("Subtitles provider"), sp_choice, "Subtitle/Provider"); p->OptionAdd(expert, _("Video Panning"), "Video/Video Pan"); - p->OptionAdd(expert, _("Default to UI Zoom"), "Video/Default to UI Zoom"); + p->OptionAdd(expert, _("Default to Video Zoom"), "Video/Default to Video Zoom") + ->SetToolTip("Reverses the behavior of Ctrl while scrolling the video display. If not set, scrolling will default to UI zoom and Ctrl+scrolling will zoom the video. If set, this will be reversed."); #ifdef WITH_AVISYNTH diff --git a/src/text_selection_controller.cpp b/src/text_selection_controller.cpp index deefa6573..7290370c0 100644 --- a/src/text_selection_controller.cpp +++ b/src/text_selection_controller.cpp @@ -73,3 +73,18 @@ void TextSelectionController::SetSelection(int start, int end) { changing = false; AnnounceSelectionChanged(); } + + +void TextSelectionController::CommitStagedChanges() { + if (has_staged_selection) { + if (staged_selection_start <= staged_selection_end) { + SetSelection(staged_selection_start, staged_selection_end); + } else { + // commit some crimes to get this to work in all cases + SetInsertionPoint(staged_selection_end == 0 ? staged_selection_start : 0); + SetSelection(staged_selection_start, staged_selection_start); + SetInsertionPoint(staged_selection_end); + } + has_staged_selection = false; + } +} \ No newline at end of file diff --git a/src/text_selection_controller.h b/src/text_selection_controller.h index be6949ef4..634ec4a9a 100644 --- a/src/text_selection_controller.h +++ b/src/text_selection_controller.h @@ -25,6 +25,10 @@ class TextSelectionController { int insertion_point = 0; bool changing = false; + int staged_selection_start = 0; + int staged_selection_end = 0; + bool has_staged_selection = false; + wxStyledTextCtrl *ctrl = nullptr; void UpdateUI(wxStyledTextEvent &evt); @@ -35,10 +39,23 @@ public: void SetSelection(int start, int end); void SetInsertionPoint(int point); + // This set of functions allows staging changes to the selection or insertion points, which can then be applied later. + // This is useful when one is still waiting on other changes to be applied, but already listening for changes to the + // selection in the eventually visible text. + // They also provide a wrapper for setting a selection whose insertion point is on the left side. + void StageSetSelection(int start, int end) { staged_selection_start = start; staged_selection_end = end; has_staged_selection = true; }; + void StageSetInsertionPoint(int point) { StageSetSelection(point, point); }; + void CommitStagedChanges(); + void DropStagedChanges() { has_staged_selection = false; }; + int GetSelectionStart() const { return selection_start; } int GetSelectionEnd() const { return selection_end; } int GetInsertionPoint() const { return insertion_point; } + int GetStagedSelectionStart() const { return has_staged_selection ? staged_selection_start : selection_start; } + int GetStagedSelectionEnd() const { return has_staged_selection ? staged_selection_end : selection_end; } + int GetStagedInsertionPoint() const { return has_staged_selection ? staged_selection_end : insertion_point; } + void SetControl(wxStyledTextCtrl *ctrl); ~TextSelectionController(); diff --git a/src/video_display.cpp b/src/video_display.cpp index aeeaec508..b58cbe64e 100644 --- a/src/video_display.cpp +++ b/src/video_display.cpp @@ -409,8 +409,11 @@ void VideoDisplay::OnMouseLeave(wxMouseEvent& event) { void VideoDisplay::OnMouseWheel(wxMouseEvent& event) { bool videoPan = OPT_GET("Video/Video Pan")->GetBool(); if (int wheel = event.GetWheelRotation()) { - if (ForwardMouseWheelEvent(this, event)) { - if (!videoPan || (event.ControlDown() != OPT_GET("Video/Default to UI Zoom")->GetBool())) { + if (ForwardMouseWheelEvent(this, event) && !OPT_GET("Video/Disable Scroll Zoom")->GetBool()) { + if (OPT_GET("Video/Reverse Zoom")->GetBool()) { + wheel = -wheel; + } + if (!videoPan || (event.ControlDown() == OPT_GET("Video/Default to Video Zoom")->GetBool())) { SetWindowZoom(windowZoomValue + .125 * (wheel / event.GetWheelDelta())); } else { SetVideoZoom(wheel / event.GetWheelDelta());