David Robillard
99904735e0
git-svn-id: svn://localhost/ardour2/branches/midi@1614 d708f5d6-7413-0410-9779-e7cbd77b26cf
377 lines
11 KiB
C++
377 lines
11 KiB
C++
/*
|
|
Copyright (C) 2003 Paul Davis
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
#include <gtkmm.h>
|
|
#include <gtkmm2ext/gtk_ui.h>
|
|
|
|
#include "imageframe_time_axis.h"
|
|
#include "imageframe_time_axis_group.h"
|
|
#include "marker_time_axis.h"
|
|
#include "marker_time_axis_view.h"
|
|
#include "public_editor.h"
|
|
#include "utils.h"
|
|
#include "imageframe_view.h"
|
|
#include "imageframe.h"
|
|
#include "canvas_impl.h"
|
|
#include "gui_thread.h"
|
|
|
|
using namespace sigc ;
|
|
using namespace ARDOUR ;
|
|
using namespace Gtk;
|
|
|
|
sigc::signal<void,ImageFrameView*> ImageFrameView::GoingAway;
|
|
|
|
/**
|
|
* Constructs a new ImageFrameView upon the canvas
|
|
*
|
|
* @param item_id unique id of this item
|
|
* @param parent the parent canvas item
|
|
* @param tv the time axis view that this item is to be placed upon
|
|
* @param group the ImageFrameGroup that this item is a member of
|
|
* @param spu the current samples per canvas unit
|
|
* @param start the start frame ogf this item
|
|
* @param duration the duration of this item
|
|
* @param rgb_data the rgb data of the image
|
|
* @param width the width of the original rgb_data image data
|
|
* @param height the width of the origianl rgb_data image data
|
|
* @param num_channels the number of color channels within rgb_data
|
|
*/
|
|
ImageFrameView::ImageFrameView(const string & item_id,
|
|
ArdourCanvas::Group *parent,
|
|
ImageFrameTimeAxis* tv,
|
|
ImageFrameTimeAxisGroup* item_group,
|
|
double spu,
|
|
Gdk::Color& basic_color,
|
|
nframes_t start,
|
|
nframes_t duration,
|
|
unsigned char* rgb_data,
|
|
uint32_t width,
|
|
uint32_t height,
|
|
uint32_t num_channels)
|
|
: TimeAxisViewItem(item_id, *parent, *tv, spu, basic_color, start, duration,
|
|
TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
|
|
TimeAxisViewItem::ShowNameHighlight|
|
|
TimeAxisViewItem::ShowFrame|
|
|
TimeAxisViewItem::ShowHandles))
|
|
|
|
{
|
|
the_parent_group = item_group ;
|
|
set_name_text(item_id) ;
|
|
|
|
image_data_width = width ;
|
|
image_data_height = height ;
|
|
image_data_num_channels = num_channels ;
|
|
|
|
//This should be art_free'd once the ArtPixBuf is destroyed - this should happen when we destroy the imageframe canvas item
|
|
unsigned char* the_rgb_data = (unsigned char*) art_alloc(width*height*num_channels) ;
|
|
memcpy(the_rgb_data, rgb_data, (width*height*num_channels)) ;
|
|
|
|
ArtPixBuf* pbuf ;
|
|
pbuf = art_pixbuf_new_rgba(the_rgb_data, width, height, (num_channels * width));
|
|
imageframe = 0 ;
|
|
|
|
//calculate our image width based on the track height
|
|
double im_ratio = (double)width/(double)height ;
|
|
double im_width = ((double)(trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE) * im_ratio) ;
|
|
|
|
imageframe = new ImageFrame (*group, pbuf, 1.0, 1.0, ANCHOR_NW, im_width, (trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE));
|
|
|
|
frame_handle_start->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_start_handle_event), frame_handle_start, this));
|
|
frame_handle_end->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_end_handle_event), frame_handle_end, this));
|
|
group->signal_event().connect (bind (mem_fun (trackview.editor, &PublicEditor::canvas_imageframe_item_view_event), imageframe, this));
|
|
|
|
frame_handle_start->raise_to_top();
|
|
frame_handle_end->raise_to_top();
|
|
|
|
set_position(start, this) ;
|
|
set_duration(duration, this) ;
|
|
}
|
|
|
|
/**
|
|
* Destructor
|
|
* Reposible for removing and destroying all marker items associated with this item
|
|
*/
|
|
ImageFrameView::~ImageFrameView()
|
|
{
|
|
GoingAway (this);
|
|
|
|
// destroy any marker items we have associated with this item
|
|
|
|
for(MarkerViewList::iterator iter = marker_view_list.begin(); iter != marker_view_list.end(); ++iter)
|
|
{
|
|
MarkerView* mv = (*iter) ;
|
|
|
|
MarkerViewList::iterator next = iter ;
|
|
next++ ;
|
|
|
|
// remove the item from our marker list
|
|
// the current iterator becomes invalid after this point, so we cannot call next upon it
|
|
// luckily enough, we already have next
|
|
marker_view_list.erase(iter) ;
|
|
|
|
// remove the item from the marker time axis
|
|
MarkerTimeAxisView* mtav = dynamic_cast<MarkerTimeAxis*>(&mv->get_time_axis_view())->get_view() ;
|
|
if(mtav)
|
|
{
|
|
mtav->remove_marker_view(mv, this) ;
|
|
}
|
|
|
|
mv->set_marked_item(0) ;
|
|
delete mv ;
|
|
mv = 0 ;
|
|
|
|
// set our iterator to next, as we have invalided the current iterator with the call to erase
|
|
iter = next ;
|
|
}
|
|
|
|
// if we are the currently selected item withi the parent track, we need to se-select
|
|
if(the_parent_group)
|
|
{
|
|
if(the_parent_group->get_view().get_selected_imageframe_view() == this)
|
|
{
|
|
the_parent_group->get_view().clear_selected_imageframe_item(false) ;
|
|
}
|
|
}
|
|
|
|
if(imageframe)
|
|
{
|
|
delete imageframe;
|
|
imageframe = 0 ;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------//
|
|
// Position and duration Accessors/Mutators
|
|
|
|
/**
|
|
* Set the position of this item to the specified value
|
|
*
|
|
* @param pos the new position
|
|
* @param src the identity of the object that initiated the change
|
|
* @return true if the position change was a success, false otherwise
|
|
*/
|
|
bool
|
|
ImageFrameView::set_position(nframes_t pos, void* src, double* delta)
|
|
{
|
|
nframes_t old_pos = frame_position ;
|
|
|
|
// do the standard stuff
|
|
bool ret = TimeAxisViewItem::set_position(pos, src, delta) ;
|
|
|
|
// everything went ok with the standard stuff?
|
|
if (ret) {
|
|
/* move each of our associated markers with this ImageFrameView */
|
|
for (MarkerViewList::iterator i = marker_view_list.begin(); i != marker_view_list.end(); ++i)
|
|
{
|
|
// calculate the offset of the marker
|
|
MarkerView* mv = (MarkerView*)*i ;
|
|
nframes_t marker_old_pos = mv->get_position() ;
|
|
|
|
mv->set_position(pos + (marker_old_pos - old_pos), src) ;
|
|
}
|
|
}
|
|
|
|
return(ret) ;
|
|
}
|
|
|
|
/**
|
|
* Sets the duration of this item
|
|
*
|
|
* @param dur the new duration of this item
|
|
* @param src the identity of the object that initiated the change
|
|
* @return true if the duration change was succesful, false otherwise
|
|
*/
|
|
bool
|
|
ImageFrameView::set_duration(nframes_t dur, void* src)
|
|
{
|
|
/* do the standard stuff */
|
|
bool ret = TimeAxisViewItem::set_duration(dur, src) ;
|
|
|
|
// eveything went ok with the standard stuff?
|
|
if(ret)
|
|
{
|
|
/* handle setting the sizes of our canvas itesm based on the new duration */
|
|
imageframe->property_drawwidth() = trackview.editor.frame_to_pixel(get_duration());
|
|
}
|
|
|
|
return(ret) ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------//
|
|
// Parent Component Methods
|
|
|
|
/**
|
|
* Sets the parent ImageFrameTimeAxisGroup of thie item
|
|
* each Item must be part of exactly one group (or 'scene') upon the timeline
|
|
*
|
|
* @param group the new parent group
|
|
*/
|
|
void
|
|
ImageFrameView::set_time_axis_group(ImageFrameTimeAxisGroup* group)
|
|
{
|
|
the_parent_group = group ;
|
|
}
|
|
|
|
/**
|
|
* Returns the parent group of this item
|
|
*
|
|
* @return the parent group of this item
|
|
*/
|
|
ImageFrameTimeAxisGroup*
|
|
ImageFrameView::get_time_axis_group()
|
|
{
|
|
return(the_parent_group) ;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------//
|
|
// ui methods
|
|
|
|
/**
|
|
* Set the height of this item
|
|
*
|
|
* @param h the new height
|
|
*/
|
|
void
|
|
ImageFrameView::set_height (gdouble h)
|
|
{
|
|
// set the image size
|
|
// @todo might have to re-get the image data, for a large height...hmmm.
|
|
double im_ratio = (double)image_data_width/(double)image_data_height ;
|
|
|
|
imageframe->property_width() = (h - TimeAxisViewItem::NAME_Y_OFFSET) * im_ratio;
|
|
imageframe->property_height() = h - TimeAxisViewItem::NAME_Y_OFFSET;
|
|
|
|
frame->raise_to_top();
|
|
imageframe->raise_to_top();
|
|
name_highlight->raise_to_top();
|
|
name_text->raise_to_top();
|
|
frame_handle_start->raise_to_top();
|
|
frame_handle_end->raise_to_top();
|
|
|
|
name_text->property_y() = h - TimeAxisViewItem::NAME_Y_OFFSET;
|
|
frame->property_y2() = h;
|
|
|
|
name_highlight->property_y1() = (gdouble) h - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
|
|
name_highlight->property_y2() = (gdouble) h - 1.0;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------//
|
|
// MarkerView methods
|
|
|
|
/**
|
|
* Adds a markerView to the list of marker views associated with this item
|
|
*
|
|
* @param item the marker item to add
|
|
* @param src the identity of the object that initiated the change
|
|
*/
|
|
void
|
|
ImageFrameView::add_marker_view_item(MarkerView* item, void* src)
|
|
{
|
|
marker_view_list.push_back(item) ;
|
|
|
|
item->GoingAway.connect(bind(mem_fun(*this, &ImageFrameView::remove_marker_view_item), (void*)this));
|
|
|
|
MarkerViewAdded(item, src) ; /* EMIT_SIGNAL */
|
|
}
|
|
|
|
/**
|
|
* Removes the named marker view from the list of marker view associated with this item
|
|
* The Marker view is not destroyed on removal, so the caller must handle the item themself
|
|
*
|
|
* @param markId the id/name of the item to remove
|
|
* @param src the identity of the object that initiated the change
|
|
* @return the removed marker item
|
|
*/
|
|
MarkerView*
|
|
ImageFrameView::remove_named_marker_view_item(const string & markerId, void* src)
|
|
{
|
|
MarkerView* mv = 0 ;
|
|
MarkerViewList::iterator i = marker_view_list.begin() ;
|
|
|
|
while(i != marker_view_list.end())
|
|
{
|
|
if (((MarkerView*)*i)->get_item_name() == markerId)
|
|
{
|
|
mv = (*i) ;
|
|
|
|
marker_view_list.erase(i) ;
|
|
|
|
MarkerViewRemoved(mv,src) ; /* EMIT_SIGNAL */
|
|
|
|
// iterator is now invalid, but since we should only ever have
|
|
// one item with the specified name, things are ok, and we can
|
|
// break from the while loop
|
|
break ;
|
|
}
|
|
i++ ;
|
|
}
|
|
|
|
return(mv) ;
|
|
}
|
|
|
|
/**
|
|
* Removes item from the list of marker views assocaited with this item
|
|
* This method will do nothing if item if not assiciated with this item
|
|
*
|
|
* @param item the item to remove
|
|
* @param src the identity of the object that initiated the change
|
|
*/
|
|
void
|
|
ImageFrameView::remove_marker_view_item(MarkerView* mv, void* src)
|
|
{
|
|
ENSURE_GUI_THREAD(bind (mem_fun(*this, &ImageFrameView::remove_marker_view_item), mv, src));
|
|
|
|
MarkerViewList::iterator i ;
|
|
|
|
if((i = find (marker_view_list.begin(), marker_view_list.end(), mv)) != marker_view_list.end()) {
|
|
marker_view_list.erase(i) ;
|
|
MarkerViewRemoved (mv, src) ; /* EMIT_SIGNAL */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if the named marker is one of those associated with this item
|
|
*
|
|
* @param markId the id/name of the item to search for
|
|
*/
|
|
bool
|
|
ImageFrameView::has_marker_view_item(const string & mname)
|
|
{
|
|
bool result = false ;
|
|
|
|
for (MarkerViewList::const_iterator ci = marker_view_list.begin(); ci != marker_view_list.end(); ++ci)
|
|
{
|
|
if (((MarkerView*)*ci)->get_item_name() == mname)
|
|
{
|
|
result = true ;
|
|
|
|
// found the item, so we can break the for loop
|
|
break ;
|
|
}
|
|
}
|
|
|
|
return(result) ;
|
|
}
|