diff options
author | Robin Gareus <robin@gareus.org> | 2017-07-16 22:13:46 +0200 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2017-07-17 21:06:04 +0200 |
commit | f6e182b937efda6ed0ba50dbc02af98524beb61c (patch) | |
tree | c5304cbfc96a82f00582ef038e2a9223881a7489 /libs/widgets | |
parent | b6e4dfe37be32009ce7ffc58d4a6139923c12981 (diff) |
Move Gtkmm2ext widgets into libwidget
Diffstat (limited to 'libs/widgets')
-rw-r--r-- | libs/widgets/ardour_fader.cc | 747 | ||||
-rw-r--r-- | libs/widgets/auto_spin.cc | 298 | ||||
-rw-r--r-- | libs/widgets/barcontroller.cc | 169 | ||||
-rw-r--r-- | libs/widgets/click_box.cc | 188 | ||||
-rw-r--r-- | libs/widgets/fastmeter.cc | 919 | ||||
-rw-r--r-- | libs/widgets/focus_entry.cc | 49 | ||||
-rw-r--r-- | libs/widgets/searchbar.cc | 92 | ||||
-rw-r--r-- | libs/widgets/slider_controller.cc | 122 | ||||
-rw-r--r-- | libs/widgets/widgets/ardour_fader.h | 158 | ||||
-rw-r--r-- | libs/widgets/widgets/auto_spin.h | 76 | ||||
-rw-r--r-- | libs/widgets/widgets/barcontroller.h | 83 | ||||
-rw-r--r-- | libs/widgets/widgets/click_box.h | 81 | ||||
-rw-r--r-- | libs/widgets/widgets/fastmeter.h | 177 | ||||
-rw-r--r-- | libs/widgets/widgets/focus_entry.h | 43 | ||||
-rw-r--r-- | libs/widgets/widgets/searchbar.h | 40 | ||||
-rw-r--r-- | libs/widgets/widgets/slider_controller.h | 81 | ||||
-rw-r--r-- | libs/widgets/wscript | 8 |
17 files changed, 3331 insertions, 0 deletions
diff --git a/libs/widgets/ardour_fader.cc b/libs/widgets/ardour_fader.cc new file mode 100644 index 0000000000..e48c9f6ae5 --- /dev/null +++ b/libs/widgets/ardour_fader.cc @@ -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()); +} diff --git a/libs/widgets/auto_spin.cc b/libs/widgets/auto_spin.cc new file mode 100644 index 0000000000..9d86eb50ac --- /dev/null +++ b/libs/widgets/auto_spin.cc @@ -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); + } +} diff --git a/libs/widgets/barcontroller.cc b/libs/widgets/barcontroller.cc new file mode 100644 index 0000000000..896652c600 --- /dev/null +++ b/libs/widgets/barcontroller.cc @@ -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); +} diff --git a/libs/widgets/click_box.cc b/libs/widgets/click_box.cc new file mode 100644 index 0000000000..b3271a0cc4 --- /dev/null +++ b/libs/widgets/click_box.cc @@ -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; +} diff --git a/libs/widgets/fastmeter.cc b/libs/widgets/fastmeter.cc new file mode 100644 index 0000000000..cf1d9dd070 --- /dev/null +++ b/libs/widgets/fastmeter.cc @@ -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 (); +} diff --git a/libs/widgets/focus_entry.cc b/libs/widgets/focus_entry.cc new file mode 100644 index 0000000000..b503bc1dbd --- /dev/null +++ b/libs/widgets/focus_entry.cc @@ -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); +} diff --git a/libs/widgets/searchbar.cc b/libs/widgets/searchbar.cc new file mode 100644 index 0000000000..69ceac8007 --- /dev/null +++ b/libs/widgets/searchbar.cc @@ -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 (); +} diff --git a/libs/widgets/slider_controller.cc b/libs/widgets/slider_controller.cc new file mode 100644 index 0000000000..4bf784a505 --- /dev/null +++ b/libs/widgets/slider_controller.cc @@ -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) +{ +} diff --git a/libs/widgets/widgets/ardour_fader.h b/libs/widgets/widgets/ardour_fader.h new file mode 100644 index 0000000000..b9270cae51 --- /dev/null +++ b/libs/widgets/widgets/ardour_fader.h @@ -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__ */ diff --git a/libs/widgets/widgets/auto_spin.h b/libs/widgets/widgets/auto_spin.h new file mode 100644 index 0000000000..ecea31e7fe --- /dev/null +++ b/libs/widgets/widgets/auto_spin.h @@ -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 diff --git a/libs/widgets/widgets/barcontroller.h b/libs/widgets/widgets/barcontroller.h new file mode 100644 index 0000000000..fc2ac358cc --- /dev/null +++ b/libs/widgets/widgets/barcontroller.h @@ -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__ diff --git a/libs/widgets/widgets/click_box.h b/libs/widgets/widgets/click_box.h new file mode 100644 index 0000000000..fa4868467f --- /dev/null +++ b/libs/widgets/widgets/click_box.h @@ -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 diff --git a/libs/widgets/widgets/fastmeter.h b/libs/widgets/widgets/fastmeter.h new file mode 100644 index 0000000000..b1d2f3f4f9 --- /dev/null +++ b/libs/widgets/widgets/fastmeter.h @@ -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 diff --git a/libs/widgets/widgets/focus_entry.h b/libs/widgets/widgets/focus_entry.h new file mode 100644 index 0000000000..f0fa22232b --- /dev/null +++ b/libs/widgets/widgets/focus_entry.h @@ -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 diff --git a/libs/widgets/widgets/searchbar.h b/libs/widgets/widgets/searchbar.h new file mode 100644 index 0000000000..2b3957f38f --- /dev/null +++ b/libs/widgets/widgets/searchbar.h @@ -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 */ diff --git a/libs/widgets/widgets/slider_controller.h b/libs/widgets/widgets/slider_controller.h new file mode 100644 index 0000000000..e80e76d7bc --- /dev/null +++ b/libs/widgets/widgets/slider_controller.h @@ -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 diff --git a/libs/widgets/wscript b/libs/widgets/wscript index 6432589177..dd2ee9587b 100644 --- a/libs/widgets/wscript +++ b/libs/widgets/wscript @@ -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', ] |