prevent crash when using WM close button on "new route name contains bad chars" dialog.
[ardour.git] / gtk2_ardour / time_axis_view.cc
index a76f2e07ef9f4b468cf7a6245128ae03182b95e0..9deb7eb3e880946e8c124ed397cde4428c23d6b1 100644 (file)
@@ -27,6 +27,7 @@
 #include "pbd/error.h"
 #include "pbd/convert.h"
 #include "pbd/stacktrace.h"
+#include "pbd/unwind.h"
 
 #include <gtkmm2ext/doi.h>
 #include <gtkmm2ext/utils.h>
@@ -35,6 +36,8 @@
 #include "canvas/canvas.h"
 #include "canvas/rectangle.h"
 #include "canvas/debug.h"
+#include "canvas/utils.h"
+#include "canvas/colors.h"
 
 #include "ardour/profile.h"
 
@@ -66,16 +69,26 @@ using namespace Editing;
 using namespace ArdourCanvas;
 using Gtkmm2ext::Keyboard;
 
+#define TOP_LEVEL_WIDGET controls_ebox
+
 const double trim_handle_size = 6.0; /* pixels */
 uint32_t TimeAxisView::button_height = 0;
 uint32_t TimeAxisView::extra_height = 0;
 int const TimeAxisView::_max_order = 512;
+unsigned int TimeAxisView::name_width_px = 100;
 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
-Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
+Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
+Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::midi_scroomer_size_group = Glib::RefPtr<Gtk::SizeGroup>();
+
+void
+TimeAxisView::setup_sizes()
+{
+       name_width_px = ceilf (100.f * ARDOUR_UI::ui_scale);
+}
 
 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
        : AxisView (sess)
-       , controls_table (4, 4)
+       , controls_table (3, 3)
        , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
        , _name_editing (false)
        , height (0)
@@ -90,22 +103,37 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        , _y_position (0)
        , _editor (ed)
        , name_entry (0)
+       , ending_name_edit (false)
        , control_parent (0)
        , _order (0)
        , _effective_height (0)
        , _resize_drag_start (-1)
+       , _did_resize (false)
        , _preresize_cursor (0)
        , _have_preresize_cursor (false)
        , _ebox_release_can_act (true)
 {
+       if (!controls_meters_size_group) {
+               controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
+       }
+       if (!midi_scroomer_size_group) {
+               midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
+       }
        if (extra_height == 0) {
                compute_heights ();
        }
 
-       _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
+       _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group ());
        CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
        _canvas_display->hide(); // reveal as needed
 
