13
0
livetrax/gtk2_ardour/marker.cc
Paul Davis 7e19053b88 Fix dragging objects on the canvas and remove redundant canvas groups
Delivery of fake motion events to the editor needed the event coordinates to be
in canvas space, as they are with "real" events. Editor and other objects had
many redundant groups from timbyr's work on gnomecanvas to scroll by moving
groups. We don't need this anymore with cairo-canvas (though possibly a
stationay background group for the canvas might be useful again one day as in
the SAE logo. Its implementation would be fairly different though, since we
would have to explicitly move the group on every scroll, since nothing else
ever moves on scroll).

Also tweaks to text item placement, and switch TimeAxisViewItem from
name_pixbuf to name_text, since ArdourCanvas::Text is already "pixbuf optimized".
2013-04-15 13:50:05 -04:00

537 lines
12 KiB
C++

/*
Copyright (C) 2001 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 <sigc++/bind.h>
#include "ardour/tempo.h"
#include "canvas/rectangle.h"
#include "canvas/group.h"
#include "canvas/line.h"
#include "canvas/polygon.h"
#include "canvas/text.h"
#include "ardour_ui.h"
/*
* ardour_ui.h include was moved to the top of the list
* due to a conflicting definition of 'Rect' between
* Apple's MacTypes.h and GTK.
*/
#include "marker.h"
#include "public_editor.h"
#include "utils.h"
#include "rgb_macros.h"
#include <gtkmm2ext/utils.h>
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace Gtkmm2ext;
PBD::Signal1<void,Marker*> Marker::CatchDeletion;
Marker::Marker (PublicEditor& ed, ArdourCanvas::Group& parent, guint32 rgba, const string& annotation,
Type type, framepos_t frame, bool handle_events)
: editor (ed)
, _parent (&parent)
, _line (0)
, _type (type)
, _selected (false)
, _shown (false)
, _line_shown (false)
, _canvas_height (0)
, _color (rgba)
, _left_label_limit (DBL_MAX)
, _right_label_limit (DBL_MAX)
, _label_offset (0)
{
/* Shapes we use:
Mark:
(0,0) -> (6,0)
^ |
| V
(0,5) (6,5)
\ /
(3,13)
TempoMark:
MeterMark:
(3,0)
/ \
(0,5) -> (6,5)
^ |
| V
(0,10)<-(6,10)
Start:
0,0\
| \
| \ 6,6
| /
| /
0,12
End:
/12,0
/ |
/ |
6,6 |
\ |
\ |
\ |
12,12
PunchIn:
0,0 ------> 13,0
| /
| /
| /
| /
| /
| /
0,13
PunchOut
0,0 -->-13,0
\ |
\ |
\ |
\ |
\ |
\ |
13,13
*/
switch (type) {
case Mark:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
points->push_back (ArdourCanvas::Duple (6.0, 0.0));
points->push_back (ArdourCanvas::Duple (6.0, 5.0));
points->push_back (ArdourCanvas::Duple (3.0, 13.0));
points->push_back (ArdourCanvas::Duple (0.0, 5.0));
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
_shift = 3;
_label_offset = 8.0;
break;
case Tempo:
case Meter:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (3.0, 0.0));
points->push_back (ArdourCanvas::Duple (6.0, 5.0));
points->push_back (ArdourCanvas::Duple (6.0, 10.0));
points->push_back (ArdourCanvas::Duple (0.0, 10.0));
points->push_back (ArdourCanvas::Duple (0.0, 5.0));
points->push_back (ArdourCanvas::Duple (3.0, 0.0));
_shift = 3;
_label_offset = 8.0;
break;
case SessionStart:
case RangeStart:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
points->push_back (ArdourCanvas::Duple (6.5, 6.5));
points->push_back (ArdourCanvas::Duple (0.0, 13.0));
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
_shift = 0;
_label_offset = 13.0;
break;
case SessionEnd:
case RangeEnd:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (6.5, 6.5));
points->push_back (ArdourCanvas::Duple (13.0, 0.0));
points->push_back (ArdourCanvas::Duple (13.0, 13.0));
points->push_back (ArdourCanvas::Duple (6.5, 6.5));
_shift = 13;
_label_offset = 6.0;
break;
case LoopStart:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
points->push_back (ArdourCanvas::Duple (13.0, 13.0));
points->push_back (ArdourCanvas::Duple (0.0, 13.0));
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
_shift = 0;
_label_offset = 12.0;
break;
case LoopEnd:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (13.0, 0.0));
points->push_back (ArdourCanvas::Duple (13.0, 13.0));
points->push_back (ArdourCanvas::Duple (0.0, 13.0));
points->push_back (ArdourCanvas::Duple (13.0, 0.0));
_shift = 13;
_label_offset = 0.0;
break;
case PunchIn:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
points->push_back (ArdourCanvas::Duple (13.0, 0.0));
points->push_back (ArdourCanvas::Duple (0.0, 13.0));
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
_shift = 0;
_label_offset = 13.0;
break;
case PunchOut:
points = new ArdourCanvas::Points ();
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
points->push_back (ArdourCanvas::Duple (12.0, 0.0));
points->push_back (ArdourCanvas::Duple (12.0, 12.0));
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
_shift = 13;
_label_offset = 0.0;
break;
}
frame_position = frame;
unit_position = editor.sample_to_pixel (frame);
unit_position -= _shift;
group = new ArdourCanvas::Group (&parent, ArdourCanvas::Duple (unit_position, 0));
#ifdef CANVAS_DEBUG
group->name = string_compose ("Marker::group for %1", annotation);
#endif
_name_background = new ArdourCanvas::Rectangle (group);
#ifdef CANVAS_DEBUG
_name_background->name = string_compose ("Marker::_name_background for %1", annotation);
#endif
_name_background->set_outline_width (1);
/* adjust to properly locate the tip */
mark = new ArdourCanvas::Polygon (group);
#ifdef CANVAS_DEBUG
mark->name = string_compose ("Marker::mark for %1", annotation);
#endif
mark->set (*points);
set_color_rgba (rgba);
mark->set_outline_width (1);
/* setup name pixbuf sizes */
name_font = get_font_for_style (N_("MarkerText"));
Gtk::Label foo;
Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
int width;
layout->set_font_description (name_font);
Gtkmm2ext::get_ink_pixel_size (layout, width, name_height);
_name_item = new ArdourCanvas::Text (group);
_name_item->set_font_description (name_font);
_name_item->set_color (RGBA_TO_UINT (0,0,0,255));
#ifdef CANVAS_DEBUG
_name_item->name = string_compose ("Marker::_name_item for %1", annotation);
#endif
_name_item->set_position (ArdourCanvas::Duple (_label_offset, (13.0 / 2.0) - (name_height / 2.0)));
set_name (annotation.c_str());
editor.ZoomChanged.connect (sigc::mem_fun (*this, &Marker::reposition));
/* events will be handled by both the group and the mark itself, so
* make sure they can both be used to lookup this object.
*/
group->set_data ("marker", this);
mark->set_data ("marker", this);
if (handle_events) {
group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
}
}
Marker::~Marker ()
{
CatchDeletion (this); /* EMIT SIGNAL */
/* destroying the parent group destroys its contents, namely any polygons etc. that we added */
delete group;
delete _line;
}
void Marker::reparent(ArdourCanvas::Group & parent)
{
group->reparent (&parent);
_parent = &parent;
}
void
Marker::set_selected (bool s)
{
_selected = s;
setup_line ();
}
void
Marker::set_show_line (bool s)
{
_line_shown = s;
setup_line ();
}
void
Marker::setup_line ()
{
if (_shown && (_selected || _line_shown)) {
if (_line == 0) {
_line = new ArdourCanvas::Line (group);
_line->set_outline_color (ARDOUR_UI::config()->canvasvar_EditPoint.get());
_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
}
/* work out where to start the line from so that it extends from the top of the canvas */
double yo = 0;
double xo = 0;
_line->item_to_canvas (xo, yo);
_line->set_x0 (_shift);
_line->set_x1 (_shift);
_line->set_y0 (-yo); // zero in world coordinates, negative in item/parent coordinate space
_line->set_y1 (-yo + _canvas_height);
_line->set_outline_color (_selected ? ARDOUR_UI::config()->canvasvar_EditPoint.get() : _color);
_line->raise_to_top ();
_line->show ();
} else {
if (_line) {
_line->hide ();
}
}
}
void
Marker::canvas_height_set (double h)
{
_canvas_height = h;
setup_line ();
}
ArdourCanvas::Item&
Marker::the_item() const
{
return *group;
}
void
Marker::set_name (const string& new_name)
{
_name = new_name;
setup_name_display ();
}
/** @return true if our label is on the left of the mark, otherwise false */
bool
Marker::label_on_left () const
{
return (_type == SessionEnd || _type == RangeEnd || _type == LoopEnd || _type == PunchOut);
}
void
Marker::setup_name_display ()
{
double limit = DBL_MAX;
if (label_on_left ()) {
limit = _left_label_limit;
} else {
limit = _right_label_limit;
}
/* Work out how wide the name can be */
int name_width = min ((double) pixel_width (_name, name_font) + 2, limit);
if (name_width == 0) {
name_width = 1;
}
if (label_on_left ()) {
_name_item->set_x_position (-name_width);
}
_name_item->set (_name);
// CAIROCANVAS
// need to "clip" name to name_width and name_height
if (label_on_left ()) {
_name_background->set_x0 (_name_item->position().x - 2);
_name_background->set_x1 (_name_item->position().x + name_width + _shift);
} else {
_name_background->set_x0 (_name_item->position().x - _label_offset + 2);
_name_background->set_x1 (_name_item->position().x + name_width);
}
_name_background->set_y0 (0);
_name_background->set_y1 (13);
}
void
Marker::set_position (framepos_t frame)
{
unit_position = editor.sample_to_pixel (frame) - _shift;
group->set_x_position (unit_position);
frame_position = frame;
}
void
Marker::reposition ()
{
set_position (frame_position);
}
void
Marker::show ()
{
_shown = true;
group->show ();
setup_line ();
}
void
Marker::hide ()
{
_shown = false;
group->hide ();
setup_line ();
}
void
Marker::set_color_rgba (uint32_t c)
{
_color = c;
mark->set_fill_color (_color);
mark->set_outline_color (_color);
if (_line && !_selected) {
_line->set_outline_color (_color);
}
_name_background->set_fill (true);
_name_background->set_fill_color (UINT_RGBA_CHANGE_A (_color, 0x70));
_name_background->set_outline_color (_color);
}
/** Set the number of pixels that are available for a label to the left of the centre of this marker */
void
Marker::set_left_label_limit (double p)
{
/* Account for the size of the marker */
_left_label_limit = p - 13;
if (_left_label_limit < 0) {
_left_label_limit = 0;
}
if (label_on_left ()) {
setup_name_display ();
}
}
/** Set the number of pixels that are available for a label to the right of the centre of this marker */
void
Marker::set_right_label_limit (double p)
{
/* Account for the size of the marker */
_right_label_limit = p - 13;
if (_right_label_limit < 0) {
_right_label_limit = 0;
}
if (!label_on_left ()) {
setup_name_display ();
}
}
/***********************************************************************/
TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
ARDOUR::TempoSection& temp)
: Marker (editor, parent, rgba, text, Tempo, 0, false),
_tempo (temp)
{
set_position (_tempo.frame());
group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), group, this));
}
TempoMarker::~TempoMarker ()
{
}
/***********************************************************************/
MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
ARDOUR::MeterSection& m)
: Marker (editor, parent, rgba, text, Meter, 0, false),
_meter (m)
{
set_position (_meter.frame());
group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), group, this));
}
MeterMarker::~MeterMarker ()
{
}