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