David Robillard
587dc283ea
I attempted to preserve the "don't draw unless different" by ditching rounding for more precise display_span, but that didn't work. An alternative solution would be to draw on adjustment change if there's text, since then we need to redraw regardless of slider position, but it seemed weird even just with respect to the slider, so I opted for this, which really definitely redraws when the adjustment changes, period. If this proves to be a performance issue we'll have to figure that out.
720 lines
19 KiB
C++
720 lines
19 KiB
C++
/*
|
|
Copyright (C) 2006 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.
|
|
|
|
$Id: fastmeter.h 570 2006-06-07 21:21:21Z sampo $
|
|
*/
|
|
|
|
|
|
#include <iostream>
|
|
#include <assert.h>
|
|
|
|
#include "pbd/stacktrace.h"
|
|
|
|
#include "gtkmm2ext/cairo_widget.h"
|
|
#include "gtkmm2ext/keyboard.h"
|
|
#include "gtkmm2ext/pixfader.h"
|
|
#include "gtkmm2ext/utils.h"
|
|
|
|
using namespace Gtkmm2ext;
|
|
using namespace Gtk;
|
|
using namespace std;
|
|
|
|
#define CORNER_RADIUS 2.5
|
|
#define CORNER_SIZE 2
|
|
#define CORNER_OFFSET 1
|
|
#define FADER_RESERVE 6
|
|
|
|
std::list<PixFader::FaderImage*> PixFader::_patterns;
|
|
|
|
PixFader::PixFader (Gtk::Adjustment& adj, int orientation, int fader_length, int fader_girth)
|
|
: _layout (0)
|
|
, _tweaks (Tweaks(0))
|
|
, _adjustment (adj)
|
|
, _text_width (0)
|
|
, _text_height (0)
|
|
, _span (fader_length)
|
|
, _girth (fader_girth)
|
|
, _min_span (fader_length)
|
|
, _min_girth (fader_girth)
|
|
, _orien (orientation)
|
|
, _pattern (0)
|
|
, _hovering (false)
|
|
, _dragging (false)
|
|
, _centered_text (true)
|
|
, _current_parent (0)
|
|
{
|
|
_default_value = _adjustment.get_value();
|
|
update_unity_position ();
|
|
|
|
add_events (
|
|
Gdk::BUTTON_PRESS_MASK
|
|
| Gdk::BUTTON_RELEASE_MASK
|
|
| Gdk::POINTER_MOTION_MASK
|
|
| Gdk::SCROLL_MASK
|
|
| Gdk::ENTER_NOTIFY_MASK
|
|
| Gdk::LEAVE_NOTIFY_MASK
|
|
);
|
|
|
|
_adjustment.signal_value_changed().connect (mem_fun (*this, &PixFader::adjustment_changed));
|
|
_adjustment.signal_changed().connect (mem_fun (*this, &PixFader::adjustment_changed));
|
|
|
|
if (_orien == VERT) {
|
|
CairoWidget::set_size_request(_girth, _span);
|
|
} else {
|
|
CairoWidget::set_size_request(_span, _girth);
|
|
}
|
|
}
|
|
|
|
PixFader::~PixFader ()
|
|
{
|
|
if (_parent_style_change) _parent_style_change.disconnect();
|
|
if (_layout) _layout.clear (); // drop reference to existing layout
|
|
}
|
|
|
|
cairo_pattern_t*
|
|
PixFader::find_pattern (double afr, double afg, double afb,
|
|
double abr, double abg, double abb,
|
|
int w, int h)
|
|
{
|
|
for (list<FaderImage*>::iterator f = _patterns.begin(); f != _patterns.end(); ++f) {
|
|
if ((*f)->matches (afr, afg, afb, abr, abg, abb, w, h)) {
|
|
return (*f)->pattern;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
PixFader::create_patterns ()
|
|
{
|
|
Gdk::Color c = get_style()->get_fg (get_state());
|
|
float fr, fg, fb;
|
|
float br, bg, bb;
|
|
|
|
fr = c.get_red_p ();
|
|
fg = c.get_green_p ();
|
|
fb = c.get_blue_p ();
|
|
|
|
c = get_style()->get_bg (get_state());
|
|
|
|
br = c.get_red_p ();
|
|
bg = c.get_green_p ();
|
|
bb = c.get_blue_p ();
|
|
|
|
cairo_surface_t* surface;
|
|
cairo_t* tc = 0;
|
|
|
|
if (get_width() <= 1 || get_height() <= 1) {
|
|
return;
|
|
}
|
|
|
|
if ((_pattern = find_pattern (fr, fg, fb, br, bg, bb, get_width(), get_height())) != 0) {
|
|
/* found it - use it */
|
|
return;
|
|
}
|
|
|
|
if (_orien == VERT) {
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, get_width(), get_height() * 2.0);
|
|
tc = cairo_create (surface);
|
|
|
|
/* paint background + border */
|
|
|
|
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, get_width(), 0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 0, br*0.4,bg*0.4,bb*0.4, 1.0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.25, br*0.6,bg*0.6,bb*0.6, 1.0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 1, br*0.8,bg*0.8,bb*0.8, 1.0);
|
|
cairo_set_source (tc, shade_pattern);
|
|
cairo_rectangle (tc, 0, 0, get_width(), get_height() * 2.0);
|
|
cairo_fill (tc);
|
|
|
|
cairo_pattern_destroy (shade_pattern);
|
|
|
|
/* paint lower shade */
|
|
|
|
shade_pattern = cairo_pattern_create_linear (0.0, 0.0, get_width() - 2 - CORNER_OFFSET , 0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 0, fr*0.8,fg*0.8,fb*0.8, 1.0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 1, fr*0.6,fg*0.6,fb*0.6, 1.0);
|
|
cairo_set_source (tc, shade_pattern);
|
|
Gtkmm2ext::rounded_top_half_rectangle (tc, CORNER_OFFSET, get_height() + CORNER_OFFSET,
|
|
get_width() - CORNER_SIZE, get_height(), CORNER_RADIUS);
|
|
cairo_fill (tc);
|
|
|
|
cairo_pattern_destroy (shade_pattern);
|
|
|
|
_pattern = cairo_pattern_create_for_surface (surface);
|
|
|
|
} else {
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, get_width() * 2.0, get_height());
|
|
tc = cairo_create (surface);
|
|
|
|
/* paint right shade (background section)*/
|
|
|
|
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height());
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 0, br*0.4,bg*0.4,bb*0.4, 1.0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.25, br*0.6,bg*0.6,bb*0.6, 1.0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 1, br*0.8,bg*0.8,bb*0.8, 1.0);
|
|
cairo_set_source (tc, shade_pattern);
|
|
cairo_rectangle (tc, 0, 0, get_width() * 2.0, get_height());
|
|
cairo_fill (tc);
|
|
|
|
/* paint left shade (active section/foreground) */
|
|
|
|
shade_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height());
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 0, fr*0.8,fg*0.8,fb*0.8, 1.0);
|
|
cairo_pattern_add_color_stop_rgba (shade_pattern, 1, fr*0.6,fg*0.6,fb*0.6, 1.0);
|
|
cairo_set_source (tc, shade_pattern);
|
|
Gtkmm2ext::rounded_right_half_rectangle (tc, CORNER_OFFSET, CORNER_OFFSET,
|
|
get_width() - CORNER_OFFSET, get_height() - CORNER_SIZE, CORNER_RADIUS);
|
|
cairo_fill (tc);
|
|
cairo_pattern_destroy (shade_pattern);
|
|
|
|
_pattern = cairo_pattern_create_for_surface (surface);
|
|
}
|
|
|
|
/* cache it for others to use */
|
|
|
|
_patterns.push_back (new FaderImage (_pattern, fr, fg, fb, br, bg, bb, get_width(), get_height()));
|
|
|
|
cairo_destroy (tc);
|
|
cairo_surface_destroy (surface);
|
|
}
|
|
|
|
void
|
|
PixFader::render (cairo_t *cr, cairo_rectangle_t* area)
|
|
{
|
|
if (!_pattern) {
|
|
create_patterns();
|
|
}
|
|
|
|
if (!_pattern) {
|
|
/* this isn't supposed to be happen, but some wackiness whereby
|
|
* the pixfader ends up with a 1xN or Nx1 size allocation
|
|
* leads to it. the basic wackiness needs fixing but we
|
|
* shouldn't crash. just fill in the expose area with
|
|
* our bg color.
|
|
*/
|
|
|
|
CairoWidget::set_source_rgb_a (cr, get_style()->get_bg (get_state()), 1);
|
|
cairo_rectangle (cr, area->x, area->y, area->width, area->height);
|
|
cairo_fill (cr);
|
|
return;
|
|
}
|
|
|
|
OnExpose();
|
|
int ds = display_span ();
|
|
const float w = get_width();
|
|
const float h = get_height();
|
|
|
|
CairoWidget::set_source_rgb_a (cr, get_parent_bg(), 1);
|
|
cairo_rectangle (cr, 0, 0, w, h);
|
|
cairo_fill(cr);
|
|
|
|
cairo_set_line_width (cr, 2);
|
|
cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
|
|
|
|
cairo_matrix_t matrix;
|
|
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET, w-CORNER_SIZE, h-CORNER_SIZE, CORNER_RADIUS);
|
|
// we use a 'trick' here: The stoke is off by .5px but filling the interior area
|
|
// after a stroke of 2px width results in an outline of 1px
|
|
cairo_stroke_preserve(cr);
|
|
|
|
if (_orien == VERT) {
|
|
|
|
if (ds > h - FADER_RESERVE - CORNER_OFFSET) {
|
|
ds = h - FADER_RESERVE - CORNER_OFFSET;
|
|
}
|
|
|
|
if (!CairoWidget::flat_buttons() ) {
|
|
cairo_set_source (cr, _pattern);
|
|
cairo_matrix_init_translate (&matrix, 0, (h - ds));
|
|
cairo_pattern_set_matrix (_pattern, &matrix);
|
|
} else {
|
|
CairoWidget::set_source_rgb_a (cr, get_style()->get_bg (get_state()), 1);
|
|
cairo_fill (cr);
|
|
CairoWidget::set_source_rgb_a (cr, get_style()->get_fg (get_state()), 1);
|
|
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, ds + CORNER_OFFSET,
|
|
w - CORNER_SIZE, h - ds - CORNER_SIZE, CORNER_RADIUS);
|
|
}
|
|
cairo_fill (cr);
|
|
|
|
} else {
|
|
|
|
if (ds < FADER_RESERVE) {
|
|
ds = FADER_RESERVE;
|
|
}
|
|
assert(ds <= w);
|
|
|
|
/*
|
|
* if ds == w, the pattern does not need to be translated
|
|
* if ds == 0 (or FADER_RESERVE), the pattern needs to be moved
|
|
* w to the left, which is -w in pattern space, and w in user space
|
|
* if ds == 10, then the pattern needs to be moved w - 10
|
|
* to the left, which is -(w-10) in pattern space, which
|
|
* is (w - 10) in user space
|
|
* thus: translation = (w - ds)
|
|
*/
|
|
|
|
if (!CairoWidget::flat_buttons() ) {
|
|
cairo_set_source (cr, _pattern);
|
|
cairo_matrix_init_translate (&matrix, w - ds, 0);
|
|
cairo_pattern_set_matrix (_pattern, &matrix);
|
|
} else {
|
|
CairoWidget::set_source_rgb_a (cr, get_style()->get_bg (get_state()), 1);
|
|
cairo_fill (cr);
|
|
CairoWidget::set_source_rgb_a (cr, get_style()->get_fg (get_state()), 1);
|
|
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET,
|
|
ds - CORNER_SIZE, h - CORNER_SIZE, CORNER_RADIUS);
|
|
}
|
|
cairo_fill (cr);
|
|
}
|
|
|
|
/* draw the unity-position line if it's not at either end*/
|
|
if (!(_tweaks & NoShowUnityLine) && _unity_loc > CORNER_RADIUS) {
|
|
cairo_set_line_width(cr, 1);
|
|
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
|
Gdk::Color c = get_style()->get_fg (Gtk::STATE_ACTIVE);
|
|
cairo_set_source_rgba (cr, c.get_red_p() * 1.5, c.get_green_p() * 1.5, c.get_blue_p() * 1.5, 0.85);
|
|
if (_orien == VERT) {
|
|
if (_unity_loc < h - CORNER_RADIUS) {
|
|
cairo_move_to (cr, 1.5, _unity_loc + CORNER_OFFSET + .5);
|
|
cairo_line_to (cr, _girth - 1.5, _unity_loc + CORNER_OFFSET + .5);
|
|
cairo_stroke (cr);
|
|
}
|
|
} else {
|
|
if (_unity_loc < w - CORNER_RADIUS) {
|
|
cairo_move_to (cr, _unity_loc - CORNER_OFFSET + .5, 1.5);
|
|
cairo_line_to (cr, _unity_loc - CORNER_OFFSET + .5, _girth - 1.5);
|
|
cairo_stroke (cr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_layout && !_text.empty() && _orien == HORIZ) {
|
|
cairo_save (cr);
|
|
if (_centered_text) {
|
|
/* center text */
|
|
cairo_move_to (cr, (w - _text_width)/2.0, h/2.0 - _text_height/2.0);
|
|
} else if (ds > .5 * w) {
|
|
cairo_move_to (cr, CORNER_OFFSET + 3, h/2.0 - _text_height/2.0);
|
|
cairo_set_operator(cr, CAIRO_OPERATOR_XOR);
|
|
} else {
|
|
cairo_move_to (cr, w - _text_width - CORNER_OFFSET - 3, h/2.0 - _text_height/2.0);
|
|
}
|
|
CairoWidget::set_source_rgb_a (cr, get_style()->get_text (get_state()), 1);
|
|
pango_cairo_show_layout (cr, _layout->gobj());
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
if (!get_sensitive()) {
|
|
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET, w-CORNER_SIZE, h-CORNER_SIZE, CORNER_RADIUS);
|
|
cairo_set_source_rgba (cr, 0.505, 0.517, 0.525, 0.4);
|
|
cairo_fill (cr);
|
|
} else if (_hovering) {
|
|
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET, w-CORNER_SIZE, h-CORNER_SIZE, CORNER_RADIUS);
|
|
cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.1);
|
|
cairo_fill (cr);
|
|
}
|
|
}
|
|
|
|
void
|
|
PixFader::on_size_request (GtkRequisition* req)
|
|
{
|
|
if (_orien == VERT) {
|
|
req->width = (_min_girth ? _min_girth : -1);
|
|
req->height = (_min_span ? _min_span : -1);
|
|
} else {
|
|
req->height = (_min_girth ? _min_girth : -1);
|
|
req->width = (_min_span ? _min_span : -1);
|
|
}
|
|
}
|
|
|
|
void
|
|
PixFader::on_size_allocate (Gtk::Allocation& alloc)
|
|
{
|
|
CairoWidget::on_size_allocate(alloc);
|
|
|
|
if (_orien == VERT) {
|
|
_girth = alloc.get_width ();
|
|
_span = alloc.get_height ();
|
|
} else {
|
|
_girth = alloc.get_height ();
|
|
_span = alloc.get_width ();
|
|
}
|
|
|
|
if (is_realized()) {
|
|
/* recreate patterns in case we've changed size */
|
|
create_patterns ();
|
|
}
|
|
|
|
update_unity_position ();
|
|
}
|
|
|
|
bool
|
|
PixFader::on_button_press_event (GdkEventButton* ev)
|
|
{
|
|
if (ev->type != GDK_BUTTON_PRESS) {
|
|
if (_dragging) {
|
|
remove_modal_grab();
|
|
_dragging = false;
|
|
gdk_pointer_ungrab (GDK_CURRENT_TIME);
|
|
StopGesture ();
|
|
}
|
|
return (_tweaks & NoButtonForward) ? true : false;
|
|
}
|
|
|
|
if (ev->button != 1 && ev->button != 2) {
|
|
return false;
|
|
}
|
|
|
|
add_modal_grab ();
|
|
StartGesture ();
|
|
_grab_loc = (_orien == VERT) ? ev->y : ev->x;
|
|
_grab_start = (_orien == VERT) ? ev->y : ev->x;
|
|
_grab_window = ev->window;
|
|
_dragging = true;
|
|
gdk_pointer_grab(ev->window,false,
|
|
GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
|
|
NULL,NULL,ev->time);
|
|
|
|
if (ev->button == 2) {
|
|
set_adjustment_from_event (ev);
|
|
}
|
|
|
|
return (_tweaks & NoButtonForward) ? true : false;
|
|
}
|
|
|
|
bool
|
|
PixFader::on_button_release_event (GdkEventButton* ev)
|
|
{
|
|
double ev_pos = (_orien == VERT) ? ev->y : ev->x;
|
|
|
|
switch (ev->button) {
|
|
case 1:
|
|
if (_dragging) {
|
|
remove_modal_grab();
|
|
_dragging = false;
|
|
gdk_pointer_ungrab (GDK_CURRENT_TIME);
|
|
StopGesture ();
|
|
|
|
if (!_hovering) {
|
|
if (!(_tweaks & NoVerticalScroll)) {
|
|
Keyboard::magic_widget_drop_focus();
|
|
}
|
|
queue_draw ();
|
|
}
|
|
|
|
if (ev_pos == _grab_start) {
|
|
/* no motion - just a click */
|
|
ev_pos = rint(ev_pos);
|
|
|
|
if (ev->state & Keyboard::TertiaryModifier) {
|
|
_adjustment.set_value (_default_value);
|
|
} else if (ev->state & Keyboard::GainFineScaleModifier) {
|
|
_adjustment.set_value (_adjustment.get_lower());
|
|
#if 0 // ignore clicks
|
|
} else if (ev_pos == slider_pos) {
|
|
; // click on current position, no move.
|
|
} else if ((_orien == VERT && ev_pos < slider_pos) || (_orien == HORIZ && ev_pos > slider_pos)) {
|
|
/* above the current display height, remember X Window coords */
|
|
_adjustment.set_value (_adjustment.get_value() + _adjustment.get_step_increment());
|
|
} else {
|
|
_adjustment.set_value (_adjustment.get_value() - _adjustment.get_step_increment());
|
|
#endif
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (_dragging) {
|
|
remove_modal_grab();
|
|
_dragging = false;
|
|
StopGesture ();
|
|
set_adjustment_from_event (ev);
|
|
gdk_pointer_ungrab (GDK_CURRENT_TIME);
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
PixFader::on_scroll_event (GdkEventScroll* ev)
|
|
{
|
|
double scale;
|
|
bool ret = false;
|
|
|
|
if (ev->state & Keyboard::GainFineScaleModifier) {
|
|
if (ev->state & Keyboard::GainExtraFineScaleModifier) {
|
|
scale = 0.005;
|
|
} else {
|
|
scale = 0.1;
|
|
}
|
|
} else {
|
|
scale = 1.0;
|
|
}
|
|
|
|
if (_orien == VERT) {
|
|
switch (ev->direction) {
|
|
case GDK_SCROLL_UP:
|
|
_adjustment.set_value (_adjustment.get_value() + (_adjustment.get_page_increment() * scale));
|
|
ret = true;
|
|
break;
|
|
case GDK_SCROLL_DOWN:
|
|
_adjustment.set_value (_adjustment.get_value() - (_adjustment.get_page_increment() * scale));
|
|
ret = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
int dir = ev->direction;
|
|
|
|
if (ev->state & Keyboard::ScrollHorizontalModifier || !(_tweaks & NoVerticalScroll)) {
|
|
if (ev->direction == GDK_SCROLL_UP) dir = GDK_SCROLL_RIGHT;
|
|
if (ev->direction == GDK_SCROLL_DOWN) dir = GDK_SCROLL_LEFT;
|
|
}
|
|
|
|
switch (dir) {
|
|
case GDK_SCROLL_RIGHT:
|
|
_adjustment.set_value (_adjustment.get_value() + (_adjustment.get_page_increment() * scale));
|
|
ret = true;
|
|
break;
|
|
case GDK_SCROLL_LEFT:
|
|
_adjustment.set_value (_adjustment.get_value() - (_adjustment.get_page_increment() * scale));
|
|
ret = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
PixFader::on_motion_notify_event (GdkEventMotion* ev)
|
|
{
|
|
if (_dragging) {
|
|
double scale = 1.0;
|
|
double const ev_pos = (_orien == VERT) ? ev->y : ev->x;
|
|
|
|
if (ev->window != _grab_window) {
|
|
_grab_loc = ev_pos;
|
|
_grab_window = ev->window;
|
|
return true;
|
|
}
|
|
|
|
if (ev->state & Keyboard::GainFineScaleModifier) {
|
|
if (ev->state & Keyboard::GainExtraFineScaleModifier) {
|
|
scale = 0.005;
|
|
} else {
|
|
scale = 0.1;
|
|
}
|
|
}
|
|
|
|
double const delta = ev_pos - _grab_loc;
|
|
_grab_loc = ev_pos;
|
|
|
|
const double off = FADER_RESERVE + ((_orien == VERT) ? CORNER_OFFSET : 0);
|
|
const double span = _span - off;
|
|
double fract = (delta / span);
|
|
|
|
fract = min (1.0, fract);
|
|
fract = max (-1.0, fract);
|
|
|
|
// X Window is top->bottom for 0..Y
|
|
|
|
if (_orien == VERT) {
|
|
fract = -fract;
|
|
}
|
|
|
|
_adjustment.set_value (_adjustment.get_value() + scale * fract * (_adjustment.get_upper() - _adjustment.get_lower()));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PixFader::adjustment_changed ()
|
|
{
|
|
queue_draw ();
|
|
}
|
|
|
|
/** @return pixel offset of the current value from the right or bottom of the fader */
|
|
int
|
|
PixFader::display_span ()
|
|
{
|
|
float fract = (_adjustment.get_value () - _adjustment.get_lower()) / ((_adjustment.get_upper() - _adjustment.get_lower()));
|
|
int ds;
|
|
if (_orien == VERT) {
|
|
const double off = FADER_RESERVE + CORNER_OFFSET;
|
|
const double span = _span - off;
|
|
ds = (int)rint (span * (1.0 - fract));
|
|
} else {
|
|
const double off = FADER_RESERVE;
|
|
const double span = _span - off;
|
|
ds = (int)rint (span * fract + off);
|
|
}
|
|
|
|
return ds;
|
|
}
|
|
|
|
void
|
|
PixFader::update_unity_position ()
|
|
{
|
|
if (_orien == VERT) {
|
|
const double span = _span - FADER_RESERVE - CORNER_OFFSET;
|
|
_unity_loc = (int) rint (span * (1 - ((_default_value - _adjustment.get_lower()) / (_adjustment.get_upper() - _adjustment.get_lower())))) - 1;
|
|
} else {
|
|
const double span = _span - FADER_RESERVE;
|
|
_unity_loc = (int) rint (FADER_RESERVE + (_default_value - _adjustment.get_lower()) * span / (_adjustment.get_upper() - _adjustment.get_lower()));
|
|
}
|
|
|
|
queue_draw ();
|
|
}
|
|
|
|
bool
|
|
PixFader::on_enter_notify_event (GdkEventCrossing*)
|
|
{
|
|
_hovering = true;
|
|
if (!(_tweaks & NoVerticalScroll)) {
|
|
Keyboard::magic_widget_grab_focus ();
|
|
}
|
|
queue_draw ();
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
PixFader::on_leave_notify_event (GdkEventCrossing*)
|
|
{
|
|
if (!_dragging) {
|
|
_hovering = false;
|
|
if (!(_tweaks & NoVerticalScroll)) {
|
|
Keyboard::magic_widget_drop_focus();
|
|
}
|
|
queue_draw ();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
PixFader::set_adjustment_from_event (GdkEventButton* ev)
|
|
{
|
|
const double off = FADER_RESERVE + ((_orien == VERT) ? CORNER_OFFSET : 0);
|
|
const double span = _span - off;
|
|
double fract = (_orien == VERT) ? (1.0 - ((ev->y - off) / span)) : ((ev->x - off) / span);
|
|
|
|
fract = min (1.0, fract);
|
|
fract = max (0.0, fract);
|
|
|
|
_adjustment.set_value (fract * (_adjustment.get_upper () - _adjustment.get_lower ()));
|
|
}
|
|
|
|
void
|
|
PixFader::set_default_value (float d)
|
|
{
|
|
_default_value = d;
|
|
update_unity_position ();
|
|
}
|
|
|
|
void
|
|
PixFader::set_tweaks (Tweaks t)
|
|
{
|
|
bool need_redraw = false;
|
|
if ((_tweaks & NoShowUnityLine) ^ (t & NoShowUnityLine)) {
|
|
need_redraw = true;
|
|
}
|
|
_tweaks = t;
|
|
if (need_redraw) {
|
|
queue_draw();
|
|
}
|
|
}
|
|
|
|
void
|
|
PixFader::set_text (const std::string& str, bool centered, bool expose)
|
|
{
|
|
if (_layout && _text == str) {
|
|
return;
|
|
}
|
|
if (!_layout && !str.empty()) {
|
|
_layout = Pango::Layout::create (get_pango_context());
|
|
}
|
|
|
|
_text = str;
|
|
_centered_text = centered;
|
|
if (_layout) {
|
|
_layout->set_text (str);
|
|
_layout->get_pixel_size (_text_width, _text_height);
|
|
// queue_resize ();
|
|
if (expose) queue_draw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
PixFader::on_state_changed (Gtk::StateType old_state)
|
|
{
|
|
Widget::on_state_changed (old_state);
|
|
create_patterns ();
|
|
queue_draw ();
|
|
}
|
|
|
|
void
|
|
PixFader::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
|
|
{
|
|
if (_layout) {
|
|
std::string txt = _layout->get_text();
|
|
_layout.clear (); // drop reference to existing layout
|
|
_text = "";
|
|
set_text (txt, _centered_text, false);
|
|
}
|
|
/* patterns are cached and re-created as needed
|
|
* during 'expose' in the GUI thread */
|
|
_pattern = 0;
|
|
queue_draw ();
|
|
}
|
|
|
|
Gdk::Color
|
|
PixFader::get_parent_bg ()
|
|
{
|
|
Widget* parent = get_parent ();
|
|
|
|
while (parent) {
|
|
if (parent->get_has_window()) {
|
|
break;
|
|
}
|
|
parent = parent->get_parent();
|
|
}
|
|
|
|
if (parent && parent->get_has_window()) {
|
|
if (_current_parent != parent) {
|
|
if (_parent_style_change) _parent_style_change.disconnect();
|
|
_current_parent = parent;
|
|
_parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &PixFader::on_style_changed));
|
|
}
|
|
return parent->get_style ()->get_bg (parent->get_state());
|
|
}
|
|
|
|
return get_style ()->get_bg (get_state());
|
|
}
|