static global definition for track-header width
[ardour.git] / gtk2_ardour / time_axis_view.cc
index fa181476cf58d6c3ef4338019c0303889510908e..13b0546d46ff4d1febd18abe2f22e0c8b3780d01 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "pbd/error.h"
 #include "pbd/convert.h"
+#include "pbd/stacktrace.h"
 
 #include <gtkmm2ext/doi.h>
 #include <gtkmm2ext/utils.h>
@@ -35,6 +36,8 @@
 #include "canvas/rectangle.h"
 #include "canvas/debug.h"
 
+#include "ardour/profile.h"
+
 #include "ardour_ui.h"
 #include "ardour_dialog.h"
 #include "global_signals.h"
@@ -57,6 +60,7 @@ using namespace std;
 using namespace Gtk;
 using namespace Gdk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Editing;
 using namespace ArdourCanvas;
@@ -66,16 +70,20 @@ 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; // TODO adjust with font-scaling on style-change
 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
+Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
 
 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
        : AxisView (sess)
-       , controls_table (2, 8)
+       , controls_table (3, 3)
+       , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
        , _name_editing (false)
        , height (0)
        , display_menu (0)
        , parent (rent)
        , selection_group (0)
+       , _ghost_group (0)
        , _hidden (false)
        , in_destructor (false)
        , _size_menu (0)
@@ -89,61 +97,68 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
        , _resize_drag_start (-1)
        , _preresize_cursor (0)
        , _have_preresize_cursor (false)
-       , _ghost_group (0)
        , _ebox_release_can_act (true)
 {
+       if (!controls_meters_size_group) {
+               controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
+       }
        if (extra_height == 0) {
                compute_heights ();
        }
 
-       _canvas_display = new Group (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
+       _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
+       CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
        _canvas_display->hide(); // reveal as needed
 
-       selection_group = new Group (_canvas_display);
+       selection_group = new ArdourCanvas::Container (_canvas_display);
+       CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
        selection_group->set_data (X_("timeselection"), (void *) 1);
        selection_group->hide();
-
-       _ghost_group = new Group (_canvas_display);
+       
+       _ghost_group = new ArdourCanvas::Container (_canvas_display);
+       CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
        _ghost_group->lower_to_bottom();
        _ghost_group->show();
 
        name_label.set_name ("TrackLabel");
        name_label.set_alignment (0.0, 0.5);
+       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::Requisition req;
        an_entry->size_request (req);
        name_label.set_size_request (-1, req.height);
+       name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
        delete an_entry;
 
-       name_hbox.pack_start (name_label, true, true);
+       name_hbox.pack_end (name_label, true, true);
+
+       // set min. track-header width if fader is not visible
+       name_hbox.set_size_request(name_width_px, 0);
+
        name_hbox.show ();
        name_label.show ();
-       
-       controls_table.set_size_request (200);
+
        controls_table.set_row_spacings (2);
        controls_table.set_col_spacings (2);
        controls_table.set_border_width (2);
-       controls_table.set_homogeneous (true);
 
-       controls_table.attach (name_hbox, 0, 5, 0, 1,  Gtk::FILL|Gtk::EXPAND,  Gtk::FILL|Gtk::EXPAND, 3, 0);
+       if (ARDOUR::Profile->get_mixbus() ) {
+               controls_table.attach (name_hbox, 4, 5, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
+       } else {
+               controls_table.attach (name_hbox, 1, 2, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
+       }
        controls_table.show_all ();
        controls_table.set_no_show_all ();
 
-       HSeparator* separator = manage (new HSeparator());
-       separator->set_name("TrackSeparator");
-       separator->set_size_request(-1, 1);
-       separator->show();
-
        controls_vbox.pack_start (controls_table, false, false);
        controls_vbox.show ();
 
-       controls_hbox.pack_start (controls_vbox, true, true);
-       controls_hbox.show ();
+       top_hbox.pack_start (controls_vbox, true, true);
+       top_hbox.show ();
 
-       //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
-       controls_ebox.add (controls_hbox);
+       controls_ebox.add (top_hbox);
        controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
                                  Gdk::BUTTON_RELEASE_MASK|
                                  Gdk::POINTER_MOTION_MASK|
@@ -160,7 +175,16 @@ 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_vbox.pack_start (controls_ebox, true, true, 0);
+       time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
+       time_axis_frame.add(controls_ebox);
+       time_axis_frame.show();
+
+       HSeparator* separator = manage (new HSeparator());
+       separator->set_name("TrackSeparator");
+       separator->set_size_request(-1, 1);
+       separator->show();
+
+       time_axis_vbox.pack_start (time_axis_frame, true, true);
        time_axis_vbox.pack_end (*separator, false, false);
        time_axis_vbox.show();
 
@@ -178,7 +202,7 @@ TimeAxisView::~TimeAxisView()
        }
 
        for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
- delete (*i)->rect;
              delete (*i)->rect;
                delete (*i)->start_trim;
                delete (*i)->end_trim;
 
@@ -293,10 +317,7 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
                        }
                        e.stepping_axis_view()->step_height (false);
                        return true;
-               } else if (Keyboard::no_modifiers_active (ev->state)) {
-                       _editor.scroll_tracks_up_line();
-                       return true;
-               }
+               } 
                break;
 
        case GDK_SCROLL_DOWN:
