Move Gtkmm2ext widgets into libwidget

This commit is contained in:
Robin Gareus 2017-07-16 22:13:46 +02:00
parent b6e4dfe37b
commit f6e182b937
48 changed files with 218 additions and 252 deletions

View file

@ -0,0 +1,747 @@
/*
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/utils.h"
#include "widgets/ardour_fader.h"
using namespace Gtk;
using namespace std;
using namespace Gtkmm2ext;
using namespace ArdourWidgets;
#define CORNER_RADIUS 2.5
#define CORNER_SIZE 2
#define CORNER_OFFSET 1
#define FADER_RESERVE 6
std::list<ArdourFader::FaderImage*> ArdourFader::_patterns;
ArdourFader::ArdourFader (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, &ArdourFader::adjustment_changed));
_adjustment.signal_changed().connect (mem_fun (*this, &ArdourFader::adjustment_changed));
signal_grab_broken_event ().connect (mem_fun (*this, &ArdourFader::on_grab_broken_event));
if (_orien == VERT) {
CairoWidget::set_size_request(_girth, _span);
} else {
CairoWidget::set_size_request(_span, _girth);
}
}
ArdourFader::~ArdourFader ()
{
if (_parent_style_change) _parent_style_change.disconnect();
if (_layout) _layout.clear (); // drop reference to existing layout
}
void
ArdourFader::flush_pattern_cache () {
for (list<FaderImage*>::iterator f = _patterns.begin(); f != _patterns.end(); ++f) {
cairo_pattern_destroy ((*f)->pattern);
}
_patterns.clear();
}
cairo_pattern_t*
ArdourFader::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
ArdourFader::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
ArdourFader::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t* area)
{
cairo_t* cr = ctx->cobj();
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 && CairoWidget::widget_prelight()) {
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
ArdourFader::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
ArdourFader::on_size_allocate (Gtk::Allocation& alloc)
{
int old_girth = _girth;
int old_span = _span;
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() && ((old_girth != _girth) || (old_span != _span))) {
/* recreate patterns in case we've changed size */
create_patterns ();
}
update_unity_position ();
}
bool
ArdourFader::on_grab_broken_event (GdkEventGrabBroken* ev)
{
if (_dragging) {
remove_modal_grab();
_dragging = false;
gdk_pointer_ungrab (GDK_CURRENT_TIME);
StopGesture ();
}
return (_tweaks & NoButtonForward) ? true : false;
}
bool
ArdourFader::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
ArdourFader::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
ArdourFader::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
ArdourFader::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
ArdourFader::adjustment_changed ()
{
queue_draw ();
}
/** @return pixel offset of the current value from the right or bottom of the fader */
int
ArdourFader::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
ArdourFader::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
ArdourFader::on_enter_notify_event (GdkEventCrossing*)
{
_hovering = true;
if (!(_tweaks & NoVerticalScroll)) {
Keyboard::magic_widget_grab_focus ();
}
queue_draw ();
return false;
}
bool
ArdourFader::on_leave_notify_event (GdkEventCrossing*)
{
if (!_dragging) {
_hovering = false;
if (!(_tweaks & NoVerticalScroll)) {
Keyboard::magic_widget_drop_focus();
}
queue_draw ();
}
return false;
}
void
ArdourFader::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
ArdourFader::set_default_value (float d)
{
_default_value = d;
update_unity_position ();
}
void
ArdourFader::set_tweaks (Tweaks t)
{
bool need_redraw = false;
if ((_tweaks & NoShowUnityLine) ^ (t & NoShowUnityLine)) {
need_redraw = true;
}
_tweaks = t;
if (need_redraw) {
queue_draw();
}
}
void
ArdourFader::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
ArdourFader::on_state_changed (Gtk::StateType old_state)
{
Widget::on_state_changed (old_state);
create_patterns ();
queue_draw ();
}
void
ArdourFader::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
ArdourFader::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, &ArdourFader::on_style_changed));
}
return parent->get_style ()->get_bg (parent->get_state());
}
return get_style ()->get_bg (get_state());
}

298
libs/widgets/auto_spin.cc Normal file
View file

@ -0,0 +1,298 @@
/*
Copyright (C) 1999 Paul Barton-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$
*/
#include <cmath>
#include "gtkmm2ext/keyboard.h"
#include "widgets/auto_spin.h"
using namespace Gtkmm2ext;
using namespace ArdourWidgets;
using namespace std;
#define upper adjustment.get_upper()
#define lower adjustment.get_lower()
#define step_increment adjustment.get_step_increment()
#define page_increment adjustment.get_page_increment()
const unsigned int AutoSpin::initial_timer_interval = 500; /* msecs */
const unsigned int AutoSpin::timer_interval = 20; /* msecs */
const unsigned int AutoSpin::climb_timer_calls = 5; /* between climbing */
AutoSpin::AutoSpin (Gtk::Adjustment &adjr, gfloat cr, bool round_to_steps_yn)
: adjustment (adjr),
climb_rate (cr)
{
initial = adjustment.get_value ();
left_is_decrement = true;
wrap = false;
have_timer = false;
need_timer = false;
timer_calls = 0;
round_to_steps = round_to_steps_yn;
}
void
AutoSpin::stop_timer ()
{
if (have_timer) {
g_source_remove (timeout_tag);
have_timer = false;
}
}
gint
AutoSpin::stop_spinning (GdkEventButton */*ev*/)
{
need_timer = false;
stop_timer ();
return FALSE;
}
gint
AutoSpin::button_press (GdkEventButton *ev)
{
bool shifted = false;
bool control = false;
bool with_decrement = false;
stop_spinning (0);
if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
return true;
}
if (ev->state & Keyboard::TertiaryModifier) {
/* use page shift */
shifted = true;
}
if (ev->state & Keyboard::PrimaryModifier) {
/* go to upper/lower bound on button1/button2 */
control = true;
}
/* XXX should figure out which button is left/right */
switch (ev->button) {
case 1:
if (control) {
set_value (left_is_decrement ? lower : upper);
return TRUE;
} else {
if (left_is_decrement) {
with_decrement = true;
} else {
with_decrement = false;
}
}
break;
case 2:
if (!control) {
set_value (initial);
}
return TRUE;
break;
case 3:
if (control) {
set_value (left_is_decrement ? upper : lower);
return TRUE;
}
break;
case 4:
if (!control) {
adjust_value (shifted ? page_increment : step_increment);
} else {
set_value (upper);
}
return TRUE;
break;
case 5:
if (!control) {
adjust_value (shifted ? -page_increment : -step_increment);
} else {
set_value (lower);
}
return TRUE;
break;
}
start_spinning (with_decrement, shifted);
return TRUE;
}
gint
AutoSpin::scroll_event (GdkEventScroll *ev)
{
stop_spinning (0);
gfloat increment = step_increment;
if (ev->state & Keyboard::TertiaryModifier) {
increment = page_increment;
}
switch (ev->direction) {
case GDK_SCROLL_DOWN:
case GDK_SCROLL_LEFT:
adjust_value (-increment);
break;
case GDK_SCROLL_RIGHT:
case GDK_SCROLL_UP:
adjust_value (increment);
break;
}
return TRUE;
}
void
AutoSpin::start_spinning (bool decrement, bool page)
{
timer_increment = page ? page_increment : step_increment;
if (decrement) {
timer_increment = -timer_increment;
}
adjust_value (timer_increment);
have_timer = true;
timer_calls = 0;
timeout_tag = g_timeout_add (initial_timer_interval,
AutoSpin::_timer,
this);
}
gint
AutoSpin::_timer (void *arg)
{
return ((AutoSpin *) arg)->timer ();
}
void
AutoSpin::set_value (gfloat value)
{
if (round_to_steps)
adjustment.set_value (floor((value / step_increment) + 0.5f) * step_increment);
else
adjustment.set_value (value);
}
bool
AutoSpin::adjust_value (gfloat increment)
{
gfloat val;
bool done = false;
val = adjustment.get_value ();
val += increment;
if (val > upper) {
if (wrap) {
val = lower;
} else {
val = upper;
done = true;
}
} else if (val < lower) {
if (wrap) {
val = upper;
} else {
val = lower;
done = true;
}
}
set_value (val);
return done;
}
gint
AutoSpin::timer ()
{
bool done;
int retval = FALSE;
done = adjust_value (timer_increment);
if (need_timer) {
/* we're in the initial call, which happened
after initial_timer_interval msecs. Now
request a much more frequent update.
*/
timeout_tag = g_timeout_add (timer_interval,
_timer,
this);
have_timer = true;
need_timer = false;
/* cancel this initial timeout */
retval = FALSE;
} else {
/* this is the regular "fast" call after each
timer_interval msecs.
*/
if (timer_calls < climb_timer_calls) {
timer_calls++;
} else {
if (climb_rate > 0.0) {
if (timer_increment > 0) {
timer_increment += climb_rate;
} else {
timer_increment -= climb_rate;
}
}
timer_calls = 0;
}
if (!done) {
retval = TRUE;
}
}
return retval;
}
void
AutoSpin::set_bounds (gfloat init, gfloat up, gfloat down, bool with_reset)
{
adjustment.set_upper (up);
adjustment.set_lower (down);
initial = init;
adjustment.changed ();
if (with_reset) {
adjustment.set_value (init);
}
}

