Compare commits

..

8 Commits
mia ... mia

Author SHA1 Message Date
Mia Herkt bf532779a2
Icons based on Breeze 2023-03-26 08:56:39 +02:00
Mia Herkt a3024815a0
Properly skip events
So the mouse doesn’t get trapped because the button release event is
swallowed up.
2023-03-26 08:56:36 +02:00
Mia Herkt 774ff2bbaf
audio display: limit mouse event rate
Same as with visual typesetting.
2023-03-26 08:56:35 +02:00
Mia Herkt d2b3e38565
visual typesetting: limit event frequency
This change makes Aegisub a lot more responsive on platforms like X11
where mouse events may come in at very high frequencies and/or in bursts.
2023-03-26 08:56:34 +02:00
Mia Herkt 9b3fa75ab2
Visual tools: Make X/Y rotation less sensitive 2023-03-26 08:56:33 +02:00
Mia Herkt 45fbd74f12
Hack shortcuts to set colors from video display 2023-03-26 08:56:32 +02:00
Mia Herkt 54d8bde136
Remove annoying modal dialogs 2023-03-26 08:56:31 +02:00
Mia Herkt cb1157a308
Fix video toolbox keyframe color 2023-03-26 08:56:27 +02:00
154 changed files with 172366 additions and 215332 deletions

View File

