#include "gtkmm2ext/keyboard.h"
#include "speaker_dialog.h"
+#include "gui_thread.h"
#include "i18n.h"
, azimuth_adjustment (0, 0.0, 360.0, 10.0, 1.0)
, azimuth_spinner (azimuth_adjustment)
, add_speaker_button (_("Add Speaker"))
- , use_system_button (_("Use System"))
-
+ , remove_speaker_button (_("Remove Speaker"))
+ /* initialize to 0 so that set_selected works below */
+ , selected_index (0)
+ , ignore_speaker_position_change (false)
+ , ignore_azimuth_change (false)
{
-
side_vbox.set_homogeneous (false);
- side_vbox.set_border_width (9);
+ side_vbox.set_border_width (6);
side_vbox.set_spacing (6);
- side_vbox.pack_start (azimuth_spinner, false, false);
side_vbox.pack_start (add_speaker_button, false, false);
- side_vbox.pack_start (use_system_button, false, false);
aspect_frame.set_size_request (200, 200);
aspect_frame.set_shadow_type (SHADOW_NONE);
hbox.set_spacing (6);
hbox.set_border_width (6);
hbox.pack_start (aspect_frame, true, true);
- hbox.pack_start (side_vbox, false, false);
+ hbox.pack_start (side_vbox, true, true);
+
+ HBox* current_speaker_hbox = manage (new HBox);
+ current_speaker_hbox->set_spacing (4);
+ current_speaker_hbox->pack_start (*manage (new Label (_("Azimuth:"))), false, false);
+ current_speaker_hbox->pack_start (azimuth_spinner, true, true);
+ current_speaker_hbox->pack_start (remove_speaker_button, true, true);
get_vbox()->pack_start (hbox);
+ get_vbox()->pack_start (*current_speaker_hbox, true, true);
get_vbox()->show_all ();
darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
darea.signal_button_release_event().connect (sigc::mem_fun (*this, &SpeakerDialog::darea_button_release_event));
darea.signal_motion_notify_event().connect (sigc::mem_fun (*this, &SpeakerDialog::darea_motion_notify_event));
+ add_speaker_button.signal_clicked().connect (sigc::mem_fun (*this, &SpeakerDialog::add_speaker));
+ remove_speaker_button.signal_clicked().connect (sigc::mem_fun (*this, &SpeakerDialog::remove_speaker));
+ azimuth_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &SpeakerDialog::azimuth_changed));
+
drag_index = -1;
+
+ /* selected index initialised to 0 above; this will set `no selection' and
+ sensitize widgets accordingly.
+ */
+ set_selected (-1);
}
void
-SpeakerDialog::set_speakers (boost::shared_ptr<Speakers> s)
+SpeakerDialog::set_speakers (boost::shared_ptr<Speakers> s)
{
- speakers = *s;
+ _speakers = s;
}
-Speakers
+boost::shared_ptr<Speakers>
SpeakerDialog::get_speakers () const
{
- return speakers;
+ return _speakers.lock ();
}
bool
SpeakerDialog::darea_expose_event (GdkEventExpose* event)
{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return false;
+ }
+
gint x, y;
cairo_t* cr;
cairo_fill_preserve (cr);
cairo_clip (cr);
- if (height > 100) {
- cairo_translate (cr, 10.0, 10.0);
- }
+ cairo_translate (cr, x_origin, y_origin);
/* horizontal line of "crosshairs" */
cairo_stroke (cr);
/* vertical line of "crosshairs" */
-
+
cairo_move_to (cr, width/2+0.5, 0.5);
cairo_line_to (cr, width/2+0.5, height+0.5);
cairo_stroke (cr);
cairo_stroke (cr);
float arc_radius;
-
+
cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
-
+
if (height < 100) {
cairo_set_font_size (cr, 10);
arc_radius = 2.0;
arc_radius = 4.0;
}
- uint32_t n = 0;
- for (vector<Speaker>::iterator i = speakers.speakers().begin(); i != speakers.speakers().end(); ++i) {
-
+ int n = 0;
+ for (vector<Speaker>::iterator i = speakers->speakers().begin(); i != speakers->speakers().end(); ++i) {
+
Speaker& s (*i);
CartesianVector c (s.coords());
-
+
cart_to_gtk (c);
-
+
x = (gint) floor (c.x);
y = (gint) floor (c.y);
-
+
/* XXX need to shift circles so that they are centered on the circle */
-
+
cairo_arc (cr, x, y, arc_radius, 0, 2.0 * M_PI);
- cairo_set_source_rgb (cr, 0.8, 0.2, 0.1);
+ if (selected_index == n) {
+ cairo_set_source_rgb (cr, 0.8, 0.8, 0.2);
+ } else {
+ cairo_set_source_rgb (cr, 0.8, 0.2, 0.1);
+ }
cairo_close_path (cr);
cairo_fill (cr);
-
+
cairo_move_to (cr, x + 6, y + 6);
-
+
char buf[256];
snprintf (buf, sizeof (buf), "%d:%d", n+1, (int) lrint (s.angles().azi));
cairo_show_text (cr, buf);
cairo_destroy (cr);
return true;
-
+
}
void
SpeakerDialog::cart_to_gtk (CartesianVector& c) const
{
/* "c" uses a coordinate space that is:
-
+
center = 0.0
dimension = 2.0 * 2.0
so max values along each axis are -1..+1
width -= 20;
height -= 20;
}
+
+ x_origin = (alloc.get_width() - width) / 2;
+ y_origin = (alloc.get_height() - height) / 2;
}
bool
SpeakerDialog::darea_button_press_event (GdkEventButton *ev)
{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return false;
+ }
+
GdkModifierType state;
if (ev->type == GDK_2BUTTON_PRESS && ev->button == 1) {
switch (ev->button) {
case 1:
case 2:
- drag_index = find_closest_object (ev->x, ev->y);
- drag_x = (int) floor (ev->x);
- drag_y = (int) floor (ev->y);
+ {
+ int const index = find_closest_object (ev->x, ev->y);
+ set_selected (index);
+
+ drag_index = index;
+ int const drag_x = (int) floor (ev->x);
+ int const drag_y = (int) floor (ev->y);
state = (GdkModifierType) ev->state;
+ if (drag_index >= 0) {
+ CartesianVector c;
+ speakers->speakers()[drag_index].angles().cartesian (c);
+ cart_to_gtk (c);
+ drag_offset_x = drag_x - x_origin - c.x;
+ drag_offset_y = drag_y - y_origin - c.y;
+ }
+
return handle_motion (drag_x, drag_y, state);
break;
+ }
default:
break;
bool
SpeakerDialog::darea_button_release_event (GdkEventButton *ev)
{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return false;
+ }
+
gint x, y;
GdkModifierType state;
bool ret = false;
state = (GdkModifierType) ev->state;
if (Keyboard::modifier_state_contains (state, Keyboard::TertiaryModifier)) {
-
- for (vector<Speaker>::iterator i = speakers.speakers().begin(); i != speakers.speakers().end(); ++i) {
+
+ for (vector<Speaker>::iterator i = speakers->speakers().begin(); i != speakers->speakers().end(); ++i) {
/* XXX DO SOMETHING TO SET SPEAKER BACK TO "normal" */
}
break;
}
-
+
drag_index = -1;
return ret;
}
int
-SpeakerDialog::find_closest_object (gdouble x, gdouble y)
+SpeakerDialog::find_closest_object (gdouble x, gdouble y)
{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return -1;
+ }
+
float distance;
float best_distance = FLT_MAX;
int n = 0;
int which = -1;
- for (vector<Speaker>::iterator i = speakers.speakers().begin(); i != speakers.speakers().end(); ++i, ++n) {
+ for (vector<Speaker>::iterator i = speakers->speakers().begin(); i != speakers->speakers().end(); ++i, ++n) {
Speaker& candidate (*i);
CartesianVector c;
-
+
candidate.angles().cartesian (c);
cart_to_gtk (c);
}
}
- if (best_distance > 20) { // arbitrary
+ if (best_distance > 20) { // arbitrary
return -1;
}
bool
SpeakerDialog::handle_motion (gint evx, gint evy, GdkModifierType state)
{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return false;
+ }
+
if (drag_index < 0) {
return false;
}
return false;
}
+ /* correct event coordinates to have their origin at the corner of our graphic
+ rather than the corner of our allocation */
+
+ double obx = evx - x_origin;
+ double oby = evy - y_origin;
+
+ /* and compensate for any distance between the mouse pointer and the centre
+ of the object being dragged */
+
+ obx -= drag_offset_x;
+ oby -= drag_offset_y;
if (state & GDK_BUTTON1_MASK && !(state & GDK_BUTTON2_MASK)) {
CartesianVector c;
bool need_move = false;
- Speaker& moving (speakers.speakers()[drag_index]);
+ Speaker& moving (speakers->speakers()[drag_index]);
moving.angles().cartesian (c);
cart_to_gtk (c);
- if ((evx != c.x) || (evy != c.y)) {
+ if (obx != c.x || oby != c.y) {
need_move = true;
}
if (need_move) {
- CartesianVector cp (evx, evy, 0.0);
+ CartesianVector cp (obx, oby, 0.0);
/* canonicalize position */
/* position actual signal on circle */
clamp_to_circle (cp.x, cp.y);
-
+
/* generate an angular representation and set drag target (GUI) position */
AngularVector a;
queue_draw ();
}
- }
+ }
return true;
}
+
+void
+SpeakerDialog::add_speaker ()
+{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return;
+ }
+
+ speakers->add_speaker (PBD::AngularVector (0, 0, 0));
+ queue_draw ();
+}
+
+void
+SpeakerDialog::set_selected (int i)
+{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return;
+ }
+
+ if (i == selected_index) {
+ return;
+ }
+
+ selected_index = i;
+ queue_draw ();
+
+ selected_speaker_connection.disconnect ();
+
+ azimuth_spinner.set_sensitive (selected_index != -1);
+ remove_speaker_button.set_sensitive (selected_index != -1);
+
+ if (selected_index != -1) {
+ azimuth_adjustment.set_value (speakers->speakers()[selected_index].angles().azi);
+ speakers->speakers()[selected_index].PositionChanged.connect (
+ selected_speaker_connection, MISSING_INVALIDATOR,
+ boost::bind (&SpeakerDialog::speaker_position_changed, this),
+ gui_context ()
+ );
+ }
+}
+
+void
+SpeakerDialog::azimuth_changed ()
+{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return;
+ }
+
+ assert (selected_index != -1);
+
+ if (ignore_azimuth_change) {
+ return;
+ }
+
+ ignore_speaker_position_change = true;
+ speakers->move_speaker (speakers->speakers()[selected_index].id, PBD::AngularVector (azimuth_adjustment.get_value (), 0, 0));
+ ignore_speaker_position_change = false;
+
+ queue_draw ();
+}
+
+void
+SpeakerDialog::speaker_position_changed ()
+{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return;
+ }
+
+ assert (selected_index != -1);
+
+ if (ignore_speaker_position_change) {
+ return;
+ }
+
+ ignore_azimuth_change = true;
+ azimuth_adjustment.set_value (speakers->speakers()[selected_index].angles().azi);
+ ignore_azimuth_change = false;
+
+ queue_draw ();
+}
+
+void
+SpeakerDialog::remove_speaker ()
+{
+ boost::shared_ptr<Speakers> speakers = _speakers.lock ();
+ if (!speakers) {
+ return;
+ }
+
+ assert (selected_index != -1);
+
+ speakers->remove_speaker (speakers->speakers()[selected_index].id);
+ set_selected (-1);
+
+ queue_draw ();
+}