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