View file

@ -0,0 +1,169 @@
/*
Copyright (C) 2004 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 <string>
#include <sstream>
#include <climits>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <pbd/controllable.h>
#include "gtkmm2ext/gtk_ui.h"
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/keyboard.h"
#include "gtkmm2ext/cairo_widget.h"
#include "widgets/barcontroller.h"
#include "pbd/i18n.h"
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace ArdourWidgets;
BarController::BarController (Gtk::Adjustment& adj,
boost::shared_ptr<PBD::Controllable> mc)
: _slider (&adj, mc, 60, 16)
, _switching (false)
, _switch_on_release (false)
{
add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
set (.5, .5, 1.0, 1.0);
set_border_width (0);
_slider.set_tweaks (ArdourFader::NoShowUnityLine);
_slider.StartGesture.connect (sigc::mem_fun(*this, &BarController::passtrhu_gesture_start));
_slider.StopGesture.connect (sigc::mem_fun(*this, &BarController::passtrhu_gesture_stop));
_slider.OnExpose.connect (sigc::mem_fun(*this, &BarController::before_expose));
_slider.set_name (get_name());
Gtk::SpinButton& spinner = _slider.get_spin_button();
spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
spinner.set_digits (9);
spinner.set_numeric (true);
spinner.set_name ("BarControlSpinner");
add (_slider);
show_all ();
}
BarController::~BarController ()
{
}
bool
BarController::on_button_press_event (GdkEventButton* ev)
{
if (get_child() != &_slider) {
return false;
}
if (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS) {
_switch_on_release = true;
return true;
} else {
_switch_on_release = false;
}
return false;
}
bool
BarController::on_button_release_event (GdkEventButton* ev)
{
if (get_child() != &_slider) {
return false;
}
if (ev->button == 1 && _switch_on_release) {
Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
return true;
}
return false;
}
void
BarController::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
{
_slider.set_name (get_name());
}
gint
BarController::switch_to_bar ()
{
if (_switching || get_child() == &_slider) {
return FALSE;
}
_switching = true;
remove ();
add (_slider);
_slider.show ();
_slider.queue_draw ();
_switching = false;
SpinnerActive (false); /* EMIT SIGNAL */
return FALSE;
}
gint
BarController::switch_to_spinner ()
{
if (_switching || get_child() != &_slider) {
return FALSE;
}
_switching = true;
Gtk::SpinButton& spinner = _slider.get_spin_button();
if (spinner.get_parent()) {
spinner.get_parent()->remove(spinner);
}
remove ();
add (spinner);
spinner.show ();
spinner.select_region (0, spinner.get_text_length());
spinner.grab_focus ();
_switching = false;
SpinnerActive (true); /* EMIT SIGNAL */
return FALSE;
}
void
BarController::entry_activated ()
{
switch_to_bar ();
}
bool
BarController::entry_focus_out (GdkEventFocus* /*ev*/)
{
entry_activated ();
return true;
}
void
BarController::before_expose ()
{
double xpos = -1;
_slider.set_text (get_label (xpos), false, false);
}
void
BarController::set_sensitive (bool yn)
{
Alignment::set_sensitive (yn);
_slider.set_sensitive (yn);
}

188
libs/widgets/click_box.cc Normal file
View file

@ -0,0 +1,188 @@
/*
Copyright (C) 1999 Paul Barton-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$
*/
#include <iostream>
#include <cstdio> /* for sprintf, sigh ... */
#include "pbd/controllable.h"
#include "gtkmm2ext/utils.h"
#include "widgets/click_box.h"
using namespace std;
using namespace Gtk;
using namespace ArdourWidgets;
using namespace sigc;
ClickBox::ClickBox (Gtk::Adjustment *adjp, const string &name, bool round_to_steps)
: AutoSpin (*adjp,0,round_to_steps)
{
layout = create_pango_layout ("");
twidth = 0;
theight = 0;
add_events (Gdk::BUTTON_RELEASE_MASK|
Gdk::BUTTON_PRESS_MASK|
Gdk::ENTER_NOTIFY_MASK|
Gdk::LEAVE_NOTIFY_MASK);
get_adjustment().signal_value_changed().connect (mem_fun (*this, &ClickBox::set_label));
signal_style_changed().connect (mem_fun (*this, &ClickBox::style_changed));
signal_button_press_event().connect (mem_fun (*this, &ClickBox::button_press_handler));
signal_button_release_event().connect (mem_fun (*this, &ClickBox::button_release_handler));
set_name (name);
set_label ();
}
ClickBox::~ClickBox ()
{
}
bool
ClickBox::button_press_handler (GdkEventButton* ev)
{
if (_binding_proxy.button_press_handler (ev)) {
return true;
}
add_modal_grab();
AutoSpin::button_press (ev);
return true;
}
bool
ClickBox::on_scroll_event (GdkEventScroll* ev)
{
AutoSpin::scroll_event (ev);
return true;
}
bool
ClickBox::button_release_handler (GdkEventButton* ev)
{
switch (ev->button) {
case 1:
case 2:
case 3:
stop_spinning (0);
default:
remove_modal_grab();
break;
}
return true;
}
void
ClickBox::set_label ()
{
char buf[32];
int width, height;
bool const h = _printer (buf, get_adjustment());
if (!h) {
/* the printer didn't handle it, so use a default */
sprintf (buf, "%.2f", get_adjustment().get_value ());
}
layout->set_text (buf);
layout->get_pixel_size (width, height);
if (twidth < width && (width > 50)) {
/* override GenericPluginUI::build_control_ui()
* Gtkmm2ext::set_size_request_to_display_given_text ("g9999999")
* see http://tracker.ardour.org/view.php?id=6499
*/
set_size_request (std::min (300, width + 6), height + 4);
}
twidth = width; theight = height;
queue_draw ();
}
void
ClickBox::style_changed (const Glib::RefPtr<Gtk::Style>&)
{
layout->context_changed ();
layout->get_pixel_size (twidth, theight);
}
bool
ClickBox::on_expose_event (GdkEventExpose *ev)
{
/* Why do we do things like this rather than use a Gtk::Label?
Because whenever Gtk::Label::set_label() is called, it
triggers a recomputation of its own size, along with that
of its container and on up the tree. That's intended
to be unnecessary here.
*/
Gtk::DrawingArea::on_expose_event (ev);
Glib::RefPtr<Gtk::Style> style (get_style());
Glib::RefPtr<Gdk::GC> fg_gc (style->get_fg_gc (Gtk::STATE_NORMAL));
Glib::RefPtr<Gdk::GC> bg_gc (style->get_bg_gc (Gtk::STATE_NORMAL));
Glib::RefPtr<Gdk::Window> win (get_window());
GdkRectangle base_rect;
GdkRectangle draw_rect;
gint x, y, width, height, depth;
win->get_geometry (x, y, width, height, depth);
base_rect.width = width;
base_rect.height = height;
base_rect.x = 0;
base_rect.y = 0;
gdk_rectangle_intersect (&ev->area, &base_rect, &draw_rect);
win->draw_rectangle (bg_gc, true, draw_rect.x, draw_rect.y, draw_rect.width, draw_rect.height);
if (twidth && theight) {
win->draw_layout (fg_gc, (width - twidth) / 2, (height - theight) / 2, layout);
}
return true;
}
void
ClickBox::set_printer (sigc::slot<bool, char *, Gtk::Adjustment &> p)
{
_printer = p;
set_label ();
}
bool
ClickBox::on_enter_notify_event (GdkEventCrossing* ev)
{
boost::shared_ptr<PBD::Controllable> c (_binding_proxy.get_controllable ());
if (c) {
PBD::Controllable::GUIFocusChanged (boost::weak_ptr<PBD::Controllable> (c));
}
return false;
}
bool
ClickBox::on_leave_notify_event (GdkEventCrossing* ev)
{
if (_binding_proxy.get_controllable()) {
PBD::Controllable::GUIFocusChanged (boost::weak_ptr<PBD::Controllable> ());
}
return false;
}

