diff options
Diffstat (limited to 'gtk2_ardour/ardour_ui.cc')
-rw-r--r-- | gtk2_ardour/ardour_ui.cc | 3385 |
1 files changed, 3385 insertions, 0 deletions
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc new file mode 100644 index 0000000000..1dcada4a7c --- /dev/null +++ b/gtk2_ardour/ardour_ui.cc @@ -0,0 +1,3385 @@ +/* + Copyright (C) 1999-2002 Paul Davis + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include <algorithm> +#include <cmath> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <cerrno> +#include <fstream> + +#include <iostream> + +#include <gtk--.h> +#include <pbd/error.h> +#include <pbd/basename.h> +#include <pbd/pathscanner.h> +#include <pbd/failed_constructor.h> +#include <gtkmmext/gtk_ui.h> +#include <gtkmmext/pix.h> +#include <gtkmmext/utils.h> +#include <gtkmmext/click_box.h> +#include <gtkmmext/selector.h> +#include <gtkmmext/fastmeter.h> +#include <gtkmmext/stop_signal.h> +#include <gtkmmext/popup.h> + +#include <midi++/port.h> +#include <midi++/mmc.h> + +#include <ardour/ardour.h> +#include <ardour/port.h> +#include <ardour/audioengine.h> +#include <ardour/playlist.h> +#include <ardour/utils.h> +#include <ardour/diskstream.h> +#include <ardour/filesource.h> +#include <ardour/recent_sessions.h> +#include <ardour/session_diskstream.h> +#include <ardour/port.h> +#include <ardour/audio_track.h> + +#include "ardour_ui.h" +#include "ardour_message.h" +#include "public_editor.h" +#include "audio_clock.h" +#include "keyboard.h" +#include "mixer_ui.h" +#include "prompter.h" +#include "opts.h" +#include "keyboard_target.h" +#include "add_route_dialog.h" +#include "new_session_dialog.h" +#include "about.h" +#include "utils.h" +#include "gui_thread.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace Gtkmmext; +using namespace Gtk; +using namespace SigC; + +ARDOUR_UI *ARDOUR_UI::theArdourUI = 0; +SoundFileSelector* ARDOUR_UI::sfdb_window = 0; + +SigC::Signal1<void,bool> ARDOUR_UI::Blink; +SigC::Signal0<void> ARDOUR_UI::RapidScreenUpdate; +SigC::Signal0<void> ARDOUR_UI::SuperRapidScreenUpdate; +SigC::Signal1<void,jack_nframes_t> ARDOUR_UI::Clock; + +/* XPM */ +static const gchar *h_meter_strip_xpm[] = { +"186 5 187 2", +" c None", +". c #2BFE00", +"+ c #2DFE00", +"@ c #2FFE01", +"# c #32FE01", +"$ c #34FE02", +"% c #36FE02", +"& c #38FE03", +"* c #3BFE03", +"= c #3DFD04", +"- c #3FFD04", +"; c #41FD05", +"> c #44FD05", +", c #46FD06", +"' c #48FD06", +") c #4AFD07", +"! c #4DFD07", +"~ c #4FFD08", +"{ c #51FC08", +"] c #53FC09", +"^ c #56FC09", +"/ c #58FC09", +"( c #5AFC0A", +"_ c #5CFC0A", +": c #5FFC0B", +"< c #61FC0B", +"[ c #63FB0C", +"} c #65FB0C", +"| c #68FB0D", +"1 c #6AFB0D", +"2 c #6CFB0E", +"3 c #6EFB0E", +"4 c #71FB0F", +"5 c #73FB0F", +"6 c #75FB10", +"7 c #77FA10", +"8 c #7AFA11", +"9 c #7CFA11", +"0 c #7EFA12", +"a c #80FA12", +"b c #83FA12", +"c c #85FA13", +"d c #87FA13", +"e c #89FA14", +"f c #8CF914", +"g c #8EF915", +"h c #90F915", +"i c #92F916", +"j c #95F916", +"k c #97F917", +"l c #99F917", +"m c #9BF918", +"n c #9EF818", +"o c #A0F819", +"p c #A2F819", +"q c #A4F81A", +"r c #A7F81A", +"s c #A9F81A", +"t c #ABF81B", +"u c #ADF81B", +"v c #B0F81C", +"w c #B2F71C", +"x c #B4F71D", +"y c #B6F71D", +"z c #B9F71E", +"A c #BBF71E", +"B c #BDF71F", +"C c #BFF71F", +"D c #C2F720", +"E c #C4F720", +"F c #C6F621", +"G c #C8F621", +"H c #CBF622", +"I c #CDF622", +"J c #CFF623", +"K c #D1F623", +"L c #D4F624", +"M c #D6F624", +"N c #D8F524", +"O c #DAF525", +"P c #DDF525", +"Q c #DFF526", +"R c #E1F526", +"S c #E3F527", +"T c #E6F527", +"U c #E8F528", +"V c #EAF528", +"W c #ECF429", +"X c #EFF429", +"Y c #F1F42A", +"Z c #F3F42A", +"` c #F5F42B", +" . c #F8F42B", +".. c #FAF42C", +"+. c #FCF42C", +"@. c #FFF42D", +"#. c #FFF22C", +"$. c #FFF12B", +"%. c #FFF02A", +"&. c #FFEF2A", +"*. c #FFEE29", +"=. c #FFED28", +"-. c #FFEC28", +";. c #FFEB27", +">. c #FFE926", +",. c #FFE826", +"'. c #FFE725", +"). c #FFE624", +"!. c #FFE524", +"~. c #FFE423", +"{. c #FFE322", +"]. c #FFE222", +"^. c #FFE021", +"/. c #FFDF20", +"(. c #FFDE20", +"_. c #FFDD1F", +":. c #FFDC1E", +"<. c #FFDB1E", +"[. c #FFDA1D", +"}. c #FFD91C", +"|. c #FFD71B", +"1. c #FFD61B", +"2. c #FFD51A", +"3. c #FFD419", +"4. c #FFD319", +"5. c #FFD218", +"6. c #FFD117", +"7. c #FFD017", +"8. c #FFCF16", +"9. c #FFCD15", +"0. c #FFCC15", +"a. c #FFCB14", +"b. c #FFCA13", +"c. c #FFC913", +"d. c #FFC812", +"e. c #FFC711", +"f. c #FFC611", +"g. c #FFC410", +"h. c #FFC30F", +"i. c #FFC20F", +"j. c #FFC10E", +"k. c #FFC00D", +"l. c #FFBF0C", +"m. c #FFBE0C", +"n. c #FFBD0B", +"o. c #FFBB0A", +"p. c #FFBA0A", +"q. c #FFB909", +"r. c #FFB808", +"s. c #FFB708", +"t. c #FFB607", +"u. c #FFB506", +"v. c #FFB406", +"w. c #FFB205", +"x. c #FFB104", +"y. c #FFB004", +"z. c #FFAF03", +"A. c #FFAE02", +"B. c #FFAD02", +"C. c #FFAC01", +"D. c #FFAB00", +"E. c #FFA900", +"F. c #F11F00", +"G. c #F21E00", +"H. c #F21C00", +"I. c #F31B00", +"J. c #F31A00", +"K. c #F41800", +"L. c #F41700", +"M. c #F51600", +"N. c #F61400", +"O. c #F61300", +"P. c #F71100", +"Q. c #F71000", +"R. c #F80F00", +"S. c #F90D00", +"T. c #F90C00", +"U. c #FA0B00", +"V. c #FA0900", +"W. c #FB0800", +"X. c #FC0600", +"Y. c #FC0500", +"Z. c #FD0400", +"`. c #FD0200", +" + c #FE0100", +".+ c #FE0000", +"++ c #FF0000", +". + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ` ...+.@.@.#.$.%.&.*.=.-.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +.+", +". + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ` ...+.@.@.#.$.%.&.*.=.-.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +.+", +". + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ` ...+.@.@.#.$.%.&.*.=.-.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +++", +". + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ` ...+.@.@.#.$.%.&.*.=.-.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +++", +". + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ` ...+.@.@.#.$.%.&.*.=.-.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +++"}; + +/* XPM */ +static const gchar * v_meter_strip_xpm[] = { +"5 250 230 2", +" c None", +". c #FE0000", +"+ c #FF0000", +"@ c #FE0100", +"# c #FD0200", +"$ c #FD0300", +"% c #FD0400", +"& c #FC0500", +"* c #FC0600", +"= c #FC0700", +"- c #FB0800", +"; c #FA0900", +"> c #FA0A00", +", c #FA0B00", +"' c #F90C00", +") c #F90D00", +"! c #F80E00", +"~ c #F80F00", +"{ c #F71000", +"] c #F71100", +"^ c #F61200", +"/ c #F61300", +"( c #F61400", +"_ c #F51600", +": c #F41700", +"< c #F41800", +"[ c #F31A00", +"} c #F31B00", +"| c #F21C00", +"1 c #F21E00", +"2 c #F11F00", +"3 c #F54A00", +"4 c #FFA900", +"5 c #FFAB00", +"6 c #FFAC01", +"7 c #FFAD02", +"8 c #FFAE02", +"9 c #FFAF03", +"0 c #FFB004", +"a c #FFB104", +"b c #FFB205", +"c c #FFB406", +"d c #FFB506", +"e c #FFB607", +"f c #FFB708", +"g c #FFB808", +"h c #FFB909", +"i c #FFBA0A", +"j c #FFBB0A", +"k c #FFBC0A", +"l c #FFBD0B", +"m c #FFBE0C", +"n c #FFBF0C", +"o c #FFC00D", +"p c #FFC10E", +"q c #FFC20F", +"r c #FFC30F", +"s c #FFC410", +"t c #FFC511", +"u c #FFC611", +"v c #FFC711", +"w c #FFC812", +"x c #FFC913", +"y c #FFCA13", +"z c #FFCB14", +"A c #FFCC15", +"B c #FFCD15", +"C c #FFCF16", +"D c #FFD017", +"E c #FFD117", +"F c #FFD218", +"G c #FFD319", +"H c #FFD419", +"I c #FFD51A", +"J c #FFD61B", +"K c #FFD71B", +"L c #FFD81C", +"M c #FFD91C", +"N c #FFDA1D", +"O c #FFDB1E", +"P c #FFDC1E", +"Q c #FFDD1F", +"R c #FFDE20", +"S c #FFDF20", +"T c #FFE021", +"U c #FFE222", +"V c #FFE322", +"W c #FFE423", +"X c #FFE524", +"Y c #FFE624", +"Z c #FFE725", +"` c #FFE826", +" . c #FFE926", +".. c #FFEA26", +"+. c #FFEB27", +"@. c #FFEC28", +"#. c #FFED28", +"$. c #FFEE29", +"%. c #FFEF2A", +"&. c #FFF02A", +"*. c #FFF12B", +"=. c #FFF22C", +"-. c #FFF32D", +";. c #FFF42D", +">. c #FDF42C", +",. c #FBF42C", +"'. c #FAF42C", +"). c #F8F42B", +"!. c #F6F42B", +"~. c #F4F42B", +"{. c #F3F42A", +"]. c #F1F42A", +"^. c #F0F429", +"/. c #EEF429", +"(. c #ECF429", +"_. c #EAF528", +":. c #E9F528", +"<. c #E7F528", +"[. c #E5F527", +"}. c #E3F527", +"|. c #E2F526", +"1. c #E0F526", +"2. c #DFF526", +"3. c #DDF525", +"4. c #DBF525", +"5. c #D9F525", +"6. c #D8F524", +"7. c #D6F624", +"8. c #D5F624", +"9. c #D3F624", +"0. c #D1F623", +"a. c #CFF623", +"b. c #CEF622", +"c. c #CCF622", +"d. c #CBF622", +"e. c #C9F621", +"f. c #C7F621", +"g. c #C5F621", +"h. c #C4F720", +"i. c #C2F720", +"j. c #C0F71F", +"k. c #BEF71F", +"l. c #BDF71F", +"m. c #BBF71E", +"n. c #BAF71E", +"o. c #B8F71E", +"p. c #B6F71D", +"q. c #B5F71D", +"r. c #B3F71D", +"s. c #B2F71C", +"t. c #B0F81C", +"u. c #AEF81B", +"v. c #ACF81B", +"w. c #ABF81B", +"x. c #A9F81A", +"y. c #A8F81A", +"z. c #A6F81A", +"A. c #A4F81A", +"B. c #A2F819", +"C. c #A1F819", +"D. c #9FF819", +"E. c #9EF818", +"F. c #9BF918", +"G. c #9AF917", +"H. c #98F917", +"I. c #97F917", +"J. c #95F916", +"K. c #93F916", +"L. c #91F916", +"M. c #90F915", +"N. c #8EF915", +"O. c #8DF914", +"P. c #8BF914", +"Q. c #89FA14", +"R. c #87FA13", +"S. c #86FA13", +"T. c #84FA13", +"U. c #83FA12", +"V. c #81FA12", +"W. c #7FFA12", +"X. c #7DFA12", +"Y. c #7CFA11", +"Z. c #7AFA11", +"`. c #78FA10", +" + c #76FA10", +".+ c #75FB10", +"++ c #73FB0F", +"@+ c #72FB0F", +"#+ c #70FB0F", +"$+ c #6EFB0E", +"%+ c #6DFB0E", +"&+ c #6BFB0E", +"*+ c #6AFB0D", +"=+ c #68FB0D", +"-+ c #66FB0C", +";+ c #64FB0C", +">+ c #63FB0C", +",+ c #61FC0B", +"'+ c #60FC0B", +")+ c #5EFC0B", +"!+ c #5CFC0A", +"~+ c #5AFC0A", +"{+ c #59FC09", +"]+ c #57FC09", +"^+ c #56FC09", +"/+ c #53FC09", +"(+ c #52FC08", +"_+ c #50FC08", +":+ c #4FFD08", +"<+ c #4DFD07", +"[+ c #4BFD07", +"}+ c #49FD07", +"|+ c #48FD06", +"1+ c #46FD06", +"2+ c #45FD05", +"3+ c #43FD05", +"4+ c #41FD05", +"5+ c #3FFD04", +"6+ c #3EFD04", +"7+ c #3CFD04", +"8+ c #3BFE03", +"9+ c #39FE03", +"0+ c #37FE02", +"a+ c #35FE02", +"b+ c #34FE02", +"c+ c #32FE01", +"d+ c #30FE01", +"e+ c #2EFE01", +"f+ c #2DFE00", +"g+ c #2BFE00", +". . + + + ", +". . + + + ", +"@ @ @ @ @ ", +"# # # # # ", +"$ $ $ $ $ ", +"% % % % % ", +"& & & & & ", +"* * * * * ", +"= = = = = ", +"- - - - - ", +"; ; ; ; ; ", +"> > > > > ", +", , , , , ", +"' ' ' ' ' ", +") ) ) ) ) ", +"! ! ! ! ! ", +"~ ~ ~ ~ ~ ", +"{ { { { { ", +"] ] ] ] ] ", +"^ ^ ^ ^ ^ ", +"/ / / / / ", +"( ( ( ( ( ", +"_ _ _ _ _ ", +": : : : : ", +": : : : : ", +"< < < < < ", +"[ [ [ [ [ ", +"} } } } } ", +"} } } } } ", +"| | | | | ", +"1 1 1 1 1 ", +"2 2 2 2 2 ", +"3 3 3 3 3 ", +"4 4 4 4 4 ", +"5 5 5 5 5 ", +"6 6 6 6 6 ", +"6 6 6 6 6 ", +"7 7 7 7 7 ", +"8 8 8 8 8 ", +"9 9 9 9 9 ", +"9 9 9 9 9 ", +"0 0 0 0 0 ", +"a a a a a ", +"a a a a a ", +"b b b b b ", +"c c c c c ", +"d d d d d ", +"d d d d d ", +"e e e e e ", +"f f f f f ", +"g g g g g ", +"g g g g g ", +"h h h h h ", +"i i i i i ", +"j j j j j ", +"k k k k k ", +"l l l l l ", +"m m m m m ", +"n n n n n ", +"n n n n n ", +"o o o o o ", +"p p p p p ", +"q q q q q ", +"q q q q q ", +"r r r r r ", +"s s s s s ", +"t t t t t ", +"u u u u u ", +"v v v v v ", +"w w w w w ", +"x x x x x ", +"x x x x x ", +"y y y y y ", +"z z z z z ", +"A A A A A ", +"A A A A A ", +"B B B B B ", +"C C C C C ", +"D D D D D ", +"D D D D D ", +"E E E E E ", +"F F F F F ", +"G G G G G ", +"G G G G G ", +"H H H H H ", +"I I I I I ", +"I I I I I ", +"J J J J J ", +"K K K K K ", +"L L L L L ", +"M M M M M ", +"N N N N N ", +"O O O O O ", +"P P P P P ", +"P P P P P ", +"Q Q Q Q Q ", +"R R R R R ", +"S S S S S ", +"S S S S S ", +"T T T T T ", +"U U U U U ", +"V V V V V ", +"V V V V V ", +"W W W W W ", +"X X X X X ", +"Y Y Y Y Y ", +"Y Y Y Y Y ", +"Z Z Z Z Z ", +"` ` ` ` ` ", +" . . . . .", +"..........", +"+.+.+.+.+.", +"@.@.@.@.@.", +"#.#.#.#.#.", +"#.#.#.#.#.", +"$.$.$.$.$.", +"%.%.%.%.%.", +"&.&.&.&.&.", +"&.&.&.&.&.", +"*.*.*.*.*.", +"=.=.=.=.=.", +"-.-.-.-.-.", +";.;.;.;.;.", +";.;.;.;.;.", +">.>.>.>.>.", +",.,.,.,.,.", +"'.'.'.'.'.", +").).).).).", +"!.!.!.!.!.", +"~.~.~.~.~.", +"{.{.{.{.{.", +"].].].].].", +"^.^.^.^.^.", +"/././././.", +"(.(.(.(.(.", +"_._._._._.", +":.:.:.:.:.", +"<.<.<.<.<.", +"[.[.[.[.[.", +"}.}.}.}.}.", +"|.|.|.|.|.", +"1.1.1.1.1.", +"2.2.2.2.2.", +"3.3.3.3.3.", +"4.4.4.4.4.", +"5.5.5.5.5.", +"6.6.6.6.6.", +"7.7.7.7.7.", +"8.8.8.8.8.", +"9.9.9.9.9.", +"0.0.0.0.0.", +"a.a.a.a.a.", +"b.b.b.b.b.", +"c.c.c.c.c.", +"d.d.d.d.d.", +"e.e.e.e.e.", +"f.f.f.f.f.", +"g.g.g.g.g.", +"h.h.h.h.h.", +"i.i.i.i.i.", +"j.j.j.j.j.", +"k.k.k.k.k.", +"l.l.l.l.l.", +"m.m.m.m.m.", +"n.n.n.n.n.", +"o.o.o.o.o.", +"p.p.p.p.p.", +"q.q.q.q.q.", +"r.r.r.r.r.", +"s.s.s.s.s.", +"t.t.t.t.t.", +"u.u.u.u.u.", +"v.v.v.v.v.", +"w.w.w.w.w.", +"x.x.x.x.x.", +"y.y.y.y.y.", +"z.z.z.z.z.", +"A.A.A.A.A.", +"B.B.B.B.B.", +"C.C.C.C.C.", +"D.D.D.D.D.", +"E.E.E.E.E.", +"F.F.F.F.F.", +"G.G.G.G.G.", +"H.H.H.H.H.", +"I.I.I.I.I.", +"J.J.J.J.J.", +"K.K.K.K.K.", +"L.L.L.L.L.", +"M.M.M.M.M.", +"N.N.N.N.N.", +"O.O.O.O.O.", +"P.P.P.P.P.", +"Q.Q.Q.Q.Q.", +"R.R.R.R.R.", +"S.S.S.S.S.", +"T.T.T.T.T.", +"U.U.U.U.U.", +"V.V.V.V.V.", +"W.W.W.W.W.", +"X.X.X.X.X.", +"Y.Y.Y.Y.Y.", +"Z.Z.Z.Z.Z.", +"`.`.`.`.`.", +" + + + + +", +".+.+.+.+.+", +"++++++++++", +"@+@+@+@+@+", +"#+#+#+#+#+", +"$+$+$+$+$+", +"%+%+%+%+%+", +"&+&+&+&+&+", +"*+*+*+*+*+", +"=+=+=+=+=+", +"-+-+-+-+-+", +";+;+;+;+;+", +">+>+>+>+>+", +",+,+,+,+,+", +"'+'+'+'+'+", +")+)+)+)+)+", +"!+!+!+!+!+", +"~+~+~+~+~+", +"{+{+{+{+{+", +"]+]+]+]+]+", +"^+^+^+^+^+", +"/+/+/+/+/+", +"(+(+(+(+(+", +"_+_+_+_+_+", +":+:+:+:+:+", +"<+<+<+<+<+", +"[+[+[+[+[+", +"}+}+}+}+}+", +"|+|+|+|+|+", +"1+1+1+1+1+", +"2+2+2+2+2+", +"3+3+3+3+3+", +"4+4+4+4+4+", +"5+5+5+5+5+", +"6+6+6+6+6+", +"7+7+7+7+7+", +"8+8+8+8+8+", +"9+9+9+9+9+", +"0+0+0+0+0+", +"a+a+a+a+a+", +"b+b+b+b+b+", +"c+c+c+c+c+", +"d+d+d+d+d+", +"e+e+e+e+e+", +"f+f+f+f+f+", +"g+g+g+g+g+"}; + +static const char* channel_setup_names[] = { + "mono", + "stereo", + "3 channels", + "4 channels", + "5 channels", + "8 channels", + "manual setup", + 0 +}; + +vector<string> channel_combo_strings; + +ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], string rcfile) + + : Gtkmmext::UI ("ardour", argcp, argvp, rcfile), + + primary_clock (X_("TransportClockDisplay"), true, false, true), + secondary_clock (X_("SecondaryClockDisplay"), true, false, true), + preroll_clock (X_("PreRollClock"), true, true), + postroll_clock (X_("PostRollClock"), true, true), + + /* adjuster table */ + + adjuster_table (3, 3), + + /* preroll stuff */ + + preroll_button (_("pre\nroll")), + postroll_button (_("post\nroll")), + + /* big clock */ + + big_clock ("BigClockDisplay", true), + + /* transport */ + + shuttle_units_button (_("% ")), + shuttle_style_button (_("spring")), + + punch_in_button (_("punch\nin")), + punch_out_button (_("punch\nout")), + auto_return_button (_("auto\nreturn")), + auto_play_button (_("auto\nplay")), + auto_input_button (_("auto\ninput")), + click_button (_("click")), + follow_button (_("follow\nPH")), + auditioning_alert_button (_("AUDITIONING")), + solo_alert_button (_("SOLO")), + + session_selector (1, 0), + + shown_flag (false) + +{ + using namespace Gtk::Menu_Helpers; + + Gtkmmext::init(); + + /* actually, its already loaded, but ... */ + + cerr << "Loading UI configuration file " << rcfile << endl; + + about = 0; + + if (theArdourUI == 0) { + theArdourUI = this; + } + + editor = 0; + mixer = 0; + session = 0; + _session_is_new = false; + big_clock_window = 0; + session_selector_window = 0; + last_key_press_time = 0; + connection_editor = 0; + add_route_dialog = 0; + route_params = 0; + meter_bridge = 0; + option_editor = 0; + location_ui = 0; + sfdb_window = 0; + new_session_window = 0; + open_session_selector = 0; + have_configure_timeout = false; + have_disk_overrun_displayed = false; + have_disk_underrun_displayed = false; + _will_create_new_session_automatically = false; + session_loaded = false; + + last_configure_time.tv_sec = 0; + last_configure_time.tv_usec = 0; + + shuttle_grabbed = false; + shuttle_fract = 0.0; + + set_shuttle_units (Percentage); + set_shuttle_behaviour (Sprung); + + shuttle_unit_menu.items().push_back (MenuElem (_("Percentage"), bind (slot (*this, &ARDOUR_UI::set_shuttle_units), + Percentage))); + shuttle_unit_menu.items().push_back (MenuElem (_("Semitones"), bind (slot (*this, &ARDOUR_UI::set_shuttle_units), + Semitones))); + + shuttle_style_menu.items().push_back (MenuElem (_("Sprung"), bind (slot (*this, &ARDOUR_UI::set_shuttle_behaviour), + Sprung))); + shuttle_style_menu.items().push_back (MenuElem (_("Wheel"), bind (slot (*this, &ARDOUR_UI::set_shuttle_behaviour), + Wheel))); + + gettimeofday (&last_peak_grab, 0); + gettimeofday (&last_shuttle_request, 0); + + ARDOUR::DiskStream::CannotRecordNoInput.connect (slot (*this, &ARDOUR_UI::cannot_record_no_input)); + ARDOUR::DiskStream::DeleteSources.connect (slot (*this, &ARDOUR_UI::delete_sources_in_the_right_thread)); + ARDOUR::DiskStream::DiskOverrun.connect (slot (*this, &ARDOUR_UI::disk_overrun_handler)); + ARDOUR::DiskStream::DiskUnderrun.connect (slot (*this, &ARDOUR_UI::disk_underrun_handler)); + + /* handle pending state with a dialog */ + + ARDOUR::Session::AskAboutPendingState.connect (slot (*this, &ARDOUR_UI::pending_state_dialog)); + + channel_combo_strings = internationalize (channel_setup_names); + + /* have to wait for AudioEngine and Configuration before proceeding */ +} + +void +ARDOUR_UI::cannot_record_no_input (DiskStream* ds) +{ + ENSURE_GUI_THREAD (bind (slot (*this, &ARDOUR_UI::cannot_record_no_input), ds)); + + string msg = compose (_("\ +You cannot record-enable\n\ +track %1\n\ +because it has no input connections.\n\ +You would be wasting space recording silence."), + ds->name()); + + ArdourMessage message (editor, X_("cannotrecord"), msg); +} + +void +ARDOUR_UI::set_engine (AudioEngine& e) +{ + engine = &e; + + engine->Stopped.connect (slot (*this, &ARDOUR_UI::engine_stopped)); + engine->Running.connect (slot (*this, &ARDOUR_UI::engine_running)); + engine->Halted.connect (slot (*this, &ARDOUR_UI::engine_halted)); + engine->SampleRateChanged.connect (slot (*this, &ARDOUR_UI::update_sample_rate)); + + _tooltips.enable(); + + keyboard = new Keyboard; + install_keybindings (); + + FastMeter::set_vertical_xpm (v_meter_strip_xpm); + FastMeter::set_horizontal_xpm (h_meter_strip_xpm); + + if (setup_windows ()) { + throw failed_constructor (); + } + + if (GTK_ARDOUR::show_key_actions) { + KeyboardTarget::show_all_actions (); + exit (0); + } + + /* start with timecode, metering enabled + */ + + blink_timeout_tag = -1; + + /* this being a GUI and all, we want peakfiles */ + + FileSource::set_build_peakfiles (true); + FileSource::set_build_missing_peakfiles (true); + + if (Source::start_peak_thread ()) { + throw failed_constructor(); + } + + /* start the time-of-day-clock */ + + update_wall_clock (); + Main::timeout.connect (slot (*this, &ARDOUR_UI::update_wall_clock), 60000); + + update_disk_space (); + update_cpu_load (); + update_sample_rate (engine->frame_rate()); + + starting.connect (slot (*this, &ARDOUR_UI::startup)); + stopping.connect (slot (*this, &ARDOUR_UI::shutdown)); +} + +ARDOUR_UI::~ARDOUR_UI () +{ + save_ardour_state (); + + if (keyboard) { + delete keyboard; + } + + if (editor) { + delete editor; + } + + if (mixer) { + delete mixer; + } + + if (add_route_dialog) { + delete add_route_dialog; + } + + Source::stop_peak_thread (); +} + +gint +ARDOUR_UI::configure_timeout () +{ + struct timeval now; + struct timeval diff; + + if (last_configure_time.tv_sec == 0 && last_configure_time.tv_usec == 0) { + /* no configure events yet */ + return TRUE; + } + + gettimeofday (&now, 0); + timersub (&now, &last_configure_time, &diff); + + /* force a gap of 0.5 seconds since the last configure event + */ + + if (diff.tv_sec == 0 && diff.tv_usec < 500000) { + return TRUE; + } else { + have_configure_timeout = false; + save_ardour_state (); + return FALSE; + } +} + +gboolean +ARDOUR_UI::configure_handler (GdkEventConfigure* conf) +{ + if (have_configure_timeout) { + gettimeofday (&last_configure_time, 0); + } else { + TimeoutSig t; + t.connect (slot (*this, &ARDOUR_UI::configure_timeout), 100); + have_configure_timeout = true; + } + + return FALSE; +} + +void +ARDOUR_UI::save_ardour_state () +{ + if (!keyboard || !mixer || !editor) { + return; + } + + /* XXX this is all a bit dubious. add_extra_xml() uses + a different lifetime model from add_instant_xml(). + */ + + XMLNode* node = new XMLNode (keyboard->get_state()); + Config->add_extra_xml (*node); + Config->save_state(); + + XMLNode& enode (static_cast<Stateful*>(editor)->get_state()); + XMLNode& mnode (mixer->get_state()); + + if (session) { + session->add_instant_xml(enode, session->path()); + session->add_instant_xml(mnode, session->path()); + } else { + Config->add_instant_xml(enode, Config->get_user_ardour_path()); + Config->add_instant_xml(mnode, Config->get_user_ardour_path()); + } +} + +void +ARDOUR_UI::startup () +{ + /* Once the UI is up and running, start the audio engine. Doing + this before the UI is up and running can cause problems + when not running with SCHED_FIFO, because the amount of + CPU and disk work needed to get the UI started can interfere + with the scheduling of the audio thread. + */ + + Gtk::Main::idle.connect (slot (*this, &ARDOUR_UI::start_engine)); +} + +void +ARDOUR_UI::finish() +{ + if (session && session->dirty()) { + switch (ask_about_saving_session(_("quit"))) { + case -1: + return; + break; + case 1: + /* use the default name */ + if (save_state_canfail ("")) { + /* failed - don't quit */ + ArdourMessage (editor, X_("badsave dialog"), + _("\ +Ardour was unable to save your session.\n\n\ +If you still wish to quit, please use the\n\n\ +\"Just quit\" option.")); + return; + } + break; + case 0: + break; + } + } + + quit(); +} + +int +ARDOUR_UI::ask_about_saving_session (string what) +{ + ArdourDialog window ("saving dialog"); + Gtk::VBox packer; + Gtk::Label prompt_label; + Gtk::HBox button_packer; + + string msg; + + msg = compose(_("Save and %1"), what); + + Gtk::Button save_button (msg); + save_button.set_name ("EditorGTKButton"); + + msg = compose(_("Just %1"), what); + + Gtk::Button nosave_button (msg); + nosave_button.set_name ("EditorGTKButton"); + + msg = compose(_("Don't %1"), what); + + Gtk::Button noquit_button (msg); + noquit_button.set_name ("EditorGTKButton"); + + string prompt; + string type; + + if (session->snap_name() == session->name()) { + type = _("session"); + } else { + type = _("snapshot"); + } + prompt = compose(_("The %1\n\"%2\"\nhas not been saved.\n\nAny changes made this time\nwill be lost unless you save it.\n\nWhat do you want to do?"), + type, session->snap_name()); + + prompt_label.set_text (prompt); + prompt_label.set_alignment (0.5, 0.5); + prompt_label.set_name (X_("PrompterLabel")); + + save_button.clicked.connect (bind(slot(window,&ArdourDialog::stop), 1)); + nosave_button.clicked.connect (bind(slot(window,&ArdourDialog::stop), 0)); + noquit_button.clicked.connect (bind(slot(window,&ArdourDialog::stop), -1)); + + button_packer.set_spacing (10); + button_packer.pack_start (save_button); + button_packer.pack_start (nosave_button); + button_packer.pack_start (noquit_button); + + packer.set_spacing (10); + packer.set_border_width (10); + packer.pack_start (prompt_label); + packer.pack_start (button_packer); + + window.set_name (_("Prompter")); + window.set_title (_("ardour: save session?")); + window.set_position (GTK_WIN_POS_MOUSE); + window.set_modal (true); + window.add (packer); + window.show_all (); + + window.realize(); + window.get_window().set_decorations (GdkWMDecoration (GDK_DECOR_BORDER|GDK_DECOR_RESIZEH)); + window.set_keyboard_input (true); + + save_the_session = 0; + + editor->ensure_float (window); + + window.run (); + + return window.run_status(); +} + +gint +ARDOUR_UI::every_second () +{ + update_cpu_load (); + update_buffer_load (); + update_disk_space (); + // update_disk_rate (); + return TRUE; +} + +gint +ARDOUR_UI::every_point_one_seconds () +{ + struct timeval now; + struct timeval diff; + + /* do not attempt to grab peak power more than once per cycle. + */ + + gettimeofday (&now, 0); + timersub (&now, &last_peak_grab, &diff); + + if ((diff.tv_usec + (diff.tv_sec * 1000000)) >= engine->usecs_per_cycle()) { + IO::GrabPeakPower(); /* EMIT_SIGNAL */ + last_peak_grab = now; + } + + update_speed_display (); + RapidScreenUpdate(); /* EMIT_SIGNAL */ + return TRUE; +} + +gint +ARDOUR_UI::every_point_zero_one_seconds () +{ + SuperRapidScreenUpdate(); /* EMIT_SIGNAL */ + return TRUE; +} + +void +ARDOUR_UI::update_sample_rate (jack_nframes_t ignored) +{ + char buf[32]; + + ENSURE_GUI_THREAD (bind (slot (*this, &ARDOUR_UI::update_sample_rate), ignored)); + + if (!engine->connected()) { + + snprintf (buf, sizeof (buf), _("disconnected")); + + } else { + + jack_nframes_t rate = engine->frame_rate(); + + if (fmod (rate, 1000.0) != 0.0) { + snprintf (buf, sizeof (buf), _("SR: %.1f kHz / %4.1f msecs"), + (float) rate/1000.0f, + (engine->frames_per_cycle() / (float) rate) * 1000.0f); + } else { + snprintf (buf, sizeof (buf), _("SR: %u kHz / %4.1f msecs"), + rate/1000, + (engine->frames_per_cycle() / (float) rate) * 1000.0f); + } + } + + sample_rate_label.set_text (buf); +} + +void +ARDOUR_UI::update_cpu_load () +{ + char buf[32]; + snprintf (buf, sizeof (buf), _("DSP Load: %.1f%%"), engine->get_cpu_load()); + cpu_load_label.set_text (buf); +} + +void +ARDOUR_UI::update_disk_rate () +{ + char buf[64]; + + if (session) { + snprintf (buf, sizeof (buf), _("Disk r:%5.1f w:%5.1f MB/s"), + session->read_data_rate()/1048576.0f, session->write_data_rate()/1048576.0f); + disk_rate_label.set_text (buf); + } else { + disk_rate_label.set_text (""); + } +} + +void +ARDOUR_UI::update_buffer_load () +{ + char buf[64]; + + if (session) { + snprintf (buf, sizeof (buf), _("Buffers p:%" PRIu32 "%% c:%" PRIu32 "%%"), + session->playback_load(), session->capture_load()); + buffer_load_label.set_text (buf); + } else { + buffer_load_label.set_text (""); + } +} + +void +ARDOUR_UI::count_recenabled_diskstreams (DiskStream& ds) +{ + if (ds.record_enabled()) { + rec_enabled_diskstreams++; + } +} + +void +ARDOUR_UI::update_disk_space() +{ + if (session == 0) { + return; + } + + jack_nframes_t frames = session->available_capture_duration(); + char buf[64]; + + if (frames == max_frames) { + strcpy (buf, _("space: 24hrs+")); + } else { + int hrs; + int mins; + int secs; + jack_nframes_t fr = session->frame_rate(); + + if (session->actively_recording()){ + + rec_enabled_diskstreams = 0; + session->foreach_diskstream (this, &ARDOUR_UI::count_recenabled_diskstreams); + + if (rec_enabled_diskstreams) { + frames /= rec_enabled_diskstreams; + } + + } else { + + /* hmmm. shall we divide by the route count? or the diskstream count? + or what? for now, do nothing ... + */ + + } + + hrs = frames / (fr * 3600); + frames -= hrs * fr * 3600; + mins = frames / (fr * 60); + frames -= mins * fr * 60; + secs = frames / fr; + + snprintf (buf, sizeof(buf), _("space: %02dh:%02dm:%02ds"), hrs, mins, secs); + } + + disk_space_label.set_text (buf); +} + +gint +ARDOUR_UI::update_wall_clock () +{ + time_t now; + struct tm *tm_now; + char buf[16]; + + time (&now); + tm_now = localtime (&now); + + sprintf (buf, "%02d:%02d", tm_now->tm_hour, tm_now->tm_min); + wall_clock_label.set_text (buf); + + return TRUE; +} + +void +ARDOUR_UI::toggle_recording_plugins () +{ + /* XXX use toggle_some_session_state */ + + if (session == 0) { + return; + } + + session->set_recording_plugins (!session->get_recording_plugins()); +} + +void +ARDOUR_UI::toggle_auto_play () + +{ + toggle_some_session_state (auto_play_button, + &Session::get_auto_play, + &Session::set_auto_play); +} + +void +ARDOUR_UI::toggle_auto_return () + +{ + toggle_some_session_state (auto_return_button, + &Session::get_auto_return, + &Session::set_auto_return); +} + +void +ARDOUR_UI::toggle_click () +{ + toggle_some_session_state (click_button, + &Session::get_clicking, + &Session::set_clicking); +} + +void +ARDOUR_UI::follow_changed () +{ + bool x; + + if (!editor) { + return; + } + + if (follow_button.get_active() != (x = editor->follow_playhead())) { + follow_button.set_active (x); + } +} + +void +ARDOUR_UI::toggle_follow () +{ + bool x; + + if (!editor) { + return; + } + + if (editor->follow_playhead() != (x = follow_button.get_active())) { + editor->set_follow_playhead (x); + } +} + +void +ARDOUR_UI::toggle_session_auto_loop () +{ + if (session) { + if (session->get_auto_loop()) { + if (session->transport_rolling()) { + transport_roll(); + } + else { + session->request_auto_loop (false); + } + } + else { + session->request_auto_loop (true); + } + } +} + +void +ARDOUR_UI::toggle_session_punch_in () +{ + if (session) { + session->set_punch_in (!session->get_punch_in()); + } +} + +void +ARDOUR_UI::toggle_punch_out () +{ + toggle_some_session_state (punch_out_button, + &Session::get_punch_out, + &Session::set_punch_out); +} + +void +ARDOUR_UI::toggle_punch_in () +{ + toggle_some_session_state (punch_in_button, + &Session::get_punch_in, + &Session::set_punch_in); +} + +void +ARDOUR_UI::map_button_state () + +{ + map_some_session_state (auto_return_button, + &Session::get_auto_return); + map_some_session_state (auto_play_button, + &Session::get_auto_play); + map_some_session_state (auto_input_button, + &Session::get_auto_input); + map_some_session_state (punch_in_button, + &Session::get_punch_in); + map_some_session_state (punch_out_button, + &Session::get_punch_out); + map_some_session_state (click_button, + &Session::get_clicking); +} + +void +ARDOUR_UI::queue_map_control_change (Session::ControlType t) +{ + ENSURE_GUI_THREAD (bind (slot (*this, &ARDOUR_UI::map_control_change), t)); +} + +void +ARDOUR_UI::map_control_change (Session::ControlType t) +{ + switch (t) { + case Session::AutoPlay: + map_some_session_state (auto_play_button, &Session::get_auto_play); + break; + + case Session::AutoLoop: + break; + + case Session::AutoReturn: + map_some_session_state (auto_return_button, &Session::get_auto_return); + break; + + case Session::AutoInput: + map_some_session_state (auto_input_button, &Session::get_auto_input); + break; + + case Session::PunchOut: + map_some_session_state (punch_in_button, &Session::get_punch_out); + break; + + case Session::PunchIn: + map_some_session_state (punch_in_button, &Session::get_punch_in); + break; + + case Session::Clicking: + map_some_session_state (click_button, &Session::get_clicking); + break; + + case Session::SlaveType: +// map_some_session_state (mtc_slave_button, &Session::get_mtc_slave); + break; + + case Session::SendMTC: +// map_some_session_state (send_mtc_button, &Session::get_send_mtc); + break; + + case Session::SendMMC: +// map_some_session_state (send_mmc_button, &Session::get_send_mmc); + break; + + case Session::MMCControl: +// map_some_session_state (mmc_control_button, &Session::get_mmc_control); + break; + + case Session::MidiFeedback: +// map_some_session_state (mmc_control_button, &Session::get_mmc_control); + break; + case Session::MidiControl: +// map_some_session_state (mmc_control_button, &Session::get_mmc_control); + break; + + case Session::Live: + break; + + case Session::RecordingPlugins: + break; + + case Session::CrossFadesActive: + break; + + case Session::EditingMode: + break; + + case Session::PlayRange: + break; + + case Session::AlignChoice: + /* don't care, this is handled by the options editor */ + break; + case Session::SeamlessLoop: + /* don't care, this is handled by the options editor */ + break; + + } +} + +void +ARDOUR_UI::control_methods_adjusted () + +{ + int which_method; + + which_method = (int) online_control_button->adjustment.get_value(); + switch (which_method) { + case 0: + allow_mmc_and_local (); + break; + case 1: + allow_mmc_only (); + break; + case 2: + allow_local_only (); + break; + default: + fatal << _("programming error: impossible control method") << endmsg; + } +} + + +void +ARDOUR_UI::mmc_device_id_adjusted () + +{ +#if 0 + if (mmc) { + int dev_id = (int) mmc_id_button->adjustment.get_value(); + mmc->set_device_id (dev_id); + } +#endif +} + +void +ARDOUR_UI::map_some_session_state (ToggleButton& button, + bool (Session::*get)() const) + +{ + bool x; + + if (session == 0) { + return; + } + + if (button.get_active() != (x = (session->*get)())) { + button.set_active (x); + } +} + +void +ARDOUR_UI::toggle_some_session_state (ToggleButton& button, + bool (Session::*get)() const, + void (Session::*set)(bool)) + +{ + bool button_state; + bool session_state; + + if (session == 0) { + return; + } + + button_state = button.get_active (); + session_state = (session->*get)(); + + if (button_state != session_state) { + (session->*set) (button_state); +#if 0 + + /* check that it worked, and reverse + the button state if it didn't + */ + + if ((session->*get)() != button_state) { + button->set_active (!button_state); + } +#endif + + } +} + +gint +ARDOUR_UI::session_menu (GdkEventButton *ev) +{ + session_popup_menu->popup (0, 0, 0, 0); + return TRUE; +} + +void +ARDOUR_UI::redisplay_recent_sessions () +{ + using namespace Gtkmmext; + using namespace Gtk::CTree_Helpers; + + vector<string *> *sessions; + vector<string *>::iterator i; + RecentSessionsSorter cmp; + + /* ---------------------------------------- */ + /* XXX MAKE ME A FUNCTION (no CTree::clear() in gtkmm 1.2) */ + + gtk_ctree_remove_node (session_selector.gtkobj(), NULL); + /* ---------------------------------------- */ + + + RecentSessions rs; + ARDOUR::read_recent_sessions (rs); + + if (rs.empty()) { + session_selector.thaw(); + return; + } + /* sort them alphabetically */ + sort(rs.begin(), rs.end(), cmp); + sessions = new vector<string*>; + for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) { + sessions->push_back (new string ((*i).second)); + } + + session_selector.freeze(); + + for (i = sessions->begin(); i != sessions->end(); ++i) { + + vector<string*>* states; + vector<const gchar*> item; + string fullpath = *(*i); + + /* remove any trailing / */ + + if (fullpath[fullpath.length()-1] == '/') { + fullpath = fullpath.substr (0, fullpath.length()-1); + } + + /* now get available states for this session */ + + if ((states = Session::possible_states(fullpath)) == 0) { + /* no state file? */ + continue; + } + + /* OK, try to add entries for this session */ + + + /* add the parent */ + + item.clear (); + string basen = PBD::basename (fullpath); + item.push_back (basen.c_str()); + session_selector.rows().push_back (Element (item)); + + session_selector.rows().back().set_data (new string (fullpath), deferred_delete<string>); + + if (states->size() == 1) { + + /* only 1 state, show it at the top level */ + + session_selector.rows().back().set_leaf (true); + + } else { + + session_selector.rows().back().set_leaf (false); + + vector<string *>::iterator i2; + + /* add the children */ + + for (i2 = states->begin(); i2 != states->end(); ++i2) { + + string statename = *(*i2); + + item.clear (); + item.push_back (statename.c_str()); + + session_selector.rows().back().subtree().push_back (Element (item)); + session_selector.rows().back().subtree().back().set_data (new string (statename), + deferred_delete<string>); + session_selector.rows().back().subtree().back().set_leaf (true); + + delete *i2; + } + } + + delete states; + } + + session_selector.thaw(); + delete sessions; +} + +void +ARDOUR_UI::session_selection (Gtk::CTree_Helpers::Row row, gint column) +{ + using namespace Gtk::CTree_Helpers; + + string session_name; + string session_path; + string session_state; + + if (!row.is_leaf()) { + row.expand(); + return; + } + + string *stp = static_cast<string *> (row.get_data()); + + if ((*stp)[0] != '/' && (*stp)[0] != '.') { + + /* its a state file node, so get the parent for the session information, + and combine with the state file name. + */ + + string *spp = static_cast<string *> (row.get_parent().get_data()); + + session_name = *spp; + session_path = *spp; + session_state = *stp; + + } else { + + /* its a session directory node, so just get the session path, + and use "default" to load the state. + */ + + string *spp = static_cast<string *> (row.get_data()); + + session_name = *spp; + session_path = *spp; + session_state = PBD::basename (*spp); + } + + session_selector_window->hide (); + _session_is_new = false; + load_session (session_path, session_state); +} + +void +ARDOUR_UI::build_session_selector () +{ + session_selector_window = new ArdourDialog ("session selector"); + + Gtk::VBox *vpacker = new Gtk::VBox; + Gtk::ScrolledWindow *scroller = new Gtk::ScrolledWindow; + Gtk::HBox *button_packer = new Gtk::HBox; + Gtk::Button *cancel_button = new Gtk::Button (_("cancel")); + Gtk::Button *rescan_button = new Gtk::Button (_("rescan")); + + button_packer->pack_start (*rescan_button); + button_packer->pack_start (*cancel_button); + + vpacker->pack_start (*scroller); + vpacker->pack_start (*button_packer, false, false); + + scroller->add (session_selector); + scroller->set_policy(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + session_selector_window->add (*vpacker); + session_selector_window->set_name ("SessionSelectorWindow"); + session_selector_window->set_usize (200, 400); + + session_selector_window->delete_event.connect (bind (slot (just_hide_it), static_cast<Gtk::Window*>(session_selector_window))); + cancel_button->clicked.connect (bind (slot (*this, &ARDOUR_UI::hide_dialog), session_selector_window)); + session_selector.tree_select_row.connect (slot (*this, &ARDOUR_UI::session_selection)); +} + +void +ARDOUR_UI::fs_cancel_clicked (Gtk::FileSelection* fs) +{ + fs->hide_all(); + fs->get_selection_entry()->set_text(""); + allow_focus (false); +} + +gint +ARDOUR_UI::fs_delete_event (GdkEventAny* ev, Gtk::FileSelection* fs) +{ + fs_cancel_clicked (fs); + return 1; +} + +void +ARDOUR_UI::open_session () +{ + /* popup selector window */ + + if (open_session_selector == 0) { + open_session_selector = new Gtk::FileSelection(_("open session")); + open_session_selector->get_ok_button()->clicked.connect (slot (*this, &ARDOUR_UI::open_ok_clicked)); + open_session_selector->get_cancel_button()->clicked.connect (bind (slot (*this, &ARDOUR_UI::fs_cancel_clicked), open_session_selector)); + open_session_selector->delete_event.connect (bind (slot (*this, &ARDOUR_UI::fs_delete_event), open_session_selector)); + } + + open_session_selector->show_all (); + allow_focus (true); + + /* wait for selection */ +} + +void +ARDOUR_UI::open_ok_clicked () +{ + open_session_selector->hide_all(); + string session_path = open_session_selector->get_filename(); + string path, name; + bool isnew; + + if (session_path.length() > 0) { + if (Session::find_session (session_path, path, name, isnew) == 0) { + _session_is_new = isnew; + load_session (path, name); + } + } + + open_session_selector->get_selection_entry()->set_text(""); + + /* XXX hack hack hack */ + + GtkCList* clist = (GtkCList*) open_session_selector->gtkobj()->file_list; + gtk_clist_unselect_all (clist); + + allow_focus(false); +} + +void +ARDOUR_UI::open_recent_session () +{ + /* popup selector window */ + + if (session_selector_window == 0) { + build_session_selector (); + } + + redisplay_recent_sessions (); + session_selector_window->show_all (); + + /* wait for selection */ +} + +void +ARDOUR_UI::session_add_midi_track () +{ + cerr << _("Patience is a virtue.\n"); +} + +void +ARDOUR_UI::session_add_audio_route (bool disk, int32_t input_channels, int32_t output_channels) +{ + Route* route; + + if (session == 0) { + warning << _("You cannot add a track without a session already loaded.") << endmsg; + return; + } + + try { + if (disk) { + if ((route = session->new_audio_track (input_channels, output_channels)) == 0) { + error << _("could not create new audio track") << endmsg; + } + } else { + if ((route = session->new_audio_route (input_channels, output_channels)) == 0) { + error << _("could not create new audio bus") << endmsg; + } + } + +#if CONTROLOUTS + if (need_control_room_outs) { + pan_t pans[2]; + + pans[0] = 0.5; + pans[1] = 0.5; + + route->set_stereo_control_outs (control_lr_channels); + route->control_outs()->set_stereo_pan (pans, this); + } +#endif /* CONTROLOUTS */ + } + + catch (...) { + ArdourMessage msg (editor, X_("noport dialog"), + _("There are insufficient JACK ports available\n\ +to create a new track or bus.\n\ +You should save Ardour, exit and\n\ +restart JACK with more ports.")); + } +} + +void +ARDOUR_UI::diskstream_added (DiskStream* ds) +{ + // meter_bridge_dialog_check->set_sensitive (true); +} + +void +ARDOUR_UI::do_transport_locate (jack_nframes_t new_position) +{ + jack_nframes_t _preroll; + + if (session) { + _preroll = session->convert_to_frames_at (new_position, session->preroll); + + if (new_position > _preroll) { + new_position -= _preroll; + } else { + new_position = 0; + } + + session->request_locate (new_position); + } +} + +void +ARDOUR_UI::transport_goto_start () +{ + if (session) { + session->request_locate (0); + + + /* force displayed area in editor to start no matter + what "follow playhead" setting is. + */ + + if (editor) { + editor->reposition_x_origin (0); + } + } +} + +void +ARDOUR_UI::transport_goto_end () +{ + if (session) { + jack_nframes_t frame = session->current_end_frame(); + session->request_locate (frame); + + /* force displayed area in editor to start no matter + what "follow playhead" setting is. + */ + + if (editor) { + editor->reposition_x_origin (frame); + } + } +} + +gint +ARDOUR_UI::mouse_transport_stop (GdkEventButton *ev) +{ + + + if (session) { + if (session->transport_stopped()) { + session->request_locate (session->last_transport_start()); + } else { + if (session->get_auto_loop()) { + session->request_auto_loop (false); + } + + Keyboard::ModifierMask mask = Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift); + session->request_stop (Keyboard::modifier_state_equals (ev->state, mask)); + } + } + + return TRUE; +} + +gint +ARDOUR_UI::mouse_transport_roll (GdkEventButton* ev) +{ + transport_roll (); + return TRUE; +} + +void +ARDOUR_UI::transport_stop () +{ + if (!session) { + return; + } + + if (session->is_auditioning()) { + session->cancel_audition (); + return; + } + + if (session->get_auto_loop()) { + session->request_auto_loop (false); + } + + session->request_stop (); +} + +void +ARDOUR_UI::transport_stop_and_forget_capture () +{ + if (session) { + session->request_stop (true); + } +} + +void +ARDOUR_UI::remove_last_capture() +{ + if (editor) { + editor->remove_last_capture(); + } +} + +void +ARDOUR_UI::transport_record () +{ + if (session) { + switch (session->record_status()) { + case Session::Disabled: + if (session->ntracks() == 0) { + string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu."); + ArdourMessage msg (editor, X_("cannotrecenable"), txt); + return; + } + session->maybe_enable_record (); + break; + case Session::Recording: + case Session::Enabled: + session->disable_record (); + } + } +} + +void +ARDOUR_UI::transport_roll () +{ + bool rolling; + + if (!session) { + return; + } + + rolling = session->transport_rolling (); + + if (session->get_auto_loop()) { + session->request_auto_loop (false); + auto_loop_button.set_active (false); + roll_button.set_active (true); + } else if (session->get_play_range ()) { + session->request_play_range (false); + play_selection_button.set_active (false); + } else if (rolling) { + session->request_locate (session->last_transport_start(), true); + } + + session->request_transport_speed (1.0f); +} + +void +ARDOUR_UI::transport_loop() +{ + if (session) { + if (session->get_auto_loop()) { + if (session->transport_rolling()) { + Location * looploc = session->locations()->auto_loop_location(); + if (looploc) { + session->request_locate (looploc->start(), true); + } + } + } + else { + session->request_auto_loop (true); + } + } +} + +void +ARDOUR_UI::transport_play_selection () +{ + if (!session) { + return; + } + + if (!session->get_play_range()) { + session->request_stop (); + } + + editor->play_selection (); +} + +void +ARDOUR_UI::transport_rewind (int option) +{ + float current_transport_speed; + + if (session) { + current_transport_speed = session->transport_speed(); + + if (current_transport_speed >= 0.0f) { + switch (option) { + case 0: + session->request_transport_speed (-1.0f); + break; + case 1: + session->request_transport_speed (-4.0f); + break; + case -1: + session->request_transport_speed (-0.5f); + break; + } + } else { + /* speed up */ + session->request_transport_speed (current_transport_speed * 1.5f); + } + } +} + +void +ARDOUR_UI::transport_forward (int option) +{ + float current_transport_speed; + + if (session) { + current_transport_speed = session->transport_speed(); + + if (current_transport_speed <= 0.0f) { + switch (option) { + case 0: + session->request_transport_speed (1.0f); + break; + case 1: + session->request_transport_speed (4.0f); + break; + case -1: + session->request_transport_speed (0.5f); + break; + } + } else { + /* speed up */ + session->request_transport_speed (current_transport_speed * 1.5f); + } + } +} + +void +ARDOUR_UI::toggle_monitor_enable (guint32 dstream) +{ + if (session == 0) { + return; + } + + DiskStream *ds; + + if ((ds = session->diskstream_by_id (dstream)) != 0) { + Port *port = ds->io()->input (0); + port->request_monitor_input (!port->monitoring_input()); + } +} + +void +ARDOUR_UI::toggle_record_enable (guint32 dstream) +{ + if (session == 0) { + return; + } + + DiskStream *ds; + + if ((ds = session->diskstream_by_id (dstream)) != 0) { + ds->set_record_enabled (!ds->record_enabled(), this); + } +} + +void +ARDOUR_UI::queue_transport_change () +{ + Gtkmmext::UI::instance()->call_slot (slot (*this, &ARDOUR_UI::map_transport_state)); +} + +void +ARDOUR_UI::map_transport_state () +{ + float sp = session->transport_speed(); + + if (sp == 1.0f) { + transport_rolling (); + } else if (sp < 0.0f) { + transport_rewinding (); + } else if (sp > 0.0f) { + transport_forwarding (); + } else { + transport_stopped (); + } +} + +void +ARDOUR_UI::send_all_midi_feedback () +{ + if (session) { + session->send_all_midi_feedback(); + } +} + +void +ARDOUR_UI::allow_local_only () +{ + +} + +void +ARDOUR_UI::allow_mmc_only () +{ + +} + +void +ARDOUR_UI::allow_mmc_and_local () +{ + +} + +void +ARDOUR_UI::GlobalClickBox::printer (char buf[32], Adjustment &adj, void *arg) +{ + snprintf (buf, sizeof(buf), "%s", ((GlobalClickBox *) arg)->strings[ + (int) adj.get_value()].c_str()); +} + +void +ARDOUR_UI::engine_stopped () +{ + ENSURE_GUI_THREAD (slot (*this, &ARDOUR_UI::engine_stopped)); + + jack_disconnect_item->set_sensitive (false); + jack_reconnect_item->set_sensitive (true); + jack_bufsize_menu->set_sensitive (false); +} + + +void +ARDOUR_UI::engine_running () +{ + ENSURE_GUI_THREAD (slot (*this, &ARDOUR_UI::engine_running)); + + jack_disconnect_item->set_sensitive (true); + jack_reconnect_item->set_sensitive (false); + jack_bufsize_menu->set_sensitive (true); +} + +void +ARDOUR_UI::engine_halted () +{ + ENSURE_GUI_THREAD (slot (*this, &ARDOUR_UI::engine_halted)); + + jack_disconnect_item->set_sensitive (false); + jack_reconnect_item->set_sensitive (true); + jack_bufsize_menu->set_sensitive (false); + + update_sample_rate (0); + + ArdourMessage msg (editor, X_("halted"), + _("\ +JACK has either been shutdown or it\n\ +disconnected Ardour because Ardour\n\ +was not fast enough. You can save the\n\ +session and/or try to reconnect to JACK .")); +} + +int32_t +ARDOUR_UI::do_engine_start () +{ + try { + engine->start(); + } + + catch (AudioEngine::PortRegistrationFailure& err) { + engine->stop (); + error << _("Unable to create all required ports") + << endmsg; + unload_session (); + return -1; + } + + catch (...) { + engine->stop (); + error << _("Unable to start the session running") + << endmsg; + unload_session (); + return -2; + } + + return 0; +} + +gint +ARDOUR_UI::start_engine () +{ + if (do_engine_start () == 0) { + if (session && _session_is_new) { + /* we need to retain initial visual + settings for a new session + */ + session->save_state (""); + } + + /* there is too much going on, in too many threads, for us to + end up with a clean session. So wait 1 second after loading, + and fix it up. its ugly, but until i come across a better + solution, its what we have. + */ + + Main::timeout.connect (slot (*this, &ARDOUR_UI::make_session_clean), 1000); + } + + return FALSE; +} + +void +ARDOUR_UI::update_clocks () +{ + Clock (session->audible_frame()); /* EMIT_SIGNAL */ +} + +void +ARDOUR_UI::start_clocking () +{ + clock_signal_connection = RapidScreenUpdate.connect (slot (*this, &ARDOUR_UI::update_clocks)); +} + +void +ARDOUR_UI::stop_clocking () +{ + clock_signal_connection.disconnect (); +} + +void +ARDOUR_UI::toggle_clocking () +{ +#if 0 + if (clock_button.get_active()) { + start_clocking (); + } else { + stop_clocking (); + } +#endif +} + +gint +ARDOUR_UI::_blink (void *arg) + +{ + ((ARDOUR_UI *) arg)->blink (); + return TRUE; +} + +void +ARDOUR_UI::blink () +{ + Blink (blink_on = !blink_on); /* EMIT_SIGNAL */ +} + +void +ARDOUR_UI::start_blinking () +{ + /* Start the blink signal. Everybody with a blinking widget + uses Blink to drive the widget's state. + */ + + if (blink_timeout_tag < 0) { + blink_on = false; + blink_timeout_tag = gtk_timeout_add (240, _blink, this); + } +} + +void +ARDOUR_UI::stop_blinking () +{ + if (blink_timeout_tag >= 0) { + gtk_timeout_remove (blink_timeout_tag); + blink_timeout_tag = -1; + } +} + + +void +ARDOUR_UI::add_diskstream_to_menu (DiskStream& dstream) +{ + using namespace Gtk; + using namespace Menu_Helpers; + + if (dstream.hidden()) { + return; + } + + MenuList& items = diskstream_menu->items(); + items.push_back (MenuElem (dstream.name(), bind (slot (*this, &ARDOUR_UI::diskstream_selected), (gint32) dstream.id()))); +} + +void +ARDOUR_UI::diskstream_selected (gint32 id) +{ + selected_dstream = id; + Main::quit (); +} + +gint32 +ARDOUR_UI::select_diskstream (GdkEventButton *ev) +{ + using namespace Gtk; + using namespace Menu_Helpers; + + if (session == 0) { + return -1; + } + + diskstream_menu = new Menu(); + diskstream_menu->set_name ("ArdourContextMenu"); + using namespace Gtk; + using namespace Menu_Helpers; + + MenuList& items = diskstream_menu->items(); + items.push_back (MenuElem (_("No Stream"), (bind (slot (*this, &ARDOUR_UI::diskstream_selected), -1)))); + + session->foreach_diskstream (this, &ARDOUR_UI::add_diskstream_to_menu); + + if (ev) { + diskstream_menu->popup (ev->button, ev->time); + } else { + diskstream_menu->popup (0, 0); + } + + selected_dstream = -1; + + Main::run (); + + delete diskstream_menu; + + return selected_dstream; +} + +void +ARDOUR_UI::name_io_setup (AudioEngine& engine, + string& buf, + IO& io, + bool in) +{ + if (in) { + if (io.n_inputs() == 0) { + buf = _("none"); + return; + } + + /* XXX we're not handling multiple ports yet. */ + + const char **connections = io.input(0)->get_connections(); + + if (connections == 0 || connections[0] == '\0') { + buf = _("off"); + } else { + buf = connections[0]; + } + + free (connections); + + } else { + + if (io.n_outputs() == 0) { + buf = _("none"); + return; + } + + /* XXX we're not handling multiple ports yet. */ + + const char **connections = io.output(0)->get_connections(); + + if (connections == 0 || connections[0] == '\0') { + buf = _("off"); + } else { + buf = connections[0]; + } + + free (connections); + } +} + +void +ARDOUR_UI::snapshot_session () +{ + ArdourPrompter prompter (true); + string now; + time_t n; + + time (&n); + now = ctime (&n); + now = now.substr (0, now.length() - 1); + + prompter.set_name ("Prompter"); + prompter.set_prompt (_("Name for snapshot")); + prompter.set_initial_text (now); + prompter.done.connect (Gtk::Main::quit.slot()); + prompter.show_all (); + + Gtk::Main::run (); + + if (prompter.status == Gtkmmext::Prompter::entered) { + string snapname; + + prompter.get_result (snapname); + if (snapname.length()){ + save_state (snapname); + } + } +} + +void +ARDOUR_UI::save_state (string name) +{ + (void) save_state_canfail (name); +} + +int +ARDOUR_UI::save_state_canfail (string name) +{ + if (session) { + int ret; + + if (name.length() == 0) { + name = session->snap_name(); + } + + if ((ret = session->save_state (name)) != 0) { + return ret; + } + } + save_ardour_state (); /* XXX cannot fail? yeah, right ... */ + return 0; +} + +void +ARDOUR_UI::restore_state (string name) +{ + if (session) { + if (name.length() == 0) { + name = session->name(); + } + session->restore_state (name); + } +} + +void +ARDOUR_UI::allow_focus (bool yn) +{ + if (keyboard) { + keyboard->allow_focus (yn); + } +} + +void +ARDOUR_UI::primary_clock_value_changed () +{ + if (session) { + session->request_locate (primary_clock.current_time ()); + } +} + +void +ARDOUR_UI::secondary_clock_value_changed () +{ + if (session) { + session->request_locate (secondary_clock.current_time ()); + } +} + +void +ARDOUR_UI::rec_enable_button_blink (bool onoff, DiskStream *dstream, Widget *w) +{ + if (session && dstream && dstream->record_enabled()) { + + Session::RecordState rs; + + rs = session->record_status (); + + switch (rs) { + case Session::Disabled: + case Session::Enabled: + if (w->get_state() != GTK_STATE_SELECTED) { + w->set_state (GTK_STATE_SELECTED); + } + break; + + case Session::Recording: + if (w->get_state() != GTK_STATE_ACTIVE) { + w->set_state (GTK_STATE_ACTIVE); + } + break; + } + + } else { + if (w->get_state() != GTK_STATE_NORMAL) { + w->set_state (GTK_STATE_NORMAL); + } + } +} + +void +ARDOUR_UI::transport_rec_enable_blink (bool onoff) +{ + if (session == 0) { + return; + } + + switch (session->record_status()) { + case Session::Enabled: + if (onoff) { + rec_button.set_state (GTK_STATE_ACTIVE); + } else { + rec_button.set_state (GTK_STATE_NORMAL); + } + break; + + case Session::Recording: + rec_button.set_state (GTK_STATE_ACTIVE); + break; + + default: + rec_button.set_active (false); + rec_button.set_state (GTK_STATE_NORMAL); + break; + } +} + +gint +ARDOUR_UI::generic_focus_in_event (GdkEventFocus *ev) +{ + ARDOUR_UI::instance()->allow_focus (true); + return FALSE; +} + +gint +ARDOUR_UI::generic_focus_out_event (GdkEventFocus *ev) +{ + ARDOUR_UI::instance()->allow_focus (false); + return FALSE; +} + +gint +ARDOUR_UI::hide_and_quit (GdkEventAny *ev, ArdourDialog *window) +{ + window->hide(); + Gtk::Main::quit (); + return TRUE; +} + +void +ARDOUR_UI::start_keyboard_prefix () +{ + keyboard->start_prefix(); +} + +void +ARDOUR_UI::save_template () + +{ + ArdourPrompter prompter (true); + prompter.set_name ("Prompter"); + prompter.set_prompt (_("Name for mix template:")); + prompter.set_initial_text(session->name() + _("-template")); + + prompter.done.connect(Gtk::Main::quit.slot()); + prompter.show_all(); + + Gtk::Main::run(); + + if (prompter.status == Gtkmmext::Prompter::entered) { + string name; + + prompter.get_result (name); + + if (name.length()) { + session->save_template (name); + } + } +} + +void +ARDOUR_UI::new_session (bool startup, string predetermined_path) +{ + if (new_session_window == 0){ + new_session_window = new NewSessionDialog (*engine, startup, predetermined_path); + editor->ensure_float (*new_session_window); + } + + new_session_window->run (); + + /* write favorites either way */ + Session::FavoriteDirs favs; + new_session_window->file_selector.get_favorites (favs); + Session::write_favorite_dirs (favs); + + if (new_session_window->run_status()) { + return; + } + + string session_path = new_session_window->file_selector.get_path (); + string session_name = PBD::basename (session_path); + + // Check that it doesn't already exist. + access(session_path.c_str(), R_OK); + if (errno != ENOENT){ + error << compose(_("Session %1 already exists at %2"), session_name, session_path) << endmsg; + return; + } + + _session_is_new = true; + + if (session_path[session_path.length()-1] != '/') { + + string template_name = new_session_window->get_template_name (); + + if (template_name.length()) { + + load_session (session_path, session_name, &template_name); + + } else { + + uint32_t cchns; + uint32_t mchns; + Session::AutoConnectOption iconnect; + Session::AutoConnectOption oconnect; + + if (new_session_window->use_control_button.get_active()) { + cchns = (uint32_t) channel_combo_get_channel_count (new_session_window->control_out_channel_combo); + } else { + cchns = 0; + } + if (new_session_window->use_master_button.get_active()) { + mchns = (uint32_t) channel_combo_get_channel_count (new_session_window->master_out_channel_combo); + } else { + mchns = 0; + } + + if (new_session_window->connect_to_physical_inputs_button.get_active()) { + iconnect = Session::AutoConnectPhysical; + } else { + iconnect = Session::AutoConnectOption (0); + } + + if (new_session_window->connect_to_master_button.get_active ()) { + oconnect = Session::AutoConnectMaster; + } else if (new_session_window->connect_to_physical_outputs_button.get_active ()) { + oconnect = Session::AutoConnectPhysical; + } else { + oconnect = Session::AutoConnectOption (0); + } + + uint32_t nphysin = (uint32_t) new_session_window->in_count_adjustment.get_value(); + uint32_t nphysout = (uint32_t) new_session_window->out_count_adjustment.get_value(); + + build_session (session_path, session_name, cchns, mchns, iconnect, oconnect, nphysin, nphysout, + engine->frame_rate() * 60 * 5); + } + } +} + +int +ARDOUR_UI::load_session (string path, string snap_name, string* mix_template) +{ + Session *new_session; + int x; + session_loaded = false; + x = unload_session (); + + if (x < 0) { + return -1; + } else if (x > 0) { + return 0; + } + + /* if it already exists, we must have write access */ + + if (::access (path.c_str(), F_OK) == 0 && ::access (path.c_str(), W_OK)) { + ArdourMessage msg (editor, X_("noaccess dialog"), _("\ +You do not have write access to this session.\n\ +This prevents the session from being loaded.")); + return -1; + } + + try { + new_session = new Session (*engine, path, snap_name, mix_template); + } + + catch (...) { + + error << compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name) << endmsg; + return -1; + } + + connect_to_session (new_session); + + //if (engine->running()) { + //mixer->show_window(); + //} + session_loaded = true; + return 0; +} + +int +ARDOUR_UI::make_session_clean () +{ + if (session) { + session->set_clean (); + } + + return FALSE; +} + +int +ARDOUR_UI::build_session (string path, string snap_name, + uint32_t control_channels, + uint32_t master_channels, + Session::AutoConnectOption input_connect, + Session::AutoConnectOption output_connect, + uint32_t nphysin, + uint32_t nphysout, + jack_nframes_t initial_length) +{ + Session *new_session; + int x; + + session_loaded = false; + x = unload_session (); + if (x < 0) { + return -1; + } else if (x > 0) { + return 0; + } + + _session_is_new = true; + + try { + new_session = new Session (*engine, path, snap_name, input_connect, output_connect, + control_channels, master_channels, nphysin, nphysout, initial_length); + } + + catch (...) { + + error << compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name) << endmsg; + return -1; + } + + connect_to_session (new_session); + + //if (engine->running()) { + //mixer->show_window(); + //} + session_loaded = true; + return 0; +} + +void +ARDOUR_UI::hide_dialog (ArdourDialog *dialog) +{ + dialog->hide_all(); +} + +void +ARDOUR_UI::show () +{ + if (editor) { + editor->show_window (); + shown_flag = true; + } + + if (session && mixer) { + mixer->show_window (); + } + + if (about) { + about->get_window().raise (); + } +} + +void +ARDOUR_UI::show_splash () +{ + if (about == 0) { + about = new About(this); + about->show_all(); + about->show_sub (true); + about->get_window().raise (); + } + else { + about->get_window().set_decorations (GdkWMDecoration (GDK_DECOR_BORDER|GDK_DECOR_RESIZEH)); + about->show_all (); + about->get_window().raise (); + } +} + +void +ARDOUR_UI::hide_splash () +{ + if (about) { + // about->hide(); + } +} + +void +ARDOUR_UI::display_cleanup_results (Session::cleanup_report& rep, const gchar* list_title, string msg) +{ + size_t removed; + + removed = rep.paths.size(); + + if (removed == 0) { + ArdourMessage msg (editor, X_("cleanupresults"), + _("\ +No audio files were ready for cleanup\n\n\ +If this seems suprising, check for any existing\n\ +snapshots. These may still include regions that\n\ +require some unused files to continue to exist.")); + return; + } + + ArdourDialog results ("cleanup results"); + + const gchar* list_titles[] = { + list_title, + 0 + }; + + Gtk::CList list (internationalize (list_titles)); + Gtk::ScrolledWindow list_scroller; + Gtk::Label txt; + Gtk::Button ok_button (_("OK")); + Gtk::VBox vpacker; + const char* rowtext[1]; + + list_scroller.set_policy (GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + vpacker.set_border_width (10); + vpacker.set_spacing (10); + + if (rep.space < 1048576.0f) { + if (removed > 1) { + txt.set_text (compose (msg, removed, _("files"), (float) rep.space / 1024.0f, "kilo")); + } else { + txt.set_text (compose (msg, removed, _("file"), (float) rep.space / 1024.0f, "kilo")); + } + } else { + if (removed > 1) { + txt.set_text (compose (msg, removed, _("files"), (float) rep.space / 1048576.0f, "mega")); + } else { + txt.set_text (compose (msg, removed, _("file"), (float) rep.space / 1048576.0f, "mega")); + } + } + + vpacker.pack_start (txt, false, false); + + for (vector<string>::iterator i = rep.paths.begin(); i != rep.paths.end(); ++i) { + rowtext[0] = (*i).c_str(); + list.rows().push_back (rowtext); + } + + list_scroller.add_with_viewport (list); + list_scroller.set_usize (-1, 250); + + vpacker.pack_start (list_scroller, true, true); + vpacker.pack_start (ok_button, false, false); + + ok_button.clicked.connect (Main::quit.slot ()); + results.Hiding.connect (Main::quit.slot ()); + + results.add (vpacker); + + results.set_position (GTK_WIN_POS_MOUSE); + results.set_title (_("ardour: cleanup")); + results.set_modal (true); + results.run (); +} + +void +ARDOUR_UI::cleanup () +{ + if (session == 0) { + /* shouldn't happen: menu item is insensitive */ + return; + } + + ArdourDialog checker (X_("cleanup confirm dialog")); + Gtk::Label label (_("\ +Cleanup is a destructive operation.\n\ +ALL undo/redo information will be lost if you cleanup.\n\ +Unused audio files will be moved to a \"dead sounds\" location.")); + + Gtk::Button ok_button (_("Proceed with cleanup")); + Gtk::Button cancel_button (_("Cancel")); + Gtk::HBox bbox; + Gtk::VBox vbox; + + bbox.set_border_width (6); + bbox.set_spacing (12); + bbox.pack_start (ok_button, true, false); + bbox.pack_start (cancel_button, true, false); + + vbox.set_border_width (6); + vbox.set_spacing (12); + vbox.pack_start (label, false, false); + vbox.pack_start (bbox, false, false); + + checker.add (vbox); + checker.set_name (_("CleanupDialog")); + checker.set_title (_("ardour cleanup")); + checker.set_wmclass (_("ardour_cleanup"), "Ardour"); + checker.set_position (GTK_WIN_POS_MOUSE); + checker.realize (); + checker.get_window().set_decorations (GdkWMDecoration (GDK_DECOR_BORDER|GDK_DECOR_RESIZEH)); + + ok_button.clicked.connect (bind (slot (checker, &ArdourDialog::stop), 1)); + cancel_button.clicked.connect (bind (slot (checker, &ArdourDialog::stop), 0)); + + checker.run (); + + if (checker.run_status() != 1) { + return; + } + + Session::cleanup_report rep; + + editor->prepare_for_cleanup (); + + if (session->cleanup_sources (rep)) { + return; + } + + display_cleanup_results (rep, + _("cleaned files"), + _("\ +The following %1 %2 were not in use.\n\ +The next time you flush the wastebasket\n\ +it will release an additional %3 %4bytes\n\ +of disk space" + )); +} + +void +ARDOUR_UI::flush_trash () +{ + if (session == 0) { + /* shouldn't happen: menu item is insensitive */ + return; + } + + Session::cleanup_report rep; + + if (session->cleanup_trash_sources (rep)) { + return; + } + + display_cleanup_results (rep, + _("deleted file"), + _("The following %1 file%2 were deleted, releasing %3 %4bytes of disk space")); +} + +void +ARDOUR_UI::add_route () +{ + int count; + + if (!session) { + return; + } + + if (add_route_dialog == 0) { + add_route_dialog = new AddRouteDialog; + editor->ensure_float (*add_route_dialog); + } + + if (add_route_dialog->is_visible()) { + /* we're already doing this */ + return; + } + + add_route_dialog->run (); + + if (add_route_dialog->run_status()) { + return; + } + + if ((count = add_route_dialog->count()) <= 0) { + return; + } + + uint32_t input_chan = add_route_dialog->channels (); + uint32_t output_chan; + string name_template = add_route_dialog->name_template (); + bool track = add_route_dialog->track (); + + Session::AutoConnectOption oac = session->get_output_auto_connect(); + + if (oac & Session::AutoConnectMaster) { + output_chan = (session->master_out() ? session->master_out()->n_inputs() : input_chan); + } else { + output_chan = input_chan; + } + + /* XXX do something with name template */ + + while (count) { + if (track) { + session_add_audio_track (input_chan, output_chan); + } else { + session_add_audio_bus (input_chan, output_chan); + } + --count; + + while (Main::events_pending()) { + Main::iteration (); + } + } +} + +XMLNode* +ARDOUR_UI::mixer_settings () const +{ + XMLNode* node = 0; + + if (session) { + node = session->instant_xml(X_("Mixer"), session->path()); + } else { + node = Config->instant_xml(X_("Mixer"), Config->get_user_ardour_path()); + } + + if (!node) { + node = new XMLNode (X_("Mixer")); + } + + return node; +} + +XMLNode* +ARDOUR_UI::editor_settings () const +{ + XMLNode* node = 0; + + if (session) { + node = session->instant_xml(X_("Editor"), session->path()); + } else { + node = Config->instant_xml(X_("Editor"), Config->get_user_ardour_path()); + } + + if (!node) { + node = new XMLNode (X_("Editor")); + } + return node; +} + +XMLNode* +ARDOUR_UI::keyboard_settings () const +{ + XMLNode* node = 0; + + node = Config->extra_xml(X_("Keyboard")); + + if (!node) { + node = new XMLNode (X_("Keyboard")); + } + return node; +} + +void +ARDOUR_UI::halt_on_xrun_message () +{ + ENSURE_GUI_THREAD (slot (*this, &ARDOUR_UI::halt_on_xrun_message)); + + ArdourMessage msg (editor, X_("haltonxrun"), + _("Recording was stopped because your system could not keep up.")); +} + +void +ARDOUR_UI::delete_sources_in_the_right_thread (list<ARDOUR::Source*>* deletion_list) +{ + ENSURE_GUI_THREAD (bind (slot (*this, &ARDOUR_UI::delete_sources_in_the_right_thread), deletion_list)); + + for (list<Source*>::iterator i = deletion_list->begin(); i != deletion_list->end(); ++i) { + delete *i; + } + + delete deletion_list; +} + +void +ARDOUR_UI::disk_overrun_handler () +{ + ENSURE_GUI_THREAD (slot (*this, &ARDOUR_UI::disk_underrun_handler)); + + if (!have_disk_overrun_displayed) { + have_disk_overrun_displayed = true; + ArdourMessage msg (editor, X_("diskrate dialog"), _("\ +The disk system on your computer\n\ +was not able to keep up with Ardour.\n\ +\n\ +Specifically, it failed to write data to disk\n\ +quickly enough to keep up with recording.\n")); + have_disk_overrun_displayed = false; + } +} + +void +ARDOUR_UI::disk_underrun_handler () +{ + ENSURE_GUI_THREAD (slot (*this, &ARDOUR_UI::disk_underrun_handler)); + + if (!have_disk_underrun_displayed) { + have_disk_underrun_displayed = true; + ArdourMessage msg (editor, X_("diskrate2 dialog"), + (_("The disk system on your computer\n\ +was not able to keep up with Ardour.\n\ +\n\ +Specifically, it failed to read data from disk\n\ +quickly enough to keep up with playback.\n"))); + have_disk_underrun_displayed = false; + } +} + +void +ARDOUR_UI::disk_underrun_message_gone () +{ + have_disk_underrun_displayed = false; +} + +void +ARDOUR_UI::disk_overrun_message_gone () +{ + have_disk_underrun_displayed = false; +} + +int +ARDOUR_UI::pending_state_dialog () +{ + ArdourDialog dialog ("pending state dialog"); + Button use_button (_("Recover from crash")); + Button cancel_button (_("Ignore crash data")); + Label message (_("\ +This session appears to have been in\n\ +middle of recording when ardour or\n\ +the computer was shutdown.\n\ +\n\ +Ardour can recover any captured audio for\n\ +you, or it can ignore it. Please decide\n\ +what you would like to do.\n")); + HBox hpacker; + VBox vpacker; + + vpacker.set_border_width (12); + vpacker.set_spacing (7); + vpacker.pack_start (message); + vpacker.pack_start (hpacker); + + hpacker.set_spacing (7); + hpacker.pack_start (use_button); + hpacker.pack_start (cancel_button); + + use_button.clicked.connect (bind (slot (dialog, &ArdourDialog::stop), 0)); + cancel_button.clicked.connect (bind (slot (dialog, &ArdourDialog::stop), 1)); + + dialog.add (vpacker); + dialog.set_position (GTK_WIN_POS_CENTER); + dialog.show_all (); + dialog.realize(); + dialog.get_window().set_decorations (GdkWMDecoration (GDK_DECOR_BORDER|GDK_DECOR_RESIZEH)); + + dialog.run (); + + if (dialog.run_status () == 0) { + return 1; + } + + return 0; +} + + +void +ARDOUR_UI::disconnect_from_jack () +{ + if (engine) { + if( engine->disconnect_from_jack ()) { + ArdourMessage msg (editor, X_("nojack dialog"), + _("Could not disconnect from JACK")); + } + + update_sample_rate (0); + } +} + +void +ARDOUR_UI::reconnect_to_jack () +{ + if (engine) { + if (engine->reconnect_to_jack ()) { + ArdourMessage msg (editor, X_("nojack dialog"), + _("Could not reconnect to JACK")); + } + + update_sample_rate (0); + } +} + +void +ARDOUR_UI::set_jack_buffer_size (jack_nframes_t nframes) +{ + engine->request_buffer_size (nframes); + update_sample_rate (0); +} + +int +ARDOUR_UI::cmdline_new_session (string path) +{ + if (path[0] != '/') { + char buf[PATH_MAX+1]; + string str; + + getcwd (buf, sizeof (buf)); + str = buf; + str += '/'; + str += path; + path = str; + } + + new_session (false, path); + + _will_create_new_session_automatically = false; /* done it */ + return FALSE; /* don't call it again */ +} |