Merged with trunk R1612.
[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,
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         pbuf = art_pixbuf_new_rgba(the_rgb_data, width, height, (num_channels * width));
90         imageframe = 0 ;
91         
92         //calculate our image width based on the track height
93         double im_ratio = (double)width/(double)height ;
94         double im_width = ((double)(trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE) * im_ratio) ;
95         
96         imageframe = new ImageFrame (*group, pbuf, 1.0, 1.0, ANCHOR_NW, im_width, (trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE));
97
98         frame_handle_start->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_start_handle_event), frame_handle_start, this));
99         frame_handle_end->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_end_handle_event), frame_handle_end, this));
100         group->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_item_view_event), imageframe, this));
101
102         frame_handle_start->raise_to_top();
103         frame_handle_end->raise_to_top();
104         
105     set_position(start, this) ;
106     set_duration(duration, this) ;
107 }
108
109 /**
110  * Destructor
111  * Reposible for removing and destroying all marker items associated with this item
112  */
113 ImageFrameView::~ImageFrameView()
114 {
115         GoingAway (this);
116
117         // destroy any marker items we have associated with this item
118                 
119         for(MarkerViewList::iterator iter = marker_view_list.begin(); iter != marker_view_list.end(); ++iter)
120         {
121                 MarkerView* mv = (*iter) ;
122                 
123                 MarkerViewList::iterator next = iter ;
124                 next++ ;
125
126                 // remove the item from our marker list
127                 // the current iterator becomes invalid after this point, so we cannot call next upon it
128                 // luckily enough, we already have next
129                 marker_view_list.erase(iter) ;
130
131                 // remove the item from the marker time axis
132                 MarkerTimeAxisView* mtav = dynamic_cast<MarkerTimeAxis*>(&mv->get_time_axis_view())->get_view() ;
133                 if(mtav)
134                 {
135                         mtav->remove_marker_view(mv, this) ;
136                 }
137
138                 mv->set_marked_item(0) ;
139                 delete mv ;
140                 mv = 0 ;
141                 
142                 // set our iterator to next, as we have invalided the current iterator with the call to erase 
143                 iter = next ;
144         }
145         
146         // if we are the currently selected item withi the parent track, we need to se-select
147         if(the_parent_group)
148         {
149                 if(the_parent_group->get_view().get_selected_imageframe_view() == this)
150                 {
151                         the_parent_group->get_view().clear_selected_imageframe_item(false) ;
152                 }
153         }
154
155         if(imageframe)
156         {
157 delete imageframe;
158                 imageframe = 0 ;
159         }
160 }
161
162
163 //---------------------------------------------------------------------------------------//
164 // Position and duration Accessors/Mutators
165
166 /**
167  * Set the position of this item to the specified value
168  *
169  * @param pos the new position
170  * @param src the identity of the object that initiated the change
171  * @return true if the position change was a success, false otherwise
172  */
173 bool
174 ImageFrameView::set_position(nframes_t pos, void* src, double* delta)
175 {
176         nframes_t old_pos = frame_position ;
177         
178         // do the standard stuff
179         bool ret = TimeAxisViewItem::set_position(pos, src, delta) ;
180
181         // everything went ok with the standard stuff?
182         if (ret) {
183                 /* move each of our associated markers with this ImageFrameView */
184                 for (MarkerViewList::iterator i = marker_view_list.begin(); i != marker_view_list.end(); ++i)
185                 {
186                         // calculate the offset of the marker
187                         MarkerView* mv = (MarkerView*)*i ;
188                         nframes_t marker_old_pos = mv->get_position() ;
189                         
190                         mv->set_position(pos + (marker_old_pos - old_pos), src) ;
191                 }
192         }
193         
194         return(ret) ;
195 }
196                  
197 /**
198  * Sets the duration of this item
199  *
200  * @param dur the new duration of this item
201  * @param src the identity of the object that initiated the change
202  * @return true if the duration change was succesful, false otherwise
203  */
204 bool
205 ImageFrameView::set_duration(nframes_t dur, void* src)
206 {
207         /* do the standard stuff */
208         bool ret = TimeAxisViewItem::set_duration(dur, src) ;
209         
210         // eveything went ok with the standard stuff?
211         if(ret)
212         {
213                 /* handle setting the sizes of our canvas itesm based on the new duration */
214                 imageframe->property_drawwidth() = trackview.editor.frame_to_pixel(get_duration());
215         }
216         
217         return(ret) ;
218 }
219
220 //---------------------------------------------------------------------------------------//
221 // Parent Component Methods
222                 
223 /**
224  * Sets the parent ImageFrameTimeAxisGroup of thie item
225  * each Item must be part of exactly one group (or 'scene') upon the timeline
226  *
227  * @param group the new parent group
228  */
229 void
230 ImageFrameView::set_time_axis_group(ImageFrameTimeAxisGroup* group)
231 {
232         the_parent_group = group ;
233 }
234                 
235 /**
236  * Returns the parent group of this item
237  *
238  * @return the parent group of this item
239  */
240 ImageFrameTimeAxisGroup*
241 ImageFrameView::get_time_axis_group()
242 {
243         return(the_parent_group) ;
244 }
245
246
247 //---------------------------------------------------------------------------------------//
248 // ui methods
249                 
250 /**
251  * Set the height of this item
252  *
253  * @param h the new height
254  */
255 void
256 ImageFrameView::set_height (gdouble h)
257 {
258         // set the image size
259         // @todo might have to re-get the image data, for a large height...hmmm.
260         double im_ratio = (double)image_data_width/(double)image_data_height ;
261
262         imageframe->property_width() = (h - TimeAxisViewItem::NAME_Y_OFFSET) * im_ratio;
263         imageframe->property_height() = h - TimeAxisViewItem::NAME_Y_OFFSET;
264
265         frame->raise_to_top();
266         imageframe->raise_to_top();
267         name_highlight->raise_to_top();
268         name_text->raise_to_top();
269         frame_handle_start->raise_to_top();
270         frame_handle_end->raise_to_top();
271  
272         name_text->property_y() = h - TimeAxisViewItem::NAME_Y_OFFSET;
273         frame->property_y2() = h;
274
275         name_highlight->property_y1() = (gdouble) h - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
276         name_highlight->property_y2() = (gdouble) h - 1.0;
277 }
278
279
280 //---------------------------------------------------------------------------------------//
281 // MarkerView methods
282
283 /**
284  * Adds a markerView to the list of marker views associated with this item
285  *
286  * @param item the marker item to add
287  * @param src the identity of the object that initiated the change
288  */
289 void
290 ImageFrameView::add_marker_view_item(MarkerView* item, void* src)
291 {
292         marker_view_list.push_back(item) ;
293         
294         item->GoingAway.connect(bind(mem_fun(*this, &ImageFrameView::remove_marker_view_item), (void*)this));
295         
296          MarkerViewAdded(item, src) ; /* EMIT_SIGNAL */
297 }
298                 
299 /**
300  * Removes the named marker view from the list of marker view associated with this item
301  * The Marker view is not destroyed on removal, so the caller must handle the item themself
302  *
303  * @param markId the id/name of the item to remove
304  * @param src the identity of the object that initiated the change
305  * @return the removed marker item
306  */
307 MarkerView*
308 ImageFrameView::remove_named_marker_view_item(const string & markerId, void* src)
309 {
310         MarkerView* mv = 0 ;
311         MarkerViewList::iterator i = marker_view_list.begin() ;
312         
313         while(i != marker_view_list.end())
314         {
315                 if (((MarkerView*)*i)->get_item_name() == markerId)
316                 {
317                         mv = (*i) ;
318
319                         marker_view_list.erase(i) ;
320                         
321                          MarkerViewRemoved(mv,src) ; /* EMIT_SIGNAL */
322                         
323                         // iterator is now invalid, but since we should only ever have
324                         // one item with the specified name, things are ok, and we can
325                         // break from the while loop
326                         break ;
327                 }
328                 i++ ;
329         }
330         
331         return(mv) ;
332 }
333                 
334 /**
335  * Removes item from the list of marker views assocaited with this item
336  * This method will do nothing if item if not assiciated with this item
337  *
338  * @param item the item to remove
339  * @param src the identity of the object that initiated the change
340  */
341 void
342 ImageFrameView::remove_marker_view_item(MarkerView* mv, void* src)
343 {
344         ENSURE_GUI_THREAD(bind (mem_fun(*this, &ImageFrameView::remove_marker_view_item), mv, src));
345
346         MarkerViewList::iterator i ;
347         
348         if((i = find (marker_view_list.begin(), marker_view_list.end(), mv)) != marker_view_list.end()) {
349                 marker_view_list.erase(i) ;
350                  MarkerViewRemoved (mv, src) ; /* EMIT_SIGNAL */
351         }
352 }
353                 
354 /**
355  * Determines if the named marker is one of those associated with this item
356  *
357  * @param markId the id/name of the item to search for
358  */
359 bool
360 ImageFrameView::has_marker_view_item(const string & mname)
361 {
362         bool result = false ;
363         
364         for (MarkerViewList::const_iterator ci = marker_view_list.begin(); ci != marker_view_list.end(); ++ci)
365         {
366                 if (((MarkerView*)*ci)->get_item_name() == mname)
367                 {
368                         result = true ;
369                         
370                         // found the item, so we can break the for loop
371                         break ;
372                 }
373         }
374         
375         return(result) ;
376 }