919
libs/widgets/fastmeter.cc Normal file
View file

@ -0,0 +1,919 @@
/*
Copyright (C) 2003-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.
*/
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stdlib.h>
#include <glibmm.h>
#include <gdkmm.h>
#include "gtkmm2ext/utils.h"
#include "widgets/fastmeter.h"
#define UINT_TO_RGB(u,r,g,b) { (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
#define UINT_TO_RGBA(u,r,g,b,a) { UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
using namespace Gtk;
using namespace Glib;
using namespace Gtkmm2ext;
using namespace ArdourWidgets;
using namespace std;
int FastMeter::min_pattern_metric_size = 16;
int FastMeter::max_pattern_metric_size = 1024;
bool FastMeter::no_rgba_overlay = false;
FastMeter::Pattern10Map FastMeter::vm_pattern_cache;
FastMeter::PatternBgMap FastMeter::vb_pattern_cache;
FastMeter::Pattern10Map FastMeter::hm_pattern_cache;
FastMeter::PatternBgMap FastMeter::hb_pattern_cache;
FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len,
int clr0, int clr1, int clr2, int clr3,
int clr4, int clr5, int clr6, int clr7,
int clr8, int clr9,
int bgc0, int bgc1,
int bgh0, int bgh1,
float stp0, float stp1,
float stp2, float stp3,
int styleflags
)
: pixheight(0)
, pixwidth(0)
, _styleflags(styleflags)
, orientation(o)
, hold_cnt(hold)
, hold_state(0)
, bright_hold(false)
, current_level(0)
, current_peak(0)
, highlight(false)
{
last_peak_rect.width = 0;
last_peak_rect.height = 0;
last_peak_rect.x = 0;
last_peak_rect.y = 0;
no_rgba_overlay = ! Glib::getenv("NO_METER_SHADE").empty();
_clr[0] = clr0;
_clr[1] = clr1;
_clr[2] = clr2;
_clr[3] = clr3;
_clr[4] = clr4;
_clr[5] = clr5;
_clr[6] = clr6;
_clr[7] = clr7;
_clr[8] = clr8;
_clr[9] = clr9;
_bgc[0] = bgc0;
_bgc[1] = bgc1;
_bgh[0] = bgh0;
_bgh[1] = bgh1;
_stp[0] = stp0;
_stp[1] = stp1;
_stp[2] = stp2;
_stp[3] = stp3;
set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
pixrect.x = 1;
pixrect.y = 1;
if (!len) {
len = 250;
}
if (orientation == Vertical) {
pixheight = len;
pixwidth = dimen;
fgpattern = request_vertical_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, _bgc, false);
} else {
pixheight = dimen;
pixwidth = len;
fgpattern = request_horizontal_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, _bgc, false);
}
pixrect.width = pixwidth;
pixrect.height = pixheight;
request_width = pixrect.width + 2;
request_height= pixrect.height + 2;
clear ();
}
FastMeter::~FastMeter ()
{
}
void
FastMeter::flush_pattern_cache () {
hb_pattern_cache.clear();
hm_pattern_cache.clear();
vb_pattern_cache.clear();
vm_pattern_cache.clear();
}
Cairo::RefPtr<Cairo::Pattern>
FastMeter::generate_meter_pattern (
int width, int height, int *clr, float *stp, int styleflags, bool horiz)
{
guint8 r,g,b,a;
double knee;
const double soft = 3.0 / (double) height;
const double offs = -1.0 / (double) height;
cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
/*
Cairo coordinate space goes downwards as y value goes up, so invert
knee-based positions by using (1.0 - y)
*/
UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
cairo_pattern_add_color_stop_rgb (pat, 0.0,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[3] / 115.0f; // -0dB
UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[2]/ 115.0f; // -3dB || -2dB
UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[1] / 115.0f; // -9dB
UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[0] / 115.0f; // -18dB
UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
cairo_pattern_add_color_stop_rgb (pat, 1.0,
r/255.0, g/255.0, b/255.0);
if ((styleflags & 1) && !no_rgba_overlay) {
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 0.0, 0.0, 0.0, 0.15);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.4, 1.0, 1.0, 1.0, 0.05);
cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0.0, 0.0, 0.0, 0.25);
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
tc = cairo_create (surface);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_pattern_destroy (pat);
cairo_set_source (tc, shade_pattern);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_pattern_destroy (shade_pattern);
if (styleflags & 2) { // LED stripes
cairo_save (tc);
cairo_set_line_width(tc, 1.0);
cairo_set_source_rgba(tc, .0, .0, .0, 0.4);
//cairo_set_operator (tc, CAIRO_OPERATOR_SOURCE);
for (int i = 0; float y = 0.5 + i * 2.0; ++i) {
if (y >= height) {
break;
}
cairo_move_to(tc, 0, y);
cairo_line_to(tc, width, y);
cairo_stroke (tc);
}
cairo_restore (tc);
}
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
if (horiz) {
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
tc = cairo_create (surface);
cairo_matrix_t m;
cairo_matrix_init_rotate (&m, -M_PI/2.0);
cairo_matrix_translate (&m, -height, 0);
cairo_pattern_set_matrix (pat, &m);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, height, width);
cairo_fill (tc);
cairo_pattern_destroy (pat);
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
return p;
}
Cairo::RefPtr<Cairo::Pattern>
FastMeter::generate_meter_background (
int width, int height, int *clr, bool shade, bool horiz)
{
guint8 r0,g0,b0,r1,g1,b1,a;
cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
cairo_pattern_add_color_stop_rgb (pat, 0.0,
r1/255.0, g1/255.0, b1/255.0);
cairo_pattern_add_color_stop_rgb (pat, 1.0,
r0/255.0, g0/255.0, b0/255.0);
if (shade && !no_rgba_overlay) {
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.0, 1.0, 1.0, 1.0, 0.15);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.6, 0.0, 0.0, 0.0, 0.10);
cairo_pattern_add_color_stop_rgba (shade_pattern, 1.0, 1.0, 1.0, 1.0, 0.20);
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
tc = cairo_create (surface);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_set_source (tc, shade_pattern);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_pattern_destroy (pat);
cairo_pattern_destroy (shade_pattern);
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
if (horiz) {
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
tc = cairo_create (surface);
cairo_matrix_t m;
cairo_matrix_init_rotate (&m, -M_PI/2.0);
cairo_matrix_translate (&m, -height, 0);
cairo_pattern_set_matrix (pat, &m);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, height, width);
cairo_fill (tc);
cairo_pattern_destroy (pat);
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
return p;
}
Cairo::RefPtr<Cairo::Pattern>
FastMeter::request_vertical_meter(
int width, int height, int *clr, float *stp, int styleflags)
{
height = max(height, min_pattern_metric_size);
height = min(height, max_pattern_metric_size);
const Pattern10MapKey key (width, height,
stp[0], stp[1], stp[2], stp[3],
clr[0], clr[1], clr[2], clr[3],
clr[4], clr[5], clr[6], clr[7],
clr[8], clr[9], styleflags);
Pattern10Map::iterator i;
if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
width, height, clr, stp, styleflags, false);
vm_pattern_cache[key] = p;
return p;
}
Cairo::RefPtr<Cairo::Pattern>
FastMeter::request_vertical_background(
int width, int height, int *bgc, bool shade)
{
height = max(height, min_pattern_metric_size);
height = min(height, max_pattern_metric_size);
height += 2;
const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
PatternBgMap::iterator i;
if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
width, height, bgc, shade, false);
vb_pattern_cache[key] = p;
return p;
}
Cairo::RefPtr<Cairo::Pattern>
FastMeter::request_horizontal_meter(
int width, int height, int *clr, float *stp, int styleflags)
{
width = max(width, min_pattern_metric_size);
width = min(width, max_pattern_metric_size);
const Pattern10MapKey key (width, height,
stp[0], stp[1], stp[2], stp[3],
clr[0], clr[1], clr[2], clr[3],
clr[4], clr[5], clr[6], clr[7],
clr[8], clr[9], styleflags);
Pattern10Map::iterator i;
if ((i = hm_pattern_cache.find (key)) != hm_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
height, width, clr, stp, styleflags, true);
hm_pattern_cache[key] = p;
return p;
}
Cairo::RefPtr<Cairo::Pattern>
FastMeter::request_horizontal_background(
int width, int height, int *bgc, bool shade)
{
width = max(width, min_pattern_metric_size);
width = min(width, max_pattern_metric_size);
width += 2;
const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
PatternBgMap::iterator i;
if ((i = hb_pattern_cache.find (key)) != hb_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
height, width, bgc, shade, true);
hb_pattern_cache[key] = p;
return p;
}
void
FastMeter::set_hold_count (long val)
{
if (val < 1) {
val = 1;
}
hold_cnt = val;
hold_state = 0;
current_peak = 0;
queue_draw ();
}
void
FastMeter::on_size_request (GtkRequisition* req)
{
if (orientation == Vertical) {
vertical_size_request (req);
} else {
horizontal_size_request (req);
}
}
void
FastMeter::vertical_size_request (GtkRequisition* req)
{
req->height = request_height;
req->height = max(req->height, min_pattern_metric_size);
req->height = min(req->height, max_pattern_metric_size);
req->height += 2;
req->width = request_width;
}
void
FastMeter::horizontal_size_request (GtkRequisition* req)
{
req->width = request_width;
req->width = max(req->width, min_pattern_metric_size);
req->width = min(req->width, max_pattern_metric_size);
req->width += 2;
req->height = request_height;
}
void
FastMeter::on_size_allocate (Gtk::Allocation &alloc)
{
if (orientation == Vertical) {
vertical_size_allocate (alloc);
} else {
horizontal_size_allocate (alloc);
}
queue_draw ();
}
void
FastMeter::vertical_size_allocate (Gtk::Allocation &alloc)
{
if (alloc.get_width() != request_width) {
alloc.set_width (request_width);
}
int h = alloc.get_height();
h = max (h, min_pattern_metric_size + 2);
h = min (h, max_pattern_metric_size + 2);
if (h != alloc.get_height()) {
alloc.set_height (h);
}
if (pixheight != h) {
fgpattern = request_vertical_meter (request_width, h, _clr, _stp, _styleflags);
bgpattern = request_vertical_background (request_width, h, highlight ? _bgh : _bgc, highlight);
pixheight = h - 2;
pixwidth = request_width - 2;
}
CairoWidget::on_size_allocate (alloc);
}
void
FastMeter::horizontal_size_allocate (Gtk::Allocation &alloc)
{
if (alloc.get_height() != request_height) {
alloc.set_height (request_height);
}
int w = alloc.get_width();
w = max (w, min_pattern_metric_size + 2);
w = min (w, max_pattern_metric_size + 2);
if (w != alloc.get_width()) {
alloc.set_width (w);
}
if (pixwidth != w) {
fgpattern = request_horizontal_meter (w, request_height, _clr, _stp, _styleflags);
bgpattern = request_horizontal_background (w, request_height, highlight ? _bgh : _bgc, highlight);
pixwidth = w - 2;
pixheight = request_height - 2;
}
CairoWidget::on_size_allocate (alloc);
}
void
FastMeter::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t* area)
{
if (orientation == Vertical) {
return vertical_expose (ctx->cobj(), area);
} else {
return horizontal_expose (ctx->cobj(), area);
}
}
void
FastMeter::vertical_expose (cairo_t* cr, cairo_rectangle_t* area)
{
gint top_of_meter;
GdkRectangle intersection;
GdkRectangle background;
GdkRectangle eventarea;
cairo_set_source_rgb (cr, 0, 0, 0); // black
rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2);
cairo_stroke (cr);
top_of_meter = (gint) floor (pixheight * current_level);
/* reset the height & origin of the rect that needs to show the pixbuf
*/
pixrect.height = top_of_meter;
pixrect.y = 1 + pixheight - top_of_meter;
background.x = 1;
background.y = 1;
background.width = pixrect.width;
background.height = pixheight - top_of_meter;
eventarea.x = area->x;
eventarea.y = area->y;
eventarea.width = area->width;
eventarea.height = area->height;
if (gdk_rectangle_intersect (&background, &eventarea, &intersection)) {
cairo_set_source (cr, bgpattern->cobj());
cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
cairo_fill (cr);
}
if (gdk_rectangle_intersect (&pixrect, &eventarea, &intersection)) {
// draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
cairo_set_source (cr, fgpattern->cobj());
cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
cairo_fill (cr);
}
// draw peak bar
if (hold_state) {
last_peak_rect.x = 1;
last_peak_rect.width = pixwidth;
last_peak_rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
if (_styleflags & 2) { // LED stripes
last_peak_rect.y = max(0, (last_peak_rect.y & (~1)));
}
if (bright_hold || (_styleflags & 2)) {
last_peak_rect.height = max(0, min(3, pixheight - last_peak_rect.y - 1 ));
} else {
last_peak_rect.height = max(0, min(2, pixheight - last_peak_rect.y - 1 ));
}
cairo_set_source (cr, fgpattern->cobj());
cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
if (bright_hold && !no_rgba_overlay) {
cairo_fill_preserve (cr);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3);
}
cairo_fill (cr);
} else {
last_peak_rect.width = 0;
last_peak_rect.height = 0;
}
}
void
FastMeter::horizontal_expose (cairo_t* cr, cairo_rectangle_t* area)
{
gint right_of_meter;
GdkRectangle intersection;
GdkRectangle background;
GdkRectangle eventarea;
cairo_set_source_rgb (cr, 0, 0, 0); // black
rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2);
cairo_stroke (cr);
right_of_meter = (gint) floor (pixwidth * current_level);
/* reset the height & origin of the rect that needs to show the pixbuf
*/
pixrect.width = right_of_meter;
background.x = 1 + right_of_meter;
background.y = 1;
background.width = pixwidth - right_of_meter;
background.height = pixheight;
eventarea.x = area->x;
eventarea.y = area->y;
eventarea.width = area->width;
eventarea.height = area->height;
if (gdk_rectangle_intersect (&background, &eventarea, &intersection)) {
cairo_set_source (cr, bgpattern->cobj());
cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
cairo_fill (cr);
}
if (gdk_rectangle_intersect (&pixrect, &eventarea, &intersection)) {
cairo_set_source (cr, fgpattern->cobj());
cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
cairo_fill (cr);
}
// draw peak bar
if (hold_state) {
last_peak_rect.y = 1;
last_peak_rect.height = pixheight;
const int xpos = floor (pixwidth * current_peak);
if (bright_hold || (_styleflags & 2)) {
last_peak_rect.width = min(3, xpos );
} else {
last_peak_rect.width = min(2, xpos );
}
last_peak_rect.x = 1 + max(0, xpos - last_peak_rect.width);
cairo_set_source (cr, fgpattern->cobj());
cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
if (bright_hold && !no_rgba_overlay) {
cairo_fill_preserve (cr);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3);
}
cairo_fill (cr);
} else {
last_peak_rect.width = 0;
last_peak_rect.height = 0;
}
}
void
FastMeter::set (float lvl, float peak)
{
float old_level = current_level;
float old_peak = current_peak;
if (pixwidth <= 0 || pixheight <=0) return;
if (peak == -1) {
if (lvl >= current_peak) {
current_peak = lvl;
hold_state = hold_cnt;
}
if (hold_state > 0) {
if (--hold_state == 0) {
current_peak = lvl;
}
}
bright_hold = false;
} else {
current_peak = peak;
hold_state = 1;
bright_hold = true;
}
current_level = lvl;
const float pixscale = (orientation == Vertical) ? pixheight : pixwidth;
#define PIX(X) floor(pixscale * (X))
if (PIX(current_level) == PIX(old_level) && PIX(current_peak) == PIX(old_peak) && (hold_state == 0 || peak != -1)) {
return;
}
Glib::RefPtr<Gdk::Window> win;
if (! (win = get_window())) {
queue_draw ();
return;
}
if (orientation == Vertical) {
queue_vertical_redraw (win, old_level);
} else {
queue_horizontal_redraw (win, old_level);
}
}
void
FastMeter::queue_vertical_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
{
GdkRectangle rect;
gint new_top = (gint) floor (pixheight * current_level);
rect.x = 1;
rect.width = pixwidth;
rect.height = new_top;
rect.y = 1 + pixheight - new_top;
if (current_level > old_level) {
/* colored/pixbuf got larger, just draw the new section */
/* rect.y stays where it is because of X coordinates */
/* height of invalidated area is between new.y (smaller) and old.y
(larger).
X coordinates just make my brain hurt.
*/
rect.height = pixrect.y - rect.y;
} else {
/* it got smaller, compute the difference */
/* rect.y becomes old.y (the smaller value) */
rect.y = pixrect.y;
/* rect.height is the old.y (smaller) minus the new.y (larger)
*/
rect.height = pixrect.height - rect.height;
}
GdkRegion* region = 0;
bool queue = false;
if (rect.height != 0) {
/* ok, first region to draw ... */
region = gdk_region_rectangle (&rect);
queue = true;
}
/* redraw the last place where the last peak hold bar was;
the next expose will draw the new one whether its part of
expose region or not.
*/
if (last_peak_rect.width * last_peak_rect.height != 0) {
if (!queue) {
region = gdk_region_new ();
queue = true;
}
gdk_region_union_with_rect (region, &last_peak_rect);
}
if (hold_state && current_peak > 0) {
if (!queue) {
region = gdk_region_new ();
queue = true;
}
rect.x = 1;
rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
if (_styleflags & 2) { // LED stripes
rect.y = max(0, (rect.y & (~1)));
}
if (bright_hold || (_styleflags & 2)) {
rect.height = max(0, min(3, pixheight - last_peak_rect.y -1 ));
} else {
rect.height = max(0, min(2, pixheight - last_peak_rect.y -1 ));
}
rect.width = pixwidth;
gdk_region_union_with_rect (region, &rect);
}
if (queue) {
gdk_window_invalidate_region (win->gobj(), region, true);
}
if (region) {
gdk_region_destroy(region);
region = 0;
}
}
void
FastMeter::queue_horizontal_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
{
GdkRectangle rect;
gint new_right = (gint) floor (pixwidth * current_level);
rect.height = pixheight;
rect.y = 1;
if (current_level > old_level) {
rect.x = 1 + pixrect.width;
/* colored/pixbuf got larger, just draw the new section */
rect.width = new_right - pixrect.width;
} else {
/* it got smaller, compute the difference */
rect.x = 1 + new_right;
/* rect.height is the old.x (smaller) minus the new.x (larger) */
rect.width = pixrect.width - new_right;
}
GdkRegion* region = 0;
bool queue = false;
if (rect.height != 0) {
/* ok, first region to draw ... */
region = gdk_region_rectangle (&rect);
queue = true;
}
/* redraw the last place where the last peak hold bar was;
the next expose will draw the new one whether its part of
expose region or not.
*/
if (last_peak_rect.width * last_peak_rect.height != 0) {
if (!queue) {
region = gdk_region_new ();
queue = true;
}
gdk_region_union_with_rect (region, &last_peak_rect);
}
if (hold_state && current_peak > 0) {
if (!queue) {
region = gdk_region_new ();
queue = true;
}
rect.y = 1;
rect.height = pixheight;
const int xpos = floor (pixwidth * current_peak);
if (bright_hold || (_styleflags & 2)) {
rect.width = min(3, xpos);
} else {
rect.width = min(2, xpos);
}
rect.x = 1 + max(0, xpos - rect.width);
gdk_region_union_with_rect (region, &rect);
}
if (queue) {
gdk_window_invalidate_region (win->gobj(), region, true);
}
if (region) {
gdk_region_destroy(region);
region = 0;
}
}
void
FastMeter::set_highlight (bool onoff)
{
if (highlight == onoff) {
return;
}
highlight = onoff;
if (orientation == Vertical) {
bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);
} else {
bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);
}
queue_draw ();
}
void
FastMeter::clear ()
{
current_level = 0;
current_peak = 0;
hold_state = 0;
queue_draw ();
}

