28d2aac1a16d4edc4b66002fa57377819c535edf
[ardour.git] / gtk2_ardour / imageframe_time_axis_group.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
22 #include <gtkmm.h>
23 #include <gtkmm2ext/gtk_ui.h>
24
25 #include "imageframe_time_axis_group.h"
26 #include "imageframe_time_axis_view.h"
27 #include "imageframe_view.h"
28 #include "imageframe_time_axis.h"
29 #include "canvas-simplerect.h"
30 #include "region_selection.h"
31 #include "public_editor.h"
32 #include "gui_thread.h"
33
34 #include "i18n.h"
35
36 using namespace ARDOUR ;
37
38 //---------------------------------------------------------------------------------------//
39 // Constructor / Desctructor
40
41 /**
42  * Constructs a new ImageFrameTimeAxisGroup.
43  *
44  * @param iftav the parent ImageFrameTimeAxis of this view helper
45  * @param group_id the unique name/id of this group
46  */
47 ImageFrameTimeAxisGroup::ImageFrameTimeAxisGroup(ImageFrameTimeAxisView& iftav, const string & group_id)
48         : _view_helper(iftav), _group_id(group_id)
49 {
50         selected_imageframe_item = 0 ;
51         is_selected = false ;
52 }
53
54 /**
55  * Destructor
56  * Responsible for destroying any Items that may have been added to this group
57  *
58  */
59 ImageFrameTimeAxisGroup::~ImageFrameTimeAxisGroup()
60 {
61         // Destroy all the ImageFramViews that we have
62         for(ImageFrameViewList::iterator iter = imageframe_views.begin(); iter != imageframe_views.end(); ++iter)
63         {
64                 ImageFrameView* ifv = *iter ;
65
66                 ImageFrameViewList::iterator next = iter ;
67                 next++ ;
68
69                 imageframe_views.erase(iter) ;
70
71                 delete ifv ;
72                 ifv = 0 ;
73
74                 iter = next ;
75         }
76
77          GoingAway ; /* EMIT_SIGNAL */
78 }
79
80
81 //---------------------------------------------------------------------------------------//
82 // Name/Id Accessors/Mutators
83
84 /**
85  * Set the name/Id of this group.
86  *
87  * @param new_name the new name of this group
88  * @param src the identity of the object that initiated the change
89  */
90 void
91 ImageFrameTimeAxisGroup::set_group_name(const string & new_name, void* src)
92 {
93         if(_group_id != new_name)
94         {
95                 std::string temp_name = _group_id ;
96                 _group_id = new_name ;
97                  NameChanged(_group_id, temp_name, src) ; /* EMIT_SIGNAL */
98         }
99 }
100
101 /**
102  * Returns the id of this group
103  * The group id must be unique upon a time axis
104  *
105  * @return the id of this group
106  */
107 std::string
108 ImageFrameTimeAxisGroup::get_group_name() const
109 {
110         return(_group_id) ;
111 }
112
113
114 //---------------------------------------------------------------------------------------//
115 // ui methods & data
116
117 /**
118  * Sets the height of the time axis view and the item upon it
119  *
120  * @param height the new height
121  */
122 int
123 ImageFrameTimeAxisGroup::set_item_heights(gdouble h)
124 {
125         /* limit the values to something sane-ish */
126         if (h < 10.0 || h > 1000.0)
127         {
128                 return(-1) ;
129         }
130
131         // set the heights of all the imaeg frame views within the group
132         for(ImageFrameViewList::const_iterator citer = imageframe_views.begin(); citer != imageframe_views.end(); ++citer)
133         {
134                 (*citer)->set_height(h) ;
135         }
136
137         return(0) ;
138 }
139
140 /**
141  * Sets the current samples per unit.
142  * this method tells each item upon the time axis of the change
143  *
144  * @param spu the new samples per canvas unit value
145  */
146 int
147 ImageFrameTimeAxisGroup::set_item_samples_per_units(gdouble spp)
148 {
149         if(spp < 1.0)
150         {
151                 return(-1) ;
152         }
153
154         for(ImageFrameViewList::const_iterator citer = imageframe_views.begin(); citer != imageframe_views.end(); ++citer)
155         {
156                 (*citer)->set_samples_per_unit(spp) ;
157         }
158
159         return(0) ;
160 }
161
162 /**
163  * Sets the color of the items contained uopn this view helper
164  *
165  * @param color the new base color
166  */
167 void
168 ImageFrameTimeAxisGroup::apply_item_color(Gdk::Color& color)
169 {
170         region_color = color ;
171         for(ImageFrameViewList::const_iterator citer = imageframe_views.begin(); citer != imageframe_views.end(); citer++)
172         {
173                 (*citer)->set_color (region_color) ;
174         }
175 }
176
177
178
179 //---------------------------------------------------------------------------------------//
180 // child ImageFrameView methods
181
182 /**
183  * Adds an ImageFrameView to the list of items upon this time axis view helper
184  * the new ImageFrameView is returned
185  *
186  * @param item_id the unique id of the new item
187  * @param image_id the id/name of the image data we are usin
188  * @param start the position the new item should be placed upon the time line
189  * @param duration the duration the new item should be placed upon the timeline
190  * @param rgb_data the rgb data of the image
191  * @param width the original image width of the rgb_data (not the size to display)
192  * @param height the irigianl height of the rgb_data
193  * @param num_channels the number of channles within the rgb_data
194  * @param src the identity of the object that initiated the change
195  */
196 ImageFrameView*
197 ImageFrameTimeAxisGroup::add_imageframe_item(const string & frame_id, nframes_t start, nframes_t duration, unsigned char* rgb_data, uint32_t width, uint32_t height, uint32_t num_channels, void* src)
198 {
199         ImageFrameView* ifv = 0 ;
200
201         //check that there is not already an imageframe with that id
202         if(get_named_imageframe_item(frame_id) == 0)
203         {
204                 ifv = new ImageFrameView(frame_id,
205                         _view_helper.canvas_item()->property_parent(),
206                         &(_view_helper.trackview()),
207                         this,
208                         _view_helper.trackview().editor.get_current_zoom(),
209                         region_color,
210                         start,
211                         duration,
212                         rgb_data,
213                         width,
214                         height,
215                         num_channels) ;
216
217                 imageframe_views.push_front(ifv) ;
218
219                 scoped_connect (ifv->GoingAway, boost::bind (&ImageFrameTimeAxisGroup::remove_imageframe_item, this, (void*)this));
220
221                  ImageFrameAdded(ifv, src) ; /* EMIT_SIGNAL */
222         }
223
224         return(ifv) ;
225 }
226
227
228 /**
229  * Returns the named ImageFrameView or 0 if the named view does not exist on this view helper
230  *
231  * @param item_id the unique id of the item to search for
232  * @return the named ImageFrameView, or 0 if it is not held upon this view
233  */
234 ImageFrameView*
235 ImageFrameTimeAxisGroup::get_named_imageframe_item(const string & frame_id)
236 {
237         ImageFrameView* ifv =  0 ;
238
239         for (ImageFrameViewList::const_iterator i = imageframe_views.begin(); i != imageframe_views.end(); ++i)
240         {
241                 if (((ImageFrameView*)*i)->get_item_name() == frame_id)
242                 {
243                         ifv = ((ImageFrameView*)*i) ;
244                         break ;
245                 }
246         }
247         return(ifv) ;
248 }
249
250 /**
251  * Removes the currently selected ImageFrameView
252  *
253  * @param src the identity of the object that initiated the change
254  * @todo need to remoev this, the selected item within group is no longer
255  *       used in favour of a time axis selected item
256  * @see add_imageframe_view
257  */
258 void
259 ImageFrameTimeAxisGroup::remove_selected_imageframe_item(void* src)
260 {
261         std::string frame_id ;
262
263         if(selected_imageframe_item)
264         {
265                 ImageFrameViewList::iterator i ;
266
267                 if((i = find(imageframe_views.begin(), imageframe_views.end(), selected_imageframe_item)) != imageframe_views.end())
268                 {
269                         imageframe_views.erase(i) ;
270                         frame_id = selected_imageframe_item->get_item_name() ;
271
272                         // note that we delete the item here
273                         delete(selected_imageframe_item) ;
274                         selected_imageframe_item = 0 ;
275
276                         std::string track_id = _view_helper.trackview().name() ;
277                          ImageFrameRemoved(track_id, _group_id, frame_id, src) ; /* EMIT_SIGNAL */
278                 }
279         }
280         else
281         {
282                 //cerr << "No Selected ImageFrame" << endl ;
283         }
284 }
285
286
287 /**
288  * Removes and returns the named ImageFrameView from the list of ImageFrameViews held by this view helper
289  *
290  * @param item_id the ImageFrameView unique id to remove
291  * @param src the identity of the object that initiated the change
292  * @see add_imageframe_view
293  */
294 ImageFrameView*
295 ImageFrameTimeAxisGroup::remove_named_imageframe_item(const string & frame_id, void* src)
296 {
297         ImageFrameView* removed = 0 ;
298
299         for(ImageFrameViewList::iterator iter = imageframe_views.begin(); iter != imageframe_views.end(); ++iter)
300         {
301                 ImageFrameView* tempItem = *iter ;
302                 if(tempItem->get_item_name() == frame_id)
303                 {
304                         removed = tempItem ;
305                         imageframe_views.erase(iter) ;
306
307                         if (removed == selected_imageframe_item)
308                         {
309                                 selected_imageframe_item = 0 ;
310                         }
311
312                         std::string track_id = _view_helper.trackview().name() ;
313                          ImageFrameRemoved(track_id, _group_id, frame_id, src) ; /* EMIT_SIGNAL */
314
315                         // break from the for loop
316                         break ;
317                 }
318                 iter++ ;
319         }
320
321         return(removed) ;
322 }
323
324 /**
325  * Removes ifv from the list of ImageFrameViews upon this TimeAxis.
326  * if ifv is not upon this TimeAxis, this method takes no action
327  *
328  * @param ifv the ImageFrameView to remove
329  */
330 void
331 ImageFrameTimeAxisGroup::remove_imageframe_item(ImageFrameView* ifv, void* src)
332 {
333         ENSURE_GUI_THREAD (*this, &ImageFrameTimeAxisGroup::remove_imageframe_item, ifv, src)
334
335         ImageFrameViewList::iterator i;
336         if((i = find (imageframe_views.begin(), imageframe_views.end(), ifv)) != imageframe_views.end())
337         {
338                 imageframe_views.erase(i) ;
339
340                 std::string frame_id = ifv->get_item_name() ;
341                 std::string track_id = _view_helper.trackview().name() ;
342                  ImageFrameRemoved(track_id, _group_id, frame_id, src) ; /* EMIT_SIGNAL */
343         }
344 }
345
346 //---------------------------------------------------------------------------------------//
347 // Selected group methods
348
349 /**
350  * Sets the currently selected item upon this time axis
351  *
352  * @param ifv the item to set selected
353  */
354 //void
355 //ImageFrameTimeAxisGroup::set_selected_imageframe_item(ImageFrameView* ifv)
356 //{
357 //      if(selected_imageframe_item)
358 //      {
359 //              selected_imageframe_item->set_selected(false, this) ;
360 //      }
361 //
362 //      selected_imageframe_item = ifv ;
363 //
364 //      if(!ifv->get_selected())
365 //      {
366 //              selected_imageframe_item->set_selected(true, this) ;
367 //      }
368 //}
369
370
371 /**
372  * Sets the currently selected item upon this time axis to the named item
373  *
374  * @param item_id the name/id of the item to set selected
375  */
376 //void
377 //ImageFrameTimeAxisGroup::set_selected_imageframe_item(std::string frame_id)
378 //{
379 //      selected_imageframe_item = get_named_imageframe_item(frame_id) ;
380 //}
381
382
383 /**
384  * Returns the currently selected item upon this time axis
385  *
386  * @return the currently selected item pon this time axis
387  */
388 // ImageFrameView*
389 // ImageFrameTimeAxisGroup::get_selected_imageframe_item()
390 // {
391         // return(selected_imageframe_item) ;
392 // }
393
394
395
396 /**
397  * Returns whether this grou pis currently selected
398  *
399  * @returns true if this group is currently selected
400  */
401 bool
402 ImageFrameTimeAxisGroup::get_selected() const
403 {
404         return(is_selected) ;
405 }
406
407
408 /**
409  * Sets he selected state of this group
410  *
411  * @param yn set true if this group is selected, false otherwise
412  */
413 void
414 ImageFrameTimeAxisGroup::set_selected(bool yn)
415 {
416         is_selected = yn ;
417 }
418
419
420
421 //---------------------------------------------------------------------------------------//
422 // Handle time axis removal
423
424 /**
425  * Handles the Removal of this VisualTimeAxis
426  * This _needs_ to be called to alert others of the removal properly, ie where the source
427  * of the removal came from.
428  *
429  * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
430  *     just now to capture the source of the removal
431  *
432  * @param src the identity of the object that initiated the change
433  */
434 void
435 ImageFrameTimeAxisGroup::remove_this_group(void* src)
436 {
437         /*
438            defer to idle loop, otherwise we'll delete this object
439            while we're still inside this function ...
440         */
441         Glib::signal_idle().connect(sigc::bind(ptr_fun(&ImageFrameTimeAxisGroup::idle_remove_this_group), this, src));
442 }
443
444 /**
445  * Callback used to remove this group during the gtk idle loop
446  * This is used to avoid deleting the obejct while inside the remove_this_group
447  * method
448  *
449  * @param group the ImageFrameTimeAxisGroup to remove
450  * @param src the identity of the object that initiated the change
451  */
452 gint
453 ImageFrameTimeAxisGroup::idle_remove_this_group(ImageFrameTimeAxisGroup* group, void* src)
454 {
455         delete group ;
456         group = 0 ;
457          group->GroupRemoved(group->get_group_name(), src) ; /* EMIT_SIGNAL */
458         return(false) ;
459 }
460