From c2a506082a9345e2004a4c7a7afc7dab8ad36f96 Mon Sep 17 00:00:00 2001 From: Pablo Gil Date: Wed, 28 Jul 2021 19:27:49 +0200 Subject: [PATCH 01/19] add mini-map icon --- app/ui/style/olive-dark/olive-dark.qrc | 4 + app/ui/style/olive-dark/png/map.128.png | Bin 0 -> 934 bytes app/ui/style/olive-dark/png/map.16.png | Bin 0 -> 356 bytes app/ui/style/olive-dark/png/map.32.png | Bin 0 -> 417 bytes app/ui/style/olive-dark/png/map.64.png | Bin 0 -> 569 bytes app/ui/style/olive-dark/svg/map.svg | 155 +++++++++++++++++++++++ app/ui/style/olive-light/olive-light.qrc | 4 + app/ui/style/olive-light/png/map.128.png | Bin 0 -> 1071 bytes app/ui/style/olive-light/png/map.16.png | Bin 0 -> 384 bytes app/ui/style/olive-light/png/map.32.png | Bin 0 -> 460 bytes app/ui/style/olive-light/png/map.64.png | Bin 0 -> 629 bytes app/ui/style/olive-light/svg/map.svg | 155 +++++++++++++++++++++++ 12 files changed, 318 insertions(+) create mode 100644 app/ui/style/olive-dark/png/map.128.png create mode 100644 app/ui/style/olive-dark/png/map.16.png create mode 100644 app/ui/style/olive-dark/png/map.32.png create mode 100644 app/ui/style/olive-dark/png/map.64.png create mode 100644 app/ui/style/olive-dark/svg/map.svg create mode 100644 app/ui/style/olive-light/png/map.128.png create mode 100644 app/ui/style/olive-light/png/map.16.png create mode 100644 app/ui/style/olive-light/png/map.32.png create mode 100644 app/ui/style/olive-light/png/map.64.png create mode 100644 app/ui/style/olive-light/svg/map.svg diff --git a/app/ui/style/olive-dark/olive-dark.qrc b/app/ui/style/olive-dark/olive-dark.qrc index 551f40913..cf8b1381e 100644 --- a/app/ui/style/olive-dark/olive-dark.qrc +++ b/app/ui/style/olive-dark/olive-dark.qrc @@ -110,6 +110,10 @@ png/magnet.32.png png/magnet.64.png png/magnet.128.png + png/map.16.png + png/map.32.png + png/map.64.png + png/map.128.png png/minus.16.png png/minus.32.png png/minus.64.png diff --git a/app/ui/style/olive-dark/png/map.128.png b/app/ui/style/olive-dark/png/map.128.png new file mode 100644 index 0000000000000000000000000000000000000000..d02a8be78b2b36a22023427ce0267fb1970873d1 GIT binary patch literal 934 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9Fx?I6t9|MX)jP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IXpdt~FBFEB_jQk=7-^8Ml%shpl z(&Fs=vhAnO%mm6CLkuY`N={|SC@Cqh($`PU&&|!xE7mK|%+AbBP036I8K)28>4zld zrRT?ICgb}oTTCjL%Oqz zt}}1F7Mj3+gLPZO)_rZ4_U>Rh*P#4h!IYxAUzeO?$XY9Xz&&C2&5Bo-Id14Lt(jey zpw@8G`oD4BzfF>i@0azn_*>1}(LQ^B&o;^TZ*^lC-)x)j$~^Pt?R5;>sw7+v&j7Lh zY}>yl?b|kO23Es{0ycqj42L8gcBFUzYYaNI`nQD3=apOM>3^!~XL!h?Fo&_l7_P>G znIoM%!Mul?o8@Omy$*h$yMcKPv-V-XFX!_gSQjwKOb`0^Wn)GN!`5rh53naBzlgM2 z!=SIe{rc8xhl5U~zIIKvV^Vwnr+wGY%Vv$=C(oSS#o6#N@xNitzh2)5zOP*;G2M`? zYOpk!Z>iZJ?#yq{{`DHS3hM@|FAT*QcEv#quM)^{uqR^-zwH5;x~*{vnho+GW1ktT zvlbWuO|{6~>k!0n{J_x*&VSmuj~Sbb3Lkh<^V?a@okjdWqWpD^j$L*Rnol=f`_=g| za=W>BM}@}no>=334o5WO6c)`rY5(o?6EXc~$_{tY0(VDiJ^PBS^X?y4kZN;B4)W`w z2SVQmuh6$&{{98mgEj4rCOzvfmzS^>Tsf>Db?nT&GS&iyo?GV^&Fhr_#<(%#l%REu zwLJ+ldm8?pymv3?U6t$FQqddCWsSNCYj0L$J?#4UeJ3zMWq($MCp*teMZdUpR;F%S T#hzmV%pMG$u6{1-oD!M<{G^5W literal 0 HcmV?d00001 diff --git a/app/ui/style/olive-dark/png/map.16.png b/app/ui/style/olive-dark/png/map.16.png new file mode 100644 index 0000000000000000000000000000000000000000..63f0a15b747fa43c8d3defdf26ff73627b915303 GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9G08$g&*{RsbBprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&=TMUJH<8TmyFzKKO8nRyC9 zrN!C#W!q1mnF*9Ph8R*>l$^?tQBqQ1rLUiypPQSXSFBf_nVp%Nnv$6aGEN`F(+^3^ zOV5wbOwP}Xo3AkesC|;Bi(`mI@6m||IS)CAxZLNDc5^5abkY+`5H*{~<&?z{HX*3U zN5AR8L9t_}cb(8mn=W8<{O6h3`+q0q&s@T7z%Xs5=mqNqUmh9?Utn6wy?RFM=2uI_ z7M#fWdnv2oxA~M3rdKEGX5E~^!XWiPpi)$TVfO#VX?vEcpE+RrNzR_}{cg1v8ZZBF m?)bTNmy5%TFE{6YW%|BNA!zGsLo1-`7(8A5T-G@yGywqU?S}jS literal 0 HcmV?d00001 diff --git a/app/ui/style/olive-dark/png/map.32.png b/app/ui/style/olive-dark/png/map.32.png new file mode 100644 index 0000000000000000000000000000000000000000..a55df93a3579007b87cfb899a56d9952081c917f GIT binary patch literal 417 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?5GL=a}2dU(e+prB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&=TMUJH<8TmyFzKKO8nRyC9 zrN!C#W!q1mnF*9Ph8R*>l$^?tQBqQ1rLUiypPQSXSFBf_nVp%Nnv$6aGEN`F(+^3^ zOV5wbOwP}Xo3AkesQtXBi(^Pc>)UC2c@G%~xZP*f&`e;HayhD$bC9jakzXiD-mIhL zMkC{*0u!??A@?WO#SX16ez)lRNz3TQ6Z@4JR#h>4J8)#OIlG=x(H6#_glC?4CtXtJ z&S=g5dE8RhUUcHs8DXKdBG)}*7wacANGMb^oHESmTE(@2@mYLS{DD}PdChDFpJET+ z(=8|y{Z-TC+I=rIR`CbV5e9o%1^GqW{ToCI=H<5k2@8Ky!#|_@>6Ndw-XA1aym9#1 upPKWAFU4-D>vGanMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&=TMUJH<8TmyFzKKO8nRyC9 zrN!C#W!q1mnF*9Ph8R*>l$^?tQBqQ1rLUiypPQSXSFBf_nVp%Nnv$6aGEN`F(+^3^ zOV5wbOwP}Xo3Al}fq^mI)5S5QV$R!HhF;8$B5nJ{w;C;I-(V6ZcI$|B1+#u)a_(gB z3T9g;g{`qcRUICeI(yDanJGlFm3?!6_J5Y_q+0#|H9l-lQng zUv>D()VDjYqoZ4~X^PU)FNH6DzY1?)UBSF!BSR^Jsn`O(1;?1KFkXphuxgOv4qy)m z-rI2QaRFzLm-_+P0?wZ5jGe604k#(@E9O#VYS1jWoV?$#;C9b*%hpx<4=f3|zxoc- z)CRk0f8*yo6ARhT83i*Ynei1vmX3q0LoaJbU3v71_iEc6R1+dsyw}?9P%0B=a-GxG zp8tgI6pjqJs52*Rc27Srdx2HMtXHfh?-*m>-}qC$DxOj7g4Fs{&9-#&>)RU49R);p s@7!j7FtT{;V=sO(YG@c4izFOe42&@bPgg&ebxsLQ09=aRy8r+H literal 0 HcmV?d00001 diff --git a/app/ui/style/olive-dark/svg/map.svg b/app/ui/style/olive-dark/svg/map.svg new file mode 100644 index 000000000..b65b63e58 --- /dev/null +++ b/app/ui/style/olive-dark/svg/map.svg @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + Martin Ruskov + + + http://commons.wikimedia.org/wiki/Tango_icon + + + + + + + + + + + + + + + + + diff --git a/app/ui/style/olive-light/olive-light.qrc b/app/ui/style/olive-light/olive-light.qrc index 09583f156..d080d292e 100644 --- a/app/ui/style/olive-light/olive-light.qrc +++ b/app/ui/style/olive-light/olive-light.qrc @@ -110,6 +110,10 @@ png/magnet.32.png png/magnet.64.png png/magnet.128.png + png/map.16.png + png/map.32.png + png/map.64.png + png/map.128.png png/minus.16.png png/minus.32.png png/minus.64.png diff --git a/app/ui/style/olive-light/png/map.128.png b/app/ui/style/olive-light/png/map.128.png new file mode 100644 index 0000000000000000000000000000000000000000..724b977a4772765cb34e52b328612ce490a30257 GIT binary patch literal 1071 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9Fx?I6t9|MX)jP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IXpdt~FBFEB_jQk=7-^8Ml%shpl z(&Fs=vhAnO%mm6CLkuY`N={|SC@Cqh($`PU&&|!xE7mK|%+AbBP036I8K)28>4zld zrRT?ICgEaktG3V{wgIRadMA{w}FFEGJvXpf@=MfJ69Y-W|y*9n^ zO}U;n|M!Vj7N*!)4&pl-?l~@)?Q?dCp+wqKtD_e#oxHO3S;}IEi6JLalupijUpsA5 zuI!I^lV->LXMoOwfdk7v{2TXL!WnAj8<9 z%ut9>OO{}^y=)Ed(#m=F{V#9n=vaUD&9Mv2q5n$1Z{DO6e=cov{Fyax9xn=FeDnT& z{k{mDI)ly8f`(r#O3jrX`At$OFDozKFWc*Oe%a-h@v-qCFO?N)_y2I_|HPQ5FV_A3 ztj^nCPDYGRD(mjAJN{^Yx%K(a)v@s|Y7Hj7-3!wrLgkH5vj_;r^V%L>wMu{G+)11V z&PZ9O#xu`7zea>1N&w`468$tMh(X+$|AXMam%o^d7((m@ zJA6Iy_2q@)H4nDT+8J!bI3vTSWbGiMibd@hh@Ahxfv0fvezh~m3 zH*eP5xp(j1%`^LqH=oF)Z(@aIpxelT2}{xkmWU(ac&{kO&BVd))6=PWC~bL--@*ct;tkf^7tpUXO@geCz0 COwI`a literal 0 HcmV?d00001 diff --git a/app/ui/style/olive-light/png/map.16.png b/app/ui/style/olive-light/png/map.16.png new file mode 100644 index 0000000000000000000000000000000000000000..c6283ee791670e266a77a8360748fe6f9af429c2 GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9G08$g&*{RsbBprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&=TMUJH<8TmyFzKKO8nRyC9 zrN!C#W!q1mnF*9Ph8R*>l$^?tQBqQ1rLUiypPQSXSFBf_nVp%Nnv$6aGEN`F(+^3^ zOV5wbOwP}Xo3AkesC}KMi(`mI@6m||IS&O0xTMzwxhcGM70?$;5H*{~bwuh2m(J0} zQ&rj<9vtM`^jK4|P2lsy2Qv4K_{+q9l+SqnQ?aFB+L0AXn&tLORg67VDtiSzv^>%> zxle7{bA;o}8iodeV5!sRn@X}W(ySR~GuDNMmd;k4u!VI3{|7Dy^@u-gQcRQNbBbD* zWxn&AWb^CH-T8}~zOBFSRFs?c+fQL7pQ6X5!oCISlXhP>oSXWIO`qMY$dpxk6VT-h Mp00i_>zopr03Fhb=>Px# literal 0 HcmV?d00001 diff --git a/app/ui/style/olive-light/png/map.32.png b/app/ui/style/olive-light/png/map.32.png new file mode 100644 index 0000000000000000000000000000000000000000..04a464eeda1c7dbe3199ad6bc349bdc4c37e1dc0 GIT binary patch literal 460 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?5GL=a}2dU(e+prB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&=TMUJH<8TmyFzKKO8nRyC9 zrN!C#W!q1mnF*9Ph8R*>l$^?tQBqQ1rLUiypPQSXSFBf_nVp%Nnv$6aGEN`F(+^3^ zOV5wbOwP}Xo3AkesQs6xi(^Pc>)UCLS%(coT+-|2q>8=0m*89$`&4Of%Sqm%o_En2 zJN@$mI9R3Q71%Ydl`L-Xd}CtTcSm6F+%N8D6lyscCswyJOnCm2?LgEeoes^L3>EsP zKOJRo2ogMX=l59^QHF%NJh@YQj><4hdVF)$s%h6t&Te|M+IE{=qDE4KM8YQq_3Iz_ z4SX-F8%NI(op{c2`|8(K_E`-1|NhMO6FQT_`e5y9yB{2rR4R9bXusUJ-mH@U2*Y;i z1D4lgR(+M9P{{1@OXX(ecIz|tzg{)pXZ*im4_|D3!Y4V4n~jganMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&=TMUJH<8TmyFzKKO8nRyC9 zrN!C#W!q1mnF*9Ph8R*>l$^?tQBqQ1rLUiypPQSXSFBf_nVp%Nnv$6aGEN`F(+^3^ zOV5wbOwP}Xo3Al}fq`+cr;B4q#hkaZ9K8-Zh_wB`uEi1+@Qmr@p`~1tdh@>UR59x- zRfOEkX6yF%$0R!J8WwMp{$ykS+DS!Z@x>R#_ba|= zA7D!0-msD322+j>!!`zK?grfh$KXPX_!oS&km;zKwX9sJXx*yA`^AEIx?tDzeZ?A#GOy154=R6Pt^UjR0^CSs zTWmr?uQ$G``*|)=woMGk_;GF_OTgUQ>FrURp>rccuXA5xpO_mc63h^M>2r3Da0>T^ zPP>L{whP|M&;IWRa{7(f4v&)}FDIp~KP&pds~|D(dldVFInv!HSPpb8y27h1I1?CF jIf91`cn%x<_`@)>C|N>vS)3R!wi!HK{an^LB{Ts5N^}CF literal 0 HcmV?d00001 diff --git a/app/ui/style/olive-light/svg/map.svg b/app/ui/style/olive-light/svg/map.svg new file mode 100644 index 000000000..fa176eb33 --- /dev/null +++ b/app/ui/style/olive-light/svg/map.svg @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + Martin Ruskov + + + http://commons.wikimedia.org/wiki/Tango_icon + + + + + + + + + + + + + + + + + From e5c2960ed6b3489792bb04c351d7df435d1acc42 Mon Sep 17 00:00:00 2001 From: Pablo Gil Date: Thu, 29 Jul 2021 20:21:37 +0200 Subject: [PATCH 02/19] remove "mini-map" Node toolbar button text and add new icon --- app/ui/icons/icons.cpp | 3 +++ app/ui/icons/icons.h | 3 +++ app/widget/nodeview/nodeviewtoolbar.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/ui/icons/icons.cpp b/app/ui/icons/icons.cpp index f5466bb01..322237b65 100644 --- a/app/ui/icons/icons.cpp +++ b/app/ui/icons/icons.cpp @@ -60,6 +60,7 @@ QIcon icon::Sequence; QIcon icon::Video; QIcon icon::Audio; QIcon icon::Image; +QIcon icon::MiniMap; QIcon icon::TriUp; QIcon icon::TriLeft; QIcon icon::TriDown; @@ -110,6 +111,8 @@ void icon::LoadAll(const QString& theme) Audio = Create(theme, "audiosource"); Image = Create(theme, "imagesource"); + MiniMap = Create(theme, "map"); + TriUp = Create(theme, "tri-up"); TriLeft = Create(theme, "tri-left"); TriDown = Create(theme, "tri-down"); diff --git a/app/ui/icons/icons.h b/app/ui/icons/icons.h index 88bbf207e..c8c1788a0 100644 --- a/app/ui/icons/icons.h +++ b/app/ui/icons/icons.h @@ -65,6 +65,9 @@ extern QIcon Video; extern QIcon Audio; extern QIcon Image; +// Node Icons +extern QIcon MiniMap; + // Triangle Arrows extern QIcon TriUp; extern QIcon TriLeft; diff --git a/app/widget/nodeview/nodeviewtoolbar.cpp b/app/widget/nodeview/nodeviewtoolbar.cpp index c63be73db..6ffaa6d35 100644 --- a/app/widget/nodeview/nodeviewtoolbar.cpp +++ b/app/widget/nodeview/nodeviewtoolbar.cpp @@ -43,13 +43,13 @@ void NodeViewToolBar::changeEvent(QEvent *e) void NodeViewToolBar::Retranslate() { add_node_btn_->setToolTip(tr("Add Node")); - minimap_btn_->setText(tr("Mini-Map")); minimap_btn_->setToolTip(tr("Toggle Mini-Map")); } void NodeViewToolBar::UpdateIcons() { add_node_btn_->setIcon(icon::Add); + minimap_btn_->setIcon(icon::MiniMap); } } From 2c2dd440aba2b2f2b968af4980dc3e116376e825 Mon Sep 17 00:00:00 2001 From: Alexandre Prokoudine Date: Tue, 3 Aug 2021 02:35:03 +0300 Subject: [PATCH 03/19] Update Russian translation --- app/ts/ru_RU.ts | 859 ++++++++++++++++++++++++++++-------------------- 1 file changed, 501 insertions(+), 358 deletions(-) diff --git a/app/ts/ru_RU.ts b/app/ts/ru_RU.ts index a6a2e6b54..2e6123ff7 100644 --- a/app/ts/ru_RU.ts +++ b/app/ts/ru_RU.ts @@ -4,37 +4,37 @@ AudioParams - + %1 Hz %1 Гц - + Mono Моно - + Stereo Стерео - + 2.1 2.1 - + 5.1 5.1 - + 7.1 7.1 - + Unknown (0x%1) Неизвестно (0x%1) @@ -42,24 +42,24 @@ Config - + Error loading settings Ошибка при загрузке настроек - + Failed to load application settings. This session will use defaults. %1 - + Error saving settings Ошибка при сохранении настроек - + Failed to save application settings. The application may lack write permissions for this location. @@ -95,12 +95,12 @@ NodeCopyPasteWidget - + Error pasting nodes Ошибка при вставке нод - + Failed to paste nodes: %1 Не удалось вставить ноды: %1 @@ -209,7 +209,7 @@ NodeViewItem - + %1... %1... @@ -420,19 +420,39 @@ olive::AboutDialog - + + Welcome to %1 + Приветствуем в %1 + + + About %1 О программе %1 - + Olive is a free open source non-linear video editor. This software is licensed under the GNU GPL Version 3. Olive — свободный нелинейный видеоредактор. Программа распространяется на условиях GNU GPL v3. - - <html>Olive wouldn't be possible without the support of gracious donations from <a href='https://www.patreon.com/olivevideoeditor'>Patreon</a></html>: - + + <b>Olive relies on support from the community to continue its development.</b> + <b>Разработка Olive возможна благодаря поддержке сообщества.</b> + + + + Olive wouldn't be possible without the support of gracious donations from the following people. + Проекту уже помогают: + + + + <html>%1 If you like this project, please consider making a <a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or <a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to support its development.</html> + <html>%1 Если вам нравится этот проект, будем признательны за <a href='https://olivevideoeditor.org/donate.php'>разовое</a> или <a href='https://www.patreon.com/olivevideoeditor'>ежемесячное</a> пожертвование для поддержки разработки.</html> + + + + Don't show this message again + Больше не показывать это сообщение @@ -480,27 +500,27 @@ olive::Block - + Length Длительность - + Media In Факт. начало - + Enabled Включено - + Speed Скорость - + Reverse Развернуть @@ -864,180 +884,182 @@ Не удалось создать последовательность - + Possible image sequence detected Обнаружена возможная последовательность изображений - + The file '%1' looks like it might be part of an image sequence. Would you like to import it as such? - + You must specify a project file to export Необходимо указать проектный файл, который экспортировать - + Specified project does not exist Указанный проект не существует - + Failed to open startup file Не удалось открыть стартовый файл - + The project "%1" doesn't exist. A new project will be started instead. Проект "%1" не существует. Вместо него будет создан новый проект. - - + + Missing OpenTimelineIO Libraries Отсутствуют библиотеки OpenTimelineIO - - + + This build was compiled without OpenTimelineIO and therefore cannot open OpenTimelineIO files. Это сборка подготовлена без OpenTimelineIO, поэтому вы не сможете открыть файлы OpenTimelineIO. - - + + Error Ошибка - + This Sequence is empty. There is nothing to export. Эта последовательность пустая. В ней нет данных для экспорта. - + No valid sequence detected. Make sure a sequence is loaded and it has a connected Viewer node. - - + + Auto-Recovery Error Ошибка при автовосстановлении - + Failed to save auto-recovery to "%1". Olive may not have permission to this directory. - + Olive Project Проект Olive - + OpenTimelineIO OpenTimelineIO - + The following projects had unsaved changes when Olive forcefully quit. Would you like to load them? - + В следующих проектах не были сохранены изменения, когда Olive упал. Хотите загрузить их? - + Found auto-recoveries but failed to load the auto-recovery index. Auto-recover projects will have to be opened manually. Your recoverable projects are still available at: %1 - + Найдены данные для автовосстановления, но не удалось загрузить индекс автовосстановления. Автоматически восстанавливаемые проекты придется открыть вручную. + +Восстанавливаемые проекты по-прежнему доступны здесь: %1 - + The following project versions have been auto-saved: Следующие версии проектов были автоматически сохранены: - + Save Project As Сохранить проект как - + Load Project Загрузка проекта - + Label Node Метка ноды - + Set node label Указать метку ноды - + Sequence %1 Последовательность %1 - + Cannot open recent project Не удалось открыть недавний проект - + The project "%1" doesn't exist. Would you like to remove this file from the recent list? Проект"%1" не существует. Хотите удалить этот файл из списка недавно открывавшихся? - + Unsaved Changes Несохраненные изменения - + The project '%1' has unsaved changes. Would you like to save them? В проекте '%1' остались несохраненные изменения. Сохранить их? - + Save Сохранить - + Save All Сохранить все - + Don't Save Не сохранять - + Don't Save All Не сохранять все - + Failed to cache sequence Не удалось закэшировать последовательность - + No active viewer found with this sequence. Для этой последовательности не найден активный монитор. - + Open Project Открыть проект @@ -1077,7 +1099,7 @@ Your recoverable projects are still available at: %1 Waiting for crash report to be generated... - + Создаётся отчёт о падении программы… @@ -1108,7 +1130,7 @@ Your recoverable projects are still available at: %1 Failed to find symbols necessary to send report. This is a packaging issue. Please notify the maintainers of this package. - + Не удалось найти отладочные символы, чтобы отправить отчёт. Это проблема сборки программы. Сообщите об этом автору сборки. @@ -1242,54 +1264,54 @@ Your recoverable projects are still available at: %1 olive::DiskCacheDialog - + Disk Cache: %1 Дисковый кэш: %1 - + Disk Cache Settings Параметры кэша на диске - + Maximum Disk Cache: Макс. кэш на диске: - + %1 GB %1 Гбайт - - - + + + Clear Disk Cache Очистить дисковый кэш - + Automatically clear disk cache on close Автоматически стирать при закрытии - + Are you sure you want to clear the disk cache in '%1'? Вы действительно хотите стереть дисковый кэш в '%1'? - + Disk Cache Cleared Дисковый кэш очищен - + Disk cache failed to fully clear. You may have to delete the cache files manually. - + Disk Cache Partially Cleared Дисковый кэш частично очищен @@ -1995,42 +2017,42 @@ Your recoverable projects are still available at: %1 olive::Footage - + Filename Имя файла - + Loop Mode - + None Нет - + Loop Петля - + Clamp - + %1: Image - %2x%3 - %1: Изображение - %2x%3 + %1: Изображение - %2x%3 - + %1: Video - %2x%3 - %1: Видео - %2x%3 + %1: Видео - %2x%3 - + %1: Audio - %n Channel(s), %2Hz %1: Звук - %n канал, %2Гц @@ -2039,32 +2061,32 @@ Your recoverable projects are still available at: %1 - + Video Видео - + Audio Звук - + Subtitle - Субтитры + Субтитры - + Unknown - Неизвестно + Неизвестно - + Filename: %1 Имя файла: %1 - + Invalid @@ -2076,7 +2098,7 @@ Your recoverable projects are still available at: %1 Import video, audio, or still image files into the composition. - + Вставить видео, звук или статическое изображение в проект. @@ -2120,7 +2142,7 @@ Your recoverable projects are still available at: %1 olive::FootageViewerPanel - + Footage Viewer Просмотр видеоматериала @@ -2243,7 +2265,7 @@ Your recoverable projects are still available at: %1 Frame to Export: - + Экспортируемый кадр: @@ -2272,27 +2294,27 @@ Your recoverable projects are still available at: %1 Свойства ключевого кадра - + In: Вход: - + Out: Выход: - + Linear Линейный - + Hold Константа - + Bezier Безье @@ -2323,17 +2345,17 @@ Your recoverable projects are still available at: %1 olive::LoadOTIOTask - + Failed to load OpenTimelineIO from file "%1" Не удалось загрузить OpenTimelineIO из файла "%1" - + Unknown OpenTimelineIO root element Неизвестный корневой элемент OpenTimelineIO - + Failed to load clip Не удалось загрузить клип @@ -2341,402 +2363,427 @@ Your recoverable projects are still available at: %1 olive::MainMenu - + &Save '%1' Со&хранить '%1' - + Save '%1' &As Сохранить '%1' к&ак - + Close '%1' Закрыть '%1' - + Close All Except '%1' Закрыть все кроме '%1' - + &Save Project &Сохранить проект - + Save Project &As Сохранить проект &как - + Close Project Закрыть проект - + Close All Except Current Project Закрыть все проекты кроме текущего - + (None) (нет) - + &File &Файл - + &New &Создать - + &Open Project &Открыть проект - + Open &Recent Открыть из &недавнего - + &Clear Recent List О&чистить список недавних - + Sa&ve All Projects Сохра&нить все проекты - + &Import... &Импортировать… - + &Export &Экспортировать - + &Media... &Медиаданные… - + Close All Projects Закрыть все проекты - + E&xit В&ыход - + &Edit &Правка - + Delete (alt) - + Удалить (alt) - + Insert Вставить - + Overwrite Переписать - + Select &All Выд&елить всё - + Deselect All Снять выделение - + Ripple to In Point Сдвиг до точки входа - + Ripple to Out Point Сдвиг до точки выхода - + Edit to In Point Правка до точки входа - + Edit to Out Point Правка до точки выхода - + + Nudge Left + Сдвинуть влево + + + + Nudge Right + Сдвинуть вправо + + + + Move In Point to Playhead + Точка входа к указателю воспроизведения + + + + Move Out Point to Playhead + Точка выхода к указателю воспроизведения + + + Delete In/Out Point Удалить точку входа/выхода - + Ripple Delete In/Out Point Удалить со сдвигом точку входа/выхода - + Set/Edit Marker Установить/Изменить маркер - + &View &Вид - + Zoom In Приблизить - + Zoom Out Отдалить - + Increase Track Height Увеличить высоту дорожки - + Decrease Track Height Уменьшить высоту дорожки - + Toggle Show All Показывать весь проект - + Full Screen Полноэкранный режим - + Full Screen Viewer Просмотр в полноэкранном режиме - + &Playback Вос&произведение - + Go to Start К началу - + Previous Frame К предыдущему кадру - + Play/Pause Воспроизведение/Пауза - + Play In to Out Проиграть от входа до выхода - + Next Frame К следующему кадру - + Go to End В конец - + Go to Previous Cut К предыдущему отрезу - + Go to Next Cut К следующему отрезу - + Go to In Point К точке входа - + Go to Out Point К точке выхода - + Shuttle Left Уменьшить скорость - + Shuttle Stop Пауза - + Shuttle Right Увеличить скорость - + Loop Петля - + &Sequence П&оследовательность - + Cache Entire Sequence Закэшировать всю последовательность - + Cache Sequence In/Out Закэшировать вход/выход последовательности - + + Clear Disk Cache + Очистить дисковый кэш + + + &Window &Окно - + Maximize Panel Развернуть панель - + Lock Panels Закрепить панели - + Reset to Default Layout Вернуть исходный вид панелей - + &Tools &Инструменты - + Pointer Tool Указатель - + Edit Tool Выделение - + Ripple Tool Монтаж со сдвигом - + Rolling Tool Монтаж с совмещением - + Razor Tool Подрезка - + Slip Tool Прокрутка с совмещением - + Slide Tool Прокрутка - + Hand Tool Навигация - + Zoom Tool Масштаб - + Transition Tool Переход - + Enable Snapping Включить прилипание - + Preferences Параметры - + &Help &Справка - + A&ction Search &Найти команду - + Send &Feedback... &Дать обратную связь… - + &About... &О программе… @@ -2761,12 +2808,12 @@ Your recoverable projects are still available at: %1 olive::MainWindow - + Driver Warning - Предупреждение драйвера + Предупреждение о драйвере - + Olive has detected your system is using the Nouveau graphics driver. This driver is known to have stability and performance issues with Olive. It is highly recommended you install the proprietary NVIDIA driver before continuing to use Olive. @@ -2924,127 +2971,132 @@ This driver is known to have stability and performance issues with Olive. It is olive::MenuShared - + &Project &Проект - + &Sequence П&оследовательность - + &Folder П&апка - + Cu&t В&ырезать - + Cop&y С&копировать - + &Paste &Вставить - + Paste Insert - + Duplicate Сделать копию - + Delete Удалить - + Ripple Delete Удалить со сдвигом - + Split Разделить - + + Speed/Duration + Скорость и длительность + + + Set In Point Установить точку входа - + Set Out Point Установить точку выхода - + Reset In Point Сбросить точку входа - + Reset Out Point Сбросить точку выхода - + Clear In/Out Point Очистить точки входа/выхода - + Add Default Transition Добавить переход по умолчанию - + Link/Unlink Связать/Убрать связь - + Enable/Disable Включить/Отключить - + Nest Вложить - + Frames Кадры - + Drop Frame С пропуском кадров - + Non-Drop Frame Без пропуска кадров - + Milliseconds Миллисекунды - + Seconds Секунды @@ -3103,67 +3155,67 @@ This driver is known to have stability and performance issues with Olive. It is olive::Node - + Input Вход - + Output Выход - + General Общие - + Distort Искажения - + Math Математика - + Color Цвет - + Filter Фильтр - + Timeline Монтажный стол - + Generator Генератор - + Channel Канал - + Transition Переход - + Project Проект - + Uncategorized Без категории @@ -3171,7 +3223,7 @@ This driver is known to have stability and performance issues with Olive. It is olive::NodePanel - + Node Editor Редактор нод @@ -3222,7 +3274,7 @@ This driver is known to have stability and performance issues with Olive. It is olive::NodeParamViewItem - + %1 (%2) %1 (%2) @@ -3230,8 +3282,13 @@ This driver is known to have stability and performance issues with Olive. It is olive::NodeParamViewItemBody - - + + ... + + + + + %1: %1: @@ -3249,6 +3306,19 @@ This driver is known to have stability and performance issues with Olive. It is + + olive::NodePropertiesDialog + + + Node Properties + + + + + Name: + Название: + + olive::NodeTablePanel @@ -3305,17 +3375,17 @@ This driver is known to have stability and performance issues with Olive. It is X - + X Y - + Y Z - + Z @@ -3326,71 +3396,84 @@ This driver is known to have stability and performance issues with Olive. It is olive::NodeView - + Label Метка - - Auto-Position - Автопозиционирование - - - + Open in Viewer - + Smooth Edges Плавные края - + Filter Фильтр - - Show All - Показывать все + + Show All Nodes + Показывать все ноды - - Show Selected Blocks Only - Показывать только выбранные блоки + + Show Selected + Показывать выбранное - + Direction Направление - + Top to Bottom Сверху вниз - + Bottom to Top Снизу вверх - + Left to Right Слева направо - + Right to Left Справа налево - + Add Добавить + + olive::NodeViewToolBar + + + Add Node + Добавить + + + + Mini-Map + Миникарта + + + + Toggle Mini-Map + Включить или выключить миникарту + + olive::PanNode @@ -3472,7 +3555,7 @@ This driver is known to have stability and performance issues with Olive. It is Pixel Sampler - + Пипетка @@ -3989,13 +4072,13 @@ This driver is known to have stability and performance issues with Olive. It is olive::Project - + Root - - + + (untitled) (без названия) @@ -4003,22 +4086,22 @@ This driver is known to have stability and performance issues with Olive. It is olive::ProjectExplorer - + &New &Создать - + &Import... &Импортировать… - + Confirm Item Deletion Подтвердите удаление объекта - + The item "%1" is currently connected to the following nodes: %2 @@ -4027,52 +4110,52 @@ Are you sure you wish to delete this footage? - + %1 (%2) %1 (%2) - + Open in New Tab Открыть в новой вкладке - + Open in New Window Открыть в новом окне - + Reveal in Explorer Открыть в Проводнике - + Reveal in Finder Открыть в Finder - + Reveal in File Manager Открыть в файловом менеджере - + Pre-Cache Предкэширование - + No sequences exist in project - + For "%1" - + P&roperties С&войства @@ -4150,17 +4233,17 @@ Are you sure you wish to delete this footage? olive::ProjectPanel - + Folder Папка - + Project Проект - + (none) (нет) @@ -4272,22 +4355,32 @@ Are you sure you wish to delete this footage? olive::ProjectViewModel - + Name Название - + Duration Длительность - + Rate Частота - + + Modified + Изменено + + + + Created + Создано + + + Move Items Переместить объекты @@ -4362,17 +4455,17 @@ Are you sure you wish to delete this footage? olive::Sequence - + Video Tracks Видеодорожки - + Audio Tracks Звуковые дорожки - + Subtitle Tracks Дорожки субтитров @@ -4420,12 +4513,12 @@ Are you sure you wish to delete this footage? Введите название этой последовательности - + Confirm Set As Default - + Are you sure you want to set the current parameters as defaults? @@ -4468,12 +4561,17 @@ Are you sure you wish to delete this footage? Качество: - + + Auto-Cache: + Автокэширование: + + + Save Preset Сохранить профиль - + (%1x%2) (%1x%2) @@ -4516,42 +4614,42 @@ Are you sure you wish to delete this footage? PAL - + %1 23.976 FPS %1 23,976 к/с - + %1 25 FPS %1 25 к/с - + %1 29.97 FPS %1 29,97 к/с - + %1 50 FPS %1 50 к/с - + %1 59.94 FPS %1 59,94 к/с - + %1 Standard - + %1 Widescreen - + Delete Preset Удалить профиль @@ -4569,12 +4667,12 @@ Are you sure you wish to delete this footage? --- - + --- Invalid Value - + Некорректное значение @@ -4601,7 +4699,7 @@ Are you sure you wish to delete this footage? Generate a solid color. - + Создать сплошную заливку. @@ -4609,6 +4707,39 @@ Are you sure you wish to delete this footage? Цвет + + olive::SpeedDurationDialog + + + Speed/Duration + Скорость и длительность + + + + Speed: + Скорость: + + + + Duration: + Длительность: + + + + Link Speed and Duration + Связать скорость и длительность + + + + Ripple Trailing Clips + + + + + Rippling is a stub and will not do anything. Do you wish to continue? + + + olive::StringSlider @@ -4731,7 +4862,7 @@ Are you sure you wish to delete this footage? Enable HTML - + Включить HTML @@ -4808,7 +4939,7 @@ Are you sure you wish to delete this footage? Time Remap - + Переназначить время @@ -4819,28 +4950,40 @@ Are you sure you wish to delete this footage? olive::TimelinePanel - + Timeline Монтажный стол + + olive::TimelineView + + + In: %1 +Out: %2 +Duration: %3 + Начало: %1 +Конец: %2 +Длительность: %3 + + olive::TimelineWidget - - + + Properties Свойства - + Use Audio Time Units - + Show Waveforms - + Показывать волновую форму @@ -4922,42 +5065,42 @@ Are you sure you wish to delete this footage? olive::Track - + Track Дорожка - + Node for representing and processing a single array of Blocks sorted by time. Also represents the end of a Sequence. - + Blocks Блоки - + Muted Приглушено - + Video %1 Видео %1 - + Audio %1 Звук %1 - + Subtitle %1 Субтитры %1 - + Track %1 Дорожка %1 @@ -5035,38 +5178,38 @@ Are you sure you wish to delete this footage? Transform an image in 2D space. Equivalent to multiplying by an orthographic matrix. - + Преобразует изображение в 2D-пространстве. Эквивалент умножения на прямоугольную матрицу. olive::TransitionBlock - + From От - + To До - + Curve Кривая - + Linear Линейный - + Exponential Экспоненциальный - + Logarithmic Логарифмический @@ -5220,12 +5363,12 @@ Are you sure you wish to delete this footage? Divider: - + Делитель: Stream Index: - + Индекс потока: @@ -5298,45 +5441,50 @@ Are you sure you wish to delete this footage? olive::ViewerOutput - + Viewer Монитор - + Interface between a Viewer panel and the node system. - + %1 FPS %1 к/с - + %1 Hz %1 Гц - + Video Parameters Параметры видео - + Audio Parameters Параметры звука - + Texture Текстура - + Samples Сэмплы + + + Auto-Cache + Автокэширование + olive::ViewerPanel @@ -5349,98 +5497,93 @@ Are you sure you wish to delete this footage? olive::ViewerWidget - + Error Ошибка - + No in or out points are set to cache. - - + + Safe Margins Безопасная область - + Zoom Масштаб - + Fit Уместить - + %1% %1% - + Full Screen Полноэкранный режим - + Screen %1: %2x%3 Экран %1: %2×%3 - + Deinterlace - + Scopes Анализаторы - + Cache Кэш - - Auto-Cache - Автокэширование - - - + Show FPS Показывать частоту кадров - + Cache Entire Sequence Закэшировать всю последовательность - + Cache Sequence In/Out Закэшировать вход/выход последовательности - + Off Выкл. - + On Вкл - + Custom Aspect Другое соотношение - + Show Audio Waveform Показывать волновую форму @@ -5456,7 +5599,7 @@ Are you sure you wish to delete this footage? Adjusts the volume of an audio source. - + Изменить громкость источника звука. From 9f4470f55cb799313b46cc0802535d627f8ac621 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 2 Aug 2021 19:57:19 -0700 Subject: [PATCH 04/19] fixed snapping regression introduced by earlier commit --- app/widget/timebased/timebasedview.cpp | 2 ++ app/widget/timeruler/seekablewidget.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/widget/timebased/timebasedview.cpp b/app/widget/timebased/timebasedview.cpp index 0156771ab..b9ae6b68f 100644 --- a/app/widget/timebased/timebasedview.cpp +++ b/app/widget/timebased/timebasedview.cpp @@ -217,6 +217,8 @@ bool TimeBasedView::PlayheadMove(QMouseEvent *event) rational movement; snap_service_->SnapPoint({mouse_time}, &movement, SnapService::kSnapAll & ~SnapService::kSnapToPlayhead); + + mouse_time += movement; } SetTime(mouse_time); diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index b410557d3..a5845c25c 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -151,6 +151,8 @@ void SeekableWidget::SeekToScreenPoint(int screen) snap_service_->SnapPoint({playhead_time}, &movement, SnapService::kSnapAll & ~SnapService::kSnapToPlayhead); + + playhead_time += movement; } if (playhead_time != GetTime()) { From 8954271e8740284cc04f03e828cd18414e0931ef Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 3 Aug 2021 10:08:09 -0700 Subject: [PATCH 05/19] nodeparamview: fix combobox set with separators --- .../nodeparamview/nodeparamviewwidgetbridge.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 78d54cc26..d6b939408 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -514,7 +514,17 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() { QComboBox* cb = static_cast(widgets_.first()); cb->blockSignals(true); - cb->setCurrentIndex(input_.GetValueAtTime(node_time).toInt()); + int index = input_.GetValueAtTime(node_time).toInt(); + int real_row = -1; + for (int i=0; icount(); i++) { + if (!cb->itemText(i).isEmpty()) { + real_row++; + } + + if (real_row == index) { + cb->setCurrentIndex(i); + } + } cb->blockSignals(false); break; } From 330a37da9202ec719818b57db5487f98caa0a3db Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 3 Aug 2021 11:55:31 -0700 Subject: [PATCH 06/19] nodes: implemented shape generator --- app/node/factory.cpp | 6 +- app/node/factory.h | 1 + app/node/generator/CMakeLists.txt | 1 + app/node/generator/shape/CMakeLists.txt | 24 ++++++ app/node/generator/shape/shapenode.cpp | 89 ++++++++++++++++++++++ app/node/generator/shape/shapenode.h | 61 +++++++++++++++ app/node/generator/shape/shapenodebase.cpp | 49 ++++++++++++ app/node/generator/shape/shapenodebase.h | 46 +++++++++++ app/node/node.h | 6 +- app/shaders/shape.frag | 39 ++++++++++ 10 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 app/node/generator/shape/CMakeLists.txt create mode 100644 app/node/generator/shape/shapenode.cpp create mode 100644 app/node/generator/shape/shapenode.h create mode 100644 app/node/generator/shape/shapenodebase.cpp create mode 100644 app/node/generator/shape/shapenodebase.h create mode 100644 app/shaders/shape.frag diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 9e18f0a99..9f6c7f335 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -33,6 +33,7 @@ #include "distort/transform/transformdistortnode.h" #include "generator/matrix/matrix.h" #include "generator/polygon/polygon.h" +#include "generator/shape/shapenode.h" #include "generator/solid/solid.h" #include "generator/text/text.h" #include "filter/blur/blur.h" @@ -239,13 +240,14 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id) return new TimeRemapNode(); case kSubtitleBlock: return new SubtitleBlock(); + case kShapeGenerator: + return new ShapeNode(); case kInternalNodeCount: break; } - // We should never get here - abort(); + return nullptr; } } diff --git a/app/node/factory.h b/app/node/factory.h index 8ba811cd3..a53a3d6ff 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -59,6 +59,7 @@ public: kValueNode, kTimeRemapNode, kSubtitleBlock, + kShapeGenerator, // Count value kInternalNodeCount diff --git a/app/node/generator/CMakeLists.txt b/app/node/generator/CMakeLists.txt index 8c5706e50..dfdabb071 100644 --- a/app/node/generator/CMakeLists.txt +++ b/app/node/generator/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(matrix) add_subdirectory(polygon) +add_subdirectory(shape) add_subdirectory(solid) add_subdirectory(text) diff --git a/app/node/generator/shape/CMakeLists.txt b/app/node/generator/shape/CMakeLists.txt new file mode 100644 index 000000000..b3d0fed04 --- /dev/null +++ b/app/node/generator/shape/CMakeLists.txt @@ -0,0 +1,24 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2021 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/generator/shape/shapenode.cpp + node/generator/shape/shapenode.h + node/generator/shape/shapenodebase.cpp + node/generator/shape/shapenodebase.h + PARENT_SCOPE +) diff --git a/app/node/generator/shape/shapenode.cpp b/app/node/generator/shape/shapenode.cpp new file mode 100644 index 000000000..0c8f258a4 --- /dev/null +++ b/app/node/generator/shape/shapenode.cpp @@ -0,0 +1,89 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "shapenode.h" + +namespace olive { + +#define super ShapeNodeBase + +QString ShapeNode::kTypeInput = QStringLiteral("type_in"); + +ShapeNode::ShapeNode() +{ + PrependInput(kTypeInput, NodeValue::kCombo); +} + +QString ShapeNode::Name() const +{ + return tr("Shape"); +} + +QString ShapeNode::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.shape"); +} + +QVector ShapeNode::Category() const +{ + return {kCategoryGenerator}; +} + +QString ShapeNode::Description() const +{ + return tr("Generate a 2D primitive shape."); +} + +void ShapeNode::Retranslate() +{ + super::Retranslate(); + + SetInputName(kTypeInput, tr("Type")); + + // Coordinate with Type enum + SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse")}); +} + +ShaderCode ShapeNode::GetShaderCode(const QString &shader_id) const +{ + Q_UNUSED(shader_id) + + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag"))); +} + +NodeValueTable ShapeNode::Value(const QString &output, NodeValueDatabase &value) const +{ + Q_UNUSED(output) + + ShaderJob job; + + job.InsertValue(this, kTypeInput, value); + job.InsertValue(this, kPositionInput, value); + job.InsertValue(this, kSizeInput, value); + job.InsertValue(this, kColorInput, value); + job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution"))); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); + + NodeValueTable table = value.Merge(); + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); + return table; +} + +} diff --git a/app/node/generator/shape/shapenode.h b/app/node/generator/shape/shapenode.h new file mode 100644 index 000000000..0381be00d --- /dev/null +++ b/app/node/generator/shape/shapenode.h @@ -0,0 +1,61 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SHAPENODE_H +#define SHAPENODE_H + +#include "shapenodebase.h" + +namespace olive { + +class ShapeNode : public ShapeNodeBase +{ + Q_OBJECT +public: + ShapeNode(); + + enum Type { + kRectangle, + kEllipse + }; + + NODE_DEFAULT_DESTRUCTOR(ShapeNode) + NODE_COPY_FUNCTION(ShapeNode) + + virtual QString Name() const override; + virtual QString id() const override; + virtual QVector Category() const override; + virtual QString Description() const override; + + virtual void Retranslate() override; + + virtual ShaderCode GetShaderCode(const QString& shader_id) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; + + static QString kTypeInput; + +private: + + +}; + +} + +#endif // SHAPENODE_H diff --git a/app/node/generator/shape/shapenodebase.cpp b/app/node/generator/shape/shapenodebase.cpp new file mode 100644 index 000000000..e8e77db77 --- /dev/null +++ b/app/node/generator/shape/shapenodebase.cpp @@ -0,0 +1,49 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "shapenodebase.h" + +#include + +namespace olive { + +#define super Node + +QString ShapeNodeBase::kPositionInput = QStringLiteral("pos_in"); +QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in"); +QString ShapeNodeBase::kColorInput = QStringLiteral("color_in"); + +ShapeNodeBase::ShapeNodeBase() +{ + AddInput(kPositionInput, NodeValue::kVec2, QVector2D(10, 10)); + AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100)); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 0.0, 0.0, 1.0))); +} + +void ShapeNodeBase::Retranslate() +{ + super::Retranslate(); + + SetInputName(kPositionInput, tr("Position")); + SetInputName(kSizeInput, tr("Size")); + SetInputName(kColorInput, tr("Color")); +} + +} diff --git a/app/node/generator/shape/shapenodebase.h b/app/node/generator/shape/shapenodebase.h new file mode 100644 index 000000000..368b3f20a --- /dev/null +++ b/app/node/generator/shape/shapenodebase.h @@ -0,0 +1,46 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SHAPENODEBASE_H +#define SHAPENODEBASE_H + +#include "node/node.h" + +namespace olive { + +class ShapeNodeBase : public Node +{ + Q_OBJECT +public: + ShapeNodeBase(); + + NODE_DEFAULT_DESTRUCTOR(ShapeNodeBase) + + virtual void Retranslate() override; + + static QString kPositionInput; + static QString kSizeInput; + static QString kColorInput; + +}; + +} + +#endif // SHAPENODEBASE_H diff --git a/app/node/node.h b/app/node/node.h index 22c02b528..dde2e706f 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -46,7 +46,11 @@ namespace olive { -#define NODE_DEFAULT_DESTRUCTOR(x) virtual ~x() override {DisconnectAll();} +#define NODE_DEFAULT_DESTRUCTOR(x) \ + virtual ~x() override {DisconnectAll();} + +#define NODE_COPY_FUNCTION(x) \ + virtual Node *copy() const override {return new x();} class NodeGraph; class Folder; diff --git a/app/shaders/shape.frag b/app/shaders/shape.frag new file mode 100644 index 000000000..2c379b586 --- /dev/null +++ b/app/shaders/shape.frag @@ -0,0 +1,39 @@ +// Input texture coordinate +varying vec2 ove_texcoord; + +// Match with ShapeNode::Type +#define SHAPE_RECTANGLE 0 +#define SHAPE_ELLIPSE 1 + +uniform vec2 pos_in; +uniform vec2 size_in; +uniform int type_in; +uniform vec2 resolution_in; +uniform vec4 color_in; + +void main() { + vec2 real_position = pos_in/resolution_in; + vec2 real_size = size_in/resolution_in; + + vec4 col = vec4(0.0); + + if (type_in == SHAPE_RECTANGLE) { + if (ove_texcoord.x >= real_position.x && ove_texcoord.y >= real_position.y + && ove_texcoord.x < real_position.x+real_size.x && ove_texcoord.y < real_position.y+real_size.y) { + col = color_in; + } + } else if (type_in == SHAPE_ELLIPSE) { + vec2 center = pos_in+size_in*0.5; + float radius = size_in.y*0.5; + float aspect_ratio = size_in.x/size_in.y; + + vec2 offset = ove_texcoord*resolution_in - center; + offset.x /= aspect_ratio; + float d = length(offset)-radius; + float t = clamp(d, 0.0, 1.0); + + col = color_in * (1.0-t); + } + + gl_FragColor = col; +} From bdbeb111e81c0ecf35997f6588dc0898af744944 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 7 Aug 2021 20:37:40 -0700 Subject: [PATCH 07/19] ui: make color setting undoable --- app/widget/nodeview/nodeview.cpp | 6 +++++- app/widget/nodeview/nodeviewundo.cpp | 22 ++++++++++++++++++++ app/widget/nodeview/nodeviewundo.h | 21 +++++++++++++++++++ app/widget/timelinewidget/timelinewidget.cpp | 6 +++++- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index e804391e1..da7f65da5 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -363,9 +363,13 @@ void NodeView::Duplicate() void NodeView::SetColorLabel(int index) { + MultiUndoCommand *command = new MultiUndoCommand(); + for (Node* node : qAsConst(selected_nodes_)) { - node->SetOverrideColor(index); + command->add_child(new NodeOverrideColorCommand(node, index)); } + + Core::instance()->undo_stack()->push(command); } void NodeView::ZoomIn() diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index 0197dcf94..3b43a7b9a 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -171,4 +171,26 @@ Project *NodeRenameCommand::GetRelevantProject() const return nodes_.isEmpty() ? nullptr : nodes_.first()->project(); } +NodeOverrideColorCommand::NodeOverrideColorCommand(Node *node, int index) : + node_(node), + new_index_(index) +{ +} + +Project *NodeOverrideColorCommand::GetRelevantProject() const +{ + return node_->project(); +} + +void NodeOverrideColorCommand::redo() +{ + old_index_ = node_->GetOverrideColor(); + node_->SetOverrideColor(new_index_); +} + +void NodeOverrideColorCommand::undo() +{ + node_->SetOverrideColor(old_index_); +} + } diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index fcf23ae92..f678007b1 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -342,6 +342,27 @@ private: }; +class NodeOverrideColorCommand : public UndoCommand +{ +public: + NodeOverrideColorCommand(Node *node, int index); + + virtual Project * GetRelevantProject() const override; + +protected: + virtual void redo() override; + + virtual void undo() override; + +private: + Node *node_; + + int old_index_; + + int new_index_; + +}; + } #endif // NODEVIEWUNDO_H diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 92df030d0..0a921eb9d 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -720,9 +720,13 @@ void TimelineWidget::ToggleSelectedEnabled() void TimelineWidget::SetColorLabel(int index) { + MultiUndoCommand *command = new MultiUndoCommand(); + foreach (Block* b, selected_blocks_) { - b->SetOverrideColor(index); + command->add_child(new NodeOverrideColorCommand(b, index)); } + + Core::instance()->undo_stack()->push(command); } void TimelineWidget::NudgeLeft() From b5ad35bde2df9fe1d71a2b31a3cee040dd2fe982 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 10:40:55 -0700 Subject: [PATCH 08/19] ffmpegencoder: fix subtitle timestamps --- app/codec/ffmpeg/ffmpegencoder.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index a2926f15d..a1dc95843 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -396,7 +396,7 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block) subtitle.num_rects = 1; subtitle.rects = &rect_array; - subtitle.pts = Timecode::time_to_timestamp(sub_block->in(), av_get_time_base_q(), Timecode::kFloor); + subtitle.pts = Timecode::time_to_timestamp(sub_block->in(), subtitle_codec_ctx_->time_base, Timecode::kFloor); subtitle.end_display_time = qRound64(sub_block->length().toDouble() * 1000); QVector out_buf(1024 * 1024); @@ -412,9 +412,9 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block) pkt->data = out_buf.data(); pkt->size = sub_sz; pkt->pts = subtitle.pts; - pkt->duration = subtitle.end_display_time; + pkt->duration = av_rescale_q(subtitle.end_display_time, {1, 1000}, subtitle_codec_ctx_->time_base); pkt->dts = pkt->pts; - av_packet_rescale_ts(pkt, av_get_time_base_q(), subtitle_stream_->time_base); + av_packet_rescale_ts(pkt, subtitle_codec_ctx_->time_base, subtitle_stream_->time_base); int err = av_interleaved_write_frame(fmt_ctx_, pkt); bool ret = true; @@ -694,6 +694,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV codec_ctx->height = params().video_params().height(); codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational(); codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational(); + codec_ctx->framerate = params().video_params().frame_rate().toAVRational(); codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8()); if (params().video_params().interlacing() != VideoParams::kInterlaceNone) { From a0bd9b5d8ced883d4da9297a2d20a08b9f634f9b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 11:07:57 -0700 Subject: [PATCH 09/19] ffmpegencoder: rescale audio frames just in case --- app/codec/ffmpeg/ffmpegencoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index a1dc95843..5a3836515 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -322,7 +322,7 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio) if (audio_frame_offset_ == audio_max_samples_ || (i == converted && !input_data)) { // Got all the samples we needed, write the frame - audio_frame_->pts = audio_write_count_; + audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base); WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_); audio_write_count_ += audio_frame_offset_; @@ -336,7 +336,7 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio) if (!input_data && audio_frame_offset_ > 0) { audio_frame_->nb_samples = audio_frame_offset_; - audio_frame_->pts = audio_write_count_; + audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base); WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_); } From 5c5f6bd0c377784abc2ddf3425d6bd16bf583823 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 11:08:16 -0700 Subject: [PATCH 10/19] ffmpegencoder: set gop size --- app/codec/ffmpeg/ffmpegencoder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 5a3836515..838d4c747 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -696,6 +696,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational(); codec_ctx->framerate = params().video_params().frame_rate().toAVRational(); codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8()); + codec_ctx->gop_size = 12; if (params().video_params().interlacing() != VideoParams::kInterlaceNone) { // FIXME: I actually don't know what these flags do, the documentation helpfully doesn't From e4bf8a92f31c7ccb70e1025f4fcad85a4c7f097f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 12:14:11 -0700 Subject: [PATCH 11/19] ffmpegencoder: set avg_frame_rate --- app/codec/ffmpeg/ffmpegencoder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 838d4c747..2fab82435 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -696,7 +696,6 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational(); codec_ctx->framerate = params().video_params().frame_rate().toAVRational(); codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8()); - codec_ctx->gop_size = 12; if (params().video_params().interlacing() != VideoParams::kInterlaceNone) { // FIXME: I actually don't know what these flags do, the documentation helpfully doesn't @@ -820,6 +819,10 @@ bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ct return false; } + if (codec->type == AVMEDIA_TYPE_VIDEO) { + stream->avg_frame_rate = codec_ctx->framerate; + } + return true; } From d713ba3be779546d8a34a6815a8a024293c2632b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 17:05:41 -0700 Subject: [PATCH 12/19] fixed 32-bit integer overflow on long audio --- app/codec/CMakeLists.txt | 4 - app/codec/conformmanager.cpp | 18 +-- app/codec/decoder.cpp | 25 ++- app/codec/decoder.h | 3 +- app/codec/ffmpeg/ffmpegdecoder.cpp | 5 +- app/codec/ffmpeg/ffmpegdecoder.h | 1 - app/codec/waveinput.cpp | 244 ----------------------------- app/codec/waveinput.h | 75 --------- app/codec/waveoutput.cpp | 180 --------------------- app/codec/waveoutput.h | 68 -------- 10 files changed, 20 insertions(+), 603 deletions(-) delete mode 100644 app/codec/waveinput.cpp delete mode 100644 app/codec/waveinput.h delete mode 100644 app/codec/waveoutput.cpp delete mode 100644 app/codec/waveoutput.h diff --git a/app/codec/CMakeLists.txt b/app/codec/CMakeLists.txt index 94d6b4062..42f456059 100644 --- a/app/codec/CMakeLists.txt +++ b/app/codec/CMakeLists.txt @@ -33,9 +33,5 @@ set(OLIVE_SOURCES codec/frame.h codec/samplebuffer.cpp codec/samplebuffer.h - codec/waveinput.cpp - codec/waveinput.h - codec/waveoutput.cpp - codec/waveoutput.h PARENT_SCOPE ) diff --git a/app/codec/conformmanager.cpp b/app/codec/conformmanager.cpp index 38b5874a5..340f1e763 100644 --- a/app/codec/conformmanager.cpp +++ b/app/codec/conformmanager.cpp @@ -57,19 +57,13 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i QString ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms) { - QString index_fn = QStringLiteral("%1.%2:%3").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()), - QString::number(stream.stream())); + QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()), + QString::number(stream.stream()), + QString::number(params.sample_rate()), + QString::number(params.format()), + QString::number(params.channel_layout())); - index_fn = QDir(cache_path).filePath(index_fn); - - index_fn.append('.'); - index_fn.append(QString::number(params.sample_rate())); - index_fn.append('.'); - index_fn.append(QString::number(params.format())); - index_fn.append('.'); - index_fn.append(QString::number(params.channel_layout())); - - return index_fn; + return QDir(cache_path).filePath(index_fn); } void ConformManager::ConformTaskFinished(Task *task, bool succeeded) diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index 5647d1519..cac7d553f 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -25,8 +25,6 @@ #include "codec/ffmpeg/ffmpegdecoder.h" #include "codec/oiio/oiiodecoder.h" -#include "codec/waveinput.h" -#include "codec/waveoutput.h" #include "common/ffmpegutils.h" #include "common/filefunctions.h" #include "common/timecodefunctions.h" @@ -129,7 +127,7 @@ Decoder::RetrieveAudioData Decoder::RetrieveAudio(const TimeRange &range, const } // See if we got the conform - SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode); + SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode, params); return {kOK, out_buffer, nullptr}; } @@ -265,13 +263,11 @@ bool Decoder::ConformAudioInternal(const QString& filename, const AudioParams &p return false; } -SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode) +SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params) { - WaveInput input(conform_filename); - - if (input.open()) { - const AudioParams& input_params = input.params(); + QFile input(conform_filename); + if (input.open(QFile::ReadOnly)) { QByteArray packed_data(input_params.time_to_bytes(range.length()), Qt::Uninitialized); qint64 read_index = input_params.time_to_bytes(range.in()); @@ -279,12 +275,12 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam while (write_index < packed_data.size()) { if (loop_mode == Footage::kLoopModeLoop) { - while (read_index >= input.data_length()) { - read_index -= input.data_length(); + while (read_index >= input.size()) { + read_index -= input.size(); } while (read_index < 0) { - read_index += input.data_length(); + read_index += input.size(); } } @@ -294,13 +290,14 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam // Reading before 0, write silence here until audio data would actually start write_count = qMin(-read_index, qint64(packed_data.size())); memset(packed_data.data() + write_index, 0, write_count); - } else if (read_index >= input.data_length()) { + } else if (read_index >= input.size()) { // Reading after data length, write silence until the end of the buffer write_count = packed_data.size() - write_index; memset(packed_data.data() + write_index, 0, write_count); } else { - write_count = qMin(input.data_length() - read_index, packed_data.size() - write_index); - input.read(read_index, packed_data.data() + write_index, write_count); + write_count = qMin(input.size() - read_index, packed_data.size() - write_index); + input.seek(read_index); + input.read(packed_data.data() + write_index, write_count); } read_index += write_count; diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 5a7382444..3b9a64ac9 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -33,7 +33,6 @@ extern "C" { #include "codec/frame.h" #include "codec/samplebuffer.h" -#include "codec/waveoutput.h" #include "common/rational.h" #include "node/project/footage/footage.h" #include "node/project/footage/footagedescription.h" @@ -313,7 +312,7 @@ signals: private: void UpdateLastAccessed(); - SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode); + SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms); CodecStream stream_; diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 7b8659603..ccee600aa 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -38,7 +38,6 @@ extern "C" { #include #include -#include "codec/waveinput.h" #include "common/define.h" #include "common/ffmpegutils.h" #include "common/filefunctions.h" @@ -465,7 +464,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar swr_init(resampler); - WaveOutput wave_out(filename, params); + QFile wave_out(filename); AVPacket* pkt = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); @@ -473,7 +472,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar bool success = false; - if (wave_out.open()) { + if (wave_out.open(QFile::WriteOnly)) { while (true) { // Check if we have a `cancelled` ptr and its value if (cancelled && *cancelled) { diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 62572a904..1239cc421 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -37,7 +37,6 @@ extern "C" { #include #include "codec/decoder.h" -#include "codec/waveoutput.h" #include "ffmpegframepool.h" namespace olive { diff --git a/app/codec/waveinput.cpp b/app/codec/waveinput.cpp deleted file mode 100644 index b7342a88c..000000000 --- a/app/codec/waveinput.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "waveinput.h" - -extern "C" { -#include -} - -#include -#include - -namespace olive { - -WaveInput::WaveInput(const QString &f) : - file_(f) -{ -} - -WaveInput::~WaveInput() -{ - close(); -} - -bool WaveInput::open() -{ - if (!file_.open(QFile::ReadOnly)) { - return false; - } - - if (file_.read(4) != "RIFF") { - close(); - qCritical() << "No RIFF found"; - return false; - } - - // Skip filesize bytes - file_.seek(file_.pos() + 4); - - if (file_.read(4) != "WAVE") { - close(); - qCritical() << "No WAVE found"; - return false; - } - - // Find fmt_ section - if (!find_str(&file_, "fmt ")) { - close(); - qCritical() << "No fmt found"; - return false; - } - - // Skip fmt_ section size - file_.seek(file_.pos()+4); - - // Create data stream for reading bytes into types - QDataStream data_stream(&file_); - data_stream.setByteOrder(QDataStream::LittleEndian); - - // Read data type - uint16_t data_type; - data_stream >> data_type; - - bool data_is_float; - switch (data_type) { - case 1: // PCM Integer - data_is_float = false; - break; - case 3: - data_is_float = true; - break; - default: - // If it's neither float nor int, we can't work with this file - close(); - qCritical() << "Invalid WAV type" << data_type; - return false; - } - - // Read number of channels - uint16_t channel_count; - data_stream >> channel_count; - - uint64_t channel_layout = static_cast(av_get_default_channel_layout(channel_count)); - - int32_t sample_rate; - data_stream >> sample_rate; - - // Skip bytes per second value and bytes per sample value - file_.seek(file_.pos() + 6); - - uint16_t bits_per_sample; - data_stream >> bits_per_sample; - - AudioParams::Format format; - - switch (bits_per_sample) { - case 8: - format = AudioParams::kFormatUnsigned8; - break; - case 16: - format = AudioParams::kFormatSigned16; - break; - case 32: - if (data_is_float) { - format = AudioParams::kFormatFloat32; - } else { - format = AudioParams::kFormatSigned32; - } - break; - case 64: - if (data_is_float) { - format = AudioParams::kFormatFloat64; - } else { - format = AudioParams::kFormatSigned64; - } - break; - default: - // We don't know this format... - close(); - qCritical() << "Invalid format found" << bits_per_sample; - return false; - } - - // We're good to go! - params_ = AudioParams(sample_rate, channel_layout, format); - - if (!find_str(&file_, "data")) { - close(); - qCritical() << "No data tag found"; - return false; - } - - data_stream >> data_size_; - data_position_ = file_.pos(); - - return true; -} - -bool WaveInput::is_open() const -{ - return file_.isOpen(); -} - -QByteArray WaveInput::read(qint64 length) -{ - if (!is_open()) { - return QByteArray(); - } - - return file_.read(qMin(calculate_max_read(), length)); -} - -QByteArray WaveInput::read(qint64 offset, qint64 length) -{ - if (!is_open()) { - return QByteArray(); - } - - seek(offset); - return file_.read(qMin(calculate_max_read(), length)); -} - -qint64 WaveInput::read(qint64 offset, char *buffer, qint64 length) -{ - if (!is_open()) { - return 0; - } - - Q_ASSERT(length > 0); - - seek(offset); - return file_.read(buffer, qMin(calculate_max_read(), length)); -} - -bool WaveInput::seek(qint64 pos) -{ - return file_.seek(data_position_ + qMin(pos, qint64(data_size_))); -} - -bool WaveInput::at_end() const -{ - return file_.pos() == (data_position_ + data_size_); -} - -const AudioParams &WaveInput::params() const -{ - return params_; -} - -void WaveInput::close() -{ - if (file_.isOpen()) { - file_.close(); - } -} - -const quint32 &WaveInput::data_length() const -{ - return data_size_; -} - -int WaveInput::sample_count() const -{ - return params_.bytes_to_samples(data_size_); -} - -bool WaveInput::find_str(QFile *f, const char *str) -{ - qint64 pos = f->pos(); - while (f->read(4) != str) { - if (f->atEnd()) { - return false; - } - - pos++; - f->seek(pos); - } - - return true; -} - -qint64 WaveInput::calculate_max_read() const -{ - return data_size_ - (file_.pos() - data_position_ ); -} - -} diff --git a/app/codec/waveinput.h b/app/codec/waveinput.h deleted file mode 100644 index d8b863da1..000000000 --- a/app/codec/waveinput.h +++ /dev/null @@ -1,75 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef WAVEINPUT_H -#define WAVEINPUT_H - -#include - -#include "render/audioparams.h" - -namespace olive { - -class WaveInput -{ -public: - WaveInput(const QString& f); - - ~WaveInput(); - - DISABLE_COPY_MOVE(WaveInput) - - bool open(); - - bool is_open() const; - - QByteArray read(qint64 length); - QByteArray read(qint64 offset, qint64 length); - qint64 read(qint64 offset, char *buffer, qint64 length); - - bool seek(qint64 pos); - - bool at_end() const; - - const AudioParams& params() const; - - void close(); - - const quint32& data_length() const; - - int sample_count() const; - -private: - bool find_str(QFile* f, const char* str); - - qint64 calculate_max_read() const; - - AudioParams params_; - - QFile file_; - - qint64 data_position_; - - quint32 data_size_; -}; - -} - -#endif // WAVEINPUT_H diff --git a/app/codec/waveoutput.cpp b/app/codec/waveoutput.cpp deleted file mode 100644 index d5fd431c5..000000000 --- a/app/codec/waveoutput.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "waveoutput.h" - -#include "render/audioparams.h" - -namespace olive { - -const int16_t kWAVIntegerFormat = 1; -const int16_t kWAVFloatFormat = 3; - -WaveOutput::WaveOutput(const QString &f, - const AudioParams& params) : - file_(f), - params_(params) -{ - Q_ASSERT(params_.is_valid()); -} - -WaveOutput::~WaveOutput() -{ - close(); -} - -bool WaveOutput::open() -{ - data_length_ = 0; - - if (file_.open(QFile::WriteOnly)) { - // RIFF header - file_.write("RIFF"); - - // Total file size minus RIFF and this integer (minus 8 bytes, filled in later) - write_int(&file_, 0); - - // File type header - file_.write("WAVE"); - - // Begin format descriptor chunk - file_.write("fmt "); - - // Format chunk size - write_int(&file_, 16); - - // Type of format - switch (params_.format()) { - case AudioParams::kFormatUnsigned8: - case AudioParams::kFormatSigned16: - case AudioParams::kFormatSigned32: - case AudioParams::kFormatSigned64: - write_int(&file_, kWAVIntegerFormat); - break; - case AudioParams::kFormatFloat32: - case AudioParams::kFormatFloat64: - write_int(&file_, kWAVFloatFormat); - break; - case AudioParams::kFormatInvalid: - case AudioParams::kFormatCount: - qWarning() << "Invalid sample format for WAVE audio"; - file_.close(); - return false; - } - - // Number of channels - write_int(&file_, static_cast(params_.channel_count())); - - // Sample rate - write_int(&file_, params_.sample_rate()); - - // Bytes per second - write_int(&file_, params_.samples_to_bytes(params_.sample_rate())); - - // Bytes per sample - write_int(&file_, static_cast(params_.samples_to_bytes(1))); - - // Bits per sample per channel - write_int(&file_, static_cast(params_.bits_per_sample())); - - // Data chunk header - file_.write("data"); - - // Size of data chunk (filled in later) - write_int(&file_, 0); - - return true; - } - - return false; -} - -void WaveOutput::write(const QByteArray &bytes) -{ - if (file_.isOpen()) { - file_.write(bytes); - - data_length_ += bytes.size(); - } -} - -void WaveOutput::write(const char *bytes, int length) -{ - if (file_.isOpen()) { - file_.write(bytes, length); - - data_length_ += length; - } -} - -void WaveOutput::close() -{ - if (file_.isOpen()) { - - // Write file sizes - file_.seek(4); - write_int(&file_, data_length_ + 36); - - file_.seek(40); - write_int(&file_, data_length_); - - file_.close(); - } -} - -const int& WaveOutput::data_length() const -{ - return data_length_; -} - -const AudioParams &WaveOutput::params() const -{ - return params_; -} - -void WaveOutput::switch_endianness(QByteArray& array) -{ - int half_sz = array.size()/2; - - for (int i=0;i -void WaveOutput::write_int(QFile *file, T integer) -{ - QByteArray bytes; - bytes.resize(sizeof(T)); - memcpy(bytes.data(), &integer, static_cast(bytes.size())); - - // WAV expects little-endian, so if the integer is big endian we need to switch - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - switch_endianness(bytes); - } - - file->write(bytes); -} - -} diff --git a/app/codec/waveoutput.h b/app/codec/waveoutput.h deleted file mode 100644 index 1626a207c..000000000 --- a/app/codec/waveoutput.h +++ /dev/null @@ -1,68 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef WAVEAUDIO_H -#define WAVEAUDIO_H - -#include -#include - -#include "render/audioparams.h" - -namespace olive { - -class WaveOutput -{ -public: - WaveOutput(const QString& f, - const AudioParams& params); - - ~WaveOutput(); - - DISABLE_COPY_MOVE(WaveOutput) - - bool open(); - - void write(const QByteArray& bytes); - void write(const char* bytes, int length); - - void close(); - - const int& data_length() const; - - const AudioParams& params() const; - -private: - template - void write_int(QFile* file, T integer); - - void switch_endianness(QByteArray &array); - - QFile file_; - - AudioParams params_; - - int data_length_; - -}; - -} - -#endif // WAVEAUDIO_H From e36c001e745729428d3ca21b99453707d79881ef Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 17:30:07 -0700 Subject: [PATCH 13/19] audio: prevent playback of any audio after length Fixes #1705 --- app/render/audioplaybackcache.cpp | 32 ++++++++++++++++++++++--------- app/render/audioplaybackcache.h | 7 +++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/render/audioplaybackcache.cpp b/app/render/audioplaybackcache.cpp index ed507581b..93b3c592a 100644 --- a/app/render/audioplaybackcache.cpp +++ b/app/render/audioplaybackcache.cpp @@ -26,6 +26,7 @@ #include #include "common/filefunctions.h" +#include "node/output/viewer/viewer.h" namespace olive { @@ -390,7 +391,14 @@ void AudioPlaybackCache::UpdateOffsetsFrom(int index) AudioPlaybackCache::PlaybackDevice *AudioPlaybackCache::CreatePlaybackDevice(QObject* parent) const { - return new PlaybackDevice(playlist_, params_.bytes_per_sample_per_channel(), parent); + PlaybackDevice *d = new PlaybackDevice(playlist_, params_.bytes_per_sample_per_channel(), parent); + + // If we're child of a viewer, set the data limit so audio doesn't play beyond the length + if (ViewerOutput *viewer = viewer_parent()) { + d->SetDataLimit(params_.time_to_bytes_per_channel(viewer->GetAudioLength())); + } + + return d; } AudioPlaybackCache::Segment::Segment(qint64 size) @@ -403,7 +411,8 @@ AudioPlaybackCache::PlaybackDevice::PlaybackDevice(const AudioPlaybackCache::Pla playlist_(playlist), current_segment_(0), segment_read_index_(0), - sample_size_(sample_sz) + sample_size_(sample_sz), + limit_(INT64_MAX) { } @@ -437,10 +446,15 @@ qint64 AudioPlaybackCache::PlaybackDevice::readData(char *data, qint64 maxSize) while (read_size < maxSize && current_segment_ >= 0 - && current_segment_ < playlist_.size()) { + && current_segment_ < playlist_.size() + && playlist_.at(current_segment_).offset() + segment_read_index_ < limit_) { const Segment& cs = playlist_.at(current_segment_); qint64 current_segment_sz = cs.size(); + if (cs.offset() + current_segment_sz > limit_) { + current_segment_sz = limit_ - cs.offset(); + } + QVector segment_files(cs.channels()); segment_files.fill(nullptr); @@ -480,13 +494,13 @@ qint64 AudioPlaybackCache::PlaybackDevice::readData(char *data, qint64 maxSize) // Add to the read index segment_read_index_ += sample_size_; + } - // If we've reached the end of this segment, tick the counter over to the next segment - if (segment_read_index_ == current_segment_sz) { - // Jump to the next file - segment_read_index_ = 0; - current_segment_++; - } + // If we've reached the end of this segment, tick the counter over to the next segment + if (segment_read_index_ == current_segment_sz) { + // Jump to the next file + segment_read_index_ = 0; + current_segment_++; } } diff --git a/app/render/audioplaybackcache.h b/app/render/audioplaybackcache.h index 4e4bc76de..ee697808d 100644 --- a/app/render/audioplaybackcache.h +++ b/app/render/audioplaybackcache.h @@ -145,6 +145,11 @@ public: public: PlaybackDevice(const Playlist& playlist, int sample_sz, QObject* parent = nullptr); + void SetDataLimit(qint64 limit) + { + limit_ = limit; + } + virtual ~PlaybackDevice() override; virtual bool isSequential() const override @@ -178,6 +183,8 @@ public: int sample_size_; + qint64 limit_; + }; /** From 72d71ddf8cc42c60c8393b75e09991bb3e57c65e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 9 Aug 2021 11:28:01 -0700 Subject: [PATCH 14/19] mainmenu: added remaining tools --- app/window/mainwindow/mainmenu.cpp | 12 ++++++++++++ app/window/mainwindow/mainmenu.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index d158904dd..6b90a62f7 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -250,6 +250,16 @@ MainMenu::MainMenu(MainWindow *parent) : tools_transition_item_->setData(Tool::kTransition); tools_group_->addAction(tools_transition_item_); + tools_add_item_ = tools_menu_->AddItem("addtool", this, &MainMenu::ToolItemTriggered, "A"); + tools_add_item_->setCheckable(true); + tools_add_item_->setData(Tool::kAdd); + tools_group_->addAction(tools_add_item_); + + tools_record_item_ = tools_menu_->AddItem("recordtool", this, &MainMenu::ToolItemTriggered, "R"); + tools_record_item_->setCheckable(true); + tools_record_item_->setData(Tool::kRecord); + tools_group_->addAction(tools_record_item_); + tools_menu_->addSeparator(); tools_snapping_item_ = tools_menu_->AddItem("snapping", Core::instance(), &Core::SetSnapping, "S"); @@ -748,6 +758,8 @@ void MainMenu::Retranslate() tools_hand_item_->setText(tr("Hand Tool")); tools_zoom_item_->setText(tr("Zoom Tool")); tools_transition_item_->setText(tr("Transition Tool")); + tools_add_item_->setText(tr("Add Tool")); + tools_record_item_->setText(tr("Record Tool")); tools_snapping_item_->setText(tr("Enable Snapping")); tools_preferences_item_->setText(tr("Preferences")); diff --git a/app/window/mainwindow/mainmenu.h b/app/window/mainwindow/mainmenu.h index 4cedd3522..ceffb7ac2 100644 --- a/app/window/mainwindow/mainmenu.h +++ b/app/window/mainwindow/mainmenu.h @@ -279,6 +279,8 @@ private: QAction* tools_hand_item_; QAction* tools_zoom_item_; QAction* tools_transition_item_; + QAction* tools_add_item_; + QAction* tools_record_item_; QAction* tools_snapping_item_; QAction* tools_preferences_item_; From 420042bb5759fecf4f1db7e465ddf3d977633b8a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 9 Aug 2021 11:37:45 -0700 Subject: [PATCH 15/19] project: added revert function This can be triggered either with File > Revert or by re-opening an already open project Fixes #1710 --- app/core.cpp | 58 ++++++++++++++++++++++++++++-- app/core.h | 7 ++++ app/window/mainwindow/mainmenu.cpp | 3 ++ app/window/mainwindow/mainmenu.h | 1 + 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/app/core.cpp b/app/core.cpp index dc41884a8..290393867 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -833,6 +833,42 @@ void Core::SaveUnrecoveredList() } } +bool Core::RevertProjectInternal(Project *p, bool by_opening_existing) +{ + if (p->filename().isEmpty()) { + QMessageBox::critical(main_window_, tr("Revert"), + tr("This project has not yet been saved, therefore there is no last saved state to revert to.")); + } else { + QString msg; + + if (by_opening_existing) { + msg = tr("The project \"%1\" is already open. By re-opening it, the project will revert to " + "its last saved state. Any unsaved changes will be lost. Do you wish to continue?").arg(p->filename()); + } else { + msg = tr("This will revert the project \"%1\" back to its last saved state. " + "All unsaved changes will be lost. Do you wish to continue?").arg(p->name()); + } + + if (QMessageBox::question(main_window_, tr("Revert"), msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { + // Copy filename because CloseProject is going to delete `p` + QString filename = p->filename(); + + // Close project without prompting to save it + CloseProjectBehavior b = kCloseProjectDontSave; + CloseProject(p, false, b); + + // NOTE: `p` will be deleted now, so don't try accessing it + + // Re-open project at the filename + OpenProjectInternal(filename); + + return true; + } + } + + return false; +} + void Core::SaveAutorecovery() { if (Config::Current()[QStringLiteral("AutorecoveryEnabled")].toBool()) { @@ -1009,6 +1045,15 @@ bool Core::SaveAllProjects() return true; } +void Core::RevertActiveProject() +{ + Project *p = GetActiveProject(); + + if (p) { + RevertProjectInternal(p, false); + } +} + bool Core::CloseActiveProject() { return CloseProject(GetActiveProject(), true); @@ -1194,9 +1239,18 @@ void Core::OpenProjectInternal(const QString &filename, bool recovery_project) { // See if this project is open already foreach (Project* p, open_projects_) { - if (p->filename() == filename) { + // Comparing QFileInfos will handle case insensitivity and both slash directions on platforms + // where this is necessary (not naming any names *cough* Windows) + if (QFileInfo(p->filename()) == QFileInfo(filename)) { // This project is already open - AddOpenProject(p); + bool reverted = RevertProjectInternal(p, true); + + if (!reverted) { + // Calling this will focus attention to the project that the user just tried to re-open + AddOpenProject(p); + } + + // Don't do anything else return; } } diff --git a/app/core.h b/app/core.h index 67fb6a95d..a85a0d17e 100644 --- a/app/core.h +++ b/app/core.h @@ -329,6 +329,11 @@ public slots: */ bool SaveAllProjects(); + /** + * @brief Revert project to last saved state (basically close and open it) + */ + void RevertActiveProject(); + /** * @brief Closes the active project * @@ -506,6 +511,8 @@ private: void SaveUnrecoveredList(); + bool RevertProjectInternal(Project *p, bool by_opening_existing); + /** * @brief Internal main window object */ diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 6b90a62f7..62452ca19 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -58,6 +58,8 @@ MainMenu::MainMenu(MainWindow *parent) : file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), &Core::SaveActiveProjectAs, "Ctrl+Shift+S"); file_save_all_item_ = file_menu_->AddItem("saveallproj", Core::instance(), &Core::SaveAllProjects); file_menu_->addSeparator(); + file_revert_item_ = file_menu_->AddItem("revert", Core::instance(), &Core::RevertActiveProject, "Ctrl+F12"); + file_menu_->addSeparator(); file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, "Ctrl+I"); file_menu_->addSeparator(); file_export_menu_ = new Menu(file_menu_); @@ -679,6 +681,7 @@ void MainMenu::Retranslate() file_open_recent_menu_->setTitle(tr("Open &Recent")); file_open_recent_clear_item_->setText(tr("&Clear Recent List")); file_save_all_item_->setText(tr("Sa&ve All Projects")); + file_revert_item_->setText(tr("Revert")); file_import_item_->setText(tr("&Import...")); file_export_menu_->setTitle(tr("&Export")); file_export_media_item_->setText(tr("&Media...")); diff --git a/app/window/mainwindow/mainmenu.h b/app/window/mainwindow/mainmenu.h index ceffb7ac2..ff038e64a 100644 --- a/app/window/mainwindow/mainmenu.h +++ b/app/window/mainwindow/mainmenu.h @@ -203,6 +203,7 @@ private: QAction* file_save_item_; QAction* file_save_as_item_; QAction* file_save_all_item_; + QAction* file_revert_item_; QAction* file_import_item_; Menu* file_export_menu_; QAction* file_export_media_item_; From d3cd01c66a54ff66ec0c2d7ac174dd73b430a02b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 9 Aug 2021 11:39:25 -0700 Subject: [PATCH 16/19] mainmenu: update shortcut --- app/window/mainwindow/mainmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 62452ca19..a990084bf 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -58,7 +58,7 @@ MainMenu::MainMenu(MainWindow *parent) : file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), &Core::SaveActiveProjectAs, "Ctrl+Shift+S"); file_save_all_item_ = file_menu_->AddItem("saveallproj", Core::instance(), &Core::SaveAllProjects); file_menu_->addSeparator(); - file_revert_item_ = file_menu_->AddItem("revert", Core::instance(), &Core::RevertActiveProject, "Ctrl+F12"); + file_revert_item_ = file_menu_->AddItem("revert", Core::instance(), &Core::RevertActiveProject, "F12"); file_menu_->addSeparator(); file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, "Ctrl+I"); file_menu_->addSeparator(); From e511fde899f3aa56e20fe1f9f91716da279d36c9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 9 Aug 2021 12:52:25 -0700 Subject: [PATCH 17/19] timeline: invalidate when undoing a place command Fixes #1715 --- app/widget/timelinewidget/undo/timelineundopointer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/widget/timelinewidget/undo/timelineundopointer.cpp b/app/widget/timelinewidget/undo/timelineundopointer.cpp index 0954cd41c..4f5c448b6 100644 --- a/app/widget/timelinewidget/undo/timelineundopointer.cpp +++ b/app/widget/timelinewidget/undo/timelineundopointer.cpp @@ -426,9 +426,7 @@ void TrackPlaceBlockCommand::undo() } t->EndOperation(); - if (ripple_remove_command_) { - t->Node::InvalidateCache(insert_range, Track::kBlockInput); - } + t->Node::InvalidateCache(insert_range, Track::kBlockInput); // Remove tracks if we added them for (int i=add_track_commands_.size()-1; i>=0; i--) { From f6e510025d0b3bb405d08a0eb14413ff2e3b35a9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 9 Aug 2021 13:35:11 -0700 Subject: [PATCH 18/19] preferences: fixed issue with keyboard shortcut search --- app/dialog/preferences/tabs/preferenceskeyboardtab.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/dialog/preferences/tabs/preferenceskeyboardtab.cpp b/app/dialog/preferences/tabs/preferenceskeyboardtab.cpp index ef7ada6ee..a7e906372 100644 --- a/app/dialog/preferences/tabs/preferenceskeyboardtab.cpp +++ b/app/dialog/preferences/tabs/preferenceskeyboardtab.cpp @@ -150,6 +150,9 @@ bool PreferencesKeyboardTab::refine_shortcut_list(const QString &s, QTreeWidgetI for (int i=0;itopLevelItemCount();i++) { refine_shortcut_list(s, keyboard_tree_->topLevelItem(i)); } + + // Return value is `all_children_are_hidden` which doesn't matter at the top level + return false; } else { parent->setExpanded(!s.isEmpty()); @@ -158,7 +161,9 @@ bool PreferencesKeyboardTab::refine_shortcut_list(const QString &s, QTreeWidgetI for (int i=0;ichildCount();i++) { QTreeWidgetItem* item = parent->child(i); if (item->childCount() > 0) { - all_children_are_hidden = refine_shortcut_list(s, item); + if (!refine_shortcut_list(s, item)) { + all_children_are_hidden = false; + } } else { item->setHidden(false); if (s.isEmpty()) { @@ -183,7 +188,6 @@ bool PreferencesKeyboardTab::refine_shortcut_list(const QString &s, QTreeWidgetI return all_children_are_hidden; } - return true; } void PreferencesKeyboardTab::load_shortcut_file() { From 5e7cb068d9c82f4766c134b149861f827c368a1b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 12 Aug 2021 14:31:31 -0700 Subject: [PATCH 19/19] revise keyboard shortcuts to be translatable Fixes #1459 --- app/dialog/preferences/keysequenceeditor.cpp | 6 +- app/widget/menu/menu.cpp | 2 +- app/widget/menu/menu.h | 8 +- app/widget/menu/menushared.cpp | 34 +++--- app/window/mainwindow/mainmenu.cpp | 120 +++++++++---------- app/window/mainwindow/mainwindow.cpp | 2 +- 6 files changed, 86 insertions(+), 86 deletions(-) diff --git a/app/dialog/preferences/keysequenceeditor.cpp b/app/dialog/preferences/keysequenceeditor.cpp index ef57aaa3a..37ab138bb 100644 --- a/app/dialog/preferences/keysequenceeditor.cpp +++ b/app/dialog/preferences/keysequenceeditor.cpp @@ -45,9 +45,9 @@ QString KeySequenceEditor::action_name() { } QString KeySequenceEditor::export_shortcut() { - QString ks = keySequence().toString(); - if (ks != action->property("keydefault")) { - return action->property("id").toString() + "\t" + ks; + QKeySequence ks = keySequence(); + if (ks != action->property("keydefault").value()) { + return action->property("id").toString() + "\t" + ks.toString(); } return nullptr; } diff --git a/app/widget/menu/menu.cpp b/app/widget/menu/menu.cpp index f925877bb..6745940b8 100644 --- a/app/widget/menu/menu.cpp +++ b/app/widget/menu/menu.cpp @@ -87,7 +87,7 @@ void Menu::InsertAlphabetically(Menu *menu) InsertAlphabetically(menu->menuAction()); } -void Menu::ConformItem(QAction *a, const QString &id, const QString &key) +void Menu::ConformItem(QAction *a, const QString &id, const QKeySequence &key) { a->setProperty("id", id); diff --git a/app/widget/menu/menu.h b/app/widget/menu/menu.h index a11a0eafc..0bc4f4b7d 100644 --- a/app/widget/menu/menu.h +++ b/app/widget/menu/menu.h @@ -122,7 +122,7 @@ public: QAction* AddItem(const QString& id, const typename QtPrivate::FunctionPointer::Object *receiver, Func member, - const QString &key = QString()) + const QKeySequence &key = QKeySequence()) { QAction* a = CreateItem(this, id, receiver, member, key); @@ -171,7 +171,7 @@ public: const QString& id, const typename QtPrivate::FunctionPointer::Object *receiver, Func member, - const QString& key = QString()) + const QKeySequence &key = QKeySequence()) { QAction* a = new QAction(parent); @@ -215,7 +215,7 @@ public: const QString& id, const typename QtPrivate::FunctionPointer::Object *receiver, Func member, - const QString& key = QString()) + const QKeySequence &key = QKeySequence()) { ConformItem(a, id, key); @@ -224,7 +224,7 @@ public: static void ConformItem(QAction *a, const QString& id, - const QString& key = QString()); + const QKeySequence &key = QKeySequence()); private: void Init(); diff --git a/app/widget/menu/menushared.cpp b/app/widget/menu/menushared.cpp index 7b0506a45..1cabcf6de 100644 --- a/app/widget/menu/menushared.cpp +++ b/app/widget/menu/menushared.cpp @@ -33,32 +33,32 @@ MenuShared* MenuShared::instance_ = nullptr; MenuShared::MenuShared() { // "New" menu shared items - new_project_item_ = Menu::CreateItem(this, "newproj", Core::instance(), &Core::CreateNewProject, "Ctrl+N"); - new_sequence_item_ = Menu::CreateItem(this, "newseq", Core::instance(), &Core::CreateNewSequence, "Ctrl+Shift+N"); + new_project_item_ = Menu::CreateItem(this, "newproj", Core::instance(), &Core::CreateNewProject, tr("Ctrl+N")); + new_sequence_item_ = Menu::CreateItem(this, "newseq", Core::instance(), &Core::CreateNewSequence, tr("Ctrl+Shift+N")); new_folder_item_ = Menu::CreateItem(this, "newfolder", Core::instance(), &Core::CreateNewFolder); // "Edit" menu shared items - edit_cut_item_ = Menu::CreateItem(this, "cut", this, &MenuShared::CutTriggered, "Ctrl+X"); - edit_copy_item_ = Menu::CreateItem(this, "copy", this, &MenuShared::CopyTriggered, "Ctrl+C"); - edit_paste_item_ = Menu::CreateItem(this, "paste", this, &MenuShared::PasteTriggered, "Ctrl+V"); - edit_paste_insert_item_ = Menu::CreateItem(this, "pasteinsert", this, &MenuShared::PasteInsertTriggered, "Ctrl+Shift+V"); - edit_duplicate_item_ = Menu::CreateItem(this, "duplicate", this, &MenuShared::DuplicateTriggered, "Ctrl+D"); - edit_delete_item_ = Menu::CreateItem(this, "delete", this, &MenuShared::DeleteSelectedTriggered, "Del"); - edit_ripple_delete_item_ = Menu::CreateItem(this, "rippledelete", this, &MenuShared::RippleDeleteTriggered, "Shift+Del"); - edit_split_item_ = Menu::CreateItem(this, "split", this, &MenuShared::SplitAtPlayheadTriggered, "Ctrl+K"); - edit_speedduration_item_ = Menu::CreateItem(this, "speeddur", this, &MenuShared::SpeedDurationTriggered, "Ctrl+R"); + edit_cut_item_ = Menu::CreateItem(this, "cut", this, &MenuShared::CutTriggered, tr("Ctrl+X")); + edit_copy_item_ = Menu::CreateItem(this, "copy", this, &MenuShared::CopyTriggered, tr("Ctrl+C")); + edit_paste_item_ = Menu::CreateItem(this, "paste", this, &MenuShared::PasteTriggered, tr("Ctrl+V")); + edit_paste_insert_item_ = Menu::CreateItem(this, "pasteinsert", this, &MenuShared::PasteInsertTriggered, tr("Ctrl+Shift+V")); + edit_duplicate_item_ = Menu::CreateItem(this, "duplicate", this, &MenuShared::DuplicateTriggered, tr("Ctrl+D")); + edit_delete_item_ = Menu::CreateItem(this, "delete", this, &MenuShared::DeleteSelectedTriggered, tr("Del")); + edit_ripple_delete_item_ = Menu::CreateItem(this, "rippledelete", this, &MenuShared::RippleDeleteTriggered, tr("Shift+Del")); + edit_split_item_ = Menu::CreateItem(this, "split", this, &MenuShared::SplitAtPlayheadTriggered, tr("Ctrl+K")); + edit_speedduration_item_ = Menu::CreateItem(this, "speeddur", this, &MenuShared::SpeedDurationTriggered, tr("Ctrl+R")); // "In/Out" menu shared items - inout_set_in_item_ = Menu::CreateItem(this, "setinpoint", this, &MenuShared::SetInTriggered, "I"); - inout_set_out_item_ = Menu::CreateItem(this, "setoutpoint", this, &MenuShared::SetOutTriggered, "O"); + inout_set_in_item_ = Menu::CreateItem(this, "setinpoint", this, &MenuShared::SetInTriggered, tr("I")); + inout_set_out_item_ = Menu::CreateItem(this, "setoutpoint", this, &MenuShared::SetOutTriggered, tr("O")); inout_reset_in_item_ = Menu::CreateItem(this, "resetin", this, &MenuShared::ResetInTriggered); inout_reset_out_item_ = Menu::CreateItem(this, "resetout", this, &MenuShared::ResetOutTriggered); - inout_clear_inout_item_ = Menu::CreateItem(this, "clearinout", this, &MenuShared::ClearInOutTriggered, "G"); + inout_clear_inout_item_ = Menu::CreateItem(this, "clearinout", this, &MenuShared::ClearInOutTriggered, tr("G")); // "Clip Edit" menu shared items - clip_add_default_transition_item_ = Menu::CreateItem(this, "deftransition", this, &MenuShared::DefaultTransitionTriggered, "Ctrl+Shift+D"); - clip_link_unlink_item_ = Menu::CreateItem(this, "linkunlink", this, &MenuShared::ToggleLinksTriggered, "Ctrl+L"); - clip_enable_disable_item_ = Menu::CreateItem(this, "enabledisable", this, &MenuShared::EnableDisableTriggered, "Shift+E"); + clip_add_default_transition_item_ = Menu::CreateItem(this, "deftransition", this, &MenuShared::DefaultTransitionTriggered, tr("Ctrl+Shift+D")); + clip_link_unlink_item_ = Menu::CreateItem(this, "linkunlink", this, &MenuShared::ToggleLinksTriggered, tr("Ctrl+L")); + clip_enable_disable_item_ = Menu::CreateItem(this, "enabledisable", this, &MenuShared::EnableDisableTriggered, tr("Shift+E")); clip_nest_item_ = Menu::CreateItem(this, "nest", this, &MenuShared::NestTriggered); // TimeRuler menu shared items diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index a990084bf..8dfc0bcd4 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -50,26 +50,26 @@ MainMenu::MainMenu(MainWindow *parent) : file_menu_ = new Menu(this, this, &MainMenu::FileMenuAboutToShow); file_new_menu_ = new Menu(file_menu_); MenuShared::instance()->AddItemsForNewMenu(file_new_menu_); - file_open_item_ = file_menu_->AddItem("openproj", Core::instance(), &Core::OpenProject, "Ctrl+O"); + file_open_item_ = file_menu_->AddItem("openproj", Core::instance(), &Core::OpenProject, tr("Ctrl+O")); file_open_recent_menu_ = new Menu(file_menu_); file_open_recent_separator_ = file_open_recent_menu_->addSeparator(); file_open_recent_clear_item_ = file_open_recent_menu_->AddItem("clearopenrecent", Core::instance(), &Core::ClearOpenRecentList); - file_save_item_ = file_menu_->AddItem("saveproj", Core::instance(), &Core::SaveActiveProject, "Ctrl+S"); - file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), &Core::SaveActiveProjectAs, "Ctrl+Shift+S"); + file_save_item_ = file_menu_->AddItem("saveproj", Core::instance(), &Core::SaveActiveProject, tr("Ctrl+S")); + file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), &Core::SaveActiveProjectAs, tr("Ctrl+Shift+S")); file_save_all_item_ = file_menu_->AddItem("saveallproj", Core::instance(), &Core::SaveAllProjects); file_menu_->addSeparator(); - file_revert_item_ = file_menu_->AddItem("revert", Core::instance(), &Core::RevertActiveProject, "F12"); + file_revert_item_ = file_menu_->AddItem("revert", Core::instance(), &Core::RevertActiveProject, tr("F12")); file_menu_->addSeparator(); - file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, "Ctrl+I"); + file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, tr("Ctrl+I")); file_menu_->addSeparator(); file_export_menu_ = new Menu(file_menu_); - file_export_media_item_ = file_export_menu_->AddItem("export", Core::instance(), &Core::DialogExportShow, "Ctrl+M"); + file_export_media_item_ = file_export_menu_->AddItem("export", Core::instance(), &Core::DialogExportShow, tr("Ctrl+M")); file_menu_->addSeparator(); file_close_project_item_ = file_menu_->AddItem("closeproj", Core::instance(), &Core::CloseActiveProject); file_close_all_projects_item_ = file_menu_->AddItem("closeallproj", Core::instance(), static_cast(&Core::CloseAllProjects)); file_close_all_except_item_ = file_menu_->AddItem("closeallexcept", Core::instance(), &Core::CloseAllExceptActiveProject); file_menu_->addSeparator(); - file_exit_item_ = file_menu_->AddItem("exit", parent, &MainWindow::close, "Ctrl+Q"); + file_exit_item_ = file_menu_->AddItem("exit", parent, &MainWindow::close); // // EDIT MENU @@ -80,10 +80,10 @@ MainMenu::MainMenu(MainWindow *parent) : connect(edit_menu_, &Menu::aboutToHide, this, &MainMenu::EditMenuAboutToHide); edit_undo_item_ = Core::instance()->undo_stack()->GetUndoAction(); - Menu::ConformItem(edit_undo_item_, "undo", "Ctrl+Z"); + Menu::ConformItem(edit_undo_item_, "undo", tr("Ctrl+Z")); edit_menu_->addAction(edit_undo_item_); edit_redo_item_ = Core::instance()->undo_stack()->GetRedoAction(); - Menu::ConformItem(edit_redo_item_, "redo", "Ctrl+Shift+Z"); + Menu::ConformItem(edit_redo_item_, "redo", tr("Ctrl+Shift+Z")); edit_menu_->addAction(edit_redo_item_); edit_menu_->addSeparator(); @@ -92,49 +92,49 @@ MainMenu::MainMenu(MainWindow *parent) : // Create "alternate delete" action so we can pick up backspace as well as delete while still // keeping them configurable edit_delete2_item_ = new QAction(); - Menu::ConformItem(edit_delete2_item_, "delete2", MenuShared::instance(), &MenuShared::DeleteSelectedTriggered, "Backspace"); + Menu::ConformItem(edit_delete2_item_, "delete2", MenuShared::instance(), &MenuShared::DeleteSelectedTriggered, tr("Backspace")); auto actions = edit_menu_->actions(); edit_menu_->insertAction(actions.at(actions.indexOf(MenuShared::instance()->edit_delete_item()) + 1), edit_delete2_item_); } edit_menu_->addSeparator(); - edit_select_all_item_ = edit_menu_->AddItem("selectall", this, &MainMenu::SelectAllTriggered, "Ctrl+A"); - edit_deselect_all_item_ = edit_menu_->AddItem("deselectall", this, &MainMenu::DeselectAllTriggered, "Ctrl+Shift+A"); + edit_select_all_item_ = edit_menu_->AddItem("selectall", this, &MainMenu::SelectAllTriggered, tr("Ctrl+A")); + edit_deselect_all_item_ = edit_menu_->AddItem("deselectall", this, &MainMenu::DeselectAllTriggered, tr("Ctrl+Shift+A")); edit_menu_->addSeparator(); MenuShared::instance()->AddItemsForClipEditMenu(edit_menu_); edit_menu_->addSeparator(); - edit_insert_item_ = edit_menu_->AddItem("insert", this, &MainMenu::InsertTriggered, ","); - edit_overwrite_item_ = edit_menu_->AddItem("overwrite", this, &MainMenu::OverwriteTriggered, "."); + edit_insert_item_ = edit_menu_->AddItem("insert", this, &MainMenu::InsertTriggered, tr(",")); + edit_overwrite_item_ = edit_menu_->AddItem("overwrite", this, &MainMenu::OverwriteTriggered, tr(".")); edit_menu_->addSeparator(); - edit_ripple_to_in_item_ = edit_menu_->AddItem("rippletoin", this, &MainMenu::RippleToInTriggered, "Q"); - edit_ripple_to_out_item_ = edit_menu_->AddItem("rippletoout", this, &MainMenu::RippleToOutTriggered, "W"); - edit_edit_to_in_item_ = edit_menu_->AddItem("edittoin", this, &MainMenu::EditToInTriggered, "Ctrl+Alt+Q"); - edit_edit_to_out_item_ = edit_menu_->AddItem("edittoout", this, &MainMenu::EditToOutTriggered, "Ctrl+Alt+W"); + edit_ripple_to_in_item_ = edit_menu_->AddItem("rippletoin", this, &MainMenu::RippleToInTriggered, tr("Q")); + edit_ripple_to_out_item_ = edit_menu_->AddItem("rippletoout", this, &MainMenu::RippleToOutTriggered, tr("W")); + edit_edit_to_in_item_ = edit_menu_->AddItem("edittoin", this, &MainMenu::EditToInTriggered, tr("Ctrl+Alt+Q")); + edit_edit_to_out_item_ = edit_menu_->AddItem("edittoout", this, &MainMenu::EditToOutTriggered, tr("Ctrl+Alt+W")); edit_menu_->addSeparator(); - edit_nudge_left_item_ = edit_menu_->AddItem("nudgeleft", this, &MainMenu::NudgeLeftTriggered, "Alt+Left"); - edit_nudge_right_item_ = edit_menu_->AddItem("nudgeright", this, &MainMenu::NudgeRightTriggered, "Alt+Right"); - edit_move_in_to_playhead_item_ = edit_menu_->AddItem("moveintoplayhead", this, &MainMenu::MoveInToPlayheadTriggered, "["); - edit_move_out_to_playhead_item_ = edit_menu_->AddItem("moveouttoplayhead", this, &MainMenu::MoveOutToPlayheadTriggered, "]"); + edit_nudge_left_item_ = edit_menu_->AddItem("nudgeleft", this, &MainMenu::NudgeLeftTriggered, tr("Alt+Left")); + edit_nudge_right_item_ = edit_menu_->AddItem("nudgeright", this, &MainMenu::NudgeRightTriggered, tr("Alt+Right")); + edit_move_in_to_playhead_item_ = edit_menu_->AddItem("moveintoplayhead", this, &MainMenu::MoveInToPlayheadTriggered, tr("[")); + edit_move_out_to_playhead_item_ = edit_menu_->AddItem("moveouttoplayhead", this, &MainMenu::MoveOutToPlayheadTriggered, tr("]")); edit_menu_->addSeparator(); MenuShared::instance()->AddItemsForInOutMenu(edit_menu_); - edit_delete_inout_item_ = edit_menu_->AddItem("deleteinout", this, &MainMenu::DeleteInOutTriggered, ";"); - edit_ripple_delete_inout_item_ = edit_menu_->AddItem("rippledeleteinout", this, &MainMenu::RippleDeleteInOutTriggered, "'"); + edit_delete_inout_item_ = edit_menu_->AddItem("deleteinout", this, &MainMenu::DeleteInOutTriggered, tr(";")); + edit_ripple_delete_inout_item_ = edit_menu_->AddItem("rippledeleteinout", this, &MainMenu::RippleDeleteInOutTriggered, tr("'")); edit_menu_->addSeparator(); - edit_set_marker_item_ = edit_menu_->AddItem("marker", this, &MainMenu::SetMarkerTriggered, "M"); + edit_set_marker_item_ = edit_menu_->AddItem("marker", this, &MainMenu::SetMarkerTriggered, tr("M")); // // VIEW MENU // view_menu_ = new Menu(this, this, &MainMenu::ViewMenuAboutToShow); - view_zoom_in_item_ = view_menu_->AddItem("zoomin", this, &MainMenu::ZoomInTriggered, "="); - view_zoom_out_item_ = view_menu_->AddItem("zoomout", this, &MainMenu::ZoomOutTriggered, "-"); - view_increase_track_height_item_ = view_menu_->AddItem("vzoomin", this, &MainMenu::IncreaseTrackHeightTriggered, "Ctrl+="); - view_decrease_track_height_item_ = view_menu_->AddItem("vzoomout", this, &MainMenu::DecreaseTrackHeightTriggered, "Ctrl+-"); - view_show_all_item_ = view_menu_->AddItem("showall", this, &MainMenu::ToggleShowAllTriggered, "\\"); + view_zoom_in_item_ = view_menu_->AddItem("zoomin", this, &MainMenu::ZoomInTriggered, tr("=")); + view_zoom_out_item_ = view_menu_->AddItem("zoomout", this, &MainMenu::ZoomOutTriggered, tr("-")); + view_increase_track_height_item_ = view_menu_->AddItem("vzoomin", this, &MainMenu::IncreaseTrackHeightTriggered, tr("Ctrl+=")); + view_decrease_track_height_item_ = view_menu_->AddItem("vzoomout", this, &MainMenu::DecreaseTrackHeightTriggered, tr("Ctrl+-")); + view_show_all_item_ = view_menu_->AddItem("showall", this, &MainMenu::ToggleShowAllTriggered, tr("\\")); view_show_all_item_->setCheckable(true); view_menu_->addSeparator(); - view_full_screen_item_ = view_menu_->AddItem("fullscreen", parent, &MainWindow::SetFullscreen, "F11"); + view_full_screen_item_ = view_menu_->AddItem("fullscreen", parent, &MainWindow::SetFullscreen, tr("F11")); view_full_screen_item_->setCheckable(true); view_full_screen_viewer_item_ = view_menu_->AddItem("fullscreenviewer", this, &MainMenu::FullScreenViewerTriggered); @@ -143,28 +143,28 @@ MainMenu::MainMenu(MainWindow *parent) : // PLAYBACK MENU // playback_menu_ = new Menu(this, this, &MainMenu::PlaybackMenuAboutToShow); - playback_gotostart_item_ = playback_menu_->AddItem("gotostart", this, &MainMenu::GoToStartTriggered, "Home"); - playback_prevframe_item_ = playback_menu_->AddItem("prevframe", this, &MainMenu::PrevFrameTriggered, "Left"); - playback_playpause_item_ = playback_menu_->AddItem("playpause", this, &MainMenu::PlayPauseTriggered, "Space"); - playback_playinout_item_ = playback_menu_->AddItem("playintoout", this, &MainMenu::PlayInToOutTriggered, "Shift+Space"); - playback_nextframe_item_ = playback_menu_->AddItem("nextframe", this, &MainMenu::NextFrameTriggered, "Right"); - playback_gotoend_item_ = playback_menu_->AddItem("gotoend", this, &MainMenu::GoToEndTriggered, "End"); + playback_gotostart_item_ = playback_menu_->AddItem("gotostart", this, &MainMenu::GoToStartTriggered, tr("Home")); + playback_prevframe_item_ = playback_menu_->AddItem("prevframe", this, &MainMenu::PrevFrameTriggered, tr("Left")); + playback_playpause_item_ = playback_menu_->AddItem("playpause", this, &MainMenu::PlayPauseTriggered, tr("Space")); + playback_playinout_item_ = playback_menu_->AddItem("playintoout", this, &MainMenu::PlayInToOutTriggered, tr("Shift+Space")); + playback_nextframe_item_ = playback_menu_->AddItem("nextframe", this, &MainMenu::NextFrameTriggered, tr("Right")); + playback_gotoend_item_ = playback_menu_->AddItem("gotoend", this, &MainMenu::GoToEndTriggered, tr("End")); playback_menu_->addSeparator(); - playback_prevcut_item_ = playback_menu_->AddItem("prevcut", this, &MainMenu::GoToPrevCutTriggered, "Up"); - playback_nextcut_item_ = playback_menu_->AddItem("nextcut", this, &MainMenu::GoToNextCutTriggered, "Down"); + playback_prevcut_item_ = playback_menu_->AddItem("prevcut", this, &MainMenu::GoToPrevCutTriggered, tr("Up")); + playback_nextcut_item_ = playback_menu_->AddItem("nextcut", this, &MainMenu::GoToNextCutTriggered, tr("Down")); playback_menu_->addSeparator(); - playback_gotoin_item_ = playback_menu_->AddItem("gotoin", this, &MainMenu::GoToInTriggered, "Shift+I"); - playback_gotoout_item_ = playback_menu_->AddItem("gotoout", this, &MainMenu::GoToOutTriggered, "Shift+O"); + playback_gotoin_item_ = playback_menu_->AddItem("gotoin", this, &MainMenu::GoToInTriggered, tr("Shift+I")); + playback_gotoout_item_ = playback_menu_->AddItem("gotoout", this, &MainMenu::GoToOutTriggered, tr("Shift+O")); playback_menu_->addSeparator(); - playback_shuttleleft_item_ = playback_menu_->AddItem("decspeed", this, &MainMenu::ShuttleLeftTriggered, "J"); - playback_shuttlestop_item_ = playback_menu_->AddItem("pause", this, &MainMenu::ShuttleStopTriggered, "K"); - playback_shuttleright_item_ = playback_menu_->AddItem("incspeed", this, &MainMenu::ShuttleRightTriggered, "L"); + playback_shuttleleft_item_ = playback_menu_->AddItem("decspeed", this, &MainMenu::ShuttleLeftTriggered, tr("J")); + playback_shuttlestop_item_ = playback_menu_->AddItem("pause", this, &MainMenu::ShuttleStopTriggered, tr("K")); + playback_shuttleright_item_ = playback_menu_->AddItem("incspeed", this, &MainMenu::ShuttleRightTriggered, tr("L")); playback_menu_->addSeparator(); @@ -188,7 +188,7 @@ MainMenu::MainMenu(MainWindow *parent) : // window_menu_ = new Menu(this, this, &MainMenu::WindowMenuAboutToShow); window_menu_separator_ = window_menu_->addSeparator(); - window_maximize_panel_item_ = window_menu_->AddItem("maximizepanel", parent, &MainWindow::ToggleMaximizedPanel, "`"); + window_maximize_panel_item_ = window_menu_->AddItem("maximizepanel", parent, &MainWindow::ToggleMaximizedPanel, tr("`")); window_lock_layout_item_ = window_menu_->AddItem("lockpanels", PanelManager::instance(), &PanelManager::SetPanelsLocked); window_lock_layout_item_->setCheckable(true); window_menu_->addSeparator(); @@ -202,81 +202,81 @@ MainMenu::MainMenu(MainWindow *parent) : tools_group_ = new QActionGroup(this); - tools_pointer_item_ = tools_menu_->AddItem("pointertool", this, &MainMenu::ToolItemTriggered, "V"); + tools_pointer_item_ = tools_menu_->AddItem("pointertool", this, &MainMenu::ToolItemTriggered, tr("V")); tools_pointer_item_->setCheckable(true); tools_pointer_item_->setData(Tool::kPointer); tools_group_->addAction(tools_pointer_item_); - tools_edit_item_ = tools_menu_->AddItem("edittool", this, &MainMenu::ToolItemTriggered, "X"); + tools_edit_item_ = tools_menu_->AddItem("edittool", this, &MainMenu::ToolItemTriggered, tr("X")); tools_edit_item_->setCheckable(true); tools_edit_item_->setData(Tool::kEdit); tools_group_->addAction(tools_edit_item_); - tools_ripple_item_ = tools_menu_->AddItem("rippletool", this, &MainMenu::ToolItemTriggered, "B"); + tools_ripple_item_ = tools_menu_->AddItem("rippletool", this, &MainMenu::ToolItemTriggered, tr("B")); tools_ripple_item_->setCheckable(true); tools_ripple_item_->setData(Tool::kRipple); tools_group_->addAction(tools_ripple_item_); - tools_rolling_item_ = tools_menu_->AddItem("rollingtool", this, &MainMenu::ToolItemTriggered, "N"); + tools_rolling_item_ = tools_menu_->AddItem("rollingtool", this, &MainMenu::ToolItemTriggered, tr("N")); tools_rolling_item_->setCheckable(true); tools_rolling_item_->setData(Tool::kRolling); tools_group_->addAction(tools_rolling_item_); - tools_razor_item_ = tools_menu_->AddItem("razortool", this, &MainMenu::ToolItemTriggered, "C"); + tools_razor_item_ = tools_menu_->AddItem("razortool", this, &MainMenu::ToolItemTriggered, tr("C")); tools_razor_item_->setCheckable(true); tools_razor_item_->setData(Tool::kRazor); tools_group_->addAction(tools_razor_item_); - tools_slip_item_ = tools_menu_->AddItem("sliptool", this, &MainMenu::ToolItemTriggered, "Y"); + tools_slip_item_ = tools_menu_->AddItem("sliptool", this, &MainMenu::ToolItemTriggered, tr("Y")); tools_slip_item_->setCheckable(true); tools_slip_item_->setData(Tool::kSlip); tools_group_->addAction(tools_slip_item_); - tools_slide_item_ = tools_menu_->AddItem("slidetool", this, &MainMenu::ToolItemTriggered, "U"); + tools_slide_item_ = tools_menu_->AddItem("slidetool", this, &MainMenu::ToolItemTriggered, tr("U")); tools_slide_item_->setCheckable(true); tools_slide_item_->setData(Tool::kSlide); tools_group_->addAction(tools_slide_item_); - tools_hand_item_ = tools_menu_->AddItem("handtool", this, &MainMenu::ToolItemTriggered, "H"); + tools_hand_item_ = tools_menu_->AddItem("handtool", this, &MainMenu::ToolItemTriggered, tr("H")); tools_hand_item_->setCheckable(true); tools_hand_item_->setData(Tool::kHand); tools_group_->addAction(tools_hand_item_); - tools_zoom_item_ = tools_menu_->AddItem("zoomtool", this, &MainMenu::ToolItemTriggered, "Z"); + tools_zoom_item_ = tools_menu_->AddItem("zoomtool", this, &MainMenu::ToolItemTriggered, tr("Z")); tools_zoom_item_->setCheckable(true); tools_zoom_item_->setData(Tool::kZoom); tools_group_->addAction(tools_zoom_item_); - tools_transition_item_ = tools_menu_->AddItem("transitiontool", this, &MainMenu::ToolItemTriggered, "T"); + tools_transition_item_ = tools_menu_->AddItem("transitiontool", this, &MainMenu::ToolItemTriggered, tr("T")); tools_transition_item_->setCheckable(true); tools_transition_item_->setData(Tool::kTransition); tools_group_->addAction(tools_transition_item_); - tools_add_item_ = tools_menu_->AddItem("addtool", this, &MainMenu::ToolItemTriggered, "A"); + tools_add_item_ = tools_menu_->AddItem("addtool", this, &MainMenu::ToolItemTriggered, tr("A")); tools_add_item_->setCheckable(true); tools_add_item_->setData(Tool::kAdd); tools_group_->addAction(tools_add_item_); - tools_record_item_ = tools_menu_->AddItem("recordtool", this, &MainMenu::ToolItemTriggered, "R"); + tools_record_item_ = tools_menu_->AddItem("recordtool", this, &MainMenu::ToolItemTriggered, tr("R")); tools_record_item_->setCheckable(true); tools_record_item_->setData(Tool::kRecord); tools_group_->addAction(tools_record_item_); tools_menu_->addSeparator(); - tools_snapping_item_ = tools_menu_->AddItem("snapping", Core::instance(), &Core::SetSnapping, "S"); + tools_snapping_item_ = tools_menu_->AddItem("snapping", Core::instance(), &Core::SetSnapping, tr("S")); tools_snapping_item_->setCheckable(true); tools_snapping_item_->setChecked(Core::instance()->snapping()); tools_menu_->addSeparator(); - tools_preferences_item_ = tools_menu_->AddItem("prefs", Core::instance(), &Core::DialogPreferencesShow, "Ctrl+,"); + tools_preferences_item_ = tools_menu_->AddItem("prefs", Core::instance(), &Core::DialogPreferencesShow, tr("Ctrl+,")); // // HELP MENU // help_menu_ = new Menu(this); - help_action_search_item_ = help_menu_->AddItem("actionsearch", this, &MainMenu::ActionSearchTriggered, "/"); + help_action_search_item_ = help_menu_->AddItem("actionsearch", this, &MainMenu::ActionSearchTriggered, tr("/")); help_menu_->addSeparator(); help_feedback_item_ = help_menu_->AddItem("feedback", this, &MainMenu::HelpFeedbackTriggered); help_menu_->addSeparator(); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index fec8e5453..612cc68cd 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -677,7 +677,7 @@ void SaveCustomShortcutsInternal(QMenu* menu, QMap* shortcuts) if (a->menu()) { SaveCustomShortcutsInternal(a->menu(), shortcuts); } else if (!a->isSeparator()) { - QString default_shortcut = a->property("keydefault").toString(); + QString default_shortcut = a->property("keydefault").value().toString(); QString current_shortcut = a->shortcut().toString(); if (current_shortcut != default_shortcut) { QString action_id = a->property("id").toString();