View file

@ -0,0 +1,49 @@
/*
Copyright (C) 2000-2007 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 "widgets/focus_entry.h"
using namespace ArdourWidgets;
FocusEntry::FocusEntry ()
: next_release_selects (false)
{
}
bool
FocusEntry::on_button_press_event (GdkEventButton* ev)
{
if (!has_focus()) {
next_release_selects = true;
}
return Entry::on_button_press_event (ev);
}
bool
FocusEntry::on_button_release_event (GdkEventButton* ev)
{
if (next_release_selects) {
bool ret = Entry::on_button_release_event (ev);
select_region (0, -1);
next_release_selects = false;
return ret;
}
return Entry::on_button_release_event (ev);
}

92
libs/widgets/searchbar.cc Normal file
View file

@ -0,0 +1,92 @@
#include <iostream>
#include "gtkmm2ext/keyboard.h"
#include "widgets/searchbar.h"
using namespace ArdourWidgets;
SearchBar::SearchBar (const std::string& label, bool icon_resets)
: placeholder_text (label)
, icon_click_resets (icon_resets)
{
set_text (placeholder_text);
set_alignment (Gtk::ALIGN_CENTER);
signal_key_press_event().connect (sigc::mem_fun (*this, &SearchBar::key_press_event));
signal_focus_in_event().connect (sigc::mem_fun (*this, &SearchBar::focus_in_event));
signal_focus_out_event().connect (sigc::mem_fun (*this, &SearchBar::focus_out_event));
signal_changed().connect (sigc::mem_fun (*this, &SearchBar::search_string_changed));
signal_icon_release().connect (sigc::mem_fun (*this, &SearchBar::icon_clicked_event));
}
bool
SearchBar::focus_in_event (GdkEventFocus*)
{
if (get_text ().compare (placeholder_text) == 0) {
set_text ("");
}
icon = get_icon_pixbuf ();
if (icon) {
set_icon_from_pixbuf (Glib::RefPtr<Gdk::Pixbuf> ());
}
return true;
}
bool
SearchBar::focus_out_event (GdkEventFocus*)
{
if (get_text ().empty ()) {
set_text (placeholder_text);
}
if (icon) {
set_icon_from_pixbuf (icon);
icon.reset ();
}
search_string_changed ();
return false;
}
bool
SearchBar::key_press_event (GdkEventKey* ev)
{
switch (ev->keyval) {
case GDK_Escape:
set_text (placeholder_text);
return true;
default:
break;
}
return false;
}
void
SearchBar::icon_clicked_event (Gtk::EntryIconPosition, const GdkEventButton*)
{
if (icon_click_resets) {
reset ();
}
else {
search_string_changed ();
}
}
void
SearchBar::search_string_changed () const
{
const std::string& text = get_text ();
if (text.empty() || text.compare (placeholder_text) == 0) {
sig_search_string_updated ("");
return;
}
sig_search_string_updated (text);
}
void
SearchBar::reset ()
{
set_text (placeholder_text);
search_string_changed ();
}

View file

@ -0,0 +1,122 @@
/*
Copyright (C) 1998-99 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$
*/
#include <string>
#include "gtkmm2ext/gtk_ui.h"
#include "pbd/controllable.h"
#include "widgets/ardour_fader.h"
#include "widgets/slider_controller.h"
#include "pbd/i18n.h"
using namespace PBD;
using namespace ArdourWidgets;
SliderController::SliderController (Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable> mc, int orientation, int fader_length, int fader_girth)
: ArdourFader (*adj, orientation, fader_length, fader_girth)
, _ctrl (mc)
, _ctrl_adj (adj)
, _spin_adj (0, 0, 1.0, .1, .01)
, _spin (_spin_adj, 0, 2)
, _ctrl_ignore (false)
, _spin_ignore (false)
{
if (mc) {
_spin_adj.set_lower (mc->lower ());
_spin_adj.set_upper (mc->upper ());
_spin_adj.set_step_increment(_ctrl->interface_to_internal(adj->get_step_increment()) - mc->lower ());
_spin_adj.set_page_increment(_ctrl->interface_to_internal(adj->get_page_increment()) - mc->lower ());
adj->signal_value_changed().connect (sigc::mem_fun(*this, &SliderController::ctrl_adjusted));
_spin_adj.signal_value_changed().connect (sigc::mem_fun(*this, &SliderController::spin_adjusted));
_binding_proxy.set_controllable (mc);
}
_spin.set_name ("SliderControllerValue");
_spin.set_numeric (true);
_spin.set_snap_to_ticks (false);
}
bool
SliderController::on_button_press_event (GdkEventButton *ev)
{
if (_binding_proxy.button_press_handler (ev)) {
return true;
}
return ArdourFader::on_button_press_event (ev);
}
bool
SliderController::on_enter_notify_event (GdkEventCrossing* ev)
{
boost::shared_ptr<PBD::Controllable> c (_binding_proxy.get_controllable ());
if (c) {
PBD::Controllable::GUIFocusChanged (boost::weak_ptr<PBD::Controllable> (c));
}
return ArdourFader::on_enter_notify_event (ev);
}
bool
SliderController::on_leave_notify_event (GdkEventCrossing* ev)
{
if (_binding_proxy.get_controllable()) {
PBD::Controllable::GUIFocusChanged (boost::weak_ptr<PBD::Controllable> ());
}
return ArdourFader::on_leave_notify_event (ev);
}
void
SliderController::ctrl_adjusted ()
{
assert (_ctrl); // only used w/BarControlle
if (_spin_ignore) return;
_ctrl_ignore = true;
// TODO consider using internal_to_user, too (amp, dB)
// (also needs _spin_adj min/max range changed accordingly
// and dedicated support for log-scale, revert parts of ceff2e3a62f839)
_spin_adj.set_value (_ctrl->interface_to_internal (_ctrl_adj->get_value()));
_ctrl_ignore = false;
}
void
SliderController::spin_adjusted ()
{
assert (_ctrl); // only used w/BarController
if (_ctrl_ignore) return;
_spin_ignore = true;
// TODO consider using user_to_internal, as well
_ctrl_adj->set_value(_ctrl->internal_to_interface (_spin_adj.get_value()));
_spin_ignore = false;
}
VSliderController::VSliderController (Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable> mc, int fader_length, int fader_girth)
: SliderController (adj, mc, VERT, fader_length, fader_girth)
{
}
HSliderController::HSliderController (Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable> mc, int fader_length, int fader_girth)
: SliderController (adj, mc, HORIZ, fader_length, fader_girth)
{
}

