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