Fix a double-free, introduced in b06713bd8e57
[ardour.git] / gtk2_ardour / option_editor.cc
1 /*
2   Copyright (C) 2001-2009 Paul Davis
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19 #include <algorithm>
20
21 #include <gtkmm/box.h>
22 #include <gtkmm/alignment.h>
23 #include "gtkmm2ext/utils.h"
24
25 #include "ardour/dB.h"
26 #include "ardour/rc_configuration.h"
27 #include "ardour/session.h"
28 #include "ardour/types.h"
29 #include "ardour/utils.h"
30
31 #include "pbd/configuration.h"
32 #include "pbd/replace_all.h"
33 #include "pbd/strsplit.h"
34
35 #include "gui_thread.h"
36 #include "option_editor.h"
37 #include "public_editor.h"
38 #include "utils.h"
39 #include "pbd/i18n.h"
40
41 using namespace std;
42 using namespace Gtk;
43 using namespace Gtkmm2ext;
44 using namespace ARDOUR;
45
46 void
47 OptionEditorComponent::add_widget_to_page (OptionEditorPage* p, Gtk::Widget* w)
48 {
49         int const n = p->table.property_n_rows();
50         int m = n + 1;
51         if (!_note.empty ()) {
52                 ++m;
53         }
54
55         p->table.resize (m, 3);
56         p->table.attach (*w, 1, 3, n, n + 1, FILL | EXPAND);
57
58         maybe_add_note (p, n + 1);
59 }
60
61 void
62 OptionEditorComponent::add_widgets_to_page (OptionEditorPage* p, Gtk::Widget* wa, Gtk::Widget* wb, bool expand)
63 {
64         int const n = p->table.property_n_rows();
65         int m = n + 1;
66         if (!_note.empty ()) {
67                 ++m;
68         }
69
70         p->table.resize (m, 3);
71         p->table.attach (*wa, 1, 2, n, n + 1, FILL);
72         if (expand) {
73                 p->table.attach (*wb, 2, 3, n, n + 1, FILL | EXPAND);
74         } else {
75                 Alignment* a = manage (new Alignment (0, 0.5, 0, 1.0));
76                 a->add (*wb);
77                 p->table.attach (*a, 2, 3, n, n + 1, FILL | EXPAND);
78         }
79         maybe_add_note (p, n + 1);
80 }
81
82 void
83 OptionEditorComponent::maybe_add_note (OptionEditorPage* p, int n)
84 {
85         if (!_note.empty ()) {
86                 Gtk::Label* l = manage (left_aligned_label (string_compose (X_("<i>%1</i>"), _note)));
87                 l->set_use_markup (true);
88                 l->set_line_wrap (true);
89                 p->table.attach (*l, 1, 3, n, n + 1, FILL | EXPAND);
90         }
91 }
92
93 void
94 OptionEditorComponent::set_note (string const & n)
95 {
96         _note = n;
97 }
98
99 /*--------------------------*/
100
101 OptionEditorHeading::OptionEditorHeading (string const & h)
102 {
103         std::stringstream s;
104         s << "<b>" << h << "</b>";
105         _label = manage (left_aligned_label (s.str()));
106         _label->set_use_markup (true);
107 }
108
109 /*--------------------------*/
110
111 void
112 OptionEditorHeading::add_to_page (OptionEditorPage* p)
113 {
114         int const n = p->table.property_n_rows();
115         if (!_note.empty ()) {
116                 p->table.resize (n + 3, 3);
117         } else {
118                 p->table.resize (n + 2, 3);
119         }
120
121         p->table.attach (*manage (new Label ("")), 0, 3, n, n + 1, FILL | EXPAND);
122         p->table.attach (*_label, 0, 3, n + 1, n + 2, FILL | EXPAND);
123         maybe_add_note (p, n + 2);
124 }
125
126 /*--------------------------*/
127
128 void
129 OptionEditorBlank::add_to_page (OptionEditorPage* p)
130 {
131         int const n = p->table.property_n_rows();
132         p->table.resize (n + 1, 3);
133         p->table.attach (_dummy, 2, 3, n, n + 1, FILL | EXPAND, SHRINK, 0, 0);
134         _dummy.set_size_request (-1, 1);
135         _dummy.show ();
136 }
137
138 /*--------------------------*/
139
140 RcConfigDisplay::RcConfigDisplay (string const & i, string const & n, sigc::slot<string> g, char s)
141         : _get (g)
142         , _id (i)
143         , _sep (s)
144 {
145         _label = manage (right_aligned_label (n));
146         _info = manage (new Label);
147         _info-> set_line_wrap (true);
148         set_state_from_config ();
149 }
150
151 void
152 RcConfigDisplay::set_state_from_config ()
153 {
154         string p = _get();
155         if (_sep) {
156                 std::replace (p.begin(), p.end(), _sep, '\n');
157         }
158         _info->set_text (p);
159 }
160
161 void
162 RcConfigDisplay::parameter_changed (std::string const & p)
163 {
164         if (p == _id) {
165                 set_state_from_config ();
166         }
167 }
168
169 void
170 RcConfigDisplay::add_to_page (OptionEditorPage *p)
171 {
172         int const n = p->table.property_n_rows();
173         int m = n + 1;
174         p->table.resize (m, 3);
175         p->table.attach (*_label, 1, 2, n, n + 1, FILL | EXPAND);
176         p->table.attach (*_info,  2, 3, n, n + 1, FILL | EXPAND);
177 }
178
179 /*--------------------------*/
180
181 RcActionButton::RcActionButton (std::string const & t, const Glib::SignalProxy0< void >::SlotType & slot, std::string const & l)
182         : _label (NULL)
183 {
184         _button = manage (new Button (t));
185         _button->signal_clicked().connect (slot);
186         if (!l.empty ()) {
187                 _label = manage (right_aligned_label (l));
188         }
189 }
190
191 void
192 RcActionButton::add_to_page (OptionEditorPage *p)
193 {
194         int const n = p->table.property_n_rows();
195         int m = n + 1;
196         p->table.resize (m, 3);
197         if (_label) {
198                 p->table.attach (*_label,  1, 2, n, n + 1, FILL | EXPAND);
199                 p->table.attach (*_button, 2, 3, n, n + 1, FILL | EXPAND);
200         } else {
201                 p->table.attach (*_button, 1, 3, n, n + 1, FILL | EXPAND);
202         }
203 }
204
205 /*--------------------------*/
206
207 BoolOption::BoolOption (string const & i, string const & n, sigc::slot<bool> g, sigc::slot<bool, bool> s)
208         : Option (i, n),
209           _get (g),
210           _set (s)
211 {
212         _button = manage (new CheckButton);
213         _label = manage (new Label);
214         _label->set_markup (n);
215         _button->add (*_label);
216         _button->set_active (_get ());
217         _button->signal_toggled().connect (sigc::mem_fun (*this, &BoolOption::toggled));
218 }
219
220 void
221 BoolOption::add_to_page (OptionEditorPage* p)
222 {
223         add_widget_to_page (p, _button);
224 }
225
226 void
227 BoolOption::set_state_from_config ()
228 {
229         _button->set_active (_get ());
230 }
231
232 void
233 BoolOption::toggled ()
234 {
235         if (!_set (_button->get_active ())) {
236                 _button->set_active (_get ());
237         }
238 }
239
240 /*--------------------------*/
241
242 RouteDisplayBoolOption::RouteDisplayBoolOption (string const & i, string const & n, sigc::slot<bool> g, sigc::slot<bool, bool> s)
243         : BoolOption (i, n, g, s)
244 {
245 }
246
247 void
248 RouteDisplayBoolOption::toggled ()
249 {
250         DisplaySuspender ds;
251         BoolOption::toggled ();
252 }
253
254 /*--------------------------*/
255
256 EntryOption::EntryOption (string const & i, string const & n, sigc::slot<string> g, sigc::slot<bool, string> s)
257         : Option (i, n),
258           _get (g),
259           _set (s)
260 {
261         _label = manage (left_aligned_label (n + ":"));
262         _entry = manage (new Entry);
263         _entry->signal_activate().connect (sigc::mem_fun (*this, &EntryOption::activated));
264         _entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &EntryOption::focus_out));
265         _entry->signal_insert_text().connect (sigc::mem_fun (*this, &EntryOption::filter_text));
266 }
267
268 void
269 EntryOption::add_to_page (OptionEditorPage* p)
270 {
271         add_widgets_to_page (p, _label, _entry);
272 }
273
274 void
275 EntryOption::set_state_from_config ()
276 {
277         _entry->set_text (_get ());
278 }
279
280 void
281 EntryOption::set_sensitive (bool s)
282 {
283         _entry->set_sensitive (s);
284 }
285
286 void
287 EntryOption::filter_text (const Glib::ustring&, int*)
288 {
289         std::string text = _entry->get_text ();
290         for (size_t i = 0; i < _invalid.length(); ++i) {
291                 text.erase (std::remove(text.begin(), text.end(), _invalid.at(i)), text.end());
292         }
293         if (text != _entry->get_text ()) {
294                 _entry->set_text (text);
295         }
296 }
297
298 void
299 EntryOption::activated ()
300 {
301         _set (_entry->get_text ());
302 }
303
304 bool
305 EntryOption::focus_out (GdkEventFocus*)
306 {
307         _set (_entry->get_text ());
308         return true;
309 }
310
311 /*--------------------------*/
312 HSliderOption::HSliderOption (
313                 std::string const& i,
314                 std::string const& n,
315                 sigc::slot<float> g,
316                 sigc::slot<bool, float> s,
317                 double lower, double upper,
318                 double step_increment,
319                 double page_increment,
320                 double mult,
321                 bool logarithmic
322                 )
323         : Option (i, n)
324         , _get (g)
325         , _set (s)
326         , _adj (lower, lower, upper, step_increment, page_increment, 0)
327         , _hscale (_adj)
328         , _label (n + ":")
329         , _mult (mult)
330         , _log (logarithmic)
331 {
332         _label.set_alignment (0, 0.5);
333         _label.set_name ("OptionsLabel");
334         _adj.set_value (_get());
335         _adj.signal_value_changed().connect (sigc::mem_fun (*this, &HSliderOption::changed));
336         _hscale.set_update_policy (Gtk::UPDATE_DISCONTINUOUS);
337 }
338
339 void
340 HSliderOption::set_state_from_config ()
341 {
342         if (_log) {
343                 _adj.set_value (log10(_get()) / _mult);
344         } else {
345                 _adj.set_value (_get() / _mult);
346         }
347 }
348
349 void
350 HSliderOption::changed ()
351 {
352         if (_log) {
353                 _set (pow (10, _adj.get_value () * _mult));
354         } else {
355                 _set (_adj.get_value () * _mult);
356         }
357 }
358
359 void
360 HSliderOption::add_to_page (OptionEditorPage* p)
361 {
362         add_widgets_to_page (p, &_label, &_hscale);
363 }
364
365 void
366 HSliderOption::set_sensitive (bool yn)
367 {
368         _hscale.set_sensitive (yn);
369 }
370
371 /*--------------------------*/
372
373 ComboStringOption::ComboStringOption (
374                 std::string const & i,
375                 std::string const & n,
376                 sigc::slot<std::string> g,
377                 sigc::slot<bool, std::string> s
378                 )
379         : Option (i, n)
380         , _get (g)
381         , _set (s)
382 {
383         _label = Gtk::manage (new Gtk::Label (n + ":"));
384         _label->set_alignment (0, 0.5);
385         _combo = Gtk::manage (new Gtk::ComboBoxText);
386         _combo->signal_changed().connect (sigc::mem_fun (*this, &ComboStringOption::changed));
387 }
388
389 void
390 ComboStringOption::set_state_from_config () {
391         _combo->set_active_text (_get());
392 }
393
394 void
395 ComboStringOption::add_to_page (OptionEditorPage* p)
396 {
397         add_widgets_to_page (p, _label, _combo);
398 }
399
400 /** Set the allowed strings for this option
401  *  @param strings a vector of allowed strings
402  */
403 void
404 ComboStringOption::set_popdown_strings (const std::vector<std::string>& strings) {
405         _combo->clear_items ();
406         for (std::vector<std::string>::const_iterator i = strings.begin(); i != strings.end(); ++i) {
407                 _combo->append_text (*i);
408         }
409 }
410
411 void
412 ComboStringOption::clear () {
413         _combo->clear_items();
414 }
415
416 void
417 ComboStringOption::changed () {
418         _set (_combo->get_active_text ());
419 }
420
421 void
422 ComboStringOption::set_sensitive (bool yn) {
423         _combo->set_sensitive (yn);
424 }
425
426 /*--------------------------*/
427
428 /** Construct a BoolComboOption.
429  *  @param i id
430  *  @param n User-visible name.
431  *  @param t Text to give for the variable being true.
432  *  @param f Text to give for the variable being false.
433  *  @param g Slot to get the variable's value.
434  *  @param s Slot to set the variable's value.
435  */
436 BoolComboOption::BoolComboOption (
437         string const & i, string const & n, string const & t, string const & f,
438         sigc::slot<bool> g, sigc::slot<bool, bool> s
439         )
440         : Option (i, n)
441         , _get (g)
442         , _set (s)
443 {
444         _label = manage (new Label (n + ":"));
445         _label->set_alignment (0, 0.5);
446         _combo = manage (new ComboBoxText);
447
448         /* option 0 is the false option */
449         _combo->append_text (f);
450         /* and option 1 is the true */
451         _combo->append_text (t);
452
453         _combo->signal_changed().connect (sigc::mem_fun (*this, &BoolComboOption::changed));
454 }
455
456 void
457 BoolComboOption::set_state_from_config ()
458 {
459         _combo->set_active (_get() ? 1 : 0);
460 }
461
462 void
463 BoolComboOption::add_to_page (OptionEditorPage* p)
464 {
465         add_widgets_to_page (p, _label, _combo);
466 }
467
468 void
469 BoolComboOption::changed ()
470 {
471         _set (_combo->get_active_row_number () == 0 ? false : true);
472 }
473
474 void
475 BoolComboOption::set_sensitive (bool yn)
476 {
477         _combo->set_sensitive (yn);
478 }
479
480 /*--------------------------*/
481
482 FaderOption::FaderOption (string const & i, string const & n, sigc::slot<gain_t> g, sigc::slot<bool, gain_t> s)
483         : Option (i, n)
484         , _db_adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()), 0, 1, 0.01, 0.1)
485         , _get (g)
486         , _set (s)
487 {
488         _db_slider = manage (new ArdourWidgets::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable>(), 220, 18));
489
490         _label.set_text (n + ":");
491         _label.set_alignment (0, 0.5);
492         _label.set_name (X_("OptionsLabel"));
493
494         _fader_centering_box.pack_start (*_db_slider, true, false);
495
496         _box.set_spacing (4);
497         _box.set_homogeneous (false);
498         _box.pack_start (_fader_centering_box, false, false);
499         _box.pack_start (_db_display, false, false);
500         _box.pack_start (*manage (new Label ("dB")), false, false);
501         _box.show_all ();
502
503         set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
504
505         _db_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &FaderOption::db_changed));
506         _db_display.signal_activate().connect (sigc::mem_fun (*this, &FaderOption::on_activate));
507         _db_display.signal_key_press_event().connect (sigc::mem_fun (*this, &FaderOption::on_key_press), false);
508 }
509
510 void
511 FaderOption::set_state_from_config ()
512 {
513         gain_t const val = _get ();
514         _db_adjustment.set_value (gain_to_slider_position_with_max (val, Config->get_max_gain ()));
515
516         char buf[16];
517
518         if (val == 0.0) {
519                 snprintf (buf, sizeof (buf), "-inf");
520         } else {
521                 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
522         }
523
524         _db_display.set_text (buf);
525 }
526
527 void
528 FaderOption::db_changed ()
529 {
530         _set (slider_position_to_gain_with_max (_db_adjustment.get_value (), Config->get_max_gain()));
531 }
532
533 void
534 FaderOption::on_activate ()
535 {
536         float db_val = atof (_db_display.get_text ().c_str ());
537         gain_t coeff_val = dB_to_coefficient (db_val);
538
539         _db_adjustment.set_value (gain_to_slider_position_with_max (coeff_val, Config->get_max_gain ()));
540 }
541
542 bool
543 FaderOption::on_key_press (GdkEventKey* ev)
544 {
545         if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
546                 /* drop through to normal handling */
547                 return false;
548         }
549         /* illegal key for gain entry */
550         return true;
551 }
552
553 void
554 FaderOption::add_to_page (OptionEditorPage* p)
555 {
556         add_widgets_to_page (p, &_label, &_box);
557 }
558
559 /*--------------------------*/
560
561 ClockOption::ClockOption (string const & i, string const & n, sigc::slot<std::string> g, sigc::slot<bool, std::string> s)
562         : Option (i, n)
563         , _clock (X_("timecode-offset"), true, X_(""), true, false, true, false)
564         , _get (g)
565         , _set (s)
566 {
567         _label.set_text (n + ":");
568         _label.set_alignment (0, 0.5);
569         _label.set_name (X_("OptionsLabel"));
570         _clock.ValueChanged.connect (sigc::mem_fun (*this, &ClockOption::save_clock_time));
571 }
572
573 void
574 ClockOption::set_state_from_config ()
575 {
576         Timecode::Time TC;
577         samplepos_t when;
578         if (!Timecode::parse_timecode_format(_get(), TC)) {
579                 _clock.set (0, true);
580         }
581         TC.rate = _session->samples_per_timecode_frame();
582         TC.drop = _session->timecode_drop_frames();
583         _session->timecode_to_sample(TC, when, false, false);
584         if (TC.negative) { when=-when; }
585         _clock.set (when, true);
586 }
587
588 void
589 ClockOption::save_clock_time ()
590 {
591         Timecode::Time TC;
592         _session->sample_to_timecode(_clock.current_time(), TC, false, false);
593         _set (Timecode::timecode_format_time(TC));
594 }
595
596 void
597 ClockOption::add_to_page (OptionEditorPage* p)
598 {
599         add_widgets_to_page (p, &_label, &_clock);
600 }
601
602 void
603 ClockOption::set_session (Session* s)
604 {
605         _session = s;
606         _clock.set_session (s);
607 }
608
609 /*--------------------------*/
610
611 WidgetOption::WidgetOption (string const & i, string const & n, Gtk::Widget& w)
612         : Option (i, n)
613         , _widget (&w)
614 {
615 }
616
617 void
618 WidgetOption::add_to_page (OptionEditorPage* p)
619 {
620         add_widget_to_page (p, _widget);
621 }
622
623 /*--------------------------*/
624
625 OptionEditorPage::OptionEditorPage ()
626         : table (1, 3)
627 {
628         init ();
629 }
630
631 OptionEditorPage::OptionEditorPage (Gtk::Notebook& n, std::string const & t)
632         : table (1, 3)
633 {
634         init ();
635         box.pack_start (table, false, false);
636         box.set_border_width (4);
637         n.append_page (box, t);
638 }
639
640 void
641 OptionEditorPage::init ()
642 {
643         table.set_spacings (4);
644         table.set_col_spacing (0, 32);
645 }
646
647 /*--------------------------*/
648
649 void
650 OptionEditorMiniPage::add_to_page (OptionEditorPage* p)
651 {
652         int const n = p->table.property_n_rows();
653         int m = n + 1;
654         if (!_note.empty ()) {
655                 ++m;
656         }
657         p->table.resize (m, 3);
658         p->table.attach (box, 0, 3, n, n + 1, FILL | EXPAND, SHRINK, 0, 0);
659         maybe_add_note (p, n + 1);
660 }
661
662 /*--------------------------*/
663
664 /** Construct an OptionEditor.
665  *  @param o Configuration to edit.
666  *  @param t Title for the dialog.
667  */
668 OptionEditor::OptionEditor (PBD::Configuration* c)
669         : _config (c)
670         , option_tree (TreeStore::create (option_columns))
671         , option_treeview (option_tree)
672 {
673         using namespace Notebook_Helpers;
674
675         _notebook.set_show_tabs (false);
676         _notebook.set_show_border (true);
677         _notebook.set_name ("OptionsNotebook");
678
679         option_treeview.append_column ("", option_columns.name);
680         option_treeview.set_enable_search(true);
681         option_treeview.set_search_column(0);
682         option_treeview.set_name ("OptionsTreeView");
683         option_treeview.set_headers_visible (false);
684
685         option_treeview.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
686         option_treeview.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &OptionEditor::treeview_row_selected));
687
688         /* Watch out for changes to parameters */
689         _config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&OptionEditor::parameter_changed, this, _1), gui_context());
690 }
691
692 OptionEditor::~OptionEditor ()
693 {
694         for (std::map<std::string, OptionEditorPage*>::iterator i = _pages.begin(); i != _pages.end(); ++i) {
695                 for (std::list<OptionEditorComponent*>::iterator j = i->second->components.begin(); j != i->second->components.end(); ++j) {
696                         delete *j;
697                 }
698                 delete i->second;
699         }
700 }
701
702 /** Called when a configuration parameter has been changed.
703  *  @param p Parameter name.
704  */
705 void
706 OptionEditor::parameter_changed (std::string const & p)
707 {
708         ENSURE_GUI_THREAD (*this, &OptionEditor::parameter_changed, p)
709
710         for (std::map<std::string, OptionEditorPage*>::iterator i = _pages.begin(); i != _pages.end(); ++i) {
711                 for (std::list<OptionEditorComponent*>::iterator j = i->second->components.begin(); j != i->second->components.end(); ++j) {
712                         (*j)->parameter_changed (p);
713                 }
714         }
715 }
716
717 void
718 OptionEditor::treeview_row_selected ()
719 {
720         Glib::RefPtr<Gtk::TreeSelection> selection = option_treeview.get_selection();
721         TreeModel::iterator iter = selection->get_selected();
722
723         if (iter) {
724                 TreeModel::Row row = *iter;
725                 Gtk::Widget* w = row[option_columns.widget];
726                 if (w) {
727                         _notebook.set_current_page (_notebook.page_num (*w));
728                 }
729         }
730 }
731
732 TreeModel::iterator
733 OptionEditor::find_path_in_treemodel (std::string const & pn, bool create_missing)
734 {
735         /* split page name, which is actually a path, into each component */
736
737         std::vector<std::string> components;
738         split (pn, components, '/');
739
740         /* start with top level children */
741
742         TreeModel::Children children = option_tree->children();
743         TreeModel::iterator iter;
744
745         /* foreach path component ... */
746
747         for (std::vector<std::string>::const_iterator s = components.begin(); s != components.end(); ++s) {
748
749                 for (iter = children.begin(); iter != children.end(); ++iter) {
750                         TreeModel::Row row = *iter;
751                         const std::string row_name = row[option_columns.name];
752                         if (row_name == (*s)) {
753                                 break;
754                         }
755                 }
756
757                 if (iter == children.end()) {
758                         /* the current component is missing; bail out or create it */
759                         if (!create_missing) {
760                                 return option_tree->get_iter(TreeModel::Path(""));
761                         } else {
762                                 iter = option_tree->append (children);
763                                 TreeModel::Row row = *iter;
764                                 row[option_columns.name] = *s;
765                                 row[option_columns.widget] = 0;
766                         }
767                 }
768
769                 /* from now on, iter points to a valid row, either the one we found or a new one */
770                 /* set children to the row's children to continue searching */
771                 children = (*iter)->children ();
772
773         }
774
775         return iter;
776 }
777
778 void
779 OptionEditor::add_path_to_treeview (std::string const & pn, Gtk::Widget& widget)
780 {
781         option_treeview.set_model (Glib::RefPtr<TreeStore>());
782
783         TreeModel::iterator row_iter = find_path_in_treemodel(pn, true);
784
785         assert(row_iter);
786
787         TreeModel::Row row = *row_iter;
788         row[option_columns.widget] = &widget;
789
790         option_treeview.set_model (option_tree);
791         option_treeview.expand_all ();
792 }
793
794 /** Add a component to a given page.
795  *  @param pn Page name (will be created if it doesn't already exist)
796  *  @param o Component.
797  */
798 void
799 OptionEditor::add_option (std::string const & pn, OptionEditorComponent* o)
800 {
801         if (_pages.find (pn) == _pages.end()) {
802                 OptionEditorPage* oep = new OptionEditorPage (_notebook, pn);
803                 _pages[pn] = oep;
804
805                 add_path_to_treeview (pn, oep->box);
806         }
807
808         OptionEditorPage* p = _pages[pn];
809         p->components.push_back (o);
810
811         o->add_to_page (p);
812         o->set_state_from_config ();
813 }
814
815 /** Add a new page
816  *  @param pn Page name (will be created if it doesn't already exist)
817  *  @param w widget that fills the page
818  */
819 void
820 OptionEditor::add_page (std::string const & pn, Gtk::Widget& w)
821 {
822         if (_pages.find (pn) == _pages.end()) {
823                 OptionEditorPage* oep = new OptionEditorPage (_notebook, pn);
824                 _pages[pn] = oep;
825                 add_path_to_treeview (pn, oep->box);
826         }
827
828         OptionEditorPage* p = _pages[pn];
829         p->box.pack_start (w, true, true);
830 }
831
832 void
833 OptionEditor::set_current_page (string const & p)
834 {
835         TreeModel::iterator row_iter = find_path_in_treemodel(p);
836
837         if (row_iter) {
838                 option_treeview.get_selection()->select(row_iter);
839         }
840
841 }
842
843 /*--------------------------*/
844
845 DirectoryOption::DirectoryOption (string const & i, string const & n, sigc::slot<string> g, sigc::slot<bool, string> s)
846         : Option (i, n)
847         , _get (g)
848         , _set (s)
849 {
850         _file_chooser.set_action (Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
851         _file_chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &DirectoryOption::selection_changed));
852 }
853
854 void
855 DirectoryOption::set_state_from_config ()
856 {
857         _file_chooser.set_current_folder (poor_mans_glob(_get ()));
858 }
859
860 void
861 DirectoryOption::add_to_page (OptionEditorPage* p)
862 {
863         Gtk::Label *label = manage (new Label (_name));
864         label->set_alignment (0, 0.5);
865         label->set_name (X_("OptionsLabel"));
866         add_widgets_to_page (p, label, &_file_chooser);
867 }
868
869 void
870 DirectoryOption::selection_changed ()
871 {
872         _set (poor_mans_glob(_file_chooser.get_filename ()));
873 }
874
875 /*--------------------------*/
876
877 OptionEditorContainer::OptionEditorContainer (PBD::Configuration* c, string const& str)
878         : OptionEditor (c)
879 {
880         set_border_width (4);
881         Frame* f = manage (new Frame ());
882         f->add (treeview());
883         f->set_shadow_type (Gtk::SHADOW_OUT);
884         f->set_border_width (0);
885         hpacker.pack_start (*f, false, false, 4);
886         hpacker.pack_start (notebook(), false, false);
887         pack_start (hpacker, true, true);
888
889         show_all ();
890 }
891
892 OptionEditorWindow::OptionEditorWindow (PBD::Configuration* c, string const& str)
893         : OptionEditor (c)
894         , ArdourWindow (str)
895 {
896         container.set_border_width (4);
897         Frame* f = manage (new Frame ());
898         f->add (treeview());
899         f->set_shadow_type (Gtk::SHADOW_OUT);
900         f->set_border_width (0);
901         hpacker.pack_start (*f, false, false);
902         hpacker.pack_start (notebook(), true, true, 4);
903
904         container.pack_start (hpacker, true, true);
905
906         hpacker.show_all ();
907         container.show ();
908
909         add (container);
910 }