View file

@ -0,0 +1,158 @@
/*
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.
*/
#ifndef _WIDGETS_ARDOUR_FADER_H_
#define _WIDGETS_ARDOUR_FADER_H_
#include <cmath>
#include <stdint.h>
#include <gdkmm.h>
#include <gtkmm/adjustment.h>
#include "gtkmm2ext/cairo_widget.h"
#include "widgets/visibility.h"
namespace ArdourWidgets {
class LIBWIDGETS_API ArdourFader : public CairoWidget
{
public:
ArdourFader (Gtk::Adjustment& adjustment, int orientation, int span, int girth);
virtual ~ArdourFader ();
static void flush_pattern_cache();
sigc::signal<void> StartGesture;
sigc::signal<void> StopGesture;
sigc::signal<void> OnExpose;
void set_default_value (float);
void set_text (const std::string&, bool centered = true, bool expose = true);
enum Tweaks {
NoShowUnityLine = 0x1,
NoButtonForward = 0x2,
NoVerticalScroll = 0x4,
};
Tweaks tweaks() const { return _tweaks; }
void set_tweaks (Tweaks);
protected:
void on_size_request (GtkRequisition*);
void on_size_allocate (Gtk::Allocation& alloc);
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
bool on_grab_broken_event (GdkEventGrabBroken*);
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event (GdkEventButton*);
bool on_motion_notify_event (GdkEventMotion*);
bool on_scroll_event (GdkEventScroll* ev);
bool on_enter_notify_event (GdkEventCrossing* ev);
bool on_leave_notify_event (GdkEventCrossing* ev);
void on_state_changed (Gtk::StateType);
void on_style_changed (const Glib::RefPtr<Gtk::Style>&);
enum Orientation {
VERT,
HORIZ,
};
private:
Glib::RefPtr<Pango::Layout> _layout;
std::string _text;
Tweaks _tweaks;
Gtk::Adjustment& _adjustment;
int _text_width;
int _text_height;
int _span, _girth;
int _min_span, _min_girth;
int _orien;
cairo_pattern_t* _pattern;
bool _hovering;
GdkWindow* _grab_window;
double _grab_loc;
double _grab_start;
bool _dragging;
float _default_value;
int _unity_loc;
bool _centered_text;
sigc::connection _parent_style_change;
Widget * _current_parent;
Gdk::Color get_parent_bg ();
void create_patterns();
void adjustment_changed ();
void set_adjustment_from_event (GdkEventButton *);
void update_unity_position ();
int display_span ();
struct FaderImage {
cairo_pattern_t* pattern;
double fr;
double fg;
double fb;
double br;
double bg;
double bb;
int width;
int height;
FaderImage (cairo_pattern_t* p,
double afr, double afg, double afb,
double abr, double abg, double abb,
int w, int h)
: pattern (p)
, fr (afr)
, fg (afg)
, fb (afb)
, br (abr)
, bg (abg)
, bb (abb)
, width (w)
, height (h)
{}
bool matches (double afr, double afg, double afb,
double abr, double abg, double abb,
int w, int h) {
return width == w &&
height == h &&
afr == fr &&
afg == fg &&
afb == fb &&
abr == br &&
abg == bg &&
abb == bb;
}
};
static std::list<FaderImage*> _patterns;
static cairo_pattern_t* find_pattern (double afr, double afg, double afb,
double abr, double abg, double abb,
int w, int h);
};
} /* namespace */
#endif /* __gtkmm2ext_pixfader_h__ */

