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