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