changes to help strp silence
[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         MarkerView::CatchDeletion.connect (*this, ui_bind (&ImageFrameView::remove_marker_view_item, this, _1), gui_context());
108 }
109
110 /**
111  * Destructor
112  * Reposible for removing and destroying all marker items associated with this item
113  */
114 ImageFrameView::~ImageFrameView()
115 {
116         CatchDeletion (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         delete imageframe;
157         imageframe = 0;
158 }
159
160
161 //---------------------------------------------------------------------------------------//
162 // Position and duration Accessors/Mutators
163
164 /**
165  * Set the position of this item to the specified value
166  *
167  * @param pos the new position
168  * @param src the identity of the object that initiated the change
169  * @return true if the position change was a success, false otherwise
170  */
171 bool
172 ImageFrameView::set_position(nframes64_t pos, void* src, double* delta)
173 {
174         nframes64_t old_pos = frame_position;
175
176         // do the standard stuff
177         bool ret = TimeAxisViewItem::set_position(pos, src, delta);
178
179         // everything went ok with the standard stuff?
180         if (ret) {
181                 /* move each of our associated markers with this ImageFrameView */
182                 for (MarkerViewList::iterator i = marker_view_list.begin(); i != marker_view_list.end(); ++i)
183                 {
184                         // calculate the offset of the marker
185                         MarkerView* mv = (MarkerView*)*i;
186                         nframes64_t marker_old_pos = mv->get_position();
187
188                         mv->set_position(pos + (marker_old_pos - old_pos), src);
189                 }
190         }
191
192         return(ret);
193 }
194
195 /**
196  * Sets the duration of this item
197  *
198  * @param dur the new duration of this item
199  * @param src the identity of the object that initiated the change
200  * @return true if the duration change was succesful, false otherwise
201  */
202 bool
203 ImageFrameView::set_duration(nframes64_t dur, void* src)
204 {
205         /* do the standard stuff */
206         bool ret = TimeAxisViewItem::set_duration(dur, src);
207
208         // eveything went ok with the standard stuff?
209         if(ret)
210         {
211                 /* handle setting the sizes of our canvas itesm based on the new duration */
212                 imageframe->property_drawwidth() = trackview.editor.frame_to_pixel(get_duration());
213         }
214
215         return(ret);
216 }
217
218 //---------------------------------------------------------------------------------------//
219 // Parent Component Methods
220
221 /**
222  * Sets the parent ImageFrameTimeAxisGroup of thie item
223  * each Item must be part of exactly one group (or 'scene') upon the timeline
224  *
225  * @param group the new parent group
226  */
227 void
228 ImageFrameView::set_time_axis_group(ImageFrameTimeAxisGroup* group)
229 {
230         the_parent_group = group;
231 }
232
233 /**
234  * Returns the parent group of this item
235  *
236  * @return the parent group of this item
237  */
238 ImageFrameTimeAxisGroup*
239 ImageFrameView::get_time_axis_group()
240 {
241         return(the_parent_group);
242 }
243
244
245 //---------------------------------------------------------------------------------------//
246 // ui methods
247
248 /**
249  * Set the height of this item
250  *
251  * @param h the new height
252  */
253 void
254 ImageFrameView::set_height (gdouble h)
255 {
256         // set the image size
257         // @todo might have to re-get the image data, for a large height...hmmm.
258         double im_ratio = (double)image_data_width/(double)image_data_height;
259
260         imageframe->property_width() = (h - TimeAxisViewItem::NAME_Y_OFFSET) * im_ratio;
261         imageframe->property_height() = h - TimeAxisViewItem::NAME_Y_OFFSET;
262
263         frame->raise_to_top();
264         imageframe->raise_to_top();
265         name_highlight->raise_to_top();
266         name_pixbuf->raise_to_top();
267         frame_handle_start->raise_to_top();
268         frame_handle_end->raise_to_top();
269
270         name_pixbuf->property_y() = h - TimeAxisViewItem::NAME_Y_OFFSET;
271         frame->property_y2() = h;
272
273         name_highlight->property_y1() = (gdouble) h - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
274         name_highlight->property_y2() = (gdouble) h - 1.0;
275 }
276
277
278 //---------------------------------------------------------------------------------------//
279 // MarkerView methods
280
281 /**
282  * Adds a markerView to the list of marker views associated with this item
283  *
284  * @param item the marker item to add
285  * @param src the identity of the object that initiated the change
286  */
287 void
288 ImageFrameView::add_marker_view_item(MarkerView* item, void* src)
289 {
290         marker_view_list.push_back(item);
291          MarkerViewAdded(item, src); /* EMIT_SIGNAL */
292 }
293
294 /**
295  * Removes the named marker view from the list of marker view associated with this item
296  * The Marker view is not destroyed on removal, so the caller must handle the item themself
297  *
298  * @param markId the id/name of the item to remove
299  * @param src the identity of the object that initiated the change
300  * @return the removed marker item
301  */
302 MarkerView*
303 ImageFrameView::remove_named_marker_view_item(const string & markerId, void* src)
304 {
305         MarkerView* mv = 0;
306         MarkerViewList::iterator i = marker_view_list.begin();
307
308         while(i != marker_view_list.end())
309         {
310                 if (((MarkerView*)*i)->get_item_name() == markerId)
311                 {
312                         mv = (*i);
313
314                         marker_view_list.erase(i);
315
316                          MarkerViewRemoved(mv,src); /* EMIT_SIGNAL */
317
318                         // iterator is now invalid, but since we should only ever have
319                         // one item with the specified name, things are ok, and we can
320                         // break from the while loop
321                         break;
322                 }
323                 i++;
324         }
325
326         return(mv);
327 }
328
329 /**
330  * Removes item from the list of marker views assocaited with this item
331  * This method will do nothing if item if not assiciated with this item
332  *
333  * @param item the item to remove
334  * @param src the identity of the object that initiated the change
335  */
336 void
337 ImageFrameView::remove_marker_view_item (MarkerView* mv)
338 {
339         ENSURE_GUI_THREAD (*this, &ImageFrameView::remove_marker_view_item, mv, src)
340
341         MarkerViewList::iterator i;
342
343         if ((i = find (marker_view_list.begin(), marker_view_list.end(), mv)) != marker_view_list.end()) {
344                 marker_view_list.erase(i);
345                 MarkerViewRemoved (mv, src); /* EMIT_SIGNAL */
346         }
347 }
348
349 /**
350  * Determines if the named marker is one of those associated with this item
351  *
352  * @param markId the id/name of the item to search for
353  */
354 bool
355 ImageFrameView::has_marker_view_item(const string & mname)
356 {
357         bool result = false;
358
359         for (MarkerViewList::const_iterator ci = marker_view_list.begin(); ci != marker_view_list.end(); ++ci)
360         {
361                 if (((MarkerView*)*ci)->get_item_name() == mname)
362                 {
363                         result = true;
364
365                         // found the item, so we can break the for loop
366                         break;
367                 }
368         }
369
370         return(result);
371 }