@@ -308,10 +329,7 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
                        }
                        e.stepping_axis_view()->step_height (true);
                        return true;
-               } else if (Keyboard::no_modifiers_active (ev->state)) {
-                       _editor.scroll_tracks_down_line();
-                       return true;
-               }
+               } 
                break;
 
        default:
@@ -319,7 +337,14 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
                break;
        }
 
-       return false;
+       /* Just forward to the normal canvas scroll method. The coordinate
+          systems are different but since the canvas is always larger than the
+          track headers, and aligned with the trackview area, this will work.
+
+          In the not too distant future this layout is going away anyway and
+          headers will be on the canvas.
+       */
+       return _editor.canvas_scroll_event (ev, false);
 }
 
 bool
@@ -366,32 +391,11 @@ 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.)
-                *
-                * First, translate the event coordinates into the canvas
-                * coordinate space that DragManager::motion_handler is
-                * expecting (this requires translation into the *window*
-                * coordinates for the track canvas window, and then conversion
-                * from window to canvas coordinate spaces).
-                * 
-                * Then fake a DragManager motion event so that when
-                * maybe_autoscroll asks DragManager for the current pointer
-                * position it will get the correct answers.
-               */
-
-               int tx, ty;
-               controls_ebox.translate_coordinates (*_editor.get_track_canvas(), ev->x, ev->y, tx, ty);
-
-               /* x-axis of track headers is not shared with the canvas, but
-                  the y-axis is, so we we can get a valid translation here.
                */
 
-               Duple canvas_coord = _editor.get_track_canvas()->canvas()->window_to_canvas (Duple (tx, ty));
-               ev->y = (int) floor (canvas_coord.y);
+               _editor.maybe_autoscroll (false, true, true);
 
-               _editor.drags()->motion_handler ((GdkEvent *) ev, false);
-               _editor.maybe_autoscroll (false, true, false, ev->y_root < _resize_drag_start);
-
-               /* now do the actual TAV resize */
+               /* 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;
@@ -541,6 +545,8 @@ TimeAxisView::set_height (uint32_t h)
                /* resize the selection rect */
                show_selection (_editor.get_selection().time);
        }
+
+       _editor.override_visible_track_count ();
 }
 
 bool
@@ -603,6 +609,8 @@ TimeAxisView::begin_name_edit ()
 
                name_entry = manage (new Gtkmm2ext::FocusEntry);
                
+               name_entry->set_width_chars(8); // min width, entry expands
+
                name_entry->set_name ("EditorTrackNameDisplay");
                name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
                name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
@@ -614,7 +622,7 @@ TimeAxisView::begin_name_edit ()
                        name_hbox.remove (name_label);
                }
                
-               name_hbox.pack_start (*name_entry);
+               name_hbox.pack_end (*name_entry, true, true);
                name_entry->show ();
 
                name_entry->select_region (0, -1);
@@ -659,7 +667,7 @@ TimeAxisView::end_name_edit (int response)
 
        /* put the name label back */
 
-       name_hbox.pack_start (name_label);
+       name_hbox.pack_end (name_label);
        name_label.show ();
 
        if (edit_next) {
@@ -688,7 +696,7 @@ TimeAxisView::end_name_edit (int response)
                }
 
                if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
-                       _editor.ensure_time_axis_view_is_visible (**i);
+                       _editor.ensure_time_axis_view_is_visible (**i, false);
                        (*i)->begin_name_edit ();
                } 
 
@@ -719,7 +727,7 @@ TimeAxisView::end_name_edit (int response)
                }
                
                if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
-                       _editor.ensure_time_axis_view_is_visible (**i);
+                       _editor.ensure_time_axis_view_is_visible (**i, false);
                        (*i)->begin_name_edit ();
                } 
        }
@@ -765,13 +773,18 @@ TimeAxisView::set_selected (bool yn)
        Selectable::set_selected (yn);
 
        if (_selected) {
+               time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
+               time_axis_frame.set_name ("MixerStripSelectedFrame");
                controls_ebox.set_name (controls_base_selected_name);
-               time_axis_vbox.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_NONE);
+               time_axis_frame.set_name (controls_base_unselected_name);
                controls_ebox.set_name (controls_base_unselected_name);