View file

@ -0,0 +1,76 @@
/*
Copyright (C) 2000 Paul Barton-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.
*/
#ifndef _WIDGETS_AUTO_SPIN_H_
#define _WIDGETS_AUTO_SPIN_H_
#ifdef interface
#undef interface
#endif
#include <gtkmm.h>
#include "widgets/visibility.h"
namespace ArdourWidgets {
class LIBWIDGETS_API AutoSpin
{
public:
AutoSpin (Gtk::Adjustment &adj, gfloat cr = 0, bool round_to_steps_yn = false);
Gtk::Adjustment &get_adjustment() { return adjustment; }
void use_left_as_decrement (bool yn) { left_is_decrement = yn; }
void set_wrap (bool yn) { wrap = yn; }
void set_climb_rate (gfloat cr) { climb_rate = cr; }
void set_bounds (gfloat initial, gfloat low, gfloat high, bool with_reset = true);
gint button_press (GdkEventButton *);
gint stop_spinning (GdkEventButton *ignored_but_here_for_clicked);
void start_spinning (bool decrementing, bool use_page);
gint scroll_event (GdkEventScroll *);
private:
Gtk::Adjustment &adjustment;
gfloat climb_rate;
gfloat timer_increment;
gfloat initial;
unsigned int timer_calls;
bool have_timer;
bool need_timer;
bool wrap;
gint timeout_tag;
bool left_is_decrement;
bool round_to_steps;
static const unsigned int initial_timer_interval;
static const unsigned int timer_interval;
static const unsigned int climb_timer_calls;
void stop_timer ();
static gint _timer (void *arg);
gint timer ();
bool adjust_value (gfloat increment);
void set_value (gfloat value);
};
} /* namespace */
#endif