@ -10,7 +10,6 @@ on:
branches:
- master
- feature
workflow_dispatch:
jobs:
build:
@ -35,35 +34,12 @@ jobs:
-Dharfbuzz:gobject=disabled
-Dharfbuzz:tests=disabled
-Dharfbuzz:docs=disabled
-Dharfbuzz:icu=disabled
-Dfribidi:tests=false
-Dfribidi:docs=false
-Dlibass:fontconfig=disabled
-Davisynth=enabled
-Dbestsource=enabled
-Dvapoursynth=enabled
- name: Windows MSVC Release (wx master)
os: windows-latest
msvc: true
buildtype: release
args: >-
-Ddefault_library=static
--force-fallback-for=zlib,harfbuzz,freetype2,fribidi,libpng
-Dfreetype2:harfbuzz=disabled
-Dharfbuzz:freetype=disabled
-Dharfbuzz:cairo=disabled
-Dharfbuzz:glib=disabled
-Dharfbuzz:gobject=disabled
-Dharfbuzz:tests=disabled
-Dharfbuzz:docs=disabled
-Dharfbuzz:icu=disabled
-Dfribidi:tests=false
-Dfribidi:docs=false
-Dlibass:fontconfig=disabled
-Davisynth=enabled
-Dbestsource=enabled
-Dvapoursynth=enabled
-Dwx_version='3.3.0'
#- {
# name: Windows MinGW,
# os: windows-latest,
@ -81,24 +57,11 @@ jobs:
buildtype: release,
args: ''
}
- name: Ubuntu AppImage
os: ubuntu-22.04
buildtype: release
appimage: true
# distro ffms is currently broken
args: >-
--prefix=/usr
-Dbuild_appimage=true
-Ddefault_library=static
--force-fallback-for=ffms2
-Davisynth=enabled
-Dbestsource=enabled
-Dvapoursynth=enabled
- {
name: macOS Release,
os: macos-latest,
buildtype: release,
args: -Ddefault_library=static -Dbuild_osx_bundle=true -Dlocal_boost=true -Dvapoursynth=enabled --force-fallback-for=ffms2
args: -Ddefault_library=static -Dbuild_osx_bundle=true -Dlocal_boost=true
}
steps:
@ -112,7 +75,7 @@ jobs:
- name: Setup Meson
run: |
python -m pip install --upgrade pip setuptools
python -m pip install --upgrade pip
pip install meson
- name: Setup MSVC
@ -141,13 +104,13 @@ jobs:
brew install pulseaudio # NO OpenAL in github CI
- name: Install dependencies (Linux)
if: startsWith(matrix.config.os, 'ubuntu-')
if: matrix.config.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install ninja-build build-essential libx11-dev libwxgtk3.0-gtk3-dev libfreetype6-dev pkg-config libfontconfig1-dev libass-dev libasound2-dev libffms2-dev intltool libboost-all-dev libhunspell-dev libuchardet-dev libpulse-dev libopenal-dev libjansson-dev nasm
sudo apt-get install ninja-build build-essential libx11-dev libwxgtk3.0-gtk3-dev libfreetype6-dev pkg-config libfontconfig1-dev libass-dev libasound2-dev libffms2-dev intltool libboost-all-dev
- name: Configure
run: meson setup build ${{ matrix.config.args }} -Dbuildtype=${{ matrix.config.buildtype }}
run: meson build ${{ matrix.config.args }} -Dbuildtype=${{ matrix.config.buildtype }}
- name: Build
run: meson compile -C build
@ -196,27 +159,3 @@ jobs:
name: ${{ matrix.config.name }} - installer
path: build/Aegisub-*.dmg
if-no-files-found: error
# Linux artifacts (AppImage)
- name: Generate AppImage
if: matrix.config.appimage
run: |
mkdir -p appimage/appdir
meson install -C build --destdir=../appimage/appdir
cd appimage
sudo apt-get install libfuse2
curl -L "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20220822-1/linuxdeploy-x86_64.AppImage" -o linuxdeploy
curl -L "https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage" -o appimagetool
chmod +x linuxdeploy appimagetool
./linuxdeploy --appdir appdir --desktop-file=appdir/aegisub.desktop
./appimagetool appdir
- name: Upload artifacts - Linux AppImage
uses: actions/upload-artifact@v3
if: matrix.config.appimage
with:
name: ${{ matrix.config.name }}
path: appimage/*.AppImage
if-no-files-found: error

View File

@ -1,7 +1,5 @@
## arch1t3cht's Aegisub "fork"
Download release builds [here](https://github.com/arch1t3cht/Aegisub/releases), or the latest CI builds [here](https://github.com/arch1t3cht/Aegisub/actions).
The release page also has detailed list of all changes and new features. If you're interested in the technical details or want to compile yourself, read on.
Go [here](#branchfeature-list) for the new features.
### Don't we have enough Aegisub forks already??
We absolutely do, and I'm aware that adding another one [doesn't sound like](https://xkcd.com/927/) a [good idea on paper](https://cdn.discordapp.com/attachments/425357202963038208/1007103606421459004/unknown.png). However,
@ -52,6 +50,9 @@ This is probably because you're building with wxgtk2. Building with wxgtk3 fixes
The exact way of switching depends on your Linux distribution, but essentially you need to ensure that `wx-config` or the next best variant of it points to wxgtk3. If it points to wxgtk2 by default and deinstalling wxgtk2 isn't an option, you can also temporarily move it out of the path or use a `native-file` in your meson project. Then, fully reconfigure meson using `meson configure --clearcache` and `meson setup --reconfigure`.
#### I get errors like "Option not found" after merging one of these branches
The changes to `default_config.json` or similar files weren't detected by meson due to missing regen dependencies. You can either merge the `bugfixes` branch or rebuild from scratch.
#### 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 specific regression 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.
@ -62,13 +63,11 @@ If you're compiling yourself, try adding `--force-fallback-for=zlib` to the meso
### Compilation
If you're just looking to install Aegisub, you might want to check out the [releases page](https://github.com/arch1t3cht/Aegisub/releases) or the [CI builds](https://github.com/arch1t3cht/Aegisub/actions) first.
For compilation on Windows, see the TSTools documentation below. Also check the [GitHub workflow](https://github.com/arch1t3cht/Aegisub/blob/cibuilds/.github/workflows/ci.yml) for the project arguments.
On Arch Linux, there is an AUR package called [aegisub-arch1t3cht-git](https://aur.archlinux.org/packages/aegisub-arch1t3cht-git). It's not maintained by me but seems to work.
On other Linux distributions or for manual compilation you can use this package or the [TSTools PKGBUILD](https://aur.archlinux.org/packages/aegisub-ttools-meson-git) as a reference, in particular for installing the necessary dependencies if you don't want to compile them yourself.
On other distributions or for manual compilation you can use this package or the [TSTools PKGBUILD](https://aur.archlinux.org/packages/aegisub-ttools-meson-git) as a reference, in particular for installing the necessary dependencies if you don't want to compile them yourself.
If all dependencies are installed:
- Install Meson
- Clone the repository
@ -133,7 +132,7 @@ All other dependencies are either stored in the repository or are included as su
Building:
1. Clone Aegisub's repository: `git clone https://github.com/arch1t3cht/Aegisub.git`
1. Clone Aegisub's repository: `git clone https://github.com/TypesettingTools/Aegisub.git`
2. From the Visual Studio "x64 Native Tools Command Prompt", generate the build directory: `meson build -Ddefault_library=static` (if building for release, add `--buildtype=release`)
3. Build with `cd build` and `ninja`

View File

@ -23,10 +23,10 @@ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
cleantags_version = "1.302"
cleantags_modified = "12 October 2023"
cleantags_version = "1.301"
cleantags_modified = "13 November 2009"
ktag = "\\[kK][fo]?[%d.]+"
ktag = "\\[kK][fo]?%d+"
--[[ The main function that performs the cleaning up
Takes: text

View File

@ -100,7 +100,7 @@ function io.open(fname, mode)
local file = assert(orig_open("nul", "rb"))
if ffi.C._wfreopen(wfname, wmode, file) == nil then
local msg, errno = select(2, file:close())
return nil, fname .. ": " .. tostring(msg), errno
return nil, fname .. ": " .. msg, errno
end
return file

View File

@ -1,5 +1,4 @@
subdir('include')
subdir('vapoursynth')
automation_dir = dataroot / 'automation'

View File

@ -22,18 +22,14 @@ Aegisub using the following variables:
- __aegi_hasaudio: int: If nonzero, Aegisub will try to load an audio track
from the same file.
The script can control the progress dialog shown by Aegisub with certain log
messages. Check the functions defined below for more information.
This module provides some utility functions to obtain timecodes, keyframes, and
other data.
"""
import os
import os.path
import re
from enum import Enum
from collections import deque
from typing import Any, Dict, List, Tuple, Callable
from typing import Any, Dict, List, Tuple
import vapoursynth as vs
core = vs.core
@ -43,28 +39,6 @@ aegi_vsplugins: str = ""
plugin_extension = ".dll" if os.name == "nt" else ".so"
def progress_set_message(message: str):
"""
Sets the message of Aegisub's progress dialog.
"""
vs.core.log_message(vs.MESSAGE_TYPE_DEBUG, f"__aegi_set_message,{message}")
def progress_set_progress(percent: float):
"""
Sets the progress shown in Aegisub's progress dialog to
the given percentage.
"""
vs.core.log_message(vs.MESSAGE_TYPE_DEBUG, f"__aegi_set_progress,{percent}")
def progress_set_indeterminate():
"""
Sets Aegisub's progress dialog to show indeterminate progress.
"""
vs.core.log_message(vs.MESSAGE_TYPE_DEBUG, f"__aegi_set_indeterminate,")
def set_paths(vars: dict):
"""
Initialize the wrapper library with the given configuration directories.
@ -123,19 +97,16 @@ def make_keyframes_filename(filename: str) -> str:
lwindex_re1 = re.compile(r"Index=(?P<Index>-?[0-9]+),POS=(?P<POS>-?[0-9]+),PTS=(?P<PTS>-?[0-9]+),DTS=(?P<DTS>-?[0-9]+),EDI=(?P<EDI>-?[0-9]+)")
lwindex_re2 = re.compile(r"Key=(?P<Key>-?[0-9]+),Pic=(?P<Pic>-?[0-9]+),POC=(?P<POC>-?[0-9]+),Repeat=(?P<Repeat>-?[0-9]+),Field=(?P<Field>-?[0-9]+)")
streaminfo_re = re.compile(r"Codec=(?P<Codec>[0-9]+),TimeBase=(?P<TimeBase>[0-9\/]+),Width=(?P<Width>[0-9]+),Height=(?P<Height>[0-9]+),Format=(?P<Format>[0-9a-zA-Z]+),ColorSpace=(?P<ColorSpace>[0-9]+)")
videoindex_re = re.compile(r"<ActiveVideoStreamIndex>(?P<VideoStreamIndex>[0-9+]+)</ActiveVideoStreamIndex>")
class LWIndexFrame:
pts: int
key: int
index: int
def __init__(self, raw: list[str]):
match1 = lwindex_re1.match(raw[0])
match2 = lwindex_re2.match(raw[1])
if not match1 or not match2:
raise ValueError("Invalid lwindex format")
self.index = int(match1.group("Index"))
self.pts = int(match1.group("PTS"))
self.key = int(match2.group("Key"))
@ -153,19 +124,11 @@ def info_from_lwindex(indexfile: str) -> Dict[str, List[int]]:
with open(indexfile, encoding="latin1") as f:
index = f.read().splitlines()
videoindex_str = next(l for l in index if l.startswith("<ActiveVideoStreamIndex>"))
videoindex_match = videoindex_re.match(videoindex_str)
if not videoindex_match:
raise ValueError("Invalid lwindex format: Invalid ActiveVideoStreamIndex line")
videoindex = int(videoindex_match.group("VideoStreamIndex"))
# The picture list starts after the last </StreamInfo> tag
indexstart, indexend = (len(index) - index[::-1].index("</StreamInfo>")), index.index("</LibavReaderIndex>")
indexstart, indexend = index.index("</StreamInfo>") + 1, index.index("</LibavReaderIndex>")
frames = [LWIndexFrame(index[i:i+2]) for i in range(indexstart, indexend, 2)]
frames = [f for f in frames if f.index == videoindex] # select the first stream
frames.sort(key=int)
streaminfo = streaminfo_re.match(index[index.index(f"<StreamInfo={videoindex},0>") + 1]) # info of first stream
streaminfo = streaminfo_re.match(index[indexstart - 2])
if not streaminfo:
raise ValueError("Invalid lwindex format")
@ -193,9 +156,6 @@ def wrap_lwlibavsource(filename: str, cachedir: str | None = None, **kwargs: Any
pass
cachefile = os.path.join(cachedir, make_lwi_cache_filename(filename))
progress_set_message("Loading video file")
progress_set_indeterminate()
ensure_plugin("lsmas", "libvslsmashsource", "To use Aegisub's LWLibavSource wrapper, the `lsmas` plugin for VapourSynth must be installed")
if b"-Dcachedir" not in core.lsmas.Version()["config"]: # type: ignore
@ -203,7 +163,6 @@ def wrap_lwlibavsource(filename: str, cachedir: str | None = None, **kwargs: Any
clip = core.lsmas.LWLibavSource(source=filename, cachefile=cachefile, **kwargs)
progress_set_message("Getting timecodes and keyframes from the index file")
return clip, info_from_lwindex(cachefile)
@ -212,6 +171,7 @@ def make_keyframes(clip: vs.VideoNode, use_scxvid: bool = False,
**kwargs: Any) -> List[int]:
"""
Generates a list of keyframes from a clip, using either WWXD or Scxvid.
Will be slightly more efficient with the `akarin` plugin installed.
:param clip: Clip to process.
:param use_scxvid: Whether to use Scxvid. If False, the function uses WWXD.
@ -221,9 +181,6 @@ def make_keyframes(clip: vs.VideoNode, use_scxvid: bool = False,
The remaining keyword arguments are passed on to the respective filter.
"""
progress_set_message("Generating keyframes")
progress_set_progress(1)
clip = core.resize.Bilinear(clip, width=resize_h * clip.width // clip.height, height=resize_h, format=resize_format)
if use_scxvid:
@ -239,12 +196,12 @@ def make_keyframes(clip: vs.VideoNode, use_scxvid: bool = False,
nonlocal done
keyframes[n] = f.props._SceneChangePrev if use_scxvid else f.props.Scenechange # type: ignore
done += 1
if done % (clip.num_frames // 200) == 0:
progress_set_progress(100 * done / clip.num_frames)
if done % (clip.num_frames // 25) == 0:
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "Detecting keyframes... {}% done.\n".format(100 * done // clip.num_frames))
return f
deque(clip.std.ModifyFrame(clip, _cb).frames(close=True), 0)
progress_set_progress(100)
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "Done detecting keyframes.\n")
return [n for n in range(clip.num_frames) if keyframes[n]]
@ -259,49 +216,26 @@ def save_keyframes(filename: str, keyframes: List[int]):
f.write("".join(f"{n}\n" for n in keyframes))
class GenKeyframesMode(Enum):
NEVER = 0
ALWAYS = 1
ASK = 2
def ask_gen_keyframes(_: str) -> bool:
from tkinter.messagebox import askyesno
progress_set_message("Asking whether to generate keyframes")
progress_set_indeterminate()
result = askyesno("Generate Keyframes", \
"No keyframes file was found for this video file.\nShould Aegisub detect keyframes from the video?\nThis will take a while.", default="no")
progress_set_message("")
return result
def get_keyframes(filename: str, clip: vs.VideoNode, fallback: str | List[int],
generate: GenKeyframesMode = GenKeyframesMode.ASK,
ask_callback: Callable = ask_gen_keyframes, **kwargs: Any) -> str | List[int]:
def try_get_keyframes(filename: str, default: str | List[int]) -> str | List[int]:
"""
Looks for a keyframes file for the given filename.
If no file was found, this function can generate a keyframe file for the given clip next
Checks if a keyframes file for the given filename is present and, if so,
returns it. Otherwise, returns the given list of keyframes.
"""
kffilename = make_keyframes_filename(filename)
return kffilename if os.path.exists(kffilename) else default
def get_keyframes(filename: str, clip: vs.VideoNode, **kwargs: Any) -> str:
"""
When not already present, creates a keyframe file for the given clip next
to the given filename using WWXD or Scxvid (see the make_keyframes docstring).
Whether or not keyframes are generated depends on the `generate` argument.
Depending on the `generate` argument, the function will
- always generate keyframes when no file was found
- never generate keyframes when no file was found
(and return the fallback keyframes instead)
- show a dialog to ask the user whether keyframes should be
generated or not
Additional keyword arguments are passed on to make_keyframes.
"""
progress_set_message("Looking for keyframes")
progress_set_indeterminate()
kffilename = make_keyframes_filename(filename)
if not os.path.exists(kffilename):
if generate == GenKeyframesMode.NEVER:
return fallback
if generate == GenKeyframesMode.ASK and not ask_callback(filename):
return fallback
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "No keyframes file found, detecting keyframes...\n")
keyframes = make_keyframes(clip, **kwargs)
save_keyframes(kffilename, keyframes)
@ -315,8 +249,6 @@ def check_audio(filename: str, **kwargs: Any) -> bool:
won't crash if it's not installed.
Additional keyword arguments are passed on to BestAudioSource.
"""
progress_set_message("Checking if the file has an audio track")
progress_set_indeterminate()
try:
ensure_plugin("bas", "BestAudioSource", "")
vs.core.bas.Source(source=filename, **kwargs)

View File

@ -1,8 +0,0 @@
# Copy files to build directory for testing purposes
vs_files = files(
'aegisub_vs.py',
)
foreach f: vs_files
configure_file(input: f, output: '@PLAINNAME@', copy: true)
endforeach

View File

@ -23,6 +23,7 @@
#include <boost/range/algorithm.hpp>
#include <libaegisub/charset_conv.h>
#include <iconv.h>
#include "charset_6937.h"
@ -419,7 +420,7 @@ size_t IconvWrapper::DstStrLen(const char* str) {
bool IsConversionSupported(const char *src, const char *dst) {
iconv_t cd = iconv_open(dst, src);
bool supported = cd != iconv_invalid;
if (supported) iconv_close(cd);
iconv_close(cd);
return supported;
}

View File

@ -23,7 +23,6 @@
#include <vector>
#include <libaegisub/exception.h>
#include <iconv.h>
namespace agi {
namespace charset {
@ -35,6 +34,8 @@ DEFINE_EXCEPTION(BufferTooSmall, ConversionFailure);
DEFINE_EXCEPTION(BadInput, ConversionFailure);
DEFINE_EXCEPTION(BadOutput, ConversionFailure);
typedef void *iconv_t;
/// RAII handle for iconv
class Iconv {
iconv_t cd;

View File

@ -24,8 +24,6 @@
#include <memory>
#include <string>
#pragma once
#undef CreateDirectory
namespace agi {

View File

@ -129,6 +129,7 @@ struct LuaStackcheck {
void dump();
LuaStackcheck(lua_State *L) : L(L), startstack(lua_gettop(L)) { }
~LuaStackcheck() { check_stack(0); }
};
#else
struct LuaStackcheck {

View File

@ -84,12 +84,6 @@ public:
Color const& GetColor() const;
bool const& GetBool() const;
std::string const& GetDefaultString() const;
int64_t const& GetDefaultInt() const;
double const& GetDefaultDouble() const;
Color const& GetDefaultColor() const;
bool const& GetDefaultBool() const;
void SetString(const std::string);
void SetInt(const int64_t);
void SetDouble(const double);
@ -102,12 +96,6 @@ public:
std::vector<Color> const& GetListColor() const;
std::vector<bool> const& GetListBool() const;
std::vector<std::string> const& GetDefaultListString() const;
std::vector<int64_t> const& GetDefaultListInt() const;
std::vector<double> const& GetDefaultListDouble() const;
std::vector<Color> const& GetDefaultListColor() const;
std::vector<bool> const& GetDefaultListBool() const;
void SetListString(std::vector<std::string>);
void SetListInt(std::vector<int64_t>);
void SetListDouble(std::vector<double>);
@ -129,7 +117,6 @@ public:
: OptionValue(std::move(member_name)) \
, value(member_value), value_default(member_value) { } \
type const& GetValue() const { return value; } \
type const& GetDefault() const { return value_default; } \
void SetValue(type new_val) { value = std::move(new_val); NotifyChanged(); } \
OptionType GetType() const { return OptionType::type_name; } \
void Reset() { value = value_default; NotifyChanged(); } \
@ -154,7 +141,6 @@ CONFIG_OPTIONVALUE(Bool, bool)
: OptionValue(std::move(name)) \
, array(value), array_default(value) { } \
std::vector<type> const& GetValue() const { return array; } \
std::vector<type> const& GetDefault() const { return array_default; } \
void SetValue(std::vector<type> val) { array = std::move(val); NotifyChanged(); } \
OptionType GetType() const { return OptionType::List##type_name; } \
void Reset() { array = array_default; NotifyChanged(); } \
@ -170,7 +156,6 @@ CONFIG_OPTIONVALUE_LIST(Bool, bool)
#define CONFIG_OPTIONVALUE_ACCESSORS(ReturnType, Type) \
inline ReturnType const& OptionValue::Get##Type() const { return As<OptionValue##Type>(OptionType::Type)->GetValue(); } \
inline ReturnType const& OptionValue::GetDefault##Type() const { return As<OptionValue##Type>(OptionType::Type)->GetDefault(); } \
inline void OptionValue::Set##Type(ReturnType v) { As<OptionValue##Type>(OptionType::Type)->SetValue(std::move(v)); }
CONFIG_OPTIONVALUE_ACCESSORS(std::string, String)

View File

@ -75,13 +75,14 @@ void Path::FillPlatformSpecificPaths() {
SetToken("?local", home/".aegisub");
#ifdef APPIMAGE_BUILD
agi::fs::path exe = exe_dir();
agi::fs::path data_from_bin = agi::fs::path(P_DATA).lexically_relative(P_BIN);
SetToken("?data", (exe != "" ? exe/data_from_bin : home/".aegisub").make_preferred());
agi::fs::path data = exe_dir();
if (data == "") data = home/".aegisub";
SetToken("?data", data);
SetToken("?dictionary", Decode("?data/dictionaries"));
#else
SetToken("?data", P_DATA);
#endif
SetToken("?dictionary", "/usr/share/hunspell");
#endif
#else
agi::fs::path app_support = agi::util::GetApplicationSupportDirectory();

View File

@ -1,6 +1,6 @@
project('Aegisub', ['c', 'cpp'],
license: 'BSD-3-Clause',
meson_version: '>=1.0.0',
meson_version: '>=0.57.0',
default_options: ['cpp_std=c++14', 'buildtype=debugoptimized'],
version: '3.2.2')
@ -8,7 +8,6 @@ cmake = import('cmake')
if host_machine.system() == 'windows'
add_project_arguments('-DNOMINMAX', language: 'cpp')
add_project_arguments('-DUNICODE', language: 'cpp')
if not get_option('csri').disabled()
add_global_arguments('-DCSRI_NO_EXPORT', language: 'c')
@ -58,15 +57,11 @@ if get_option('buildtype') == 'release'
endif
conf = configuration_data()
conf.set_quoted('P_BIN', bindir)
conf.set_quoted('P_DATA', dataroot)
conf.set_quoted('P_LOCALE', localedir)
if get_option('credit') != ''
conf.set_quoted('BUILD_CREDIT', get_option('credit'))
endif
if get_option('build_appimage')
conf.set('APPIMAGE_BUILD', 1)
endif
conf.set('WITH_UPDATE_CHECKER', get_option('enable_update_checker'))
deps = []
@ -103,7 +98,7 @@ deps += dependency('libass', version: '>=0.9.7',
boost_modules = ['chrono', 'filesystem', 'thread', 'locale', 'regex']
if not get_option('local_boost')
boost_dep = dependency('boost', version: '>=1.60.0',
boost_dep = dependency('boost', version: '>=1.50.0',
modules: boost_modules + ['system'],
required: false,
static: get_option('default_library') == 'static')
@ -147,21 +142,16 @@ else
opt_var = cmake.subproject_options()
opt_var.add_cmake_defines({
'wxBUILD_INSTALL': false,
'wxBUILD_PRECOMP': 'OFF', # otherwise breaks project generation w/ meson
'wxBUILD_PRECOMP': false, # otherwise breaks project generation w/ meson
'wxBUILD_SHARED': build_shared,
'wxUSE_WEBVIEW': false, # breaks build on linux
'wxUSE_REGEX': 'builtin',
'CMAKE_BUILD_TYPE': build_type,
'wxUSE_IMAGE': true,
'wxBUILD_MONOLITHIC': true # otherwise breaks project generation w/ meson
})
if get_option('wx_version').version_compare('>=3.3.0')
wx = cmake.subproject('wxWidgets-master', options: opt_var)
else
wx = cmake.subproject('wxWidgets', options: opt_var)
endif
wx = cmake.subproject('wxWidgets', options: opt_var)
deps += [
wx.dependency('wxmono'),
@ -169,12 +159,6 @@ else
wx.dependency('wxscintilla')
]
if get_option('wx_version').version_compare('>=3.3.0')
deps += [
wx.dependency('wxlexilla')
]
endif
if host_machine.system() == 'windows' or host_machine.system() == 'darwin'
deps += [
wx.dependency('wxpng'),
@ -328,7 +312,7 @@ if host_machine.system() == 'windows'
endif
if host_machine.system() == 'darwin'
frameworks_dep = dependency('appleframeworks', modules : ['CoreText', 'CoreFoundation', 'AppKit', 'Carbon', 'IOKit', 'QuartzCore'])
frameworks_dep = dependency('appleframeworks', modules : ['CoreText', 'CoreFoundation', 'AppKit', 'Carbon', 'IOKit'])
deps += frameworks_dep
endif
@ -418,10 +402,7 @@ subdir('libaegisub')
subdir('packages')
subdir('po')
subdir('src')
if not meson.is_cross_build()
subdir('tests')
endif
subdir('tests')
aegisub_cpp_pch = ['src/include/agi_pre.h']
aegisub_c_pch = ['src/include/agi_pre_c.h']
@ -434,7 +415,7 @@ if host_machine.system() == 'windows'
link_depends += manifest_file
endif
aegisub = executable('aegisub', aegisub_src, version_h, acconf, resrc,
aegisub = executable('aegisub', aegisub_src, version_h, acconf,
link_with: [libresrc, libluabins, libaegisub],
link_args: link_args,
link_depends: link_depends,

View File

@ -28,4 +28,3 @@ option('update_server', type: 'string', value: 'updates.aegisub.org', descriptio
option('update_url', type: 'string', value: '/trunk', description: 'Base path to use for the update checker')
option('build_osx_bundle', type: 'boolean', value: 'false', description: 'Package Aegisub.app on OSX')
option('build_appimage', type: 'boolean', value: 'false', description: 'Prepare for AppImage packaging')

View File

@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>aegisub.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>BSD-3-Clause AND MIT AND MPL-1.1</project_license>
<name>Aegisub</name>
<summary>A free, cross-platform open source tool for creating and modifying subtitles</summary>
<description>
<p>Aegisub is a free, cross-platform open source tool for creating and modifying subtitles. Aegisub makes it quick and easy to time subtitles to audio, and features many powerful tools for styling them, including a built-in real-time video preview.</p>
<p>Aegisub was originally created as a tool to make typesetting, particularly in anime fansubs, a less painful experience. At the time of the start of the project, many other programs that supported the Advanced Substation Alpha format lacked (and in many cases, still lack; development on several competing programs have since been dropped for various reasons completely unrelated to Aegisub) many vital functions, or were too buggy and/or unreliable to be really useful.</p>
<p>Since then, Aegisub has grown into a fully fledged, highly customizable subtitle editor. It features a lot of convenient tools to help you with timing, typesetting, editing and translating subtitles, as well as a powerful scripting environment called Automation (originally mostly intended for creating karaoke effects, Automation can now be used much else, including creating macros and various other convenient tools).</p>
<p>Some highlights of Aegisub:</p>
<ul>
<li>Simple and intuitive yet powerful interface for editing subtitles</li>
<li>Support for many formats and character sets</li>
<li>Powerful video mode</li>
<li>Visual typesetting tools</li>
<li>Intuitive and customizable audio timing mode</li>
<li>Fully scriptable through the Automation module</li>
</ul>
</description>
<!-- XXX: appstreamcli validation warning: cid-desktopapp-is-not-rdns
If improving this, the <id> and filename should probably also be changed. -->
<launchable type="desktop-id">aegisub.desktop</launchable>
<kudos>
<kudo>HiDpiIcon</kudo>
<kudo>HighContrast</kudo>
<kudo>UserDocs</kudo>
</kudos>
<screenshots>
<screenshot type="default">
<caption>Typesetting</caption>
<image>https://aegisub.org/img/screenshots/unix/typesetting.png</image>
</screenshot>
<screenshot>
<caption>Audio video</caption>
<image>https://aegisub.org/img/screenshots/unix/audio-video.png</image>
</screenshot>
<screenshot>
<caption>Audio timing</caption>
<image>https://aegisub.org/img/screenshots/unix/audio-timing.png</image>
</screenshot>
</screenshots>
<developer_name>Aegisub Group</developer_name>
<url type="bugtracker">https://github.com/Aegisub/Aegisub/issues</url>
<url type="faq">https://aegisub.org/docs/latest/faq</url>
<url type="help">https://aegisub.org/docs/latest</url>
<url type="homepage">https://aegisub.org</url>
<url type="translate">https://github.com/Aegisub/Aegisub</url>
<content_rating type="oars-1.0">
<content_attribute id="social-info">mild</content_attribute>
</content_rating>
<translation type="gettext">aegisub</translation>
<provides>
<binary>aegisub</binary>
</provides>
<releases>
<!-- TODO: automatic replace at config time -->
<release version="3.2.2" date="2014-12-08"/>
</releases>
</component>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>aegisub.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>BSD-3-Clause AND MIT AND MPL-1.1</project_license>
<_name>Aegisub</_name>
<_summary>A free, cross-platform open source tool for creating and modifying subtitles</_summary>
<description>
<_p>Aegisub is a free, cross-platform open source tool for creating and modifying subtitles. Aegisub makes it quick and easy to time subtitles to audio, and features many powerful tools for styling them, including a built-in real-time video preview.</_p>
<_p>Aegisub was originally created as a tool to make typesetting, particularly in anime fansubs, a less painful experience. At the time of the start of the project, many other programs that supported the Advanced Substation Alpha format lacked (and in many cases, still lack; development on several competing programs have since been dropped for various reasons completely unrelated to Aegisub) many vital functions, or were too buggy and/or unreliable to be really useful.</_p>
<_p>Since then, Aegisub has grown into a fully fledged, highly customizable subtitle editor. It features a lot of convenient tools to help you with timing, typesetting, editing and translating subtitles, as well as a powerful scripting environment called Automation (originally mostly intended for creating karaoke effects, Automation can now be used much else, including creating macros and various other convenient tools).</_p>
<_p>Some highlights of Aegisub:</_p>
<ul>
<_li>Simple and intuitive yet powerful interface for editing subtitles</_li>
<_li>Support for many formats and character sets</_li>
<_li>Powerful video mode</_li>
<_li>Visual typesetting tools</_li>
<_li>Intuitive and customizable audio timing mode</_li>
<_li>Fully scriptable through the Automation module</_li>
</ul>
</description>
<launchable type="desktop-id">aegisub.desktop</launchable>
<kudos>
<kudo>HiDpiIcon</kudo>
<kudo>HighContrast</kudo>
<kudo>UserDocs</kudo>
</kudos>
<screenshots>
<screenshot type="default">
<_caption>Typesetting</_caption>
<image>http://static.aegisub.org/img/screenshots/unix/typesetting-efc51b7a.png</image>
</screenshot>
<screenshot>
<_caption>Audio video</_caption>
<image>http://static.aegisub.org/img/screenshots/unix/audio-video-f1f81fc2.png</image>
</screenshot>
<screenshot>
<_caption>Audio timing</_caption>
<image>http://static.aegisub.org/img/screenshots/unix/audio-timing-1d8fce7e.png</image>
</screenshot>
</screenshots>
<developer_name>Aegisub Group</developer_name>
<url type="bugtracker">https://github.com/Aegisub/Aegisub/issues</url>
<url type="faq">http://docs.aegisub.org/manual/FAQ</url>
<url type="help">http://docs.aegisub.org</url>
<url type="homepage">http://www.aegisub.org</url>
<url type="translate">https://sites.google.com/site/rockytdrontransifex/aegisub</url>
<content_rating type="oars-1.0">
<content_attribute id="social-info">mild</content_attribute>
</content_rating>
<translation type="gettext">aegisub</translation>
<provides>
<binary>aegisub</binary>
</provides>
<releases>
<release version="3.2.2" date="2014-12-08"/>
</releases>
</component>

View File

@ -9,7 +9,7 @@ TryExec=@AEGISUB_COMMAND@
Icon=aegisub
Terminal=false
Categories=AudioVideo;AudioVideoEditing;GTK;
Keywords=subtitles;subtitle;captions;captioning;video;audio;
_Keywords=subtitles;subtitle;captions;captioning;video;audio;
MimeType=application/x-srt;text/plain;text/x-ass;text/x-microdvd;text/x-ssa;
StartupNotify=true
StartupWMClass=aegisub

View File

@ -18,8 +18,8 @@ elif host_machine.system() == 'darwin'
else
conf_pkg.set('AEGISUB_COMMAND', 'aegisub')
desktop_template = configure_file(input: 'desktop/aegisub.desktop.in.in',
output: 'aegisub.desktop.in',
desktop_template = configure_file(input: 'desktop/aegisub.desktop.template.in',
output: 'aegisub.desktop.template',
configuration: conf_pkg)
i18n = import('i18n')
@ -30,16 +30,6 @@ else
install: true,
install_dir: datadir / 'applications')
appdata_template = configure_file(input: 'desktop/aegisub.appdata.xml.in.in',
output: 'aegisub.desktop.appdata.xml.in',
configuration: conf_pkg)
i18n.merge_file(input: appdata_template,
output: 'aegisub.appdata.xml',
type: 'xml',
po_dir: '../po',
install: true,
install_dir: datadir / 'metainfo')
aegisub_logos = ['16x16.png', '22x22.png', '24x24.png', '32x32.png', '48x48.png', '64x64.png', 'scalable.svg']
foreach s: aegisub_logos
@ -48,10 +38,4 @@ else
install_data('desktop' / dir / 'aegisub.' + ext,
install_dir: datadir / 'icons' / 'hicolor' / dir / 'apps')
endforeach
if get_option('build_appimage')
install_symlink('AppRun', install_dir: '/', pointing_to: bindir.strip('/') / 'aegisub')
install_symlink('.DirIcon', install_dir: '/', pointing_to: datadir.strip('/') / 'icons' / 'hicolor' / 'scalable' / 'apps' / 'aegisub.svg')
install_symlink('aegisub.desktop', install_dir: '/', pointing_to: datadir.strip('/') / 'applications' / 'aegisub.desktop')
endif
endif

View File

@ -1,8 +1,8 @@
[Files]
; Avisynth
DestDir: {app}; Source: {#DEPS_DIR}\AvisynthPlus64\x64\Output\system\DevIL.dll; Flags: ignoreversion; Components: main
DestDir: {app}; Source: {#DEPS_DIR}\AvisynthPlus64\x64\Output\AviSynth.dll; Flags: ignoreversion; Components: main
DestDir: {app}; Source: {#DEPS_DIR}\AvisynthPlus64\x64\Output\plugins\DirectShowSource.dll; Flags: ignoreversion; Components: main
DestDir: {app}; Source: {#DEPS_DIR}\AvisynthPlus64\x64\system\DevIL.dll; Flags: ignoreversion; Components: main
DestDir: {app}; Source: {#DEPS_DIR}\AvisynthPlus64\x64\AviSynth.dll; Flags: ignoreversion; Components: main
DestDir: {app}; Source: {#DEPS_DIR}\AvisynthPlus64\x64\plugins\DirectShowSource.dll; Flags: ignoreversion; Components: main
; VSFilter
DestDir: {app}\csri; Source: {#DEPS_DIR}\VSFilter\x64\VSFilter.dll; Flags: ignoreversion; Components: main
; VapourSynth

View File

@ -2,7 +2,6 @@
[Files]
DestDir: {tmp}; Source: "{#DEPS_DIR}\VC_redist\VC_redist.x{#ARCH}.exe"; Flags: nocompression deleteafterinstall
DestDir: {app}; Source: "{#DEPS_DIR}\XAudio2_redist\build\native\release\bin\x{#ARCH}\xaudio2_9redist.dll"; DestName: "XAudio2_9.dll"; OnlyBelowVersion: 10.0
[Run]
Filename: {tmp}\VC_redist.x{#ARCH}.exe; StatusMsg: {cm:InstallRuntime}; Parameters: "/install /quiet /norestart"

View File

@ -1,5 +1,5 @@
; This file declares all installables related to spell checking and thesaurii in Aegisub
[Files]
Source: {#DEPS_DIR}\dictionaries\en_US.aff; DestDir: {app}\dictionaries; Flags: skipifsourcedoesntexist ignoreversion; Components: dictionaries/en_US
Source: {#DEPS_DIR}\dictionaries\en_US.dic; DestDir: {app}\dictionaries; Flags: skipifsourcedoesntexist ignoreversion; Components: dictionaries/en_US
Source: {#DEPS_DIR}\dictionaries\en_US.aff; DestDir: {app}\dictionaries; Flags: ignoreversion; Components: dictionaries/en_US
Source: {#DEPS_DIR}\dictionaries\en_US.dic; DestDir: {app}\dictionaries; Flags: ignoreversion; Components: dictionaries/en_US

View File

@ -56,9 +56,9 @@ Copy-New-Item $InstallerDepsDir\dictionaries\en_US.aff $PortableOutputDir\dicti
Copy-New-Item $InstallerDepsDir\dictionaries\en_US.dic $PortableOutputDir\dictionaries
Write-Output 'Copying - codecs'
Write-Output 'Copying - codecs\Avisynth'
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\Output\system\DevIL.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\Output\AviSynth.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\Output\plugins\DirectShowSource.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\system\DevIL.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\AviSynth.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\plugins\DirectShowSource.dll $PortableOutputDir
Write-Output 'Copying - codecs\VapourSynth'
Copy-New-Item $InstallerDepsDir\L-SMASH-Works\libvslsmashsource.dll $PortableOutputDir\vapoursynth
Copy-New-Item $InstallerDepsDir\bestaudiosource\win64\BestAudioSource.dll $PortableOutputDir\vapoursynth
@ -68,9 +68,6 @@ Write-Output 'Copying - codecs\VSFilter'
Copy-New-Item $InstallerDepsDir\VSFilter\x64\VSFilter.dll $PortableOutputDir\csri
Write-Output 'Copying - runtimes\MS-CRT'
Copy-New-Item $InstallerDepsDir\VC_redist\VC_redist.x64.exe $PortableOutputDir\Microsoft.CRT
Write-Output 'Copying - redist\XAudio2_9'
Copy-New-Item $InstallerDepsDir\XAudio2_redist\build\native\release\bin\x64\xaudio2_9redist.dll $PortableOutputDir\Redist
Rename-Item $PortableOutputDir\Redist\xaudio2_9redist.dll $PortableOutputDir\Redist\XAudio2_9.dll
Write-Output 'Copying - automation'
Copy-New-Items "$InstallerDir\share\aegisub\automation\*" "$PortableOutputDir\automation\" -Recurse

View File

@ -1,6 +1,3 @@
packages/desktop/aegisub.desktop.in.in
packages/desktop/aegisub.appdata.xml.in.in
src/ass_style.cpp
src/audio_box.cpp
src/audio_karaoke.cpp

File diff suppressed because it is too large Load Diff

11587
po/ar.po

File diff suppressed because it is too large Load Diff

10648
po/bg.po

File diff suppressed because it is too large Load Diff

16087
po/ca.po

File diff suppressed because it is too large Load Diff

13253
po/cs.po

File diff suppressed because it is too large Load Diff

12061
po/da.po

File diff suppressed because it is too large Load Diff

11102
po/de.po

File diff suppressed because it is too large Load Diff

12637
po/el.po

File diff suppressed because it is too large Load Diff

12952
po/es.po

File diff suppressed because it is too large Load Diff

14406
po/eu.po

File diff suppressed because it is too large Load Diff

12033
po/fa.po

File diff suppressed because it is too large Load Diff

12530
po/fi.po

File diff suppressed because it is too large Load Diff

11506
po/fr_FR.po

File diff suppressed because it is too large Load Diff

11715
po/gl.po

File diff suppressed because it is too large Load Diff

14714
po/hu.po

File diff suppressed because it is too large Load Diff

14408
po/id.po

File diff suppressed because it is too large Load Diff

13518
po/it.po

File diff suppressed because it is too large Load Diff

12135
po/ja.po

File diff suppressed because it is too large Load Diff

13052
po/ko.po

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,19 @@
#!/bin/sh
set -e
maybe_append() {
while read -r msg; do
msgfile=$(printf '%s' "$msg" | cut -d'|' -f1)
msgline=$(printf '%s' "$msg" | cut -d'|' -f2)
msgid=$(printf '%s' "$msg" | cut -d'|' -f3-)
msgfile=$(echo $msg | cut -d'|' -f1)
msgline=$(echo $msg | cut -d'|' -f2)
msgid=$(echo $msg | cut -d'|' -f3-)
if ! grep -Fq "msgid $msgid" aegisub.pot; then
printf "\n#: %s:%s\nmsgid %s\nmsgstr \"\"\n\n" \
"$msgfile" "$msgline" "$msgid" >> aegisub.pot
echo "\n#: $msgfile:$msgline\nmsgid $msgid\nmsgstr \"\"\n" >> aegisub.pot
fi
done
}
find ../src ../src/command -name '*.cpp' -o -name '*.h' \
| xgettext --files-from=- -o - --c++ --sort-by-file \
-k_ -kSTR_MENU -kSTR_DISP -kSTR_HELP -kfmt_tl -kfmt_plural:2,3 -kwxT -kwxPLURAL:1,2 \
find ../src ../src/command -name \*.cpp -o -name \*.h \
| xgettext --files-from=- -o - --c++ -k_ -kSTR_MENU -kSTR_DISP -kSTR_HELP -kfmt_tl -kfmt_plural:2,3 -kwxT -kwxPLURAL:1,2 \
| sed 's/SOME DESCRIPTIVE TITLE./Aegisub 3.2/' \
| sed 's/YEAR/2005-2014/' \
| sed "s/THE PACKAGE'S COPYRIGHT HOLDER/Rodrigo Braz Monteiro, Niels Martin Hansen, Thomas Goyne et. al./" \
@ -36,27 +33,28 @@ grep '"[A-Za-z ]\+" : {' -n ../src/libresrc/default_hotkey.json \
| sed 's/^\([0-9]\+:\).*\("[^"]\+"\).*$/default_hotkey.json|\1|\2/' \
| maybe_append
find ../automation -name '*.lua' \
| LC_ALL=C sort \
| xargs grep 'tr"[^"]*"' -o -n \
find ../automation -name *.lua \
| xargs grep tr\"[^\"]\*\" -o -n \
| sed 's/\(.*\):\([0-9]\+\):tr\(".*"\)/\1|\2|\3/' \
| sed 's/\\/\\\\\\\\/g' \
| maybe_append
xgettext ../packages/desktop/aegisub.desktop.in.in \
--language=Desktop --join-existing --omit-header -o aegisub.pot
xgettext ../packages/desktop/aegisub.appdata.xml.in.in \
--language=AppData --join-existing --omit-header -o aegisub.pot
grep '^_[A-Za-z0-9]*=.*' ../packages/win_installer/fragment_strings.iss.in | while read line
for i in 'Name' 'GenericName' 'Comment' 'Keywords'
do
printf '%s\n' "$line" \
| sed 's/[^=]*=\(.*\)/packages\/win_installer\/fragment_strings.iss|1|"\1"/' \
grep ^_$i -n ../packages/desktop/aegisub.desktop.template.in \
| sed 's/\([0-9]\+\):[^=]\+=\(.*\)$/aegisub.desktop|\1|"\2"/' \
| maybe_append
done
for lang in $(cat LINGUAS) ; do
# If using gettext < 0.21, run twice to avoid reversing order of old strings
# ref: https://savannah.gnu.org/bugs/?58778
msgmerge --update --backup=none --no-fuzzy-matching --sort-by-file "$lang".po aegisub.pot
if which xmlstarlet >/dev/null 2>&1 && which jq >/dev/null 2>&1; then
for i in 'name' 'summary' 'p' 'li' 'caption'; do
xmlstarlet sel -t -v "//_$i" ../packages/desktop/aegisub.appdata.xml.template.in | jq -R .
done | nl -v0 -w1 -s'|' | sed -re 's/^/aegisub.appdata.xml|/' | maybe_append
fi
grep '^_[A-Za-z0-9]*=.*' ../packages/win_installer/fragment_strings.iss.in | while read line
do
echo "$line" \
| sed 's/[^=]*=\(.*\)/packages\/win_installer\/fragment_strings.iss|1|"\1"/' \
| maybe_append
done

View File

@ -1,14 +1,4 @@
i18n = import('i18n')
# This is currently busted on OSX
# and incomplete on any platform.
# It misses translatable strings not directly found in either
# C++ source, desktop or appdata file. This affects strings
# of the Windows installer (iss), from Lua scripts and JSON files.
# Until a solution is found, POT updates should continue to use make_pot.sh.
i18n.gettext('aegisub',
args: [
'-k_', '-kSTR_MENU', '-kSTR_DISP', '-kSTR_HELP', '-kwxT',
'-kfmt_tl', '-kfmt_plural:2,3', '-kwxPLURAL:1,2',
'--sort-by-file'
],
install_dir: localedir)

11750
po/nl.po

File diff suppressed because it is too large Load Diff

12160
po/pl.po

File diff suppressed because it is too large Load Diff

12526
po/pt_BR.po

File diff suppressed because it is too large Load Diff

13261
po/pt_PT.po

File diff suppressed because it is too large Load Diff

11634
po/ru.po

File diff suppressed because it is too large Load Diff

13263
po/sr_RS.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11505
po/uk_UA.po

File diff suppressed because it is too large Load Diff

15787
po/vi.po

File diff suppressed because it is too large Load Diff

12412
po/zh_CN.po

File diff suppressed because it is too large Load Diff

12393
po/zh_TW.po

File diff suppressed because it is too large Load Diff

View File

@ -242,7 +242,7 @@ uint32_t AssFile::AddExtradata(std::string const& key, std::string const& value)
return data.id;
}
}
Extradata.push_back(ExtradataEntry{next_extradata_id, 0, key, value});
Extradata.push_back(ExtradataEntry{next_extradata_id, key, value});
return next_extradata_id++; // return old value, then post-increment
}
@ -340,16 +340,10 @@ void AssFile::CleanExtradata() {
}
}
for (ExtradataEntry &e : Extradata) {
if (ids_used.count(e.id))
e.expiration_counter = 0;
else
e.expiration_counter++;
}
if (ids_used.size() != Extradata.size()) {
// Erase all no-longer-used extradata entries
Extradata.erase(std::remove_if(begin(Extradata), end(Extradata), [&](ExtradataEntry const& e) {
return e.expiration_counter >= 10;
return !ids_used.count(e.id);
}), end(Extradata));
}
}

View File

@ -50,11 +50,17 @@ using EntryList = typename boost::intrusive::make_list<T, boost::intrusive::cons
struct ExtradataEntry {
uint32_t id;
int expiration_counter;
std::string key;
std::string value;
};
// Both start and end are inclusive
struct LineFold {
int start;
int end;
bool collapsed;
};
struct AssFileCommit {
wxString const& message;
int *commit_id;

View File

@ -219,7 +219,7 @@ void AssParser::ParseExtradataLine(std::string const &data) {
// ensure next_extradata_id is always at least 1 more than the largest existing id
target->next_extradata_id = std::max(id+1, target->next_extradata_id);
target->Extradata.push_back(ExtradataEntry{id, 0, std::move(key), std::move(value)});
target->Extradata.push_back(ExtradataEntry{id, std::move(key), std::move(value)});
}
}

View File

@ -61,7 +61,7 @@ AudioBox::AudioBox(wxWindow *parent, agi::Context *context)
, controller(context->audioController.get())
, context(context)
, audio_open_connection(context->audioController->AddAudioPlayerOpenListener(&AudioBox::OnAudioOpen, this))
, panel(new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | (OPT_GET("App/Dark Mode")->GetBool() ? wxBORDER_SIMPLE : wxBORDER_RAISED)))
, panel(new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_RAISED))
, audioDisplay(new AudioDisplay(panel, context->audioController.get(), context))
, HorizontalZoom(new wxSlider(panel, Audio_Horizontal_Zoom, -OPT_GET("Audio/Zoom/Horizontal")->GetInt(), -50, 30, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH))
, VerticalZoom(new wxSlider(panel, Audio_Vertical_Zoom, OPT_GET("Audio/Zoom/Vertical")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))

View File

@ -56,7 +56,7 @@ BSAudioProvider::BSAudioProvider(agi::fs::path const& filename, agi::BackgroundR
{
bs.SetMaxCacheSize(OPT_GET("Provider/Audio/BestSource/Max Cache Size")->GetInt() << 20);
br->Run([&](agi::ProgressSink *ps) {
ps->SetTitle(from_wx(_("Indexing")));
ps->SetTitle(from_wx(_("Exacting")));
ps->SetMessage(from_wx(_("Creating cache... This can take a while!")));
ps->SetIndeterminate();
if (bs.GetExactDuration()) {

View File

@ -16,13 +16,11 @@
#include "audio_provider_factory.h"
#include "compat.h"
#include "factory_manager.h"
#include "options.h"
#include "utils.h"
#include <libaegisub/audio/provider.h>
#include <libaegisub/format.h>
#include <libaegisub/fs.h>
#include <libaegisub/log.h>
#include <libaegisub/path.h>
@ -41,7 +39,6 @@ struct factory {
const char *name;
std::unique_ptr<AudioProvider> (*create)(fs::path const&, BackgroundRunner *);
bool hidden;
std::function<bool(agi::fs::path const&)> wants_to_open = [](auto p) { return false; };
};
const factory providers[] = {
@ -51,13 +48,13 @@ const factory providers[] = {
{"FFmpegSource", CreateFFmpegSourceAudioProvider, false},
#endif
#ifdef WITH_AVISYNTH
{"Avisynth", CreateAvisynthAudioProvider, false, [](auto p) { return agi::fs::HasExtension(p, "avs"); }},
{"Avisynth", CreateAvisynthAudioProvider, false},
#endif
#ifdef WITH_BESTSOURCE
{"BestSource", CreateBSAudioProvider, false},
#endif
#ifdef WITH_VAPOURSYNTH
{"VapourSynth", CreateVapourSynthAudioProvider, false, [](auto p) { return agi::fs::HasExtension(p, "py") || agi::fs::HasExtension(p, "vpy"); }},
{"VapourSynth", CreateVapourSynthAudioProvider, false},
#endif
};
}
@ -66,83 +63,51 @@ std::vector<std::string> GetAudioProviderNames() {
return ::GetClasses(boost::make_iterator_range(std::begin(providers), std::end(providers)));
}
std::unique_ptr<agi::AudioProvider> SelectAudioProvider(fs::path const& filename,
Path const& path_helper,
BackgroundRunner *br) {
auto preferred = OPT_GET("Audio/Provider")->GetString();
if (!std::any_of(std::begin(providers), std::end(providers), [&](factory provider) { return provider.name == preferred; })) {
preferred = OPT_GET("Audio/Provider")->GetDefaultString();
}
auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred);
RearrangeWithPriority(sorted, filename);
bool found_file = false;
std::string errors;
auto tried_providers = sorted.begin();
for (; tried_providers < sorted.end(); tried_providers++) {
auto factory = *tried_providers;
std::string err;
try {
auto provider = factory->create(filename, br);
if (!provider) {
err = "Failed to create provider."; // Some generic error message here
} else {
LOG_I("audio_provider") << "Using audio provider: " << factory->name;
return provider;
}
}
catch (AudioDataNotFound const& ex) {
found_file = true;
err = ex.GetMessage();
}
catch (AudioProviderError const& ex) {
found_file = true;
err = ex.GetMessage();
}
errors += std::string(factory->name) + ": " + err + "\n";
LOG_D("audio_provider") << factory->name << ": " << err;
if (factory->name == preferred)
break;
}
std::vector<const factory *> remaining_providers(tried_providers + 1, sorted.end());
if (!remaining_providers.size()) {
// No provider could open the file
LOG_E("audio_provider") << "Could not open " << filename;
std::string msg = "Could not open " + filename.string() + ":\n" + errors;
if (!found_file) throw AudioDataNotFound(filename.string());
throw AudioProviderError(msg);
}
std::vector<std::string> names;
for (auto const& f : remaining_providers)
names.push_back(f->name);
int choice = wxGetSingleChoiceIndex(agi::format("Could not open %s with the preferred provider:\n\n%s\nPlease choose a different audio provider to try:", filename.string(), errors), _("Error loading audio"), to_wx(names));
if (choice == -1) {
throw agi::UserCancelException("audio loading cancelled by user");
}
auto factory = remaining_providers[choice];
auto provider = factory->create(filename, br);
if (!provider)
throw AudioProviderError("Audio provider returned null pointer");
LOG_I("audio_provider") << factory->name << ": opened " << filename;
return provider;
}
std::unique_ptr<agi::AudioProvider> GetAudioProvider(fs::path const& filename,
Path const& path_helper,
BackgroundRunner *br) {
std::unique_ptr<agi::AudioProvider> provider = SelectAudioProvider(filename, path_helper, br);
auto preferred = OPT_GET("Audio/Provider")->GetString();
auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred);
std::unique_ptr<AudioProvider> provider;
bool found_file = false;
bool found_audio = false;
std::string msg_all; // error messages from all attempted providers
std::string msg_partial; // error messages from providers that could partially load the file (knows container, missing codec)
for (auto const& factory : sorted) {
try {
provider = factory->create(filename, br);
if (!provider) continue;
LOG_I("audio_provider") << "Using audio provider: " << factory->name;
break;
}
catch (fs::FileNotFound const& err) {
LOG_D("audio_provider") << err.GetMessage();
msg_all += std::string(factory->name) + ": " + err.GetMessage() + " not found.\n";
}
catch (AudioDataNotFound const& err) {
LOG_D("audio_provider") << err.GetMessage();
found_file = true;
msg_all += std::string(factory->name) + ": " + err.GetMessage() + "\n";
}
catch (AudioProviderError const& err) {
LOG_D("audio_provider") << err.GetMessage();
found_audio = true;
found_file = true;
std::string thismsg = std::string(factory->name) + ": " + err.GetMessage() + "\n";
msg_all += thismsg;
msg_partial += thismsg;
}
}
if (!provider) {
if (found_audio)
throw AudioProviderError(msg_partial);
if (found_file)
throw AudioDataNotFound(msg_all);
throw fs::FileNotFound(filename);
}
bool needs_cache = provider->NeedsCache();

View File

@ -58,11 +58,7 @@ VapourSynthAudioProvider::VapourSynthAudioProvider(agi::fs::path const& filename
VSCleanCache();
VSCore *core = vs.GetAPI()->createCore(OPT_GET("Provider/VapourSynth/Autoload User Plugins")->GetBool() ? 0 : VSCoreCreationFlags::ccfDisableAutoLoading);
if (core == nullptr) {
throw VapourSynthError("Error creating core");
}
script = vs.GetScriptAPI()->createScript(core);
script = vs.GetScriptAPI()->createScript(nullptr);
if (script == nullptr) {
throw VapourSynthError("Error creating script API");
}
@ -90,7 +86,9 @@ VapourSynthAudioProvider::VapourSynthAudioProvider(agi::fs::path const& filename
num_samples = vi->numSamples;
}
catch (VapourSynthError const& err) {
throw agi::AudioProviderError(err.GetMessage());
// Unlike the video provider manager, the audio provider factory catches AudioProviderErrors and picks whichever source doesn't throw one.
// So just rethrow the Error here with an extra label so the user will see the error message and know the audio wasn't loaded with VS
throw VapourSynthError(agi::format("VapourSynth error: %s", err.GetMessage()));
}
template<typename T>

View File

@ -216,7 +216,6 @@ namespace Automation4 {
wxWindow *ww = config_dialog->CreateWindow(&w); // generate actual dialog contents
s->Add(ww, 0, wxALL, 5); // add contents to dialog
w.SetSizerAndFit(s);
w.SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED);
w.CenterOnParent();
w.ShowModal();
});
@ -316,14 +315,11 @@ namespace Automation4 {
std::vector<std::future<std::unique_ptr<Script>>> script_futures;
std::set<agi::fs::path> dirnames;
for (auto tok : agi::Split(path, '|')) {
auto path_it = agi::Split(path, '|');
for (auto tok : std::set<agi::StringRange>(begin(path_it), end(path_it))) {
auto dirname = config::path->Decode(agi::str(tok));
if (!agi::fs::DirectoryExists(dirname)) continue;
if (dirnames.count(dirname)) continue;
dirnames.insert(dirname);
for (auto filename : agi::fs::DirectoryIterator(dirname, "*.*"))
script_futures.emplace_back(std::async(std::launch::async, [=] {
return ScriptFactory::CreateFromFile(dirname/filename, false, false);

View File

@ -677,7 +677,6 @@ namespace {
if (lua_isnumber(L, -1) && lua_tointeger(L, -1) == 3) {
lua_pop(L, 1); // just to avoid tripping the stackcheck in debug
description = "Attempted to load an Automation 3 script as an Automation 4 Lua script. Automation 3 is no longer supported.";
stackcheck.check_stack(0);
return;
}
@ -690,7 +689,6 @@ namespace {
name = GetPrettyFilename().string();
lua_pop(L, 1);
stackcheck.check_stack(0);
// if we got this far, the script should be ready
loaded = true;
}

View File

@ -78,8 +78,6 @@ namespace Automation4 {
std::deque<PendingCommit> pending_commits;
/// Lines to delete once processing complete successfully
std::vector<std::unique_ptr<AssEntry>> lines_to_delete;
/// Lines that were allocated here and need to be deleted if the script is cancelled.
std::vector<AssEntry *> allocated_lines;
/// Create copies of all of the lines in the script info section if it
/// hasn't already happened. This is done lazily, since it only needs
@ -120,8 +118,6 @@ namespace Automation4 {
/// assumes a Lua representation of AssEntry on the top of the stack, and creates an AssEntry object of it
static std::unique_ptr<AssEntry> LuaToAssEntry(lua_State *L, AssFile *ass=nullptr);
std::unique_ptr<AssEntry> LuaToTrackedAssEntry(lua_State *L);
/// @brief Signal that the script using this file is now done running
/// @param set_undo If there's any uncommitted changes to the file,
/// they will be automatically committed with this

View File

@ -328,12 +328,6 @@ namespace Automation4 {
return result;
}
std::unique_ptr<AssEntry> LuaAssFile::LuaToTrackedAssEntry(lua_State *L) {
std::unique_ptr<AssEntry> e = LuaToAssEntry(L, ass);
allocated_lines.push_back(e.get());
return e;
}
int LuaAssFile::ObjectIndexRead(lua_State *L)
{
switch (lua_type(L, 2)) {
@ -459,7 +453,7 @@ namespace Automation4 {
// insert
CheckBounds(n);
auto e = LuaToTrackedAssEntry(L);
auto e = LuaToAssEntry(L, ass);
modification_type |= modification_mask(e.get());
QueueLineForDeletion(n - 1);
AssignLine(n - 1, std::move(e));
@ -548,7 +542,7 @@ namespace Automation4 {
for (int i = 1; i <= n; i++) {
lua_pushvalue(L, i);
auto e = LuaToTrackedAssEntry(L);
auto e = LuaToAssEntry(L, ass);
modification_type |= modification_mask(e.get());
if (lines.empty()) {
@ -592,7 +586,7 @@ namespace Automation4 {
new_entries.reserve(n - 1);
for (int i = 2; i <= n; i++) {
lua_pushvalue(L, i);
auto e = LuaToTrackedAssEntry(L);
auto e = LuaToAssEntry(L, ass);
modification_type |= modification_mask(e.get());
InsertLine(new_entries, i - 2, std::move(e));
lua_pop(L, 1);
@ -740,7 +734,6 @@ namespace Automation4 {
void LuaAssFile::Cancel()
{
for (auto& line : lines_to_delete) line.release();
for (AssEntry *line : allocated_lines) delete line;
references--;
if (!references) delete this;
}

View File

@ -326,12 +326,6 @@ namespace Automation4 {
{
lua_getfield(L, -1, "items");
read_string_array(L, items);
#ifdef __WXMAC__
if (std::find(items.begin(), items.end(), value) == items.end()) {
items.insert(items.begin(), value);
}
#endif
}
bool CanSerialiseValue() const override { return true; }

View File

@ -53,7 +53,6 @@
// Allocate storage for and initialise static members
namespace {
int avs_refcount = 0;
bool failed = false;
#ifdef _WIN32
HINSTANCE hLib = nullptr;
#else
@ -67,8 +66,8 @@ const AVS_Linkage *AVS_linkage = nullptr;
typedef IScriptEnvironment* __stdcall FUNC(int);
AviSynthWrapper::AviSynthWrapper() try {
if (!avs_refcount++) {
AviSynthWrapper::AviSynthWrapper() {
if (!avs_refcount){
#ifdef _WIN32
#define CONCATENATE(x, y) x ## y
#define _Lstr(x) CONCATENATE(L, x)
@ -95,6 +94,8 @@ AviSynthWrapper::AviSynthWrapper() try {
if (!env)
throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?");
avs_refcount++;
AVS_linkage = env->GetAVSLinkage();
// Set memory limit
@ -102,9 +103,6 @@ AviSynthWrapper::AviSynthWrapper() try {
if (memoryMax)
env->SetMemoryMax(memoryMax);
}
} catch (AvisynthError const&) {
avs_refcount--;
throw;
}
AviSynthWrapper::~AviSynthWrapper() {

View File

@ -56,14 +56,13 @@
#include <wx/scrolbar.h>
#include <wx/sizer.h>
// Check menu.h for id range allocation before editing this enum
enum {
GRID_SCROLLBAR = 1730,
MENU_SHOW_COL = (wxID_HIGHEST + 1) + 2000 // Needs 15 IDs after this
MENU_SHOW_COL = 1250 // Needs 15 IDs after this
};
BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context)
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | (OPT_GET("App/Dark Mode")->GetBool() ? wxBORDER_SIMPLE : wxSUNKEN_BORDER))
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER)
, scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL))
, context(context)
, columns(GetGridColumns())
@ -580,7 +579,7 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
void BaseGrid::OnContextMenu(wxContextMenuEvent &evt) {
wxPoint pos = evt.GetPosition();
if (pos == wxDefaultPosition || ScreenToClient(pos).y > lineHeight) {
if (!context_menu) context_menu = menu::GetMenu("grid_context", (wxID_HIGHEST + 1) + 8000, context);
if (!context_menu) context_menu = menu::GetMenu("grid_context", context);
menu::OpenPopupMenu(context_menu.get(), this);
}
else {

View File

@ -666,8 +666,8 @@ struct video_opt_autoscroll final : public Command {
struct video_pan_reset final : public validator_video_loaded {
CMD_NAME("video/pan_reset")
STR_MENU("Reset Video Pan")
STR_DISP("Reset Video Pan")
STR_MENU("Reset video pan")
STR_DISP("Reset video pan")
STR_HELP("Reset the video pan to the original value")
void operator()(agi::Context *c) override {

View File

@ -178,7 +178,7 @@ namespace {
STR_HELP("When the surrounding plane is also visible, switches which quad is locked. If inactive, the inner quad can only be resized without changing the perspective plane. If active, this holds for the outer quad instead.")
bool Validate(const agi::Context *c) override {
return c->videoDisplay->ToolIsType(typeid(VisualToolPerspective)) && c->videoDisplay->GetSubTool() & PERSP_OUTER;
return c->videoDisplay->ToolIsType(typeid(VisualToolPerspective)) && c->videoDisplay->GetSubTool() | PERSP_OUTER;
}
};

View File

@ -410,8 +410,8 @@ void ColorPickerScreenDropper::DropFromScreenXY(int x, int y) {
CGGetDisplaysWithPoint(CGPointMake(x, y), 1, &display_id, &display_count);
agi::scoped_holder<CGImageRef> img(CGDisplayCreateImageForRect(display_id, CGRectMake(x - resx / 2, y - resy / 2, resx, resy)), CGImageRelease);
size_t width = CGImageGetWidth(img);
size_t height = CGImageGetHeight(img);
NSUInteger width = CGImageGetWidth(img);
NSUInteger height = CGImageGetHeight(img);
std::vector<uint8_t> imgdata(height * width * 4);
agi::scoped_holder<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB(), CGColorSpaceRelease);
@ -596,7 +596,7 @@ DialogColorPicker::DialogColorPicker(wxWindow *parent, agi::Color initial_color,
eyedropper_bitmap = GETIMAGE(eyedropper_tool_24);
eyedropper_bitmap.SetMask(new wxMask(eyedropper_bitmap, wxColour(255, 0, 255)));
screen_dropper_icon = new wxStaticBitmap(this, -1, eyedropper_bitmap, wxDefaultPosition, wxDefaultSize, (OPT_GET("App/Dark Mode")->GetBool() ? wxBORDER_SIMPLE : wxRAISED_BORDER));
screen_dropper_icon = new wxStaticBitmap(this, -1, eyedropper_bitmap, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER);
screen_dropper = new ColorPickerScreenDropper(this, 7, 7, 8);
// Arrange the controls in a nice way

View File

@ -15,7 +15,6 @@
// Aegisub Project http://www.aegisub.org/
#include "colour_button.h"
#include "compat.h"
#include "format.h"
#include "help_button.h"
#include "libresrc/libresrc.h"
@ -41,7 +40,7 @@ namespace {
struct DialogDummyVideo {
wxDialog d;
wxString fps = OPT_GET("Video/Dummy/FPS String")->GetString();
double fps = OPT_GET("Video/Dummy/FPS")->GetDouble();
int width = OPT_GET("Video/Dummy/Last/Width")->GetInt();
int height = OPT_GET("Video/Dummy/Last/Height")->GetInt();
int length = OPT_GET("Video/Dummy/Last/Length")->GetInt();
@ -55,7 +54,7 @@ struct DialogDummyVideo {
void AddCtrl(wxString const& label, T *ctrl);
void OnResolutionShortcut(wxCommandEvent &evt);
bool UpdateLengthDisplay();
void UpdateLengthDisplay();
DialogDummyVideo(wxWindow *parent);
};
@ -86,6 +85,11 @@ wxSpinCtrl *spin_ctrl(wxWindow *parent, int min, int max, int *value) {
return ctrl;
}
// FIXME: change the misleading function name, this is TextCtrl in fact
wxControl *spin_ctrl(wxWindow *parent, double min, double max, double *value) {
return new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, DoubleValidator(value, min, max));
}
wxComboBox *resolution_shortcuts(wxWindow *parent, int width, int height) {
wxComboBox *ctrl = new wxComboBox(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
@ -117,9 +121,7 @@ DialogDummyVideo::DialogDummyVideo(wxWindow *parent)
AddCtrl(_("Video resolution:"), resolution_shortcuts(&d, width, height));
AddCtrl("", res_sizer);
AddCtrl(_("Color:"), color_sizer);
wxTextValidator fpsVal(wxFILTER_INCLUDE_CHAR_LIST, &fps);
fpsVal.SetCharIncludes("0123456789./");
AddCtrl(_("Frame rate (fps):"), new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxDefaultSize, 0, fpsVal));
AddCtrl(_("Frame rate (fps):"), spin_ctrl(&d, .1, 1000.0, &fps));
AddCtrl(_("Duration (frames):"), spin_ctrl(&d, 2, 36000000, &length)); // Ten hours of 1k FPS
AddCtrl("", length_display = new wxStaticText(&d, -1, ""));
@ -131,19 +133,17 @@ DialogDummyVideo::DialogDummyVideo(wxWindow *parent)
main_sizer->Add(new wxStaticLine(&d, wxHORIZONTAL), wxSizerFlags().HorzBorder().Expand());
main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border());
btn_sizer->GetAffirmativeButton()->Enable(UpdateLengthDisplay());
UpdateLengthDisplay();
d.SetSizerAndFit(main_sizer);
d.CenterOnParent();
d.Bind(wxEVT_COMBOBOX, &DialogDummyVideo::OnResolutionShortcut, this);
color_btn->Bind(EVT_COLOR, [=](ValueEvent<agi::Color>& e) { color = e.Get(); });
auto on_update = [&, btn_sizer](wxCommandEvent&) {
d.Bind(wxEVT_SPINCTRL, [&](wxCommandEvent&) {
d.TransferDataFromWindow();
btn_sizer->GetAffirmativeButton()->Enable(UpdateLengthDisplay());
};
d.Bind(wxEVT_SPINCTRL, on_update);
d.Bind(wxEVT_TEXT, on_update);
UpdateLengthDisplay();
});
}
static void add_label(wxWindow *parent, wxSizer *sizer, wxString const& label) {
@ -167,16 +167,8 @@ void DialogDummyVideo::OnResolutionShortcut(wxCommandEvent &e) {
d.TransferDataToWindow();
}
bool DialogDummyVideo::UpdateLengthDisplay() {
std::string dur = "-";
bool valid = false;
agi::vfr::Framerate fr;
if (DummyVideoProvider::TryParseFramerate(from_wx(fps), fr)) {
dur = agi::Time(fr.TimeAtFrame(length)).GetAssFormatted(true);
valid = true;
}
length_display->SetLabel(fmt_tl("Resulting duration: %s", dur));
return valid;
void DialogDummyVideo::UpdateLengthDisplay() {
length_display->SetLabel(fmt_tl("Resulting duration: %s", agi::Time(length / fps * 1000).GetAssFormatted(true)));
}
}
@ -185,12 +177,12 @@ std::string CreateDummyVideo(wxWindow *parent) {
if (dlg.d.ShowModal() != wxID_OK)
return "";
OPT_SET("Video/Dummy/FPS String")->SetString(from_wx(dlg.fps));
OPT_SET("Video/Dummy/FPS")->SetDouble(dlg.fps);
OPT_SET("Video/Dummy/Last/Width")->SetInt(dlg.width);
OPT_SET("Video/Dummy/Last/Height")->SetInt(dlg.height);
OPT_SET("Video/Dummy/Last/Length")->SetInt(dlg.length);
OPT_SET("Colour/Video Dummy/Last Colour")->SetColor(dlg.color);
OPT_SET("Video/Dummy/Pattern")->SetBool(dlg.pattern);
return DummyVideoProvider::MakeFilename(from_wx(dlg.fps), dlg.length, dlg.width, dlg.height, dlg.color, dlg.pattern);
return DummyVideoProvider::MakeFilename(dlg.fps, dlg.length, dlg.width, dlg.height, dlg.color, dlg.pattern);
}

View File

@ -20,7 +20,6 @@
#include "dialog_progress.h"
#include "compat.h"
#include "options.h"
#include "utils.h"
#include <libaegisub/dispatch.h>
@ -122,7 +121,7 @@ public:
};
DialogProgress::DialogProgress(wxWindow *parent, wxString const& title_text, wxString const& message)
: wxDialog(parent, -1, title_text, wxDefaultPosition, wxDefaultSize, (OPT_GET("App/Dark Mode")->GetBool() ? wxBORDER_SIMPLE : wxBORDER_RAISED))
: wxDialog(parent, -1, title_text, wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED)
, pulse_timer(GetEventHandler())
{
title = new wxStaticText(this, -1, title_text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
@ -256,8 +255,6 @@ void DialogProgress::OnCancel(wxCommandEvent &) {
}
void DialogProgress::SetProgress(int target) {
pulse_timer.Stop();
if (target == progress_target) return;
using namespace std::chrono;

View File

@ -212,7 +212,7 @@ DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Con
auto ScaleX = num_text_ctrl(&work->scalex, 0.0, 10000.0, 1, 2);
auto ScaleY = num_text_ctrl(&work->scaley, 0.0, 10000.0, 1, 2);
auto Angle = num_text_ctrl(&work->angle, -360.0, 360.0, 1.0, 2);
auto Spacing = num_text_ctrl(&work->spacing, 0.0, 1000.0, 0.1, 3);
auto Spacing = num_text_ctrl(&work->spacing, -1000.0, 1000.0, 0.1, 3);
Encoding = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, encodingStrings, wxCB_READONLY);
// Set control tooltips
@ -327,7 +327,7 @@ DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Con
// Preview
auto previewButton = new ColourButton(this, wxSize(45, 16), false, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
PreviewText = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Style Editor/Preview Text")->GetString()));
SubsPreview = new SubtitlesPreview(this, wxSize(100, 60), (OPT_GET("App/Dark Mode")->GetBool() ? wxBORDER_SIMPLE : wxSUNKEN_BORDER), OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
SubsPreview = new SubtitlesPreview(this, wxSize(100, 60), wxSUNKEN_BORDER, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
SubsPreview->SetToolTip(_("Preview of current style"));
SubsPreview->SetStyle(*style);

View File

@ -51,11 +51,6 @@
#include <wx/stattext.h>
#include <wx/stc/stc.h>
// Define macros for wxWidgets 3.1
#ifndef wxSTC_KEYMOD_SHIFT
#define wxSTC_KEYMOD_SHIFT wxSTC_SCMOD_SHIFT
#endif
static void add_hotkey(wxSizer *sizer, wxWindow *parent, const char *command, wxString const& text) {
sizer->Add(new wxStaticText(parent, -1, text));
sizer->Add(new wxStaticText(parent, -1, to_wx(hotkey::get_hotkey_str_first("Translation Assistant", command))));
@ -102,7 +97,7 @@ DialogTranslation::DialogTranslation(agi::Context *c)
translated_text->SetMarginWidth(1, 0);
translated_text->SetFocus();
translated_text->Bind(wxEVT_CHAR_HOOK, &DialogTranslation::OnKeyDown, this);
translated_text->CmdKeyAssign(wxSTC_KEY_RETURN, wxSTC_KEYMOD_SHIFT, wxSTC_CMD_NEWLINE);
translated_text->CmdKeyAssign(wxSTC_KEY_RETURN, wxSTC_SCMOD_SHIFT, wxSTC_CMD_NEWLINE);
wxSizer *translated_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Translation"));
translated_box->Add(translated_text, 1, wxEXPAND, 0);

View File

@ -17,8 +17,6 @@
#include <string>
#include <vector>
#include <libaegisub/fs.h>
namespace {
template<typename Container>
std::vector<std::string> GetClasses(Container const& c) {
@ -52,21 +50,4 @@ auto GetSorted(Container const& c, std::string const& preferred) -> std::vector<
}
return sorted;
}
template<typename Container>
auto RearrangeWithPriority(Container &c, agi::fs::path const& filename) {
size_t end_of_hidden = 0;
for (size_t i = 0; i < c.size(); i++) {
auto provider = c[i];
if (provider->hidden) {
end_of_hidden = i;
} else {
if (provider->wants_to_open(filename)) {
c.erase(c.begin() + i);
c.insert(c.begin() + end_of_hidden + 1, provider);
break;
}
}
}
}
}

View File

@ -79,13 +79,16 @@ void FoldController::AddFold(AssDialogue& start, AssDialogue& end, bool collapse
}
}
void FoldController::DoForAllFolds(std::function<void(AssDialogue&)> action) {
bool FoldController::DoForAllFolds(bool action(AssDialogue& line)) {
for (AssDialogue& line : context->ass->Events) {
if (line.Fold.valid) {
action(line);
bool result = action(line);
UpdateLineExtradata(line);
if (result)
return true;
}
}
return false;
}
void FoldController::FixFoldsPreCommit(int type, const AssDialogue *single_line) {
@ -95,22 +98,24 @@ void FoldController::FixFoldsPreCommit(int type, const AssDialogue *single_line)
}
// For each line in lines, applies action() to the opening delimiter of the innermost fold containing this line.
// Returns true as soon as any action() call returned true.
//
// In general, this can leave the folds in an inconsistent state, so unless action() is read-only this should always
// be followed by a commit.
void FoldController::DoForFoldsAt(std::vector<AssDialogue *> const& lines, std::function<void(AssDialogue&)> action) {
std::map<int, bool> visited;
bool FoldController::DoForFoldsAt(std::vector<AssDialogue *> const& lines, bool action(AssDialogue& line)) {
for (AssDialogue *line : lines) {
if (line->Fold.parent != nullptr && !(line->Fold.valid && !line->Fold.side)) {
line = line->Fold.parent;
}
if (visited.count(line->Row))
continue;
action(*line);
UpdateLineExtradata(*line);
visited[line->Row] = true;
if (!line->Fold.visited) {
bool result = action(*line);
UpdateLineExtradata(*line);
if (result)
return true;
}
line->Fold.visited = true;
}
return false;
}
void FoldController::UpdateFoldInfo() {
@ -258,6 +263,7 @@ void FoldController::LinkFolds() {
line->Fold.parent = foldStack.empty() ? nullptr : foldStack.back();
line->Fold.nextVisible = nullptr;
line->Fold.visible = highestFolded > (int) foldStack.size();
line->Fold.visited = false;
line->Fold.visibleRow = visibleRow;
if (line->Fold.visible) {
@ -293,78 +299,56 @@ int FoldController::GetMaxDepth() {
return maxdepth;
}
bool FoldController::ActionHasFold(AssDialogue& line) { return line.Fold.valid; }
bool FoldController::ActionClearFold(AssDialogue& line) { line.Fold.extraExists = false; line.Fold.valid = false; return false; }
bool FoldController::ActionOpenFold(AssDialogue& line) { line.Fold.collapsed = false; return false; }
bool FoldController::ActionCloseFold(AssDialogue& line) { line.Fold.collapsed = true; return false; }
bool FoldController::ActionToggleFold(AssDialogue& line) { line.Fold.collapsed = !line.Fold.collapsed; return false; }
void FoldController::ClearAllFolds() {
DoForAllFolds([&](AssDialogue &line) {
line.Fold.extraExists = false; line.Fold.valid = false;
});
FoldController::DoForAllFolds(FoldController::ActionClearFold);
context->ass->Commit(_("clear all folds"), AssFile::COMMIT_FOLD);
}
void FoldController::OpenAllFolds() {
DoForAllFolds([&](AssDialogue &line) {
line.Fold.collapsed = false;
});
FoldController::DoForAllFolds(FoldController::ActionOpenFold);
context->ass->Commit(_("open all folds"), AssFile::COMMIT_FOLD);
}
void FoldController::CloseAllFolds() {
DoForAllFolds([&](AssDialogue &line) {
line.Fold.collapsed = true;
});
FoldController::DoForAllFolds(FoldController::ActionCloseFold);
context->ass->Commit(_("close all folds"), AssFile::COMMIT_FOLD);
}
bool FoldController::HasFolds() {
bool hasfold = false;
DoForAllFolds([&](AssDialogue &line) {
hasfold = hasfold || line.Fold.valid;
});
return hasfold;
return FoldController::DoForAllFolds(FoldController::ActionHasFold);
}
void FoldController::ClearFoldsAt(std::vector<AssDialogue *> const& lines) {
DoForFoldsAt(lines, [&](AssDialogue &line) {
line.Fold.extraExists = false; line.Fold.valid = false;
if (line.Fold.counterpart) {
line.Fold.counterpart->Fold.extraExists = false;
line.Fold.counterpart->Fold.valid = false;
}
});
FoldController::DoForFoldsAt(lines, FoldController::ActionClearFold);
context->ass->Commit(_("clear folds"), AssFile::COMMIT_FOLD);
}
void FoldController::OpenFoldsAt(std::vector<AssDialogue *> const& lines) {
DoForFoldsAt(lines, [&](AssDialogue &line) {
line.Fold.collapsed = false;
if (line.Fold.counterpart)
line.Fold.counterpart->Fold.collapsed = line.Fold.collapsed;
});
FoldController::DoForFoldsAt(lines, FoldController::ActionOpenFold);
context->ass->Commit(_("open folds"), AssFile::COMMIT_FOLD);
}
void FoldController::CloseFoldsAt(std::vector<AssDialogue *> const& lines) {
DoForFoldsAt(lines, [&](AssDialogue &line) {
line.Fold.collapsed = true;
if (line.Fold.counterpart)
line.Fold.counterpart->Fold.collapsed = line.Fold.collapsed;
});
FoldController::DoForFoldsAt(lines, FoldController::ActionCloseFold);
context->ass->Commit(_("close folds"), AssFile::COMMIT_FOLD);
}
void FoldController::ToggleFoldsAt(std::vector<AssDialogue *> const& lines) {
DoForFoldsAt(lines, [&](AssDialogue &line) {
line.Fold.collapsed = !line.Fold.collapsed;
if (line.Fold.counterpart)
line.Fold.counterpart->Fold.collapsed = line.Fold.collapsed;
});
FoldController::DoForFoldsAt(lines, FoldController::ActionToggleFold);
context->ass->Commit(_("toggle folds"), AssFile::COMMIT_FOLD);
}
bool FoldController::AreFoldsAt(std::vector<AssDialogue *> const& lines) {
bool hasfold = false;
DoForFoldsAt(lines, [&](AssDialogue &line) {
hasfold = hasfold || line.Fold.valid;
});
return hasfold;
return FoldController::DoForFoldsAt(lines, FoldController::ActionHasFold);
}

View File

@ -56,6 +56,9 @@ class FoldInfo {
/// False if a fold is started here, true otherwise.
bool side = false;
// Used in DoForFoldsAt to ensure each line is visited only once
bool visited = false;
/// Whether the line is currently visible
bool visible = true;
@ -103,12 +106,24 @@ class FoldController {
void RawAddFold(AssDialogue& start, AssDialogue& end, bool collapsed);
void DoForFoldsAt(std::vector<AssDialogue *> const& lines, std::function<void(AssDialogue&)> action);
bool DoForFoldsAt(std::vector<AssDialogue *> const& lines, bool action(AssDialogue& line));
void DoForAllFolds(std::function<void(AssDialogue&)> action);
bool DoForAllFolds(bool action(AssDialogue& line));
void FixFoldsPreCommit(int type, const AssDialogue *single_line);
// These are used for the DoForAllFolds action and should not be used as ordinary getters/setters
static bool ActionHasFold(AssDialogue& line);
static bool ActionClearFold(AssDialogue& line);
static bool ActionOpenFold(AssDialogue& line);
static bool ActionCloseFold(AssDialogue& line);
static bool ActionToggleFold(AssDialogue& line);
/// Updates the line's extradata entry from the values in FoldInfo. Used after actions like toggling folds.
void UpdateLineExtradata(AssDialogue& line);

View File

@ -139,8 +139,6 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) {
break;
}
case AssBlockType::DRAWING:
used_styles[style].drawing = true;
break;
case AssBlockType::COMMENT:
break;
}
@ -148,11 +146,7 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) {
}
void FontCollector::ProcessChunk(std::pair<StyleInfo, UsageData> const& style) {
if (style.second.chars.empty() && !style.second.drawing) return;
if (style.second.chars.empty() && style.second.drawing) {
status_callback(fmt_tl("Font '%s' is used in a drawing, but not in any text.\n", style.first.facename), 3);
}
if (style.second.chars.empty()) return;
auto res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);

View File

@ -127,7 +127,6 @@ class FontCollector {
/// Data about where each style is used
struct UsageData {
std::vector<int> chars; ///< Characters used in this style which glyphs will be needed for
bool drawing = false; ///< Whether this style is used for a drawing
std::vector<int> lines; ///< Lines on which this style is used via overrides
std::vector<std::string> styles; ///< ASS styles which use this style
};

View File

@ -128,7 +128,7 @@ FrameMain::FrameMain()
EnableToolBar(*OPT_GET("App/Show Toolbar"));
StartupLog("Initialize menu bar");
menu::GetMenuBar("main", this, (wxID_HIGHEST + 1) + 10000, context.get());
menu::GetMenuBar("main", this, context.get());
StartupLog("Create status bar");
CreateStatusBar(2);

View File

@ -150,7 +150,9 @@ struct GridColumnFolds final : GridColumn {
bool OnMouseEvent(AssDialogue *d, agi::Context *c, wxMouseEvent &event) const override {
if ((event.LeftDown() || event.LeftDClick()) && !event.ShiftDown() && !event.CmdDown() && !event.AltDown()) {
if (d->Fold.hasFold() && !d->Fold.isEnd()) {
c->foldController->ToggleFoldsAt({d});
std::vector<AssDialogue *> lines;
lines.push_back(d);
c->foldController->ToggleFoldsAt(lines);
return true;
}
}

View File

@ -28,19 +28,6 @@ class wxMenu;
class wxMenuBar;
class wxWindow;
/*
ID allocation for menu items:
... - wxID_ANY(-1), wxID_LOWEST(4999) - wxID_HIGHEST(5999) Reserved by wxWidgets, see documentation for wxID_HIGHEST
(wxID_HIGHEST + 1) + 2000 ~ (wxID_HIGHEST + 1) + 2014 Grid column list, see base_grid.cpp
(wxID_HIGHEST + 1) + 3000 ~ (wxID_HIGHEST + 1) + 3001 Context menu, see timeedit_ctrl.cpp
(wxID_HIGHEST + 1) + 4000 ~ (wxID_HIGHEST + 1) + 7999 Context menu, see subs_edit_ctrl.cpp
(wxID_HIGHEST + 1) + 8000 ~ (wxID_HIGHEST + 1) + 8019 Grid context menu, see base_grid.cpp
(wxID_HIGHEST + 1) + 9000 ~ (wxID_HIGHEST + 1) + 9004 Video context menu, see video_display.cpp
(wxID_HIGHEST + 1) + 10000 ~ (wxID_HIGHEST + 1) + 10999 Main menu
*/
namespace menu {
DEFINE_EXCEPTION(Error, agi::Exception);
DEFINE_EXCEPTION(UnknownMenu, Error);
@ -52,7 +39,7 @@ namespace menu {
/// Throws:
/// UnknownMenu if no menu with the given name was found
/// BadMenu if there is a menu with the given name, but it is invalid
void GetMenuBar(std::string const& name, wxFrame *window, int id_base, agi::Context *c);
void GetMenuBar(std::string const& name, wxFrame *window, agi::Context *c);
/// @brief Get the menu with the specified name as a wxMenu
/// @param name Name of the menu
@ -60,7 +47,7 @@ namespace menu {
/// Throws:
/// UnknownMenu if no menu with the given name was found
/// BadMenu if there is a menu with the given name, but it is invalid
std::unique_ptr<wxMenu> GetMenu(std::string const& name, int id_base, agi::Context *c);
std::unique_ptr<wxMenu> GetMenu(std::string const& name, agi::Context *c);
/// @brief Open a popup menu at the mouse
/// @param menu Menu to open

View File

@ -16,8 +16,7 @@
"Save Charset" : "UTF-8",
"Save UI State" : true,
"Show Toolbar" : true,
"Toolbar Icon Size" : 16,
"Dark Mode" : false
"Toolbar Icon Size" : 16
},
@ -67,7 +66,7 @@
"Next Line on Commit" : true,
"Player" : "",
"Plays When Stepping Video" : false,
"Provider" : "FFmpegSource",
"Provider" : "ffmpegsource",
"Renderer" : {
"Spectrum" : {
"Cutoff" : 0,
@ -373,7 +372,6 @@
}
},
"VapourSynth" : {
"Autoload User Plugins": true,
"Cache" : {
"Files" : 500,
"Size" : 1000
@ -394,7 +392,7 @@
},
"VapourSynth" : {
"Log Level": "Information",
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment this line to make Aegisub look for a keyframes file for the video, or ask to detect keyframes on scene changes if no file was found.\n# You can also change the GenKeyframesMode. Valid values are NEVER, ALWAYS, and ASK.\n#__aegi_keyframes = a.get_keyframes(filename, clip, __aegi_keyframes, generate=a.GenKeyframesMode.ASK)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment the first following line to read keyframes from a file when present.\n#__aegi_keyframes = a.try_get_keyframes(filename, __aegi_keyframes)\n\n# Uncomment the following line to automatically generate keyframes at scene changes. This will take some time when first loading a video.\n__aegi_keyframes = a.get_keyframes(filename, clip)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
}
}
},
@ -624,7 +622,6 @@
"Grid": false,
"Org Mode": 0
},
"Shape Handle Size": 3,
"Autohide": false
},
"Align to Video" : {
@ -651,7 +648,7 @@
"Maximized" : false
},
"Dummy" : {
"FPS String" : "24000/1001",
"FPS" : 23.975999999999999091,
"Last" : {
"Height" : 720,
"Length" : 40000,
@ -662,7 +659,7 @@
"Last Script Resolution Mismatch Choice" : 2,
"Open Audio" : true,
"Overscan Mask" : false,
"Provider" : "FFmpegSource",
"Provider" : "ffmpegsource",
"Script Resolution Mismatch" : 1,
"Slider" : {
"Fast Jump Step" : 10,

View File

@ -2,9 +2,7 @@
"Audio" : {
"Player" : "DirectSound"
},
"Provider" : {
"VapourSynth" : {
"Autoload User Plugins": false
}
"Subtitle" : {
"Provider" : "CSRI/xy-vsfilter_aegisub"
}
}

View File

@ -125,10 +125,10 @@
"edit/line/delete" : [
"Ctrl-Delete"
],
"edit/line/split/before" : [
"edit/line/duplicate/shift" : [
"Ctrl-D"
],
"edit/line/split/after" : [
"edit/line/duplicate/shift_back" : [
"Ctrl-Shift-D"
],
"edit/line/paste" : [

View File

@ -238,7 +238,6 @@
{ "command" : "video/frame/save/subs" },
{ "command" : "video/frame/copy/subs" },
{},
{ "command" : "video/copy_coordinates" },
{ "command" : "video/pan_reset" }
{ "command" : "video/copy_coordinates" }
]
}

View File

@ -19,15 +19,9 @@ resrc = [
output: ['bitmap.cpp', 'bitmap.h'])
]
if host_machine.system() == 'windows'
conf_platform_json = configure_file(input: 'default_config_win.json',
output: 'default_config_platform.json',
copy: true)
else
conf_platform_json = configure_file(input: 'default_config_platform.json.in',
output: '@BASENAME@',
configuration: conf_platform)
endif
conf_platform_json = configure_file(input: 'default_config_platform.json.in',
output: '@BASENAME@',
configuration: conf_platform)
if host_machine.system() == 'darwin'
resmanifest = 'manifest_osx.respack'

View File

@ -16,8 +16,7 @@
"Save Charset" : "UTF-8",
"Save UI State" : true,
"Show Toolbar" : true,
"Toolbar Icon Size" : 16,
"Dark Mode" : false
"Toolbar Icon Size" : 16
},
@ -67,7 +66,7 @@
"Next Line on Commit" : true,
"Player" : "",
"Plays When Stepping Video" : false,
"Provider" : "FFmpegSource",
"Provider" : "ffmpegsource",
"Renderer" : {
"Spectrum" : {
"Cutoff" : 0,
@ -373,7 +372,6 @@
}
},
"VapourSynth" : {
"Autoload User Plugins": true,
"Cache" : {
"Files" : 500,
"Size" : 1000
@ -394,7 +392,7 @@
},
"VapourSynth" : {
"Log Level": "Information",
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment this line to make Aegisub look for a keyframes file for the video, or ask to detect keyframes on scene changes if no file was found.\n# You can also change the GenKeyframesMode. Valid values are NEVER, ALWAYS, and ASK.\n#__aegi_keyframes = a.get_keyframes(filename, clip, __aegi_keyframes, generate=a.GenKeyframesMode.ASK)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment the first following line to read keyframes from a file when present.\n#__aegi_keyframes = a.try_get_keyframes(filename, __aegi_keyframes)\n\n# Uncomment the following line to automatically generate keyframes at scene changes. This will take some time when first loading a video.\n__aegi_keyframes = a.get_keyframes(filename, clip)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
}
}
},
@ -624,7 +622,6 @@
"Grid": false,
"Org Mode": 0
},
"Shape Handle Size": 3,
"Autohide": false
},
"Align to Video" : {
@ -651,7 +648,7 @@
"Maximized" : false
},
"Dummy" : {
"FPS String" : "24000/1001",
"FPS" : 23.975999999999999091,
"Last" : {
"Height" : 720,
"Length" : 40000,
@ -662,7 +659,7 @@
"Last Script Resolution Mismatch Choice" : 2,
"Open Audio" : true,
"Overscan Mask" : false,
"Provider" : "FFmpegSource",
"Provider" : "ffmpegsource",
"Script Resolution Mismatch" : 1,
"Slider" : {
"Fast Jump Step" : 10,

View File

@ -128,10 +128,10 @@
"edit/line/delete" : [
"Ctrl-Backspace"
],
"edit/line/split/before" : [
"edit/line/duplicate/shift" : [
"Ctrl-D"
],
"edit/line/split/after" : [
"edit/line/duplicate/shift_back" : [
"Ctrl-Shift-D"
],
"edit/line/paste" : [

View File

@ -242,7 +242,6 @@
{ "command" : "video/frame/save/subs" },
{ "command" : "video/frame/copy/subs" },
{},
{ "command" : "video/copy_coordinates" },
{ "command" : "video/pan_reset" }
{ "command" : "video/copy_coordinates" }
]
}

View File

@ -224,12 +224,6 @@ bool AegisubApp::OnInit() {
}
#endif
#if defined(__WXMSW__) && wxVERSION_NUMBER >= 3300
if (OPT_GET("App/Dark Mode")->GetBool()) {
MSWEnableDarkMode(wxApp::DarkMode_Always);
}
#endif
// Init commands.
cmd::init_builtin_commands();

View File

@ -51,11 +51,10 @@
#endif
namespace {
/// Window ID of first menu item
static const int MENU_ID_BASE = 10000;
class MruMenu final : public wxMenu {
/// Window ID of first menu item
const int id_base;
std::string type;
std::vector<wxMenuItem *> items;
std::vector<std::string> *cmds;
@ -67,7 +66,7 @@ class MruMenu final : public wxMenu {
for (size_t i = GetMenuItemCount(); i < new_size; ++i) {
if (i >= items.size()) {
items.push_back(new wxMenuItem(this, id_base + cmds->size(), "_"));
items.push_back(new wxMenuItem(this, MENU_ID_BASE + cmds->size(), "_"));
cmds->push_back(agi::format("recent/%s/%d", boost::to_lower_copy(type), i));
}
Append(items[i]);
@ -75,8 +74,8 @@ class MruMenu final : public wxMenu {
}
public:
MruMenu(int id_base, std::string type, std::vector<std::string> *cmds)
: id_base(id_base), type(std::move(type))
MruMenu(std::string type, std::vector<std::string> *cmds)
: type(std::move(type))
, cmds(cmds)
{
}
@ -119,8 +118,6 @@ public:
/// on submenus in many cases, and registering large numbers of wxEVT_UPDATE_UI
/// handlers makes everything involves events unusably slow.
class CommandManager {
/// Window ID of first menu item
const int id_base;
/// Menu items which need to do something on menu open
std::vector<std::pair<std::string, wxMenuItem*>> dynamic_items;
/// Menu items which need to be updated only when hotkeys change
@ -168,8 +165,8 @@ class CommandManager {
}
public:
CommandManager(int id_base, agi::Context *context)
: id_base(id_base), context(context)
CommandManager(agi::Context *context)
: context(context)
, hotkeys_changed(hotkey::inst->AddHotkeyChangeListener(&CommandManager::OnHotkeysChanged, this))
{
}
@ -197,7 +194,7 @@ public:
menu_text += to_wx("\t" + hotkey::get_hotkey_str_first("Default", co->name()));
wxMenuItem *item = new wxMenuItem(parent, id_base + items.size(), menu_text, co->StrHelp(), kind);
wxMenuItem *item = new wxMenuItem(parent, MENU_ID_BASE + items.size(), menu_text, co->StrHelp(), kind);
#ifndef __WXMAC__
/// @todo Maybe make this a configuration option instead?
if (kind == wxITEM_NORMAL)
@ -232,7 +229,7 @@ public:
/// @param name MRU type
/// @param parent Menu to append the new MRU menu to
void AddRecent(std::string const& name, wxMenu *parent) {
mru.push_back(new MruMenu(id_base, name, &items));
mru.push_back(new MruMenu(name, &items));
parent->AppendSubMenu(mru.back(), _("&Recent"));
}
@ -246,7 +243,7 @@ public:
void OnMenuClick(wxCommandEvent &evt) {
// This also gets clicks on unrelated things such as the toolbar, so
// the window ID ranges really need to be unique
size_t id = static_cast<size_t>(evt.GetId() - id_base);
size_t id = static_cast<size_t>(evt.GetId() - MENU_ID_BASE);
if (id < items.size() && context)
cmd::call(items[id], context);
@ -279,13 +276,13 @@ public:
/// Wrapper for wxMenu to add a command manager
struct CommandMenu final : public wxMenu {
CommandManager cm;
CommandMenu(int id_base, agi::Context *c) : cm(id_base, c) { }
CommandMenu(agi::Context *c) : cm(c) { }
};
/// Wrapper for wxMenuBar to add a command manager
struct CommandMenuBar final : public wxMenuBar {
CommandManager cm;
CommandMenuBar(int id_base, agi::Context *c) : cm(id_base, c) { }
CommandMenuBar(agi::Context *c) : cm(c) { }
};
/// Read a string from a json object
@ -501,7 +498,7 @@ public:
}
namespace menu {
void GetMenuBar(std::string const& name, wxFrame *window, int id_base, agi::Context *c) {
void GetMenuBar(std::string const& name, wxFrame *window, agi::Context *c) {
#ifdef __WXMAC__
auto bind_events = [&](CommandMenuBar *menu) {
window->Bind(wxEVT_ACTIVATE, [=](wxActivateEvent&) { menu->cm.SetContext(c); });
@ -517,7 +514,7 @@ namespace menu {
}
#endif
auto menu = agi::make_unique<CommandMenuBar>(id_base, c);
auto menu = agi::make_unique<CommandMenuBar>(c);
for (auto const& item : get_menu(name)) {
std::string submenu, disp;
read_entry(item, "submenu", &submenu);
@ -550,8 +547,8 @@ namespace menu {
menu.release();
}
std::unique_ptr<wxMenu> GetMenu(std::string const& name, int id_base, agi::Context *c) {
auto menu = agi::make_unique<CommandMenu>(id_base, c);
std::unique_ptr<wxMenu> GetMenu(std::string const& name, agi::Context *c) {
auto menu = agi::make_unique<CommandMenu>(c);
build_menu(name, c, &menu->cm, menu.get());
menu->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
menu->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);

View File

@ -54,6 +54,7 @@ aegisub_src = files(
'command/vis_tool.cpp',
'compat.cpp',
'context.cpp',
'crash_writer.cpp',
'dialog_about.cpp',
'dialog_align.cpp',
'dialog_attachments.cpp',
@ -172,12 +173,9 @@ if host_machine.system() == 'darwin'
'font_file_lister_coretext.mm',
'osx/osx_utils.mm',
'osx/retina_helper.mm',
'osx/scintilla_ime.mm',
)
elif host_machine.system() == 'windows'
aegisub_src += files(
'avisynth_wrap.cpp',
'crash_writer_minidump.cpp',
'font_file_lister_gdi.cpp',
# 'libass_gdi_fontselect.cpp',
)
@ -222,12 +220,6 @@ elif host_machine.system() == 'windows'
aegisub_src += windows.compile_resources('res/strings.rc')
endif
if host_machine.system() != 'windows'
aegisub_src += files(
'crash_writer.cpp'
)
endif
if conf.has('WITH_FONTCONFIG')
aegisub_src += files('font_file_lister_fontconfig.cpp')
endif

View File

@ -114,49 +114,19 @@ struct MkvStdIO final : InputStream {
}
};
static bool read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser, CompressedStream *cs) {
static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser) {
std::vector<std::pair<int, std::string>> subList;
// Load blocks
uint64_t startTime, endTime, filePos;
unsigned int rt, frameSize, frameFlags;
std::vector<char> uncompBuf(cs ? 256 : 0);
while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) {
if (ps->IsCancelled()) return true;
if (ps->IsCancelled()) return;
if (frameSize == 0) continue;
const char *readBuf;
const char *readBufEnd;
if (cs) {
cs_NextFrame(cs, filePos, frameSize);
int bytesRead = 0;
int res;
do {
res = cs_ReadData(cs, &uncompBuf[bytesRead], uncompBuf.size() - bytesRead);
if (res == -1) {
const char *err = cs_GetLastError(cs);
if (!err) err = "Unknown error";
ps->Log("Failed to decompress subtitles: " + std::string(err));
ps->SetStayOpen(true);
return false;
}
bytesRead += res;
if (bytesRead >= uncompBuf.size())
uncompBuf.resize(2 * uncompBuf.size());
} while (res != 0);
readBuf = &uncompBuf[0];
readBufEnd = readBuf + bytesRead;
} else {
readBuf = input->file.read(filePos, frameSize);
readBufEnd = readBuf + frameSize;
}
const auto readBuf = input->file.read(filePos, frameSize);
const auto readBufEnd = readBuf + frameSize;
// Get start and end times
int64_t timecodeScaleLow = 1000000;
@ -200,7 +170,6 @@ static bool read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *
sort(begin(subList), end(subList));
for (auto order_value_pair : subList)
parser->AddLine(order_value_pair.second);
return true;
}
void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *target) {
@ -217,7 +186,7 @@ void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *targe
// Find tracks
for (auto track : boost::irange(0u, tracks)) {
auto trackInfo = mkv_GetTrackInfo(file, track);
if (trackInfo->Type != 0x11) continue;
if (trackInfo->Type != 0x11 || trackInfo->CompEnabled) continue;
// Known subtitle format
std::string CodecID(trackInfo->CodecID);
@ -273,13 +242,6 @@ void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *targe
parser.AddLine("[Events]");
agi::scoped_holder<CompressedStream *, decltype(&cs_Destroy)> cs(nullptr, cs_Destroy);
if (trackInfo->CompEnabled) {
cs = cs_Create(file, trackToRead, err, sizeof(err));
if (!cs)
throw MatroskaException(err);
}
// Read timecode scale
auto segInfo = mkv_GetFileInfo(file);
int64_t timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale;
@ -287,11 +249,7 @@ void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *targe
// Progress bar
auto totalTime = double(segInfo->Duration) / timecodeScale;
DialogProgress progress(nullptr, _("Parsing Matroska"), _("Reading subtitles from Matroska file."));
bool result;
progress.Run([&](agi::ProgressSink *ps) { result = read_subtitles(ps, file, &input, srt, totalTime, &parser, cs); });
if (!result)
throw MatroskaException("Failed to read subtitles");
progress.Run([&](agi::ProgressSink *ps) { read_subtitles(ps, file, &input, srt, totalTime, &parser); });
}
bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) {
@ -306,7 +264,7 @@ bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) {
for (auto track : boost::irange(0u, tracks)) {
auto trackInfo = mkv_GetTrackInfo(file, track);
if (trackInfo->Type == 0x11) {
if (trackInfo->Type == 0x11 && !trackInfo->CompEnabled) {
std::string CodecID(trackInfo->CodecID);
if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8")
return true;

View File

@ -1,212 +0,0 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#import <objc/runtime.h>
#import <wx/osx/private.h>
#import <wx/stc/stc.h>
// from src/osx/cocoa/window.mm
@interface wxNSView : NSView <NSTextInputClient> {
BOOL _hasToolTip;
NSTrackingRectTag _lastToolTipTrackTag;
id _lastToolTipOwner;
void *_lastUserData;
}
@end
@interface IMEState : NSObject
@property (nonatomic) NSRange markedRange;
@property (nonatomic) bool undoActive;
@end
@implementation IMEState
- (id)init {
self = [super init];
self.markedRange = NSMakeRange(NSNotFound, 0);
self.undoActive = false;
return self;
}
@end
@interface ScintillaNSView : wxNSView <NSTextInputClient>
@property (nonatomic, readonly) wxStyledTextCtrl *stc;
@property (nonatomic, readonly) IMEState *state;
@end
@implementation ScintillaNSView
- (Class)superclass {
return [wxNSView superclass];
}
- (wxStyledTextCtrl *)stc {
return static_cast<wxStyledTextCtrl *>(wxWidgetImpl::FindFromWXWidget(self)->GetWXPeer());
}
- (IMEState *)state {
return objc_getAssociatedObject(self, [IMEState class]);
}
- (void)invalidate {
self.state.markedRange = NSMakeRange(NSNotFound, 0);
[self.inputContext discardMarkedText];
}
#pragma mark - NSTextInputClient
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange
actualRange:(NSRangePointer)actualRange
{
return nil;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)point {
return self.stc->PositionFromPoint(wxPoint(point.x, point.y));
}
- (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex {
return NO;
}
- (NSRect)firstRectForCharacterRange:(NSRange)range
actualRange:(NSRangePointer)actualRange
{
auto stc = self.stc;
int line = stc->LineFromPosition(range.location);
int height = stc->TextHeight(line);
auto pt = stc->PointFromPosition(range.location);
int width = 0;
if (range.length > 0) {
// If the end of the range is on the next line, the range should be
// truncated to the current line and actualRange should be set to the
// truncated range
int end_line = stc->LineFromPosition(range.location + range.length);
if (end_line > line) {
range.length = stc->PositionFromLine(line + 1) - 1 - range.location;
*actualRange = range;
}
auto end_pt = stc->PointFromPosition(range.location + range.length);
width = end_pt.x - pt.x;
}
auto rect = NSMakeRect(pt.x, pt.y, width, height);
rect = [self convertRect:rect toView:nil];
return [self.window convertRectToScreen:rect];
}
- (BOOL)hasMarkedText {
return self.state.markedRange.length > 0;
}
- (void)insertText:(id)str replacementRange:(NSRange)replacementRange {
[self unmarkText];
[super insertText:str replacementRange:replacementRange];
}
- (NSRange)markedRange {
return self.state.markedRange;
}
- (NSRange)selectedRange {
long from = 0, to = 0;
self.stc->GetSelection(&from, &to);
return NSMakeRange(from, to - from);
}
- (void)setMarkedText:(id)str
selectedRange:(NSRange)range
replacementRange:(NSRange)replacementRange
{
if ([str isKindOfClass:[NSAttributedString class]])
str = [str string];
auto stc = self.stc;
auto state = self.state;
int pos = stc->GetInsertionPoint();
if (state.markedRange.length > 0) {
pos = state.markedRange.location;
stc->DeleteRange(pos, state.markedRange.length);
stc->SetSelection(pos, pos);
} else {
state.undoActive = stc->GetUndoCollection();
if (state.undoActive)
stc->SetUndoCollection(false);
}
auto utf8 = [str UTF8String];
auto utf8len = strlen(utf8);
stc->AddTextRaw(utf8, utf8len);
state.markedRange = NSMakeRange(pos, utf8len);
stc->SetIndicatorCurrent(1);
stc->IndicatorFillRange(pos, utf8len);
// Re-enable undo if we got a zero-length string as that means we're done
if (!utf8len && state.undoActive)
stc->SetUndoCollection(true);
else {
int start = pos;
// Range is in utf-16 code units
if (range.location > 0)
start += [[str substringToIndex:range.location] lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
int length = [[str substringWithRange:range] lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
stc->SetSelection(start, start + length);
}
}
- (void)unmarkText {
auto state = self.state;
if (state.markedRange.length > 0) {
self.stc->DeleteRange(state.markedRange.location, state.markedRange.length);
state.markedRange = NSMakeRange(NSNotFound, 0);
if (state.undoActive)
self.stc->SetUndoCollection(true);
}
}
- (NSArray *)validAttributesForMarkedText {
return @[];
}
@end
namespace osx { namespace ime {
void inject(wxStyledTextCtrl *ctrl) {
id view = (id)ctrl->GetHandle();
object_setClass(view, [ScintillaNSView class]);
auto state = [IMEState new];
objc_setAssociatedObject(view, [IMEState class], state,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[state release];
}
void invalidate(wxStyledTextCtrl *ctrl) {
[(ScintillaNSView *)ctrl->GetHandle() invalidate];
}
bool process_key_event(wxStyledTextCtrl *ctrl, wxKeyEvent &evt) {
if (evt.GetModifiers() != 0) return false;
if (evt.GetKeyCode() != WXK_RETURN && evt.GetKeyCode() != WXK_TAB) return false;
if (![(ScintillaNSView *)ctrl->GetHandle() hasMarkedText]) return false;
evt.Skip();
return true;
}
} }

View File

@ -92,7 +92,7 @@ void General_DefaultStyles(wxTreebook *book, Preferences *parent) {
instructions->Wrap(400);
staticbox->Add(instructions, 0, wxALL, 5);
staticbox->AddSpacer(16);
auto general = new wxFlexGridSizer(2, 5, 5);
general->AddGrowableCol(0, 1);
staticbox->Add(general, 1, wxEXPAND, 5);
@ -232,17 +232,9 @@ void Interface(wxTreebook *book, Preferences *parent) {
auto tl_assistant = p->PageSizer(_("Translation Assistant"));
p->OptionAdd(tl_assistant, _("Skip over whitespace"), "Tool/Translation Assistant/Skip Whitespace");
auto visual_tools = p->PageSizer(_("Visual Tools"));
p->OptionAdd(visual_tools, _("Shape handle size"), "Tool/Visual/Shape Handle Size");
auto color_picker = p->PageSizer(_("Colour Picker"));
p->OptionAdd(color_picker, _("Restrict Screen Picker to Window"), "Tool/Colour Picker/Restrict to Window");
#if defined(__WXMSW__) && wxVERSION_NUMBER >= 3300
auto dark_mode = p->PageSizer(_("Dark Mode"));
p->OptionAdd(dark_mode, _("Enable experimental dark mode (restart required)"), "App/Dark Mode");
#endif
p->SetSizerAndFit(p->sizer);
}
@ -467,7 +459,7 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) {
wxArrayString sp_choice = to_wx(SubtitlesProviderFactory::GetClasses());
p->OptionChoice(expert, _("Subtitles provider"), sp_choice, "Subtitle/Provider");
#ifdef WITH_AVISYNTH
auto avisynth = p->PageSizer("Avisynth");
@ -502,42 +494,26 @@ void VapourSynth(wxTreebook *book, Preferences *parent) {
const wxString log_levels[] = { "Quiet", "Fatal", "Critical", "Warning", "Information", "Debug" };
wxArrayString log_levels_choice(6, log_levels);
p->OptionChoice(general, _("Log level"), log_levels_choice, "Provider/Video/VapourSynth/Log Level");
p->CellSkip(general);
p->OptionAdd(general, _("Load user plugins"), "Provider/VapourSynth/Autoload User Plugins");
p->OptionChoice(general, _("Log Level"), log_levels_choice, "Provider/Video/VapourSynth/Log Level");
auto video = p->PageSizer(_("Default Video Script"));
auto make_default_button = [=](std::string optname, wxTextCtrl *ctrl) {
auto showdefault = new wxButton(p, -1, _("Set to Default"));
showdefault->Bind(wxEVT_BUTTON, [=](auto e) {
ctrl->SetValue(OPT_GET(optname)->GetDefaultString());
});
return showdefault;
};
auto vhint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load video files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
p->sizer->Fit(p);
vhint->Wrap(400);
video->Add(vhint, 0, wxALL, 5);
p->CellSkip(video);
video->AddSpacer(16);
auto vdef = p->OptionAddMultiline(video, "Provider/Video/VapourSynth/Default Script");
p->CellSkip(video);
video->Add(make_default_button("Provider/Video/VapourSynth/Default Script", vdef), wxSizerFlags().Right());
p->OptionAddMultiline(video, "Provider/Video/VapourSynth/Default Script");
auto audio = p->PageSizer(_("Default Audio Script"));
auto ahint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load audio files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
p->sizer->Fit(p);
ahint->Wrap(400);
audio->Add(ahint, 0, wxALL, 5);
p->CellSkip(audio);
audio->AddSpacer(16);
auto adef = p->OptionAddMultiline(audio, "Provider/Audio/VapourSynth/Default Script");
p->CellSkip(audio);
audio->Add(make_default_button("Provider/Audio/VapourSynth/Default Script", adef), wxSizerFlags().Right());
p->OptionAddMultiline(audio, "Provider/Audio/VapourSynth/Default Script");
p->SetSizerAndFit(p->sizer);
#endif

View File

@ -156,7 +156,7 @@ wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, co
}
}
wxTextCtrl *OptionPage::OptionAddMultiline(wxSizer *sizer, const char *opt_name) {
wxControl *OptionPage::OptionAddMultiline(wxSizer *sizer, const char *opt_name) {
parent->AddChangeableOption(opt_name);
const auto opt = OPT_GET(opt_name);

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