b9ff987d531705f9d57f3969bcb4aab78bfe9733
[ardour.git] / gtk2_ardour / time_axis_view.cc
1 /*
2     Copyright (C) 2000 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     $Id$
19 */
20
21 #include <cstdlib>
22 #include <cmath>
23 #include <algorithm>
24 #include <string>
25 #include <list>
26
27 #include <libgnomecanvasmm/libgnomecanvasmm.h>
28 #include <libgnomecanvasmm/canvas.h>
29 #include <libgnomecanvasmm/item.h>
30
31 #include <pbd/error.h>
32
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/selector.h>
35 #include <gtkmm2ext/stop_signal.h>
36
37 #include <ardour/session.h>
38 #include <ardour/utils.h>
39 #include <ardour/ladspa_plugin.h>
40 #include <ardour/insert.h>
41 #include <ardour/location.h>
42
43 #include "ardour_ui.h"
44 #include "public_editor.h"
45 #include "time_axis_view.h"
46 #include "simplerect.h"
47 #include "selection.h"
48 #include "keyboard.h"
49 #include "rgb_macros.h"
50 #include "utils.h"
51
52 #include "i18n.h"
53
54 using namespace Gtk;
55 using namespace sigc; 
56 using namespace ARDOUR;
57 using namespace Editing;
58 using namespace ArdourCanvas;
59
60 const double trim_handle_size = 6.0; /* pixels */
61
62 TimeAxisView::TimeAxisView (ARDOUR::Session& sess, PublicEditor& ed, TimeAxisView* rent, Canvas& canvas) 
63         : AxisView (sess), 
64           editor (ed),
65           controls_table (2, 9)
66 {
67         canvas_display = new Group (*canvas.root(), 0.0, 0.0);
68         
69         selection_group = new Group (*canvas_display);
70         selection_group->hide();
71         
72         control_parent = 0;
73         display_menu = 0;
74         size_menu = 0;
75         _marked_for_display = false;
76         _hidden = false;
77         height = 0;
78         effective_height = 0;
79         parent = rent;
80         _has_state = false;
81         last_name_entry_key_press_event = 0;
82
83         /*
84           Create the standard LHS Controls
85           We create the top-level container and name add the name label here,
86           subclasses can add to the layout as required
87         */
88
89         name_entry.set_name ("EditorTrackNameDisplay");
90         name_entry.signal_button_release_event().connect (mem_fun (*this, &TimeAxisView::name_entry_button_release));
91         name_entry.signal_button_press_event().connect (mem_fun (*this, &TimeAxisView::name_entry_button_press));
92         name_entry.signal_key_release_event().connect (mem_fun (*this, &TimeAxisView::name_entry_key_release));
93         name_entry.signal_activate().connect (mem_fun(*this, &TimeAxisView::name_entry_activated));
94         name_entry.signal_focus_in_event().connect (mem_fun (*this, &TimeAxisView::name_entry_focus_in));
95         name_entry.signal_focus_out_event().connect (mem_fun (*this, &TimeAxisView::name_entry_focus_out));
96         Gtkmm2ext::set_size_request_to_display_given_text (name_entry, N_("gTortnam"), 10, 10); // just represents a short name
97
98         name_label.set_name ("TrackLabel");
99         name_label.set_alignment (0.0, 0.5);
100
101         // name_hbox.set_border_width (2);
102         // name_hbox.set_spacing (5);
103
104         /* typically, either name_label OR name_entry are visible,
105            but not both. its up to derived classes to show/hide them as they
106            wish.
107         */
108
109         name_hbox.pack_start (name_label, true, true);
110         name_hbox.pack_start (name_entry, true, true);
111         name_hbox.show ();
112
113         controls_table.set_border_width (2);
114         controls_table.set_row_spacings (0);
115         controls_table.set_col_spacings (0);
116         controls_table.set_homogeneous (true);
117         controls_table.show ();
118
119         controls_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
120
121         controls_table.show ();
122
123         controls_vbox.pack_start (controls_table, false, false);
124         controls_vbox.show ();
125
126         controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
127         controls_ebox.add (controls_vbox);
128         controls_ebox.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
129         controls_ebox.set_flags (Gtk::CAN_FOCUS);
130
131         controls_ebox.signal_button_release_event().connect (mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
132         
133         controls_lhs_pad.set_name ("TimeAxisViewControlsPadding");
134         controls_hbox.pack_start (controls_lhs_pad,false,false);
135         controls_hbox.pack_start (controls_ebox,true,true);
136         controls_hbox.show ();
137
138         controls_frame.add (controls_hbox);
139         controls_frame.set_name ("TimeAxisViewControlsBaseUnselected");
140         controls_frame.set_shadow_type (Gtk::SHADOW_OUT);
141 }
142
143 TimeAxisView::~TimeAxisView()
144 {
145         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
146                 delete *i;
147         }
148
149         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
150                 delete (*i)->rect;
151                 delete (*i)->start_trim;
152                 delete (*i)->end_trim;
153
154         }
155
156         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
157                 delete (*i)->rect;
158                 delete (*i)->start_trim;
159                 delete (*i)->end_trim;
160         }
161
162         if (selection_group) {
163                 delete selection_group;
164                 selection_group = 0;
165         }
166
167         if (canvas_display) {
168                 delete canvas_display;
169                 canvas_display = 0;
170         }
171
172         if (display_menu) {
173                 delete display_menu;
174                 display_menu = 0;
175         }
176
177         if (size_menu) {
178                 delete size_menu;
179                 size_menu = 0;
180         }
181 }
182
183 guint32
184 TimeAxisView::show_at (double y, int& nth, VBox *parent)
185 {
186         gdouble ix1, ix2, iy1, iy2;
187         effective_height = 0;
188
189         if (control_parent) {
190                 control_parent->reorder_child (controls_frame, nth);
191         } else {
192                 control_parent = parent;
193                 parent->pack_start (controls_frame, false, false);
194                 parent->reorder_child (controls_frame, nth);
195         }
196
197         controls_frame.show ();
198         controls_ebox.show ();
199
200         /* the coordinates used here are in the system of the
201            item's parent ...
202         */
203
204         canvas_display->get_bounds (ix1, iy1, ix2, iy2);
205         Group* pg = canvas_display->property_parent();
206         pg->i2w (ix1, iy1);
207
208         if (iy1 < 0) {
209                 iy1 = 0;
210         }
211
212         canvas_display->move (0.0, y - iy1);
213         canvas_display->show();/* XXX not necessary */
214         y_position = y;
215         order = nth;
216         _hidden = false;
217         
218         effective_height = height;
219
220         /* now show children */
221         
222         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
223                 
224                 if ((*i)->marked_for_display()) {
225                         (*i)->canvas_display->show();
226                 }
227                 
228                 if (canvas_item_visible ((*i)->canvas_display)) {
229                         ++nth;
230                         effective_height += (*i)->show_at (y + effective_height, nth, parent);
231                 }
232         }
233
234         return effective_height;
235 }
236
237 gint
238 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
239 {
240         switch (ev->button) {
241         case 1:
242                 selection_click (ev);
243                 break;
244
245         case 3:
246                 popup_display_menu (ev->time);
247                 break;
248
249         case 4:
250                 if (Keyboard::no_modifier_keys_pressed (ev)) {
251                         editor.scroll_tracks_up_line ();
252                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
253                         step_height (true);
254                 }
255                 break;
256
257         case 5:
258                 if (Keyboard::no_modifier_keys_pressed (ev)) {
259                         editor.scroll_tracks_down_line ();
260                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
261                         step_height (false);
262                 }
263                 break;
264                 
265
266         }
267
268         return TRUE;
269 }
270
271 void
272 TimeAxisView::selection_click (GdkEventButton* ev)
273 {
274         if (Keyboard::modifier_state_contains (ev->state, Keyboard::Shift)) {
275
276                 if (editor.get_selection().selected (this)) {
277                         editor.get_selection().remove (this);
278                 } else {
279                         editor.get_selection().add (this);
280                 }
281
282         } else {
283
284                 editor.get_selection().set (this);
285         }
286 }
287
288 void
289 TimeAxisView::hide ()
290 {
291         if (_hidden) {
292                 return;
293         }
294
295         canvas_display->hide();
296         controls_frame.hide ();
297
298         if (control_parent) {
299                 control_parent->remove (controls_frame);
300                 control_parent = 0;
301         }
302
303         y_position = -1;
304         _hidden = true;
305         
306         /* now hide children */
307         
308         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
309                 (*i)->hide ();
310         }
311         
312         Hiding ();
313 }
314
315 void
316 TimeAxisView::step_height (bool bigger)
317 {
318         switch (height) {
319         case Largest:
320                 if (!bigger) set_height (Large);
321                 break;
322         case Large:
323                 if (bigger) set_height (Largest);
324                 else set_height (Larger);
325                 break;
326         case Larger:
327                 if (bigger) set_height (Large);
328                 else set_height (Normal);
329                 break;
330         case Normal:
331                 if (bigger) set_height (Larger);
332                 else set_height (Smaller);
333                 break;
334         case Smaller:
335                 if (bigger) set_height (Normal);
336                 else set_height (Small);
337                 break;
338         case Small:
339                 if (bigger) set_height (Smaller);
340                 break;
341         }
342 }
343
344
345 void
346 TimeAxisView::set_height (TrackHeight h)
347 {
348         height = (guint32) h;
349         controls_frame.set_size_request (-1, height);
350
351         if (canvas_item_visible (selection_group)) {
352                 /* resize the selection rect */
353                 show_selection (editor.get_selection().time);
354         }
355
356 //      for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
357 //              (*i)->set_height (h);
358 //      }
359
360 }
361
362 bool
363 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
364 {
365         switch (ev->keyval) {
366         case GDK_Tab:
367         case GDK_Up:
368         case GDK_Down:
369                 name_entry_changed ();
370                 return true;
371
372         default:
373                 break;
374         }
375
376 #ifdef TIMEOUT_NAME_EDIT        
377         /* adapt the timeout to reflect the user's typing speed */
378
379         guint32 name_entry_timeout;
380
381         if (last_name_entry_key_press_event) {
382                 /* timeout is 1/2 second or 5 times their current inter-char typing speed */
383                 name_entry_timeout = std::max (500U, (5 * (ev->time - last_name_entry_key_press_event)));
384         } else {
385                 /* start with a 1 second timeout */
386                 name_entry_timeout = 1000;
387         }
388
389         last_name_entry_key_press_event = ev->time;
390
391         /* wait 1 seconds and if no more keys are pressed, act as if they pressed enter */
392
393         name_entry_key_timeout.disconnect();
394         name_entry_key_timeout = Glib::signal_timeout().connect (mem_fun (*this, &TimeAxisView::name_entry_key_timed_out), name_entry_timeout);
395 #endif
396
397         return false;
398 }
399
400 bool
401 TimeAxisView::name_entry_focus_in (GdkEventFocus* ev)
402 {
403         name_entry.select_region (0, -1);
404         name_entry.set_name ("EditorActiveTrackNameDisplay");
405         return false;
406 }
407
408 bool
409 TimeAxisView::name_entry_focus_out (GdkEventFocus* ev)
410 {
411         /* clean up */
412
413         last_name_entry_key_press_event = 0;
414         name_entry_key_timeout.disconnect ();
415         name_entry.set_name ("EditorTrackNameDisplay");
416         
417         /* do the real stuff */
418
419         name_entry_changed ();
420
421         return false;
422 }
423
424 bool
425 TimeAxisView::name_entry_key_timed_out ()
426 {
427         name_entry_activated();
428         return false;
429 }
430
431 void
432 TimeAxisView::name_entry_activated ()
433 {
434         controls_ebox.grab_focus();
435 }
436
437 void
438 TimeAxisView::name_entry_changed ()
439 {
440 }
441
442 bool
443 TimeAxisView::name_entry_button_press (GdkEventButton *ev)
444 {
445         if (ev->button == 3) {
446                 return true;
447         }
448         return false;
449 }
450
451 bool
452 TimeAxisView::name_entry_button_release (GdkEventButton *ev)
453 {
454         if (ev->button == 3) {
455                 popup_display_menu (ev->time);
456                 return true;
457         }
458         return false;
459 }
460
461 void
462 TimeAxisView::popup_display_menu (guint32 when)
463 {
464         if (display_menu == 0) {
465                 build_display_menu ();
466         }
467         display_menu->popup (1, when);  
468 }
469
470 gint
471 TimeAxisView::size_click (GdkEventButton *ev)
472 {
473         popup_size_menu (ev->time);
474         return TRUE;
475 }
476
477 void
478 TimeAxisView::popup_size_menu (guint32 when)
479 {
480         if (size_menu == 0) {
481                 build_size_menu ();
482         }
483         size_menu->popup (1, when);
484 }
485
486 void
487 TimeAxisView::set_selected (bool yn)
488 {
489         AxisView::set_selected (yn);
490
491         if (_selected) {
492                 controls_ebox.set_name (controls_base_selected_name);
493                 controls_frame.set_name (controls_base_selected_name);
494
495                 /* propagate any existing selection, if the mode is right */
496
497                 if (editor.current_mouse_mode() == Editing::MouseRange && !editor.get_selection().time.empty()) {
498                         show_selection (editor.get_selection().time);
499                 }
500
501         } else {
502                 controls_ebox.set_name (controls_base_unselected_name);
503                 controls_frame.set_name (controls_base_unselected_name);
504
505                 hide_selection ();
506
507                 /* children will be set for the yn=true case. but when deselecting
508                    the editor only has a list of top-level trackviews, so we
509                    have to do this here.
510                 */
511
512                 for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
513                         (*i)->set_selected (false);
514                 }
515
516                 
517         }
518 }
519
520 void
521 TimeAxisView::build_size_menu ()
522 {
523         using namespace Menu_Helpers;
524
525         size_menu = new Menu;
526         size_menu->set_name ("ArdourContextMenu");
527         MenuList& items = size_menu->items();
528         
529         items.push_back (MenuElem (_("Largest"), bind (mem_fun (*this, &TimeAxisView::set_height), Largest)));
530         items.push_back (MenuElem (_("Large"), bind (mem_fun (*this, &TimeAxisView::set_height), Large)));
531         items.push_back (MenuElem (_("Larger"), bind (mem_fun (*this, &TimeAxisView::set_height), Larger)));
532         items.push_back (MenuElem (_("Normal"), bind (mem_fun (*this, &TimeAxisView::set_height), Normal)));
533         items.push_back (MenuElem (_("Smaller"), bind (mem_fun (*this, &TimeAxisView::set_height), Smaller)));
534         items.push_back (MenuElem (_("Small"), bind (mem_fun (*this, &TimeAxisView::set_height), Small)));
535 }
536
537 void
538 TimeAxisView::build_display_menu ()
539 {
540         using namespace Menu_Helpers;
541
542         display_menu = new Menu;
543         display_menu->set_name ("ArdourContextMenu");
544
545         // Just let implementing classes define what goes into the manu
546 }
547
548 void
549 TimeAxisView::set_samples_per_unit (double spu)
550 {
551         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
552                 (*i)->set_samples_per_unit (spu);
553         }
554 }
555
556 void
557 TimeAxisView::show_timestretch (jack_nframes_t start, jack_nframes_t end)
558 {
559         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
560                 (*i)->show_timestretch (start, end);
561         }
562 }
563
564 void
565 TimeAxisView::hide_timestretch ()
566 {
567         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
568                 (*i)->hide_timestretch ();
569         }
570 }
571
572 void
573 TimeAxisView::show_selection (TimeSelection& ts)
574 {
575         double x1;
576         double x2;
577         double y2;
578         SelectionRect *rect;
579
580         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
581                 (*i)->show_selection (ts);
582         }
583
584         if (canvas_item_visible (selection_group)) {
585                 while (!used_selection_rects.empty()) {
586                         free_selection_rects.push_front (used_selection_rects.front());
587                         used_selection_rects.pop_front();
588                         free_selection_rects.front()->rect->hide();
589                         free_selection_rects.front()->start_trim->hide();
590                         free_selection_rects.front()->end_trim->hide();
591                 }
592                 selection_group->hide();
593         }
594
595         selection_group->show();
596         selection_group->raise_to_top();
597         
598         for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
599                 jack_nframes_t start, end, cnt;
600
601                 start = (*i).start;
602                 end = (*i).end;
603                 cnt = end - start + 1;
604
605                 rect = get_selection_rect ((*i).id);
606                 
607                 x1 = start / editor.get_current_zoom();
608                 x2 = (start + cnt - 1) / editor.get_current_zoom();
609                 y2 = height;
610
611                 rect->rect->property_x1() = x1;
612                 rect->rect->property_y1() = 1.0;
613                 rect->rect->property_x2() = x2;
614                 rect->rect->property_y2() = y2;
615                 
616                 // trim boxes are at the top for selections
617                 
618                 if (x2 > x1) {
619                         rect->start_trim->property_x1() = x1;
620                         rect->start_trim->property_y1() = 1.0;
621                         rect->start_trim->property_x2() = x1 + trim_handle_size;
622                         rect->start_trim->property_y2() = 1.0 + trim_handle_size;
623
624                         rect->end_trim->property_x1() = x2 - trim_handle_size;
625                         rect->end_trim->property_y1() = 1.0;
626                         rect->end_trim->property_x2() = x2;
627                         rect->end_trim->property_y2() = 1.0 + trim_handle_size;
628
629                         rect->start_trim->show();
630                         rect->end_trim->show();
631                 } else {
632                         rect->start_trim->hide();
633                         rect->end_trim->hide();
634                 }
635
636                 rect->rect->show ();
637                 used_selection_rects.push_back (rect);
638         }
639 }
640
641 void
642 TimeAxisView::reshow_selection (TimeSelection& ts)
643 {
644         show_selection (ts);
645
646         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
647                 (*i)->show_selection (ts);
648         }
649 }
650
651 void
652 TimeAxisView::hide_selection ()
653 {
654         if (canvas_item_visible (selection_group)) {
655                 while (!used_selection_rects.empty()) {
656                         free_selection_rects.push_front (used_selection_rects.front());
657                         used_selection_rects.pop_front();
658                         free_selection_rects.front()->rect->hide();
659                         free_selection_rects.front()->start_trim->hide();
660                         free_selection_rects.front()->end_trim->hide();
661                 }
662                 selection_group->hide();
663         }
664         
665         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
666                 (*i)->hide_selection ();
667         }
668 }
669
670 void
671 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
672 {
673         /* find the selection rect this is for. we have the item corresponding to one
674            of the trim handles.
675          */
676
677         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
678                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
679                         
680                         /* make one trim handle be "above" the other so that if they overlap,
681                            the top one is the one last used.
682                         */
683                         
684                         (*i)->rect->raise_to_top ();
685                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
686                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
687                         
688                         break;
689                 }
690         }
691 }
692
693 SelectionRect *
694 TimeAxisView::get_selection_rect (uint32_t id)
695 {
696         SelectionRect *rect;
697
698         /* check to see if we already have a visible rect for this particular selection ID */
699
700         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
701                 if ((*i)->id == id) {
702                         return (*i);
703                 }
704         }
705
706         /* ditto for the free rect list */
707
708         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
709                 if ((*i)->id == id) {
710                         free_selection_rects.erase (i);
711                         return (*i);
712                 }
713         }
714
715         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
716         
717         if (free_selection_rects.empty()) {
718
719                 rect = new SelectionRect;
720
721                 rect->rect = new SimpleRect (*selection_group);
722                 rect->rect->property_x1() = 0.0;
723                 rect->rect->property_y1() = 0.0;
724                 rect->rect->property_x2() = 0.0;
725                 rect->rect->property_y2() = 0.0;
726                 rect->rect->property_fill_color_rgba() = color_map[cSelectionRectFill];
727                 rect->rect->property_outline_color_rgba() = color_map[cSelectionRectOutline];
728                 
729                 rect->start_trim = new SimpleRect (*selection_group);
730                 rect->start_trim->property_x1() = 0.0;
731                 rect->start_trim->property_x2() = 0.0;
732                 rect->start_trim->property_fill_color_rgba() = color_map[cSelectionStartFill];
733                 rect->start_trim->property_outline_color_rgba() = color_map[cSelectionStartOutline];
734                 
735                 rect->end_trim = new SimpleRect (*selection_group);
736                 rect->end_trim->property_x1() = 0.0;
737                 rect->end_trim->property_x2() = 0.0;
738                 rect->end_trim->property_fill_color_rgba() = color_map[cSelectionEndFill];
739                 rect->end_trim->property_outline_color_rgba() = color_map[cSelectionEndOutline];
740
741                 free_selection_rects.push_front (rect);
742
743                 rect->rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
744                 rect->start_trim->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
745                 rect->end_trim->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
746         } 
747
748         rect = free_selection_rects.front();
749         rect->id = id;
750         free_selection_rects.pop_front();
751         return rect;
752 }
753
754 bool
755 TimeAxisView::is_child (TimeAxisView* tav)
756 {
757         return find (children.begin(), children.end(), tav) != children.end();
758 }
759
760 void
761 TimeAxisView::add_child (TimeAxisView* child)
762 {
763         children.push_back (child);
764 }
765
766 void
767 TimeAxisView::remove_child (TimeAxisView* child)
768 {
769         vector<TimeAxisView*>::iterator i;
770
771         if ((i = find (children.begin(), children.end(), child)) != children.end()) {
772                 children.erase (i);
773         }
774 }
775
776 void
777 TimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& result)
778 {
779         return;
780 }
781
782 void
783 TimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
784 {
785         return;
786 }
787
788 bool
789 TimeAxisView::touched (double top, double bot)
790 {
791         /* remember: this is X Window - coordinate space starts in upper left and moves down.
792            y_position is the "origin" or "top" of the track.
793          */
794
795         double mybot = y_position + height; // XXX need to include Editor::track_spacing; 
796         
797         return ((y_position <= bot && y_position >= top) || 
798                 ((mybot <= bot) && (top < mybot)) || 
799                 (mybot >= bot && y_position < top));
800 }               
801
802 void
803 TimeAxisView::set_parent (TimeAxisView& p)
804 {
805         parent = &p;
806 }
807
808 bool
809 TimeAxisView::has_state () const
810 {
811         return _has_state;
812 }
813
814 TimeAxisView*
815 TimeAxisView::get_parent_with_state ()
816 {
817         if (parent == 0) {
818                 return 0;
819         }
820
821         if (parent->has_state()) {
822                 return parent;
823         } 
824
825         return parent->get_parent_with_state ();
826 }               
827
828 void
829 TimeAxisView::set_state (const XMLNode& node)
830 {
831         const XMLProperty *prop;
832
833         if ((prop = node.property ("track_height")) != 0) {
834
835                 if (prop->value() == "largest") {
836                         set_height (Largest);
837                 } else if (prop->value() == "large") {
838                         set_height (Large);
839                 } else if (prop->value() == "larger") {
840                         set_height (Larger);
841                 } else if (prop->value() == "normal") {
842                         set_height (Normal);
843                 } else if (prop->value() == "smaller") {
844                         set_height (Smaller);
845                 } else if (prop->value() == "small") {
846                         set_height (Small);
847                 } else {
848                         error << string_compose(_("unknown track height name \"%1\" in XML GUI information"), prop->value()) << endmsg;
849                         set_height (Normal);
850                 }
851
852         } else {
853                 set_height (Normal);
854         }
855 }
856
857 void
858 TimeAxisView::reset_height()
859 {
860         set_height ((TrackHeight) height);
861
862         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
863                 (*i)->set_height ((TrackHeight)(*i)->height);
864         }
865 }
866