View file

@ -0,0 +1,83 @@
/*
Copyright (C) 2004 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.
*/
#ifndef _WIDGETS_BAR_CONTROLLER_H_
#define _WIDGETS_BAR_CONTROLLER_H_
#include <gtkmm/alignment.h>
#include <cairo.h>
#include "gtkmm2ext/binding_proxy.h"
#include "widgets/slider_controller.h"
#include "widgets/visibility.h"
namespace ArdourWidgets {
class LIBWIDGETS_API BarController : public Gtk::Alignment
{
public:
BarController (Gtk::Adjustment& adj, boost::shared_ptr<PBD::Controllable>);
virtual ~BarController ();
void set_sensitive (bool yn);
ArdourFader::Tweaks tweaks() const { return _slider.tweaks (); }
void set_tweaks (ArdourFader::Tweaks t) { _slider.set_tweaks (t);}
sigc::signal<void> StartGesture;
sigc::signal<void> StopGesture;
/* export this to allow direct connection to button events */
Gtk::Widget& event_widget() { return _slider; }
/** Emitted when the adjustment spinner is activated or deactivated;
* the parameter is true on activation, false on deactivation.
*/
sigc::signal<void, bool> SpinnerActive;
protected:
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event (GdkEventButton*);
void on_style_changed (const Glib::RefPtr<Gtk::Style>&);
virtual std::string get_label (double& /*x*/) {
return "";
}
private:
HSliderController _slider;
bool entry_focus_out (GdkEventFocus*);
void entry_activated ();
void before_expose ();
gint switch_to_bar ();
gint switch_to_spinner ();
bool _switching;
bool _switch_on_release;
void passtrhu_gesture_start() { StartGesture (); }
void passtrhu_gesture_stop() { StopGesture (); }
};
}; /* namespace */
#endif // __gtkmm2ext_bar_controller_h__

View file

@ -0,0 +1,81 @@
/*
Copyright (C) 1999 Paul Barton-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.
*/
#ifndef _WIDGETS_CLICK_BOX_H_
#define _WIDGETS_CLICK_BOX_H_
#ifdef interface
#undef interface
#endif
#include <string>
#include <gtkmm.h>
#include "gtkmm2ext/binding_proxy.h"
#include "widgets/auto_spin.h"
#include "widgets/visibility.h"
namespace PBD {
class Controllable;
}
namespace ArdourWidgets {
class LIBWIDGETS_API ClickBox : public Gtk::DrawingArea, public AutoSpin
{
public:
ClickBox (Gtk::Adjustment *adj, const std::string &name, bool round_to_steps = false);
~ClickBox ();
/** Set a slot to `print' the value to put in the box.
* The slot should write the value of the Gtk::Adjustment
* into the char array, and should return true if it has done the printing,
* or false to use the ClickBox's default printing method.
*/
void set_printer (sigc::slot<bool, char *, Gtk::Adjustment &>);
void set_controllable (boost::shared_ptr<PBD::Controllable> c) {
_binding_proxy.set_controllable (c);
}
protected:
bool on_expose_event (GdkEventExpose*);
bool on_enter_notify_event (GdkEventCrossing* ev);
bool on_leave_notify_event (GdkEventCrossing* ev);
BindingProxy _binding_proxy;
private:
Glib::RefPtr<Pango::Layout> layout;
int twidth;
int theight;
void set_label ();
void style_changed (const Glib::RefPtr<Gtk::Style> &);
bool button_press_handler (GdkEventButton *);
bool button_release_handler (GdkEventButton *);
bool on_scroll_event (GdkEventScroll*);
sigc::slot<bool, char *, Gtk::Adjustment &> _printer;
};
} /* namespace */
#endif

View file