-               time_axis_vbox.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 ();
 
                /* children will be set for the yn=true case. but when deselecting
@@ -783,6 +796,9 @@ TimeAxisView::set_selected (bool yn)
                        (*i)->set_selected (false);
                }
        }
+
+       time_axis_frame.show();
+
 }
 
 void
@@ -828,7 +844,8 @@ TimeAxisView::show_selection (TimeSelection& ts)
        double x1;
        double x2;
        double y2;
-       SelectionRect *rect;
+       SelectionRect *rect;    time_axis_frame.show();
+
 
        for (Children::iterator i = children.begin(); i != children.end(); ++i) {
                (*i)->show_selection (ts);
@@ -860,9 +877,9 @@ TimeAxisView::show_selection (TimeSelection& ts)
 
                x1 = _editor.sample_to_pixel (start);
                x2 = _editor.sample_to_pixel (start + cnt - 1);
-               y2 = current_height();
+               y2 = current_height() - 1;
 
-               rect->rect->set (ArdourCanvas::Rect (x1, 1, x2, y2));
+               rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
 
                // trim boxes are at the top for selections
 
@@ -969,12 +986,12 @@ TimeAxisView::get_selection_rect (uint32_t id)
                rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
 
                rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
-               CANVAS_DEBUG_NAME (rect->rect, "selection rect start trim");
+               CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
                rect->start_trim->set_outline (false);
                rect->start_trim->set_fill (false);
 
                rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
-               CANVAS_DEBUG_NAME (rect->rect, "selection rect end trim");
+               CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
                rect->end_trim->set_outline (false);
                rect->end_trim->set_fill (false);
 
@@ -1098,38 +1115,26 @@ TimeAxisView::reset_height ()
 void
 TimeAxisView::compute_heights ()
 {
+       // TODO this function should be re-evaluated when font-scaling changes (!)
        Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
-       Gtk::Table two_row_table (2, 8);
-       Gtk::Table one_row_table (1, 8);
-       Button* buttons[5];
+       Gtk::Table one_row_table (1, 1);
+       ArdourButton* test_button = manage (new ArdourButton);
        const int border_width = 2;
-
-       const int separator_height = 2;
-       extra_height = (2 * border_width) + separator_height;
+       const int frame_height = 2;
+       extra_height = (2 * border_width) + frame_height;
 
        window.add (one_row_table);
+       test_button->set_name ("mute button");
+       test_button->set_text (_("M"));
 
        one_row_table.set_border_width (border_width);
-       one_row_table.set_row_spacings (0);
-       one_row_table.set_col_spacings (0);
-       one_row_table.set_homogeneous (true);
-
-       two_row_table.set_border_width (border_width);
-       two_row_table.set_row_spacings (0);
-       two_row_table.set_col_spacings (0);
-       two_row_table.set_homogeneous (true);
-
-       for (int i = 0; i < 5; ++i) {
-               buttons[i] = manage (new Button (X_("f")));
-               buttons[i]->set_name ("TrackMuteButton");
-       }
-
-       one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
+       one_row_table.set_row_spacings (2);
+       one_row_table.set_col_spacings (2);
 
+       one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
        one_row_table.show_all ();
-       Gtk::Requisition req(one_row_table.size_request ());
 
-       // height required to show 1 row of buttons
+       Gtk::Requisition req(one_row_table.size_request ());
        button_height = req.height;
 }
 
@@ -1166,14 +1171,17 @@ TimeAxisView::color_handler ()
 }
 
 /** @return Pair: TimeAxisView, layer index.
- * TimeAxisView is non-0 if this object covers y, or one of its children does.
+ * TimeAxisView is non-0 if this object covers @param y, or one of its children
+ * does. @param y is an offset from the top of the trackview area.
+ *
  * If the covering object is a child axis, then the child is returned.
  * TimeAxisView is 0 otherwise.
+ *
  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
  * and is in stacked or expanded * region display mode, otherwise 0.
  */
 std::pair<TimeAxisView*, double>
-TimeAxisView::covers_y_position (double y)
+TimeAxisView::covers_y_position (double y) const
 {
        if (hidden()) {
                return std::make_pair ((TimeAxisView *) 0, 0);
@@ -1208,7 +1216,7 @@ TimeAxisView::covers_y_position (double y)
                        break;
                }
 
-               return std::make_pair (this, l);
+               return std::make_pair (const_cast<TimeAxisView*>(this), l);
        }
 
        for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
@@ -1222,6 +1230,30 @@ TimeAxisView::covers_y_position (double y)
        return std::make_pair ((TimeAxisView *) 0, 0);
 }
 
+bool
+TimeAxisView::covered_by_y_range (double y0, double y1) const
+{
+       if (hidden()) {
+               return false;
+       }
+
+       /* if either the top or bottom of the axisview is in the vertical
+        * range, we cover it.
+        */
+       
+       if ((y0 < _y_position && y1 < _y_position) ||
+           (y0 >= _y_position + height && y1 >= _y_position + height)) {
+               return false;
+       }
+
+       for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
+               if ((*i)->covered_by_y_range (y0, y1)) {
+                       return true;
+               }
+       }
+
+       return true;
+}
 
 uint32_t
 TimeAxisView::preset_height (Height h)