From 08274f0a0ceebb84df82ef42b2d5833e03e6adcc Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Wed, 23 Jan 2008 02:22:15 +0000 Subject: [PATCH] Committed a (broken) copy of ASSDraw3 source code by ai-chan Originally committed to SVN as r1815. --- assdraw/assdraw.vcproj | 317 ++++ assdraw/src/ASSDraw3.dev | 323 ++++ assdraw/src/ASSDraw3.layout | 185 ++ assdraw/src/Makefile.win | 69 + assdraw/src/_common.hpp | 14 + assdraw/src/agg_bcspline.cpp | 289 +++ assdraw/src/agg_bcspline.h | 81 + assdraw/src/agg_conv_bcspline.h | 58 + assdraw/src/agg_vcgen_bcspline.cpp | 203 +++ assdraw/src/agg_vcgen_bcspline.h | 83 + assdraw/src/agghelper.hpp | 20 + assdraw/src/assdraw.cpp | 768 ++++++++ assdraw/src/assdraw.hpp | 208 +++ assdraw/src/assdraw.rc | 101 ++ assdraw/src/assdraw_settings.cpp | 190 ++ assdraw/src/bitmaps/Copy of assdraw3.bmp | Bin 0 -> 86454 bytes assdraw/src/bitmaps/add_.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/arr_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/arr_1.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/assdraw3.bmp | Bin 0 -> 120054 bytes assdraw/src/bitmaps/b_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/b_.cur | Bin 0 -> 4286 bytes assdraw/src/bitmaps/c_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/check_.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/del_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/del_cross.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/help.bmp | Bin 0 -> 1318 bytes assdraw/src/bitmaps/help_.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/l_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/m_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/n_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/new_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/nut_.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/p_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/pan_bg.bmp | Bin 0 -> 1134 bytes assdraw/src/bitmaps/pan_both.bmp | Bin 0 -> 1134 bytes assdraw/src/bitmaps/pan_shape.bmp | Bin 0 -> 1434 bytes assdraw/src/bitmaps/pan_shp.bmp | Bin 0 -> 1134 bytes assdraw/src/bitmaps/preview_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/rot_.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/s_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/sc_rot_.bmp | Bin 0 -> 774 bytes assdraw/src/bitmaps/src_.bmp | Bin 0 -> 776 bytes assdraw/src/bitmaps/transform.bmp | Bin 0 -> 25144 bytes assdraw/src/bitmaps/uncheck_.bmp | Bin 0 -> 774 bytes assdraw/src/canvas.cpp | 2057 ++++++++++++++++++++++ assdraw/src/canvas.hpp | 303 ++++ assdraw/src/canvas_mouse.cpp | 56 + assdraw/src/canvas_mouse.hpp | 85 + assdraw/src/cmd.cpp | 212 +++ assdraw/src/cmd.hpp | 112 ++ assdraw/src/dlgctrl.cpp | 372 ++++ assdraw/src/dlgctrl.hpp | 115 ++ assdraw/src/engine.cpp | 749 ++++++++ assdraw/src/engine.hpp | 406 +++++ assdraw/src/enums.hpp | 113 ++ assdraw/src/include_once.hpp | 94 + assdraw/src/library.cpp | 259 +++ assdraw/src/library.hpp | 88 + assdraw/src/resource.h | 15 + assdraw/src/settings.cpp | 223 +++ assdraw/src/settings.hpp | 119 ++ assdraw/src/tsukasa.ico | Bin 0 -> 17574 bytes assdraw/src/wxAGG/AGGWindow.cpp | 124 ++ assdraw/src/wxAGG/AGGWindow.h | 68 + assdraw/src/wxAGG/PixelFormatConvertor.h | 117 ++ 66 files changed, 8596 insertions(+) create mode 100644 assdraw/assdraw.vcproj create mode 100644 assdraw/src/ASSDraw3.dev create mode 100644 assdraw/src/ASSDraw3.layout create mode 100644 assdraw/src/Makefile.win create mode 100644 assdraw/src/_common.hpp create mode 100644 assdraw/src/agg_bcspline.cpp create mode 100644 assdraw/src/agg_bcspline.h create mode 100644 assdraw/src/agg_conv_bcspline.h create mode 100644 assdraw/src/agg_vcgen_bcspline.cpp create mode 100644 assdraw/src/agg_vcgen_bcspline.h create mode 100644 assdraw/src/agghelper.hpp create mode 100644 assdraw/src/assdraw.cpp create mode 100644 assdraw/src/assdraw.hpp create mode 100644 assdraw/src/assdraw.rc create mode 100644 assdraw/src/assdraw_settings.cpp create mode 100644 assdraw/src/bitmaps/Copy of assdraw3.bmp create mode 100644 assdraw/src/bitmaps/add_.bmp create mode 100644 assdraw/src/bitmaps/arr_.bmp create mode 100644 assdraw/src/bitmaps/arr_1.bmp create mode 100644 assdraw/src/bitmaps/assdraw3.bmp create mode 100644 assdraw/src/bitmaps/b_.bmp create mode 100644 assdraw/src/bitmaps/b_.cur create mode 100644 assdraw/src/bitmaps/c_.bmp create mode 100644 assdraw/src/bitmaps/check_.bmp create mode 100644 assdraw/src/bitmaps/del_.bmp create mode 100644 assdraw/src/bitmaps/del_cross.bmp create mode 100644 assdraw/src/bitmaps/help.bmp create mode 100644 assdraw/src/bitmaps/help_.bmp create mode 100644 assdraw/src/bitmaps/l_.bmp create mode 100644 assdraw/src/bitmaps/m_.bmp create mode 100644 assdraw/src/bitmaps/n_.bmp create mode 100644 assdraw/src/bitmaps/new_.bmp create mode 100644 assdraw/src/bitmaps/nut_.bmp create mode 100644 assdraw/src/bitmaps/p_.bmp create mode 100644 assdraw/src/bitmaps/pan_bg.bmp create mode 100644 assdraw/src/bitmaps/pan_both.bmp create mode 100644 assdraw/src/bitmaps/pan_shape.bmp create mode 100644 assdraw/src/bitmaps/pan_shp.bmp create mode 100644 assdraw/src/bitmaps/preview_.bmp create mode 100644 assdraw/src/bitmaps/rot_.bmp create mode 100644 assdraw/src/bitmaps/s_.bmp create mode 100644 assdraw/src/bitmaps/sc_rot_.bmp create mode 100644 assdraw/src/bitmaps/src_.bmp create mode 100644 assdraw/src/bitmaps/transform.bmp create mode 100644 assdraw/src/bitmaps/uncheck_.bmp create mode 100644 assdraw/src/canvas.cpp create mode 100644 assdraw/src/canvas.hpp create mode 100644 assdraw/src/canvas_mouse.cpp create mode 100644 assdraw/src/canvas_mouse.hpp create mode 100644 assdraw/src/cmd.cpp create mode 100644 assdraw/src/cmd.hpp create mode 100644 assdraw/src/dlgctrl.cpp create mode 100644 assdraw/src/dlgctrl.hpp create mode 100644 assdraw/src/engine.cpp create mode 100644 assdraw/src/engine.hpp create mode 100644 assdraw/src/enums.hpp create mode 100644 assdraw/src/include_once.hpp create mode 100644 assdraw/src/library.cpp create mode 100644 assdraw/src/library.hpp create mode 100644 assdraw/src/resource.h create mode 100644 assdraw/src/settings.cpp create mode 100644 assdraw/src/settings.hpp create mode 100644 assdraw/src/tsukasa.ico create mode 100644 assdraw/src/wxAGG/AGGWindow.cpp create mode 100644 assdraw/src/wxAGG/AGGWindow.h create mode 100644 assdraw/src/wxAGG/PixelFormatConvertor.h diff --git a/assdraw/assdraw.vcproj b/assdraw/assdraw.vcproj new file mode 100644 index 000000000..f61c1636a --- /dev/null +++ b/assdraw/assdraw.vcproj @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assdraw/src/ASSDraw3.dev b/assdraw/src/ASSDraw3.dev new file mode 100644 index 000000000..5afb0aa92 --- /dev/null +++ b/assdraw/src/ASSDraw3.dev @@ -0,0 +1,323 @@ +[Project] +FileName=ASSDraw3.dev +Name=ASSDraw +UnitCount=26 +Type=0 +Ver=3 +IsCpp=1 +Folders=AGG +CommandLine= +CompilerSettings=0000000001001000000100 +PchHead=-1 +PchSource=-1 +ProfilesCount=1 +ProfileIndex=0 + +[Unit1] +FileName=assdraw.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=3 +Minor=0 +Release=0 +Build=0 +LanguageID=1033 +CharsetID=1252 +CompanyName=ai-chan +FileVersion=3.0.0.0 +FileDescription=A tool for designing shapes to be used in ASS subtitle +InternalName=ASSDraw3 +LegalCopyright= +LegalTrademarks= +OriginalFilename=ASSDraw3.exe +ProductName=ASSDraw +ProductVersion=3 +AutoIncBuildNrOnRebuild=0 +AutoIncBuildNrOnCompile=0 +UnitCount=20 + +[Unit3] +FileName=cmd.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=engine.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=assdraw.rc +CompileCpp=1 +Folder=Resources +Compile=1 +Link=0 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit7] +FileName=engine.cpp +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= +CompileCpp=1 + +[Unit9] +FileName=include_once.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=assdraw.hpp +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= +CompileCpp=1 + +[Unit2] +FileName=cmd.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Profile1] +ProfileName=Default Profile +Type=0 +ObjFiles= +Includes=E:\DEV;E:\DEV\agg-2.5\include;E:\DEV\wxTreeMultiCtrl\include +Libs=E:\DEV\agg-2.5;E:\DEV\wxTreeMultiCtrl +PrivateResource=E:\DEV\_projects\ASSDraw 3\Default Profile\ASSDraw_private.rc +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler=-D__WXMSW__ -DNO_GCC_PRAGMA -fno-rtti -fno-exceptions_@@_ -Wno-deprecated_@@_ +Linker=-lwxmsw28_propgrid_@@_-mwindows_@@_-lwxmsw28_@@_-lwxmsw28_gl_@@_-lwxtiff_@@_-lwxjpeg_@@_-lwxpng_@@_-lwxzlib_@@_-lwxregex_@@_-lwxexpat_@@_-lkernel32_@@_-luser32_@@_-lgdi32_@@_-lcomdlg32_@@_-lwinspool_@@_-lwinmm_@@_-lshell32_@@_-lcomctl32_@@_-lole32_@@_-loleaut32_@@_-luuid_@@_-lrpcrt4_@@_-ladvapi32_@@_-lwsock32_@@_-lodbc32_@@_-lopengl32_@@_-lagg_@@_ +PreprocDefines= +CompilerSettings=0000000001001000000100 +Icon= +ExeOutput=E:\DEV\_projects\ASSDraw 3\Default Profile\ +ObjectOutput=E:\DEV\_projects\ASSDraw 3\Default Profile\ +OverrideOutput=1 +OverrideOutputName=ASSDraw3.exe +HostApplication= +CommandLine= +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=1 +SupportXPThemes=1 +CompilerSet=0 +compilerType=0 + +[Unit8] +FileName=dlgctrl.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit10] +FileName=resource.h +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit15] +FileName=agg_bcspline.cpp +CompileCpp=1 +Folder=AGG +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit16] +FileName=enums.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit18] +FileName=library.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit17] +FileName=library.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit13] +FileName=wxAGG\AGGWindow.cpp +CompileCpp=1 +Folder=AGG +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit14] +FileName=agg_vcgen_bcspline.cpp +CompileCpp=1 +Folder=AGG +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=canvas.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit12] +FileName=canvas.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit20] +FileName=dlgctrl.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit19] +FileName=_common.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit21] +FileName=settings.cpp +CompileCpp=1 +Folder=ASSDraw +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit22] +FileName=settings.hpp +CompileCpp=1 +Folder=ASSDraw +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit23] +FileName=assdraw_settings.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit24] +FileName=agghelper.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit25] +FileName=canvas_mouse.hpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit26] +FileName=canvas_mouse.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/assdraw/src/ASSDraw3.layout b/assdraw/src/ASSDraw3.layout new file mode 100644 index 000000000..8ed9b9f28 --- /dev/null +++ b/assdraw/src/ASSDraw3.layout @@ -0,0 +1,185 @@ +[Editor_10] +CursorCol=32 +CursorRow=553 +TopLine=535 +LeftChar=1 +Open=1 +Top=0 +[Editor_7] +CursorCol=6 +CursorRow=227 +TopLine=197 +LeftChar=1 +Open=1 +Top=1 +[Editor_5] +CursorCol=11 +CursorRow=19 +TopLine=1 +LeftChar=1 +Open=1 +Top=0 +[Editor_18] +CursorCol=11 +CursorRow=14 +TopLine=1 +LeftChar=1 +Open=0 +Top=0 +[Editor_19] +CursorCol=12 +CursorRow=3 +TopLine=3 +LeftChar=1 +Open=0 +Top=0 +[Editor_11] +CursorCol=8 +CursorRow=44 +TopLine=31 +LeftChar=1 +Open=0 +Top=0 +[Editor_3] +CursorCol=1 +CursorRow=268 +TopLine=243 +LeftChar=1 +Open=1 +Top=0 +[Editor_15] +CursorCol=15 +CursorRow=7 +TopLine=1 +LeftChar=1 +Open=1 +Top=0 +[Editor_8] +CursorCol=1 +CursorRow=69 +TopLine=33 +LeftChar=1 +Open=0 +Top=0 +[Editor_16] +CursorCol=2 +CursorRow=25 +TopLine=1 +LeftChar=1 +Open=0 +Top=0 +[Editor_0] +CursorCol=39 +CursorRow=212 +TopLine=184 +LeftChar=1 +Open=1 +Top=0 +[Editors] +Focused=7 +Order=5,0,10,17,3,6,7,22,21,20,15 +[Editor_1] +Open=0 +Top=0 +CursorCol=21 +CursorRow=63 +TopLine=30 +LeftChar=1 +[Editor_2] +Open=0 +Top=0 +CursorCol=7 +CursorRow=83 +TopLine=50 +LeftChar=1 +[Editor_4] +Open=0 +Top=0 +CursorCol=19 +CursorRow=21 +TopLine=21 +LeftChar=1 +[Editor_6] +Open=1 +Top=0 +CursorCol=1 +CursorRow=577 +TopLine=556 +LeftChar=1 +[Editor_9] +Open=0 +Top=0 +CursorCol=1 +CursorRow=1 +TopLine=1 +LeftChar=1 +[Editor_12] +Open=0 +Top=0 +CursorCol=6 +CursorRow=99 +TopLine=91 +LeftChar=1 +[Editor_13] +Open=0 +Top=0 +CursorCol=1 +CursorRow=204 +TopLine=170 +LeftChar=1 +[Editor_14] +Open=0 +Top=0 +CursorCol=1 +CursorRow=290 +TopLine=256 +LeftChar=1 +[Editor_17] +Open=1 +Top=0 +CursorCol=68 +CursorRow=133 +TopLine=112 +LeftChar=1 +[Editor_20] +Open=1 +Top=0 +CursorCol=46 +CursorRow=195 +TopLine=163 +LeftChar=1 +[Editor_21] +Open=1 +Top=0 +CursorCol=2 +CursorRow=58 +TopLine=57 +LeftChar=1 +[Editor_22] +Open=1 +Top=0 +CursorCol=32 +CursorRow=160 +TopLine=129 +LeftChar=1 +[Editor_23] +Open=0 +Top=0 +CursorCol=2 +CursorRow=6 +TopLine=1 +LeftChar=1 +[Editor_24] +CursorCol=1 +CursorRow=56 +TopLine=25 +LeftChar=1 +Open=0 +Top=0 +[Editor_25] +Open=0 +Top=0 +CursorCol=18 +CursorRow=8 +TopLine=1 +LeftChar=1 diff --git a/assdraw/src/Makefile.win b/assdraw/src/Makefile.win new file mode 100644 index 000000000..aa45a440e --- /dev/null +++ b/assdraw/src/Makefile.win @@ -0,0 +1,69 @@ +# Project: ASSDraw +# Compiler: Default GCC compiler +# Compiler Type: MingW 3 +# Makefile created by wxDev-C++ 6.10.2 on 20/01/08 23:46 + +CPP = g++.exe +CC = gcc.exe +WINDRES = windres.exe +OBJ = ../Default\ Profile/assdraw.o ../Default\ Profile/cmd.o ../Default\ Profile/engine.o ../Default\ Profile/dlgctrl.o ../Default\ Profile/canvas.o ../Default\ Profile/AGGWindow.o ../Default\ Profile/agg_vcgen_bcspline.o ../Default\ Profile/agg_bcspline.o ../Default\ Profile/library.o ../Default\ Profile/settings.o ../Default\ Profile/assdraw_settings.o ../Default\ Profile/canvas_mouse.o ../Default\ Profile/ASSDraw3_private.res +LINKOBJ = "../Default Profile/assdraw.o" "../Default Profile/cmd.o" "../Default Profile/engine.o" "../Default Profile/dlgctrl.o" "../Default Profile/canvas.o" "../Default Profile/AGGWindow.o" "../Default Profile/agg_vcgen_bcspline.o" "../Default Profile/agg_bcspline.o" "../Default Profile/library.o" "../Default Profile/settings.o" "../Default Profile/assdraw_settings.o" "../Default Profile/canvas_mouse.o" "../Default Profile/ASSDraw3_private.res" +LIBS = -L"D:/Program Files/Dev-Cpp/Lib" -L"E:/DEV/agg-2.5" -L"E:/DEV/wxTreeMultiCtrl" -lwxmsw28_propgrid -mwindows -lwxmsw28 -lwxmsw28_gl -lwxtiff -lwxjpeg -lwxpng -lwxzlib -lwxregex -lwxexpat -lkernel32 -luser32 -lgdi32 -lcomdlg32 -lwinspool -lwinmm -lshell32 -lcomctl32 -lole32 -loleaut32 -luuid -lrpcrt4 -ladvapi32 -lwsock32 -lodbc32 -lopengl32 -lagg -s +INCS = -I"include" -I"E:/DEV" -I"E:/DEV/agg-2.5/include" -I"E:/DEV/wxTreeMultiCtrl/include" +CXXINCS = -I"D:/Program Files/Dev-Cpp/lib/gcc/mingw32/3.4.2/include" -I"D:/Program Files/Dev-Cpp/include/c++/3.4.2/backward" -I"D:/Program Files/Dev-Cpp/include/c++/3.4.2/mingw32" -I"D:/Program Files/Dev-Cpp/include/c++/3.4.2" -I"D:/Program Files/Dev-Cpp/include" -I"D:/Program Files/Dev-Cpp/" -I"D:/Program Files/Dev-Cpp/include/common/wx/msw" -I"D:/Program Files/Dev-Cpp/include/common/wx/generic" -I"D:/Program Files/Dev-Cpp/include/common/wx/fl" -I"D:/Program Files/Dev-Cpp/include/common/wx/gizmos" -I"D:/Program Files/Dev-Cpp/include/common/wx/html" -I"D:/Program Files/Dev-Cpp/include/common/wx/mmedia" -I"D:/Program Files/Dev-Cpp/include/common/wx/net" -I"D:/Program Files/Dev-Cpp/include/common/wx/ogl" -I"D:/Program Files/Dev-Cpp/include/common/wx/plot" -I"D:/Program Files/Dev-Cpp/include/common/wx/protocol" -I"D:/Program Files/Dev-Cpp/include/common/wx/stc" -I"D:/Program Files/Dev-Cpp/include/common/wx/svg" -I"D:/Program Files/Dev-Cpp/include/common/wx/xml" -I"D:/Program Files/Dev-Cpp/include/common/wx/xrc" -I"D:/Program Files/Dev-Cpp/include/common/wx" -I"D:/Program Files/Dev-Cpp/include/common" -I"E:/dev/ACE-5.5" -I"E:/DEV" -I"E:/DEV/agg-2.5/include" -I"E:/DEV/wxTreeMultiCtrl/include" +RCINCS = --include-dir "D:/PROGRA~1/Dev-Cpp/include/common" +BIN = ../Default\ Profile/ASSDraw3.exe +DEFINES = +CXXFLAGS = $(CXXINCS) $(DEFINES) -D__WXMSW__ -DNO_GCC_PRAGMA -fno-rtti -fno-exceptions -Wno-deprecated -fexpensive-optimizations -O3 +CFLAGS = $(INCS) $(DEFINES) -fexpensive-optimizations -O3 +GPROF = gprof.exe +RM = rm -f +LINK = g++.exe + +.PHONY: all all-before all-after clean clean-custom +all: all-before $(BIN) all-after + +clean: clean-custom + $(RM) $(OBJ) $(BIN) + +$(BIN): $(OBJ) + $(LINK) $(LINKOBJ) -o "..\Default Profile\ASSDraw3.exe" $(LIBS) + +../Default\ Profile/assdraw.o: $(GLOBALDEPS) assdraw.cpp assdraw.hpp _common.hpp enums.hpp include_once.hpp + $(CPP) -c assdraw.cpp -o "../Default Profile/assdraw.o" $(CXXFLAGS) + +../Default\ Profile/cmd.o: $(GLOBALDEPS) cmd.cpp + $(CPP) -c cmd.cpp -o "../Default Profile/cmd.o" $(CXXFLAGS) + +../Default\ Profile/engine.o: $(GLOBALDEPS) engine.cpp + $(CPP) -c engine.cpp -o "../Default Profile/engine.o" $(CXXFLAGS) + +../Default\ Profile/dlgctrl.o: $(GLOBALDEPS) dlgctrl.cpp assdraw.hpp _common.hpp + $(CPP) -c dlgctrl.cpp -o "../Default Profile/dlgctrl.o" $(CXXFLAGS) + +../Default\ Profile/canvas.o: $(GLOBALDEPS) canvas.cpp assdraw.hpp _common.hpp cmd.hpp agghelper.hpp + $(CPP) -c canvas.cpp -o "../Default Profile/canvas.o" $(CXXFLAGS) + +../Default\ Profile/AGGWindow.o: $(GLOBALDEPS) wxAGG/AGGWindow.cpp wxAGG/AGGWindow.h wxAGG/PixelFormatConvertor.h + $(CPP) -c wxAGG/AGGWindow.cpp -o "../Default Profile/AGGWindow.o" $(CXXFLAGS) + +../Default\ Profile/agg_vcgen_bcspline.o: $(GLOBALDEPS) agg_vcgen_bcspline.cpp agg_vcgen_bcspline.h agg_bcspline.h + $(CPP) -c agg_vcgen_bcspline.cpp -o "../Default Profile/agg_vcgen_bcspline.o" $(CXXFLAGS) + +../Default\ Profile/agg_bcspline.o: $(GLOBALDEPS) agg_bcspline.cpp agg_bcspline.h + $(CPP) -c agg_bcspline.cpp -o "../Default Profile/agg_bcspline.o" $(CXXFLAGS) + +../Default\ Profile/library.o: $(GLOBALDEPS) library.cpp assdraw.hpp _common.hpp library.hpp engine.hpp _common.hpp wxAGG/AGGWindow.h wxAGG/PixelFormatConvertor.h agg_conv_bcspline.h agg_vcgen_bcspline.h agg_bcspline.h + $(CPP) -c library.cpp -o "../Default Profile/library.o" $(CXXFLAGS) + +../Default\ Profile/settings.o: $(GLOBALDEPS) settings.cpp assdraw.hpp _common.hpp settings.hpp _common.hpp + $(CPP) -c settings.cpp -o "../Default Profile/settings.o" $(CXXFLAGS) + +../Default\ Profile/assdraw_settings.o: $(GLOBALDEPS) assdraw_settings.cpp assdraw.hpp _common.hpp + $(CPP) -c assdraw_settings.cpp -o "../Default Profile/assdraw_settings.o" $(CXXFLAGS) + +../Default\ Profile/canvas_mouse.o: $(GLOBALDEPS) canvas_mouse.cpp canvas_mouse.hpp _common.hpp enums.hpp engine.hpp _common.hpp wxAGG/AGGWindow.h wxAGG/PixelFormatConvertor.h agg_conv_bcspline.h agg_vcgen_bcspline.h agg_bcspline.h canvas.hpp engine.hpp enums.hpp + $(CPP) -c canvas_mouse.cpp -o "../Default Profile/canvas_mouse.o" $(CXXFLAGS) + +../Default\ Profile/ASSDraw3_private.res: ../Default\ Profile/ASSDraw3_private.rc assdraw.rc + $(WINDRES) --input-format=rc -o "../Default Profile/ASSDraw3_private.res" $(RCINCS) ../DEFAUL~1/ASSDRA~1.RC -O coff diff --git a/assdraw/src/_common.hpp b/assdraw/src/_common.hpp new file mode 100644 index 000000000..93c17f235 --- /dev/null +++ b/assdraw/src/_common.hpp @@ -0,0 +1,14 @@ + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#define TITLE wxT("ASSDraw3") + +#define __DPDS__ wxDefaultPosition, wxDefaultSize diff --git a/assdraw/src/agg_bcspline.cpp b/assdraw/src/agg_bcspline.cpp new file mode 100644 index 000000000..b06539dc2 --- /dev/null +++ b/assdraw/src/agg_bcspline.cpp @@ -0,0 +1,289 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_bcspline.h" + +namespace agg +{ + //------------------------------------------------------------------------ + bcspline::bcspline() : + m_max(0), + m_num(0), + m_x(0), + m_y(0), + m_last_idx(-1) + { + } + + //------------------------------------------------------------------------ + bcspline::bcspline(int num) : + m_max(0), + m_num(0), + m_x(0), + m_y(0), + m_last_idx(-1) + { + init(num); + } + + //------------------------------------------------------------------------ + bcspline::bcspline(int num, const double* x, const double* y) : + m_max(0), + m_num(0), + m_x(0), + m_y(0), + m_last_idx(-1) + { + init(num, x, y); + } + + + //------------------------------------------------------------------------ + void bcspline::init(int max) + { + if(max > 2 && max > m_max) + { + m_am.resize(max * 3); + m_max = max; + m_x = &m_am[m_max]; + m_y = &m_am[m_max * 2]; + } + m_num = 0; + m_last_idx = -1; + } + + + //------------------------------------------------------------------------ + void bcspline::add_point(double x, double y) + { + if(m_num < m_max) + { + m_x[m_num] = x; + m_y[m_num] = y; + ++m_num; + } + } + + + //------------------------------------------------------------------------ + void bcspline::prepare() + { + if(m_num > 2) + { + int i, k, n1; + double* temp; + double* r; + double* s; + double h, p, d, f, e; + + for(k = 0; k < m_num; k++) + { + m_am[k] = 0.0; + } + + n1 = 3 * m_num; + + pod_array al(n1); + temp = &al[0]; + + for(k = 0; k < n1; k++) + { + temp[k] = 0.0; + } + + r = temp + m_num; + s = temp + m_num * 2; + + n1 = m_num - 1; + d = m_x[1] - m_x[0]; + e = (m_y[1] - m_y[0]) / d; + + for(k = 1; k < n1; k++) + { + h = d; + d = m_x[k + 1] - m_x[k]; + f = e; + e = (m_y[k + 1] - m_y[k]) / d; + al[k] = d / (d + h); + r[k] = 1.0 - al[k]; + s[k] = 6.0 * (e - f) / (h + d); + } + + for(k = 1; k < n1; k++) + { + p = 1.0 / (r[k] * al[k - 1] + 2.0); + al[k] *= -p; + s[k] = (s[k] - r[k] * s[k - 1]) * p; + } + + m_am[n1] = 0.0; + al[n1 - 1] = s[n1 - 1]; + m_am[n1 - 1] = al[n1 - 1]; + + for(k = n1 - 2, i = 0; i < m_num - 2; i++, k--) + { + al[k] = al[k] * al[k + 1] + s[k]; + m_am[k] = al[k]; + } + } + m_last_idx = -1; + } + + + + //------------------------------------------------------------------------ + void bcspline::init(int num, const double* x, const double* y) + { + if(num > 2) + { + init(num); + int i; + for(i = 0; i < num; i++) + { + add_point(*x++, *y++); + } + prepare(); + } + m_last_idx = -1; + } + + + //------------------------------------------------------------------------ + void bcspline::bsearch(int n, const double *x, double x0, int *i) + { + int j = n - 1; + int k; + + for(*i = 0; (j - *i) > 1; ) + { + if(x0 < x[k = (*i + j) >> 1]) j = k; + else *i = k; + } + } + + + + //------------------------------------------------------------------------ + double bcspline::interpolation(double x, int i) const + { + int j = i + 1; + double d = m_x[i] - m_x[j]; + double h = x - m_x[j]; + double r = m_x[i] - x; + double p = d * d / 6.0; + return (m_am[j] * r * r * r + m_am[i] * h * h * h) / 6.0 / d + + ((m_y[j] - m_am[j] * p) * r + (m_y[i] - m_am[i] * p) * h) / d; + } + + + //------------------------------------------------------------------------ + double bcspline::extrapolation_left(double x) const + { + double d = m_x[1] - m_x[0]; + return (-d * m_am[1] / 6 + (m_y[1] - m_y[0]) / d) * + (x - m_x[0]) + + m_y[0]; + } + + //------------------------------------------------------------------------ + double bcspline::extrapolation_right(double x) const + { + double d = m_x[m_num - 1] - m_x[m_num - 2]; + return (d * m_am[m_num - 2] / 6 + (m_y[m_num - 1] - m_y[m_num - 2]) / d) * + (x - m_x[m_num - 1]) + + m_y[m_num - 1]; + } + + //------------------------------------------------------------------------ + double bcspline::get(double x) const + { + if(m_num > 2) + { + int i; + + // Extrapolation on the left + if(x < m_x[0]) return extrapolation_left(x); + + // Extrapolation on the right + if(x >= m_x[m_num - 1]) return extrapolation_right(x); + + // Interpolation + bsearch(m_num, m_x, x, &i); + return interpolation(x, i); + } + return 0.0; + } + + + //------------------------------------------------------------------------ + double bcspline::get_stateful(double x) const + { + if(m_num > 2) + { + // Extrapolation on the left + if(x < m_x[0]) return extrapolation_left(x); + + // Extrapolation on the right + if(x >= m_x[m_num - 1]) return extrapolation_right(x); + + if(m_last_idx >= 0) + { + // Check if x is not in current range + if(x < m_x[m_last_idx] || x > m_x[m_last_idx + 1]) + { + // Check if x between next points (most probably) + if(m_last_idx < m_num - 2 && + x >= m_x[m_last_idx + 1] && + x <= m_x[m_last_idx + 2]) + { + ++m_last_idx; + } + else + if(m_last_idx > 0 && + x >= m_x[m_last_idx - 1] && + x <= m_x[m_last_idx]) + { + // x is between pevious points + --m_last_idx; + } + else + { + // Else perform full search + bsearch(m_num, m_x, x, &m_last_idx); + } + } + return interpolation(x, m_last_idx); + } + else + { + // Interpolation + bsearch(m_num, m_x, x, &m_last_idx); + return interpolation(x, m_last_idx); + } + } + return 0.0; + } + +} + diff --git a/assdraw/src/agg_bcspline.h b/assdraw/src/agg_bcspline.h new file mode 100644 index 000000000..05d236497 --- /dev/null +++ b/assdraw/src/agg_bcspline.h @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_bcspline_INCLUDED +#define AGG_bcspline_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + //----------------------------------------------------------------bcspline + // A very simple class of Bi-cubic Spline interpolation. + // First call init(num, x[], y[]) where num - number of source points, + // x, y - arrays of X and Y values respectively. Here Y must be a function + // of X. It means that all the X-coordinates must be arranged in the ascending + // order. + // Then call get(x) that calculates a value Y for the respective X. + // The class supports extrapolation, i.e. you can call get(x) where x is + // outside the given with init() X-range. Extrapolation is a simple linear + // function. + // + // See Implementation agg_bcspline.cpp + //------------------------------------------------------------------------ + class bcspline + { + public: + bcspline(); + bcspline(int num); + bcspline(int num, const double* x, const double* y); + + void init(int num); + void add_point(double x, double y); + void prepare(); + + void init(int num, const double* x, const double* y); + + double get(double x) const; + double get_stateful(double x) const; + + private: + bcspline(const bcspline&); + const bcspline& operator = (const bcspline&); + + static void bsearch(int n, const double *x, double x0, int *i); + double extrapolation_left(double x) const; + double extrapolation_right(double x) const; + double interpolation(double x, int i) const; + + int m_max; + int m_num; + double* m_x; + double* m_y; + pod_array m_am; + mutable int m_last_idx; + }; + + +} + +#endif diff --git a/assdraw/src/agg_conv_bcspline.h b/assdraw/src/agg_conv_bcspline.h new file mode 100644 index 000000000..3d40b9619 --- /dev/null +++ b/assdraw/src/agg_conv_bcspline.h @@ -0,0 +1,58 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_conv_bcspline_INCLUDED +#define AGG_conv_bcspline_INCLUDED + +#include "agg_basics.h" +#include "agg_vcgen_bcspline.h" +#include "agg_conv_adaptor_vcgen.h" + + +namespace agg +{ + + //---------------------------------------------------------conv_bcspline + template + struct conv_bcspline : public conv_adaptor_vcgen + { + typedef conv_adaptor_vcgen base_type; + + conv_bcspline(VertexSource& vs) : + conv_adaptor_vcgen(vs) {} + + void interpolation_step(double v) { base_type::generator().interpolation_step(v); } + double interpolation_step() const { return base_type::generator().interpolation_step(); } + + private: + conv_bcspline(const conv_bcspline&); + const conv_bcspline& + operator = (const conv_bcspline&); + }; + +} + + +#endif + diff --git a/assdraw/src/agg_vcgen_bcspline.cpp b/assdraw/src/agg_vcgen_bcspline.cpp new file mode 100644 index 000000000..3276ac1f8 --- /dev/null +++ b/assdraw/src/agg_vcgen_bcspline.cpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_vcgen_bcspline.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + vcgen_bcspline::vcgen_bcspline() : + m_src_vertices(), + m_spline_x(), + m_spline_y(), + m_interpolation_step(1.0/50.0), + m_closed(0), + m_status(initial), + m_src_vertex(0) + { + } + + + //------------------------------------------------------------------------ + void vcgen_bcspline::remove_all() + { + m_src_vertices.remove_all(); + m_closed = 0; + m_status = initial; + m_src_vertex = 0; + } + + + //------------------------------------------------------------------------ + void vcgen_bcspline::add_vertex(double x, double y, unsigned cmd) + { + m_status = initial; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(point_d(x, y)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(point_d(x, y)); + } + else + { + m_closed = get_close_flag(cmd); + } + } + } + + + //------------------------------------------------------------------------ + void vcgen_bcspline::rewind(unsigned) + { + m_cur_abscissa = 0.0; + m_max_abscissa = 0.0; + m_src_vertex = 0; + if(m_status == initial && m_src_vertices.size() > 2) + { + if(m_closed) + { + m_spline_x.init(m_src_vertices.size() + 8); + m_spline_y.init(m_src_vertices.size() + 8); + m_spline_x.add_point(0.0, m_src_vertices.prev(m_src_vertices.size() - 3).x); + m_spline_y.add_point(0.0, m_src_vertices.prev(m_src_vertices.size() - 3).y); + m_spline_x.add_point(1.0, m_src_vertices[m_src_vertices.size() - 3].x); + m_spline_y.add_point(1.0, m_src_vertices[m_src_vertices.size() - 3].y); + m_spline_x.add_point(2.0, m_src_vertices[m_src_vertices.size() - 2].x); + m_spline_y.add_point(2.0, m_src_vertices[m_src_vertices.size() - 2].y); + m_spline_x.add_point(3.0, m_src_vertices[m_src_vertices.size() - 1].x); + m_spline_y.add_point(3.0, m_src_vertices[m_src_vertices.size() - 1].y); + } + else + { + m_spline_x.init(m_src_vertices.size()); + m_spline_y.init(m_src_vertices.size()); + } + unsigned i; + for(i = 0; i < m_src_vertices.size(); i++) + { + double x = m_closed ? i + 4 : i; + m_spline_x.add_point(x, m_src_vertices[i].x); + m_spline_y.add_point(x, m_src_vertices[i].y); + } + m_cur_abscissa = 0.0; + m_max_abscissa = m_src_vertices.size() - 1; + if(m_closed) + { + m_cur_abscissa = 4.0; + m_max_abscissa += 5.0; + m_spline_x.add_point(m_src_vertices.size() + 4, m_src_vertices[0].x); + m_spline_y.add_point(m_src_vertices.size() + 4, m_src_vertices[0].y); + m_spline_x.add_point(m_src_vertices.size() + 5, m_src_vertices[1].x); + m_spline_y.add_point(m_src_vertices.size() + 5, m_src_vertices[1].y); + m_spline_x.add_point(m_src_vertices.size() + 6, m_src_vertices[2].x); + m_spline_y.add_point(m_src_vertices.size() + 6, m_src_vertices[2].y); + m_spline_x.add_point(m_src_vertices.size() + 7, m_src_vertices.next(2).x); + m_spline_y.add_point(m_src_vertices.size() + 7, m_src_vertices.next(2).y); + } + m_spline_x.prepare(); + m_spline_y.prepare(); + } + m_status = ready; + } + + + + + + + //------------------------------------------------------------------------ + unsigned vcgen_bcspline::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + rewind(0); + + case ready: + if(m_src_vertices.size() < 2) + { + cmd = path_cmd_stop; + break; + } + + if(m_src_vertices.size() == 2) + { + *x = m_src_vertices[m_src_vertex].x; + *y = m_src_vertices[m_src_vertex].y; + m_src_vertex++; + if(m_src_vertex == 1) return path_cmd_move_to; + if(m_src_vertex == 2) return path_cmd_line_to; + cmd = path_cmd_stop; + break; + } + + cmd = path_cmd_move_to; + m_status = polygon; + m_src_vertex = 0; + + case polygon: + if(m_cur_abscissa >= m_max_abscissa) + { + if(m_closed) + { + m_status = end_poly; + break; + } + else + { + *x = m_src_vertices[m_src_vertices.size() - 1].x; + *y = m_src_vertices[m_src_vertices.size() - 1].y; + m_status = end_poly; + return path_cmd_line_to; + } + } + + *x = m_spline_x.get(m_cur_abscissa); + *y = m_spline_y.get(m_cur_abscissa); + m_src_vertex++; + m_cur_abscissa += m_interpolation_step; + return (m_src_vertex == 1) ? path_cmd_move_to : path_cmd_line_to; + + case end_poly: + m_status = stop; + return path_cmd_end_poly | m_closed; + + case stop: + return path_cmd_stop; + } + } + return cmd; + } + + +} + diff --git a/assdraw/src/agg_vcgen_bcspline.h b/assdraw/src/agg_vcgen_bcspline.h new file mode 100644 index 000000000..6f92d9942 --- /dev/null +++ b/assdraw/src/agg_vcgen_bcspline.h @@ -0,0 +1,83 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_vcgen_bcspline_INCLUDED +#define AGG_vcgen_bcspline_INCLUDED + +#include "agg_basics.h" +#include "agg_array.h" +#include "agg_bcspline.h" + + +namespace agg +{ + + //==========================================================vcgen_bcspline + class vcgen_bcspline + { + enum status_e + { + initial, + ready, + polygon, + end_poly, + stop + }; + + public: + typedef pod_bvector vertex_storage; + + vcgen_bcspline(); + + void interpolation_step(double v) { m_interpolation_step = v; } + double interpolation_step() const { return m_interpolation_step; } + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_bcspline(const vcgen_bcspline&); + const vcgen_bcspline& operator = (const vcgen_bcspline&); + + vertex_storage m_src_vertices; + bcspline m_spline_x; + bcspline m_spline_y; + double m_interpolation_step; + unsigned m_closed; + status_e m_status; + unsigned m_src_vertex; + double m_cur_abscissa; + double m_max_abscissa; + }; + +} + + +#endif + diff --git a/assdraw/src/agghelper.hpp b/assdraw/src/agghelper.hpp new file mode 100644 index 000000000..de0ce66fc --- /dev/null +++ b/assdraw/src/agghelper.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "agg_path_storage.h" + +class agghelper +{ +public: + + static agg::path_storage RectanglePath(double left, double right, double top, double bottom) + { + agg::path_storage path; + path.move_to(left,top); + path.line_to(right,top); + path.line_to(right,bottom); + path.line_to(left,bottom); + path.line_to(left,top); + return path; + } + +}; diff --git a/assdraw/src/assdraw.cpp b/assdraw/src/assdraw.cpp new file mode 100644 index 000000000..132c01882 --- /dev/null +++ b/assdraw/src/assdraw.cpp @@ -0,0 +1,768 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: assdraw.cpp +// Purpose: ASSDraw main source file +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + + +#include "assdraw.hpp" +#include "enums.hpp" +#include "include_once.hpp" +#include +#include +#include +#include +#include + +//DEFINE_EVENT_TYPE(wxEVT_SETTINGS_CHANGED) + +// initialize wxWidget to accept our App class +IMPLEMENT_APP(ASSDrawApp) + +BEGIN_EVENT_TABLE(ASSDrawFrame, wxFrame) + EVT_TOOL(TB_CLEAR, ASSDrawFrame::OnSelect_Clear) + EVT_TOOL(TB_PREVIEW, ASSDrawFrame::OnSelect_Preview) + //EVT_TOOL(TB_EDITSRC, ASSDrawFrame::OnSelect_EditSrc) + EVT_TOOL(TB_TRANSFORM, ASSDrawFrame::OnSelect_Transform) + EVT_TOOL_RANGE(MODE_ARR, MODE_DEL, ASSDrawFrame::OnChoose_Mode) + EVT_TOOL_RANGE(DRAG_DWG, DRAG_BOTH, ASSDrawFrame::OnChoose_DragMode) + EVT_TOOL_RCLICKED(wxID_ANY, ASSDrawFrame::OnToolRClick) + EVT_COMMAND(wxID_ANY, wxEVT_SETTINGS_CHANGED, ASSDrawFrame::OnSettingsChanged) + EVT_MENU_RANGE(MENU_TB_ALL, MENU_TB_BGIMG, ASSDrawFrame::OnChoose_TBarRClickMenu) +#if wxUSE_MENUS + EVT_MENU(MENU_CLEAR, ASSDrawFrame::OnSelect_Clear) + EVT_MENU(MENU_PREVIEW, ASSDrawFrame::OnSelect_Preview) + EVT_MENU(MENU_TRANSFORM, ASSDrawFrame::OnSelect_Transform) + EVT_MENU(MENU_LIBRARY, ASSDrawFrame::OnSelect_Library) + EVT_MENU(MENU_SETTINGS, ASSDrawFrame::OnSelect_Settings) + EVT_MENU(MENU_RESETPERSPECTIVE, ASSDrawFrame::OnSelect_ResetPerspective) + EVT_MENU(MENU_HELP, ASSDrawFrame::OnSelect_Help) + EVT_MENU(wxID_ABOUT, ASSDrawFrame::OnSelect_About) + EVT_MENU(MENU_UNDO, ASSDrawFrame::OnSelect_Undo) + EVT_MENU(MENU_REDO, ASSDrawFrame::OnSelect_Redo) + EVT_MENU(MENU_PASTE, ASSDrawFrame::OnSelect_Paste) + EVT_MENU(MENU_BGIMG_REMOVE, ASSDrawFrame::OnSelect_RemoveBG) + EVT_MENU(MENU_BGIMG_ALPHA, ASSDrawFrame::OnSelect_AlphaBG) + EVT_MENU_RANGE(MODE_ARR, MODE_NUT_BILINEAR, ASSDrawFrame::OnChoose_Mode) + EVT_MENU_RANGE(DRAG_DWG, DRAG_BOTH, ASSDrawFrame::OnChoose_DragMode) + EVT_MENU_RANGE(MENU_REPOS_TOPLEFT, MENU_REPOS_BOTRIGHT, ASSDrawFrame::OnChoose_Recenter) + EVT_MENU_RANGE(MENU_REPOS_BGTOPLEFT, MENU_REPOS_BGBOTRIGHT, ASSDrawFrame::OnChoose_RecenterToBG) +#endif //wxUSE_MENUS + EVT_CLOSE(ASSDrawFrame::OnClose) +END_EVENT_TABLE() + +// ---------------------------------------------------------------------------- +// the application class: ASSDrawApp +// ---------------------------------------------------------------------------- + +// 'Main program' equivalent: the program execution "starts" here +bool ASSDrawApp::OnInit() +{ + // create the main application window + ASSDrawFrame * assdrawframe = new ASSDrawFrame( this, TITLE, wxDefaultPosition, wxSize(640, 480) ); + SetTopWindow(assdrawframe); + return TRUE; +} + + + +// ---------------------------------------------------------------------------- +// main frame: ASSDrawFrame +// ---------------------------------------------------------------------------- + +// constructor + +ASSDrawFrame::ASSDrawFrame( wxApp *app, const wxString& title, const wxPoint& pos, const wxSize& size, long style) + : wxFrame(NULL, wxID_ANY, title, pos, size, style) +{ + m_app = app; + m_mgr.SetManagedWindow(this); + m_mgr.SetFlags(m_mgr.GetFlags() | wxAUI_MGR_ALLOW_ACTIVE_PANE); + + // set the frame icon + SetIcon(wxICON(appico)); + + // Create status bar for the frame + #if wxUSE_STATUSBAR + CreateStatusBar(3); + int statwidths[] = { 64, -1, 64 }; + GetStatusBar()->SetStatusWidths(3, statwidths); + SetStatusBarPane(1); + #endif // wxUSE_STATUSBAR + + InitializeDefaultSettings(); + + // load config + configfile = wxFileName(::wxGetCwd(), _T("ASSDraw3.cfg")).GetFullPath(); + if (!::wxFileExists(configfile)) + configfile = wxFileName(wxStandardPaths::Get().GetUserConfigDir(), _T("ASSDraw3.cfg")).GetFullPath(); + + bool firsttime = !::wxFileExists(configfile); + if (firsttime) wxFileOutputStream(configfile).Close(); + wxFileInputStream cfgf(configfile); + config = new wxFileConfig(cfgf); + + // nullify transformdlg + transformdlg = NULL; + + Maximize(true); + Show(true); // to get the right client size, must call Show() first + + // config + LoadSettings(); + + // THE CANVAS + m_canvas = new ASSDrawCanvas( this , this ); + + // shapes library + shapelib = new ASSDrawShapeLibrary(this, this); + + // source text ctrl + srctxtctrl = new ASSDrawSrcTxtCtrl(this, this); + + // settings + /* + settingsdlg = NULL; + wxString settingsdllfile = wxFileName(::wxGetCwd(), "settings.dll").GetFullPath(); + + if (::wxFileExists(settingsdllfile)) + { + wxDynamicLibrary settingsdll(settingsdllfile); + wxString symbol("CreateASSDrawSettingsDialogInstance"); + if (settingsdll.IsLoaded() && settingsdll.HasSymbol(symbol)) + { + typedef ASSDrawSettingsDialog* (*FuncType)(wxWindow*,ASSDrawFrame*,int); + FuncType func = (FuncType) settingsdll.GetSymbol(symbol); + //wxDYNLIB_FUNCTION(ASSDrawSettingsDialog, CreateASSDrawSettingsDialogInstance, settingsdll) + //ASSDrawSettingsDialog* test = func(NULL,this,971231); + //test->Reparent(this); + //test->Init(); + //SetTitle(settingsdllfile); + //settingsdlg = func(NULL,this, 809131); + //settingsdlg->Init(); + //m_mgr.AddPane(settingsdlg, wxAuiPaneInfo().Name(wxT("settings")).Caption(wxT("Settings")).Right().Layer(3).Position(0).CloseButton(true).BestSize(wxSize(240, 480)).MinSize(wxSize(200, 200))); + } + } + */ + + settingsdlg = new ASSDrawSettingsDialog(this, this); + settingsdlg->Init(); + + SetMenus(); + SetToolBars(); + SetPanes(); + + // config + + config->SetPath(_T("info")); + wxString version; + config->Read("version", &version); + config->SetPath(".."); + + default_perspective = m_mgr.SavePerspective(); // back up default perspective + config->SetPath("perspective"); + wxString perspective; + if (config->Read("perspective", &perspective) && version == VERSION) m_mgr.LoadPerspective(perspective, false); + config->SetPath(".."); + + config->SetPath("library"); + int n = 0; + config->Read("n", &n); + for (int i = 0; i < n; i++) + { + wxString libcmds; + config->Read(wxString::Format("%d",i), &libcmds); + shapelib->AddShapePreview(libcmds); + } + config->SetPath(".."); + + m_mgr.Update(); + m_canvas->SetFocus(); + m_canvas->Show(); + + wxSize clientsize = m_canvas->GetClientSize(); + m_canvas->ChangeZoomLevelTo(DEFAULT_SCALE, wxPoint(clientsize.x / 2, clientsize.y / 2)); + m_canvas->MoveCanvasOriginTo(clientsize.x / 2, clientsize.y / 2); + UpdateASSCommandStringToSrcTxtCtrl(m_canvas->GenerateASS()); + + UpdateFrameUI(); + ApplySettings(); + + #ifdef BETAVERSION + wxDateTime expire(15, wxDateTime::Dec, 2007, 0, 0, 0); + wxDateTime now = wxDateTime::Now(); + if (now.IsLaterThan(expire)) + { + wxMessageDialog expired(this, "Thank you for trying ASSDraw3. This beta version has expired. Please visit http://malakith.net/aegisub/index.php?topic=912.0 to get the latest release. Visit now?", wxT("Beta version"), wxYES_NO | wxICON_INFORMATION); + if (expired.ShowModal() == wxID_YES) + ::wxLaunchDefaultBrowser(wxString("http://malakith.net/aegisub/index.php?topic=912.0")); + Close(); + } + SetTitle(wxString::Format("%s beta %d (expires %s)", TITLE, BETAVERSION, expire.FormatDate().c_str())); + #endif + + if (firsttime) + _About(); + else if (!behaviors.nosplashscreen) + _About(3); + + helpcontroller.SetParentWindow(this); + helpcontroller.Initialize(wxFileName(::wxGetCwd(), "ASSDraw3.chm").GetFullPath()); +} + +void ASSDrawFrame::SetToolBars() +{ + drawtbar = new wxToolBar(this, wxID_ANY, __DPDS__ , wxTB_FLAT | wxTB_TEXT | wxTB_NODIVIDER | wxTB_HORIZONTAL); + drawtbar->AddTool(TB_CLEAR, _T("Clear"), wxBITMAP(new_), wxNullBitmap, wxITEM_NORMAL, "", TIPS_CLEAR); + //tbar->AddTool(TB_EDITSRC, _T("Source"), wxBITMAP(src_), wxNullBitmap, wxITEM_NORMAL, "", TIPS_EDITSRC); + drawtbar->AddCheckTool(TB_PREVIEW, _T("Preview"), wxBITMAP(preview_), wxNullBitmap, "", TIPS_PREVIEW); + //drawtbar->AddTool(TB_TRANSFORM, _T("Transform"), wxBITMAP(rot_), wxNullBitmap, wxITEM_NORMAL, "", TIPS_TRANSFORM); + zoomslider = new wxSlider(drawtbar, TB_ZOOMSLIDER, 1000, 100, 5000, __DPDS__ ); + //zoomslider->SetSize(280, zoomslider->GetSize().y); + zoomslider->Connect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(ASSDrawFrame::OnZoomSliderChanged), NULL, this); + zoomslider->Connect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(ASSDrawFrame::OnZoomSliderChanged), NULL, this); + zoomslider->Connect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(ASSDrawFrame::OnZoomSliderChanged), NULL, this); + zoomslider->Connect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(ASSDrawFrame::OnZoomSliderChanged), NULL, this); + zoomslider->Connect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(ASSDrawFrame::OnZoomSliderChanged), NULL, this); + zoomslider->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(ASSDrawFrame::OnZoomSliderChanged), NULL, this); + drawtbar->AddControl(zoomslider); + drawtbar->Realize(); + + m_mgr.AddPane(drawtbar, wxAuiPaneInfo().Name(wxT("drawtbar")).Caption(TBNAME_DRAW). + ToolbarPane().Top().Position(0).Dockable(true).LeftDockable(false).RightDockable(false)); + + modetbar = new wxToolBar(this, wxID_ANY, __DPDS__ , wxTB_FLAT | wxTB_TEXT | wxTB_NODIVIDER | wxTB_HORIZONTAL); + modetbar->AddRadioTool(MODE_ARR, _T("Drag"), wxBITMAP(arr_), wxNullBitmap, "", TIPS_ARR); + modetbar->AddRadioTool(MODE_M, _T("Move"), wxBITMAP(m_), wxNullBitmap, "", TIPS_M); + //modetbar->AddRadioTool(MODE_N, _T("Move*"), wxBITMAP(n_), wxNullBitmap, "", TIPS_N); + modetbar->AddRadioTool(MODE_L, _T("Line"), wxBITMAP(l_), wxNullBitmap, "", TIPS_L); + modetbar->AddRadioTool(MODE_B, _T("Bezier"), wxBITMAP(b_), wxNullBitmap, "", TIPS_B); + //modetbar->AddRadioTool(MODE_S, _T("Spline"), wxBITMAP(s_), wxNullBitmap, "", TIPS_S); + //modetbar->AddRadioTool(MODE_P, _T("Extend"), wxBITMAP(p_), wxNullBitmap, "", TIPS_P); + //modetbar->AddRadioTool(MODE_C, _T("Close"), wxBITMAP(c_), wxNullBitmap, "", TIPS_C); + modetbar->AddRadioTool(MODE_DEL, _T("Delete"), wxBITMAP(del_), wxNullBitmap, "", TIPS_DEL); + modetbar->AddRadioTool(MODE_SCALEROTATE, _T("Scale/Rotate"), wxBITMAP(sc_rot_), wxNullBitmap, "", TIPS_SCALEROTATE); + modetbar->AddRadioTool(MODE_NUT_BILINEAR, _T("Bilinear"), wxBITMAP(nut_), wxNullBitmap, "", TIPS_NUTB); + //modetbar->AddRadioTool(MODE_NUT_PERSPECTIVE, _T("NUT:P"), wxBITMAP(arr_), wxNullBitmap, "", ""); + modetbar->Realize(); + + m_mgr.AddPane(modetbar, wxAuiPaneInfo().Name(wxT("modetbar")).Caption(TBNAME_MODE). + ToolbarPane().Top().Position(1).Dockable(true).LeftDockable(false).RightDockable(false)); + + bgimgtbar = new wxToolBar(this, wxID_ANY, __DPDS__ , wxTB_FLAT | wxTB_TEXT | wxTB_NODIVIDER | wxTB_HORIZONTAL); + bgimgtbar->SetToolBitmapSize(wxSize(24,15)); + bgimgtbar->AddCheckTool(DRAG_DWG, _T("Pan drawing"), wxBITMAP(pan_shp), wxNullBitmap, "", TIPS_DWG); + bgimgtbar->AddCheckTool(DRAG_BGIMG, _T("Pan background"), wxBITMAP(pan_bg), wxNullBitmap, "", TIPS_BGIMG); + //bgimgtbar->AddRadioTool(DRAG_BOTH, _T("Pan both"), wxBITMAP(pan_both), wxNullBitmap, "", TIPS_BOTH); + bgimgtbar->Realize(); + + m_mgr.AddPane(bgimgtbar, wxAuiPaneInfo().Name(wxT("bgimgtbar")).Caption(TBNAME_BGIMG). + ToolbarPane().Top().Position(2).Dockable(true).LeftDockable(false).RightDockable(false)); + +} + +void ASSDrawFrame::SetMenus() +{ +#if wxUSE_MENUS + drawMenu = new wxMenu; + drawMenu->Append(MENU_CLEAR, _T("&Clear\tCtrl+N"), TIPS_CLEAR); + //drawMenu->Append(MENU_EDITSRC, _T("&Source"), TIPS_EDITSRC); + drawMenu->Append(MENU_PREVIEW, _T("&Preview\tCtrl+P"), TIPS_PREVIEW, wxITEM_CHECK); + drawMenu->Append(MENU_TRANSFORM, _T("&Transform"), TIPS_TRANSFORM); + drawMenu->Append(MENU_PASTE, _T("&Paste\tCtrl+V"), TIPS_PASTE); + drawMenu->AppendSeparator(); + drawMenu->Append(MENU_UNDO, _T("&Undo\tCtrl+Z"), TIPS_UNDO); + drawMenu->Append(MENU_REDO, _T("&Redo\tCtrl+Y"), TIPS_REDO); + drawMenu->Enable(MENU_UNDO, false); + drawMenu->Enable(MENU_REDO, false); + + modeMenu = new wxMenu; + modeMenu->Append(MODE_ARR, _T("D&rag\tF1"), TIPS_ARR, wxITEM_RADIO); + modeMenu->Append(MODE_M, _T("Draw &M\tF2"), TIPS_M, wxITEM_RADIO); + modeMenu->Append(MODE_L, _T("Draw &L\tF3"), TIPS_L, wxITEM_RADIO); + modeMenu->Append(MODE_B, _T("Draw &B\tF4"), TIPS_B, wxITEM_RADIO); + modeMenu->Append(MODE_DEL, _T("&Delete\tF5"), TIPS_DEL, wxITEM_RADIO); + modeMenu->Append(MODE_SCALEROTATE, _T("&Scale/Rotate\tF6"), TIPS_NUTB, wxITEM_RADIO); + modeMenu->Append(MODE_NUT_BILINEAR, _T("&Bilinear transformation\tF7"), TIPS_SCALEROTATE, wxITEM_RADIO); + + bgimgMenu = new wxMenu; + bgimgMenu->Append(DRAG_DWG, _T("Pan/Zoom &Drawing\tShift+F1"), TIPS_DWG, wxITEM_CHECK); + bgimgMenu->Append(DRAG_BGIMG, _T("Pan/Zoom Back&ground\tShift+F2"), TIPS_BGIMG, wxITEM_CHECK); + bgimgMenu->AppendSeparator(); + bgimgMenu->Append(MENU_BGIMG_ALPHA, _T("Set background image opacity"), ""); + wxMenu* reposbgMenu = new wxMenu; + reposbgMenu->Append( MENU_REPOS_BGTOPLEFT, "Top left\tCtrl+Shift+7" ); + reposbgMenu->Append( MENU_REPOS_BGTOPRIGHT, "Top right\tCtrl+Shift+9" ); + reposbgMenu->Append( MENU_REPOS_BGCENTER, "&Center\tCtrl+Shift+5" ); + reposbgMenu->Append( MENU_REPOS_BGBOTLEFT, "Bottom left\tCtrl+Shift+1" ); + reposbgMenu->Append( MENU_REPOS_BGBOTRIGHT, "Bottom right\tCtrl+Shift+3" ); + bgimgMenu->Append(MENU_BGIMG_RECENTER, _T("Reposition [&0, 0]"), reposbgMenu); + bgimgMenu->Append(MENU_BGIMG_REMOVE, _T("Remove background\tShift+Del"), ""); + + wxMenu* reposMenu = new wxMenu; + reposMenu->Append( MENU_REPOS_TOPLEFT, "Top left\tCtrl+7" ); + reposMenu->Append( MENU_REPOS_TOPRIGHT, "Top right\tCtrl+9" ); + reposMenu->Append( MENU_REPOS_CENTER, "&Center\tCtrl+5" ); + reposMenu->Append( MENU_REPOS_BOTLEFT, "Bottom left\tCtrl+1" ); + reposMenu->Append( MENU_REPOS_BOTRIGHT, "Bottom right\tCtrl+3" ); + + tbarMenu = new wxMenu; + tbarMenu->AppendCheckItem(MENU_TB_DRAW, TBNAME_DRAW); + tbarMenu->AppendCheckItem(MENU_TB_MODE, TBNAME_MODE); + tbarMenu->AppendCheckItem(MENU_TB_BGIMG, TBNAME_BGIMG); + tbarMenu->AppendSeparator(); + tbarMenu->Append(MENU_TB_ALL, "Show all"); + tbarMenu->Append(MENU_TB_NONE, "Hide all"); + tbarMenu->Append(MENU_TB_DOCK, "Dock all"); + tbarMenu->Append(MENU_TB_UNDOCK, "Undock all"); + + viewMenu = new wxMenu; + viewMenu->Append(MENU_LIBRARY, _T("&Library"), TIPS_LIBRARY, wxITEM_CHECK); + if (settingsdlg) + viewMenu->Append(MENU_SETTINGS, _T("&Settings"), "", wxITEM_CHECK); + viewMenu->Append(MENU_TBAR, _T("&Toolbars"), tbarMenu); + viewMenu->Append(MENU_RECENTER, _T("Reposition [&0, 0]"), reposMenu); + viewMenu->AppendSeparator(); + viewMenu->Append(MENU_RESETPERSPECTIVE, _T("&Reset workspace")); + + wxMenu* helpMenu = new wxMenu; + helpMenu->Append(MENU_HELP, _T("&Manual")); + helpMenu->Append(wxID_ABOUT, _T("&About")); + + wxMenuBar *menuBar = new wxMenuBar(); + menuBar->Append(drawMenu, _T("&Canvas")); + menuBar->Append(modeMenu, _T("&Mode")); + menuBar->Append(bgimgMenu, _T("&Background")); + menuBar->Append(viewMenu, _T("&Workspace")); + menuBar->Append(helpMenu, _T("&Help")); + + + SetMenuBar(menuBar); +#endif // wxUSE_MENUS +} + +void ASSDrawFrame::SetPanes() +{ + m_mgr.AddPane(shapelib, wxAuiPaneInfo().Name(wxT("library")).Caption(wxT("Shapes Library")). + Right().Layer(2).Position(0).CloseButton(true).BestSize(wxSize(120, 480)).MinSize(wxSize(100, 200))); + + m_mgr.AddPane(m_canvas, wxAuiPaneInfo().Name(wxT("canvas")).CenterPane()); + + m_mgr.AddPane(srctxtctrl, wxAuiPaneInfo().Name(wxT("commands")).Caption(wxT("Drawing commands")). + Bottom().Layer(1).CloseButton(false).BestSize(wxSize(320, 48))); + + if (settingsdlg) + m_mgr.AddPane(settingsdlg, wxAuiPaneInfo().Name(wxT("settings")).Caption(wxT("Settings")). + Right().Layer(3).Position(0).CloseButton(true).BestSize(wxSize(240, 480)).MinSize(wxSize(200, 200)).Show(false)); +} + +ASSDrawFrame::~ASSDrawFrame() +{ + config->SetPath("info"); + config->Write("assdraw3.exe", wxStandardPaths::Get().GetExecutablePath()); + config->Write("version", VERSION); + config->SetPath(".."); + + SaveSettings(); + + config->SetPath("perspective"); + config->Write("perspective", m_mgr.SavePerspective()); + config->SetPath(".."); + + config->DeleteGroup("library"); + config->SetPath("library"); + typedef std::vector< ASSDrawShapePreview *> PrevVec; + PrevVec shapes = shapelib->GetShapePreviews(); + int n = shapes.size(); + config->Write("n", n); + for (int i = 0; i < n; i++) + config->Write(wxString::Format("%d",i), shapes[i]->GenerateASS()); + config->SetPath(".."); + + wxFileOutputStream cfgf(configfile); + config->Save(cfgf); + delete config; + + if (settingsdlg) settingsdlg->Destroy(); // we need this since wxPropertyGrid must be Clear()ed before deleting + + m_mgr.UnInit(); +} + +void ASSDrawFrame::_Clear() +{ + wxMessageDialog msgb(this, _T("Clear the canvas and create a new drawing?"), + _T("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION ); + if (msgb.ShowModal() == wxID_OK) + { + m_canvas->RefreshUndocmds(); + m_canvas->AddUndo("Clear canvas"); + m_canvas->ResetEngine(true); + wxSize siz = m_canvas->GetClientSize(); + m_canvas->ChangeZoomLevelTo(DEFAULT_SCALE, wxPoint(siz.x / 2, siz.y / 2)); + m_canvas->MoveCanvasOriginTo(siz.x / 2, siz.y / 2); + UpdateUndoRedoMenu(); + m_canvas->RefreshDisplay(); + } +} + +void ASSDrawFrame::_Preview() +{ + m_canvas->SetPreviewMode( !m_canvas->IsPreviewMode() ); + UpdateFrameUI(); + m_canvas->RefreshDisplay(); +} + +void ASSDrawFrame::_ToggleLibrary() +{ + m_mgr.GetPane(shapelib).Show(!m_mgr.GetPane(shapelib).IsShown()); + m_mgr.Update(); + UpdateFrameUI(); +} + +void ASSDrawFrame::_ToggleSettings() +{ + if (settingsdlg == NULL) return; + m_mgr.GetPane(settingsdlg).Show(!m_mgr.GetPane(settingsdlg).IsShown()); + m_mgr.Update(); + UpdateFrameUI(); +} + +void ASSDrawFrame::_ResetPerspective() +{ + m_mgr.LoadPerspective(default_perspective, false); + UpdateFrameUI(); + m_mgr.Update(); + DRAGMODE bck = m_canvas->GetDragMode(); + if (m_canvas->HasBackgroundImage()) m_canvas->SetDragMode(DRAGMODE(true, true)); + wxSize clientsize = m_canvas->GetClientSize(); + m_canvas->ChangeZoomLevelTo(DEFAULT_SCALE, wxPoint(clientsize.x / 2, clientsize.y / 2)); + m_canvas->MoveCanvasOriginTo(clientsize.x / 2, clientsize.y / 2); + m_canvas->SetDragMode(bck); + UpdateFrameUI(); +} + +void ASSDrawFrame::_Transform() +{ + if (transformdlg == NULL) + transformdlg = new ASSDrawTransformDlg( this ); + + if (transformdlg->ShowModal() == wxID_OK) + { + m_canvas->Transform( + transformdlg->xformvals.f1, + transformdlg->xformvals.f2, + transformdlg->xformvals.f3, + transformdlg->xformvals.f4, + transformdlg->xformvals.f5, + transformdlg->xformvals.f6, + transformdlg->xformvals.f7, + transformdlg->xformvals.f8 ); + m_canvas->AddUndo("Transform"); + m_canvas->RefreshDisplay(); + UpdateUndoRedoMenu(); + } +} + +void ASSDrawFrame::_Paste() +{ + if (wxTheClipboard->Open()) + { + if (wxTheClipboard->IsSupported( wxDF_BITMAP )) + { + wxBitmapDataObject data; + wxTheClipboard->GetData( data ); + m_canvas->SetBackgroundImage( data.GetBitmap().ConvertToImage() ); + //m_canvas->AskUserForBackgroundAlpha(); + } + else if (wxTheClipboard->IsSupported( wxDF_TEXT )) + { + wxTextDataObject data; + wxTheClipboard->GetData( data ); + UpdateASSCommandStringToSrcTxtCtrl( data.GetText() ); + } + wxTheClipboard->Close(); + } +} + +void ASSDrawFrame::OnChoose_Recenter(wxCommandEvent& event) +{ + int x = 0, y = 0; + wxSize f = m_canvas->GetClientSize(); + + switch (event.GetId()) + { + case MENU_REPOS_TOPLEFT: x = 0, y = 0; break; + case MENU_REPOS_TOPRIGHT: x = f.x, y = 0; break; + case MENU_REPOS_CENTER: x = f.x / 2, y = f.y / 2; break; + case MENU_REPOS_BOTLEFT: x = 0, y = f.y; break; + case MENU_REPOS_BOTRIGHT: x = f.x, y = f.y; break; + } + + m_canvas->MoveCanvasOriginTo(x, y); + m_canvas->RefreshDisplay(); +} + +void ASSDrawFrame::OnChoose_RecenterToBG(wxCommandEvent& event) +{ + unsigned w, h; + wxRealPoint disp; + double scale; + if (m_canvas->GetBackgroundInfo(w, h, disp, scale)) + { + int x = 0, y = 0; + int lx = (int)disp.x, ty = (int)disp.y; + int rx = lx + (int)(w * scale); + int by = ty + (int)(h * scale); + + switch (event.GetId()) + { + case MENU_REPOS_BGTOPLEFT: x = lx, y = ty; break; + case MENU_REPOS_BGTOPRIGHT: x = rx, y = ty; break; + case MENU_REPOS_BGCENTER: x = (rx + lx) / 2, y = (by + ty) / 2; break; + case MENU_REPOS_BGBOTLEFT: x = lx, y = by; break; + case MENU_REPOS_BGBOTRIGHT: x = rx, y = by; break; + } + + m_canvas->MoveCanvasDrawing(x - m_canvas->GetOriginX(), y - m_canvas->GetOriginY()); + m_canvas->RefreshDisplay(); + } +} + +void ASSDrawFrame::_Help() +{ + helpcontroller.DisplayContents(); +} + +void ASSDrawFrame::_About(unsigned timeout) +{ + ASSDrawAboutDlg *aboutdlg = new ASSDrawAboutDlg( this, timeout ); + aboutdlg->ShowModal(); + aboutdlg->Destroy(); +} + +void ASSDrawFrame::OnChoose_Mode(wxCommandEvent& event) +{ + m_canvas->SetDrawMode( (MODE) event.GetId() ); + UpdateFrameUI(); +} + +void ASSDrawFrame::OnChoose_DragMode(wxCommandEvent& event) +{ + DRAGMODE dm = m_canvas->GetDragMode(); + switch (event.GetId()) + { + case DRAG_DWG: dm.drawing = !dm.drawing; break; + case DRAG_BGIMG: dm.bgimg = !dm.bgimg; break; + } + m_canvas->SetDragMode( dm ); + UpdateFrameUI(); +} + +void ASSDrawFrame::OnZoomSliderChanged(wxScrollEvent &event) +{ + double zoom = (double) event.GetPosition() / 100.0; + m_canvas->ChangeZoomLevelTo(zoom, wxPoint((int) m_canvas->GetOriginX(), (int) m_canvas->GetOriginY())); +} + +void ASSDrawFrame::OnToolRClick(wxCommandEvent& event) +{ + int id = event.GetId(); + if (drawtbar->FindById(id) != NULL + || modetbar->FindById(id) != NULL + || bgimgtbar->FindById(id) != NULL) + { + PopupMenu(tbarMenu); + } +} + +void ASSDrawFrame::OnChoose_TBarRClickMenu(wxCommandEvent& event) +{ + int id = event.GetId(); + wxToolBar* tbar[3] = { drawtbar, modetbar, bgimgtbar }; + bool tb[3] = { false, false, false }; + bool show[2] = { false, true }; + bool dock[2] = { false, true }; + switch (id) + { + case MENU_TB_ALL: + tb[0] = true, tb[1] = true, tb[2] = true; + show[0] = true, show[1] = true; + break; + case MENU_TB_NONE: + tb[0] = true, tb[1] = true, tb[2] = true; + show[0] = true, show[1] = false; + break; + case MENU_TB_DOCK: + tb[0] = true, tb[1] = true, tb[2] = true; + dock[0] = true, dock[1] = true; + break; + case MENU_TB_UNDOCK: + tb[0] = true, tb[1] = true, tb[2] = true; + dock[0] = true, dock[1] = false; + break; + case MENU_TB_DRAW: + tb[0] = true; + show[0] = true, show[1] = !m_mgr.GetPane(tbar[0]).IsShown(); + break; + case MENU_TB_MODE: + tb[1] = true; + show[0] = true, show[1] = !m_mgr.GetPane(tbar[1]).IsShown(); + break; + case MENU_TB_BGIMG: + tb[2] = true; + show[0] = true, show[1] = !m_mgr.GetPane(tbar[2]).IsShown(); + break; + } + for (int i = 0; i < 3; i++) + { + if (tb[i]) + { + if (show[0]) + m_mgr.GetPane(tbar[i]).Show(show[1]); + if (dock[0]) + if (dock[1]) + m_mgr.GetPane(tbar[i]).Dock(); + else + m_mgr.GetPane(tbar[i]).Float(); + } + } + m_mgr.Update(); + UpdateFrameUI(); +} + +void ASSDrawFrame::UpdateASSCommandStringFromSrcTxtCtrl(wxString cmds) +{ + m_canvas->ParseASS(cmds, true); + m_canvas->RefreshDisplay(); +} + +void ASSDrawFrame::UpdateASSCommandStringToSrcTxtCtrl(wxString cmd) +{ + if (behaviors.capitalizecmds) + cmd.UpperCase(); + else + cmd.LowerCase(); + srctxtctrl->ChangeValue(cmd); + //srctxtctrl->AppendText(cmd); +} + +void ASSDrawFrame::UndoOrRedo(bool isundo) +{ + if (isundo) + m_canvas->Undo(); + else + m_canvas->Redo(); + UpdateUndoRedoMenu(); + UpdateFrameUI(); +} + +void ASSDrawFrame::UpdateUndoRedoMenu() +{ + wxString nextUndo = m_canvas->GetTopUndo(); + if (nextUndo.IsSameAs("")) + { + drawMenu->SetLabel(MENU_UNDO, "Undo\tCtrl+Z"); + drawMenu->Enable(MENU_UNDO, false); + } + else + { + drawMenu->SetLabel(MENU_UNDO, wxString::Format("Undo: %s\tCtrl+Z", nextUndo.c_str())); + drawMenu->Enable(MENU_UNDO, true); + } + wxString nextRedo = m_canvas->GetTopRedo(); + if (nextRedo.IsSameAs("")) + { + drawMenu->SetLabel(MENU_REDO, "Redo\tCtrl+Y"); + drawMenu->Enable(MENU_REDO, false); + } + else + { + drawMenu->SetLabel(MENU_REDO, wxString::Format("Redo: %s\tCtrl+Y", nextRedo.c_str())); + drawMenu->Enable(MENU_REDO, true); + } +} + +void ASSDrawFrame::UpdateFrameUI(unsigned level) +{ + bool hasbg = m_canvas->HasBackgroundImage(); + int zoom = (int) round(m_canvas->GetScale() * 100.0); + switch (level) + { + case 0: // all + drawtbar->ToggleTool(TB_PREVIEW, m_canvas->IsPreviewMode()); + modetbar->ToggleTool(m_canvas->GetDrawMode(), true); + #if wxUSE_MENUS + drawMenu->Check(MENU_PREVIEW, m_canvas->IsPreviewMode()); + modeMenu->Check(m_canvas->GetDrawMode(), true); + #endif + case 2: // bgimg & toolbars + //bgimgtbar->ToggleTool(m_canvas->GetDragMode(), true); + bgimgtbar->ToggleTool(DRAG_DWG, m_canvas->GetDragMode().drawing); + bgimgtbar->ToggleTool(DRAG_BGIMG, m_canvas->GetDragMode().bgimg); + bgimgtbar->EnableTool(DRAG_BGIMG, hasbg); + //m_mgr.GetPane(bgimgtbar).Show(hasbg); + m_mgr.Update(); + #if wxUSE_MENUS + viewMenu->Check(MENU_LIBRARY, m_mgr.GetPane(shapelib).IsShown()); + if (settingsdlg) + viewMenu->Check(MENU_SETTINGS, m_mgr.GetPane(settingsdlg).IsShown()); + //bgimgMenu->Check(m_canvas->GetDragMode(), true); + bgimgMenu->Check(DRAG_DWG, m_canvas->GetDragMode().drawing); + bgimgMenu->Check(DRAG_BGIMG, m_canvas->GetDragMode().bgimg); + bgimgMenu->Enable(DRAG_BGIMG, hasbg); + bgimgMenu->Enable(DRAG_BOTH, hasbg); + bgimgMenu->Enable(MENU_BGIMG_ALPHA, hasbg); + bgimgMenu->Enable(MENU_BGIMG_RECENTER, hasbg); + bgimgMenu->Enable(MENU_BGIMG_REMOVE, hasbg); + tbarMenu->Check(MENU_TB_DRAW, m_mgr.GetPane(drawtbar).IsShown()); + tbarMenu->Check(MENU_TB_MODE, m_mgr.GetPane(modetbar).IsShown()); + tbarMenu->Check(MENU_TB_BGIMG, m_mgr.GetPane(bgimgtbar).IsShown()); + #endif + case 3: // zoom slider + zoomslider->SetValue(zoom); + SetStatusText( wxString::Format("%d%%", zoom), 2 ); + zoomslider->Enable(m_canvas->GetDragMode().drawing && m_canvas->CanZoom()); + } +} + +void ASSDrawFrame::OnClose(wxCloseEvent &event) +{ + if (event.CanVeto() && behaviors.confirmquit) + { + if (wxMessageDialog(this, wxT("Do you want to close ASSDraw3 now?"), wxT("Confirmation"), wxOK | wxCANCEL).ShowModal() == wxID_OK) + Destroy(); + else + event.Veto(); + } + else + Destroy(); +} diff --git a/assdraw/src/assdraw.hpp b/assdraw/src/assdraw.hpp new file mode 100644 index 000000000..065383d74 --- /dev/null +++ b/assdraw/src/assdraw.hpp @@ -0,0 +1,208 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: assdraw.hpp +// Purpose: header file for ASSDraw main source file; also includes +// declarations for all GUI elements (except wxRuler, which has +// its own header file) +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "_common.hpp" + +#include +#include +#include +#include + +#include "canvas.hpp" // the canvas +#include "dlgctrl.hpp" // custom dialogs & controls +#include "settings.hpp" // settings property grid +#include "library.hpp" // shape library + +//#define BETAVERSION 2 +#define VERSION "3.0 final" + +// this header file declares the following classes +class ASSDrawApp; +class ASSDrawFrame; +class ASSDrawCanvas; + +class ASSDrawApp : public wxApp +{ +public: + bool OnInit(); +}; + +class ASSDrawFrame : public wxFrame +{ +public: + // constructor + ASSDrawFrame(wxApp *app, const wxString& title, const wxPoint& pos, const wxSize& size = wxDefaultSize, + long style = wxDEFAULT_FRAME_STYLE); + virtual ~ASSDrawFrame(); + + // event handlers (these functions should _not_ be virtual) + // OnSelect_* for single items, OnChoose_* for many-choose-one items + void OnSelect_Clear(wxCommandEvent& WXUNUSED(event)) { _Clear(); } + void OnSelect_Preview(wxCommandEvent& WXUNUSED(event)) { _Preview(); } + void OnSelect_Transform(wxCommandEvent& WXUNUSED(event)) { _Transform(); } + void OnSelect_Library(wxCommandEvent& WXUNUSED(event)) { _ToggleLibrary(); } + void OnSelect_Settings(wxCommandEvent& WXUNUSED(event)) { _ToggleSettings(); } + void OnSelect_ResetPerspective(wxCommandEvent& WXUNUSED(event)) { _ResetPerspective(); } + void OnSelect_Help(wxCommandEvent& WXUNUSED(event)) { _Help(); } + void OnSelect_About(wxCommandEvent& WXUNUSED(event)) { _About(); } + void OnSelect_Undo(wxCommandEvent& WXUNUSED(event)) { UndoOrRedo( true ); } + void OnSelect_Redo(wxCommandEvent& WXUNUSED(event)) { UndoOrRedo( false ); } + void OnSelect_Paste(wxCommandEvent& WXUNUSED(event)) { _Paste(); } + void OnSelect_RemoveBG(wxCommandEvent& WXUNUSED(event)) { m_canvas->RemoveBackgroundImage(); } + void OnSelect_AlphaBG(wxCommandEvent& WXUNUSED(event)) { m_canvas->AskUserForBackgroundAlpha(); } + void OnChoose_Recenter(wxCommandEvent& event); + void OnChoose_RecenterToBG(wxCommandEvent& event); + void OnChoose_Mode(wxCommandEvent& event); + void OnChoose_DragMode(wxCommandEvent& event); + void OnZoomSliderChanged(wxScrollEvent &event); + void OnToolRClick(wxCommandEvent& event); + void OnChoose_TBarRClickMenu(wxCommandEvent& event); + void OnSettingsChanged(wxCommandEvent& event); + void OnClose(wxCloseEvent &event); + + void UpdateASSCommandStringToSrcTxtCtrl(wxString cmds); + void UpdateASSCommandStringFromSrcTxtCtrl(wxString cmds); + + void UndoOrRedo(bool isundo); + void UpdateUndoRedoMenu(); + + void _Clear(); + void _Preview(); + void _Transform(); + void _ToggleLibrary(); + void _ToggleSettings(); + void _Help(); + void _About(unsigned timeout = 0); + void _Paste(); + void _ResetPerspective(); + + void UpdateFrameUI(unsigned level = 0); + + // the canvas + wxApp *m_app; + ASSDrawCanvas* m_canvas; + wxAuiManager m_mgr; + wxString default_perspective; + ASSDrawSrcTxtCtrl* srctxtctrl; + + // config + wxString configfile; + wxFileConfig *config; + + // toolbars + wxToolBar *drawtbar, *modetbar, *bgimgtbar; + + // zoom slider + wxSlider* zoomslider; + + //library + ASSDrawShapeLibrary *shapelib; + typedef std::vector< ASSDrawEngine* > DrawEngineVec; + DrawEngineVec libshapes; + + // menus +#if wxUSE_MENUS + wxMenu *drawMenu; + wxMenu *modeMenu; + wxMenu *bgimgMenu; + wxMenu *viewMenu; + wxMenu *tbarMenu; +#endif + + // dialogs + ASSDrawTransformDlg* transformdlg; + ASSDrawSettingsDialog* settingsdlg; + + // colors + struct + { + wxColour canvas_bg; + wxColour canvas_shape_normal; + wxColour canvas_shape_preview; + wxColour canvas_shape_outline; + wxColour canvas_shape_guideline; + wxColour canvas_shape_mainpoint; + wxColour canvas_shape_controlpoint; + wxColour canvas_shape_selectpoint; + wxColour library_shape; + wxColour library_libarea; + wxColour origin, ruler_h, ruler_v; + } colors; + + struct + { + long canvas_shape_normal; + long canvas_shape_preview; + long canvas_shape_outline; + long canvas_shape_guideline; + long canvas_shape_mainpoint; + long canvas_shape_controlpoint; + long canvas_shape_selectpoint; + } alphas; + + struct + { + long origincross; + } sizes; + + struct + { + bool capitalizecmds; + bool autoaskimgopac; + bool parse_spc; + bool nosplashscreen; + bool confirmquit; + } behaviors; + + void LoadSettings(); + void SaveSettings(); + void InitializeDefaultSettings(); + void ApplySettings(); + static void wxColourToAggRGBA(wxColour &colour, agg::rgba &rgba); + static void wxColourSetAlpha(wxColour &colour, long alpha); + +protected: + virtual void SetToolBars(); + virtual void SetMenus(); + virtual void SetPanes(); + DECLARE_EVENT_TABLE() + + wxHelpController helpcontroller; + +}; diff --git a/assdraw/src/assdraw.rc b/assdraw/src/assdraw.rc new file mode 100644 index 000000000..1ee736c0d --- /dev/null +++ b/assdraw/src/assdraw.rc @@ -0,0 +1,101 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +APPICO ICON "../tsukasa.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +ASSDRAW3_ BITMAP "../bitmaps/assdraw3.bmp" +NEW_ BITMAP "../bitmaps/new_.bmp" +SRC_ BITMAP "../bitmaps/src_.bmp" +PREVIEW_ BITMAP "../bitmaps/preview_.bmp" +ARR_ BITMAP "../bitmaps/arr_.bmp" +M_ BITMAP "../bitmaps/m_.bmp" +N_ BITMAP "../bitmaps/n_.bmp" +L_ BITMAP "../bitmaps/l_.bmp" +B_ BITMAP "../bitmaps/b_.bmp" +S_ BITMAP "../bitmaps/s_.bmp" +P_ BITMAP "../bitmaps/p_.bmp" +C_ BITMAP "../bitmaps/c_.bmp" +DEL_ BITMAP "../bitmaps/del_.bmp" +NUT_ BITMAP "../bitmaps/nut_.bmp" +SC_ROT_ BITMAP "../bitmaps/sc_rot_.bmp" +ROT_ BITMAP "../bitmaps/rot_.bmp" +HELP_ BITMAP "../bitmaps/help_.bmp" +TRANSFORM BITMAP "../bitmaps/transform.bmp" +PAN_SHP BITMAP "../bitmaps/pan_shp.bmp" +PAN_BG BITMAP "../bitmaps/pan_bg.bmp" +//PAN_BOTH BITMAP "../bitmaps/pan_both.bmp" +ADD BITMAP "../bitmaps/add_.bmp" +CHECK BITMAP "../bitmaps/check_.bmp" +UNCHECK BITMAP "../bitmaps/uncheck_.bmp" +DELCROSS BITMAP "../bitmaps/del_cross.bmp" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/assdraw/src/assdraw_settings.cpp b/assdraw/src/assdraw_settings.cpp new file mode 100644 index 000000000..860cdcd18 --- /dev/null +++ b/assdraw/src/assdraw_settings.cpp @@ -0,0 +1,190 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "assdraw.hpp" + +void ASSDrawFrame::InitializeDefaultSettings() +{ + colors.canvas_bg = wxColour(0xFF, 0xFF, 0xFF); + colors.canvas_shape_normal = wxColour(0x0, 0x0, 0xFF, 0x99); + colors.canvas_shape_preview = wxColour(0x0, 0x0, 0xFF); + colors.canvas_shape_outline = wxColour(0x0, 0x0, 0x0); + colors.canvas_shape_guideline = wxColour(0x66, 0x66, 0x66); + colors.canvas_shape_mainpoint = wxColour(0xFF, 0x0, 0x0, 0xCC); + colors.canvas_shape_controlpoint = wxColour(0x0, 0xFF, 0x0, 0xCC); + colors.canvas_shape_selectpoint = wxColour(0x0, 0x0, 0xCC); + colors.library_shape = wxColour(0x0, 0x66, 0x99); + colors.library_libarea = wxColour(0xFF, 0xFF, 0x99); + colors.origin = wxColour(0xFF, 0x0, 0x0); + colors.ruler_h = wxColour(0x0, 0x0, 0x66); + colors.ruler_v = wxColour(0x66, 0x0, 0x0); + + alphas.canvas_shape_normal = 128; + alphas.canvas_shape_preview = 255; + alphas.canvas_shape_outline = 255; + alphas.canvas_shape_guideline = 255; + alphas.canvas_shape_mainpoint = 128; + alphas.canvas_shape_controlpoint = 128; + alphas.canvas_shape_selectpoint = 255; + + sizes.origincross = 2; + + behaviors.capitalizecmds = false; + behaviors.autoaskimgopac = true; + behaviors.parse_spc = false; + behaviors.nosplashscreen = false; + behaviors.confirmquit = true; +} + +void ASSDrawFrame::ApplySettings() +{ + wxColourSetAlpha(colors.canvas_shape_normal, alphas.canvas_shape_normal); + wxColourSetAlpha(colors.canvas_shape_preview, alphas.canvas_shape_preview); + wxColourSetAlpha(colors.canvas_shape_outline, alphas.canvas_shape_outline); + wxColourSetAlpha(colors.canvas_shape_guideline, alphas.canvas_shape_guideline); + wxColourSetAlpha(colors.canvas_shape_mainpoint, alphas.canvas_shape_mainpoint); + wxColourSetAlpha(colors.canvas_shape_controlpoint, alphas.canvas_shape_controlpoint); + wxColourSetAlpha(colors.canvas_shape_selectpoint, alphas.canvas_shape_selectpoint); + + wxColourToAggRGBA(colors.canvas_shape_normal, m_canvas->rgba_shape_normal); + wxColourToAggRGBA(colors.canvas_shape_preview, m_canvas->rgba_shape); + wxColourToAggRGBA(colors.canvas_shape_outline, m_canvas->rgba_outline); + wxColourToAggRGBA(colors.canvas_shape_guideline, m_canvas->rgba_guideline); + wxColourToAggRGBA(colors.canvas_shape_mainpoint, m_canvas->rgba_mainpoint); + wxColourToAggRGBA(colors.canvas_shape_controlpoint, m_canvas->rgba_controlpoint); + wxColourToAggRGBA(colors.canvas_shape_selectpoint, m_canvas->rgba_selectpoint); + wxColourToAggRGBA(colors.origin, m_canvas->rgba_origin); + wxColourToAggRGBA(colors.ruler_h, m_canvas->rgba_ruler_h); + wxColourToAggRGBA(colors.ruler_v, m_canvas->rgba_ruler_v); + + m_canvas->color_bg.r = colors.canvas_bg.Red(); + m_canvas->color_bg.g = colors.canvas_bg.Green(); + m_canvas->color_bg.b = colors.canvas_bg.Blue(); + m_canvas->color_bg.a = colors.canvas_bg.Alpha(); + m_canvas->PrepareBackgroundBitmap(-1.0); + m_canvas->Refresh(); + + shapelib->libarea->SetBackgroundColour(colors.library_libarea); + typedef std::vector< ASSDrawShapePreview *> PrevVec; + PrevVec shapes = shapelib->GetShapePreviews(); + int n = shapes.size(); + for (int i = 0; i < n; i++) + wxColourToAggRGBA(colors.library_shape, shapes[i]->rgba_shape); + shapelib->libarea->Refresh(); + + m_canvas->SetDrawCmdSet(wxT(behaviors.parse_spc? "m n l b s p c _":"m n l b _")); + + UpdateASSCommandStringToSrcTxtCtrl(m_canvas->GenerateASS()); +} + +void ASSDrawFrame::wxColourToAggRGBA(wxColour &colour, agg::rgba &rgba) +{ + rgba.r = (double) colour.Red() / 255.0; + rgba.g = (double) colour.Green() / 255.0; + rgba.b = (double) colour.Blue() / 255.0; + rgba.a = (double) colour.Alpha() / 255.0; +} + +void ASSDrawFrame::wxColourSetAlpha(wxColour &colour, long alpha) +{ + colour.Set(colour.Red(), colour.Green(), colour.Blue(), alpha); +} + +void ASSDrawFrame::OnSettingsChanged(wxCommandEvent& event) +{ + ApplySettings(); +} + +void ASSDrawFrame::LoadSettings() +{ + #define CFGREADCOLOR(color) if (config->Read(#color, &tmpstr)) color.Set(tmpstr); + #define CFGREAD(var) config->Read(#var, &var); + config->SetPath("settings"); + wxString tmpstr; + CFGREADCOLOR(colors.canvas_bg) + CFGREADCOLOR(colors.canvas_shape_normal) + CFGREADCOLOR(colors.canvas_shape_preview) + CFGREADCOLOR(colors.canvas_shape_controlpoint) + CFGREADCOLOR(colors.canvas_shape_guideline) + CFGREADCOLOR(colors.canvas_shape_mainpoint) + CFGREADCOLOR(colors.canvas_shape_outline) + CFGREADCOLOR(colors.canvas_shape_selectpoint) + CFGREADCOLOR(colors.library_libarea) + CFGREADCOLOR(colors.library_shape) + CFGREADCOLOR(colors.origin) + CFGREADCOLOR(colors.ruler_h) + CFGREADCOLOR(colors.ruler_v) + CFGREAD(alphas.canvas_shape_normal) + CFGREAD(alphas.canvas_shape_preview) + CFGREAD(alphas.canvas_shape_controlpoint) + CFGREAD(alphas.canvas_shape_guideline) + CFGREAD(alphas.canvas_shape_mainpoint) + CFGREAD(alphas.canvas_shape_outline) + CFGREAD(alphas.canvas_shape_selectpoint) + CFGREAD(sizes.origincross) + CFGREAD(behaviors.autoaskimgopac) + CFGREAD(behaviors.capitalizecmds) + CFGREAD(behaviors.parse_spc) + CFGREAD(behaviors.nosplashscreen) + CFGREAD(behaviors.confirmquit) + config->SetPath(".."); +} + +void ASSDrawFrame::SaveSettings() +{ + #define CFGWRITE(var) config->Write(#var, var); + #define CFGWRITECOLOR(color) config->Write(#color, color.GetAsString(wxC2S_HTML_SYNTAX)); + config->SetPath("settings"); + CFGWRITECOLOR(colors.canvas_bg) + CFGWRITECOLOR(colors.canvas_shape_normal) + CFGWRITECOLOR(colors.canvas_shape_preview) + CFGWRITECOLOR(colors.canvas_shape_controlpoint) + CFGWRITECOLOR(colors.canvas_shape_guideline) + CFGWRITECOLOR(colors.canvas_shape_mainpoint) + CFGWRITECOLOR(colors.canvas_shape_outline) + CFGWRITECOLOR(colors.canvas_shape_selectpoint) + CFGWRITECOLOR(colors.library_libarea) + CFGWRITECOLOR(colors.library_shape) + CFGWRITECOLOR(colors.origin) + CFGWRITECOLOR(colors.ruler_h) + CFGWRITECOLOR(colors.ruler_v) + CFGWRITE(alphas.canvas_shape_normal) + CFGWRITE(alphas.canvas_shape_preview) + CFGWRITE(alphas.canvas_shape_controlpoint) + CFGWRITE(alphas.canvas_shape_guideline) + CFGWRITE(alphas.canvas_shape_mainpoint) + CFGWRITE(alphas.canvas_shape_outline) + CFGWRITE(alphas.canvas_shape_selectpoint) + CFGWRITE(sizes.origincross) + CFGWRITE(behaviors.autoaskimgopac) + CFGWRITE(behaviors.capitalizecmds) + CFGWRITE(behaviors.parse_spc) + CFGWRITE(behaviors.nosplashscreen) + CFGWRITE(behaviors.confirmquit) + config->SetPath(".."); +} diff --git a/assdraw/src/bitmaps/Copy of assdraw3.bmp b/assdraw/src/bitmaps/Copy of assdraw3.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4fe735e5bf1bc4002571d9e50ea67e9f68af2087 GIT binary patch literal 86454 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 AG5`Po literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/arr_.bmp b/assdraw/src/bitmaps/arr_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..34ec029d12add935038ec13030ba1ee9c85f6536 GIT binary patch literal 776 zcmaiwF=~WR5QbkOLLfy#8gm5k04c;Ookb4dIX2)CHr1xKkcAw=PDo?1LB$s&Hi95% zA=+#zK`?*zvF^SXGy^lvKl9D_n8&9pi5%voXUBgJB}ks68Ns`P7OY5+m;o|vQ*XI7p~VYJi@8X<~2zYEO%WOu%akg zmT?3xz?-J&`@XL0;8&|>zAq~9Us+~Z7GPPHgCI!LGz>$LJ8JMaOF}7Nj7&CnBJlg~ zeNhx2yWMvCX4^JK;y6C;6TjexVZZ@B&tq^DMIv;j!H?tE_x-<{*%0Hg+%SxVXH0(p hkNYgYd4iFE2TUnFFD~TZaqtB|Xw`KsRQ^gregG`z>f8VT literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/arr_1.bmp b/assdraw/src/bitmaps/arr_1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..14f71ee262bdbf90349b6a4192a7773ec0faf601 GIT binary patch literal 774 zcmajbv1)@r7zc1$v1_~8M-U&NgE)0_$OHHsDfkHKl+F$k=tH;(SsW5n?m&8rAP72$ zF6kr)_TNZM?uz!{&ilUm-RJSWt{!CRJYKMBm`|9Gn0L}MR!JiF=MT5+XGU3;aJsJ7 zbv;9_=`71!*Og@%zHQs<0l#aSW=s@Cp=rM_+-%-?!bRKd2O$K-Jo@*fjNrc*vuzt#QIs$Y(=?5uNOYGN9&I6%0^`aw=2`@Q`ab1(emop@ zyHCe)a3x8SOFi)m-}gNl===VN<2V-4O$q=5TzJOx2YB>n`OOo? Z1|FDFdYfF>!=vE~Ksc(ZO33^Te*mQX>Ye}q literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/assdraw3.bmp b/assdraw/src/bitmaps/assdraw3.bmp new file mode 100644 index 0000000000000000000000000000000000000000..debaf3c8ba7af02954fce4a505d249fdac00f2f9 GIT binary patch literal 120054 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�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 F{{Xl53XA{% literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/b_.bmp b/assdraw/src/bitmaps/b_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3046a904840686fb8a5f1fded2d8b9e7cfa29e2c GIT binary patch literal 776 zcmbu3K?;B{3T1NHuG_5?rQ(Z0nV@Ifd^=KuG|0s literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/b_.cur b/assdraw/src/bitmaps/b_.cur new file mode 100644 index 0000000000000000000000000000000000000000..117906dd9c265a87b372c16f74027af59e493fc1 GIT binary patch literal 4286 zcmcIn3slZ`82*@H4(7}_Zlhy1?98y0P`V;Qw{uDhtFHrTgXG`+V!;n?8zK=RDv4>-WDrzxREg_j&)HK+s#zOCTd7;A3w= z@DPDuxIiG7#NZ`?3WLw`C;!QaKL7JUM1psrxw*M-RaMpJd3kwXXJlkdd-(9-4_yIY zJ3TiycVcB_<*=rvroL@$ZN1*bBkdgSdHM3?pv=t7Y3I(JGYSX@*x=*ivzg)08T|eI zZKI;1jMLN8<*KWzKWk}e`G`5*YY(lht-UKMDuyQ{Bxvo}vExrmOUsKUCMMUqf>o{%0QYwPIw^XHc+Dk|2@nKK6v zDk&*R1OVn&d%<%B-XEAA7O58p15Smk{sj*d$6*y`a2{f#Hy&M zNalTNAdO#HS@{LHy}Z13A}{C9pZ}3zCcWK!`i_o{ff^bb#qIpp0s{kkg8v2A z_u0mc8$-ZRT3Y%=ZEfu!#=v$A!akCck`_5TJNqqIu%NIf`Jq47R>hh%YhpQ`UsF?4 zRoRPVCH?63?b}MBp`n(Xqj2aF{eph&iTJ_K&wHt_um524=FP#Zf!|T9$LXYD_5>e3JMBZS6Em$zB~1Yp5^7`o6&pFw}FAdeeUrzNnlM|Tl+C| z0=+C=yg1j^);0_tP*PGdmifEWe?nPV*|r%oW;CM@kqc>r>#cb_2!v zYUcNY#-_g1+}ww_Pf)8j=P$Icr>B>JIk$cL_Jg~2?b_q%>G=w%HSz-=K>s6Wn1{i^ z!E4{@Kj3a?=tpJc!zed@92J+2q}tj6t-Oyn_=V_w_z-fdqob3}d&$Ip+L6g?S(wjw z2LLy&vA4Gmj*gCA&hui7RR4h|lvfU=YpFBobfg|#OjM)7;?b=Qjs3tM5fNdAo`Uz! zoH?_R(SSZgF5dJXtl=Eyj+U0z6U+gA7qm-DOH;smAbPrk?;ziZ`}nD4WbeA2_8t9& z?xjy@t*skSUS2)|J;i+-fPRLzb_MJ`>HN+X)=1w4|1Q8J(0W) zS<`Zh0CM_m8*&(Kci<&K@qLWfVbcH~m3Tv5VTCJRRRyP&l?M`|;$AyZ7X8-Mp!il9Hm<72LXYOZm~G zN8h7<4Gj(bJH#!12Mi8dN`WCpwC8{gt+4c`SsKZtz5EPq+vP<5Cs(wciP3JldPAY^ zY2Nsns;Z%qz24dPy1Kf7?QwUkxtQPF`Y4&L^(KAOQzSp{I*p(Hgr+K`iSl5&)`#qz zcamSwPxLTza$8;fCy1k~V4ZdTd&Ai3n5gE6SynBTWTrl)Zx!y-LW3C5y0wEBc^@~U z^o(z)zP^74?TX|0&?BCG-cRJ*2L8wpCbww&J8A(k| zANM#v`Ytvk)lB2sDEi#Ov)wcU7JW7WiWlf@jqA^rnF;o=0wBG&DxW)IO_qnd3 z{O;UmC(^+t7Z18}U4g2FgGHVUUU;PVo}NA;ujqfz{p-oV?4-zp;Dziz{~PD)7?^_p zF7r>a%K~SHvIq$c%9Pad8shbTufcWBlpt{)*GgPd&(EvL?L`IrXn?O4A+P=a8a@L5 E05zbfxc~qF literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/c_.bmp b/assdraw/src/bitmaps/c_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8a37dae867d6be4f4791b6912361795a637de096 GIT binary patch literal 776 zcmb`BK?;B{3((}iu`o~6K=o9G zkr_B@AatVGVA}tP*~IHU!`|h?h{D1+n+^(K`|Mcj>XdhYv)}oW|0pBRsdb+btBe*Y bb|ToOlDW-}x3jJ$X^*?j+4B9ep_~UUQ`XKw literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/check_.bmp b/assdraw/src/bitmaps/check_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..41ca19aa3d69b56c0cb7a33245e35fea8559ac6b GIT binary patch literal 774 zcmZ?rWn*Rl12Z700mK48%n!tj3=%*R0*>%8Fn}-&9H0wmXlOWb;R=3)>=2-O2M34$ zxB<{8JnDg-L(_^!JuDD!~qC6aoW+*@ywYsZEbBl zJUqO-ylb~?he~0ifC_-hASxjYAc;*ORBZXWjgpd*Z{NOs`t<4DyLS=x;?R?oSJ2ql z2v?tyk`f*ji$m>!-G`4G8XDfdeH*TR&z?Qv;^HeeY{sf(;>3wPJw0E(d;zKl>H@mw z^5x6f+1Wv%;XC#pLesM6@Ugb`_HW<5LDU0X4pD#n_;DE-nFY&Mp{WOQm#X(vvc0GhvG!2%-_Q;1?%DuB|+ChR%8Fn}-&93Ts9+VHXn^^DB?it_8s`|VKVQ~5p?m&=;y{NdA{b0 zckf1#igVrHkoFDJohQ%yk8#+nrj(lDeI72`kn+XeWZtpkC?>#Vf#P8{n{~CCO2eP* z*!n-m=aPxh^x2)*6+?6#KmI?`cB_fOL{q~lmSzirZ8pPA!^POL_P@2!bUW)cA+{TF zsey|hIs8A%=YoyJQd`q`PL@l{A|An|Fd1_@|9d!YcedZ&n)dzBf&bn4|7~rzcIIMn z|K1({tD;{y*sgPQ*j}IX5pGmd%2#*$&HW`PT9!@yUm5el&T6@<{npvdC{jR;J2(6f v^1jfK{ReKy(#ii_o%Z^;9^SYVT`^qEqF#j5@gx7MVqWdpf)If#!(;#eA5HIS literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/help.bmp b/assdraw/src/bitmaps/help.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d5636e9aa2cbf0489f307f7b3e7db32fc7264a68 GIT binary patch literal 1318 zcmeH`-%Aux6vw|papO+YwYjBPT9dl#v^rXvJ35c{5`htE4@GZ5P(jdBgh7ihB9sq; zAPTWyiGe=oLk2;OAw*@d1QJ^d8)dQC4<>2T)O9*DThRZIhkMSwpL_1*o^v^;r8T?} zfkZa|2CWK71!(~`1^X^pP01^5`i?&m185)?%THu!3CD5Zq#U?UhtYK;gd43@=sjkj zzb%4+_6P<~RAcC*iQA`4jGV5;{j&|2I^T##7ovD_(Zch~hw!4~FcQ~}BGGvqvpr|< zs`ng{{g?3O<`tykSCPKcjklxM@qVHYb9V>uVR8_ehr`G?Blt8uhR=`3@nz;7z9k;O zot?(_V&ggyov1 zmDB`SPh7%PuBv=5dF$&WD^mowLqhN>LQ^s>gvz&*DLPPRRtK!uI|Lmd+?V(3HXYbZ z7?y0S5Q?%tB-Cw$6_S;6wX)`5rQkZDPVbfR!ri;J@KVE%Qlof66i;c`2usV-eP*)O Yj2Lv+(s%mEWPp35UL4@x{L}z{18nP@UjP6A literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/help_.bmp b/assdraw/src/bitmaps/help_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..eac6f2e5eb678eafb65feadc64fcb829892980fa GIT binary patch literal 774 zcmZ?rWn*Rl12Z700mK48%n!tj3=%*R0))637(f^X4p0nYyxouyF7x1OJRkjL`1}ik zfNY33nl7LqT-oaX|KSLz3nYH#KTrW&OK)8j#5B0F{r-_a1mQyMf@uLNhL{Xj3}hgy zN9Y1;ff$ag7%Lm%Frcfjst1}sTh|9oBgF4m)qkJTgsBB+1upY}CSz*BsvcD!~x2HF*bn5NNmcD3984V t08I*FKAIpN?g&x;pFs!`2t@i9t{zX&L8O2xkr6@la9u>IBvm~F0|2Sy;uQb@ literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/m_.bmp b/assdraw/src/bitmaps/m_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5e5b4c8610a1893cacce226dbc9b2b8dde127ea9 GIT binary patch literal 776 zcmZ?rD!~x2HF&IG105ahWI2+A) zxOxZ!q6{vGrWnYDOOc@-q8-@~{N@v{9@AAo5lqA3R+FI~ZW^-3;ev1mrYxoiQ1O2T XA-EP|7{sdwszgS_7*137FfafBQ7W=3 literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/n_.bmp b/assdraw/src/bitmaps/n_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..47c4ad7f2be9cbeec5ff75e88ec98af4f8300adc GIT binary patch literal 776 zcmcJJ!3lsc5Cm8K2tJg&EWy8E0sigT8f-|H1eS#a5yithhPz?Sy0r~rEu0g3VE60_ zBPVdwKqi2AhFI4eD!~q%uXKcX3!vkpbw1rDB zEiWr8d-3AMe^l`P{riN31Su&gOf4%mZk{}O@;3~yV#SL3`g%+)m!CY(%F5cjdGiez zxN_yn)vH%eoH&t~nAqFfi>YP(?!9?=c~hoL>Feuj1Op&9K0Y4ki^|H%Y15`*)#B#n z_UY57&!0bk{P^+t^XIp3-#&ZxY*<*>tXZ>W&YWp(ZoYK=Mr7{;ZGmff_wL=3Cr_?l zzkce}se*z6pq84NnylQsJ*Up%((?NC>-+cb1N{Q@2vE!N<;&C4)8{N(fvg_LhT8&n x)%o-1AzG$Rof;ewiKZS|3*1#ej{vpg=jQ`M5>q`yOF&>yCN_XoJrxBR7yxHJ?=}Dc literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/nut_.bmp b/assdraw/src/bitmaps/nut_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f76644e38be090e22b53f724db2c2b1c243c388d GIT binary patch literal 774 zcmZ?rWn*Rl12Z700mK48%n!tj3=%*R0*>%8Fn}-&93TQLU%ov%d$Fu+2RnNM9_0{a zDk@zP5^W(Nv-0zo+Sp9ORFAAI!7-;nh6kd?$!SV$?Mk?MWMx3pX3yTdwy(2QRdhj4 z3Q*1F&AWw!T6XT-3shEExD;+FP#MI8<#pxt!VGO1;(K@P1d5cGFZb}624rw@Hu(9? z04i&g;Q}gvCFe`Llsu z1BM1fF^~khXWgV;OvMn97FAK8OMvb{R*$K8SxpIC0nn#F|H8R&{{lsTu4cm(l5j4Nfvg_L?a-HpdlSe7#t|^4kktdN18TvAgoT@dX#iP00N5XIj{pDw literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/p_.bmp b/assdraw/src/bitmaps/p_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..67aeafa72c8792ba6eab9499c8424a228d02d06f GIT binary patch literal 776 zcmZ?rD!~x2HF&GeU#^6*BaTPIc zLskiA1-81V{KF7{X&(%)Z;UocTH+s-dr@TG^I5y!rMXz zXgFLKTs>R_&731kCvI6h?ceWTAManizj5}-<yl(403< zAFi9#_2=uS4|gx+g}cw~YM9&C=4+~jW)9pXK-22$|3eUv4G}-OY|{V#{~z7Bw0>si zyw2LAD`xCky?FQXxqok+AkCcRAaj7=*_~@IZ=HSg=u=qc8MDyy7Y<(`$(&=$CqvBn z`Sm>zr4^s_S@2e+^t873L6XdY_zP&%uP+~gsCW9M$%kL~FMXeuy9>=6tbq&e17$vzOlOop$-n%a6$Bzyle`fa^sTJhpTq&?q1}4km#NV9JD`qpM~htAw+$ Nngf?5o-yF&004^>(D(oV literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/pan_both.bmp b/assdraw/src/bitmaps/pan_both.bmp new file mode 100644 index 0000000000000000000000000000000000000000..84eb52fd04e479f512e58564a8e3dfacf78243a4 GIT binary patch literal 1134 zcmZ?r&0}Ez12Z700mKqO%n!tjU>1-81V{KF7{X&~+=GWB$Ng$h`dR*qz+1M;>ZvM}(X+nHF5Rqq2UsTkj)Ks7$ z5CnA6+S#)qM&Ys-Q+#%Ld5eb!+z_}?5Z?jS;{rg74lkXU;$;RzN#+`fBK&BE0J%Wt z0UZfc2{Z)aFbKQ8{yzjk#1Ai>@caGi^DCz2iwa^I0ujj+7A9!Uk);#2ES~o7_pisN z4pb=0Vj2YuB1i(j?;fBzZ=OC}H>>N<*H2eAuS$WrDp*N6Nm3|ZP7Z501IGdt&X)^6;m&XbbN5fPr|?V4nu3=a^9zku4Y0-)^C2p7BFP+xd;b0V3o+`^)339a-tC=s`OV9Zq?xmHBG4!x TIu0g*3}DKHprfm1zykpQ*PyRz literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/pan_shape.bmp b/assdraw/src/bitmaps/pan_shape.bmp new file mode 100644 index 0000000000000000000000000000000000000000..32ada0ae3d649175b6017150b02972a3e27c6735 GIT binary patch literal 1434 zcmZ?royE!k24+A~1Bm5-m>-B4869=j^A`~NG90sDy1_lVwn0vWVaI+zd`vtL= z11y1I1@sbTKskZT#sy#wH~rt-ofZDZT9YkXSio1c91-81V{KF7{X&-NJe`+!!@`A_reRf&)%-m>ca~~t&9AEil0Y_^QN){5XJfOlx%qzljtTMc zK!j-y&?u|`S!G{T)TGo@pdmnXH)bo)No!}%Mpghc0nP=o>+AnR5VH8}^70lB4~QWU z@|y25G~Xes$7;^<{{Ct+GoTxg%>lB3&I39U*$v2SO!wqTNnQ3_h-L_odn0gFrm!%w zUSu|=IosB*EmBdr6}BGPsOteMf&M~H06@Rs0&w$zL0Vy80MvUkc+I@p+D0d*d^tI^ oaE2=eGJpmmbCKCVT|m7#5)wc|fJPxpVY2aY5bY$FKn{_d@(H<%gP^#KC~gCTxM0MyCl>@!5XFNRM+F5@P(;NI(eb1Q z6_;-DG8X}{ka42I!w z$d&{Gfo``;%kR4_mrGfe@ly(RIvu~?&-Bq~v|g{XGLy-KLZNgz&5+4tf(BSQ7!1s2 zGbxn8f8N+p}kuGeem z!{IR1;0Dp;d_EtK$GGdum2C=fhl*B-!ou?RE>Q r(P+fuaU|#SnJKkNtJT6a?RL9hFj%YAv;r+sDwXik&@e{eSwemRMtj6v literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/rot_.bmp b/assdraw/src/bitmaps/rot_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b2c14a9065c762907a37fa72a54187a778d283bb GIT binary patch literal 774 zcmZ?rWn*Rl12Z700mK48%n!tj3=%*R0))637(f^X4v+ z{_)^~N*x9YP&q*(D2K1m%v4k8P_*#Kc1R*BDd`3E~rONWS0Qld~5GvWMy#nz4KQ<3Zhc3 zELsT>gs6Xb`377KoN;Br^5g0GM`P2VC@%ec=R}D5JNu8omBASg&R@Aad+~{^;-hit z#}c#7cTKu;;ON6I2){G2}Cha@Zp(DXR4ZjR-7)YzrJ|Y!&4XFD&dUBmr#6nZ||Yg6^%eG(D;o= gdV2jfTrpP0lXI7EELeWFvJn_}M`F^jsv%Dh0CwW{&;S4c literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/s_.bmp b/assdraw/src/bitmaps/s_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1e8afab554fc392c634052869b72aec8d0c3b80a GIT binary patch literal 776 zcmbu3K?;B{31FrLV8!Y?pn4gi~_hdtt8S#XGsFnXEO8D>zbA4^2Z>S(Mu=qS%8Fn}-&9Ka2BY}l}N#*9S`4P6NdzGh~a z3U_YYxNhdm#Z65!va))Eg8JRv=eWBsb#duYP^foy-oJl8M9bbidm1t`lQcAX-Q8!o zyDxWl-{S6m(B1vGyZbRWx23A8yREG!0|l2ahg*?sX12)G6sQ0Vt5*vv{-nwKtLtIoX}h8D<( z9dU7iD~2$pxVvv&vSdM)!v84@K(xV~;gUJSeJ}!QL{kjpE_8Qa(bEH0e>jTaC|>pJ z+}-DvmcrFv%VF4wSN$$`_X%-vaP=?i8P;H^KLC!qW$x}%9UUu-j6Afp;p+cPWLRL% zaKoJ8j5)&`b?({j?!ffW=IveS?Hyxn9pd0nmY=_7&6+8xDtpVtW@RW9gc`W3DW^UdQoo8>`60EeUW%?8O;Iftu*`@~0l?S3Jk4SF_5;8K^GYF~IZBY& zGeK7irDUmcZI2*OTKS eZ)`yd5PL{Raiv~_O^Q4tfc*vU6(C`!Evi0hSx8<0 literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/transform.bmp b/assdraw/src/bitmaps/transform.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8ae24e5adedc729b62762af2530d9e2792564817 GIT binary patch literal 25144 zcmeI1O>SIA41|?zvk7vFoFMQz`&=UD2#~AhCeBc;53FLd=e;-9%y^6^L*ietSWQZ@ z0sr~em%rcMKmNFU{r&R!-{tem<@2YvUoT(Z-hRH+fBR*8z9-_#Kj)v<-oV#?&p)ra zf%wjQRqdzlum7I+d+qOn^L{_l{rERc_oi#L#P35T{&AivQ~%xywr6c4&^ROg=Wnjl zdHGHI@sXL%+lT5p@10X+>OXRBKWo358F}8X-|6RN=bcyXZ3G=NvffXT_-Ug*qt4r^ zcRi2Gq2E=Gh}vyxYImLBvCml5D(C3(?CO4joq4_2Rlwe5zn2{P=a+qk`W5T=yJx+F zb>H?4uHA3$=KZ&6S$93Ef_~kY`tysADN`=@HL9G~YO^BVFj($+znB!-dQ0|0$9q^` zHpg`JmMk+f&MD#P#?<%9Jbm;AD-+UQ;~gD6pUy-twsJB*nYQj%U*~c1;CY?MEs-jB zAGXfY?Uo3h8V@G+nAJJ~M9phVceOE_oZ{E{fzlp)?A+@!uH7#8$3S$-;K0tvs#00p zhojm(oam?5r;<$@c>NekvaWH>DtB=BD>Tv?{;(b~jcmmCyx(~F`;-s;I$7U0Fhk$< zug5s&8NL^22{IuO~Nidyd5Qb4MR%bA`XHaC1(5!Pk^M0v2G-ae}_xg0N?BHPvsK~0nciJAD@-nyYQ)G|OtaCl% ze&aWMRb6A%yvL7sDMyWi1BBxCaDOX?xjoM@HO}>%`}Oa*f0noV#yt8Q70$`rp4+iK zCbQ0U-Y++EKhJkwe`i1Ie%;(IcfY+HWoD0$R>@rS`h1VTtgLh0{`1#Q?XxQ_kIwV< zN2_Em@Adf}fmvDSy7hj+Qa@tiF)b^N_sWOjXQ*T@@A55q1ZHKO>$dx?C$1|iJ&(-u z_D8GCx#8(Yy#AP)=+2$pcE9m2an%8Lu56EnKLu0jNWPkO^(hQldl$BwUu+! zLBSI7K$6MrfyE}mJ?d^fnrG-@%%(T?Wczf2;qu(>yjSr%;i*F!EWe#dp~ezo(p?l-GX{kf6! z*pBbEi|DDgPNbf5J2vm42kX`7_wR*k_Y02XnR`1m6+bX!v4hL|wdNI%z*W14leg15 zzfUuP_K%^2?#_~PHNoPq&`4|eYVnz_@%nzCutSkXssUf?w$l(*Flx!ujZJjNRuv{XxGQH?2iNL;t4XP8h3NBhwldXt?%2+B z9GV;Z(2Z$zoBtRhxtZJdC$6u1*13Mv`{hp3%kN#y`F1`p(iy()TNV8@PrrfOFG?J4^a%FEooTai6Nv(ELL`-PsWy2h$`udf__&%wak6}OlB zTX4+nIgY7uuIJsaf6INdyxlkE(dQ^|PUiO9j_om-b*}S%xtaTUzVkZgykFhODrS$z zHnw8hpJ#60aZHVK)t|qp_I#t#*HYI$RN^1!sWSENtzdiBHUf<^^1L6rxtE=HUMFoM z=$Mh`{n*XD?7Z_jX&XVujP(0~JMF7%;Jn|fT&mZ3-ml-wt7Omny~?F}o#*}fy}U~H Tyx*%_s@EC!JO8}&2HyS$98h4; literal 0 HcmV?d00001 diff --git a/assdraw/src/bitmaps/uncheck_.bmp b/assdraw/src/bitmaps/uncheck_.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b600bebece5db6b7773a9cde2d0f3a9a7aa1ebae GIT binary patch literal 774 zcmZ?rWn*Rl12Z700mK48%n!tj3=%*R0*>%8Fn}-&9H0wmXlOWb;R=3)>=2-O2M34$ gxB<|pQT4ck=l_6&4=~Db0ZjJ*qZmIxj$h*00M7m}ng9R* literal 0 HcmV?d00001 diff --git a/assdraw/src/canvas.cpp b/assdraw/src/canvas.cpp new file mode 100644 index 000000000..ab8eadeca --- /dev/null +++ b/assdraw/src/canvas.cpp @@ -0,0 +1,2057 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: canvas.cpp +// Purpose: implementations of ASSDraw main canvas class +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#include "assdraw.hpp" +#include "cmd.hpp" +#include "agg_gsv_text.h" +#include "agg_ellipse.h" +#include "agg_conv_dash.h" +#include "agg_trans_bilinear.h" +#include "agg_trans_perspective.h" + +#include "agghelper.hpp" +#include +#include +#include + +// ---------------------------------------------------------------------------- +// the main drawing canvas: ASSDrawCanvas +// ---------------------------------------------------------------------------- + +BEGIN_EVENT_TABLE(ASSDrawCanvas, ASSDrawEngine) + EVT_MOTION (ASSDrawCanvas::OnMouseMove) + EVT_LEFT_UP(ASSDrawCanvas::OnMouseLeftUp) + EVT_LEFT_DOWN(ASSDrawCanvas::OnMouseLeftDown) + EVT_RIGHT_UP(ASSDrawCanvas::OnMouseRightUp) + EVT_RIGHT_DOWN(ASSDrawCanvas::OnMouseRightDown) + EVT_RIGHT_DCLICK(ASSDrawCanvas::OnMouseRightDClick) + EVT_MOUSEWHEEL(ASSDrawCanvas::OnMouseWheel) + EVT_KEY_DOWN(ASSDrawCanvas::CustomOnKeyDown) + EVT_KEY_UP(ASSDrawCanvas::CustomOnKeyUp) + EVT_MENU(MENU_DRC_LNTOBEZ, ASSDrawCanvas::OnSelect_ConvertLineToBezier) + EVT_MENU(MENU_DRC_BEZTOLN, ASSDrawCanvas::OnSelect_ConvertBezierToLine) + EVT_MENU(MENU_DRC_C1CONTBEZ, ASSDrawCanvas::OnSelect_C1ContinuityBezier) + EVT_MENU(MENU_DRC_MOVE00, ASSDrawCanvas::OnSelect_Move00Here) + EVT_MOUSE_CAPTURE_LOST(ASSDrawCanvas::CustomOnMouseCaptureLost) +END_EVENT_TABLE() + +ASSDrawCanvas::ASSDrawCanvas(wxWindow *parent, ASSDrawFrame *frame, int extraflags) + : ASSDrawEngine( parent, extraflags ) +{ + m_frame = frame; + preview_mode = false; + lastDrag_left = NULL; + lastDrag_right = NULL; + dragAnchor_left = NULL; + dragAnchor_right = NULL; + newcommand = NULL; + mousedownAt_point = NULL; + pointedAt_point = NULL; + draw_mode = MODE_ARR; + + //drag_mode = DRAG_DWG; + dragOrigin = false; + hilite_cmd = NULL; + hilite_point = NULL; + capturemouse_left = false; + capturemouse_right = false; + //was_preview_mode = false; + bgimg.bgbmp = NULL; + bgimg.bgimg = NULL; + bgimg.alpha = 0.5; + rectbound2upd = -1, rectbound2upd2 = -1; + + rgba_shape_normal = agg::rgba(0,0,1,0.5); + rgba_outline = agg::rgba(0,0,0); + rgba_guideline = agg::rgba(0.5,0.5,0.5); + rgba_mainpoint = agg::rgba(1,0,0,0.75); + rgba_controlpoint = agg::rgba(0,1,0,0.75); + rgba_selectpoint = agg::rgba(0,0,1,0.75); + rgba_origin = agg::rgba(0,0,0); + rgba_ruler_h = agg::rgba(0,0,1); + rgba_ruler_v = agg::rgba(1,0,0); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(1, 1); + sizer->AddGrowableRow(0); + sizer->AddGrowableCol(0); + sizer->Add( this, 0, wxGROW|wxGROW, 5); + parent->SetSizer(sizer); + + // for background image loading + ::wxInitAllImageHandlers(); + bgimg.bgbmp = NULL; + bgimg.bgimg = NULL; + // drag image background file + SetDropTarget(new ASSDrawFileDropTarget(this)); + + hasStatusBar = m_frame->GetStatusBar() != NULL; + + // cursor = crosshair + SetCursor( *wxCROSS_CURSOR ); + + bgimg.alpha_dlg = new wxDialog(this, wxID_ANY, wxString("Background image opacity")); + bgimg.alpha_slider = new wxSlider(bgimg.alpha_dlg, TB_BGALPHA_SLIDER, 50, 0, 100, __DPDS__ , wxSL_LABELS); + bgimg.alpha_slider->SetSize(280, bgimg.alpha_slider->GetSize().y); + bgimg.alpha_dlg->Fit(); + bgimg.alpha_dlg->Show(false); + bgimg.alpha_slider->Connect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(ASSDrawCanvas::OnAlphaSliderChanged), NULL, this); + bgimg.alpha_slider->Connect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(ASSDrawCanvas::OnAlphaSliderChanged), NULL, this); + bgimg.alpha_slider->Connect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(ASSDrawCanvas::OnAlphaSliderChanged), NULL, this); + bgimg.alpha_slider->Connect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(ASSDrawCanvas::OnAlphaSliderChanged), NULL, this); + bgimg.alpha_slider->Connect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(ASSDrawCanvas::OnAlphaSliderChanged), NULL, this); + bgimg.alpha_slider->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(ASSDrawCanvas::OnAlphaSliderChanged), NULL, this); + + RefreshUndocmds(); + +} + +// Destructor +ASSDrawCanvas::~ASSDrawCanvas() +{ + ASSDrawEngine::ResetEngine(false); + if (pointsys) delete pointsys; + if (bgimg.bgbmp) delete bgimg.bgbmp; + if (bgimg.bgimg) delete bgimg.bgimg; +} + +void ASSDrawCanvas::ParseASS(wxString str, bool addundo) +{ + if (addundo) + AddUndo(wxT("Modify drawing commands")); + + ASSDrawEngine::ParseASS(str); + + RefreshUndocmds(); +} + +void ASSDrawCanvas::ResetEngine(bool addM) +{ + ClearPointsSelection(); + SetHighlighted(NULL, NULL); + SetPreviewMode(false); + SetDrawMode(MODE_ARR); + ASSDrawEngine::ResetEngine(addM); + RefreshUndocmds(); +} + +void ASSDrawCanvas::SetPreviewMode(bool mode) +{ + //was_preview_mode = mode; + preview_mode = mode; + if (preview_mode) + { + if (mousedownAt_point != NULL) + { + mousedownAt_point->cmd_main->Init(); + mousedownAt_point = NULL; + } + + if (pointedAt_point != NULL) + pointedAt_point = NULL; + + SetHighlighted( NULL, NULL ); + + RefreshDisplay(); + } +} + +// (Re)draw canvas +void ASSDrawCanvas::RefreshDisplay() +{ + ASSDrawEngine::RefreshDisplay(); + wxString asscmds = GenerateASS(); + if (oldasscmds != asscmds) + { + m_frame->UpdateASSCommandStringToSrcTxtCtrl(asscmds); + oldasscmds = asscmds; + } +} + +void ASSDrawCanvas::SetDrawMode( MODE mode ) +{ + draw_mode = mode; + + if (!selected_points.empty()) + ClearPointsSelection(); + + RefreshDisplay(); + + if (IsTransformMode()) + { + isshapetransformable = cmds.size() > 1; + + if (isshapetransformable) + { + + // backup cmds + backupcmds.free_all(); + for (DrawCmdList::iterator iterate = cmds.begin(); iterate != cmds.end(); iterate++) + { + DrawCmd* cmd = (*iterate); + for (PointList::iterator iterate2 = cmd->controlpoints.begin(); iterate2 != cmd->controlpoints.end(); iterate2++) + { + wxPoint pp = (*iterate2)->ToWxPoint(); + backupcmds.move_to(pp.x, pp.y); + } + wxPoint pp = (*iterate)->m_point->ToWxPoint(); + backupcmds.move_to(pp.x, pp.y); + } + + // calculate bounding rectangle + agg::trans_affine mtx; + trans_path *rm, *rb; + agg::conv_curve *rc; + ConstructPathsAndCurves(mtx, rm, rb, rc); + rasterizer.reset(); + rasterizer.add_path(*rc); + delete rm, rb, rc; + int minx = rasterizer.min_x(), miny = rasterizer.min_y(); + int maxx = rasterizer.max_x(), maxy = rasterizer.max_y(); + + rectbound[0] = wxRealPoint(minx, miny); + rectbound[1] = wxRealPoint(maxx, miny); + rectbound[2] = wxRealPoint(maxx, maxy); + rectbound[3] = wxRealPoint(minx, maxy); + for (int i = 0; i < 4; i++) + rectbound2[i] = rectbound[i]; + + rectbound2upd = -1; + rectbound2upd2 = -1; + + backupowner = NONE; + InitiateDraggingIfTransformMode(); + + if (maxx - minx < 5 || maxy - miny < 5) + isshapetransformable = false; + } + } + + RefreshUndocmds(); + m_frame->UpdateFrameUI(); + +} + +void ASSDrawCanvas::SetDragMode( DRAGMODE mode ) +{ + drag_mode = mode; +} + +bool ASSDrawCanvas::IsTransformMode() +{ + return draw_mode == MODE_NUT_BILINEAR || draw_mode == MODE_SCALEROTATE; +} + +bool ASSDrawCanvas::CanZoom() +{ + return !IsTransformMode() || !drag_mode.drawing; +} + +bool ASSDrawCanvas::CanMove() +{ + return !IsTransformMode() || dragAnchor_left == NULL; +} + +// Do the dragging +void ASSDrawCanvas::OnMouseMove(wxMouseEvent &event) +{ + mouse_point = event.GetPosition(); + int xx, yy, wx, wy; + xx = mouse_point.x; yy = mouse_point.y; + pointsys->FromWxPoint ( mouse_point, wx, wy ); + if (event.Dragging()) + { + if (IsTransformMode() && isshapetransformable && backupowner == LEFT) + { + // update bounding polygon + if (rectbound2upd > -1) + { + wxPoint diff = mouse_point - *dragAnchor_left; + wxRealPoint rdiff(diff.x, diff.y); + switch(draw_mode) + { + case MODE_NUT_BILINEAR: + if (rectbound2upd2 == -1) //only one vertex dragged + rectbound2[rectbound2upd].x = xx, rectbound2[rectbound2upd].y = yy; + else + { + rectbound2[rectbound2upd].x += diff.x, rectbound2[rectbound2upd].y += diff.y; + rectbound2[rectbound2upd2].x += diff.x, rectbound2[rectbound2upd2].y += diff.y; + } + undodesc = "Bilinear transform"; + *dragAnchor_left = mouse_point; + break; + case MODE_SCALEROTATE: + if (rectbound2upd2 == -1) //only one vertex dragged + { + int adjacent[2] = { (rectbound2upd + 3) % 4, (rectbound2upd + 1) % 4 }; + int opposite = (rectbound2upd + 2) % 4; + wxRealPoint newpoint = backup[rectbound2upd] + rdiff; + //wxPoint newadjacent[2]; + double nx, ny; + for (int i = 0; i < 2; i++) + { + bool isect = agg::calc_intersection( + backup[opposite].x, backup[opposite].y, + backup[adjacent[i]].x, backup[adjacent[i]].y, + newpoint.x, newpoint.y, + backup[adjacent[i]].x + diff.x, backup[adjacent[i]].y + diff.y, + &nx, &ny); + if (isect && !(fabs(nx - backup[opposite].x) < 10 && fabs(ny - backup[opposite].y) < 10)) + rectbound2[adjacent[i]] = wxRealPoint(nx, ny); + } + GetThe4thPoint(backup[opposite].x, backup[opposite].y, + rectbound2[adjacent[0]].x, rectbound2[adjacent[0]].y, + rectbound2[adjacent[1]].x, rectbound2[adjacent[1]].y, + &rectbound2[rectbound2upd].x, &rectbound2[rectbound2upd].y); + if (event.ShiftDown()) // shift down = maintain aspect ratio (damn so complicated) + { + // first create the guide points, which are basically backup points reoriented based on mouse position + wxRealPoint guide[4]; + guide[opposite] = backup[opposite]; + guide[adjacent[0]] = backup[adjacent[0]]; + guide[adjacent[1]] = backup[adjacent[1]]; + for (int i = 0; i < 2; i++) + { + if ((rectbound2[adjacent[i]].x < guide[opposite].x && guide[opposite].x < guide[adjacent[i]].x) + || (rectbound2[adjacent[i]].x > guide[opposite].x && guide[opposite].x > guide[adjacent[i]].x) + || (rectbound2[adjacent[i]].y < guide[opposite].y && guide[opposite].y < guide[adjacent[i]].y) + || (rectbound2[adjacent[i]].y > guide[opposite].y && guide[opposite].y > guide[adjacent[i]].y)) + { + guide[adjacent[i]] = guide[opposite] - (guide[adjacent[i]] - guide[opposite]); + } + } + guide[rectbound2upd] = guide[adjacent[0]] + (guide[adjacent[1]] - guide[opposite]); + // now we determine which rescaled sides have larger enlargement/shrinkage ratio .. + double ix[2], iy[2], dx[2], dy[2]; + for (int i = 0; i < 2; i++) + { + agg::calc_intersection(guide[opposite].x, guide[opposite].y, guide[rectbound2upd].x, guide[rectbound2upd].y, + rectbound2[rectbound2upd].x, rectbound2[rectbound2upd].y, + rectbound2[adjacent[i]].x, rectbound2[adjacent[i]].y, &ix[i], &iy[i]); + dx[i] = ix[i] - guide[opposite].x; + dy[i] = iy[i] - guide[opposite].y; + } + // .. and modify the other sides to follow the ratio + for (int i = 0; i < 2; i++) + { + int j = (i + 1) % 2; + if (fabs(dx[i]) > fabs(dx[j]) || fabs(dy[i]) > fabs(dy[j])) + { + rectbound2[rectbound2upd].x = ix[i]; + rectbound2[rectbound2upd].y = iy[i]; + GetThe4thPoint(rectbound2[adjacent[i]].x, rectbound2[adjacent[i]].y, + guide[opposite].x, guide[opposite].y, ix[i], iy[i], + &rectbound2[adjacent[j]].x, &rectbound2[adjacent[j]].y); + } + } + } + } + else // an edge dragged (i.e. move 2 vertices in sync) + { + // it is guaranteed that rectbound2upd and rectbound2upd2 are in clockwise direction + // from the way they are detected in OnMouseLeftDown() + int toupd[2] = { rectbound2upd, rectbound2upd2 }; + int adjacent[2] = { (rectbound2upd2 + 2) % 4, (rectbound2upd2 + 1) % 4 }; + wxRealPoint vertexone = backup[toupd[0]] + rdiff, vertextwo = backup[toupd[1]] + rdiff; + double nx, ny; + for (int i = 0; i < 2; i++) + { + agg::calc_intersection( + rectbound2[adjacent[i]].x, rectbound2[adjacent[i]].y, + backup[toupd[i]].x, backup[toupd[i]].y, + vertexone.x, vertexone.y, + vertextwo.x, vertextwo.y, + &nx, &ny); + if (!(fabs(nx - backup[adjacent[i]].x) < 10 && fabs(ny - backup[adjacent[i]].y) < 10)) + rectbound2[toupd[i]].x = (int) nx, rectbound2[toupd[i]].y = (int) ny; + } + } + UpdateTranformModeRectCenter(); + //*dragAnchor_left = mouse_point; + undodesc = "Scale"; + break; + } + + // update points + UpdateNonUniformTransformation(); + RefreshDisplay(); + } + } + else if (draw_mode != MODE_DEL) + { + + // point left-dragged + if (mousedownAt_point != NULL && mousedownAt_point->isselected) + { + if (!mousedownAt_point->IsAt( wx, wy )) + { + if (draw_mode == MODE_ARR) { + int movex = wx - mousedownAt_point->x(), movey = wy - mousedownAt_point->y(); + PointSet::iterator iter = selected_points.begin(); + for (; iter != selected_points.end(); iter++) + (*iter)->setXY( (*iter)->x() + movex, (*iter)->y() + movey ); + } + else + mousedownAt_point->setXY( wx, wy ); + + EnforceC1Continuity (mousedownAt_point->cmd_main, mousedownAt_point); + + RefreshDisplay(); + if (undodesc == "") + { + if (mousedownAt_point->type == MP) + undodesc = wxT("Drag point"); + else + undodesc = wxT("Drag control point"); + } + } + } + // origin left-dragged + else if (dragOrigin) + { + if (wx != 0 || wy != 0) + { + wxPoint wxp = pointsys->ToWxPoint ( wx, wy ); + MovePoints(-wx, -wy); + pointsys->originx = wxp.x; + pointsys->originy = wxp.y; + RefreshDisplay(); + undodesc = wxT("Move origin"); + } + } + else if (dragAnchor_left != NULL) + { + // handle left-dragging here + if (lastDrag_left && dragAnchor_left != lastDrag_left) + delete lastDrag_left; + lastDrag_left = new wxPoint(xx, yy); + int ax = dragAnchor_left->x, ay = dragAnchor_left->y; + int sx = lastDrag_left->x, sy = lastDrag_left->y; + int lx, rx, ty, by; + if (ax > sx) lx = sx, rx = ax; + else lx = ax, rx = sx; + if (ay > sy) ty = sy, by = ay; + else ty = ay, by = sy; + SelectPointsWithin( lx, rx, ty, by, GetSelectMode(event) ); + RefreshDisplay(); + } + } + + // right-dragged + if (dragAnchor_right != NULL) + { + if (draw_mode == MODE_SCALEROTATE) + { + if (backupowner == RIGHT) + { + double cx = rectcenter.x, cy = rectcenter.y; // center x,y + double diameter = sqrt(pow(rectbound2[0].x - rectbound2[2].x, 2) + pow(rectbound2[0].y - rectbound2[2].y, 2)); + double radius = diameter / 2; + double old_dx = dragAnchor_right->x - cx, old_dy = dragAnchor_right->y - cy; + double new_dx = mouse_point.x - cx, new_dy = mouse_point.y - cy; + double old_angle = atan2(old_dy, old_dx); + double new_angle = atan2(new_dy, new_dx); + double delta = new_angle - old_angle; + for (int i = 0; i < 4; i++) + { + old_dx = backup[i].x - cx, old_dy = backup[i].y - cy; + old_angle = atan2(old_dy, old_dx); + new_angle = old_angle + delta; + new_dx = radius * cos(new_angle), new_dy = radius * sin(new_angle); + rectbound2[i].x = (int)(new_dx + cx), rectbound2[i].y = (int)(new_dy + cy); + } + UpdateNonUniformTransformation(); + RefreshDisplay(); + undodesc = "Rotate"; + } + } + else if (CanMove()) + { + MoveCanvas(xx - dragAnchor_right->x, yy - dragAnchor_right->y); + dragAnchor_right->x = xx; + dragAnchor_right->y = yy; + RefreshDisplay(); + } + } + } + else if (!preview_mode)// not dragging and not preview mode + { + if (IsTransformMode()) + { + int oldrectbound = rectbound2upd; + rectbound2upd = -1; + rectbound2upd2 = -1; + for (int i = 0; i < 4; i++) + { + if (abs((int)rectbound2[i].x - mouse_point.x) <= pointsys->scale + && abs((int)rectbound2[i].y - mouse_point.y) <= pointsys->scale) + rectbound2upd = i; + } + for (int i = 0; rectbound2upd == -1 && i < 4; i++) + { + int j = (i+1) % 4; + wxRealPoint &pi = rectbound2[i], &pj = rectbound2[j]; + double dy = fabs(pj.y - pi.y); + double dy3 = dy / 3; + double dx = fabs(pj.x - pi.x); + double dx3 = dx / 3; + double ix, iy; + bool intersect = false; + if (dy > dx) + { + intersect = agg::calc_intersection( + pi.x, pi.y, pj.x, pj.y, + mouse_point.x - pointsys->scale, mouse_point.y, + mouse_point.x + pointsys->scale, mouse_point.y, &ix, &iy); + intersect &= fabs(mouse_point.x - ix) <= pointsys->scale; + intersect &= (pj.y > pi.y? + pj.y - dy3 > iy && iy > pi.y + dy3: + pj.y + dy3 < iy && iy < pi.y - dy3); + } + else + { + intersect = agg::calc_intersection( + pi.x, pi.y, pj.x, pj.y, + mouse_point.x, mouse_point.y - pointsys->scale, + mouse_point.x, mouse_point.y + pointsys->scale, &ix, &iy); + intersect &= fabs(mouse_point.y - iy) <= pointsys->scale; + intersect &= (pj.x > pi.x? + pj.x - dx3 > ix && ix > pi.x + dx3: + pj.x + dx3 < ix && ix < pi.x - dx3); + } + if (intersect) + { + rectbound2upd = i; + rectbound2upd2 = j; + } + } + if (rectbound2upd != -1 || oldrectbound != -1) + RefreshDisplay(); + } + else + { + /* figure out if the mouse is pointing at a point of a command + we need not do this for dragging since this same block has set + the related variables before the user starts to hold the button + (well, before you can drag something you have to move the pointer + over that thing first, right?) and we don't want to mess with those + when it's dragging + */ + + // check if mouse points on any control point first + Point* last_pointedAt_point = pointedAt_point; + ControlAt( wx, wy, pointedAt_point ); + + // then check if mouse points on any m_points + // override any control point set to pointedAt_point above + DrawCmd* p = PointAt( wx, wy ); + if (p != NULL) + { + pointedAt_point = p->m_point; + } + + if (pointedAt_point != last_pointedAt_point) + { + if (pointedAt_point != NULL) + SetHighlighted( pointedAt_point->cmd_main, pointedAt_point ); + else + SetHighlighted( NULL, NULL ); + RefreshDisplay(); + } + } + } // not dragging and preview mode = ignore all mouse movements + + // we are not done yet? + // oh yeah, we need to set the status bar just for fun + if (hasStatusBar) + { + m_frame->SetStatusText( + wxString::Format( wxT("%5d %5d"), (int)wx, (int)wy ), 0 ); + if (pointedAt_point == NULL || + (newcommand != NULL && !newcommand->initialized) ) + m_frame->SetStatusText( wxT(""), 1 ); + else + m_frame->SetStatusText( wxT(" ") + pointedAt_point->cmd_main->ToString().Upper(), 1 ); + } + +} + +// End drag points +void ASSDrawCanvas::OnMouseLeftUp(wxMouseEvent& event) +{ + ProcessOnMouseLeftUp(); + event.Skip( true ); +} + +void ASSDrawCanvas::ProcessOnMouseLeftUp() +{ + if (!capturemouse_left) return; + + // draw mode + if (newcommand != NULL) + { + newcommand->Init(); + switch (newcommand->type) + { + case M: + undodesc = wxT("Add a new M"); break; + case L: + undodesc = wxT("Add a new L"); break; + case B: + undodesc = wxT("Add a new B"); break; + } + newcommand = NULL; + // we need to manually refresh the GUI to draw the new control points + RefreshDisplay(); + } + else if ( draw_mode == MODE_DEL // if it's delete mode + && mousedownAt_point != NULL // and mouse down at a point + && mousedownAt_point == pointedAt_point ) // and released at the same point + { + // first take care of mouse readings + pointedAt_point = NULL; + Point *lastmousedownAt_point = mousedownAt_point; + mousedownAt_point = NULL; + + // try deleting + CMDTYPE t = lastmousedownAt_point->cmd_main->type; + if ( DeleteCommand( lastmousedownAt_point->cmd_main ) ) + { + ClearPointsSelection(); + SetHighlighted( NULL, NULL ); + RefreshDisplay(); + switch (t) + { + case M: + undodesc = wxT("Delete a M"); break; + case L: + undodesc = wxT("Delete a L"); break; + case B: + undodesc = wxT("Delete a B"); break; + } + } + else + { + RefreshDisplay(); // redraw before showing the error box + wxMessageDialog msgb(m_frame, + _T("You must delete that command/point last"), + _T("Error"), wxOK | wxICON_EXCLAMATION ); + msgb.ShowModal(); + } + } + else if ( lastDrag_left && dragAnchor_left ) // point selection + { + if (lastDrag_left && dragAnchor_left != lastDrag_left) + delete lastDrag_left; + delete dragAnchor_left; + lastDrag_left = NULL; + dragAnchor_left = NULL; + } + + if (dragOrigin) + { + dragOrigin = false; + RefreshDisplay(); // clear the crosshair + } + + rectbound2upd = -1; + rectbound2upd2 = -1; + backupowner = NONE; + + if (!undodesc.IsSameAs("")) + { + AddUndo( undodesc ); + undodesc = ""; + RefreshUndocmds(); + } + + mousedownAt_point = NULL; + + if (HasCapture()) + ReleaseMouse(); + capturemouse_left = false; + + RefreshDisplay(); +} + +// Set up to drag points, if any +void ASSDrawCanvas::OnMouseLeftDown(wxMouseEvent& event) +{ + + // no drawing in preview mode + if (preview_mode /*|| was_preview_mode*/) + return; + + wxPoint q = event.GetPosition(); + + // wxPoint to Point + int px, py; + pointsys->FromWxPoint(q, px, py); + + // create new cmd if in draw mode / or delete point if del tool selected + switch (draw_mode) + { + case MODE_M: + newcommand = NewCmd(M, px, py); + break; + case MODE_L: + newcommand = NewCmd(L, px, py); + break; + case MODE_B: + newcommand = NewCmd(B, px, py); + break; + } + + // continue working on the new command (if any) + // only if it is not mouse down on any control point + if (newcommand != NULL) + { + if (pointedAt_point != NULL && pointedAt_point->type == CP) + { + // oops, user clicked on a control point so cancel new command + // and let him drag the control point + delete newcommand; + newcommand = NULL; + } + else + { + // first set to drag the new command no matter what + mousedownAt_point = newcommand->m_point; + + // if user drags from existing point, insert the new command + // else append the new command + if (pointedAt_point != NULL) + InsertCmd( newcommand, pointedAt_point->cmd_main ); + else + { + if (cmds.empty() && newcommand->type != M) + AppendCmd( M, px, py ); + newcommand = AppendCmd( newcommand ); + } + + pointedAt_point = mousedownAt_point; + + //highlight it + SetHighlighted( newcommand, newcommand->m_point ); + } + } + + // we already calculated pointedAt_point in OnMouseMove() so just use it + mousedownAt_point = pointedAt_point; + SELECTMODE smode = GetSelectMode(event); + if (mousedownAt_point && !mousedownAt_point->isselected) + { + if (smode == NEW) + { + ClearPointsSelection(); + mousedownAt_point->isselected = true; + selected_points.insert(mousedownAt_point); + } + else + { + wxPoint wxp = mousedownAt_point->ToWxPoint(); + SelectPointsWithin(wxp.x, wxp.x, wxp.y, wxp.y, smode); + } + } + else if (!mousedownAt_point && smode == NEW) + ClearPointsSelection(); + + if ( mousedownAt_point == NULL && px == 0 && py == 0 ) + dragOrigin = true; + + if ( mousedownAt_point == NULL && !dragOrigin ) + { + dragAnchor_left = new wxPoint(q.x, q.y); + lastDrag_left = dragAnchor_left; + } + + if (InitiateDraggingIfTransformMode()) + backupowner = LEFT; + + CaptureMouse(); + capturemouse_left = true; + RefreshDisplay(); + + event.Skip( true ); +} + +// End drag the canvas +void ASSDrawCanvas::OnMouseRightUp(wxMouseEvent& event) +{ + ProcessOnMouseRightUp(); + event.Skip( true ); +} + +void ASSDrawCanvas::ProcessOnMouseRightUp() +{ + if (!capturemouse_right) return; + + if (lastDrag_right && dragAnchor_right != lastDrag_right) + delete lastDrag_right; + if (dragAnchor_right) + delete dragAnchor_right; + dragAnchor_right = NULL; + lastDrag_right = NULL; + + // don't crash the program + if (HasCapture()) + ReleaseMouse(); + capturemouse_right = false; + + rectbound2upd = -1; + rectbound2upd2 = -1; + backupowner = NONE; + + if (!undodesc.IsSameAs("")) + { + AddUndo( undodesc ); + undodesc = ""; + RefreshUndocmds(); + } + + RefreshDisplay(); + SetFocus(); +} + +// Set up to drag the canvas +void ASSDrawCanvas::OnMouseRightDown(wxMouseEvent &event) +{ + wxPoint q = event.GetPosition(); + dragAnchor_right = new wxPoint(q.x, q.y); + lastDrag_right = dragAnchor_right; + CaptureMouse(); + capturemouse_right = true; + + if (InitiateDraggingIfTransformMode()) + backupowner = RIGHT; +// was_preview_mode = preview_mode; +// preview_mode = false; + + event.Skip( true ); +} + +// Reset to point-and-drag mode +void ASSDrawCanvas::OnMouseRightDClick(wxMouseEvent& event) +{ + wxMenu* menu = new wxMenu(); + + if (pointedAt_point) + { + ProcessOnMouseLeftUp(); + ProcessOnMouseRightUp(); + dblclicked_point_right = pointedAt_point; + pointedAt_point = NULL; + wxMenuItem *cmdmenuitem = new wxMenuItem(menu, MENU_DUMMY, dblclicked_point_right->cmd_main->ToString()); + wxFont f = cmdmenuitem->GetFont(); + f.SetWeight(wxFONTWEIGHT_BOLD); + cmdmenuitem->SetFont(f); + menu->Append(cmdmenuitem); + menu->Enable(MENU_DUMMY, false); + switch (dblclicked_point_right->cmd_main->type) + { + case L: + menu->Append(MENU_DRC_LNTOBEZ, "Convert to Bezier curve (B command)"); + break; + case B: + if (dblclicked_point_right->type != MP) break; + menu->AppendCheckItem(MENU_DRC_BEZTOLN, "Convert to line (L command)"); + if (dblclicked_point_right->cmd_next && dblclicked_point_right->cmd_next->type == B) + { + menu->AppendCheckItem(MENU_DRC_C1CONTBEZ, "Smooth connection"); + if (static_cast(dblclicked_point_right->cmd_main)->C1Cont) + menu->Check(MENU_DRC_C1CONTBEZ, true); + } + break; + } + + } + else + { + menu->Append(MENU_DRC_MOVE00, "Move [0,0] here"); + menu->AppendSeparator(); + menu->AppendRadioItem(MODE_ARR, _T("Mode: D&rag")); + menu->AppendRadioItem(MODE_M, _T("Mode: Draw &M")); + menu->AppendRadioItem(MODE_L, _T("Mode: Draw &L")); + menu->AppendRadioItem(MODE_B, _T("Mode: Draw &B")); + menu->AppendRadioItem(MODE_DEL, _T("Mode: &Delete")); + menu->Check(GetDrawMode(), true); + } + + if (menu->GetMenuItemCount() > 0) // only if there is menu item + { + SetHighlighted(NULL, NULL); + mousedownAt_point = NULL; + RefreshDisplay(); + PopupMenu(menu); + } + delete menu; + + event.Skip( true ); +} + +bool ASSDrawCanvas::InitiateDraggingIfTransformMode() +{ + if (IsTransformMode() && isshapetransformable && backupowner == NONE) + { + for (int i = 0; i < 4; i++) + backup[i] = rectbound2[i]; + UpdateTranformModeRectCenter(); + return true; + } + else + return false; +} + +void ASSDrawCanvas::UpdateTranformModeRectCenter() +{ + double cx, cy; + if (agg::calc_intersection(rectbound2[0].x, rectbound2[0].y, rectbound2[2].x, rectbound2[2].y, + rectbound2[1].x, rectbound2[1].y, rectbound2[3].x, rectbound2[3].y, &cx, &cy)) + { + rectcenter = wxRealPoint(cx, cy); + } +} + +bool ASSDrawCanvas::GetThe4thPoint(double ox, double oy, double a1x, double a1y, double a2x, double a2y, double *x, double *y) +{ + /* + return agg::calc_intersection(a1x, a1y, + a2x + a1x - ox, + a2y + a1y - oy, + a2x, a2y, + a1x + a2x - ox, + a1y + a2y - oy, + x, y); //*/ + + *x = a1x + a2x - ox; + *y = a1y + a2y - oy; + return true; +} + +// Mousewheel +void ASSDrawCanvas::OnMouseWheel(wxMouseEvent &event) +{ + double amount = event.GetWheelRotation() / event.GetWheelDelta(); + if (event.ControlDown()) amount /= 10.0; + bgimg.new_center = wxRealPoint(mouse_point.x, mouse_point.y); + ChangeZoomLevel( amount, mouse_point ); +} + +void ASSDrawCanvas::ChangeZoomLevelTo( double zoom, wxPoint bgzoomctr ) +{ + ChangeZoomLevel(zoom - pointsys->scale, bgzoomctr); +} + +void ASSDrawCanvas::ChangeZoomLevel( double amount, wxPoint bgzoomctr ) +{ + double old_scale = pointsys->scale; + /* + switch (drag_mode) + { + case DRAG_BGIMG: + ChangeBackgroundZoomLevel(bgimg.scale + amount / 10.0, wxRealPoint(bgzoomctr.x, bgzoomctr.y)); + break; + + case DRAG_BOTH: + ChangeDrawingZoomLevel( amount ); + ChangeBackgroundZoomLevel(bgimg.scale * pointsys->scale / old_scale, + wxRealPoint(pointsys->originx, pointsys->originy)); + break; + + case DRAG_DWG: + ChangeDrawingZoomLevel( amount ); + break; + } + */ + + if (CanZoom() && drag_mode.drawing) + ChangeDrawingZoomLevel( amount ); + + if (CanZoom() && drag_mode.bgimg) + if (drag_mode.drawing) + ChangeBackgroundZoomLevel(bgimg.scale * pointsys->scale / old_scale, wxRealPoint(pointsys->originx, pointsys->originy)); + else + ChangeBackgroundZoomLevel(bgimg.scale + amount / 10.0, wxRealPoint(bgzoomctr.x, bgzoomctr.y)); + + RefreshDisplay(); +} + +void ASSDrawCanvas::ChangeDrawingZoomLevel( double scrollamount ) +{ + if (!CanZoom()) return; + double zoom = pointsys->scale + scrollamount; + if (zoom <= 50.0) + { + if (zoom < 1.0) zoom = 1.0; + pointsys->scale = zoom; + } + + m_frame->UpdateFrameUI(); +} + +void ASSDrawCanvas::ChangeBackgroundZoomLevel(double zoom, wxRealPoint newcenter) +{ + bgimg.new_scale = zoom; + bgimg.new_center = newcenter; + if (bgimg.new_scale < 0.01) bgimg.new_scale = 0.01; + UpdateBackgroundImgScalePosition(); +} + +void ASSDrawCanvas::MoveCanvasOriginTo(double originx, double originy) +{ + MoveCanvas(originx - pointsys->originx, originy - pointsys->originy); +} + +void ASSDrawCanvas::MoveCanvas(double xamount, double yamount) +{ + /* + if (drag_mode == DRAG_DWG || drag_mode == DRAG_BOTH) + MoveCanvasDrawing(xamount, yamount); + if (drag_mode == DRAG_BGIMG || drag_mode == DRAG_BOTH) + MoveCanvasBackground(xamount, yamount); + */ + if (CanMove() && drag_mode.drawing) + MoveCanvasDrawing(xamount, yamount); + if (CanMove() && drag_mode.bgimg) + MoveCanvasBackground(xamount, yamount); +} + +void ASSDrawCanvas::MoveCanvasDrawing(double xamount, double yamount) +{ + if (!CanMove()) return; + pointsys->originx += xamount; + pointsys->originy += yamount; + if (IsTransformMode()) + { + for (int i = 0; i < 4; i++) + { + rectbound[i].x += (int) xamount; + rectbound[i].y += (int) yamount; + rectbound2[i].x += (int) xamount; + rectbound2[i].y += (int) yamount; + } + unsigned vertices = backupcmds.total_vertices(); + double x, y; + for (int i = 0; i < vertices; i++) + { + backupcmds.vertex(i, &x, &y); + backupcmds.modify_vertex(i, x + xamount, y + yamount); + } + } +} + +void ASSDrawCanvas::MoveCanvasBackground(double xamount, double yamount) +{ + //bgimg.new_center.x += xamount, bgimg.new_center.y += yamount; + bgimg.new_disp.x += xamount; + bgimg.new_disp.y += yamount; + UpdateBackgroundImgScalePosition(); +} + +void ASSDrawCanvas::OnSelect_ConvertLineToBezier(wxCommandEvent& WXUNUSED(event)) +{ + if (!dblclicked_point_right) return; + AddUndo( wxT("Convert Line to Bezier") ); + DrawCmd_B *newB = new DrawCmd_B(dblclicked_point_right->x(), dblclicked_point_right->y(), + pointsys, dblclicked_point_right->cmd_main); + InsertCmd ( newB, dblclicked_point_right->cmd_main ); + ClearPointsSelection(); + SetHighlighted(NULL, NULL); + DeleteCommand(dblclicked_point_right->cmd_main); + pointedAt_point = newB->m_point; + newB->Init(); + RefreshDisplay(); + RefreshUndocmds(); +} + +void ASSDrawCanvas::OnSelect_ConvertBezierToLine(wxCommandEvent& WXUNUSED(event)) +{ + if (!dblclicked_point_right) return; + AddUndo( wxT("Convert Bezier to Line") ); + DrawCmd_L *newL = new DrawCmd_L(dblclicked_point_right->x(), dblclicked_point_right->y(), pointsys, dblclicked_point_right->cmd_main); + InsertCmd ( newL, dblclicked_point_right->cmd_main ); + ClearPointsSelection(); + SetHighlighted(NULL, NULL); + DeleteCommand(dblclicked_point_right->cmd_main); + pointedAt_point = newL->m_point; + newL->Init(); + RefreshDisplay(); + RefreshUndocmds(); +} + +void ASSDrawCanvas::OnSelect_C1ContinuityBezier(wxCommandEvent& WXUNUSED(event)) +{ + if (!dblclicked_point_right) return; + DrawCmd_B *cmdb = static_cast(dblclicked_point_right->cmd_main); + AddUndo( wxT(cmdb->C1Cont? "Unset continuous":"Set continuous") ); + cmdb->C1Cont = !cmdb->C1Cont; + RefreshUndocmds(); +} + +void ASSDrawCanvas::OnSelect_Move00Here(wxCommandEvent& WXUNUSED(event)) +{ + AddUndo( wxT("Move origin") ); + int wx, wy; + pointsys->FromWxPoint(mouse_point, wx, wy); + wxPoint wxp = pointsys->ToWxPoint(wx, wy); + pointsys->originx = wxp.x; + pointsys->originy = wxp.y; + MovePoints(-wx, -wy); + RefreshDisplay(); + RefreshUndocmds(); +} + +void ASSDrawCanvas::ConnectSubsequentCmds (DrawCmd* cmd1, DrawCmd* cmd2) +{ + ASSDrawEngine::ConnectSubsequentCmds(cmd1, cmd2); + if (cmd1 && cmd1->type == B) + { + DrawCmd_B *cmd1b = static_cast< DrawCmd_B* >(cmd1); + cmd1b->C1Cont = false; + } +} + +void ASSDrawCanvas::EnforceC1Continuity (DrawCmd* cmd, Point* pnt) +{ + if (!cmd || !pnt) return; + if (cmd->type != B && cmd->type != S) return; + if (pnt->type == MP) return; + Point *theotherpoint, *mainpoint; + if (pnt->num == 1) + { + if (!cmd->prev || (cmd->prev->type != B && cmd->prev->type != S)) return; + DrawCmd_B *prevb = static_cast< DrawCmd_B* >(cmd->prev); + if (!prevb->C1Cont) return; + PointList::iterator it = prevb->controlpoints.end(); + it--; + theotherpoint = *it; + mainpoint = prevb->m_point; + } + else if (pnt->num = cmd->controlpoints.size()) + { + DrawCmd_B *thisb = static_cast< DrawCmd_B* >(cmd); + if (!thisb->C1Cont) return; + theotherpoint = *(cmd->m_point->cmd_next->controlpoints.begin()); + mainpoint = thisb->m_point; + } + else + return; + theotherpoint->setXY( mainpoint->x() + mainpoint->x() - pnt->x(), + mainpoint->y() + mainpoint->y() - pnt->y() ); +} + +// Undo/Redo system +void ASSDrawCanvas::AddUndo( wxString desc ) +{ + PrepareUndoRedo(_undo, false, "", desc); + undos.push_back( _undo ); + // also empty redos + redos.clear(); + m_frame->UpdateUndoRedoMenu(); +} + +bool ASSDrawCanvas::UndoOrRedo(bool isundo) +{ + std::list* main = (isundo? &undos:&redos); + std::list* sub = (isundo? &redos:&undos); + + if (main->empty()) + return false; + + UndoRedo r = main->back(); + // push into sub + UndoRedo nr(r); + PrepareUndoRedo(nr, true, GenerateASS(), r.desc); + //PrepareUndoRedo(nr, false, "", r.desc); + sub->push_back( nr ); + // parse + r.Export(this); + // delete that + std::list::iterator iter = main->end(); + iter--; + main->erase(iter); + + // reset some values before refreshing + mousedownAt_point = NULL; + pointedAt_point = NULL; + SetHighlighted ( NULL, NULL ); + ClearPointsSelection(); + RefreshDisplay(); + RefreshUndocmds(); + return true; +} + +bool ASSDrawCanvas::Undo() +{ + return UndoOrRedo( true ); +} + +bool ASSDrawCanvas::Redo() +{ + return UndoOrRedo( false ); +} + +wxString ASSDrawCanvas::GetTopUndo() +{ + if (undos.empty()) + return ""; + else + return undos.back().desc; +} + +wxString ASSDrawCanvas::GetTopRedo() +{ + if (redos.empty()) + return ""; + else + return redos.back().desc; +} + +void ASSDrawCanvas::RefreshUndocmds() +{ + _undo.Import(this, true, GenerateASS()); +} + +void ASSDrawCanvas::PrepareUndoRedo(UndoRedo& ur, bool prestage, wxString cmds, wxString desc) +{ + ur.Import(this, prestage, cmds); + ur.desc = desc; +} + +// set command and point to highlight +void ASSDrawCanvas::SetHighlighted ( DrawCmd* cmd, Point* point ) +{ + hilite_cmd = cmd; + hilite_point = point; +} + +int ASSDrawCanvas::SelectPointsWithin( int lx, int rx, int ty, int by, SELECTMODE smode ) +{ + + DrawCmdList::iterator iterate = cmds.begin(); + for (; iterate != cmds.end(); iterate++) + { + wxPoint wx = (*iterate)->m_point->ToWxPoint(); + + if (wx.x >= lx && wx.x <= rx && wx.y >= ty && wx.y <= by) + (*iterate)->m_point->isselected = (smode != DEL); + else + (*iterate)->m_point->isselected &= (smode != NEW); + + if ((*iterate)->m_point->isselected) + selected_points.insert((*iterate)->m_point); + else + selected_points.erase((*iterate)->m_point); + + PointList::iterator pnt_iterator = (*iterate)->controlpoints.begin(); + PointList::iterator end = (*iterate)->controlpoints.end(); + for (; pnt_iterator != end; pnt_iterator++) + { + wxPoint wx = (*pnt_iterator)->ToWxPoint(); + + if (wx.x >= lx && wx.x <= rx && wx.y >= ty && wx.y <= by) + (*pnt_iterator)->isselected = (smode != DEL); + else + (*pnt_iterator)->isselected &= (smode != NEW); + + if ((*pnt_iterator)->isselected) + selected_points.insert(*pnt_iterator); + else + selected_points.erase(*pnt_iterator); + } + } + + return selected_points.size(); + +} + +void ASSDrawCanvas::ClearPointsSelection() +{ + if (!selected_points.empty()) + { + PointSet::iterator piter = selected_points.begin(); + for (; piter != selected_points.end(); piter++) + { + (*piter)->isselected = false; + } + selected_points.clear(); + } +} + +SELECTMODE ASSDrawCanvas::GetSelectMode(wxMouseEvent& event) +{ + SELECTMODE smode = NEW; + bool CtrlDown = event.CmdDown(); + bool AltDown = event.AltDown(); + if (CtrlDown && !AltDown) smode = ADD; + else if (!CtrlDown && AltDown) smode = DEL; + return smode; +} + +void ASSDrawCanvas::DoDraw( RendererBase& rbase, RendererPrimitives& rprim, RendererSolid& rsolid, agg::trans_affine& mtx ) +{ + ASSDrawEngine::Draw_Clear( rbase ); + int ww, hh; GetClientSize(&ww, &hh); + + if (bgimg.bgbmp) + { + rasterizer.reset(); + interpolator_type interpolator(bgimg.img_mtx); + PixelFormat::AGGType ipixfmt(bgimg.ibuf); + span_gen_type spangen(ipixfmt, agg::rgba_pre(0, 0, 0), interpolator); + agg::conv_transform< agg::path_storage > bg_border(bgimg.bg_path, bgimg.path_mtx); + agg::conv_clip_polygon< agg::conv_transform< agg::path_storage > > bg_clip(bg_border); + bg_clip.clip_box(0, 0, ww, hh); + rasterizer.add_path(bg_clip); + agg::render_scanlines_aa(rasterizer, scanline, rbase, bgimg.spanalloc, spangen); + } + + ASSDrawEngine::Draw_Draw( rbase, rprim, rsolid, mtx, preview_mode? rgba_shape:rgba_shape_normal ); + + if (!preview_mode) + { + // [0, 0] + rasterizer.reset(); + agg::path_storage org_path; + org_path.move_to(0, m_frame->sizes.origincross); + org_path.line_to(0, -m_frame->sizes.origincross); + org_path.move_to(m_frame->sizes.origincross, 0); + org_path.line_to(-m_frame->sizes.origincross, 0); + agg::conv_transform< agg::path_storage > org_path_t(org_path, mtx); + agg::conv_curve< agg::conv_transform< agg::path_storage > > crosshair(org_path_t); + agg::conv_stroke< agg::conv_curve< agg::conv_transform< agg::path_storage > > > chstroke(crosshair); + rasterizer.add_path(chstroke); + rsolid.color(rgba_origin); + render_scanlines(rsolid, false); + + if (IsTransformMode() && isshapetransformable) + { + if (draw_mode == MODE_SCALEROTATE) + { + // rotation centerpoint + rasterizer.reset(); + double len = 10.0; //m_frame->sizes.origincross * pointsys->scale; + org_path.free_all(); + org_path.move_to(rectcenter.x - len, rectcenter.y - len); + org_path.line_to(rectcenter.x + len, rectcenter.y + len); + org_path.move_to(rectcenter.x + len, rectcenter.y - len); + org_path.line_to(rectcenter.x - len, rectcenter.y + len); + agg::conv_stroke< agg::path_storage > cstroke(org_path); + rasterizer.add_path(cstroke); + agg::ellipse circ(rectcenter.x, rectcenter.y, len, len); + agg::conv_stroke< agg::ellipse > c(circ); + rasterizer.add_path(c); + rsolid.color(rgba_origin); + render_scanlines(rsolid, false); + } + + rasterizer.reset(); + agg::path_storage org_path; + org_path.move_to(rectbound2[0].x, rectbound2[0].y); + org_path.line_to(rectbound2[1].x, rectbound2[1].y); + org_path.line_to(rectbound2[2].x, rectbound2[2].y); + org_path.line_to(rectbound2[3].x, rectbound2[3].y); + org_path.line_to(rectbound2[0].x, rectbound2[0].y); + agg::conv_stroke chstroke(org_path); + chstroke.width(1); + rsolid.color(rgba_origin); + rasterizer.add_path(chstroke); + if (rectbound2upd != -1) + { + agg::ellipse circ(rectbound2[rectbound2upd].x, rectbound2[rectbound2upd].y, + pointsys->scale, pointsys->scale); + agg::conv_contour< agg::ellipse > c(circ); + rasterizer.add_path(c); + } + if (rectbound2upd2 != -1) + { + agg::ellipse circ(rectbound2[rectbound2upd2].x, rectbound2[rectbound2upd2].y, + pointsys->scale, pointsys->scale); + agg::conv_contour< agg::ellipse > c(circ); + rasterizer.add_path(c); + } + render_scanlines(rsolid, false); + } + else + { + // outlines + agg::conv_stroke< agg::conv_transform< agg::path_storage > > bguidestroke(*rb_path); + bguidestroke.width(1); + rsolid.color(rgba_guideline); + rasterizer.add_path(bguidestroke); + render_scanlines(rsolid); + + agg::conv_stroke< agg::conv_curve< agg::conv_transform< agg::path_storage > > > stroke(*rm_curve); + stroke.width(1); + rsolid.color(rgba_outline); + rasterizer.add_path(stroke); + render_scanlines(rsolid); + + double diameter = pointsys->scale; + double radius = diameter / 2.0; + // hilite + if (hilite_cmd && hilite_cmd->type != M) + { + rasterizer.reset(); + agg::path_storage h_path; + AddDrawCmdToAGGPathStorage(hilite_cmd, h_path, HILITE); + agg::conv_transform< agg::path_storage> h_path_trans(h_path, mtx); + agg::conv_curve< agg::conv_transform< agg::path_storage> > curve(h_path_trans); + agg::conv_dash< agg::conv_curve< agg::conv_transform< agg::path_storage > > > d(curve); + d.add_dash(10,5); + agg::conv_stroke< agg::conv_dash< agg::conv_curve< agg::conv_transform< agg::path_storage > > > > stroke(d); + stroke.width(3); + rsolid.color(rgba_outline); + rasterizer.add_path(stroke); + render_scanlines(rsolid); + } + + // m_point + rasterizer.reset(); + DrawCmdList::iterator ci = cmds.begin(); + while (ci != cmds.end()) + { + double lx = (*ci)->m_point->x() * pointsys->scale + pointsys->originx - radius; + double ty = (*ci)->m_point->y() * pointsys->scale + pointsys->originy - radius; + agg::path_storage sqp = agghelper::RectanglePath(lx, lx + diameter, ty, ty + diameter); + agg::conv_contour< agg::path_storage > c(sqp); + rasterizer.add_path(c); + ci++; + } + render_scanlines_aa_solid(rbase, rgba_mainpoint); + + // control_points + rasterizer.reset(); + ci = cmds.begin(); + while (ci != cmds.end()) + { + PointList::iterator pi = (*ci)->controlpoints.begin(); + while (pi != (*ci)->controlpoints.end()) + { + agg::ellipse circ((*pi)->x() * pointsys->scale + pointsys->originx, + (*pi)->y() * pointsys->scale + pointsys->originy, radius, radius); + agg::conv_contour< agg::ellipse > c(circ); + rasterizer.add_path(c); + pi++; + } + ci++; + } + render_scanlines_aa_solid(rbase, rgba_controlpoint); + + // selection + rasterizer.reset(); + PointSet::iterator si = selected_points.begin(); + while (si != selected_points.end()) + { + agg::ellipse circ((*si)->x() * pointsys->scale + pointsys->originx, + (*si)->y() * pointsys->scale + pointsys->originy, radius + 3, radius + 3); + agg::conv_stroke< agg::ellipse > s(circ); + rasterizer.add_path(s); + si++; + } + render_scanlines_aa_solid(rbase, rgba_selectpoint); + + // hover + if (hilite_point) + { + rasterizer.reset(); + agg::ellipse circ(hilite_point->x() * pointsys->scale + pointsys->originx, + hilite_point->y() * pointsys->scale + pointsys->originy, radius + 3, radius + 3); + agg::conv_stroke< agg::ellipse > s(circ); + s.width(2); + rasterizer.add_path(s); + render_scanlines_aa_solid(rbase, rgba_selectpoint); + + rasterizer.reset(); + agg::gsv_text t; + t.flip(true); + t.size(8.0); + wxPoint pxy = hilite_point->ToWxPoint(true); + t.start_point(pxy.x + 5, pxy.y -5 ); + t.text(wxString::Format("%d,%d", hilite_point->x(), hilite_point->y())); + agg::conv_stroke< agg::gsv_text > pt(t); + pt.line_cap(agg::round_cap); + pt.line_join(agg::round_join); + pt.width(1.5); + rasterizer.add_path(pt); + rsolid.color(agg::rgba(0,0,0)); + render_scanlines(rsolid, false); + + rasterizer.reset(); + agg::path_storage sb_path; + sb_path.move_to(pxy.x, 0); + sb_path.line_to(pxy.x, hh); + sb_path.move_to(0, pxy.y); + sb_path.line_to(ww, pxy.y); + agg::conv_curve< agg::path_storage > curve(sb_path); + agg::conv_dash< agg::conv_curve< agg::path_storage > > d(curve); + d.add_dash(10,5); + agg::conv_stroke< agg::conv_dash< agg::conv_curve< agg::path_storage > > > stroke(d); + stroke.width(1); + rsolid.color(agg::rgba(0,0,0,0.5)); + rasterizer.add_path(stroke); + render_scanlines(rsolid); + } + + // selection box + if (lastDrag_left) + { + double x1 = lastDrag_left->x, y1 = lastDrag_left->y; + double x2 = dragAnchor_left->x, y2 = dragAnchor_left->y; + double lx, rx, ty, by; + if (x1 < x2) lx = x1, rx = x2; + else lx = x2, rx = x1; + if (y1 < y2) ty = y1, by = y2; + else ty = y2, by = y1; + rasterizer.reset(); + agg::path_storage sb_path = agghelper::RectanglePath(lx, rx, ty, by); + agg::conv_curve< agg::path_storage > curve(sb_path); + agg::conv_dash< agg::conv_curve< agg::path_storage > > d(curve); + d.add_dash(10,5); + agg::conv_stroke< agg::conv_dash< agg::conv_curve< agg::path_storage > > > stroke(d); + stroke.width(1.0); + rsolid.color(agg::rgba(0,0,0,0.5)); + rasterizer.add_path(stroke); + render_scanlines(rsolid); + } + } + } + + // ruler + int w, h; + GetClientSize( &w, &h ); + double scale = pointsys->scale; + double coeff = 9 / scale + 1; + int numdist = (int) floor(coeff) * 5; + { + rsolid.color(rgba_ruler_h); + rasterizer.reset(); + agg::path_storage rlr_path; + double start = pointsys->originx; + int t = - (int) floor(start / scale); + double s = (start + t * scale); + double collect = s; + int len; + + for (; s <= w; s += scale) + { + bool longtick = t % numdist == 0; + if (longtick) + { + len = 10; + agg::gsv_text txt; + txt.flip(true); + txt.size(6.0); + txt.start_point(s, 20); + txt.text(wxString::Format("%d", t)); + agg::conv_stroke< agg::gsv_text > pt(txt); + rasterizer.add_path(pt); + } + else + len = 5; + t++ ; + collect += scale; + if (collect > 5.0) + { + collect = 0.0; + rlr_path.move_to(s, 0); + rlr_path.line_to(s, len); + } + } + agg::conv_stroke< agg::path_storage > rlr_stroke(rlr_path); + rlr_stroke.width(1); + rasterizer.add_path(rlr_stroke); + render_scanlines(rsolid, false); + } + { + rasterizer.reset(); + rsolid.color(rgba_ruler_v); + agg::path_storage rlr_path; + double start = pointsys->originy; + int t = - (int) floor(start / scale); + double s = (start + t * scale); + double collect = 0; + int len; + + for (; s <= h; s += scale) + { + bool longtick = t % numdist == 0; + if (longtick) + { + len = 10; + agg::gsv_text txt; + txt.flip(true); + txt.size(6.0); + txt.start_point(12, s); + txt.text(wxString::Format("%d", t)); + agg::conv_stroke< agg::gsv_text > pt(txt); + rasterizer.add_path(pt); + } + else + len = 5; + t++ ; + collect += scale; + if (collect > 5.0) + { + collect = 0.0; + rlr_path.move_to(0, s); + rlr_path.line_to(len, s); + } + } + agg::conv_stroke< agg::path_storage > rlr_stroke(rlr_path); + rlr_stroke.width(1); + rasterizer.add_path(rlr_stroke); + render_scanlines(rsolid, false); + } + +} + +void ASSDrawCanvas::ReceiveBackgroundImageFileDropEvent(const wxString& filename) +{ + const wxChar *shortfname = wxFileName::FileName(filename).GetFullName().c_str(); + m_frame->SetStatusText(wxString::Format("Loading '%s' as canvas background ...", shortfname), 1); + wxImage img; + img.LoadFile(filename); + if (img.IsOk()) + { + SetBackgroundImage(img, filename); + } + m_frame->SetStatusText(wxT("Canvas background loaded"), 1); +} + +void ASSDrawCanvas::RemoveBackgroundImage() +{ + if (bgimg.bgimg) delete bgimg.bgimg; + bgimg.bgimg = NULL; + if (bgimg.bgbmp) delete bgimg.bgbmp; + bgimg.bgbmp = NULL; + bgimg.bgimgfile = ""; + RefreshDisplay(); + drag_mode = DRAGMODE(); + bgimg.alpha_dlg->Show(false); + m_frame->UpdateFrameUI(); +} + +void ASSDrawCanvas::SetBackgroundImage(const wxImage& img, wxString fname, bool ask4alpha) +{ + if (bgimg.bgimg) delete bgimg.bgimg; + bgimg.bgimg = new wxImage(img); + bgimg.bgimgfile = fname; + PrepareBackgroundBitmap(bgimg.alpha); + UpdateBackgroundImgScalePosition(true); + RefreshDisplay(); + m_frame->UpdateFrameUI(); + if (ask4alpha && m_frame->behaviors.autoaskimgopac) + AskUserForBackgroundAlpha(); +} + +void ASSDrawCanvas::AskUserForBackgroundAlpha() +{ + bgimg.alpha_slider->SetValue((int) (100 - bgimg.alpha * 100)); + bgimg.alpha_dlg->Show(); + SetFocus(); +} + +void ASSDrawCanvas::PrepareBackgroundBitmap(double alpha) +{ + if (alpha >= 0.0 && alpha <= 1.0) bgimg.alpha = alpha; + if (bgimg.bgimg == NULL) return; + if (bgimg.bgbmp) delete bgimg.bgbmp; + bgimg.bgbmp = new wxBitmap(*bgimg.bgimg); + PixelData data(*bgimg.bgbmp); + wxAlphaPixelFormat::ChannelType* pd = (wxAlphaPixelFormat::ChannelType*) &data.GetPixels().Data(); + const int stride = data.GetRowStride(); + if (stride < 0) + pd += (data.GetHeight() - 1) * stride; + bgimg.ibuf.attach(pd, data.GetWidth(), data.GetHeight(), stride); + + // apply alpha + rasterizer.reset(); + unsigned w = bgimg.bgbmp->GetWidth(), h = bgimg.bgbmp->GetHeight(); + bgimg.bg_path = agghelper::RectanglePath(0, w, 0, h); + agg::conv_contour< agg::path_storage > cont(bgimg.bg_path); + rasterizer.add_path(cont); + PixelFormat::AGGType pxt(bgimg.ibuf); + RendererBase rpxt(pxt); + agg::render_scanlines_aa_solid(rasterizer, scanline, rpxt, agg::rgba(color_bg.r / 255.0, color_bg.g / 255.0, color_bg.b / 255.0, bgimg.alpha)); +} + +void ASSDrawCanvas::UpdateBackgroundImgScalePosition(bool firsttime) +{ + if (bgimg.bgbmp == NULL) return; + // transform the enclosing polygon + unsigned w = bgimg.bgbmp->GetWidth(), h = bgimg.bgbmp->GetHeight(); + bgimg.bg_path = agghelper::RectanglePath(0, w, 0, h); + // linear interpolation on image buffer + wxRealPoint center, disp; + double scale; + if (firsttime) // first time + { + bgimg.img_mtx = agg::trans_affine(); + scale = 1.0; + center = wxRealPoint(0.0, 0.0); + disp = wxRealPoint(0.0, 0.0); + bgimg.path_mtx = bgimg.img_mtx; + } + else + { + wxRealPoint d_disp(bgimg.new_disp.x - bgimg.disp.x, bgimg.new_disp.y - bgimg.disp.y); + scale = bgimg.new_scale; + disp = bgimg.disp; + center = bgimg.new_center; + if (bgimg.scale == scale) + { + bgimg.img_mtx.invert(); + bgimg.img_mtx *= agg::trans_affine_translation(d_disp.x, d_disp.y); + d_disp.x /= scale; + d_disp.y /= scale; + } + else + { + d_disp.x /= scale; + d_disp.y /= scale; + bgimg.img_mtx = agg::trans_affine(); + disp.x += (center.x - bgimg.center.x) * (1.0 - 1.0 / bgimg.scale); + disp.y += (center.y - bgimg.center.y) * (1.0 - 1.0 / bgimg.scale); + bgimg.img_mtx *= agg::trans_affine_translation(-center.x + disp.x, -center.y + disp.y); + bgimg.img_mtx *= agg::trans_affine_scaling(scale); + bgimg.img_mtx *= agg::trans_affine_translation(center.x + d_disp.x, center.y + d_disp.y); + } + bgimg.path_mtx = bgimg.img_mtx; + bgimg.img_mtx.invert(); + disp.x += d_disp.x; + disp.y += d_disp.y; + } + //update + bgimg.scale = scale; + bgimg.center = center; + bgimg.disp = disp; + bgimg.new_scale = scale; + bgimg.new_center = center; + bgimg.new_disp = disp; +} + +bool ASSDrawCanvas::GetBackgroundInfo(unsigned& w, unsigned& h, wxRealPoint& disp, double& scale) +{ + if (!HasBackgroundImage()) return false; + w = bgimg.bgbmp->GetWidth(), h = bgimg.bgbmp->GetHeight(); + double t, l; + agg::conv_transform trr(bgimg.bg_path, bgimg.path_mtx); + trr.rewind(0); + trr.vertex(&l, &t); + disp = wxRealPoint(l, t); + scale = bgimg.scale; + return true; +} + +void ASSDrawCanvas::UpdateNonUniformTransformation() +{ + double bound[8] = { + rectbound2[0].x, rectbound2[0].y, + rectbound2[1].x, rectbound2[1].y, + rectbound2[2].x, rectbound2[2].y, + rectbound2[3].x, rectbound2[3].y }; + agg::path_storage trans; + unsigned vertices = backupcmds.total_vertices(); + double x, y; + + //if (draw_mode == MODE_NUT_BILINEAR) + //{ + agg::trans_bilinear trans_b(rectbound[0].x, rectbound[0].y, rectbound[2].x, rectbound[2].y, bound); + agg::conv_transform transb(backupcmds, trans_b); + transb.rewind(0); + for (int i = 0; i < vertices; i++) + { + transb.vertex(&x, &y); + trans.move_to(x, y); + } + //} + /* else + { + agg::trans_perspective trans_p(rectbound[0].x, rectbound[0].y, rectbound[2].x, rectbound[2].y, bound); + agg::conv_transform transp(backupcmds, trans_p); + transp.rewind(0); + for (int i = 0; i < vertices; i++) + { + transp.vertex(&x, &y); + trans.move_to(x, y); + } + } + */ + trans.rewind(0); + for (DrawCmdList::iterator iterate = cmds.begin(); iterate != cmds.end(); iterate++) + { + DrawCmd* cmd = (*iterate); + for (PointList::iterator iterate2 = cmd->controlpoints.begin(); iterate2 != cmd->controlpoints.end(); iterate2++) + { + double x, y; + trans.vertex(&x, &y); + int wx, wy; + pointsys->FromWxPoint ( wxPoint((int)x, (int)y), wx, wy ); + (*iterate2)->setXY(wx, wy); + } + double x, y; + trans.vertex(&x, &y); + int wx, wy; + pointsys->FromWxPoint ( wxPoint((int)x, (int)y), wx, wy ); + (*iterate)->m_point->setXY(wx, wy); + } + +} + +void ASSDrawCanvas::CustomOnKeyDown(wxKeyEvent &event) +{ + int keycode = event.GetKeyCode(); + //m_frame->SetStatusText(wxString::Format("Key: %d", keycode)); + double scrollamount = (event.GetModifiers() == wxMOD_CMD? 10.0:1.0); + if (event.GetModifiers() == wxMOD_SHIFT) + { + MODE d_mode = GetDrawMode(); + if ((int) d_mode > (int) MODE_ARR && (int) d_mode < (int) MODE_SCALEROTATE) + { + mode_b4_shift = d_mode; + SetDrawMode( MODE_ARR ); + m_frame->UpdateFrameUI(); + } + } + else + { + switch (keycode) + { + case WXK_PAGEUP: + ChangeZoomLevel( 1.0 /scrollamount, wxPoint( (int) pointsys->originx, (int) pointsys->originy ) ); + RefreshDisplay(); + break; + case WXK_PAGEDOWN: + ChangeZoomLevel( - 1.0 /scrollamount, wxPoint( (int) pointsys->originx, (int) pointsys->originy ) ); + RefreshDisplay(); + break; + case WXK_UP: + MoveCanvas(0.0, -scrollamount); + RefreshDisplay(); + break; + case WXK_DOWN: + MoveCanvas(0.0, scrollamount); + RefreshDisplay(); + break; + case WXK_LEFT: + MoveCanvas(-scrollamount, 0.0); + RefreshDisplay(); + break; + case WXK_RIGHT: + MoveCanvas(scrollamount, 0.0); + RefreshDisplay(); + break; + case WXK_TAB: + if (mousedownAt_point == NULL && !IsTransformMode() && cmds.size() > 0) + { + if (pointedAt_point == NULL) + { + Point *nearest = NULL; + double dist = 0.0; + DrawCmdList::iterator it = cmds.begin(); + while(it != cmds.end()) + { + wxPoint point = (*it)->m_point->ToWxPoint(); + double distance = sqrt(pow(point.x - mouse_point.x, 2) + pow(point.y - mouse_point.y, 2)); + if (nearest == NULL || distance < dist) + { + nearest = (*it)->m_point; + dist = distance; + } + PointList::iterator it2 = (*it)->controlpoints.begin(); + while (it2 != (*it)->controlpoints.end()) + { + wxPoint point = (*it2)->ToWxPoint(); + double distance = sqrt(pow(point.x - mouse_point.x, 2) + pow(point.y - mouse_point.y, 2)); + if (nearest == NULL || distance < dist) + { + nearest = (*it2); + dist = distance; + } + it2++; + } + it++; + } + if (nearest != NULL) + { + wxPoint point = nearest->ToWxPoint(); + WarpPointer(point.x, point.y); + } + } + else + { + Point *warpto = NULL; + if (pointedAt_point->type == MP) + { + if (pointedAt_point->cmd_next != NULL) + { + if (pointedAt_point->cmd_next->controlpoints.size() > 0) + warpto = pointedAt_point->cmd_next->controlpoints.front(); + else + warpto = pointedAt_point->cmd_next->m_point; + } + } + else + { + PointList::iterator it = pointedAt_point->cmd_main->controlpoints.begin(); + while (*it != pointedAt_point) it++; + it++; + if (it == pointedAt_point->cmd_main->controlpoints.end()) + warpto = pointedAt_point->cmd_main->m_point; + else + warpto = *it; + } + if (warpto == NULL) + warpto = cmds.front()->m_point; + wxPoint point = warpto->ToWxPoint(); + WarpPointer(point.x, point.y); + } + } + break; + default: + event.Skip(); + } + } +} + +void ASSDrawCanvas::CustomOnKeyUp(wxKeyEvent &event) +{ + int keycode = event.GetKeyCode(); + if (event.GetModifiers() != wxMOD_SHIFT && (int) mode_b4_shift > (int) MODE_ARR) + { + SetDrawMode( mode_b4_shift ); + m_frame->UpdateFrameUI(); + mode_b4_shift = MODE_ARR; + } +} + +void ASSDrawCanvas::OnAlphaSliderChanged(wxScrollEvent &event) +{ + double pos = (double) event.GetPosition(); + PrepareBackgroundBitmap(1.0 - pos / 100.0); + RefreshDisplay(); +} + +void ASSDrawCanvas::CustomOnMouseCaptureLost(wxMouseCaptureLostEvent &event) +{ + if (capturemouse_left) + ProcessOnMouseLeftUp(); + + if (capturemouse_right) + ProcessOnMouseRightUp(); +} + +void UndoRedo::Import(ASSDrawCanvas *canvas, bool prestage, wxString cmds) +{ + if (prestage) + { + this->cmds = cmds; + this->backupcmds.free_all(); + this->backupcmds.concat_path(canvas->backupcmds); + for (int i = 0; i < 4; i++) + { + this->rectbound[i] = canvas->rectbound[i]; + this->rectbound2[i] = canvas->rectbound2[i]; + this->backup[i] = canvas->backup[i]; + } + this->isshapetransformable = canvas->isshapetransformable; + } + else + { + this->originx = canvas->pointsys->originx; + this->originy = canvas->pointsys->originy; + this->scale = canvas->pointsys->scale; + + this->bgimgfile = canvas->bgimg.bgimgfile; + this->bgdisp = canvas->bgimg.disp; + this->bgcenter = canvas->bgimg.center; + this->bgscale = canvas->bgimg.scale; + this->bgalpha = canvas->bgimg.alpha; + this->c1cont = canvas->PrepareC1ContData(); + this->draw_mode = canvas->draw_mode; + } +} + +void UndoRedo::Export(ASSDrawCanvas *canvas) +{ + canvas->pointsys->originx = this->originx; + canvas->pointsys->originy = this->originy; + canvas->pointsys->scale = this->scale; + canvas->ParseASS( this->cmds ); + DrawCmdList::iterator it1 = canvas->cmds.begin(); + std::vector< bool >::iterator it2 = this->c1cont.begin(); + for(; it1 != canvas->cmds.end() && it2 != this->c1cont.end(); it1++, it2++) + if (*it2 && (*it1)->type == B) + static_cast(*it1)->C1Cont = true; + + if (canvas->bgimg.bgimgfile != this->bgimgfile) + { + canvas->RemoveBackgroundImage(); + if (!this->bgimgfile.IsSameAs("") && ::wxFileExists(this->bgimgfile)) + { + canvas->bgimg.alpha = this->bgalpha; + canvas->ReceiveBackgroundImageFileDropEvent(this->bgimgfile); + } + } + else + { + canvas->bgimg.new_scale = this->bgscale; + canvas->bgimg.new_center = this->bgcenter; + canvas->bgimg.new_disp = this->bgdisp; + canvas->bgimg.alpha = this->bgalpha; + canvas->UpdateBackgroundImgScalePosition(); + } + + + //canvas->SetDrawMode(this->draw_mode); + canvas->draw_mode = this->draw_mode; + if (canvas->IsTransformMode()) + { + canvas->backupcmds.free_all(); + canvas->backupcmds.concat_path(this->backupcmds); + for (int i = 0; i < 4; i++) + { + canvas->rectbound[i] = this->rectbound[i]; + canvas->rectbound2[i] = this->rectbound2[i]; + canvas->backup[i] = this->backup[i]; + } + canvas->UpdateNonUniformTransformation(); + canvas->InitiateDraggingIfTransformMode(); + canvas->rectbound2upd = -1; + canvas->rectbound2upd2 = -1; + canvas->isshapetransformable = this->isshapetransformable; + } +} diff --git a/assdraw/src/canvas.hpp b/assdraw/src/canvas.hpp new file mode 100644 index 000000000..23f4abf4b --- /dev/null +++ b/assdraw/src/canvas.hpp @@ -0,0 +1,303 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: canvas.hpp +// Purpose: header file for ASSDraw main canvas class +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +#include "engine.hpp" +#include "enums.hpp" + +#include +#include +#include + +#include "agg_span_allocator.h" +#include "agg_span_interpolator_linear.h" +#include "agg_span_image_filter_rgb.h" +#include "agg_span_image_filter_rgba.h" +#include "agg_image_accessors.h" +#include "agg_conv_clip_polygon.h" + +class ASSDrawFrame; +class ASSDrawCanvas; + +struct UndoRedo +{ + wxString cmds; + wxString desc; + double originx, originy, scale; + + std::vector< bool > c1cont; + wxString bgimgfile; + wxRealPoint bgdisp, bgcenter; + double bgscale, bgalpha; + + MODE draw_mode; + agg::path_storage backupcmds; + wxRealPoint rectbound[4], rectbound2[4], backup[4]; + bool isshapetransformable; + + void Import(ASSDrawCanvas *canvas, bool prestage, wxString cmds = _T("")); + void Export(ASSDrawCanvas *canvas); + +}; + +// for multiple point selection +enum SELECTMODE { NEW, ADD, DEL }; + +class ASSDrawCanvas: public ASSDrawEngine, public wxClientData +{ +public: + ASSDrawCanvas( wxWindow *parent, ASSDrawFrame *frame, int extraflags = 0 ); + + // destructor + ~ASSDrawCanvas(); + + virtual void ResetEngine(bool addM); + virtual void SetPreviewMode( bool mode ); + virtual bool IsPreviewMode() { return preview_mode; } + virtual void ParseASS(wxString str, bool addundo = false); + + virtual void SetDrawMode( MODE mode ); + virtual MODE GetDrawMode() { return draw_mode; } + virtual bool IsTransformMode(); + virtual void SetDragMode( DRAGMODE mode ); + virtual DRAGMODE GetDragMode() { return drag_mode; } + virtual void RefreshDisplay(); + virtual bool CanZoom(); + virtual bool CanMove(); + + virtual void OnMouseMove(wxMouseEvent &event); + virtual void OnMouseLeftUp(wxMouseEvent &event); + virtual void OnMouseLeftDown(wxMouseEvent &event); + virtual void OnMouseRightUp(wxMouseEvent &event); + virtual void OnMouseRightDown(wxMouseEvent &event); + virtual void OnMouseRightDClick(wxMouseEvent &event); + virtual void OnMouseWheel(wxMouseEvent &event); + virtual void CustomOnKeyDown(wxKeyEvent &event); + virtual void CustomOnKeyUp(wxKeyEvent &event); + virtual void ChangeZoomLevel(double zoomamount, wxPoint bgzoomctr); + virtual void ChangeZoomLevelTo(double zoom, wxPoint bgzoomctr); + virtual void ChangeDrawingZoomLevel(double zoom); + virtual void ChangeBackgroundZoomLevel(double zoom, wxRealPoint newcenter); + virtual void MoveCanvas(double xamount, double yamount); + virtual void MoveCanvasOriginTo(double originx, double originy); + virtual void MoveCanvasDrawing(double xamount, double yamount); + virtual void MoveCanvasBackground(double xamount, double yamount); + virtual void OnSelect_ConvertLineToBezier(wxCommandEvent& WXUNUSED(event)); + virtual void OnSelect_ConvertBezierToLine(wxCommandEvent& WXUNUSED(event)); + virtual void OnSelect_C1ContinuityBezier(wxCommandEvent& WXUNUSED(event)); + virtual void OnSelect_Move00Here(wxCommandEvent& WXUNUSED(event)); + void OnAlphaSliderChanged(wxScrollEvent &event); + + // to replace _PointSystem() that has been made protected + double GetScale() { return pointsys->scale; } + double GetOriginX() { return pointsys->originx; } + double GetOriginY() { return pointsys->originy; } + + // undo/redo system + virtual void AddUndo( wxString desc ); + virtual bool UndoOrRedo(bool isundo); + virtual bool Undo(); + virtual bool Redo(); + virtual wxString GetTopUndo(); + virtual wxString GetTopRedo(); + virtual void RefreshUndocmds(); + + virtual bool HasBackgroundImage() { return bgimg.bgimg != NULL; } + virtual void RemoveBackgroundImage(); + virtual void ReceiveBackgroundImageFileDropEvent(const wxString& filename); + virtual void SetBackgroundImage(const wxImage& img, wxString fname = _T(""), bool ask4alpha = true); + virtual void PrepareBackgroundBitmap(double alpha); + virtual void AskUserForBackgroundAlpha(); + virtual bool GetBackgroundInfo(unsigned& w, unsigned& h, wxRealPoint& disp, double& scale); + + agg::rgba rgba_shape_normal, rgba_outline, rgba_guideline; + agg::rgba rgba_mainpoint, rgba_controlpoint, rgba_selectpoint; + agg::rgba rgba_origin, rgba_ruler_h, rgba_ruler_v; + +protected: + + typedef PixelFormat::AGGType::color_type color_type; + typedef agg::span_interpolator_linear<> interpolator_type; + typedef agg::span_image_filter_rgb_bilinear_clip span_gen_type; + + // The GUI window + ASSDrawFrame* m_frame; + + // highlight mechanism + DrawCmd* hilite_cmd; + Point* hilite_point; + + // mouse capture + bool capturemouse_left, capturemouse_right; + virtual void CustomOnMouseCaptureLost(wxMouseCaptureLostEvent &event); + virtual void ProcessOnMouseLeftUp(); + virtual void ProcessOnMouseRightUp(); + + // selection mechanism + PointSet selected_points; + + // if it has status bar + bool hasStatusBar; + + // some mouse readings + Point* mousedownAt_point; + Point* pointedAt_point; + Point* dblclicked_point_right; + wxPoint mouse_point; + + // The wxPoint being dragged by left button + wxPoint* dragAnchor_left; + wxPoint* lastDrag_left; + + // The wxPoint being dragged by right button + wxPoint* dragAnchor_right; + wxPoint* lastDrag_right; + + // true if the drawing origin (0, 0) is being dragged + bool dragOrigin; + + // The newest command being initialized thru dragging action + DrawCmd* newcommand; + + // the draw mode + MODE draw_mode; + DRAGMODE drag_mode; + + // holding shift key temporarily switches to drag mode (MODE_ARR) + // so we want to save the mode before the key-down to restore it on key-up + MODE mode_b4_shift; + + // true if preview mode (i.e don't draw anything except the shape itself; + // also draw the shape as closed) + bool preview_mode; + + // background image! + struct + { + agg::rendering_buffer ibuf; + wxImage *bgimg; + wxBitmap *bgbmp; + wxString bgimgfile; + agg::path_storage bg_path; + agg::span_allocator spanalloc; + //span_gen_type spangen; + agg::trans_affine img_mtx, path_mtx; + + wxRealPoint disp, center, new_disp, new_center; + double scale, new_scale, alpha; + wxDialog* alpha_dlg; + wxSlider* alpha_slider; + } bgimg; + + // Undo/redo system (simply stores the ASS commands) + std::list undos; + std::list redos; + UndoRedo _undo; + + // last action and commands (for undo/redo system) + wxString undodesc; + + wxString oldasscmds; + + // was preview_mode + //bool was_preview_mode; + + PointSystem* _PointSystem() { return pointsys; } + + // for Undo/Redo system + virtual void PrepareUndoRedo(UndoRedo& ur, bool prestage, wxString cmds, wxString desc); + + // -------------------- points highlight/selection --------------------------- + + // set command and point to highlight + virtual void SetHighlighted ( DrawCmd* cmd, Point* point ); + + // selects all points within (lx, ty) , (rx, by) returns # of selected points + virtual int SelectPointsWithin( int lx, int rx, int ty, int by, SELECTMODE smode = NEW ); + virtual void ClearPointsSelection(); + virtual SELECTMODE GetSelectMode(wxMouseEvent &event); + + // -------------------- misc --------------------------- + + // non-uniform transformation + virtual bool InitiateDraggingIfTransformMode(); + virtual void UpdateTranformModeRectCenter(); + virtual bool GetThe4thPoint(double ox, double oy, double a1x, double a1y, double a2x, double a2y, double *x, double *y); + enum { NONE, LEFT, RIGHT } backupowner; + agg::path_storage backupcmds; + int rectbound2upd, rectbound2upd2; + wxRealPoint rectbound[4], rectbound2[4], backup[4], rectcenter; + bool isshapetransformable; + + // do the real drawing + virtual void DoDraw( RendererBase& rbase, RendererPrimitives& rprim, RendererSolid& rsolid, agg::trans_affine& mtx ); + + // update background image scale & position + virtual void UpdateBackgroundImgScalePosition(bool firsttime = false); + + // perform extra stuff other than calling ASSDrawEngine::ConnectSubsequentCmds + virtual void ConnectSubsequentCmds (DrawCmd* cmd1, DrawCmd* cmd2); + + // make sure the c1 continuity is followed after performing a drag-point action + virtual void EnforceC1Continuity (DrawCmd* cmd, Point* pnt); + + // after the bounding quadrangle has changed, update the shape to fill up inside it + virtual void UpdateNonUniformTransformation(); + + friend class UndoRedo; + + DECLARE_EVENT_TABLE() +}; + +class ASSDrawFileDropTarget : public wxFileDropTarget +{ +public: + ASSDrawFileDropTarget(ASSDrawCanvas *canvas): wxFileDropTarget() + { + m_canvas = canvas; + } + + virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) + { + m_canvas->ReceiveBackgroundImageFileDropEvent(filenames.Item(0)); + } + +protected: + ASSDrawCanvas *m_canvas; + +}; diff --git a/assdraw/src/canvas_mouse.cpp b/assdraw/src/canvas_mouse.cpp new file mode 100644 index 000000000..22b64a5f4 --- /dev/null +++ b/assdraw/src/canvas_mouse.cpp @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "canvas_mouse.hpp" +#include "canvas.hpp" + +DEFINE_EVENT_TYPE( wxEVT_MOUSEONCANVAS ) + + +ASSDrawMouseOnCanvasEvent::ASSDrawMouseOnCanvasEvent(const ASSDrawCanvas* canvas) + : wxNotifyEvent(), + _canvas(canvas) +{ + _data = NULL; +} + +wxEvent* ASSDrawMouseOnCanvasEvent::Clone() +{ + ASSDrawMouseOnCanvasEvent *clone = new ASSDrawMouseOnCanvasEvent(_canvas); + clone->SetData(_data); + return clone; +} + +void ASSDrawMouseOnCanvasEvent::SetData(MouseOnCanvasData *data) +{ + _data = data; +} + +MouseOnCanvasData* ASSDrawMouseOnCanvasEvent::GetData() +{ + return _data; +} diff --git a/assdraw/src/canvas_mouse.hpp b/assdraw/src/canvas_mouse.hpp new file mode 100644 index 000000000..d64011336 --- /dev/null +++ b/assdraw/src/canvas_mouse.hpp @@ -0,0 +1,85 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "_common.hpp" +#include "enums.hpp" +#include "engine.hpp" + +#include + +struct MouseOnCanvasData +{ + MODE mode; + wxMouseEvent event; + enum { NONE, LEFT, RIGHT, BOTH } button; + + Point* mousedownAt_point; + Point* pointedAt_point; + Point* dblclicked_point_right; + + wxPoint mouse_point; + wxPoint* dragAnchor_left; + wxPoint* lastDrag_left; + wxPoint* dragAnchor_right; + wxPoint* lastDrag_right; +}; + +class ASSDrawCanvas; + +class ASSDrawMouseOnCanvasEvent : public wxNotifyEvent +{ +public: + ASSDrawMouseOnCanvasEvent(const ASSDrawCanvas* canvas); + + wxEvent* Clone(); + + void SetData(MouseOnCanvasData *data); + + MouseOnCanvasData* GetData(); + +private: + const ASSDrawCanvas* _canvas; + MouseOnCanvasData* _data; + +}; + +DECLARE_EVENT_TYPE( wxEVT_MOUSEONCANVAS, -1 ) + +typedef void (wxEvtHandler::*wxMouseOnCanvasEventFunction)(ASSDrawMouseOnCanvasEvent&); + +#define EVT_MOUSEONCANVAS(fn) \ + DECLARE_EVENT_TABLE_ENTRY( wxEVT_MOUSEONCANVAS, -1, -1, \ + (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) (wxNotifyEventFunction) \ + wxStaticCastEvent( wxMouseOnCanvasEventFunction, & fn ), (wxObject *) NULL ), + +class ASSDrawMouseOnCanvasHandler +{ + + +}; diff --git a/assdraw/src/cmd.cpp b/assdraw/src/cmd.cpp new file mode 100644 index 000000000..5c3ee3eee --- /dev/null +++ b/assdraw/src/cmd.cpp @@ -0,0 +1,212 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: cmd.cpp +// Purpose: ASSDraw drawing command classes +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#include "cmd.hpp" // the header for this file +#include +// ---------------------------------------------------------------------------- +// DrawCmd_M +// ---------------------------------------------------------------------------- + +// constructor +DrawCmd_M::DrawCmd_M ( int x, int y, PointSystem *ps, DrawCmd *prev ) + : DrawCmd ( x, y, ps, prev ) +{ + type = M; +} + +// to ASS drawing command +wxString DrawCmd_M::ToString() +{ + return wxString::Format(_T("m %d %d"), m_point->x(), m_point->y()); +} + + + +// ---------------------------------------------------------------------------- +// DrawCmd_L +// ---------------------------------------------------------------------------- + +// constructor +DrawCmd_L::DrawCmd_L ( int x, int y, PointSystem *ps, DrawCmd *prev ) + : DrawCmd ( x, y, ps, prev ) +{ + type = L; +} + +// to ASS drawing command +wxString DrawCmd_L::ToString() +{ + return wxString::Format(wxT("l %d %d"), m_point->x(), m_point->y()); +} + + + +// ---------------------------------------------------------------------------- +// DrawCmd_B +// ---------------------------------------------------------------------------- + +// constructor +DrawCmd_B::DrawCmd_B +( int x, int y, int x1, int y1, int x2, int y2, PointSystem *ps, DrawCmd *prev ) + : DrawCmd ( x, y, ps, prev ) +{ + type = B; + controlpoints.push_back( new Point(x1, y1, ps, CP, this, 1) ); + controlpoints.push_back( new Point(x2, y2, ps, CP, this, 2) ); + initialized = true; + C1Cont = false; +} + +// constructor +DrawCmd_B::DrawCmd_B ( int x, int y, PointSystem *ps, DrawCmd *prev ) + : DrawCmd ( x, y, ps, prev ) +{ + type = B; + initialized = false; + C1Cont = false; +} + +// initialize; generate control points +void DrawCmd_B::Init ( unsigned n ) +{ + // Ignore if this is already initted + if (initialized) return; + + wxPoint wx0 = prev->m_point->ToWxPoint(); + wxPoint wx1 = m_point->ToWxPoint(); + int xdiff = (wx1.x - wx0.x) / 3; + int ydiff = (wx1.y - wx0.y) / 3; + int xg, yg; + + // first control + m_point->pointsys->FromWxPoint( wx0.x + xdiff, wx0.y + ydiff, xg, yg ); + controlpoints.push_back( new Point( xg, yg, m_point->pointsys, CP, this, 1 ) ); + + // second control + m_point->pointsys->FromWxPoint( wx1.x - xdiff, wx1.y - ydiff, xg, yg ); + controlpoints.push_back( new Point( xg, yg, m_point->pointsys, CP, this, 2 ) ); + + initialized = true; + +} + +// to ASS drawing command +wxString DrawCmd_B::ToString() +{ + PointList::iterator iterate = controlpoints.begin(); + Point* c1 = (*iterate++); + Point* c2 = (*iterate); + if (initialized) + return wxString::Format(wxT("b %d %d %d %d %d %d"), c1->x(), c1->y(), c2->x(), c2->y(), m_point->x(), m_point->y()); + else + return wxString::Format(wxT("b ? ? ? ? %d %d"), m_point->x(), m_point->y()); +} + + +// ---------------------------------------------------------------------------- +// DrawCmd_S +// ---------------------------------------------------------------------------- + +// constructor +DrawCmd_S::DrawCmd_S + ( int x, int y, PointSystem *ps, DrawCmd *prev ) + : DrawCmd ( x, y, ps, prev ) +{ + type = S; + initialized = false; + closed = false; +} + +// constructor +DrawCmd_S::DrawCmd_S + ( int x, int y, std::vector< int > vals, PointSystem *ps, DrawCmd *prev ) + : DrawCmd ( x, y, ps, prev ) +{ + type = S; + std::vector< int >::iterator it = vals.begin(); + unsigned n = 0; + while (it != vals.end()) + { + int ix = *it; it++; + int iy = *it; it++; + n++; + //::wxLogMessage("%d %d\n", ix, iy); + controlpoints.push_back( new Point( ix, iy, ps, CP, this, n ) ); + } + + initialized = true; + closed = false; +} + +// initialize; generate control points +void DrawCmd_S::Init(unsigned n) +{ + // Ignore if this is already initted + if (initialized) return; + + wxPoint wx0 = prev->m_point->ToWxPoint(); + wxPoint wx1 = m_point->ToWxPoint(); + int xdiff = (wx1.x - wx0.x) / 3; + int ydiff = (wx1.y - wx0.y) / 3; + int xg, yg; + + // first control + m_point->pointsys->FromWxPoint( wx0.x + xdiff, wx0.y + ydiff, xg, yg ); + controlpoints.push_back( new Point( xg, yg, m_point->pointsys, CP, this, 1 ) ); + + // second control + m_point->pointsys->FromWxPoint( wx1.x - xdiff, wx1.y - ydiff, xg, yg ); + controlpoints.push_back( new Point( xg, yg, m_point->pointsys, CP, this, 2 ) ); + + initialized = true; + +} + +// to ASS drawing command +wxString DrawCmd_S::ToString() +{ + PointList::iterator iterate = controlpoints.begin(); + wxString assout = _T("s"); + for (; iterate != controlpoints.end(); iterate++) + { + if (initialized) + assout = wxString::Format(wxT("%s %d %d"), assout.c_str(), (*iterate)->x(), (*iterate)->y()); + else + assout = wxString::Format(wxT("%s ? ?"), assout.c_str()); + } + assout = wxString::Format(wxT("%s %d %d"), assout.c_str(), m_point->x(), m_point->y()); + if (closed) assout = wxString::Format(wxT("%s c"), assout.c_str()); + return assout; +} diff --git a/assdraw/src/cmd.hpp b/assdraw/src/cmd.hpp new file mode 100644 index 000000000..25ef16102 --- /dev/null +++ b/assdraw/src/cmd.hpp @@ -0,0 +1,112 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/////////////////////////////////////////////////////////////////////////////// +// Name: cmd.hpp +// Purpose: header file for ASSDraw drawing command classes +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "engine.hpp" // include the engine header for DrawCmd +#include // ok, we use vector too + +// this header file declares the following classes +class DrawCmd_M; +class DrawCmd_L; +class DrawCmd_B; + +// The M command +class DrawCmd_M: public DrawCmd +{ +public: + // Constructor + DrawCmd_M ( int x, int y, PointSystem *ps, DrawCmd *prev ); + + // to ASS drawing command + wxString ToString(); + +}; + +// The L command +class DrawCmd_L: public DrawCmd +{ +public: + // Constructor + DrawCmd_L ( int x, int y, PointSystem *ps, DrawCmd *prev ); + + // to ASS drawing command + wxString ToString(); + +}; + +// The B command +class DrawCmd_B: public DrawCmd +{ +public: + // Constructor + DrawCmd_B ( int x, int y, int x1, int y1, int x2, int y2, PointSystem *ps, DrawCmd *prev ); + + // Special constructor where only m_point is defined + // Need to call Init() to generate the controls + DrawCmd_B ( int x, int y, PointSystem *ps, DrawCmd *prev ); + + // Init this B command; generate controlpoints + void Init ( unsigned n = 0 ); + + // to ASS drawing command + wxString ToString(); + + //special + bool C1Cont; + +}; + +// The S command +class DrawCmd_S: public DrawCmd +{ +public: + // Constructor + DrawCmd_S ( int x, int y, PointSystem *ps, DrawCmd *prev ); + + // Constructor (with points info) + DrawCmd_S ( int x, int y, std::vector< int > vals, PointSystem *ps, DrawCmd *prev ); + + // Init this S command; generate controlpoints + void Init ( unsigned n = 0 ); + + // to ASS drawing command + wxString ToString(); + + // special + bool closed; +}; + diff --git a/assdraw/src/dlgctrl.cpp b/assdraw/src/dlgctrl.cpp new file mode 100644 index 000000000..1ee6f9b89 --- /dev/null +++ b/assdraw/src/dlgctrl.cpp @@ -0,0 +1,372 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: dlgctrl.cpp +// Purpose: custom dialogs and controls +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#include "assdraw.hpp" + +BEGIN_EVENT_TABLE(ASSDrawSrcTxtCtrl, wxTextCtrl) + EVT_CHAR(ASSDrawSrcTxtCtrl::CustomOnChar) + EVT_TEXT(wxID_ANY, ASSDrawSrcTxtCtrl::CustomOnText) +END_EVENT_TABLE() + +BEGIN_EVENT_TABLE(ASSDrawTransformDlg, wxDialog) + EVT_COMBOBOX(-1, ASSDrawTransformDlg::OnTemplatesCombo) +END_EVENT_TABLE() + +//BEGIN_EVENT_TABLE(ASSDrawCanvasRecenterButton, wxWindow) +//END_EVENT_TABLE() + +// ---------------------------------------------------------------------------- +// ASSDrawSrcTxtCtrl +// ---------------------------------------------------------------------------- + +ASSDrawSrcTxtCtrl::ASSDrawSrcTxtCtrl(wxWindow *parent, ASSDrawFrame *frame) + : wxTextCtrl(parent, wxID_ANY, "", __DPDS__ , wxTE_MULTILINE ) +{ + m_frame = frame; +} + +void ASSDrawSrcTxtCtrl::CustomOnChar(wxKeyEvent &event) +{ + switch (event.GetKeyCode()) + { + case WXK_RETURN: + m_frame->UpdateASSCommandStringFromSrcTxtCtrl(GetValue()); + break; + case WXK_TAB: + break; //do nothing + default: + //m_frame->SetTitle(wxString::Format("Key: %d", event.GetKeyCode())); + event.Skip(true); + } + + //SetBackgroundColour(IsModified()? wxColour(0xFF, 0xFF, 0x99):*wxWHITE); +} + +void ASSDrawSrcTxtCtrl::CustomOnText(wxCommandEvent &event) +{ + //SetBackgroundColour(IsModified()? wxColour(0xFF, 0xFF, 0x99):*wxWHITE); +} + +// ---------------------------------------------------------------------------- +// ASSDrawTransformDlg +// ---------------------------------------------------------------------------- + +ASSDrawTransformDlg::ASSDrawTransformDlg(ASSDrawFrame* parent) + : wxDialog(parent, -1, wxString(_T("Transform"))) +{ + m_frame = parent; + + wxBoxSizer* sizer_main = new wxBoxSizer(wxVERTICAL); + this->SetSizer(sizer_main); + + wxBoxSizer* sizer_templates = new wxBoxSizer(wxHORIZONTAL); + sizer_main->Add(sizer_templates, 0, wxGROW|wxLEFT, 5); + + sizer_templates->Add(new wxStaticText( this, -1, _("Templates"), __DPDS__ , 0 ), + 0, wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE, 5); + + combo_templates = new wxComboBox( this, -1, combo_templatesStrings[0], __DPDS__ , 10, combo_templatesStrings, wxCB_READONLY ); + sizer_templates->Add(combo_templates, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxFlexGridSizer* sizer_fields = new wxFlexGridSizer(3, 4, 0, 0); + sizer_main->Add(sizer_fields, 0, wxALIGN_CENTER_HORIZONTAL|wxLEFT, 5); + + int flag_txtctrl = wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL; + int flag_statictxt = wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL|wxADJUST_MINSIZE; + + sizer_fields->Add(new wxStaticText( this, -1, _("m11"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_m11 = new wxTextCtrl( this, -1, _T("1.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_m11, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("m12"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_m12 = new wxTextCtrl( this, -1, _T("0.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_m12, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("m21"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_m21 = new wxTextCtrl( this, -1, _T("0.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_m21, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("m22"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_m22 = new wxTextCtrl( this, -1, _T("1.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_m22, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("mx"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_mx = new wxTextCtrl( this, -1, _T("0.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_mx, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("my"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_my = new wxTextCtrl( this, -1, _T("0.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_my, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("nx"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_nx = new wxTextCtrl( this, -1, _T("0.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_nx, 0, flag_txtctrl, 5); + + sizer_fields->Add(new wxStaticText( this, -1, _("ny"), __DPDS__ , 0 ), + 0, flag_statictxt, 5); + + txtctrl_ny = new wxTextCtrl( this, -1, _T("0.0"), __DPDS__ , wxTE_RIGHT ); + sizer_fields->Add(txtctrl_ny, 0, flag_txtctrl, 5); + + wxBitmap staticbmp_bmp(wxBITMAP(transform)); + wxStaticBitmap* staticbmp = new wxStaticBitmap( this, -1, staticbmp_bmp, wxDefaultPosition, wxSize(224, 56), 0 ); + sizer_main->Add(staticbmp, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); + + wxStdDialogButtonSizer* sizer_stdbutt = new wxStdDialogButtonSizer; + + sizer_main->Add(sizer_stdbutt, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); + wxButton* button_ok = new wxButton( this, wxID_OK, _("&OK"), __DPDS__ , 0 ); + sizer_stdbutt->AddButton(button_ok); + + wxButton* button_cancel = new wxButton( this, wxID_CANCEL, _("&Cancel"), __DPDS__ , 0 ); + sizer_stdbutt->AddButton(button_cancel); + + sizer_stdbutt->Realize(); + + sizer_main->Fit(this); + +} + +void ASSDrawTransformDlg::OnTemplatesCombo(wxCommandEvent &event) +{ + int pos = -1; + for (int i = 0; i < combo_templatesCount; i++) + if (combo_templatesStrings[i].IsSameAs(((wxComboBox *) event.GetEventObject())->GetValue())) + { + pos = i; + break; + } + if (pos == -1) + return; + + txtctrl_m11->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f1) ); + txtctrl_m12->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f2) ); + txtctrl_m21->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f3) ); + txtctrl_m22->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f4) ); + txtctrl_mx->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f5) ); + txtctrl_my->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f6) ); + txtctrl_nx->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f7) ); + txtctrl_ny->SetValue( wxString::Format("%.1f", combo_templatesValues[pos].f8) ); +} + +void ASSDrawTransformDlg::EndModal(int retCode) +{ + if (retCode != wxID_OK) + { + wxDialog::EndModal(retCode); + return; + } + + bool ok = true; + + ok = ok && txtctrl_m11->GetValue().ToDouble( &xformvals.f1 ); + ok = ok && txtctrl_m12->GetValue().ToDouble( &xformvals.f2 ); + ok = ok && txtctrl_m21->GetValue().ToDouble( &xformvals.f3 ); + ok = ok && txtctrl_m22->GetValue().ToDouble( &xformvals.f4 ); + ok = ok && txtctrl_mx->GetValue().ToDouble( &xformvals.f5 ); + ok = ok && txtctrl_my->GetValue().ToDouble( &xformvals.f6 ); + ok = ok && txtctrl_nx->GetValue().ToDouble( &xformvals.f7 ); + ok = ok && txtctrl_ny->GetValue().ToDouble( &xformvals.f8 ); + + if (ok) + wxDialog::EndModal(wxID_OK); + else + wxMessageBox("One or more values entered are not real numbers.\nPlease fix.", _T("Value error"), wxOK | wxICON_INFORMATION, m_frame); + +} + + +ASSDrawAboutDlg::ASSDrawAboutDlg(ASSDrawFrame *parent, unsigned timeout) + : wxDialog(parent, wxID_ANY, wxString(TITLE), __DPDS__ , wxSIMPLE_BORDER), time_out(timeout) +{ + SetBackgroundColour(*wxWHITE); + htmlwin = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(396, 200), wxHW_DEFAULT_STYLE | wxSIMPLE_BORDER); + htmlwin->SetPage( +" \ +

ASSDraw3 is a tool for designing shapes to be used in ASS subtitle file. \ +

To add lines or curves, initiate the draw mode by clicking on the drawing tools. \ +Then, either click on empty space or drag from an existing point to add the new lines/curves. \ +Control points for Bezier curves are generated once you release the mouse button. \ +

To modify shapes, drag their points (squares) and control points (circles) in the drag mode. \ +

Some tips & tricks: \ +

    \ +
  • Set background image by dragging image file from explorer onto the canvas \ +
  • Use the Shapes Library to store your drawings \ +
  • Ctrl-Z for undo, Ctrl-Y for redo \ +
  • Use your mousewheel to zoom in/out (PageUp/PageDown keys work too) \ +
  • Dragging with right mouse button moves the drawing and/or background image around. \ +
  • Double clicking with right mouse button for popup menus. \ +
  • Holding shift key while in the draw mode temporarily switches to the drag mode \ +
  • The shapes origin (coordinate [0, 0] depicted by the small cross) is draggable \ +
\ +

Acknowledgements: \ +

\ +

ai-chan recommends Aegisub for all your subtitle and typesetting needs! \ +" + ); + htmlwin->Connect(wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler(ASSDrawAboutDlg::OnURL), NULL, this); + + wxFlexGridSizer *sizer = new wxFlexGridSizer(1); + sizer->AddGrowableCol(0); + //sizer->AddGrowableRow(1); + + sizer->Add(new BigStaticBitmapCtrl(this, wxBITMAP(assdraw3_), *wxWHITE, this), 1, wxEXPAND); + sizer->Add(htmlwin, 1, wxLEFT | wxRIGHT, 2); + sizer->Add(new wxStaticText(this, wxID_ANY, wxString::Format("Version: %s", VERSION)), 1, wxEXPAND | wxALL, 2); + sizer->Add(new wxButton(this, wxID_OK), 0, wxALIGN_CENTER | wxBOTTOM, 10); + SetSizer(sizer); + sizer->Layout(); + sizer->Fit(this); + + Center(); + //if (CanSetTransparent()) SetTransparent(0xCC); + + timer.SetOwner(this); + Connect(wxEVT_TIMER, wxTimerEventHandler(ASSDrawAboutDlg::OnTimeout)); + Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(ASSDrawAboutDlg::OnMouseEnterWindow)); +} + +ASSDrawAboutDlg::~ASSDrawAboutDlg() +{ + timer.Stop(); +} + +int ASSDrawAboutDlg::ShowModal() +{ + if (time_out > 0) + timer.Start(time_out * 1000, true); + wxDialog::ShowModal(); +} + +void ASSDrawAboutDlg::OnURL(wxHtmlLinkEvent &event) +{ + wxString URL(event.GetLinkInfo().GetHref()); + if (URL.StartsWith("http://")) + ::wxLaunchDefaultBrowser(URL); + else + event.Skip(true); +} + +void ASSDrawAboutDlg::OnTimeout(wxTimerEvent& event) +{ + if (IsShown()) + EndModal(wxID_OK); +} + +void ASSDrawAboutDlg::OnMouseEnterWindow(wxMouseEvent& event) +{ + // if mouse enters this dialog, stop the timout timer + // and dialog will only close through user input + timer.Stop(); +} + +BEGIN_EVENT_TABLE(BigStaticBitmapCtrl, wxPanel) + EVT_PAINT(BigStaticBitmapCtrl::OnPaint) + EVT_MOTION (BigStaticBitmapCtrl::OnMouseMove) + EVT_LEFT_UP(BigStaticBitmapCtrl::OnMouseLeftUp) + EVT_LEFT_DOWN(BigStaticBitmapCtrl::OnMouseLeftDown) +END_EVENT_TABLE() + +BigStaticBitmapCtrl::BigStaticBitmapCtrl(wxWindow *parent, wxBitmap bmap, wxColour bgcol, wxWindow *todrag) + : wxPanel(parent, wxID_ANY) +{ + bitmap = bmap; + bgbrush = wxBrush(bgcol); + window_to_drag = todrag; + SetSize(bitmap.GetWidth(), bitmap.GetHeight()); + Refresh(); +} + +BigStaticBitmapCtrl::~BigStaticBitmapCtrl() +{ +} + +void BigStaticBitmapCtrl::OnPaint(wxPaintEvent& event) +{ + wxPaintDC dc(this); + dc.SetBackground(bgbrush); + dc.Clear(); + dc.DrawBitmap(bitmap, wxPoint(0,0)); +} + +void BigStaticBitmapCtrl::OnMouseLeftDown(wxMouseEvent &event) +{ + if (window_to_drag != NULL) + { + dragpoint = event.GetPosition(); + } + CaptureMouse(); +} + +void BigStaticBitmapCtrl::OnMouseLeftUp(wxMouseEvent &event) +{ + ReleaseMouse(); +} + +void BigStaticBitmapCtrl::OnMouseMove(wxMouseEvent &event) +{ + if (window_to_drag != NULL && event.Dragging() && HasCapture()) + { + wxPoint npoint(event.GetPosition()); + wxPoint wndpos = window_to_drag->GetScreenPosition(); + wxPoint thispos = this->GetScreenPosition(); + //ReleaseMouse(); + window_to_drag->Move(wndpos.x + npoint.x - dragpoint.x, + wndpos.y + npoint.y - dragpoint.y); + //CaptureMouse(); + if (thispos == this->GetScreenPosition()) // if this ctrl did not move when window_to_drag moved + dragpoint = npoint; + } + event.Skip(true); +} diff --git a/assdraw/src/dlgctrl.hpp b/assdraw/src/dlgctrl.hpp new file mode 100644 index 000000000..b5225d181 --- /dev/null +++ b/assdraw/src/dlgctrl.hpp @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "_common.hpp" +#include +#include + + +class ASSDrawFrame; + +class ASSDrawSrcTxtCtrl : public wxTextCtrl +{ +public: + ASSDrawSrcTxtCtrl(wxWindow *parent, ASSDrawFrame *frame); + virtual void CustomOnChar(wxKeyEvent &event); + virtual void CustomOnText(wxCommandEvent &event); + +protected: + ASSDrawFrame *m_frame; + DECLARE_EVENT_TABLE() +}; + +struct EightDouble +{ + double f1, f2, f3, f4, f5, f6, f7, f8; +}; + +class ASSDrawTransformDlg : public wxDialog +{ + +public: + ASSDrawTransformDlg(ASSDrawFrame* parent); + void OnTemplatesCombo(wxCommandEvent &event); + void EndModal(int retCode); + + ASSDrawFrame* m_frame; + wxComboBox* combo_templates; + wxTextCtrl* txtctrl_m11; + wxTextCtrl* txtctrl_m12; + wxTextCtrl* txtctrl_m21; + wxTextCtrl* txtctrl_m22; + wxTextCtrl* txtctrl_mx; + wxTextCtrl* txtctrl_my; + wxTextCtrl* txtctrl_nx; + wxTextCtrl* txtctrl_ny; + EightDouble xformvals; + + static wxString combo_templatesStrings[]; + static int combo_templatesCount; + static EightDouble combo_templatesValues[]; + + DECLARE_EVENT_TABLE() + +}; + +class ASSDrawAboutDlg : public wxDialog +{ +public: + ASSDrawAboutDlg(ASSDrawFrame *parent, unsigned timeout = 0); + virtual ~ASSDrawAboutDlg(); + virtual void OnURL(wxHtmlLinkEvent &event); + virtual int ShowModal(); + virtual void OnTimeout(wxTimerEvent& event); + virtual void OnMouseEnterWindow(wxMouseEvent& event); + +protected: + wxTimer timer; + wxHtmlWindow *htmlwin; + const unsigned time_out; +}; + +class BigStaticBitmapCtrl : public wxPanel +{ +public: + BigStaticBitmapCtrl(wxWindow *parent, wxBitmap bmap, wxColour bgcol, wxWindow *todrag = NULL); + virtual ~BigStaticBitmapCtrl(); + virtual void OnPaint(wxPaintEvent& event); + virtual void OnMouseLeftDown(wxMouseEvent &event); + virtual void OnMouseLeftUp(wxMouseEvent &event); + virtual void OnMouseMove(wxMouseEvent &event); + +protected: + wxBitmap bitmap; + wxBrush bgbrush; + wxWindow *window_to_drag; + wxPoint dragpoint, wndpos; + + DECLARE_EVENT_TABLE() +}; diff --git a/assdraw/src/engine.cpp b/assdraw/src/engine.cpp new file mode 100644 index 000000000..fa23da684 --- /dev/null +++ b/assdraw/src/engine.cpp @@ -0,0 +1,749 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: engine.cpp +// Purpose: ASSDraw drawing engine +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#include "engine.hpp" // the header +#include "cmd.hpp" // we need the drawing command classes declaration +#include // we use string tokenizer +#include // ok, we use vector too +#include +#include + + + +// ---------------------------------------------------------------------------- +// Point +// ---------------------------------------------------------------------------- + +// constructor +Point::Point ( int _x, int _y, PointSystem* ps, POINTTYPE t, DrawCmd* cmd, unsigned n ) +{ + x_ = _x; + y_ = _y; + pointsys = ps; + cmd_main = cmd; + cmd_next = NULL; + type = t; + isselected = false; + num = n; +} + +// setters +void Point::setXY( int _x, int _y) +{ + x_ = _x; + y_ = _y; +} + +// simply returns true if px and py are the coordinate values +bool Point::IsAt( int px, int py ) +{ + return (x_ == px && y_ == py ); +} + +// convert this point to wxPoint using scale and originx, originy +wxPoint Point::ToWxPoint ( bool useorigin ) +{ + if (useorigin) + return pointsys->ToWxPoint( x_, y_ ); + else + return *(new wxPoint(x_ * (int) pointsys->scale, y_ * (int) pointsys->scale )); +} + +// check if wxpoint is nearby this point +bool Point::CheckWxPoint ( wxPoint wxpoint ) +{ + wxPoint p = ToWxPoint(); + int cx, cy; + pointsys->FromWxPoint( wxpoint, cx, cy ); + //delete &p; + return (x_ == cx && y_ == cy ); +} + + + +// ---------------------------------------------------------------------------- +// DrawCmd +// ---------------------------------------------------------------------------- + +// constructor +DrawCmd::DrawCmd ( int x, int y, PointSystem *ps, DrawCmd *pv ) +{ + m_point = new Point ( x, y, ps, MP, this ); + m_point->cmd_main = this; + prev = pv; + dobreak = false; + invisible = false; +} + +// destructor +DrawCmd::~DrawCmd ( ) +{ + if (m_point) + delete m_point; + for (PointList::iterator iter_cpoint = controlpoints.begin(); + iter_cpoint != controlpoints.end(); iter_cpoint++) + delete (*iter_cpoint); +} + + + +// ---------------------------------------------------------------------------- +// ASSDrawEngine +// ---------------------------------------------------------------------------- + +BEGIN_EVENT_TABLE(ASSDrawEngine, GUI::AGGWindow) + EVT_PAINT (ASSDrawEngine::OnPaint) +END_EVENT_TABLE() + +// constructor +ASSDrawEngine::ASSDrawEngine( wxWindow *parent, int extraflags ) + : GUI::AGGWindow(parent, -1, wxDefaultPosition, wxDefaultSize, + wxNO_FULL_REPAINT_ON_RESIZE | extraflags ) +{ + pointsys = new PointSystem(1, 0, 0) ; + refresh_called = false; + fitviewpoint_hmargin = 10; + fitviewpoint_vmargin = 10; + setfitviewpoint = false; + rgba_shape = agg::rgba(0,0,1); + color_bg = PixelFormat::AGGType::color_type(255, 255, 255); + drawcmdset = wxT("m n l b s p c _"); //the spaces and underscore are in there for a reason, guess? + ResetEngine(); +} + +ASSDrawEngine::~ASSDrawEngine() +{ + ResetEngine ( false ); + delete pointsys; +} + +// parse ASS draw commands; returns the number of parsed commands +int ASSDrawEngine::ParseASS ( wxString str ) +{ + ResetEngine( false ); + str.Replace(_T("\t"), _T("")); + str.Replace(_T("\r"), _T("")); + str.Replace(_T("\n"), _T("")); + str = str.Lower() + wxT(" _ _"); + // we don't use regex because the pattern is too simple + wxStringTokenizer tkz( str, wxT(" ") ); + wxString currcmd(wxT("")); + std::vector val; + wxString token; + long tmp_int; + + bool n_collected = false; + DrawCmd_S *s_command = NULL; + wxPoint tmp_n_pnt; + + while ( tkz.HasMoreTokens() ) + { + token = tkz.GetNextToken(); + + if ( drawcmdset.Find(token) > -1 ) + { + bool done; + + do { + done = true; + + // N + if (currcmd.IsSameAs(wxT("n")) && val.size() >= 2) + { + tmp_n_pnt.x = val[0], tmp_n_pnt.y = val[1]; + n_collected = true; + } + else if(n_collected) + { + AppendCmd ( L, tmp_n_pnt.x, tmp_n_pnt.y ); + n_collected = false; + } + + if (s_command != NULL) + { + bool ends = true; + if (currcmd.IsSameAs(wxT("p"))&& val.size() >= 2) + { + s_command->m_point->type = CP; + s_command->m_point->num = s_command->controlpoints.size() + 1; + s_command->controlpoints.push_back(s_command->m_point); + s_command->m_point = new Point(val[0], val[1], pointsys, MP, s_command); + ends = false; + } + else if (currcmd.IsSameAs(wxT("c"))) + s_command->closed = true; + + if (ends) + { + AppendCmd(s_command); + s_command = NULL; + } + } + + // M + if (currcmd.IsSameAs(wxT("m")) && val.size() >= 2) + AppendCmd ( M, val[0], val[1] ); + + // L + if (currcmd.IsSameAs(wxT("l")) && val.size() >= 2) + { + AppendCmd ( L, val[0], val[1] ); + val.erase(val.begin(), val.begin()+2); + // L is greedy + if (val.size() >= 2) + done = false; + } + + // B + if (currcmd.IsSameAs(wxT("b")) && val.size() >= 6) + { + AppendCmd ( new DrawCmd_B(val[4], val[5], val[0], val[1], + val[2], val[3], pointsys, LastCmd()) ); + val.erase(val.begin(), val.begin()+6); + // so is B + if (val.size() >= 6) + done = false; + } + + // S + if (currcmd.IsSameAs(wxT("s")) && val.size() >= 6) + { + int num = (val.size() / 2) * 2; + std::vector val2; + int i = 0; + for (; i < num - 2; i++) + val2.push_back(val[i]); + + s_command = new DrawCmd_S(val[num - 2], val[num - 1], val2, pointsys, LastCmd()); + } + // more to come later + } while (!done); + + val.clear(); + currcmd = token; + } + else if (token.ToLong( &tmp_int )) + { + val.push_back( (int) tmp_int ); + } + } + + return (int) cmds.size(); +} + +// generate ASS draw commands +wxString ASSDrawEngine::GenerateASS ( ) +{ + wxString output = _T(""); + for (DrawCmdList::iterator iterate = cmds.begin(); iterate != cmds.end(); iterate++) + output = output + (*iterate)->ToString() + _T(" "); + return output; +} + +// reset; delete all points and add a new M(0,0) +void ASSDrawEngine::ResetEngine() +{ + ResetEngine(true); +} + +// reset; delete all points and add a new M(0,0) if addM == true +void ASSDrawEngine::ResetEngine( bool addM ) +{ + for (DrawCmdList::iterator iterate = cmds.begin(); iterate != cmds.end(); iterate++) + delete (*iterate); + cmds.clear(); + if (addM) AppendCmd( M, 0, 0 ); +} + +// Create draw command of type 'type' and m_point (x, y), append to the +// list and return it +DrawCmd* ASSDrawEngine::AppendCmd ( CMDTYPE type, int x, int y ) +{ + + // use a variation of this method + return AppendCmd( NewCmd( type, x, y ) ); +} + +// Append draw command +DrawCmd* ASSDrawEngine::AppendCmd ( DrawCmd* cmd ) +{ + // no NULL command! + if (cmd == NULL) return NULL; + + // set dependency of this command on the m_point of the last command + if (!cmds.empty()) + ConnectSubsequentCmds( cmds.back(), cmd ); + else + { + // since this is the first command, if it's not an M make it into one + if (cmd->type != M) + cmd = NewCmd( M, cmd->m_point->x(), cmd->m_point->y() ); + ConnectSubsequentCmds( NULL, cmd ); + } + // put it in the list + cmds.push_back( cmd ); + + return cmd; +} + +// create draw command of type 'type' and m_point (x, y), insert to the +// list after the _cmd and return it +DrawCmd* ASSDrawEngine::InsertCmd ( CMDTYPE type, int x, int y, DrawCmd* _cmd ) +{ + // prepare the new DrawCmd + DrawCmd* c = NewCmd( type, x, y ); + + // use a variation of this method + InsertCmd( c, _cmd ); + + return NULL; +} + +// insert draw command cmd after _cmd +void ASSDrawEngine::InsertCmd ( DrawCmd* cmd, DrawCmd* _cmd ) +{ + DrawCmdList::iterator iterate = cmds.begin(); + for (; iterate != cmds.end() && *iterate != _cmd; iterate++) + { + // do nothing + } + + if (iterate == cmds.end()) + { + AppendCmd( cmd ); + } + else + { + iterate++; + if (iterate != cmds.end()) + { + ConnectSubsequentCmds( cmd, (*iterate) ); + } + cmds.insert( iterate, cmd ); + ConnectSubsequentCmds( _cmd, cmd ); + } +} + +DrawCmd* ASSDrawEngine::NewCmd ( CMDTYPE type, int x, int y ) +{ + DrawCmd* c = NULL; + + switch (type) + { + case M: + c = new DrawCmd_M(x, y, pointsys, LastCmd()); + break; + case L: + c = new DrawCmd_L(x, y, pointsys, LastCmd()); + break; + case B: + c = new DrawCmd_B(x, y, pointsys, LastCmd()); + break; + case S: + c = new DrawCmd_S(x, y, pointsys, LastCmd()); + break; + } + return c; +} + +// returns the iterator for the list +DrawCmdList::iterator ASSDrawEngine::Iterator ( ) +{ + return cmds.begin(); +} + +// returns the 'end' iterator for the list +DrawCmdList::iterator ASSDrawEngine::IteratorEnd ( ) +{ + return cmds.end(); +} + +// returns the last command in the list +DrawCmd* ASSDrawEngine::LastCmd () +{ + if (cmds.size() == 0) + return NULL; + else + return cmds.back(); +} + +// move all points by relative amount of x, y coordinates +void ASSDrawEngine::MovePoints ( int x, int y ) +{ + DrawCmdList::iterator iterate = cmds.begin(); + PointList::iterator iterate2; + + for (; iterate != cmds.end(); iterate++) + { + (*iterate)->m_point->setXY( (*iterate)->m_point->x() + x, (*iterate)->m_point->y() + y ); + for (iterate2 = (*iterate)->controlpoints.begin(); + iterate2 != (*iterate)->controlpoints.end(); iterate2++) + { + (*iterate2)->setXY( (*iterate2)->x() + x, (*iterate2)->y() + y ); + } + } +} + +// transform all points using the calculation: +// | (m11) (m12) | x | (x - mx) | + | nx | +// | (m21) (m22) | | (y - my) | | ny | +void ASSDrawEngine::Transform( float m11, float m12, float m21, float m22, + float mx, float my, float nx, float ny ) +{ + DrawCmdList::iterator iterate = cmds.begin(); + PointList::iterator iterate2; + float x, y; + for (; iterate != cmds.end(); iterate++) + { + x = ((float) (*iterate)->m_point->x()) - mx; + y = ((float) (*iterate)->m_point->y()) - my; + (*iterate)->m_point->setXY((int) (x * m11 + y * m12 + nx), (int) (x * m21 + y * m22 + ny) ); + for (iterate2 = (*iterate)->controlpoints.begin(); + iterate2 != (*iterate)->controlpoints.end(); iterate2++) + { + x = ((float) (*iterate2)->x()) - mx; + y = ((float) (*iterate2)->y()) - my; + (*iterate2)->setXY((int) (x * m11 + y * m12 + nx), (int) (x * m21 + y * m22 + ny) ); + } + } +} + +// returns some DrawCmd if its m_point = (x, y) +DrawCmd* ASSDrawEngine::PointAt ( int x, int y ) +{ + DrawCmd* c = NULL; + DrawCmdList::iterator iterate = cmds.begin(); + + for (; iterate != cmds.end(); iterate++) + { + if ( (*iterate)->m_point->IsAt( x, y ) ) + c = (*iterate); + } + + //delete &iterate; + + return c; +} + +// returns some DrawCmd if one of its control point = (x, y) +// also set &point to refer to that control point +DrawCmd* ASSDrawEngine::ControlAt ( int x, int y, Point* &point ) +{ + DrawCmd* c = NULL; + point = NULL; + DrawCmdList::iterator cmd_iterator = cmds.begin(); + PointList::iterator pnt_iterator; + PointList::iterator end; + + for (; cmd_iterator != cmds.end(); cmd_iterator++) + { + pnt_iterator = (*cmd_iterator)->controlpoints.begin(); + end = (*cmd_iterator)->controlpoints.end(); + for (; pnt_iterator != end; pnt_iterator++) + { + if ( (*pnt_iterator)->IsAt( x, y ) ) + { + c = (*cmd_iterator); + point = (*pnt_iterator); + } + } + } + + return c; +} + +// attempts to delete a commmand, returns true|false if successful|fail +bool ASSDrawEngine::DeleteCommand ( DrawCmd* cmd ) +{ + + DrawCmdList::iterator iterate = cmds.begin(); + // can't delete the first command without deleting other commands first + if ( cmd == (*iterate) && cmds.size() > 1) return false; + + DrawCmd* lastiter = NULL; + + for (; iterate != cmds.end(); iterate++) + { + if ( cmd == (*iterate) ) + { + iterate++; + DrawCmd* nxt = (iterate != cmds.end()? (*iterate):NULL); + ConnectSubsequentCmds( lastiter, nxt ); + iterate--; + cmds.erase( iterate ); + delete cmd; + break; + } + else + lastiter = (*iterate); + } + + return true; +} + +// set stuff to connect two drawing commands cmd1 and cmd2 such that +// cmd1 comes right before cmd2 +void ASSDrawEngine::ConnectSubsequentCmds (DrawCmd* cmd1, DrawCmd* cmd2) +{ + if (cmd1 != NULL) + { + cmd1->m_point->cmd_next = cmd2; + } + + if (cmd2 != NULL) + { + cmd2->prev = cmd1; + } +} + +void ASSDrawEngine::RefreshDisplay() +{ + if (!refresh_called) + { + Refresh(); + refresh_called = true; + } +} + +void ASSDrawEngine::OnPaint(wxPaintEvent& event) +{ + draw(); + onPaint(event); + if (setfitviewpoint) + { + FitToViewPoint( fitviewpoint_hmargin, fitviewpoint_vmargin ); + setfitviewpoint = false; + RefreshDisplay(); + } +} + +void ASSDrawEngine::draw() +{ + refresh_called = false; + + PixelFormat::AGGType pixf(rBuf); + RendererBase rbase(pixf); + RendererPrimitives rprim(rbase); + RendererSolid rsolid(rbase); + + agg::trans_affine mtx; + ConstructPathsAndCurves(mtx, rm_path, rb_path, rm_curve); + + rasterizer.reset(); + update_rendered_bound_coords(true); + DoDraw(rbase, rprim, rsolid, mtx); + + delete rm_path, rb_path, rm_curve; +} + +void ASSDrawEngine::ConstructPathsAndCurves(agg::trans_affine& mtx, + trans_path*& _rm_path, trans_path*& _rb_path, agg::conv_curve*& _rm_curve) +{ + mtx *= agg::trans_affine_scaling(pointsys->scale); + mtx *= agg::trans_affine_translation(pointsys->originx, pointsys->originy); + + m_path.remove_all(); + b_path.remove_all(); + + DrawCmdList::iterator ci = cmds.begin(); + while (ci != cmds.end()) + { + AddDrawCmdToAGGPathStorage(*ci, m_path); + AddDrawCmdToAGGPathStorage(*ci, b_path, CTRL_LN); + ci++; + } + _rm_path = new trans_path(m_path, mtx); + _rb_path = new trans_path(b_path, mtx); + _rm_curve = new agg::conv_curve(*rm_path); +} + +void ASSDrawEngine::DoDraw( RendererBase& rbase, RendererPrimitives& rprim, RendererSolid& rsolid, agg::trans_affine& mtx ) +{ + Draw_Clear( rbase ); + Draw_Draw( rbase, rprim, rsolid, mtx, rgba_shape ); +} + +void ASSDrawEngine::Draw_Clear( RendererBase& rbase ) +{ + rbase.clear(color_bg); +} + +void ASSDrawEngine::Draw_Draw( RendererBase& rbase, RendererPrimitives& rprim, RendererSolid& rsolid, agg::trans_affine& mtx, agg::rgba color ) +{ + agg::conv_contour< agg::conv_curve< agg::conv_transform< agg::path_storage > > > contour(*rm_curve); + rasterizer.add_path(contour); + render_scanlines_aa_solid(rbase, color); +} + +void ASSDrawEngine::AddDrawCmdToAGGPathStorage(DrawCmd* cmd, agg::path_storage& path, DRAWCMDMODE mode) +{ + if (mode == HILITE && cmd->prev) + path.move_to(cmd->prev->m_point->x(), cmd->prev->m_point->y()); + + switch(cmd->type) + { + case M: + path.move_to(cmd->m_point->x(),cmd->m_point->y()); + break; + + case B: + if (cmd->initialized) + { + //path.move_to(cmd->prev->m_point->x(),cmd->prev->m_point->y()); + PointList::iterator iterate = cmd->controlpoints.begin(); + int x[2], y[2]; + x[0] = (*iterate)->x(); + y[0] = (*iterate)->y(); + iterate++; + x[1] = (*iterate)->x(); + y[1] = (*iterate)->y(); + path.curve4(x[0], y[0], x[1], y[1], cmd->m_point->x(),cmd->m_point->y()); + break; + } + + case L: + if (mode == CTRL_LN) + path.move_to(cmd->m_point->x(),cmd->m_point->y()); + else + path.line_to(cmd->m_point->x(),cmd->m_point->y()); + break; + + case S: + unsigned np = cmd->controlpoints.size(); + agg::pod_array m_polygon(np * 2); + unsigned _pn = 0; + PointList::iterator iterate = cmd->controlpoints.begin(); + while (iterate != cmd->controlpoints.end()) + { + m_polygon[_pn] = (*iterate)->x(); _pn++; + m_polygon[_pn] = (*iterate)->y(); _pn++; + iterate++; + } + //m_polygon[_pn++] = cmd->m_point->x(); + //m_polygon[_pn++] = cmd->m_point->y(); + //path.move_to(cmd->prev->m_point->x(),cmd->prev->m_point->y()); + if (mode == CTRL_LN) + { + _pn = 0; + while (_pn < np * 2) + { + path.line_to((int) m_polygon[_pn],(int) m_polygon[_pn + 1]); + _pn += 2; + } + path.line_to(cmd->m_point->x(), cmd->m_point->y()); + } + else + { + //path.line_to((int) m_polygon[0],(int) m_polygon[1]); + aggpolygon poly(&m_polygon[0], np, false, false); + agg::conv_bcspline bspline(poly); + bspline.interpolation_step(0.01); + agg::path_storage npath; + npath.join_path(bspline); + path.join_path(npath); + if (mode == HILITE) + path.move_to((int) m_polygon[np * 2 - 2], (int) m_polygon[np * 2 - 1] ); + path.line_to(cmd->m_point->x(), cmd->m_point->y()); + } + break; + } + +} + +void ASSDrawEngine::render_scanlines_aa_solid(RendererBase& rbase, agg::rgba rgba, bool affectboundaries) +{ + agg::render_scanlines_aa_solid(rasterizer, scanline, rbase, rgba); + if (affectboundaries) update_rendered_bound_coords(); +} + +void ASSDrawEngine::render_scanlines(RendererSolid& rsolid, bool affectboundaries) +{ + agg::render_scanlines(rasterizer, scanline, rsolid); + if (affectboundaries) update_rendered_bound_coords(); +} + +void ASSDrawEngine::update_rendered_bound_coords(bool rendered_fresh) +{ + int min_x = rasterizer.min_x(); + int min_y = rasterizer.min_y(); + int max_x = rasterizer.max_x(); + int max_y = rasterizer.max_y(); + if (min_x < rendered_min_x || rendered_fresh) rendered_min_x = min_x; + if (min_y < rendered_min_y || rendered_fresh) rendered_min_y = min_y; + if (max_x > rendered_max_x || rendered_fresh) rendered_max_x = max_x; + if (max_y > rendered_max_y || rendered_fresh) rendered_max_y = max_y; +} + +void ASSDrawEngine::FitToViewPoint(int hmargin, int vmargin) +{ + wxSize v = GetClientSize(); + double wide = rendered_max_x - rendered_min_x; + double high = rendered_max_y - rendered_min_y; + double widthratio = (double) (v.x - hmargin * 2) / wide; + double heightratio = (double) (v.y - vmargin * 2) / high; + double ratio = (widthratio < heightratio? widthratio:heightratio); + pointsys->scale = pointsys->scale * ratio; + if (pointsys->scale < 0.01) pointsys->scale = 0.01; + double new_min_x = pointsys->originx + (rendered_min_x - pointsys->originx) * ratio; + double new_max_x = pointsys->originx + (rendered_max_x - pointsys->originx) * ratio; + pointsys->originx += (v.x - new_max_x + new_min_x) / 2 - new_min_x; + double new_min_y = pointsys->originy + (rendered_min_y - pointsys->originy) * ratio; + double new_max_y = pointsys->originy + (rendered_max_y - pointsys->originy) * ratio; + pointsys->originy += (v.y - new_max_y + new_min_y) / 2 - new_min_y; + RefreshDisplay(); +} + +void ASSDrawEngine::SetFitToViewPointOnNextPaint(int hmargin, int vmargin) +{ + if (vmargin >= 0) fitviewpoint_vmargin = vmargin; + if (hmargin >= 0) fitviewpoint_hmargin = hmargin; + setfitviewpoint = true; +} + +std::vector< bool > ASSDrawEngine::PrepareC1ContData() +{ + std::vector< bool > out; + for (DrawCmdList::iterator it = cmds.begin(); it != cmds.end(); it++) + { + bool c1 = false; + if ((*it)->type == B) + { + DrawCmd_B *cmdb = static_cast(*it); + c1 = cmdb->C1Cont; + } + out.push_back(c1); + } + return out; +} diff --git a/assdraw/src/engine.hpp b/assdraw/src/engine.hpp new file mode 100644 index 000000000..b730ec6fc --- /dev/null +++ b/assdraw/src/engine.hpp @@ -0,0 +1,406 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: engine.hpp +// Purpose: header file for ASSDraw drawing engine +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "_common.hpp" + +// we use these 2 standard libraries +#include +#include +#include +#include + +// agg support +#include "wxAGG/AGGWindow.h" +#include "agg_color_rgba.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_scanline_p.h" +#include "agg_renderer_base.h" +#include "agg_renderer_primitives.h" +#include "agg_renderer_scanline.h" +#include "agg_path_storage.h" +#include "agg_curves.h" +#include "agg_conv_curve.h" +#include "agg_conv_contour.h" +#include "agg_conv_stroke.h" +#include "agg_conv_bcspline.h" +#include "agg_math.h" + +#define DEFAULT_SCALE 10 + +// this header file declare the following classes +class DrawEngine; +class Point; +class PointSystem; +class DrawCmd; + +typedef std::list DrawCmdList; +typedef std::list PointList; +typedef std::set PointSet; + +// Command type +enum CMDTYPE +{ + M = 0, + N = 1, + L = 2, + B = 3, + S = 4, + P = 5, + C = 6 +}; + +// Point type +enum POINTTYPE +{ + MP, // main point + CP // control point +}; + +// A PointSystem is a centralized entity holding the parameters: +// scale, originx and originy, all of which are needed by Point +class PointSystem +{ +public: + double scale, originx, originy; + + PointSystem ( double sc = 1.0, double origx = 0.0, double origy = 0.0 ) + { Set( sc, origx, origy ); } + + // set scale, originx and originy; + void Set( double sc, double origx, double origy ) + { scale = sc, originx = origx, originy = origy; } + + wxRealPoint ToWxRealPoint ( double x, double y ) + { return wxRealPoint( originx + x * scale, originy + y * scale ); } + + // given drawing command coordinates returns the wxPoint on the GUI + wxPoint ToWxPoint ( double x, double y ) + { return wxPoint( (int) (originx + x * scale), (int) (originy + y * scale) ); } + + // given wxPoint on the GUI returns the nearest drawing command coords + void FromWxPoint ( int wxpx, int wxpy, int &x, int &y ) + { + x = int( floor( ((double) wxpx - originx) / scale + 0.5 ) ); + y = int( floor( ((double) wxpy - originy) / scale + 0.5 ) ); + } + + // given wxPoint on the GUI returns the nearest drawing command coords + void FromWxPoint ( wxPoint wxp, int &x, int &y ) + { + FromWxPoint( wxp.x, wxp.y, x, y ); + } +}; + +// The point class +// note: this actually refers to the x,y-coordinate in drawing commands, +// not the coordinate in the GUI +class Point +{ +public: + POINTTYPE type; + PointSystem *pointsys; + + // drawing commands that depend on this point + DrawCmd* cmd_main; + DrawCmd* cmd_next; + bool isselected; + unsigned num; + + // constructor + //Point ( ) { cmd_main = NULL; cmd_next = NULL; } + + // constructor + Point ( int _x, int _y, PointSystem* ps, POINTTYPE t, DrawCmd* cmd, unsigned n = 0 ); + + // getters + int x() { return x_; } + + int y() { return y_; } + + //set x and y + void setXY( int _x, int _y); + + // simply returns true if px and py are the coordinate values + bool IsAt( int px, int py ); + + // convert this point to wxPoint using scale and originx, originy + wxPoint ToWxPoint () { return ToWxPoint(true); } + + // convert this point to wxPoint using scale; + // also use originx and originy if useorigin = true + wxPoint ToWxPoint (bool useorigin); + + // check if wxpoint is nearby this point + bool CheckWxPoint ( wxPoint wxpoint ); + +private: + int x_, y_; + +}; + +// The base class for all draw commands +class DrawCmd +{ +public: + CMDTYPE type; + + // main point (almost every command has one) + // for B and S it's the last (destination) point + Point* m_point; + + // other points than the main point + // subclasses must populate this list even if they define + // new variables for other points + PointList controlpoints; + + // Linked list feature + DrawCmd *prev; + + // Must set to true if the next command should NOT utilize this command + // for the drawing + bool dobreak; + + // Set to true if invisible m_point (not drawn) + bool invisible; + + // true if this DrawCmd has been initialized with Init(), false otherwise + // (initialized means that the control points have been generated) + bool initialized; + + // ----------------------------------------------------------- + // Constructor(s) + DrawCmd ( int x, int y, PointSystem *ps, DrawCmd *pv ); + + // Destructor + virtual ~DrawCmd (); + + // Init the draw command (for example to generate the control points) + virtual void Init(unsigned n = 0) { initialized = true; } + + virtual wxString ToString() { return wxT(""); } + +}; + +class ASSDrawEngine: public GUI::AGGWindow +{ +public: + ASSDrawEngine( wxWindow *parent, int extraflags = 0 ); + + // destructor + ~ASSDrawEngine(); + + virtual void SetDrawCmdSet(wxString set) { drawcmdset = set; } + + virtual void ResetEngine(); + virtual void ResetEngine(bool addM); + virtual void RefreshDisplay(); + + void FitToViewPoint(int hmargin, int vmargin); + void SetFitToViewPointOnNextPaint(int hmargin = -1, int vmargin = -1); + + PointSystem* _PointSystem() { return pointsys; } + + // ASS draw commands; returns the number of parsed commands + virtual int ParseASS ( wxString str ); + // generate ASS draw commands + virtual wxString GenerateASS ( ); + + // drawing + virtual void OnPaint(wxPaintEvent &event); + + // -------------------- adding new commands ---------------------------- + + // create draw command of type 'type' and m_point (x, y), append to the + // list and return it + virtual DrawCmd* AppendCmd ( CMDTYPE type, int x, int y ); + + // append draw command + virtual DrawCmd* AppendCmd ( DrawCmd* cmd ); + + // create draw command of type 'type' and m_point (x, y), insert to the + // list after the _cmd and return it + virtual DrawCmd* InsertCmd ( CMDTYPE type, int x, int y, DrawCmd* _cmd ); + + // insert draw command cmd after _cmd + virtual void InsertCmd ( DrawCmd* cmd, DrawCmd* _cmd ); + + // Create new DrawCmd + DrawCmd* NewCmd ( CMDTYPE type, int x, int y ); + + // -------------------- read/modify commands --------------------------- + + // returns the iterator for the list + virtual DrawCmdList::iterator Iterator ( ); + + // returns the 'end' iterator for the list + virtual DrawCmdList::iterator IteratorEnd ( ); + + // returns the last command in the list + virtual DrawCmd* LastCmd (); + + // returns the total number of commands in the list + //virtual int CmdCount () { return cmds.size(); } + + // move all points by relative amount of x, y coordinates + virtual void MovePoints ( int x, int y ); + + // transform all points using the calculation: + // | (m11) (m12) | x | (x - mx) | + | nx | + // | (m21) (m22) | | (y - my) | | ny | + virtual void Transform( float m11, float m12, float m21, float m22, + float mx, float my, float nx, float ny ); + + // returns some DrawCmd if its m_point = (x, y) + virtual DrawCmd* PointAt ( int x, int y ); + + // returns some DrawCmd if one of its control point = (x, y) + // also set &point to refer to that control point + virtual DrawCmd* ControlAt ( int x, int y, Point* &point ); + + // attempts to delete a commmand, returns true|false if successful|fail + virtual bool DeleteCommand ( DrawCmd* cmd ); + + agg::rgba rgba_shape; + PixelFormat::AGGType::color_type color_bg; + +protected: + /// The AGG base renderer + typedef agg::renderer_base RendererBase; + /// The AGG primitives renderer + typedef agg::renderer_primitives RendererPrimitives; + /// The AGG solid renderer + typedef agg::renderer_scanline_aa_solid RendererSolid; + + enum DRAWCMDMODE { + NORMAL, + CTRL_LN, + HILITE + }; + + DrawCmdList cmds; + wxString drawcmdset; + + PointSystem* pointsys; + + // for FitToViewPoint feature + bool setfitviewpoint; + int fitviewpoint_vmargin, fitviewpoint_hmargin; + + // AGG + agg::rasterizer_scanline_aa<> rasterizer; ///< Scanline rasterizer + agg::scanline_p8 scanline; ///< Scanline container + void render_scanlines_aa_solid(RendererBase& rbase, agg::rgba rbga, bool affectboundaries = true); + void render_scanlines(RendererSolid& rsolid, bool affectboundaries = true); + int rendered_min_x, rendered_min_y, rendered_max_x, rendered_max_y; //bounding coord of rendered shapes + void update_rendered_bound_coords(bool rendered_fresh = false); + + typedef agg::conv_transform trans_path; + agg::path_storage m_path, b_path; + trans_path *rm_path, *rb_path; + agg::conv_curve *rm_curve; + + void draw(); + virtual void ConstructPathsAndCurves(agg::trans_affine& mtx, trans_path*& _rm_path, trans_path*& _rb_path, agg::conv_curve*& _rm_curve); + virtual void DoDraw( RendererBase& rbase, RendererPrimitives& rprim, RendererSolid& rsolid, agg::trans_affine& mtx ); + virtual void Draw_Clear( RendererBase& rbase ); + virtual void Draw_Draw( RendererBase& rbase, RendererPrimitives& rprim, RendererSolid& rsolid, agg::trans_affine& mtx, agg::rgba color ); + bool refresh_called; + + // set stuff to connect two drawing commands cmd1 and cmd2 such that + // cmd1 comes right before cmd2 + virtual void ConnectSubsequentCmds (DrawCmd* cmd1, DrawCmd* cmd2); + + virtual void AddDrawCmdToAGGPathStorage(DrawCmd* cmd, agg::path_storage& path, DRAWCMDMODE mode = NORMAL); + + virtual std::vector< bool > PrepareC1ContData(); + + DECLARE_EVENT_TABLE() +}; + +namespace agg +{ + class simple_polygon_vertex_source + { + public: + simple_polygon_vertex_source(const double* polygon, unsigned np, + bool roundoff = false, + bool close = true) : + m_polygon(polygon), + m_num_points(np), + m_vertex(0), + m_roundoff(roundoff), + m_close(close) + { + } + + void close(bool f) { m_close = f; } + bool close() const { return m_close; } + + void rewind(unsigned) + { + m_vertex = 0; + } + + unsigned vertex(double* x, double* y) + { + if(m_vertex > m_num_points) return path_cmd_stop; + if(m_vertex == m_num_points) + { + ++m_vertex; + return path_cmd_end_poly | (m_close ? path_flags_close : 0); + } + *x = m_polygon[m_vertex * 2]; + *y = m_polygon[m_vertex * 2 + 1]; + if(m_roundoff) + { + *x = floor(*x) + 0.5; + *y = floor(*y) + 0.5; + } + ++m_vertex; + return (m_vertex == 1) ? path_cmd_move_to : path_cmd_line_to; + } + + private: + const double* m_polygon; + unsigned m_num_points; + unsigned m_vertex; + bool m_roundoff; + bool m_close; + }; +}; + +typedef agg::simple_polygon_vertex_source aggpolygon; diff --git a/assdraw/src/enums.hpp b/assdraw/src/enums.hpp new file mode 100644 index 000000000..e59217056 --- /dev/null +++ b/assdraw/src/enums.hpp @@ -0,0 +1,113 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +// enum for IDs of menus +enum { + MENU_DUMMY = 200, + MENU_CLEAR, + MENU_PREVIEW, + MENU_TRANSFORM, + MENU_LIBRARY, + MENU_HELP, + MENU_RESETPERSPECTIVE, + MENU_SETTINGS, + MENU_UNDO, + MENU_REDO, + MENU_PASTE, + MENU_BGIMG_LOAD, + MENU_BGIMG_ALPHA, + MENU_BGIMG_REMOVE, + MENU_RECENTER, + MENU_TBAR, + MENU_REPOS_TOPLEFT, + MENU_REPOS_TOPRIGHT, + MENU_REPOS_CENTER, + MENU_REPOS_BOTLEFT, + MENU_REPOS_BOTRIGHT, + MENU_BGIMG_RECENTER, + MENU_REPOS_BGTOPLEFT, + MENU_REPOS_BGTOPRIGHT, + MENU_REPOS_BGCENTER, + MENU_REPOS_BGBOTLEFT, + MENU_REPOS_BGBOTRIGHT, + MENU_DRC_LNTOBEZ, + MENU_DRC_C1CONTBEZ, + MENU_DRC_BEZTOLN, + MENU_DRC_MOVE00, + MENU_TB_ALL, + MENU_TB_NONE, + MENU_TB_DOCK, + MENU_TB_UNDOCK, + MENU_TB_DRAW, + MENU_TB_MODE, + MENU_TB_BGIMG +}; + +// enum for modes (i.e. create m, b, l etc or normal mode) +// also use as tools IDs +enum MODE +{ + MODE_ARR = 100, + MODE_M = 101, + MODE_N = 102, + MODE_L = 103, + MODE_B = 104, + MODE_S = 105, + MODE_P = 106, + MODE_C = 107, + MODE_DEL = 108, + MODE_SCALEROTATE = 109, + MODE_NUT_BILINEAR = 110 +}; + +// enum for IDs of other tools on the toolbar +enum { + TB_CLEAR = 111, + TB_EDITSRC = 112, + TB_PREVIEW = 113, + TB_TRANSFORM = 114, + TB_HELP = 115, + TB_ZOOMSLIDER = 116, + TB_BGALPHA_SLIDER = 117 +}; + +enum DRAGMODETOOL +{ + DRAG_DWG = 120, + DRAG_BGIMG = 121, + DRAG_BOTH = 122 +}; + +struct DRAGMODE +{ + bool drawing; + bool bgimg; + DRAGMODE() { drawing = true, bgimg = false; } + DRAGMODE(bool d, bool b) { drawing = d, bgimg = b; } +}; diff --git a/assdraw/src/include_once.hpp b/assdraw/src/include_once.hpp new file mode 100644 index 000000000..aa7624ac7 --- /dev/null +++ b/assdraw/src/include_once.hpp @@ -0,0 +1,94 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: include_once.hpp +// Purpose: A header file that is included exactly once for the whole code +// (supposed to be included by assdraw.cpp only) +// Put must-define-exactly-once codes here +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +// tooltips +const wxString TIPS_CLEAR = wxT("Clear the canvas and create a new drawing"); +const wxString TIPS_EDITSRC = wxT("Edit the source"); +const wxString TIPS_PREVIEW = wxT("Draw the shapes without enlarged points and control points"); +const wxString TIPS_TRANSFORM = wxT("Transform the drawing using matrix transformation"); +const wxString TIPS_LIBRARY = wxT("Shapes library"); +const wxString TIPS_HELP = wxT("Help! Help!"); +const wxString TIPS_PASTE = wxT("Depending on what's in the clipboard, import as drawing commands or background"); +const wxString TIPS_UNDO = wxT("Undo last action"); +const wxString TIPS_REDO = wxT("Redo last undo"); +const wxString TIPS_ARR = wxT("Drag mode"); +const wxString TIPS_M = wxT("Draw M mode (Close current shape and move the virtual pen to a new point)"); +const wxString TIPS_N = wxT("Draw N mode (Same as M but doesn't close the shape)"); +const wxString TIPS_L = wxT("Draw L mode (Straight line)"); +const wxString TIPS_B = wxT("Draw B mode (Cubic Bezier curve)"); +const wxString TIPS_S = wxT("Draw S mode (Spline)"); +const wxString TIPS_P = wxT("Draw P mode (Extends a spline with another point)"); +const wxString TIPS_C = wxT("Draw C mode (Close the last spline)"); +const wxString TIPS_DEL = wxT("Delete mode"); +const wxString TIPS_NUTB = wxT("Bilinear transformation mode: Drag the vertices to distort the shape; Dragging an edge moves two adjacent vertices together"); +const wxString TIPS_SCALEROTATE = wxT("Scale/Rotate mode: Drag a vertex or an edge to rescale the shape; Right-drag to rotate"); +const wxString TIPS_DWG = wxT("Right-dragging pans drawing, mousewheel zooms in/out drawing"); +const wxString TIPS_BGIMG = wxT("Right-dragging pans background, mousewheel zooms in/out background"); +const wxString TIPS_BOTH = wxT("Right-dragging pans drawing & background, mousewheel zooms in/out drawing & background"); + +const wxString TBNAME_DRAW = wxT("Canvas"); +const wxString TBNAME_MODE = wxT("Drawing mode"); +const wxString TBNAME_BGIMG = wxT("Background"); +const wxString TBNAME_ZOOM = wxT("Zoom"); + +wxString ASSDrawTransformDlg::combo_templatesStrings[] = { + _(" + { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 5.0f }, //5 units down + { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 5.0f, 0.0f }, //5 units left + { 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 2.0f, 1.0f, 2.0f }, //90 CW (1,2) + { 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 2.0f, -1.0f, 2.0f }, //90 CCW, (-1,2) + { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f }, //180 (0,0) + { -1.0f, 0.0f, 0.0f, 1.0f, 4.0f, 0.0f, 4.0f, 0.0f }, //Flip X = 4 + { 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 3.0f, 0.0f, 3.0f }, //Flip Y = 3 + { 2.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f }, //Scale X * 2 + { 1.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f } //Scale Y * 3 +}; diff --git a/assdraw/src/library.cpp b/assdraw/src/library.cpp new file mode 100644 index 000000000..1f6932eb0 --- /dev/null +++ b/assdraw/src/library.cpp @@ -0,0 +1,259 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "assdraw.hpp" +#include "library.hpp" + +#include + +BEGIN_EVENT_TABLE(ASSDrawShapePreview, ASSDrawEngine) + EVT_SIZE(ASSDrawShapePreview::OnSize) +END_EVENT_TABLE() + +ASSDrawShapePreview::ASSDrawShapePreview( wxWindow *parent, ASSDrawShapeLibrary *_shapelib, wxString initialcmds ) + : ASSDrawEngine(parent, wxSIMPLE_BORDER) +{ + shapelib = _shapelib; + if (ParseASS(initialcmds) > 0) + SetFitToViewPointOnNextPaint(5, 5); + cb = new wxCheckBox(this, wxID_ANY, ""); +} + +void ASSDrawShapePreview::OnSize(wxSizeEvent& event) +{ + return; + wxSize siz = event.GetSize(); + + if (shapelib->layout == HORIZONTAL) + SetSize(siz.x, siz.x); + else + SetSize(siz.y, siz.y); + SetFitToViewPointOnNextPaint(10, 10); +} + +BEGIN_EVENT_TABLE(ASSDrawShapeLibrary, wxScrolledWindow) + EVT_SIZE(ASSDrawShapeLibrary::OnSize) + EVT_MENU_RANGE(MENU_RANGE_START, MENU_RANGE_END, ASSDrawShapeLibrary::OnPopupMenuClicked) + EVT_TOOL(TOOL_SAVE, ASSDrawShapeLibrary::SaveShapeFromCanvas) + EVT_TOOL_RANGE(TOOL_CHECK, TOOL_UNCHECK, ASSDrawShapeLibrary::CheckUncheckAllPreviews) + EVT_TOOL(TOOL_DELETE, ASSDrawShapeLibrary::DeleteChecked) +END_EVENT_TABLE() + +ASSDrawShapeLibrary::ASSDrawShapeLibrary( wxWindow *parent, ASSDrawFrame *frame ) + : wxScrolledWindow(parent, wxID_ANY) +{ + m_frame = frame; + //sizer = NULL; + layout = VERTICAL; + + wxToolBar *tbar = new wxToolBar(this, wxID_ANY, __DPDS__ , wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT | wxTB_NODIVIDER); + tbar->SetMargins(0, 3); + tbar->AddTool(TOOL_SAVE, wxBITMAP(add), "Save canvas"); + tbar->AddSeparator(); + tbar->AddTool(TOOL_CHECK, wxBITMAP(check), "Select all"); + tbar->AddTool(TOOL_UNCHECK, wxBITMAP(uncheck), "Select none"); + tbar->AddTool(TOOL_DELETE, wxBITMAP(delcross), "Delete selected"); + + libarea = new wxScrolledWindow(this, wxID_ANY, __DPDS__ , wxScrolledWindowStyle | wxSIMPLE_BORDER); + libarea->SetBackgroundColour(wxColour(0xFF, 0xFF, 0x99)); + sizer = new wxFlexGridSizer(0, 1, 0, 0); + ((wxFlexGridSizer*) sizer)->AddGrowableCol(0); + libarea->SetSizer(sizer); + libarea->SetScrollbars(20, 20, 50, 50); + libsizer = new wxFlexGridSizer(2, 1, 0, 0); + libsizer->AddGrowableCol(0); + libsizer->AddGrowableRow(1); + libsizer->Add(tbar, 0, wxBOTTOM, 2); + libsizer->Add(libarea,1,wxEXPAND); + tbar->Realize(); + libsizer->Layout(); + + SetSizer(libsizer); +} + +void ASSDrawShapeLibrary::OnSize(wxSizeEvent& event) +{ + if (sizer == NULL || sizer->GetChildren().size() == 0) return; + + wxSize siz = GetClientSize(); + libsizer->SetDimension(0, 0, siz.x, siz.y); + + UpdatePreviewDisplays(); +} + +ASSDrawShapePreview* ASSDrawShapeLibrary::AddShapePreview(wxString cmds, bool addtotop) +{ + ASSDrawShapePreview *prev = new ASSDrawShapePreview(libarea, this, cmds); + prev->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(ASSDrawShapeLibrary::OnMouseLeftDClick), NULL, this); + prev->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(ASSDrawShapeLibrary::OnMouseRightClick), NULL, this); + ASSDrawFrame::wxColourToAggRGBA(m_frame->colors.library_shape, prev->rgba_shape); + if (addtotop) + sizer->Insert(0, prev, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5); + else + sizer->Add(prev, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5); + UpdatePreviewDisplays(); + return prev; +} + +void ASSDrawShapeLibrary::UpdatePreviewDisplays() +{ + wxSize siz = GetClientSize(); + int dim = siz.x - 15; + libarea->Show(false); + wxwxSizerItemListNode *node = sizer->GetChildren().GetFirst(); + while (node != NULL) + { + ASSDrawShapePreview *shprvw = (ASSDrawShapePreview *) node->GetData()->GetWindow(); + shprvw->SetSize(dim, dim); + sizer->SetItemMinSize(shprvw, dim - 20, dim - 20); + shprvw->SetFitToViewPointOnNextPaint(10, 10); + shprvw->Refresh(); + node = node->GetNext(); + } + sizer->Layout(); + sizer->FitInside(libarea); + libarea->Show(true); +} + +void ASSDrawShapeLibrary::OnMouseLeftDClick(wxMouseEvent &event) +{ + ASSDrawShapePreview *preview = (ASSDrawShapePreview *) event.GetEventObject(); + LoadToCanvas(preview); +} + +void ASSDrawShapeLibrary::OnMouseRightClick(wxMouseEvent &event) +{ + ASSDrawShapePreview *prev = (ASSDrawShapePreview *) event.GetEventObject(); + if (prev && prev->shapelib == this) + { + activepreview = prev; + wxMenu *menu = new wxMenu; + wxMenuItem *menuload = new wxMenuItem(menu, MENU_LOAD, wxT("Load to canvas")); + wxFont f = menuload->GetFont(); + f.SetWeight(wxFONTWEIGHT_BOLD); + menuload->SetFont(f); + menu->Append(menuload); + //menu->Append(MENU_LOAD, wxT("Load to canvas"))->GetFont().SetWeight(wxFONTWEIGHT_BOLD); + menu->Append(MENU_COPYCLIPBOARD, wxT("Copy commands to clipboard")); + menu->Append(MENU_SAVECANVAS, wxT("Save canvas here")); + wxMenu *submenu = new wxMenu; + submenu->Append(MENU_DELETE, wxT("Confirm delete?")); + menu->Append(MENU_DUMMY, wxT("Delete from library"), submenu); + PopupMenu(menu); + delete menu; + } +} + +void ASSDrawShapeLibrary::OnPopupMenuClicked(wxCommandEvent &event) +{ + int id = event.GetId(); + switch(id) + { + case MENU_LOAD: + LoadToCanvas(activepreview); + break; + case MENU_COPYCLIPBOARD: + if (wxTheClipboard->Open()) + { + if (wxTheClipboard->IsSupported( wxDF_TEXT )) + { + wxTheClipboard->SetData( new wxTextDataObject( activepreview->GenerateASS() ) ); + } + wxTheClipboard->Close(); + } + break; + case MENU_SAVECANVAS: + activepreview->ParseASS(m_frame->m_canvas->GenerateASS()); + activepreview->SetFitToViewPointOnNextPaint(); + activepreview->RefreshDisplay(); + break; + case MENU_DELETE: + sizer->Detach(activepreview); + activepreview->Show(false); + activepreview->Destroy(); + UpdatePreviewDisplays(); + Refresh(); + break; + } +} + +void ASSDrawShapeLibrary::SaveShapeFromCanvas(wxCommandEvent& WXUNUSED(event)) +{ + AddShapePreview(m_frame->m_canvas->GenerateASS(), true); +} + +void ASSDrawShapeLibrary::CheckUncheckAllPreviews(wxCommandEvent &event) +{ + bool checked = event.GetId() == TOOL_CHECK; + wxwxSizerItemListNode *node = sizer->GetChildren().GetFirst(); + while (node != NULL) + { + ASSDrawShapePreview *shprvw = (ASSDrawShapePreview *) node->GetData()->GetWindow(); + shprvw->cb->SetValue(checked); + node = node->GetNext(); + } +} + +void ASSDrawShapeLibrary::DeleteChecked(wxCommandEvent& WXUNUSED(event)) +{ + wxwxSizerItemListNode *node = sizer->GetChildren().GetFirst(); + while (node != NULL) + { + ASSDrawShapePreview *shprvw = (ASSDrawShapePreview *) node->GetData()->GetWindow(); + if (shprvw->cb->GetValue()) + { + sizer->Detach(shprvw); + shprvw->Show(false); + shprvw->Destroy(); + } + node = node->GetNext(); + } + UpdatePreviewDisplays(); + Refresh(); +} + +std::vector< ASSDrawShapePreview *> ASSDrawShapeLibrary::GetShapePreviews() +{ + std::vector< ASSDrawShapePreview *> out; + wxwxSizerItemListNode *node = sizer->GetChildren().GetFirst(); + while (node != NULL) + { + ASSDrawShapePreview *shprvw = (ASSDrawShapePreview *) node->GetData()->GetWindow(); + out.push_back(shprvw); + node = node->GetNext(); + } + return out; +} + +void ASSDrawShapeLibrary::LoadToCanvas(ASSDrawShapePreview *preview) +{ + m_frame->m_canvas->AddUndo("Load shape from library"); + m_frame->m_canvas->ParseASS(preview->GenerateASS()); + m_frame->m_canvas->RefreshDisplay(); + m_frame->m_canvas->RefreshUndocmds(); + m_frame->UpdateFrameUI(); +} diff --git a/assdraw/src/library.hpp b/assdraw/src/library.hpp new file mode 100644 index 000000000..20179d12f --- /dev/null +++ b/assdraw/src/library.hpp @@ -0,0 +1,88 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "engine.hpp" +#include + +class ASSDrawFrame; +class ASSDrawShapeLibrary; + +enum LIBLAYOUT { VERTICAL, HORIZONTAL }; + +enum { + MENU_RANGE_START = 450, + MENU_LOAD, + MENU_COPYCLIPBOARD, + MENU_SAVECANVAS, + MENU_DELETE, + MENU_RANGE_END, + TOOL_SAVE, + TOOL_CHECK, + TOOL_UNCHECK, + TOOL_DELETE +}; + +class ASSDrawShapePreview : public ASSDrawEngine +{ +protected: + ASSDrawShapePreview( wxWindow *parent, ASSDrawShapeLibrary *shapelib, wxString initialcmds = _T("")); + virtual void OnSize(wxSizeEvent& event); + + ASSDrawShapeLibrary *shapelib; + wxCheckBox *cb; + DECLARE_EVENT_TABLE() + friend class ASSDrawShapeLibrary; +}; + +class ASSDrawShapeLibrary : public wxScrolledWindow +{ +public: + ASSDrawShapeLibrary( wxWindow *parent, ASSDrawFrame *frame ); + virtual ASSDrawShapePreview* AddShapePreview(wxString cmds, bool addtotop = false); + virtual void OnSize(wxSizeEvent& event); + virtual void OnMouseLeftDClick(wxMouseEvent &event); + virtual void OnMouseRightClick(wxMouseEvent &event); + virtual void OnPopupMenuClicked(wxCommandEvent &event); + virtual void SaveShapeFromCanvas(wxCommandEvent& WXUNUSED(event)); + virtual void CheckUncheckAllPreviews(wxCommandEvent &event); + virtual void DeleteChecked(wxCommandEvent& WXUNUSED(event)); + virtual void UpdatePreviewDisplays(); + virtual std::vector< ASSDrawShapePreview *> GetShapePreviews(); + virtual void LoadToCanvas(ASSDrawShapePreview *preview); + + wxScrolledWindow* libarea; + wxFlexGridSizer *libsizer; + wxSizer* sizer; + LIBLAYOUT layout; +protected: + ASSDrawFrame *m_frame; + ASSDrawShapePreview *activepreview; + + DECLARE_EVENT_TABLE() +}; diff --git a/assdraw/src/resource.h b/assdraw/src/resource.h new file mode 100644 index 000000000..dab23b6f5 --- /dev/null +++ b/assdraw/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by assdraw.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/assdraw/src/settings.cpp b/assdraw/src/settings.cpp new file mode 100644 index 000000000..41bd680c1 --- /dev/null +++ b/assdraw/src/settings.cpp @@ -0,0 +1,223 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/////////////////////////////////////////////////////////////////////////////// +// Name: settings.cpp +// Purpose: settings property grid +// Author: ai-chan +// Created: 08/26/06 +// Copyright: (c) ai-chan +// Licence: 3-clause BSD +/////////////////////////////////////////////////////////////////////////////// + +#include "assdraw.hpp" +#include "settings.hpp" + +DEFINE_EVENT_TYPE(wxEVT_SETTINGS_CHANGED) + +// ---------------------------------------------------------------------------- +// ASSDrawSettingsDialog +// ---------------------------------------------------------------------------- + +ASSDrawSettingsDialog::ASSDrawSettingsDialog(wxWindow *parent, ASSDrawFrame *frame, int id) + : wxPanel(parent, id) +{ + m_frame = frame; + propgrid = NULL; +} + +void ASSDrawSettingsDialog::Init() +{ + propgrid = new wxPropertyGrid(this, + wxID_ANY, + wxDefaultPosition, + wxDefaultSize, + //wxPG_BOLD_MODIFIED | + //wxPG_SPLITTER_AUTO_CENTER | + //wxPG_AUTO_SORT | + //wxPG_HIDE_MARGIN | wxPG_STATIC_SPLITTER | + wxPG_TOOLTIPS | + //wxPG_NOCATEGORIES | + wxTAB_TRAVERSAL //| + //wxSUNKEN_BORDER + ); + + #define APPENDCOLOURPROP(pgid, label, color) pgid = propgrid->Append( wxColourProperty(wxT(label), wxPG_LABEL, color) ); + #define APPENDUINTPROP(pgid, label, uint) \ + pgid = propgrid->Append( wxUIntProperty(wxT(label), wxPG_LABEL, uint) ); \ + propgrid->SetPropertyValidator( pgid, validator ); + #define APPENDBOOLPROP(pgid, label, boolvar) \ + pgid = propgrid->Append( wxBoolProperty ( wxT(label), wxPG_LABEL, boolvar ) ); \ + propgrid->SetPropertyAttribute( pgid, wxPG_BOOL_USE_CHECKBOX, (long)1 ); + wxLongPropertyValidator validator(0x0,0xFF); + + propgrid->Append( wxPropertyCategory(wxT("Appearance"),wxPG_LABEL) ); + APPENDCOLOURPROP(colors_canvas_bg_pgid, "Canvas", m_frame->colors.canvas_bg) + APPENDCOLOURPROP(colors_canvas_shape_normal_pgid, "Drawing", m_frame->colors.canvas_shape_normal) + APPENDUINTPROP(alphas_canvas_shape_normal_pgid, "Drawing @", m_frame->alphas.canvas_shape_normal) + APPENDCOLOURPROP(colors_canvas_shape_preview_pgid, "Preview", m_frame->colors.canvas_shape_preview) + APPENDUINTPROP(alphas_canvas_shape_preview_pgid, "Preview @", m_frame->alphas.canvas_shape_preview) + APPENDCOLOURPROP(colors_canvas_shape_outline_pgid, "Outline", m_frame->colors.canvas_shape_outline) + APPENDUINTPROP(alphas_canvas_shape_outline_pgid, "Outline @", m_frame->alphas.canvas_shape_outline) + APPENDCOLOURPROP(colors_canvas_shape_guideline_pgid, "Control lines", m_frame->colors.canvas_shape_guideline) + APPENDUINTPROP(alphas_canvas_shape_guideline_pgid, "Control lines @", m_frame->alphas.canvas_shape_guideline) + APPENDCOLOURPROP(colors_canvas_shape_mainpoint_pgid, "Points", m_frame->colors.canvas_shape_mainpoint) + APPENDUINTPROP(alphas_canvas_shape_mainpoint_pgid, "Points @", m_frame->alphas.canvas_shape_mainpoint) + APPENDCOLOURPROP(colors_canvas_shape_controlpoint_pgid, "Control points", m_frame->colors.canvas_shape_controlpoint) + APPENDUINTPROP(alphas_canvas_shape_controlpoint_pgid, "Control points @", m_frame->alphas.canvas_shape_controlpoint) + APPENDCOLOURPROP(colors_canvas_shape_selectpoint_pgid, "Selected points", m_frame->colors.canvas_shape_selectpoint) + APPENDUINTPROP(alphas_canvas_shape_selectpoint_pgid, "Selected points @", m_frame->alphas.canvas_shape_selectpoint) + APPENDCOLOURPROP(colors_library_libarea_pgid, "Library", m_frame->colors.library_libarea) + APPENDCOLOURPROP(colors_library_shape_pgid, "Library shapes", m_frame->colors.library_shape) + APPENDCOLOURPROP(colors_origin_pgid, "Origin", m_frame->colors.origin) + APPENDUINTPROP(sizes_origincross_pgid, "Origin cross size", m_frame->sizes.origincross) + APPENDCOLOURPROP(colors_ruler_h_pgid, "H ruler", m_frame->colors.ruler_h) + APPENDCOLOURPROP(colors_ruler_v_pgid, "V ruler", m_frame->colors.ruler_v) + + propgrid->Append( wxPropertyCategory(wxT("Behaviors"),wxPG_LABEL) ); + APPENDBOOLPROP(behaviors_capitalizecmds_pgid, "Capitalize commands", m_frame->behaviors.capitalizecmds); + APPENDBOOLPROP(behaviors_autoaskimgopac_pgid, "Ask for image opacity", m_frame->behaviors.autoaskimgopac); + APPENDBOOLPROP(behaviors_parse_spc_pgid, "Parse S/P/C", m_frame->behaviors.parse_spc); + APPENDBOOLPROP(behaviors_nosplashscreen_pgid, "No splash screen", m_frame->behaviors.nosplashscreen); + APPENDBOOLPROP(behaviors_confirmquit_pgid, "Confirm quit", m_frame->behaviors.confirmquit); + + wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 1, 0, 0); + sizer->AddGrowableCol(0); + sizer->AddGrowableRow(0); + sizer->Add(propgrid, 1, wxEXPAND); + + wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL); + wxButton *abutton = new wxButton(this, wxID_ANY, "Apply"); + abutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ASSDrawSettingsDialog::OnSettingsApplyButtonClicked), NULL, this); + bsizer->Add(abutton, 2, wxEXPAND); + wxButton *rbutton = new wxButton(this, wxID_ANY, "Revert"); + rbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ASSDrawSettingsDialog::OnSettingsRevertButtonClicked), NULL, this); + bsizer->Add(rbutton, 1, wxEXPAND); + bsizer->Layout(); + + sizer->Add(bsizer, 1, wxEXPAND); + sizer->Layout(); + SetSizer(sizer); +} + +ASSDrawSettingsDialog::~ASSDrawSettingsDialog() +{ + if (propgrid) propgrid->Clear(); +} + +void ASSDrawSettingsDialog::OnSettingsApplyButtonClicked(wxCommandEvent &event) +{ + + wxButton *button = (wxButton *) event.GetEventObject(); + //wxPropertyGrid *propgrid = (wxPropertyGrid *) button->GetClientData(); + if (propgrid == NULL) return; + + #define PARSECOLOR(color, pgid) \ + { \ + wxVariant variant = propgrid->GetPropertyValue(pgid); \ + color = *wxGetVariantCast(variant,wxColour); \ + } + + #define PARSE(ptr, pgid) propgrid->GetPropertyValue(pgid).Convert(ptr); + + PARSECOLOR(m_frame->colors.canvas_bg, colors_canvas_bg_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_controlpoint, colors_canvas_shape_controlpoint_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_guideline, colors_canvas_shape_guideline_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_mainpoint, colors_canvas_shape_mainpoint_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_normal, colors_canvas_shape_normal_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_outline, colors_canvas_shape_outline_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_preview, colors_canvas_shape_preview_pgid) + PARSECOLOR(m_frame->colors.canvas_shape_selectpoint, colors_canvas_shape_selectpoint_pgid) + PARSECOLOR(m_frame->colors.library_libarea, colors_library_libarea_pgid) + PARSECOLOR(m_frame->colors.library_shape, colors_library_shape_pgid) + PARSECOLOR(m_frame->colors.origin, colors_origin_pgid) + PARSECOLOR(m_frame->colors.ruler_h, colors_ruler_h_pgid) + PARSECOLOR(m_frame->colors.ruler_v, colors_ruler_v_pgid) + + PARSE(&m_frame->alphas.canvas_shape_controlpoint, alphas_canvas_shape_controlpoint_pgid) + PARSE(&m_frame->alphas.canvas_shape_guideline, alphas_canvas_shape_guideline_pgid) + PARSE(&m_frame->alphas.canvas_shape_mainpoint, alphas_canvas_shape_mainpoint_pgid) + PARSE(&m_frame->alphas.canvas_shape_normal, alphas_canvas_shape_normal_pgid) + PARSE(&m_frame->alphas.canvas_shape_outline, alphas_canvas_shape_outline_pgid) + PARSE(&m_frame->alphas.canvas_shape_preview, alphas_canvas_shape_preview_pgid) + PARSE(&m_frame->alphas.canvas_shape_selectpoint, alphas_canvas_shape_selectpoint_pgid) + + PARSE(&m_frame->sizes.origincross, sizes_origincross_pgid) + + PARSE(&m_frame->behaviors.autoaskimgopac, behaviors_autoaskimgopac_pgid) + PARSE(&m_frame->behaviors.capitalizecmds, behaviors_capitalizecmds_pgid) + PARSE(&m_frame->behaviors.parse_spc, behaviors_parse_spc_pgid) + PARSE(&m_frame->behaviors.nosplashscreen, behaviors_nosplashscreen_pgid) + PARSE(&m_frame->behaviors.confirmquit, behaviors_confirmquit_pgid) + + wxCommandEvent evento( wxEVT_SETTINGS_CHANGED, event.GetId() ); + evento.SetEventObject( button ); + m_frame->GetEventHandler()->ProcessEvent( evento ); + +} + +void ASSDrawSettingsDialog::OnSettingsRevertButtonClicked(wxCommandEvent &event) +{ + RefreshSettingsDisplay(); +} + +void ASSDrawSettingsDialog::RefreshSettingsDisplay() +{ + if (propgrid == NULL) return; + + #define UPDATESETTING(value, pgid) propgrid->SetPropertyValue(pgid, value); + + UPDATESETTING(m_frame->colors.canvas_bg, colors_canvas_bg_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_controlpoint, colors_canvas_shape_controlpoint_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_guideline, colors_canvas_shape_guideline_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_mainpoint, colors_canvas_shape_mainpoint_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_normal, colors_canvas_shape_normal_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_outline, colors_canvas_shape_outline_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_preview, colors_canvas_shape_preview_pgid) + UPDATESETTING(m_frame->colors.canvas_shape_selectpoint, colors_canvas_shape_selectpoint_pgid) + UPDATESETTING(m_frame->colors.library_libarea, colors_library_libarea_pgid) + UPDATESETTING(m_frame->colors.library_shape, colors_library_shape_pgid) + UPDATESETTING(m_frame->colors.origin, colors_origin_pgid) + UPDATESETTING(m_frame->colors.ruler_h, colors_ruler_h_pgid) + UPDATESETTING(m_frame->colors.ruler_v, colors_ruler_v_pgid) + + UPDATESETTING(m_frame->alphas.canvas_shape_controlpoint, alphas_canvas_shape_controlpoint_pgid) + UPDATESETTING(m_frame->alphas.canvas_shape_guideline, alphas_canvas_shape_guideline_pgid) + UPDATESETTING(m_frame->alphas.canvas_shape_mainpoint, alphas_canvas_shape_mainpoint_pgid) + UPDATESETTING(m_frame->alphas.canvas_shape_normal, alphas_canvas_shape_normal_pgid) + UPDATESETTING(m_frame->alphas.canvas_shape_outline, alphas_canvas_shape_outline_pgid) + UPDATESETTING(m_frame->alphas.canvas_shape_preview, alphas_canvas_shape_preview_pgid) + UPDATESETTING(m_frame->alphas.canvas_shape_selectpoint, alphas_canvas_shape_selectpoint_pgid) + + UPDATESETTING(m_frame->sizes.origincross, sizes_origincross_pgid) + + UPDATESETTING(m_frame->behaviors.capitalizecmds, behaviors_capitalizecmds_pgid) + UPDATESETTING(m_frame->behaviors.autoaskimgopac, behaviors_autoaskimgopac_pgid) + UPDATESETTING(m_frame->behaviors.parse_spc, behaviors_parse_spc_pgid) + UPDATESETTING(m_frame->behaviors.nosplashscreen, behaviors_nosplashscreen_pgid) + UPDATESETTING(m_frame->behaviors.confirmquit, behaviors_confirmquit_pgid) + +} diff --git a/assdraw/src/settings.hpp b/assdraw/src/settings.hpp new file mode 100644 index 000000000..c11c42c36 --- /dev/null +++ b/assdraw/src/settings.hpp @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "_common.hpp" +#include +#include + +class ASSDrawFrame; + +DECLARE_EVENT_TYPE(wxEVT_SETTINGS_CHANGED, -1) + +class ASSDrawSettingsDialog : public wxPanel +{ +public: + + ASSDrawFrame* m_frame; + + ASSDrawSettingsDialog( wxWindow *parent, ASSDrawFrame *frame, int id = wxID_ANY ); + virtual ~ASSDrawSettingsDialog(); + + virtual void Init(); + virtual void OnSettingsApplyButtonClicked(wxCommandEvent &event); + virtual void OnSettingsRevertButtonClicked(wxCommandEvent &event); + virtual void RefreshSettingsDisplay(); + + wxPGId colors_canvas_bg_pgid; + wxPGId colors_canvas_shape_normal_pgid; + wxPGId colors_canvas_shape_preview_pgid; + wxPGId colors_canvas_shape_outline_pgid; + wxPGId colors_canvas_shape_guideline_pgid; + wxPGId colors_canvas_shape_mainpoint_pgid; + wxPGId colors_canvas_shape_controlpoint_pgid; + wxPGId colors_canvas_shape_selectpoint_pgid; + wxPGId colors_library_shape_pgid; + wxPGId colors_library_libarea_pgid; + wxPGId colors_origin_pgid; + wxPGId colors_ruler_h_pgid; + wxPGId colors_ruler_v_pgid; + wxPGId alphas_canvas_shape_normal_pgid; + wxPGId alphas_canvas_shape_preview_pgid; + wxPGId alphas_canvas_shape_outline_pgid; + wxPGId alphas_canvas_shape_guideline_pgid; + wxPGId alphas_canvas_shape_mainpoint_pgid; + wxPGId alphas_canvas_shape_controlpoint_pgid; + wxPGId alphas_canvas_shape_selectpoint_pgid; + wxPGId sizes_origincross_pgid; + wxPGId behaviors_capitalizecmds_pgid; + wxPGId behaviors_autoaskimgopac_pgid; + wxPGId behaviors_parse_spc_pgid; + wxPGId behaviors_nosplashscreen_pgid; + wxPGId behaviors_confirmquit_pgid; + + wxPropertyGrid *propgrid; + //DECLARE_EVENT_TABLE() + +}; + +class wxLongPropertyValidator : public wxValidator +{ +public: + + wxLongPropertyValidator( const int min, const int max ) + : wxValidator() + { + m_min = min, m_max = max; + } + + virtual wxObject* Clone() const + { + return new wxLongPropertyValidator( m_min, m_max ); + } + + virtual bool Validate(wxWindow* WXUNUSED(parent)) + { + wxTextCtrl* tc = wxDynamicCast(GetWindow(), wxTextCtrl); + wxCHECK_MSG(tc, true, wxT("validator window must be wxTextCtrl")); + + wxString val = tc->GetValue(); + + long valint = 0; + val.ToLong(&valint); + + if (valint < m_min) valint = m_min; + if (valint > m_max) valint = m_max; + + tc->SetValue(wxString::Format(_T("%d"), valint)); + + return true; + } + +private: + int m_min, m_max; +}; diff --git a/assdraw/src/tsukasa.ico b/assdraw/src/tsukasa.ico new file mode 100644 index 0000000000000000000000000000000000000000..f645f0773c987c58343e8a4b1d722ad64f900f5d GIT binary patch literal 17574 zcmdUW2Ut~C);5`BzGSMIZzhvuX1>HEldegOnnXo0_5^#4h#hQ*6;zrODN4V9^xm7I zfPionR6v@54G>YWB1KV9^jdiTcOAG!Gm}YXp8tQo=RXgNea_zdoc*q~*Is+=wfEK0 zc~0jAozFhgVcJ(G@m(FAaXLCWef#!4zpSHklX*SQFX<${q@&~ig^te4%zsYjC7l(& z)X^ECr=#-$-{B{87LnvT$~B=U`MLjx?+<=!c}sfNFVl+gay9VkG2nM6fGMX>q69!b z$?QfhrtbGda=AZ>&n2PwVk)Lts4&B=6H`4p@vTK75{ja6^hy#k&Lm*E%Q770dxuHM zSGu68IDsR)uOwBG-iyw}Z}NfPAGvqyjgrQ2Qj*M8CCQsA|O-3TO( z$)$lvI2DcKXLh0D%x=;y1SV|46tk_E;oXeM9_{RdTbSW~2?q8~2+ayY+{pq(&XM)i zV0S)|J-ZhhJzXI9`t^G|{olp;&+30Ry#JK&;X`1MA655<-0}l`va22cR$Tn6F?D-_ z)^}g})jLj0Q>|A|Moxh*D$l2({Co0?Top(O|cwd&TE!qx6s~_$hz*#Up9r6cmgdiQvx$V*3mOOgE2v_qVUd~^xH-2XPeynSGeDObMTXKzi{N--Q{B@pnTLAl#Uz&yHAEIwqfjt-^9Co z(hnm)_!!|kf>71cgHhxxc*TyFk;JypR6+8EwE2g4Ur5R)<&kn4A7k69E%2K>3Gj_l zgJQO zNGa^ExJDrU0${h%j@Tne)5BA?q>NJ_S zB2UWgC#`I#=*3?$avangW0ME!VOdlzlGt8E9jSpBTZyN;N5)#*k}mTo$CTwIn7Oiupz@S@x%U5cK}gmfG{e!w4HJd z*iZS2PKeBj|x*G0J#e{ek^a}sv(*u|Tt z@kgU_!)m23f@#BV+B>4b+!7Yw8XWl{Zte4a6LUOv@khUYGfe{*+F}2=32<647lsRF zp|tA}Z1?U)I{Q3^a*rerap!vEFM2F86@3?c`yT`P8`YTaP>DrOr!n8A5(}L&75&nV zX7R=GrL&bv@)fn#nvzt9)wS+dw`@ZORc|(bB-Rw9YGR368!N1eTsu~7NK#C z6|p5yzR589Vv3^{w>B00cU?kRClMNt7#NS#Q*3#)M;K0;Ss}VCA7R%oD(#94!!C6p z>_Us8H~DU7K%z+c8@0}#vmRQ6f)dGbyDle%JugtYtw*VVve)Nsl_ zrWZT=z&AzV<=~=aII)5Q$v2vm4At~Cx3svmso=lm65PXcU<};#^^p`2&hJIz-d0Bp z>i-s^&m6KxRvZP9hn1>q-?a!w$|n9I}!3fP?k!b4JmbR%N{R4PAKjv^1K zoohm2I^>7lM#;-{|Hb`VI_;)MMRm(S^a_K6{p7?#@P;0YO>OZH&E~(&eauNL@ z|GqKkbZ|%df@QpCA6mI$_-}uO;QZZ6JFKpxM&2&Sz57V9DJ8jtTZl7Ij|Da*vAB`0ME5czJL?ygOw)7Dt32hxHG#EMTw9VQgPT z@)SM}RJ{cvFRLf&&+`$Q=eID6e=!JTg5fDiuk#_MvQxo89Xs0aV~jg}mI9Ph%f zdk_+be~lumt%}}@oi8JWHC#mGrStGTUW%COm!FJD!9w*{?2L?Qp?s3qQ?W}y*E^|e zj}hD2fsEdKDGMquLx*=L@e`w`*dXE;@`yy1;nH60qCe57F>^=10ZTveTD0VW*Gz*H z!S6fOka&rIXFO`wZtju2*fMX<msgY<=977|`1&F#3Q$VkDjie-mz{QAiljz{)F;#cG^Iuu768HK)o9x-|Q zT!ZXu^dkxXGi!Z=9{hocs8{9-)}=o#zQ}=@^LZZwb#j~{dy&8JAf&ZHk_riK!6%eY z5*`Gf@Q_c^mdUf^i{6!!!h=GfUbTkp5xbDcUUY~)T-65AjW`l92?eF#^LHjVOc*>Q z5y9Co89eU1sV>(LBL9FyRG4bz&$_;0IKGVbmohozV2iVC>ljIRkoF_%>lKWGQ?Tbf z=)o#|AoN50f?N2`?=6!xLU?Qv?t4d|YU38R7eyj|RT5L;Cm+XFujPF&b}0`^z{D>N zvR`}cwQmr-8_o24M!ohH^NF8j6|`AB^I({wZ}qD;F^$75TMryP-=O&O!h^_eq4ipY z7r~j)ra|iECKPq|@FM&OR^e0hB;?|COjeA%1^QXGA7v%fpM7q_55tn`y8V!k7e9C>gurp*5{mcY}NW6{2r{!`WNVjD08}Z zA_@Y-6koWKZ6E4et>U}UIJcH~Rl+OZCtX-)P2Qu>ZRvp`gJp<{i0$#0Y3DQ6 zZb9bGAcY^n>3t+$(W|4p*YY5JV{Ebp$;VGBW2BtVzPom#)N~6jZFW+0Q^wvyCtuuj z@xeXX48@oB6ZyqKy>unzPq{1eGWp=W?xz#2M1SPk<+HsNaWN=ZzFgt=o`W~qxpwaH zK~m6R?M6qypFTLhyCVv*^H(DFa1jn%=|JMzO~~bVsb`-EPQStuMYrW%NO&maoVRrR zBosY(&|_Q4_XdeMcn9}j=hzbb?Jj|^v)F`<6*&lIs^~h$zyV{fWddnS>3?FU^g%QR zd^Bcq4|*M)Df7nqvKiDQhT*Yq5Z{Z&8B^+_4|Pz;H4H85?dXhAPhF^ocK#^KIe^TY z_h2?*B5GD_LPhO4RFMb&qGF|Q1iQ#t((vRo#F(smVqZ;YC+~=iIi4V6jOjzqSh$6S zVlnGaI#Y;Kek_;yhxm(E)1J_d${IlMH*E7m|3UMf;1@nRx@8phUPnss(3k#$*Z>v3 z;~9-+;m0k4i&ddelv->>9jos!w?N&3)kw5+Q1E&c6h5Q(f!obB;^VACW!nP~tLc44%5>k6QUl zstUJv0Pf;k*JXa8_c~oe1J7@$!fu3p9ch-6&dLKa1T97)?k zac+|XGWI0lss-(cT@W(E;!*y9qARIX^hWGt+WDKPUgL-)8wZ@=bCM0QxRHPNM3j1k z!uZl@EMWc=(kv1^6_ne%^~hfOpYpD?cV!UiEK7T?(MwkP{t&x7_n$U9W(_o0!fJ-N zfso5GJAQv1FQC5GQ_oT}*{|$l8$XozxFJf`N6zd6Gdo$%rtd z-=g$OFp9$%V`d$h--P>w0P2fN6cSur5vZbV@QB4>S6^&sIZ(hCk% zMT(sfyrM6LB;iAgKf#5%uHa`saPE4-2W^qF>E4Kq7vnO=~KZj zsrFuUXZDTrxJ+9IKgwQ}LhR&0<~Gj#XIMAZA=pEt4P9= z_SuB@^UIE6E#u+YYit$#unkAHp*==Sb->;w+W^~OSi~eMIw?A`kR;!fK9yAZK;*w5 zo0!Gtk-5QxOinj`VGo z-am!)pOxz$iM3F0@!RxZK`W!Af>%p|U-;TUlKai1$<`LIuBpKE%v~5{zeOn%of8tg z+CI?oBlwMPTtuz-o`PLPnTzgFmqJ!8{Ex6}`B~C*^r8*FZ%z`7l5QjkKU$J}p_L@* zlW%GZl{&)Dyoa4izSf>eUy9ynNq7*RtoEmK;!Ej~Ke0db2!OBIKcA!BY84jl)l*E z%9wIEZf^~u{qcu=R_c@f65P@s(ift4BR3k>r0uZzzbfw^<8OIM(#&-o4R@q3I1?*8 zh`eP?2zKFVv&=#C8Q?(sA->*r+HeOIr)PhqZ?xk_cs4kmti&>ZtiAtN`Abe@nXc`+ z(kU3Wl0=Ro)7eFPB5c>J`6Ofg`gKRvu3uMSwrO3inc2F0N83dzQ_~kGrtj2S&~~cM z_)fza!&BM+E7+fwiAi~Gf9LDht?ZjF`y1|${@L^6)8F_{?nSO5-IsEvk0d2tZQ3o*rE*q6!glok*|Z#{P3HS@(5AiB~$(suHj>KMv{g*G8`SFx-qHRCx z8ZxBc2R_4xo){k+kk%)s%J{ti`(dAk*bke!WKvm_y%F*X1Cdo02(yq>O!vHoDLh9s z)-n@m#eqmW7=&r2OW>28h2T)eOn2;rx#^Nx^RdI5#x0#WLFetae^Le}gN9D#8It=e zKL6ZP0RQpTH@_CzI{3@>tl>l9GHWC<(>-9wvttt-j^bOp66mc@fv4?4`ZH7EE@y6Z zzd+pRuW@DT4rCdxLA35rEYKa&_QfZkkNPPg_>P*i`Ovtrh#EGcWd5*OZ_IpW(EM-S z*FCgr#Au{*-#TuYDZCku2rDc@$pgkNA9f@D@-2jM-zvs+2NH~okT`xUa>tKDlg$pa z*n1)88-0X+Jr0&LmTw#J)vVdm^ybNEf986p@4zXN;W}!1LD`tG*sVKSv-z{pQx<$Q zbk}zue}-fyALJc5ibCqw@y9)J?qlrZ5>grID5;pV@A6e7WEa8zy9Kzwc)(-EvvTJy z#NJtk*fMtNl?9WG>%Et(pYcrNd@!3hZ`br8b3X_gH}hoasL{yR8wu-S<1&|S@@|MO zYsKzn#`npuw!MQL8U!A_0iT06vG?+QCBJ~}r{Cwg{xU;4>UzdcLX;T872EB|U$qwd z<}60~Vq=`ObDRIe_CpL-+znY|IVo(4@x9{FW61vqn0)>vqUvrTvX=Xw=efsow}(f; z;9JrRgAI+CZ+a6eUC%1xt&sV9I)I1^bZ-w_L9Ogp$$E(~GIKh9x=G@4I8xg)~BfP^x5OVAo$1~$97r7VT#C?5DPrO=W9drH`EEDVDka`hz zsXga5B|rNiV@jRm|9CS_1*J0Rm!PbBTJ1fMH*YEKIJlvfYi+$Z^?1Vc$@RSK(eE*} zf9SHs66ym7us<~okq!08rOY$#^z2<6=>C5HB9HgEBuNjlT_NHB07>%qv@tdk&h~f4 zuzlG(XKa4y8r)>O^)c7J3kOdet!-UTNAHDuZV`CIeH72ln^BvRiG$Q}ztRHitSaYO zm`-He?o#%7GKD{ySlRpZKiQK;H*gRA9OF^s`!L%tBDo(rh?$f0**^UnSvN60T)S~A z8qIBaACKfERxwYvf7^}g6YfJ5t>26*JN%(GSOjYid%W`QThQYfroi%26hH1(&eue+ zKV<)VdoC%DAulrRU2+G$$F9OMEe#Wlmf%lszJcHW@&&e~Z9fTDwm33=PS1?`aKh5J zQg6Vdzt0=>?Q+4<=-{j1>TvPKJ?d?pkqKg~EU+#p5br#-xBKPXIY?>iLLBuWhrl@3JCO4eJO23^j_HyGHfn22@l>Vn~WOlK? zX%F^m9x#SbhD{kc@H}x`sS{sY1C8wWcu@vCJM!2w64kPQMw`&;#B-kutX2juT~;4r zxO7sw#BCXay=t?ab>q;)7|aF6APdi*RmS$=M{*{o3wa#hqUSQ!^GVX*LhO!b_G3jC zb1q*(jV<|-vjaPK@w-VlWo}JAW6;e#m&k=yJ0pzNmqZ(`IUHq357|Evjn>kBEN(Cs zb%Xo%NB5;Gv9=O^Z(n=+lRcJ$AaH^fUDg1Dr;*)S$8)>2LZ|+ z)qdK(^p;koU1?h=wY3F%NpgSq;Ug48CgYxWG_FZZmh$e|_l&0*O6uI=K*$IYJ6hz zr9V`M3Xpv6tRkO%oF`hTYj;VvcwR$kpR$!bvl`h4lNbPPLcjuxs*hfI<+pX--sx@b zUTAalMw{3N?*w%12t${nA3C0? zBBQPz`F1YopzLH%;<}RxWx?TS_TpT?c+W}Mf0lSTvBWO336rsBVYYNr`_V$&^bJ6# zBXKik-_3oAJJufDgrKfkc_Xj<1pHk*QOS5wW!F8#*PT)3J(>HmYR@9dF93(xN8#T7 zxWRpIc*T>CcmyQx!lF~fIPDooJn^XA;>5O-aF6ku$Yoo1Nc(cN+-|sh@9}+f^9+0U zt}NDxLi=Xwt{caXOE~Jn;}9Pei_`0DP*imiv1iUJ_DE!qmXv|a$Y{h=o<@zeipD4& z7yM)4Sa?K2_9gn*>s42&; zsAQhE@nh^W8Xern%?%5u{iY0J_hMhu4XAH-qTt>`6a*w;Uwjhw?jdgXSTuRXVC$(W zeD^@Zxbs8!r*Q1a9w*1aHJ=FJL_JO)t!P@b+Wg77_@kLK|334`!Lo&{Z_#;;de(-f z#zr*erEzeVa#Bsh)twjs{f>;sZl)l5m0go_C|XzA{N?)-0|FzP~PrK z(U869rd-R7XulcTE(=XT_QfXTpS^&KJ0h@)G0>BaKCnK090rdbK8e3CqW!84XCEeI z(tz!UCS^Asov(qhwXMPTEq{MEzV>+GQfZ&GNa*oH81r`-36ZHdy*&)~HhV#1=}X(h z_!(_`UQsQYxIYuI!Wc?;>Ljkq-hk}w`o!WP_uMk9or}J2@!7lS zmnx<%z29+1+7}$lNXDe)_wQqUJ$5HV^wWlx>H+I-Uahy}H>veXTu)>om?S2%`k)#| z;WV?(}hyUy{Dc$|YHElJv7Mjj;n;t*x zw&1s=57t#zo+x+pDY?5fsH)Q;r0S%rSApf3)B9IzIy=_fYrpngb<)52tEhjCZ{@XP z12=ZGuGo5~bJyD2H=O3?#C?#wX3ZN%*O1qU>GZkH5JPnjJzv16MK3+aRH2bfNy=r%|5_^GJ?Xi|USf#ic_DuP zMN%JzD&AzALr)1)6g&^mUzXpK^(BoY$?wdR>*Oj2V?p0l@v~*UFTO6<`QSVb&M9JB z;W>O5e`_-g89d(?@bx#!y4{7}@tZJHS)1 zVgBZ)#>^UxB#B+*Y_n2g1xq%0DRy(BO&aE{Z9>{=d&QP!SvsSR{@+O}6`HKJuRp4JPKffw2-$_rTbZ5+gFg!S8UwcDxP_#A0zX+ zaYHRE>P~}m1*q>ZzFdaYttUZyf!S?WLHmul^o7JmZfd5T>}bWN&g)qA_%X~#;z#au z-VWLa#itDn3`XL1R~)o+RN@R`vt#^&6n{hFT6=SR5y;<*i2vuGlB(Ey_x;&QoF_gq z5n1txiVq~dq1e2vto^8U@<2J)w5N6iqQXB6XT1Z^y3GR}T>m!nyw4ryKs5V=Kz!bk z5UvgBFNxpM?H+^XphT_@=r3{3FN{k;NnEC~CY80oz4%ncC+SYvhwk)T>}A|ZVqdb} zOgxsJGf4C;@OwL(xXb8PS!{{^pnMB%PTpBxHD&vQ`eyQ zF2R(k#L@gpo0WCEtTknQ8Cb)0E@L54XCFY;pb{5My?PBp+ zC4{U)lZ%zh%x!G0o?TU3Hp|MSH2h@qHH~J|X0E5S*RNUbX;xfTJj=?;~j(9IFVAltK==-9!#>@R*#xH*Ha{tdF7H`=yeemFZ zFAp*t-M`Q8wB?^p=>JNeKA#L4<#$4(xgH%jYS19Vy@Nh|=Z~*Qjo)Ixgirq5r%&+d z`wukA)pTs2fqKxOH{a5e@;6`koaHkvU2oNBTF+HA^Z)gV-D<66_I1FC=IiqQWy{KPmbaW|`QD-F2LA=u@nH=Z+n_ZK=WH=L-R_up?IL!FuV?KtD#>*)OTIzMpz_^+OO>HTPy zXR`e7dM_BL_o}Ci*3=m-iR}S!eC;VsojO;YJ5il6O!v#NYD2Z5LAypn1RDOal19^H zAOO_ruk?ltWBJ~UL(Jt|*+oyiE??db0AzAdM)=y2!9q5#;c;f7~t=>~<~8Om$RwiD&!6%f;KrYxOAoN#j>IyL?|p|KWe#PkEWfs4wY> zpYjVn{Nsi9ULS~AAAX6+U+ZH2;Hen&%>?wFJsHDo3^9^1!A1JU_-6fjSgu&g-y#l0 zz))R;4jm1T8ROxnKLb89rz365H^>+>72D=4!HyY52w$`s++#$Vtu6L$w1C>u7KfIt zMls{@g{wEBXw3%HFt&Aeqb<_xEfF2E1u1d17{9d$nI?`nV(yIcP40}pIN_Z6b{yx~ zoKsdFsN#CPYHJ8;Y7-fC6Wfuf?aCXuz#s+K0K z>TH4e?Tc7^w-ZKpIpofYt>)Tk&7()kdP`aN-0Q-|`<>XVxd-zH51yP! zh;(p4G}p)nIcKM+b|Rl=|MEj)u`MYHLCLAGPtS&5KIg`SM8q*RnwQG`s=fOVzke^H z4ycilnS=DK11Mx{rGAGOs_lGn*4_^n_?x)$zzD|ScwTp#H(H&2(dMi|3+L{ejyuuL zxK!JAKiuRT-^$;fUFG?bcGn0cW_Fvuhq%ia?p@cOdHz1<`bRwHbf0GuZ!i{A$n$$O zfw9o=?9xNVc^;A47(;01+PGU{Q$jNTGe#sayUvJI+?U@jB&I+UmBMpgyO38MiO9-a zxT_hXK3D=@^;ztTD?r|XV{qV)|5OLsP?dEU^*IIHxHtm0f&343H z>_Bo0W8pXXo0=Qk_h3wX_ie^NJ364gtHD9WWpcWD_5z z^`}rlKcnQzWfZjEM#1gdD7@Q6dVs@^dt$2e7?eGo5~dtx|347@lov;g4hkMVBqXPD zpe}*qw}bwx&pTiK{q5iU{PB0;2Tn&voyv=F z`Ro7iclm}U2alY{-Fvby(zmbK3p#FV<`3SVpPrGK^pdZI`MZCz+4jbgU&gERzWTMX zx5sDi|M_G4Pb&@`FFH|lqQuH?^CusDU}+b-tGeLSo?}}bd^h!bYrCV<0MnycRjG^J zmw9bjkMhz$=Z&t}X)A3LM*8gBfiEWS3-JH2IA%f0*u=OphCIehofNjN|01LD<5s9- zmHqYfSuK}YBR04)yZ=l}o! literal 0 HcmV?d00001 diff --git a/assdraw/src/wxAGG/AGGWindow.cpp b/assdraw/src/wxAGG/AGGWindow.cpp new file mode 100644 index 000000000..024124ece --- /dev/null +++ b/assdraw/src/wxAGG/AGGWindow.cpp @@ -0,0 +1,124 @@ +#include "AGGWindow.h" + +#include "agg_rendering_buffer.h" +#include "agg_pixfmt_rgb.h" + +namespace GUI { + + BEGIN_EVENT_TABLE(AGGWindow, wxWindow) + EVT_PAINT(AGGWindow::onPaint) + EVT_SIZE(AGGWindow::onSize) + EVT_ERASE_BACKGROUND(AGGWindow::onEraseBackground) + END_EVENT_TABLE() + + AGGWindow::AGGWindow(wxWindow* parent, wxWindowID id, + const wxPoint& pos, const wxSize& size, long style): + wxWindow(parent, id, pos, size, style, wxT("AGGWindow")), + bitmap(NULL) { + } + + void AGGWindow::init(const int width, const int height) { + memDC.SelectObject(wxNullBitmap); + delete bitmap; + + int ncheight = height, ncwidth = width; + if (ncwidth < 1) ncwidth = 1; + if (ncheight < 1) ncheight = 1; + + bitmap = new wxBitmap(ncwidth, ncheight, PixelFormat::wxWidgetsType::BitsPerPixel); + memDC.SelectObject(*bitmap); + + // Draw the bitmap + attachAndDraw(); + + // Request a full redraw of the window + Refresh(false); + } + + AGGWindow::~AGGWindow() { + memDC.SelectObject(wxNullBitmap); + delete bitmap; + } + + void AGGWindow::attachAndDraw() { + // Get raw access to the wxWidgets bitmap -- this locks the pixels and + // unlocks on destruction. + PixelData data(*bitmap); + assert(data); + +#if 1 + // This cast looks like it is ignoring byte-ordering, but the + // pixel format already explicitly handles that. + assert(data.GetPixels().IsOk()); + wxAlphaPixelFormat::ChannelType* pd = (wxAlphaPixelFormat::ChannelType*) &data.GetPixels().Data(); + + // wxWidgets always returns a pointer to the first row of pixels, whether + // that is stored at the beginning of the buffer (stride > 0) or at the + // end of the buffer (stride < 0). AGG always wants a pointer to the + // beginning of the buffer, no matter what the stride. (AGG does handle + // negative strides correctly.) + // Upshot: if the stride is negative, rewind the pointer from the end of + // the buffer to the beginning. + const int stride = data.GetRowStride(); + if (stride < 0) + pd += (data.GetHeight() - 1) * stride; + + rBuf.attach(pd, data.GetWidth(), data.GetHeight(), stride); + + // Call the user code to actually draw. + draw(); +#else + PixelData::Iterator p(data); + + // we draw a (10, 10)-(20, 20) rect manually using the given r, g, b + p.Offset(data, 10, 10); + + for ( int y = 0; y < 10; ++y ) + { + PixelData::Iterator rowStart = p; + + for ( int x = 0; x < 10; ++x, ++p ) + { + p.Red() = 255; + p.Green() = 0; + p.Blue() = 255; + } + + p = rowStart; + p.OffsetY(data, 1); + } +#endif + } + + void AGGWindow::onSize(wxSizeEvent& event) { + const wxSize size = GetClientSize(); + if (bitmap && size.GetWidth() == bitmap->GetWidth() && size.GetHeight() == bitmap->GetHeight()) + return; + + init(size.GetWidth(), size.GetHeight()); + } + + void AGGWindow::onPaint(wxPaintEvent& event) { + wxPaintDC dc(this); + + wxCoord width, height; + dc.GetSize(&width, &height); + if (!bitmap || bitmap->GetWidth() != width || bitmap->GetHeight() != height) + init(width, height); + + // Iterate over regions needing repainting + wxRegionIterator regions(GetUpdateRegion()); + wxRect rect; + while (regions) { + rect = regions.GetRect(); + dc.Blit(rect.x, rect.y, rect.width, rect.height, &memDC, rect.x, rect.y); + ++regions; + } + } + + void AGGWindow::onEraseBackground(wxEraseEvent& WXUNUSED(event)) { + // Do nothing to "avoid flashing in MSW" Grr. + } + +} + diff --git a/assdraw/src/wxAGG/AGGWindow.h b/assdraw/src/wxAGG/AGGWindow.h new file mode 100644 index 000000000..01039da39 --- /dev/null +++ b/assdraw/src/wxAGG/AGGWindow.h @@ -0,0 +1,68 @@ +#ifndef WX_AGG_WINDOW_H +#define WX_AGG_WINDOW_H + +#include "PixelFormatConvertor.h" + +#include +#include +#include +#include + +#include "agg_rendering_buffer.h" + +namespace GUI { + + /// A simple widget that displays a bitmap that AGG can draw on. + /// It reallocates the bitmap so that it always is the current size of the + /// entire panel and calls the virtual method draw() to draw to the bitmap. + /// This should be useable anywhere a wxWindow can be, e.g. in actual windows, + /// buttons, etc. + class AGGWindow: public wxWindow { + public: + /// Create an AGGWindow. Defaults are taken from wxWindow::wxWindow(), see + /// that documentation for more information. + AGGWindow(wxWindow* parent, wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL); + + /// Clean up resources held + virtual ~AGGWindow(); + + protected: + + /// The conversion between wxWidgets' pixel format and AGG's pixel format + typedef PixelFormatConvertor PixelFormat; + + /// The wxWidgets "pixel data" type, an accessor to the raw pixels + typedef wxPixelData PixelData; + + + /// Create the bitmap given the current size. + void init(const int width, const int height); + + /// Attach the AGG rendering buffer to the bitmap and call the user draw() code. + void attachAndDraw(); + + /// Paint the bitmap onto the panel. + void onPaint(wxPaintEvent& event); + + /// Resize the bitmap to match the window. + void onSize(wxSizeEvent& event); + + /// Handle the erase-background event. + void onEraseBackground(wxEraseEvent& event); + + /// Draw into the bitmap using AGG. + virtual void draw() = 0; + + + wxBitmap* bitmap; ///< wxWidgets bitmap for AGG to draw into + wxMemoryDC memDC; ///< Memory "device context" for drawing the bitmap + + agg::rendering_buffer rBuf; ///< AGG's rendering buffer, pointing into the bitmap + + DECLARE_EVENT_TABLE() /// Allocate wxWidgets storage for event handlers + }; +} + +#endif diff --git a/assdraw/src/wxAGG/PixelFormatConvertor.h b/assdraw/src/wxAGG/PixelFormatConvertor.h new file mode 100644 index 000000000..d1b0741c4 --- /dev/null +++ b/assdraw/src/wxAGG/PixelFormatConvertor.h @@ -0,0 +1,117 @@ +/* +* Copyright (c) 2007, ai-chan +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the ASSDraw3 Team nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY AI-CHAN ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL AI-CHAN BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WX_AGG_PIXEL_FORMAT_CONVERTOR_H +#define WX_AGG_PIXEL_FORMAT_CONVERTOR_H + +#include "agg_pixfmt_rgb.h" +#include "agg_pixfmt_rgba.h" + +namespace { + + /// Given a particular combination of channel type, bits per pixel and + /// channel indices, return the AGG format that matches. + /// The actual template specializations that follow give the actual types, + /// and using a combination of parameters that are not listed will give + /// a compile-time error. + template + struct wxWidgetsToAGGHelper { + //empty + }; + + /// 24-bit RGB + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_rgb24 format; + }; + + /// 24-bit BGR + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_bgr24 format; + }; + + /// 32-bit RGB, alpha unused but stored as ARGB. + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_argb32 format; + }; + + /// 32-bit RGB, alpha unused but stored as RGBA. + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_rgba32 format; + }; + + /// 32-bit BGR, alpha unused but stored as ABGR. + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_abgr32 format; + }; + + /// 32-bit BGR, alpha unused but stored as BGRA. + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_bgra32 format; + }; + + /// 32-bit RGBA + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_rgba32 format; + }; + + /// 32-bit BGRA + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_bgra32 format; + }; + + /// 32-bit ARGB + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_argb32 format; + }; + + /// 32-bit ABGR + template <> struct wxWidgetsToAGGHelper { + typedef agg::pixfmt_abgr32 format; + }; +} + +namespace GUI { + /// Convert between a wxWidgets pixel format class and an AGG pixel format class. + /// Usage examples: + /// PixelFormatConvertor::AGGType or + /// PixelFormatConvertor::AGGType. + template + class PixelFormatConvertor { + public: + typedef wxWidgetsPixelFormat wxWidgetsType; + + // Break out the wxWidgets parameters and feed to the helper class. + typedef typename wxWidgetsToAGGHelper::format AGGType; + }; +} + +#endif