+       _canvas_separator = new ArdourCanvas::Line(_canvas_display);
+       CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
+       _canvas_separator->set (ArdourCanvas::Duple(0.0, 0.0), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, 0.0));
+       _canvas_separator->set_outline_color(ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
+       _canvas_separator->set_outline_width(1.0);
+       _canvas_separator->hide();
+
        selection_group = new ArdourCanvas::Container (_canvas_display);
        CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
        selection_group->set_data (X_("timeselection"), (void *) 1);
@@ -121,7 +149,8 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        name_label.set_width_chars (12);
        ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
 
-       Gtk::Entry* an_entry = new Gtk::Entry;
+       Gtk::Entry* an_entry = new Gtkmm2ext::FocusEntry;
+       an_entry->set_name ("EditorTrackNameDisplay");
        Gtk::Requisition req;
        an_entry->size_request (req);
        name_label.set_size_request (-1, req.height);
@@ -131,11 +160,8 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        name_hbox.pack_end (name_label, true, true);
 
        // set min. track-header width if fader is not visible
-       if (ARDOUR::Profile->get_mixbus() ) {
-               name_hbox.set_size_request(100, 0);
-       } else {
-               name_hbox.set_size_request(90, 0);
-       }
+       name_hbox.set_size_request(name_width_px, -1);
+
        name_hbox.show ();
        name_label.show ();
 
@@ -144,9 +170,9 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        controls_table.set_border_width (2);
 
        if (ARDOUR::Profile->get_mixbus() ) {
-               controls_table.attach (name_hbox, 4, 5, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
+               controls_table.attach (name_hbox, 4, 5, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
        } else {
-               controls_table.attach (name_hbox, 1, 2, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
+               controls_table.attach (name_hbox, 1, 2, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
        }
        controls_table.show_all ();
        controls_table.set_no_show_all ();
@@ -157,7 +183,7 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        top_hbox.pack_start (controls_vbox, true, true);
        top_hbox.show ();
 
-       controls_ebox.add (top_hbox);
+       controls_ebox.add (time_axis_hbox);
        controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
                                  Gdk::BUTTON_RELEASE_MASK|
                                  Gdk::POINTER_MOTION_MASK|
@@ -174,10 +200,26 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
        controls_ebox.show ();
 
-       time_axis_frame.set_shadow_type (Gtk::SHADOW_OUT);
-       time_axis_frame.add(controls_ebox);
+       time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
+       time_axis_frame.add(top_hbox);
        time_axis_frame.show();
 
+       HSeparator* separator = manage (new HSeparator());
+       separator->set_name("TrackSeparator");
+       separator->set_size_request(-1, 1);
+       separator->show();
+
+       scroomer_placeholder.set_size_request (-1, -1);
+       scroomer_placeholder.show();
+       midi_scroomer_size_group->add_widget (scroomer_placeholder);
+
+       time_axis_vbox.pack_start (*separator, false, false);
+       time_axis_vbox.pack_start (time_axis_frame, true, true);
+       time_axis_vbox.show();
+       time_axis_hbox.pack_start (time_axis_vbox, true, true);
+       time_axis_hbox.show();
+       top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
+
        ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
 
        GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
@@ -185,6 +227,8 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
 
 TimeAxisView::~TimeAxisView()
 {
+       CatchDeletion (this);
+
        in_destructor = true;
 
        for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
@@ -192,16 +236,16 @@ TimeAxisView::~TimeAxisView()
        }
 
        for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
-               delete (*i)->rect;
-               delete (*i)->start_trim;
-               delete (*i)->end_trim;
+               delete (*i)->rect; (*i)->rect=0;
+               delete (*i)->start_trim; (*i)->start_trim = 0;
+               delete (*i)->end_trim; (*i)->end_trim = 0;
 
        }
 
        for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
-               delete (*i)->rect;
-               delete (*i)->start_trim;
-               delete (*i)->end_trim;
+               delete (*i)->rect; (*i)->rect = 0;
+               delete (*i)->start_trim; (*i)->start_trim = 0;
+               delete (*i)->end_trim; (*i)->end_trim = 0;
        }
 
        delete selection_group;
@@ -224,9 +268,10 @@ TimeAxisView::hide ()
        }
 
        _canvas_display->hide ();
+       _canvas_separator->hide ();
 
        if (control_parent) {
-               control_parent->remove (time_axis_frame);
+               control_parent->remove (TOP_LEVEL_WIDGET);
                control_parent = 0;
        }
 
@@ -258,11 +303,11 @@ guint32
 TimeAxisView::show_at (double y, int& nth, VBox *parent)
 {
        if (control_parent) {
-               control_parent->reorder_child (time_axis_frame, nth);
+               control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
        } else {
                control_parent = parent;
-               parent->pack_start (time_axis_frame, false, false);
-               parent->reorder_child (time_axis_frame, nth);
+               parent->pack_start (TOP_LEVEL_WIDGET, false, false);
+               parent->reorder_child (TOP_LEVEL_WIDGET, nth);
        }
 
        _order = nth;
@@ -270,12 +315,11 @@ TimeAxisView::show_at (double y, int& nth, VBox *parent)
        if (_y_position != y) {
                _canvas_display->set_y_position (y);
                _y_position = y;
-
        }
 
        _canvas_display->raise_to_top ();
        _canvas_display->show ();
-
+       
        _hidden = false;
 
        _effective_height = current_height ();
@@ -291,6 +335,12 @@ TimeAxisView::show_at (double y, int& nth, VBox *parent)
                }
        }
 
+       /* put separator at the bottom of this time axis view */
+
+       _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
+       _canvas_separator->lower_to_bottom ();
+       _canvas_separator->show ();
+
        return _effective_height;
 }
 
@@ -366,9 +416,9 @@ TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
 }
 
 void
-TimeAxisView::idle_resize (uint32_t h)
+TimeAxisView::idle_resize (int32_t h)
 {
-       set_height (h);
+       set_height (std::max(0, h));
 }
 
 
@@ -381,15 +431,16 @@ TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
                 * are pretending that the drag is taking place over the canvas
                 * (which perhaps in the glorious future, when track headers
                 * and the canvas are unified, will actually be true.)
-               */
+                */
 
                _editor.maybe_autoscroll (false, true, true);
 
                /* now schedule the actual TAV resize */
-                int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
-                _editor.add_to_idle_resize (this, delta);
-                _resize_drag_start = ev->y_root;
-        } else {
+               int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
+               _editor.add_to_idle_resize (this, delta);
+               _resize_drag_start = ev->y_root;
+               _did_resize = true;
+       } else {
                /* not dragging but ... */
                maybe_set_cursor (ev->y);
        }
@@ -447,6 +498,11 @@ TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
                }
                _editor.stop_canvas_autoscroll ();
                _resize_drag_start = -1;
+               if (_did_resize) {
+                       _did_resize = false;
+                       // don't change selection
+                       return true;
+               }
        }
 
        if (!_ebox_release_can_act) {
@@ -514,13 +570,21 @@ TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
 }
 
 void