@ -0,0 +1,177 @@
/*
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.
*/
#ifndef _WIDGETS_FAST_METER_H_
#define _WIDGETS_FAST_METER_H_
#include <map>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <cairomm/pattern.h>
#include "gtkmm2ext/cairo_widget.h"
#include "widgets/visibility.h"
namespace ArdourWidgets {
class LIBWIDGETS_API FastMeter : public CairoWidget {
public:
enum Orientation {
Horizontal,
Vertical
};
FastMeter (long hold_cnt, unsigned long width, Orientation, int len=0,
int clr0=0x008800ff, int clr1=0x008800ff,
int clr2=0x00ff00ff, int clr3=0x00ff00ff,
int clr4=0xffaa00ff, int clr5=0xffaa00ff,
int clr6=0xffff00ff, int clr7=0xffff00ff,
int clr8=0xff0000ff, int clr9=0xff0000ff,
int bgc0=0x333333ff, int bgc1=0x444444ff,
int bgh0=0x991122ff, int bgh1=0x551111ff,
float stp0 = 55.0, // log_meter(-18);
float stp1 = 77.5, // log_meter(-9);
float stp2 = 92.5, // log_meter(-3); // 95.0, // log_meter(-2);
float stp3 = 100.0,
int styleflags = 3
);
virtual ~FastMeter ();
static void flush_pattern_cache();
void set (float level, float peak = -1);
void clear ();
float get_level() { return current_level; }
float get_user_level() { return current_user_level; }
float get_peak() { return current_peak; }
long hold_count() { return hold_cnt; }
void set_hold_count (long);
void set_highlight (bool);
bool get_highlight () { return highlight; }
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
protected:
void on_size_request (GtkRequisition*);
void on_size_allocate (Gtk::Allocation&);
private:
Cairo::RefPtr<Cairo::Pattern> fgpattern;
Cairo::RefPtr<Cairo::Pattern> bgpattern;
gint pixheight;
gint pixwidth;
float _stp[4];
int _clr[10];
int _bgc[2];
int _bgh[2];
int _styleflags;
Orientation orientation;
GdkRectangle pixrect;
GdkRectangle last_peak_rect;
gint request_width;
gint request_height;
unsigned long hold_cnt;
unsigned long hold_state;
bool bright_hold;
float current_level;
float current_peak;
float current_user_level;
bool highlight;
void vertical_expose (cairo_t*, cairo_rectangle_t*);
void vertical_size_request (GtkRequisition*);
void vertical_size_allocate (Gtk::Allocation&);
void queue_vertical_redraw (const Glib::RefPtr<Gdk::Window>&, float);
void horizontal_expose (cairo_t*, cairo_rectangle_t*);
void horizontal_size_request (GtkRequisition*);
void horizontal_size_allocate (Gtk::Allocation&);
void queue_horizontal_redraw (const Glib::RefPtr<Gdk::Window>&, float);
static bool no_rgba_overlay;
static Cairo::RefPtr<Cairo::Pattern> generate_meter_pattern (
int, int, int *, float *, int, bool);
static Cairo::RefPtr<Cairo::Pattern> request_vertical_meter (
int, int, int *, float *, int);
static Cairo::RefPtr<Cairo::Pattern> request_horizontal_meter (
int, int, int *, float *, int);
static Cairo::RefPtr<Cairo::Pattern> generate_meter_background (
int, int, int *, bool, bool);
static Cairo::RefPtr<Cairo::Pattern> request_vertical_background (
int, int, int *, bool);
static Cairo::RefPtr<Cairo::Pattern> request_horizontal_background (
int, int, int *, bool);
struct Pattern10MapKey {
Pattern10MapKey (
int w, int h,
float stp0, float stp1, float stp2, float stp3,
int c0, int c1, int c2, int c3,
int c4, int c5, int c6, int c7,
int c8, int c9, int st
)
: dim(w, h)
, stp(stp0, stp1, stp2, stp3)
, cols(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)
, style(st)
{}
inline bool operator<(const Pattern10MapKey& rhs) const {
return (dim < rhs.dim)
|| (dim == rhs.dim && stp < rhs.stp)
|| (dim == rhs.dim && stp == rhs.stp && cols < rhs.cols)
|| (dim == rhs.dim && stp == rhs.stp && cols == rhs.cols && style < rhs.style);
}
boost::tuple<int, int> dim;
boost::tuple<float, float, float, float> stp;
boost::tuple<int, int, int, int, int, int, int, int, int, int> cols;
int style;
};
typedef std::map<Pattern10MapKey, Cairo::RefPtr<Cairo::Pattern> > Pattern10Map;
struct PatternBgMapKey {
PatternBgMapKey (int w, int h, int c0, int c1, bool shade)
: dim(w, h)
, cols(c0, c1)
, sh(shade)
{}
inline bool operator<(const PatternBgMapKey& rhs) const {
return (dim < rhs.dim) || (dim == rhs.dim && cols < rhs.cols) || (dim == rhs.dim && cols == rhs.cols && (sh && !rhs.sh));
}
boost::tuple<int, int> dim;
boost::tuple<int, int> cols;
bool sh;
};
typedef std::map<PatternBgMapKey, Cairo::RefPtr<Cairo::Pattern> > PatternBgMap;
static Pattern10Map vm_pattern_cache;
static PatternBgMap vb_pattern_cache;
static Pattern10Map hm_pattern_cache;
static PatternBgMap hb_pattern_cache;
static int min_pattern_metric_size; // min dimension for axis that displays the meter level
static int max_pattern_metric_size; // max dimension for axis that displays the meter level
};
} /* namespace */
#endif

View file

@ -0,0 +1,43 @@
/*
Copyright (C) 2000-2007 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.
*/
#ifndef _WIDGETS_FOCUS_ENTRY_H_
#define _WIDGETS_FOCUS_ENTRY_H_
#include <gtkmm/entry.h>
#include "widgets/visibility.h"
namespace ArdourWidgets {
class LIBWIDGETS_API FocusEntry : public Gtk::Entry
{
public:
FocusEntry ();
protected:
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event (GdkEventButton*);
private:
bool next_release_selects;
};
} /* end namespace */
#endif

View file

@ -0,0 +1,40 @@
#pragma once
#include <gtkmm/entry.h>
#include <string>
#include "widgets/visibility.h"
namespace ArdourWidgets {
class LIBWIDGETS_API SearchBar : public Gtk::Entry
{
public:
SearchBar (
const std::string& placeholder_text = "Search...",
bool icon_click_resets = true);
/** resets the searchbar to the initial state */
void reset ();
/* emitted when the filter has been updated */
sigc::signal<void, const std::string&> signal_search_string_updated () { return sig_search_string_updated; }
protected:
bool focus_in_event (GdkEventFocus*);
bool focus_out_event (GdkEventFocus*);
bool key_press_event (GdkEventKey*);
void icon_clicked_event (Gtk::EntryIconPosition, const GdkEventButton*);
const std::string placeholder_text;
sigc::signal<void, const std::string&> sig_search_string_updated;
private:
void search_string_changed () const;
Glib::RefPtr<Gdk::Pixbuf> icon;
bool icon_click_resets;
};
} /* end namespace */

View file

@ -0,0 +1,81 @@
/*
Copyright (C) 1998-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.
*/
#ifndef _WIDGETS_SLIDER_CONTROLLER_H_
#define _WIDGETS_SLIDER_CONTROLLER_H_
#ifdef interface
#undef interface
#endif
#include <gtkmm.h>
#include <boost/shared_ptr.hpp>
#include "gtkmm2ext/popup.h"
#include "gtkmm2ext/binding_proxy.h"
#include "widgets/ardour_fader.h"
#include "widgets/visibility.h"
namespace PBD {
class Controllable;
}
namespace ArdourWidgets {
class LIBWIDGETS_API SliderController : public ArdourWidgets::ArdourFader
{
public:
SliderController (Gtk::Adjustment* adj, boost::shared_ptr<PBD::Controllable> mc, int orientation, int, int);
virtual ~SliderController () {}
Gtk::SpinButton& get_spin_button () { assert(_ctrl); return _spin; }
void set_controllable (boost::shared_ptr<PBD::Controllable> c) { _binding_proxy.set_controllable (c); }
protected:
bool on_button_press_event (GdkEventButton *ev);
bool on_enter_notify_event (GdkEventCrossing* ev);
bool on_leave_notify_event (GdkEventCrossing* ev);
void ctrl_adjusted();
void spin_adjusted();
BindingProxy _binding_proxy;
boost::shared_ptr<PBD::Controllable> _ctrl;
Gtk::Adjustment *_ctrl_adj;
Gtk::Adjustment _spin_adj;
Gtk::SpinButton _spin;
bool _ctrl_ignore;
bool _spin_ignore;
};
class LIBWIDGETS_API VSliderController : public SliderController
{
public:
VSliderController (Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable> mc, int, int);
};
class LIBWIDGETS_API HSliderController : public SliderController
{
public:
HSliderController (Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable> mc, int, int);
};
}; /* namespace */
#endif

View file

@ -29,9 +29,17 @@ widgets_sources = [
'ardour_button.cc',
'ardour_display.cc',
'ardour_dropdown.cc',
'ardour_fader.cc',
'ardour_knob.cc',
'ardour_spacer.cc',
'ardour_spinner.cc',
'auto_spin.cc',
'barcontroller.cc',
'click_box.cc',
'fastmeter.cc',
'focus_entry.cc',
'searchbar.cc',
'slider_controller.cc',
'tooltips.cc',
'ui_config.cc',
]