fixes for destructive track offsets of various kinds; move from jack_nframes_t -...
[ardour.git] / gtk2_ardour / editor_imageframe.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis 
3     Written by Colin Law, CMT, Glasgow
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19     $Id$
20 */
21
22 #include "imageframe_view.h"
23 #include "imageframe_time_axis.h"
24 #include "imageframe_time_axis_view.h"
25 #include "imageframe_time_axis_group.h"
26 #include "marker_time_axis_view.h"
27 #include "marker_time_axis.h"
28 #include "marker_view.h"
29 #include "editor.h"
30 #include "i18n.h"
31 #include "canvas_impl.h"
32
33 #include <gtkmm2ext/gtk_ui.h>
34 #include <pbd/error.h>
35
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <unistd.h>
40 #include <arpa/inet.h>
41
42 #include "imageframe_socket_handler.h"
43 #include "ardour_image_compositor_socket.h"
44 #include "public_editor.h"
45
46 using namespace Gtk;
47 using namespace PBD;
48
49 /* <CMT Additions file="editor.cc"> */
50
51 void
52 Editor::add_imageframe_time_axis(const string & track_name, void* src)
53 {
54         // check for duplicate name
55         if(get_named_time_axis(track_name))
56         {
57                 warning << "Repeated time axis name" << std::endl ;
58         }
59         else
60         {
61                 Gtkmm2ext::UI::instance()->call_slot(bind(mem_fun(*this, &Editor::handle_new_imageframe_time_axis_view),track_name, src)) ;
62         }
63 }
64
65 void
66 Editor::connect_to_image_compositor()
67 {
68         if(image_socket_listener == 0)
69         {
70                 image_socket_listener = ImageFrameSocketHandler::create_instance(*this) ;
71         }
72         
73         if(image_socket_listener->is_connected() == true)
74         {
75                 return ;
76         }
77
78         // XXX should really put this somewhere safe
79         const char * host_ip = "127.0.0.1" ;
80         
81         bool retcode = image_socket_listener->connect(host_ip, ardourvis::DEFAULT_PORT) ;
82         
83         if(retcode == false)
84         {
85                 // XXX need to get some return status here
86                 warning << "Image Compositor Connection attempt failed" << std::endl ;
87                 return ;
88         }
89         
90         // add the socket to the gui loop, and keep the retuned tag value of the input
91         gint tag = gdk_input_add(image_socket_listener->get_socket_descriptor(), GDK_INPUT_READ,ImageFrameSocketHandler::image_socket_callback,image_socket_listener) ;
92         image_socket_listener->set_gdk_input_tag(tag) ;
93 }
94
95 void
96 Editor::scroll_timeaxis_to_imageframe_item(const TimeAxisViewItem* item)
97 {
98         // GTK2FIX
99         //nframes_t offset = static_cast<nframes_t>(frames_per_unit * (edit_hscroll_slider_width/2)) ;
100         nframes_t offset = 0;
101
102         nframes_t x_pos = 0 ;
103         if(item->get_position() < offset)
104         {
105                 x_pos = 0 ;
106         }
107         else
108         {
109                 x_pos = item->get_position() - offset + (item->get_duration() / 2) ;
110         }
111         
112         reposition_x_origin(x_pos) ;
113 }
114
115 void
116 Editor::add_imageframe_marker_time_axis(const string & track_name, TimeAxisView* marked_track, void* src)
117 {
118         // Can we only bind 2 data Items?
119         // @todo we really want to bind the src attribute too, for the moment tracks can only be added remotely,
120         //       so this is not too much of an issue, however will need to be looked at again
121         Gtkmm2ext::UI::instance()->call_slot(sigc::bind(mem_fun(*this, &Editor::handle_new_imageframe_marker_time_axis_view),track_name, marked_track)) ;
122 }
123
124 void
125 Editor::popup_imageframe_edit_menu(int button, int32_t time, ArdourCanvas::Item* ifv, bool with_item)
126 {
127         ImageFrameTimeAxis* ifta = dynamic_cast<ImageFrameTimeAxis*>(clicked_trackview) ;
128         
129         if(ifta)
130         {
131                 ImageFrameTimeAxisGroup* iftag = ifta->get_view()->get_selected_imageframe_group() ;
132         
133                 if(iftag)
134                 {
135                         ImageFrameView* selected_ifv = ifta->get_view()->get_selected_imageframe_view() ;
136                         ifta->popup_imageframe_edit_menu(button, time, selected_ifv, with_item) ;
137                 }
138         }
139 }
140
141 void
142 Editor::popup_marker_time_axis_edit_menu(int button, int32_t time, ArdourCanvas::Item* ifv, bool with_item)
143 {
144         MarkerTimeAxis* mta = dynamic_cast<MarkerTimeAxis*>(clicked_trackview) ;
145         
146         if(mta)
147         {
148                 MarkerView* selected_mv = mta->get_view()->get_selected_time_axis_item() ;
149                 if(selected_mv)
150                 {
151                         mta->popup_marker_time_axis_edit_menu(button,time, selected_mv, with_item) ;
152                 }
153         }
154 }
155
156 TimeAxisView*
157 Editor::get_named_time_axis(const string & name)
158 {
159         TimeAxisView* tav = 0 ;
160         
161         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i)
162         {
163                 if (((TimeAxisView*)*i)->name() == name)
164                 {
165                         tav = ((TimeAxisView*)*i) ;
166                         break ;
167                 }
168         }
169         return(tav) ;
170 }
171
172 /* </CMT Additions file="editor.cc"> */
173
174
175
176
177
178
179 /* <CMT Additions file="editor_canvas_events.cc"> */
180 bool
181 Editor::canvas_imageframe_item_view_event (GdkEvent *event, ArdourCanvas::Item* item, ImageFrameView *ifv)
182 {
183         gint ret = FALSE ;
184         ImageFrameTimeAxisGroup* iftag = 0 ;
185         
186         switch (event->type)
187         {
188                 case GDK_BUTTON_PRESS:
189                 case GDK_2BUTTON_PRESS:
190                 case GDK_3BUTTON_PRESS:
191                         clicked_trackview = &ifv->get_time_axis_view();
192                         iftag = ifv->get_time_axis_group() ;
193                         dynamic_cast<ImageFrameTimeAxis*>(clicked_trackview)->get_view()->set_selected_imageframe_view(iftag, ifv);
194                         ret = button_press_handler (item, event, ImageFrameItem) ;
195                         break ;
196                 case GDK_BUTTON_RELEASE:
197                         ret = button_release_handler (item, event, ImageFrameItem) ;
198                         break ;
199                 case GDK_MOTION_NOTIFY:
200                         ret = motion_handler (item, event, ImageFrameItem) ;
201                         break ;
202                 default:
203                         break ;
204         }
205         return(ret) ;
206 }
207
208 bool
209 Editor::canvas_imageframe_start_handle_event (GdkEvent *event, ArdourCanvas::Item* item, ImageFrameView *ifv)
210 {
211         gint ret = FALSE ;
212         ImageFrameTimeAxisGroup* iftag = 0 ;
213         
214         switch (event->type)
215         {
216                 case GDK_BUTTON_PRESS:
217                 case GDK_2BUTTON_PRESS:
218                 case GDK_3BUTTON_PRESS:
219                         clicked_trackview = &ifv->get_time_axis_view() ;
220                         iftag = ifv->get_time_axis_group() ;
221                         dynamic_cast<ImageFrameTimeAxis*>(clicked_trackview)->get_view()->set_selected_imageframe_view(iftag, ifv);
222                         
223                         ret = button_press_handler (item, event, ImageFrameHandleStartItem) ;
224                         break ;
225                 case GDK_BUTTON_RELEASE:
226                         ret = button_release_handler (item, event, ImageFrameHandleStartItem) ;
227                         break;
228                 case GDK_MOTION_NOTIFY:
229                         ret = motion_handler (item, event, ImageFrameHandleStartItem) ;
230                         break ;
231                 case GDK_ENTER_NOTIFY:
232                         ret = enter_handler (item, event, ImageFrameHandleStartItem) ;
233                         break ;
234                 case GDK_LEAVE_NOTIFY:
235                         ret = leave_handler (item, event, ImageFrameHandleStartItem) ;
236                         break ;
237                 default:
238                         break ;
239         }
240         return(ret) ;
241 }
242
243 bool
244 Editor::canvas_imageframe_end_handle_event (GdkEvent *event, ArdourCanvas::Item* item, ImageFrameView *ifv)
245 {
246         gint ret = FALSE ;
247         ImageFrameTimeAxisGroup* iftag = 0 ;
248         
249         switch (event->type)
250         {
251                 case GDK_BUTTON_PRESS:
252                 case GDK_2BUTTON_PRESS:
253                 case GDK_3BUTTON_PRESS:
254                         clicked_trackview = &ifv->get_time_axis_view() ;
255                         iftag = ifv->get_time_axis_group() ;
256                         dynamic_cast<ImageFrameTimeAxis*>(clicked_trackview)->get_view()->set_selected_imageframe_view(iftag, ifv);
257                         
258                         ret = button_press_handler (item, event, ImageFrameHandleEndItem) ;
259                         break ;
260                 case GDK_BUTTON_RELEASE:
261                         ret = button_release_handler (item, event, ImageFrameHandleEndItem) ;
262                         break ;
263                 case GDK_MOTION_NOTIFY:
264                         ret = motion_handler (item, event, ImageFrameHandleEndItem) ;
265                         break ;
266                 case GDK_ENTER_NOTIFY:
267                         ret = enter_handler (item, event, ImageFrameHandleEndItem) ;
268                         break ;
269                 case GDK_LEAVE_NOTIFY:
270                         ret = leave_handler (item, event, ImageFrameHandleEndItem);
271                         break ;
272                 default:
273                         break ;
274         }
275         return(ret) ;
276 }
277
278 bool
279 Editor::canvas_imageframe_view_event (GdkEvent* event, ArdourCanvas::Item* item, ImageFrameTimeAxis* ifta)
280 {
281         gint ret = FALSE ;
282         switch (event->type)
283         {
284                 case GDK_BUTTON_PRESS:
285                 case GDK_2BUTTON_PRESS:
286                 case GDK_3BUTTON_PRESS:
287                         clicked_trackview = ifta ;
288                         ret = button_press_handler (item, event, ImageFrameTimeAxisItem) ;
289                         break ;
290                 case GDK_BUTTON_RELEASE:
291                         ret = button_release_handler (item, event, ImageFrameTimeAxisItem) ;
292                         break ;
293                 case GDK_MOTION_NOTIFY:
294                         break ;
295                 default:
296                         break ;
297         }
298         return(ret) ;
299 }
300
301 bool
302 Editor::canvas_marker_time_axis_view_event (GdkEvent* event, ArdourCanvas::Item* item, MarkerTimeAxis* mta)
303 {
304         gint ret = FALSE ;
305         switch (event->type)
306         {
307                 case GDK_BUTTON_PRESS:
308                 case GDK_2BUTTON_PRESS:
309                 case GDK_3BUTTON_PRESS:
310                         clicked_trackview = mta ;
311                         ret = button_press_handler(item, event, MarkerTimeAxisItem) ;
312                         break ;
313                 case GDK_BUTTON_RELEASE:
314                         ret = button_release_handler(item, event, MarkerTimeAxisItem) ;
315                         break ;
316                 case GDK_MOTION_NOTIFY:
317                 default:
318                         break ;
319         }
320         return(ret) ;
321 }
322
323
324 bool
325 Editor::canvas_markerview_item_view_event (GdkEvent* event, ArdourCanvas::Item* item, MarkerView* mta)
326 {
327         gint ret = FALSE ;
328         switch (event->type)
329         {
330                 case GDK_BUTTON_PRESS:
331                 case GDK_2BUTTON_PRESS:
332                 case GDK_3BUTTON_PRESS:
333                         clicked_trackview = &mta->get_time_axis_view() ;
334                         dynamic_cast<MarkerTimeAxis*>(clicked_trackview)->get_view()->set_selected_time_axis_item(mta);
335                         ret = button_press_handler(item, event, MarkerViewItem) ;
336                         break ;
337                 case GDK_BUTTON_RELEASE:
338                         ret = button_release_handler(item, event, MarkerViewItem) ;
339                         break ;
340                 case GDK_MOTION_NOTIFY:
341                         ret = motion_handler(item, event, MarkerViewItem) ;
342                         break ;
343                 default:
344                         break ;
345         }
346         return(ret) ;
347 }
348
349 bool
350 Editor::canvas_markerview_start_handle_event (GdkEvent* event, ArdourCanvas::Item* item, MarkerView* mta)
351 {
352         gint ret = FALSE ;
353         switch (event->type)
354         {
355                 case GDK_BUTTON_PRESS:
356                 case GDK_2BUTTON_PRESS:
357                 case GDK_3BUTTON_PRESS:
358                         clicked_trackview = &mta->get_time_axis_view() ;
359                         dynamic_cast<MarkerTimeAxis*>(clicked_trackview)->get_view()->set_selected_time_axis_item(mta) ;
360                         ret = button_press_handler(item, event, MarkerViewHandleStartItem) ;
361                         break ;
362                 case GDK_BUTTON_RELEASE:
363                         ret = button_release_handler(item, event, MarkerViewHandleStartItem) ;
364                         break ;
365                 case GDK_MOTION_NOTIFY:
366                         ret = motion_handler(item, event, MarkerViewHandleStartItem) ;
367                         break ;
368                 case GDK_ENTER_NOTIFY:
369                         ret = enter_handler(item, event, MarkerViewHandleStartItem) ;
370                         break ;
371                 case GDK_LEAVE_NOTIFY:
372                         ret = leave_handler(item, event, MarkerViewHandleStartItem) ;
373                         break ;
374                 default:
375                         break ;
376         }
377         return(ret) ;
378 }
379
380 bool
381 Editor::canvas_markerview_end_handle_event (GdkEvent* event, ArdourCanvas::Item* item, MarkerView* mta)
382 {
383         gint ret = FALSE ;
384         switch (event->type)
385         {
386                 case GDK_BUTTON_PRESS:
387                 case GDK_2BUTTON_PRESS:
388                 case GDK_3BUTTON_PRESS:
389                         clicked_trackview = &mta->get_time_axis_view() ;
390                         dynamic_cast<MarkerTimeAxis*>(clicked_trackview)->get_view()->set_selected_time_axis_item(mta) ;
391                         ret = button_press_handler(item, event, MarkerViewHandleEndItem) ;
392                         break ;
393                 case GDK_BUTTON_RELEASE:
394                         ret = button_release_handler(item, event, MarkerViewHandleEndItem) ;
395                         break ;
396                 case GDK_MOTION_NOTIFY:
397                         ret = motion_handler(item, event, MarkerViewHandleEndItem) ;
398                         break ;
399                 case GDK_ENTER_NOTIFY:
400                         ret = enter_handler(item, event, MarkerViewHandleEndItem) ;
401                         break ;
402                 case GDK_LEAVE_NOTIFY:
403                         ret = leave_handler(item, event, MarkerViewHandleEndItem) ;
404                         break ;
405                 default:
406                         break ;
407         }
408         return(ret) ;
409 }
410
411
412 /* </CMT Additions file="editor_canvas_events.cc"> */
413
414
415 /*
416         ---------------------------------------------------------------------------------------------------
417         ---------------------------------------------------------------------------------------------------
418         ---------------------------------------------------------------------------------------------------
419 */
420
421
422
423 /* <CMT Additions file="editor_mouse.cc"> */
424
425 void
426 Editor::start_imageframe_grab(ArdourCanvas::Item* item, GdkEvent* event)
427 {
428         ImageFrameView* ifv = ((ImageFrameTimeAxis*)clicked_trackview)->get_view()->get_selected_imageframe_view() ;
429         drag_info.copy = false ;
430         drag_info.item = item ;
431         drag_info.data = ifv ;
432         drag_info.motion_callback = &Editor::imageframe_drag_motion_callback;
433         drag_info.finished_callback = &Editor::timeaxis_item_drag_finished_callback;
434         drag_info.last_frame_position = ifv->get_position() ;
435  
436         drag_info.last_trackview = &ifv->get_time_axis_view() ;
437         
438         /* this is subtle. raising the regionview itself won't help,
439            because raise_to_top() just puts the item on the top of
440            its parent's stack. so, we need to put the trackview canvas_display group
441            on the top, since its parent is the whole canvas.
442
443            however, this hides the measure bars within that particular trackview,
444            so move them to the top afterwards.
445         */
446
447         drag_info.item->raise_to_top();
448         drag_info.last_trackview->canvas_display->raise_to_top();
449         //time_line_group->raise_to_top();
450         cursor_group->raise_to_top ();
451
452         start_grab(event) ;
453
454         drag_info.pointer_frame_offset = pixel_to_frame(drag_info.grab_x) - drag_info.last_frame_position;
455 }
456
457
458 void
459 Editor::start_markerview_grab(ArdourCanvas::Item* item, GdkEvent* event)
460 {
461         MarkerView* mv = ((MarkerTimeAxis*)clicked_trackview)->get_view()->get_selected_time_axis_item() ;
462         drag_info.copy = false ;
463         drag_info.item = item ;
464         drag_info.data = mv ;
465         drag_info.motion_callback = &Editor::markerview_drag_motion_callback;
466         drag_info.finished_callback = &Editor::timeaxis_item_drag_finished_callback;
467         drag_info.last_frame_position = mv->get_position() ;
468
469         drag_info.last_trackview = &mv->get_time_axis_view() ;
470
471         /* this is subtle. raising the regionview itself won't help,
472            because raise_to_top() just puts the item on the top of
473            its parent's stack. so, we need to put the trackview canvas_display group
474            on the top, since its parent is the whole canvas.
475
476            however, this hides the measure bars within that particular trackview,
477            so move them to the top afterwards.
478         */
479
480         drag_info.item->raise_to_top();
481         drag_info.last_trackview->canvas_display->raise_to_top();
482         //time_line_group->raise_to_top();
483         cursor_group->raise_to_top ();
484
485         start_grab(event) ;
486   
487         drag_info.pointer_frame_offset = pixel_to_frame(drag_info.grab_x) - drag_info.last_frame_position ;
488 }
489
490
491 void
492 Editor::markerview_drag_motion_callback(ArdourCanvas::Item*, GdkEvent* event)
493 {
494         double cx, cy ;
495
496         MarkerView* mv = reinterpret_cast<MarkerView*>(drag_info.data) ;
497         nframes_t pending_region_position ;
498         nframes_t pointer_frame ;
499
500         pointer_frame = event_frame(event, &cx, &cy) ;
501
502         snap_to(pointer_frame) ;
503
504         if (pointer_frame > (nframes_t) drag_info.pointer_frame_offset)
505         {
506                 pending_region_position = pointer_frame - drag_info.pointer_frame_offset ;
507                 snap_to(pending_region_position) ;
508                 
509                 // we dont allow marker items to extend beyond, or in front of the marked items so
510                 // cap the value to the marked items position and duration
511                 if((pending_region_position + mv->get_duration()) >= ((mv->get_marked_item()->get_position()) + (mv->get_marked_item()->get_duration()))) 
512                 {
513                         pending_region_position = (mv->get_marked_item()->get_position() + mv->get_marked_item()->get_duration()) - (mv->get_duration()) ;
514                 }
515                 else if(pending_region_position <= mv->get_marked_item()->get_position()) 
516                 {
517                         pending_region_position = mv->get_marked_item()->get_position() ;
518                 }
519         }
520         else
521         {
522                 pending_region_position = mv->get_marked_item()->get_position() ;
523         }
524
525         drag_info.last_frame_position = pending_region_position ;
526         
527         // we treat this as a special case, usually we want to send the identitiy of the caller
528         // but in this case, that would trigger our socket handler to handle the event, sending
529         // notification to the image compositor. This would be fine, except that we have not
530         // finished the drag, we therefore do not want to sent notification until we have
531         // completed the drag, only then do we want the image compositor notofied.
532         // We therefore set the caller identity to the special case of 0
533         mv->set_position(pending_region_position, 0) ;
534
535         show_verbose_time_cursor(pending_region_position) ;
536 }
537
538 void
539 Editor::imageframe_drag_motion_callback(ArdourCanvas::Item*, GdkEvent* event)
540 {
541         double cx, cy ;
542         
543         ImageFrameView* ifv = reinterpret_cast<ImageFrameView*>(drag_info.data) ;
544         
545         nframes_t pending_region_position;
546         nframes_t pointer_frame;
547
548         pointer_frame = event_frame(event, &cx, &cy) ;
549
550         snap_to(pointer_frame) ;
551
552         if (pointer_frame > (nframes_t) drag_info.pointer_frame_offset)
553         {
554                 pending_region_position = pointer_frame - drag_info.pointer_frame_offset ;
555                 snap_to(pending_region_position) ;
556         }
557         else
558         {
559                 pending_region_position = 0 ;
560         }
561
562         drag_info.grab_x = cx;
563         //drag_info.last_frame_position = pending_region_position ;
564         drag_info.current_pointer_frame = pending_region_position ;
565         
566         // we treat this as a special case, usually we want to send the identitiy of the caller
567         // but in this case, that would trigger our socket handler to handle the event, sending
568         // notification to the image compositor. This would be fine, except that we have not
569         // finished the drag, we therefore do not want to sent notification until we have
570         // completed the drag, only then do we want the image compositor notofied.
571         // We therefore set the caller identity to the special case of 0
572         ifv->set_position(pending_region_position, 0) ;
573         
574         show_verbose_time_cursor(pending_region_position) ;
575 }
576
577 void
578 Editor::timeaxis_item_drag_finished_callback(ArdourCanvas::Item*, GdkEvent* event)
579 {
580         nframes_t where ;
581         TimeAxisViewItem* tavi = reinterpret_cast<TimeAxisViewItem*>(drag_info.data) ;
582
583         bool item_x_movement = (drag_info.last_frame_position != tavi->get_position()) ;
584
585         hide_verbose_canvas_cursor() ;
586
587         /* no x or y movement either means the regionview hasn't been moved, or has been moved
588            but is back in it's original position/trackview.*/
589
590         if(!item_x_movement && event && event->type == GDK_BUTTON_RELEASE)
591         {
592                 /* No motion: either set the current region, or align the clicked region
593                    with the current one.
594                  */
595                  return;
596         }
597
598         if(item_x_movement)
599         {
600                 /* base the new region position on the current position of the regionview.*/
601                 where = drag_info.current_pointer_frame ;
602                 
603                 // final call to set position after the motion to tell interested parties of the new position
604                 tavi->set_position(where, this) ;
605         }
606         else
607         {
608                 //where = tavi->get_position() ;
609         }
610   
611
612 }
613
614
615 void
616 Editor::imageframe_start_handle_op(ArdourCanvas::Item* item, GdkEvent* event)
617 {
618         // get the selected item from the parent time axis
619         ImageFrameTimeAxis* ifta = dynamic_cast<ImageFrameTimeAxis*>(clicked_trackview) ;
620         if(ifta)
621         {
622                 ImageFrameView* ifv = ifta->get_view()->get_selected_imageframe_view() ;
623
624                 if (ifv == 0) {
625                         fatal << _("programming error: no ImageFrameView selected") << endmsg;
626                         /*NOTREACHED*/
627                         return ;
628                 }
629
630                 drag_info.item = ifv->get_canvas_frame() ;
631                 drag_info.data = ifv;
632                 drag_info.grab_x = event->motion.x;
633                 drag_info.cumulative_x_drag = 0;
634                 drag_info.motion_callback = &Editor::imageframe_start_handle_trim_motion ;
635                 drag_info.finished_callback = &Editor::imageframe_start_handle_end_trim ;
636                 
637                 start_grab(event) ;
638                 
639                 show_verbose_time_cursor(ifv->get_position(), 10) ;
640         }
641 }
642
643 void
644 Editor::imageframe_end_handle_op(ArdourCanvas::Item* item, GdkEvent* event)
645 {
646         // get the selected item from the parent time axis
647         ImageFrameTimeAxis* ifta = dynamic_cast<ImageFrameTimeAxis*>(clicked_trackview) ;
648
649         if(ifta)
650         {
651                 ImageFrameView* ifv = ifta->get_view()->get_selected_imageframe_view() ;
652         
653                 if (ifv == 0)
654                 {
655                         fatal << _("programming error: no ImageFrameView selected") << endmsg ;
656                         /*NOTREACHED*/
657                         return ;
658                 }
659         
660                 drag_info.item = ifv->get_canvas_frame() ;
661                 drag_info.data = ifv ;
662                 drag_info.grab_x = event->motion.x ;
663                 drag_info.cumulative_x_drag = 0 ;
664                 drag_info.motion_callback = &Editor::imageframe_end_handle_trim_motion ;
665                 drag_info.finished_callback = &Editor::imageframe_end_handle_end_trim ;
666
667                 start_grab(event, trimmer_cursor) ;
668
669                 show_verbose_time_cursor(ifv->get_position() + ifv->get_duration(), 10) ;
670         }
671 }
672
673 void
674 Editor::imageframe_start_handle_trim_motion(ArdourCanvas::Item* item, GdkEvent* event)
675 {
676         ImageFrameView* ifv = reinterpret_cast<ImageFrameView*> (drag_info.data) ;
677         
678         nframes_t start = 0 ;
679         nframes_t end = 0 ;
680         nframes_t pointer_frame = event_frame(event) ;
681         
682         // chekc th eposition of the item is not locked
683         if(!ifv->get_position_locked()) {
684                 snap_to(pointer_frame) ;
685
686                 if(pointer_frame != drag_info.last_pointer_frame) {
687                         start = ifv->get_position() ;
688                         end = ifv->get_position() + ifv->get_duration() ;
689                         
690                         if (pointer_frame > end) {
691                                 start = end ;
692                         } else {
693                                 start = pointer_frame ;
694                         }
695                         
696                         // are we getting bigger or smaller?
697                         nframes_t new_dur_val = end - start ;
698                         
699                         // start handle, so a smaller pointer frame increases our component size
700                         if(pointer_frame <= drag_info.grab_frame) 
701                         {
702                                 if(ifv->get_max_duration_active() && (new_dur_val > ifv->get_max_duration()))
703                                 {
704                                         new_dur_val = ifv->get_max_duration() ;
705                                         start = end - new_dur_val ;
706                                 }
707                                 else
708                                 {
709                                         // current values are ok
710                                 }
711                         }
712                         else
713                         {
714                                 if(ifv->get_min_duration_active() && (new_dur_val < ifv->get_min_duration()))
715                                 {
716                                         new_dur_val = ifv->get_min_duration() ;
717                                         start = end - new_dur_val ;
718                                 }
719                                 else
720                                 {
721                                         // current values are ok
722                                 }
723                         }
724                 
725                         drag_info.last_pointer_frame = pointer_frame ;
726         
727                         /* re-calculatethe duration and position of the imageframeview */
728                         drag_info.cumulative_x_drag = new_dur_val ;
729
730                         // we treat this as a special case, usually we want to send the identitiy of the caller
731                         // but in this case, that would trigger our socket handler to handle the event, sending
732                         // notification to the image compositor. This would be fine, except that we have not
733                         // finished the drag, we therefore do not want to sent notification until we have
734                         // completed the drag, only then do we want the image compositor notofied.
735                         // We therefore set the caller identity to the special case of 0
736                         ifv->set_duration(new_dur_val, 0) ;
737                         ifv->set_position(start, 0) ;
738                 }
739         }
740         
741         show_verbose_time_cursor(start, 10) ;
742 }
743
744 void
745 Editor::imageframe_start_handle_end_trim(ArdourCanvas::Item* item, GdkEvent* event)
746 {
747         ImageFrameView* ifv = reinterpret_cast<ImageFrameView *> (drag_info.data) ;
748         
749         if (drag_info.cumulative_x_drag == 0)
750         {
751                 /* just a click */
752         }
753         else
754         {
755                 nframes_t temp = ifv->get_position() + ifv->get_duration() ;
756                 
757                 ifv->set_position((nframes_t) (temp - drag_info.cumulative_x_drag), this) ;
758                 ifv->set_duration((nframes_t) drag_info.cumulative_x_drag, this) ;
759         }
760 }
761
762 void
763 Editor::imageframe_end_handle_trim_motion(ArdourCanvas::Item* item, GdkEvent* event)
764 {
765         ImageFrameView* ifv = reinterpret_cast<ImageFrameView *> (drag_info.data) ;
766         
767         nframes_t start = 0 ;
768         nframes_t end = 0 ;
769         nframes_t pointer_frame = event_frame(event) ;
770         nframes_t new_dur_val = 0 ;
771
772         snap_to(pointer_frame) ;
773         
774         if (pointer_frame != drag_info.last_pointer_frame)
775         {
776                 start = ifv->get_position() ;
777                 end = ifv->get_position() + ifv->get_duration() ;
778                 if (pointer_frame < start)
779                 {
780                         end = start ;
781                 }
782                 else
783                 {
784                         end = pointer_frame ;
785                 }
786                 
787                 new_dur_val = end - start ;
788                 
789                 // are we getting bigger or smaller?
790                 if(pointer_frame >= drag_info.last_pointer_frame)
791                 {
792                         if(ifv->get_max_duration_active() && (new_dur_val > ifv->get_max_duration()))
793                         {
794                                 new_dur_val = ifv->get_max_duration() ;
795                         }
796                 }
797                 else
798                 {
799                         if(ifv->get_min_duration_active() && (new_dur_val < ifv->get_min_duration()))
800                         {
801                                 new_dur_val = ifv->get_min_duration() ;
802                         }
803                 }
804                 
805                 drag_info.last_pointer_frame = pointer_frame ;
806                 drag_info.cumulative_x_drag = new_dur_val ;
807                 
808                 // we treat this as a special case, usually we want to send the identitiy of the caller
809                 // but in this case, that would trigger our socket handler to handle the event, sending
810                 // notification to the image compositor. This would be fine, except that we have not
811                 // finished the drag, we therefore do not want to sent notification until we have
812                 // completed the drag, only then do we want the image compositor notofied.
813                 // We therefore set the caller identity to the special case of 0
814                 ifv->set_duration(new_dur_val, 0) ;
815         }
816         
817         show_verbose_time_cursor(new_dur_val, 10) ;
818 }
819
820
821 void
822 Editor::imageframe_end_handle_end_trim (ArdourCanvas::Item* item, GdkEvent* event)
823 {
824         ImageFrameView* ifv = reinterpret_cast<ImageFrameView *> (drag_info.data) ;
825
826         if (drag_info.cumulative_x_drag == 0)
827         {
828                 /* just a click */
829         }
830         else
831         {
832                 nframes_t new_duration = (nframes_t)drag_info.cumulative_x_drag ;
833                 if((new_duration <= ifv->get_max_duration()) && (new_duration >= ifv->get_min_duration()))
834                 {
835                         ifv->set_duration(new_duration, this) ;
836                 }
837         }
838 }
839
840
841 void
842 Editor::markerview_item_start_handle_op(ArdourCanvas::Item* item, GdkEvent* event)
843 {
844         MarkerView* mv = reinterpret_cast<MarkerTimeAxis*>(clicked_trackview)->get_view()->get_selected_time_axis_item() ;
845
846         if (mv == 0)
847         {
848                 fatal << _("programming error: no MarkerView selected") << endmsg ;
849                 /*NOTREACHED*/
850                 return ;
851         }
852
853         drag_info.item = mv->get_canvas_frame() ;
854         drag_info.data = mv;
855         drag_info.grab_x = event->motion.x;
856
857         drag_info.cumulative_x_drag = 0 ;
858         drag_info.motion_callback = &Editor::markerview_start_handle_trim_motion ;
859         drag_info.finished_callback = &Editor::markerview_start_handle_end_trim ;
860
861         start_grab(event, trimmer_cursor) ;
862 }
863
864 void
865 Editor::markerview_item_end_handle_op(ArdourCanvas::Item* item, GdkEvent* event)
866 {
867         MarkerView* mv = reinterpret_cast<MarkerTimeAxis*>(clicked_trackview)->get_view()->get_selected_time_axis_item() ;
868         if (mv == 0)
869         {
870                 fatal << _("programming error: no MarkerView selected") << endmsg ;
871                 /*NOTREACHED*/
872                 return ;
873         }
874         
875         drag_info.item = mv->get_canvas_frame() ;
876         drag_info.data = mv ;
877         drag_info.grab_x = event->motion.x ;
878         drag_info.cumulative_x_drag = 0 ;
879         
880         drag_info.motion_callback = &Editor::markerview_end_handle_trim_motion ;
881         drag_info.finished_callback = &Editor::markerview_end_handle_end_trim ;
882         
883         start_grab(event, trimmer_cursor) ;
884 }
885
886
887 void
888 Editor::markerview_start_handle_trim_motion(ArdourCanvas::Item* item, GdkEvent* event)
889 {
890         MarkerView* mv = reinterpret_cast<MarkerView*> (drag_info.data) ;
891         
892         nframes_t start = 0 ;
893         nframes_t end = 0 ;
894         nframes_t pointer_frame = event_frame(event) ;
895         
896         // chekc th eposition of the item is not locked
897         if(!mv->get_position_locked())
898         {
899                 snap_to(pointer_frame) ;
900                 if(pointer_frame != drag_info.last_pointer_frame)
901                 {
902                         start = mv->get_position() ;
903                         end = mv->get_position() + mv->get_duration() ;
904                         
905                         if (pointer_frame > end)
906                         {
907                                 start = end ;
908                         }
909                         else
910                         {
911                                 start = pointer_frame ;
912                         }
913                         
914                         // are we getting bigger or smaller?
915                         nframes_t new_dur_val = end - start ;
916                         
917                         if(pointer_frame <= drag_info.grab_frame)
918                         {
919                                 if(mv->get_max_duration_active() && (new_dur_val > mv->get_max_duration()))
920                                 {
921                                         new_dur_val = mv->get_max_duration() ;
922                                         start = end - new_dur_val ;
923                                 }
924                                 else
925                                 {
926                                         // current values are ok
927                                 }
928                         }
929                         else
930                         {
931                                 if(mv->get_min_duration_active() && (new_dur_val < mv->get_min_duration()))
932                                 {
933                                         new_dur_val = mv->get_min_duration() ;
934                                         start = end - new_dur_val ;
935                                 }
936                                 else
937                                 {
938                                         // current values are ok
939                                 }
940                         }
941                 
942                         drag_info.last_pointer_frame = pointer_frame ;
943         
944                         /* re-calculatethe duration and position of the imageframeview */
945                         drag_info.cumulative_x_drag = new_dur_val ;
946          
947                         // we treat this as a special case, usually we want to send the identitiy of the caller
948                         // but in this case, that would trigger our socket handler to handle the event, sending
949                         // notification to the image compositor. This would be fine, except that we have not
950                         // finished the drag, we therefore do not want to sent notification until we have
951                         // completed the drag, only then do we want the image compositor notofied.
952                         // We therefore set the caller identity to the special case of 0
953                         mv->set_duration(new_dur_val, 0) ;
954                         mv->set_position(start, 0) ;
955                 }
956         }
957         
958         show_verbose_time_cursor(start, 10) ;
959 }
960
961 void
962 Editor::markerview_start_handle_end_trim(ArdourCanvas::Item* item, GdkEvent* event)
963 {
964         MarkerView* mv = reinterpret_cast<MarkerView*> (drag_info.data) ;
965         
966         if (drag_info.cumulative_x_drag == 0)
967         {
968                 /* just a click */
969         }
970         else
971         {
972                 nframes_t temp = mv->get_position() + mv->get_duration() ;
973                 
974                 mv->set_position((nframes_t) (temp - drag_info.cumulative_x_drag), this) ;
975                 mv->set_duration((nframes_t) drag_info.cumulative_x_drag, this) ;
976         }
977 }
978
979 void
980 Editor::markerview_end_handle_trim_motion(ArdourCanvas::Item* item, GdkEvent* event)
981 {
982         MarkerView* mv = reinterpret_cast<MarkerView*> (drag_info.data) ;
983         
984         nframes_t start = 0 ;
985         nframes_t end = 0 ;
986         nframes_t pointer_frame = event_frame(event) ;
987         nframes_t new_dur_val = 0 ;
988
989         snap_to(pointer_frame) ;
990         
991         if (pointer_frame != drag_info.last_pointer_frame)
992         {
993                 start = mv->get_position() ;
994                 end = mv->get_position() + mv->get_duration() ;
995                 
996                 if(pointer_frame < start)
997                 {
998                         end = start ;
999                 }
1000                 else
1001                 {
1002                         end = pointer_frame ;
1003                 }
1004                 
1005                 new_dur_val = end - start ;
1006                 
1007                 // are we getting bigger or smaller?
1008                 if(pointer_frame >= drag_info.last_pointer_frame)
1009                 {
1010                         // we cant extend beyond the item we are marking
1011                         ImageFrameView* marked_item = mv->get_marked_item() ;
1012                         nframes_t marked_end = marked_item->get_position() + marked_item->get_duration() ;
1013                         
1014                         if(mv->get_max_duration_active() && (new_dur_val > mv->get_max_duration()))
1015                         {
1016                                 if((start + mv->get_max_duration()) > marked_end)
1017                                 {
1018                                         new_dur_val = marked_end - start ;
1019                                 }
1020                                 else
1021                                 {
1022                                         new_dur_val = mv->get_max_duration() ;
1023                                 }
1024                         }
1025                         else if(end > marked_end)
1026                         {
1027                                 new_dur_val = marked_end - start ;
1028                         }
1029                 }
1030                 else
1031                 {
1032                         if(mv->get_min_duration_active() && (new_dur_val < mv->get_min_duration()))
1033                         {
1034                                 new_dur_val = mv->get_min_duration() ;
1035                         }
1036                 }
1037
1038
1039                 drag_info.last_pointer_frame = pointer_frame ;
1040                 drag_info.cumulative_x_drag = new_dur_val ;
1041                 
1042                 // we treat this as a special case, usually we want to send the identitiy of the caller
1043                 // but in this case, that would trigger our socket handler to handle the event, sending
1044                 // notification to the image compositor. This would be fine, except that we have not
1045                 // finished the drag, we therefore do not want to sent notification until we have
1046                 // completed the drag, only then do we want the image compositor notofied.
1047                 // We therefore set the caller identity to the special case of 0
1048                 mv->set_duration(new_dur_val, 0) ;
1049         }
1050         
1051         show_verbose_time_cursor(new_dur_val, 10) ;
1052 }
1053
1054
1055 void
1056 Editor::markerview_end_handle_end_trim (ArdourCanvas::Item* item, GdkEvent* event)
1057 {
1058         MarkerView* mv = reinterpret_cast<MarkerView*> (drag_info.data) ;
1059
1060         if (drag_info.cumulative_x_drag == 0)
1061         {
1062                 /* just a click */
1063         }
1064         else
1065         {
1066                 nframes_t new_duration = (nframes_t)drag_info.cumulative_x_drag ;
1067                 mv->set_duration(new_duration, this) ;
1068         }
1069 }
1070
1071
1072 /* </CMT Additions file="editor_mouse.cc"> */
1073
1074
1075
1076
1077
1078
1079
1080 /* <CMT Additions file="editor_route_list.cc"> */
1081
1082 void
1083 Editor::handle_new_imageframe_time_axis_view(const string & track_name, void* src)
1084 {
1085         ImageFrameTimeAxis* iftav ;
1086         iftav = new ImageFrameTimeAxis(track_name, *this, *session, track_canvas) ;
1087         iftav->set_time_axis_name(track_name, this) ;
1088         track_views.push_back(iftav) ;
1089
1090         TreeModel::Row row = *(route_display_model->append());
1091
1092         row[route_display_columns.text] = iftav->name();
1093         row[route_display_columns.tv] = iftav;
1094         route_list_display.get_selection()->select (row);
1095
1096         iftav->GoingAway.connect(bind(mem_fun(*this, &Editor::remove_route), (TimeAxisView*)iftav)) ;
1097         iftav->gui_changed.connect(mem_fun(*this, &Editor::handle_gui_changes)) ;
1098 }
1099
1100 void
1101 Editor::handle_new_imageframe_marker_time_axis_view(const string & track_name, TimeAxisView* marked_track)
1102 {
1103         MarkerTimeAxis* mta = new MarkerTimeAxis (*this, *this->current_session(), track_canvas, track_name, marked_track) ;
1104         ((ImageFrameTimeAxis*)marked_track)->add_marker_time_axis(mta, this) ;
1105         track_views.push_back(mta) ;
1106
1107         TreeModel::Row row = *(route_display_model->append());
1108
1109         row[route_display_columns.text] = mta->name();
1110         row[route_display_columns.tv] = mta;
1111         route_list_display.get_selection()->select (row);
1112
1113         mta->GoingAway.connect(bind(mem_fun(*this, &Editor::remove_route), (TimeAxisView*)mta)) ;
1114  }
1115
1116
1117 /* </CMT Additions file="editor_route_list.cc"> */