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