-TimeAxisView::set_height (uint32_t h)
+TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
 {
+       uint32_t lanes = 0;
+       if (m == TotalHeight) {
+               for (Children::iterator i = children.begin(); i != children.end(); ++i) {
+                       if ( !(*i)->hidden()) ++lanes;
+               }
+       }
+       h /= (lanes + 1);
+
        if (h < preset_height (HeightSmall)) {
                h = preset_height (HeightSmall);
        }
 
-       time_axis_frame.property_height_request () = h;
+       TOP_LEVEL_WIDGET.property_height_request () = h;
        height = h;
 
        char buf[32];
@@ -536,6 +600,12 @@ TimeAxisView::set_height (uint32_t h)
                show_selection (_editor.get_selection().time);
        }
 
+       if (m != OnlySelf) {
+               for (Children::iterator i = children.begin(); i != children.end(); ++i) {
+                       (*i)->set_height(h, OnlySelf);
+               }
+       }
+
        _editor.override_visible_track_count ();
 }
 
@@ -628,6 +698,15 @@ TimeAxisView::end_name_edit (int response)
        if (!name_entry) {
                return;
        }
+
+       if (ending_name_edit) {
+               /* already doing this, and focus out or other event has caused 
+                  us to re-enter this code.
+               */
+               return;
+       }
+       
+       PBD::Unwinder<bool> uw (ending_name_edit, true);
        
        bool edit_next = false;
        bool edit_prev = false;
@@ -756,6 +835,10 @@ TimeAxisView::popup_display_menu (guint32 when)
 void
 TimeAxisView::set_selected (bool yn)
 {
+       if (can_edit_name() && name_entry && name_entry->get_visible()) {
+               end_name_edit (RESPONSE_CANCEL);
+       }
+
        if (yn == _selected) {
                return;
        }
@@ -767,11 +850,13 @@ TimeAxisView::set_selected (bool yn)
                time_axis_frame.set_name ("MixerStripSelectedFrame");
                controls_ebox.set_name (controls_base_selected_name);
                controls_vbox.set_name (controls_base_selected_name);
+               time_axis_vbox.set_name (controls_base_selected_name);
        } else {
-               time_axis_frame.set_shadow_type (Gtk::SHADOW_OUT);
+               time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
                time_axis_frame.set_name (controls_base_unselected_name);
                controls_ebox.set_name (controls_base_unselected_name);
                controls_vbox.set_name (controls_base_unselected_name);
+               time_axis_vbox.set_name (controls_base_unselected_name);
 
                hide_selection ();
 
@@ -872,7 +957,7 @@ TimeAxisView::show_selection (TimeSelection& ts)
                // trim boxes are at the top for selections
 
                if (x2 > x1) {
-                       rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
+                       rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
                        rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
 
                        rect->start_trim->show();
@@ -971,7 +1056,7 @@ TimeAxisView::get_selection_rect (uint32_t id)
                rect->rect = new ArdourCanvas::Rectangle (selection_group);
                CANVAS_DEBUG_NAME (rect->rect, "selection rect");
                rect->rect->set_outline (false);
-               rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
+               rect->rect->set_fill_color (ARDOUR_UI::config()->color_mod ("selection rect", "selection rect"));
 
                rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
                CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
@@ -1028,7 +1113,7 @@ TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
  *  @param result Filled in with selectable things.
  */
 void
-TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
+TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/, bool /*within*/)
 {
        return;
 }
@@ -1105,7 +1190,7 @@ TimeAxisView::compute_heights ()
 {
        // TODO this function should be re-evaluated when font-scaling changes (!)
        Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
-       Gtk::Table one_row_table (1, 8);
+       Gtk::Table one_row_table (1, 1);
        ArdourButton* test_button = manage (new ArdourButton);
        const int border_width = 2;
        const int frame_height = 2;
@@ -1113,7 +1198,8 @@ TimeAxisView::compute_heights ()
 
        window.add (one_row_table);
        test_button->set_name ("mute button");
-       test_button->set_text (_("M"));
+       test_button->set_text (S_("Mute|M"));
+       test_button->set_tweaks (ArdourButton::TrackHeader);
 
        one_row_table.set_border_width (border_width);
        one_row_table.set_row_spacings (2);
@@ -1135,26 +1221,26 @@ TimeAxisView::color_handler ()
 
        for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
 
-               (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
-               (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
+               (*i)->rect->set_fill_color (ARDOUR_UI::config()->color_mod ("selection rect", "selection rect"));
+               (*i)->rect->set_outline_color (ARDOUR_UI::config()->color ("selection"));
 
-               (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
-               (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
+               (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
+               (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
                
-               (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
-               (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
+               (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
+               (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
        }
        
        for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
                
-               (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
-               (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
+               (*i)->rect->set_fill_color (ARDOUR_UI::config()->color_mod ("selection rect", "selection rect"));
+               (*i)->rect->set_outline_color (ARDOUR_UI::config()->color ("selection"));
                
-               (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
-               (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
+               (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
+               (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
                
-               (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
-               (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
+               (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
+               (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
        }
 }
 
@@ -1259,7 +1345,7 @@ TimeAxisView::preset_height (Height h)
                return button_height + extra_height;
        }
 
-       /* NOTREACHED */
+       abort(); /* NOTREACHED */
        return 0;
 }