add JAG to author list
[ardour.git] / gtk2_ardour / imageframe_view.cc
1 /*
2     Copyright (C) 2003 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <cmath>
22
23 #include <gtkmm.h>
24 #include <gtkmm2ext/gtk_ui.h>
25
26 #include "imageframe_time_axis.h"
27 #include "imageframe_time_axis_group.h"
28 #include "marker_time_axis.h"
29 #include "marker_time_axis_view.h"
30 #include "public_editor.h"
31 #include "utils.h"
32 #include "imageframe_view.h"
33 #include "imageframe.h"
34 #include "canvas_impl.h"
35 #include "gui_thread.h"
36
37 using namespace sigc ;
38 using namespace ARDOUR ;
39 using namespace Gtk;
40
41 sigc::signal<void,ImageFrameView*> ImageFrameView::GoingAway;
42
43 /**
44  * Constructs a new ImageFrameView upon the canvas
45  *
46  * @param item_id unique id of this item
47  * @param parent the parent canvas item
48  * @param tv the time axis view that this item is to be placed upon
49  * @param group the ImageFrameGroup that this item is a member of
50  * @param spu the current samples per canvas unit
51  * @param start the start frame ogf this item
52  * @param duration the duration of this item
53  * @param rgb_data the rgb data of the image
54  * @param width the width of the original rgb_data image data
55  * @param height the width of the origianl rgb_data image data
56  * @param num_channels the number of color channels within rgb_data
57  */
58 ImageFrameView::ImageFrameView(const string & item_id,
59         ArdourCanvas::Group *parent,
60         ImageFrameTimeAxis* tv,
61         ImageFrameTimeAxisGroup* item_group,
62         double spu,
63        Gdk::Color& basic_color,
64         nframes_t start,
65         nframes_t duration,
66         unsigned char* rgb_data,
67         uint32_t width,
68         uint32_t height,
69         uint32_t num_channels)
70   : TimeAxisViewItem(item_id, *parent, *tv, spu, basic_color, start, duration, false,
71                      TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
72                                                    TimeAxisViewItem::ShowNameHighlight|
73                                                    TimeAxisViewItem::ShowFrame|
74                                                    TimeAxisViewItem::ShowHandles))
75
76 {
77         the_parent_group = item_group ;
78         set_name_text(item_id) ;
79         
80         image_data_width = width ;
81         image_data_height = height ;
82         image_data_num_channels = num_channels ;
83         
84         //This should be art_free'd once the ArtPixBuf is destroyed - this should happen when we destroy the imageframe canvas item
85         unsigned char* the_rgb_data = (unsigned char*) art_alloc(width*height*num_channels) ;
86         memcpy(the_rgb_data, rgb_data, (width*height*num_channels)) ;
87
88         ArtPixBuf* pbuf ;
89         if (num_channels==3)
90           pbuf = art_pixbuf_new_rgb(the_rgb_data, width, height, (num_channels * width));
91         else if (num_channels==4)
92           pbuf = art_pixbuf_new_rgba(the_rgb_data, width, height, (num_channels * width));
93         else  {
94           ; // error unsupported image data format
95           art_free(the_rgb_data);
96           std::cerr << "imageframe_view: unsupported image data format" << std::endl;
97           return;
98         }
99         imageframe = 0 ;
100         
101         //calculate our image width based on the track height
102         double im_ratio = (double)width/(double)height ;
103         double im_width = ((double)(trackview.current_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE) * im_ratio) ;
104         
105         imageframe = new ImageFrame (*group, pbuf, 1.0, 1.0, ANCHOR_NW, im_width, (trackview.current_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE));
106
107         frame_handle_start->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_start_handle_event), frame_handle_start, this));
108         frame_handle_end->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_end_handle_event), frame_handle_end, this));
109         group->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_item_view_event), imageframe, this));
110
111         frame_handle_start->raise_to_top();
112         frame_handle_end->raise_to_top();
113         
114     set_position(start, this) ;
115     set_duration(duration, this) ;
116 }
117
118 /**
119  * Destructor
120  * Reposible for removing and destroying all marker items associated with this item
121  */
122 ImageFrameView::~ImageFrameView()
123 {
124         GoingAway (this);
125
126         // destroy any marker items we have associated with this item
127                 
128         for(MarkerViewList::iterator iter = marker_view_list.begin(); iter != marker_view_list.end(); ++iter)
129         {
130                 MarkerView* mv = (*iter) ;
131                 
132                 MarkerViewList::iterator next = iter ;
133                 next++ ;
134
135                 // remove the item from our marker list
136                 // the current iterator becomes invalid after this point, so we cannot call next upon it
137                 // luckily enough, we already have next
138                 marker_view_list.erase(iter) ;
139
140                 // remove the item from the marker time axis
141                 MarkerTimeAxisView* mtav = dynamic_cast<MarkerTimeAxis*>(&mv->get_time_axis_view())->get_view() ;
142                 if(mtav)
143                 {
144                         mtav->remove_marker_view(mv, this) ;
145                 }
146
147                 mv->set_marked_item(0) ;
148                 delete mv ;
149                 mv = 0 ;
150                 
151                 // set our iterator to next, as we have invalided the current iterator with the call to erase 
152                 iter = next ;
153         }
154         
155         // if we are the currently selected item withi the parent track, we need to se-select
156         if(the_parent_group)
157         {
158                 if(the_parent_group->get_view().get_selected_imageframe_view() == this)
159                 {
160                         the_parent_group->get_view().clear_selected_imageframe_item(false) ;
161                 }
162         }
163
164         if(imageframe)
165         {
166 delete imageframe;
167                 imageframe = 0 ;
168         }
169 }
170
171
172 //---------------------------------------------------------------------------------------//
173 // Position and duration Accessors/Mutators
174
175 /**
176  * Set the position of this item to the specified value
177  *
178  * @param pos the new position
179  * @param src the identity of the object that initiated the change
180  * @return true if the position change was a success, false otherwise
181  */
182 bool
183 ImageFrameView::set_position(nframes_t pos, void* src, double* delta)
184 {
185         nframes_t old_pos = frame_position ;
186         
187         // do the standard stuff
188         bool ret = TimeAxisViewItem::set_position(pos, src, delta) ;
189
190         // everything went ok with the standard stuff?
191         if (ret) {
192                 /* move each of our associated markers with this ImageFrameView */
193                 for (MarkerViewList::iterator i = marker_view_list.begin(); i != marker_view_list.end(); ++i)
194                 {
195                         // calculate the offset of the marker
196                         MarkerView* mv = (MarkerView*)*i ;
197                         nframes_t marker_old_pos = mv->get_position() ;
198                         
199                         mv->set_position(pos + (marker_old_pos - old_pos), src) ;
200                 }
201         }
202         
203         return(ret) ;
204 }
205                  
206 /**
207  * Sets the duration of this item
208  *
209  * @param dur the new duration of this item
210  * @param src the identity of the object that initiated the change
211  * @return true if the duration change was succesful, false otherwise
212  */
213 bool
214 ImageFrameView::set_duration(nframes_t dur, void* src)
215 {
216         /* do the standard stuff */
217         bool ret = TimeAxisViewItem::set_duration(dur, src) ;
218         
219         // eveything went ok with the standard stuff?
220         if(ret)
221         {
222                 /* handle setting the sizes of our canvas itesm based on the new duration */
223                 imageframe->property_drawwidth() = trackview.editor.frame_to_pixel(get_duration());
224         }
225         
226         return(ret) ;
227 }
228
229 //---------------------------------------------------------------------------------------//
230 // Parent Component Methods
231                 
232 /**
233  * Sets the parent ImageFrameTimeAxisGroup of thie item
234  * each Item must be part of exactly one group (or 'scene') upon the timeline
235  *
236  * @param group the new parent group
237  */
238 void
239 ImageFrameView::set_time_axis_group(ImageFrameTimeAxisGroup* group)
240 {
241         the_parent_group = group ;
242 }
243                 
244 /**
245  * Returns the parent group of this item
246  *
247  * @return the parent group of this item
248  */
249 ImageFrameTimeAxisGroup*
250 ImageFrameView::get_time_axis_group()
251 {
252         return(the_parent_group) ;
253 }
254
255
256 //---------------------------------------------------------------------------------------//
257 // ui methods
258                 
259 /**
260  * Set the height of this item
261  *
262  * @param h the new height
263  */
264 void
265 ImageFrameView::set_height (gdouble h)
266 {
267         // set the image size
268         // @todo might have to re-get the image data, for a large height...hmmm.
269         double im_ratio = (double)image_data_width/(double)image_data_height ;
270
271         imageframe->property_width() = (h - TimeAxisViewItem::NAME_Y_OFFSET) * im_ratio;
272         imageframe->property_height() = h - TimeAxisViewItem::NAME_Y_OFFSET;
273
274         frame->raise_to_top();
275         imageframe->raise_to_top();
276         name_highlight->raise_to_top();
277         name_pixbuf->raise_to_top();
278         frame_handle_start->raise_to_top();
279         frame_handle_end->raise_to_top();
280  
281         name_pixbuf->property_y() = h - TimeAxisViewItem::NAME_Y_OFFSET;
282         frame->property_y2() = h;
283
284         name_highlight->property_y1() = (gdouble) h - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
285         name_highlight->property_y2() = (gdouble) h - 1.0;
286 }
287
288
289 //---------------------------------------------------------------------------------------//
290 // MarkerView methods
291
292 /**
293  * Adds a markerView to the list of marker views associated with this item
294  *
295  * @param item the marker item to add
296  * @param src the identity of the object that initiated the change
297  */
298 void
299 ImageFrameView::add_marker_view_item(MarkerView* item, void* src)
300 {
301         marker_view_list.push_back(item) ;
302         
303         item->GoingAway.connect(bind(mem_fun(*this, &ImageFrameView::remove_marker_view_item), (void*)this));
304         
305          MarkerViewAdded(item, src) ; /* EMIT_SIGNAL */
306 }
307                 
308 /**
309  * Removes the named marker view from the list of marker view associated with this item
310  * The Marker view is not destroyed on removal, so the caller must handle the item themself
311  *
312  * @param markId the id/name of the item to remove
313  * @param src the identity of the object that initiated the change
314  * @return the removed marker item
315  */
316 MarkerView*
317 ImageFrameView::remove_named_marker_view_item(const string & markerId, void* src)
318 {
319         MarkerView* mv = 0 ;
320         MarkerViewList::iterator i = marker_view_list.begin() ;
321         
322         while(i != marker_view_list.end())
323         {
324                 if (((MarkerView*)*i)->get_item_name() == markerId)
325                 {
326                         mv = (*i) ;
327
328                         marker_view_list.erase(i) ;
329                         
330                          MarkerViewRemoved(mv,src) ; /* EMIT_SIGNAL */
331                         
332                         // iterator is now invalid, but since we should only ever have
333                         // one item with the specified name, things are ok, and we can
334                         // break from the while loop
335                         break ;
336                 }
337                 i++ ;
338         }
339         
340         return(mv) ;
341 }
342                 
343 /**
344  * Removes item from the list of marker views assocaited with this item
345  * This method will do nothing if item if not assiciated with this item
346  *
347  * @param item the item to remove
348  * @param src the identity of the object that initiated the change
349  */
350 void
351 ImageFrameView::remove_marker_view_item(MarkerView* mv, void* src)
352 {
353         ENSURE_GUI_THREAD(bind (mem_fun(*this, &ImageFrameView::remove_marker_view_item), mv, src));
354
355         MarkerViewList::iterator i ;
356         
357         if((i = find (marker_view_list.begin(), marker_view_list.end(), mv)) != marker_view_list.end()) {
358                 marker_view_list.erase(i) ;
359                  MarkerViewRemoved (mv, src) ; /* EMIT_SIGNAL */
360         }
361 }
362                 
363 /**
364  * Determines if the named marker is one of those associated with this item
365  *
366  * @param markId the id/name of the item to search for
367  */
368 bool
369 ImageFrameView::has_marker_view_item(const string & mname)
370 {
371         bool result = false ;
372         
373         for (MarkerViewList::const_iterator ci = marker_view_list.begin(); ci != marker_view_list.end(); ++ci)
374         {
375                 if (((MarkerView*)*ci)->get_item_name() == mname)
376                 {
377                         result = true ;
378                         
379                         // found the item, so we can break the for loop
380                         break ;
381                 }
382         }
383         
384         return(result) ;
385 }