X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fgtkmm2ext%2Fcairo_widget.cc;h=4cf3fc3ae667e925f4f128f68fa6ed2d2b4da101;hb=ea8d089e3c1374da1cb693928bce01e5e722d1eb;hp=03c23000a267c0cd0c8b7dfa8272e2af01ef2972;hpb=ad017365f7a73f8ba57f667cc1aa36478b48c50e;p=ardour.git diff --git a/libs/gtkmm2ext/cairo_widget.cc b/libs/gtkmm2ext/cairo_widget.cc index 03c23000a2..4cf3fc3ae6 100644 --- a/libs/gtkmm2ext/cairo_widget.cc +++ b/libs/gtkmm2ext/cairo_widget.cc @@ -16,25 +16,34 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#if !defined USE_CAIRO_IMAGE_SURFACE && !defined NDEBUG +#define OPTIONAL_CAIRO_IMAGE_SURFACE +#endif #include "gtkmm2ext/cairo_widget.h" #include "gtkmm2ext/gui_thread.h" +#include "gtkmm2ext/rgb_macros.h" -#include "i18n.h" +#ifdef __APPLE__ +#include +#include "gtkmm2ext/nsglview.h" +#endif + +#include "pbd/i18n.h" static const char* has_cairo_widget_background_info = "has_cairo_widget_background_info"; bool CairoWidget::_flat_buttons = false; +bool CairoWidget::_widget_prelight = true; -static void noop() { } -sigc::slot CairoWidget::focus_handler (sigc::ptr_fun (noop)); +sigc::slot CairoWidget::focus_handler; void CairoWidget::set_source_rgb_a( cairo_t* cr, Gdk::Color col, float a) //ToDo: this one and the Canvas version should be in a shared file (?) { float r = col.get_red_p (); float g = col.get_green_p (); float b = col.get_blue_p (); - + cairo_set_source_rgba(cr, r, g, b, a); } @@ -45,36 +54,226 @@ CairoWidget::CairoWidget () , _grabbed (false) , _name_proxy (this, X_("name")) , _current_parent (0) + , _canvas_widget (false) + , _nsglview (0) { _name_proxy.connect (sigc::mem_fun (*this, &CairoWidget::on_name_changed)); } CairoWidget::~CairoWidget () { + if (_canvas_widget) { + gtk_widget_set_realized (GTK_WIDGET(gobj()), false); + } if (_parent_style_change) _parent_style_change.disconnect(); } +void +CairoWidget::set_canvas_widget () +{ + assert (!_nsglview); + assert (!_canvas_widget); + ensure_style (); + gtk_widget_set_realized (GTK_WIDGET(gobj()), true); + _canvas_widget = true; +} + +void +CairoWidget::use_nsglview () +{ + assert (!_nsglview); + assert (!_canvas_widget); + assert (!is_realized()); +#ifdef ARDOUR_CANVAS_NSVIEW_TAG // patched gdkquartz.h + _nsglview = Gtkmm2ext::nsglview_create (this); +#endif +} + +int +CairoWidget::get_width () const +{ + if (_canvas_widget) { + return _allocation.get_width (); + } + return Gtk::EventBox::get_width (); +} + +int +CairoWidget::get_height () const +{ + if (_canvas_widget) { + return _allocation.get_height (); + } + return Gtk::EventBox::get_height (); +} + +void +CairoWidget::size_allocate (Gtk::Allocation& alloc) +{ + if (_canvas_widget) { + memcpy (&_allocation, &alloc, sizeof(Gtk::Allocation)); + return; + } + Gtk::EventBox::size_allocate (alloc); +} + + bool CairoWidget::on_button_press_event (GdkEventButton*) { - focus_handler(); + focus_handler (this); return false; } +uint32_t +CairoWidget::background_color () +{ + if (_need_bg) { + Gdk::Color bg (get_parent_bg()); + return RGBA_TO_UINT (bg.get_red() / 255, bg.get_green() / 255, bg.get_blue() / 255, 255); + } else { + return 0; + } +} + +#ifdef USE_TRACKS_CODE_FEATURES + +/* This is Tracks version of this method. + + The use of get_visible_window() in this method is an abuse of the GDK/GTK + semantics. It can and may break on different GDK backends, and uses a + side-effect/unintended behaviour in GDK/GTK to try to accomplish something + which should be done differently. I (Paul) have confirmed this with the GTK + development team. + + For this reason, this code is not acceptable for ordinary merging into the Ardour libraries. + + Ardour Developers: you are not obligated to maintain the internals of this + implementation in the face of build-time environment changes (e.g. -D + defines etc). +*/ + bool CairoWidget::on_expose_event (GdkEventExpose *ev) { - cairo_t* cr = gdk_cairo_create (get_window ()->gobj()); - cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); - cairo_clip_preserve (cr); + cairo_rectangle_t expose_area; + expose_area.width = ev->area.width; + expose_area.height = ev->area.height; + +#ifdef USE_CAIRO_IMAGE_SURFACE_FOR_CAIRO_WIDGET + Cairo::RefPtr cr; + if (get_visible_window ()) { + expose_area.x = 0; + expose_area.y = 0; + if (!_image_surface) { + _image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height()); + } + cr = Cairo::Context::create (_image_surface); + } else { + expose_area.x = ev->area.x; + expose_area.y = ev->area.y; + cr = get_window()->create_cairo_context (); + } +#else + expose_area.x = ev->area.x; + expose_area.y = ev->area.y; + Cairo::RefPtr cr = get_window()->create_cairo_context (); +#endif + + cr->rectangle (expose_area.x, expose_area.y, expose_area.width, expose_area.height); + cr->clip (); /* paint expose area the color of the parent window bg */ - - Gdk::Color bg (get_parent_bg()); - - cairo_set_source_rgb (cr, bg.get_red_p(), bg.get_green_p(), bg.get_blue_p()); - cairo_fill (cr); + + if (get_visible_window ()) { + Gdk::Color bg (get_parent_bg()); + cr->rectangle (expose_area.x, expose_area.y, expose_area.width, expose_area.height); + cr->set_source_rgb (bg.get_red_p(), bg.get_green_p(), bg.get_blue_p()); + cr->fill (); + } + + render (cr, &expose_area); + +#ifdef USE_CAIRO_IMAGE_SURFACE_FOR_CAIRO_WIDGET + if(get_visible_window ()) { + _image_surface->flush(); + /* now blit our private surface back to the GDK one */ + + Cairo::RefPtr cairo_context = get_window()->create_cairo_context (); + + cairo_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height); + cairo_context->clip (); + cairo_context->set_source (_image_surface, ev->area.x, ev->area.y); + cairo_context->set_operator (Cairo::OPERATOR_OVER); + cairo_context->paint (); + } +#endif + + Gtk::Widget* child = get_child (); + + if (child) { + propagate_expose (*child, ev); + } + + return true; +} + +#else + +/* Ardour mainline: not using Tracks code features. + + Tracks Developers: please do not modify this version of + ::on_expose_event(). The version used by Tracks is before the preceding + #else and contains hacks required for the Tracks GUI to work. +*/ + +bool +CairoWidget::on_expose_event (GdkEventExpose *ev) +{ +#ifdef __APPLE__ + if (_nsglview) { + Gtkmm2ext::nsglview_queue_draw (_nsglview, ev->area.x, ev->area.y, ev->area.width, ev->area.height); + return true; + } +#endif +#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE + Cairo::RefPtr cr; + if (getenv("ARDOUR_IMAGE_SURFACE")) { + if (!image_surface) { + image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height()); + } + cr = Cairo::Context::create (image_surface); + } else { + cr = get_window()->create_cairo_context (); + } +#elif defined USE_CAIRO_IMAGE_SURFACE + + if (!image_surface) { + image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height()); + } + + Cairo::RefPtr cr = Cairo::Context::create (image_surface); +#else + Cairo::RefPtr cr = get_window()->create_cairo_context (); +#endif + + cr->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height); + + if (_need_bg) { + cr->clip_preserve (); + + /* paint expose area the color of the parent window bg + */ + + Gdk::Color bg (get_parent_bg()); + + cr->set_source_rgb (bg.get_red_p(), bg.get_green_p(), bg.get_blue_p()); + cr->fill (); + } else { + std::cerr << get_name() << " skipped bg fill\n"; + cr->clip (); + } cairo_rectangle_t expose_area; expose_area.x = ev->area.x; @@ -84,20 +283,65 @@ CairoWidget::on_expose_event (GdkEventExpose *ev) render (cr, &expose_area); - cairo_destroy (cr); +#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE + if (getenv("ARDOUR_IMAGE_SURFACE")) { +#endif +#if defined USE_CAIRO_IMAGE_SURFACE || defined OPTIONAL_CAIRO_IMAGE_SURFACE + image_surface->flush(); + /* now blit our private surface back to the GDK one */ + + Cairo::RefPtr cairo_context = get_window()->create_cairo_context (); + + cairo_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height); + cairo_context->clip (); + cairo_context->set_source (image_surface, 0, 0); + cairo_context->set_operator (Cairo::OPERATOR_SOURCE); + cairo_context->paint (); +#endif +#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE + } +#endif return true; } +#endif + /** Marks the widget as dirty, so that render () will be called on * the next GTK expose event. */ void -CairoWidget::set_dirty () +CairoWidget::set_dirty (cairo_rectangle_t *area) { ENSURE_GUI_THREAD (*this, &CairoWidget::set_dirty); - queue_draw (); + if (!area) { + queue_draw (); + } else { + // TODO emit QueueDrawArea -> ArdourCanvas::Widget + if (QueueDraw ()) { + return; + } + queue_draw_area (area->x, area->y, area->width, area->height); + } +} + +void +CairoWidget::queue_draw () +{ + if (QueueDraw ()) { + return; + } + Gtk::EventBox::queue_draw (); +} + +void +CairoWidget::queue_resize () +{ + if (QueueResize ()) { + return; + } + Gtk::EventBox::queue_resize (); } /** Handle a size allocation. @@ -106,8 +350,35 @@ CairoWidget::set_dirty () void CairoWidget::on_size_allocate (Gtk::Allocation& alloc) { - Gtk::EventBox::on_size_allocate (alloc); + if (!_canvas_widget) { + Gtk::EventBox::on_size_allocate (alloc); + } else { + memcpy (&_allocation, &alloc, sizeof(Gtk::Allocation)); + } + +#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE + if (getenv("ARDOUR_IMAGE_SURFACE")) { +#endif +#if defined USE_CAIRO_IMAGE_SURFACE || defined OPTIONAL_CAIRO_IMAGE_SURFACE + image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, alloc.get_width(), alloc.get_height()); +#endif +#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE + } +#endif + if (_canvas_widget) { + return; + } +#ifdef __APPLE__ + if (_nsglview) { + gint xx, yy; + gtk_widget_translate_coordinates( + GTK_WIDGET(gobj()), + GTK_WIDGET(get_toplevel()->gobj()), + 0, 0, &xx, &yy); + Gtkmm2ext::nsglview_resize (_nsglview, xx, yy, alloc.get_width(), alloc.get_height()); + } +#endif set_dirty (); } @@ -185,7 +456,47 @@ CairoWidget::set_active (bool yn) void CairoWidget::on_style_changed (const Glib::RefPtr&) { - queue_draw(); + set_dirty (); +} + +void +CairoWidget::on_realize () +{ + Gtk::EventBox::on_realize(); +#ifdef __APPLE__ + if (_nsglview) { + Gtkmm2ext::nsglview_overlay (_nsglview, get_window()->gobj()); + } +#endif +} + +void +CairoWidget::on_map () +{ + Gtk::EventBox::on_map(); +#ifdef __APPLE__ + if (_nsglview) { + Gtkmm2ext::nsglview_set_visible (_nsglview, true); + Gtk::Allocation a = get_allocation(); + gint xx, yy; + gtk_widget_translate_coordinates( + GTK_WIDGET(gobj()), + GTK_WIDGET(get_toplevel()->gobj()), + 0, 0, &xx, &yy); + Gtkmm2ext::nsglview_resize (_nsglview, xx, yy, a.get_width(), a.get_height()); + } +#endif +} + +void +CairoWidget::on_unmap () +{ + Gtk::EventBox::on_unmap(); +#ifdef __APPLE__ + if (_nsglview) { + Gtkmm2ext::nsglview_set_visible (_nsglview, false); + } +#endif } void @@ -201,7 +512,7 @@ CairoWidget::on_state_changed (Gtk::StateType) set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive)); } - queue_draw (); + set_dirty (); } void @@ -232,7 +543,13 @@ CairoWidget::set_flat_buttons (bool yn) } void -CairoWidget::set_focus_handler (sigc::slot s) +CairoWidget::set_widget_prelight (bool yn) +{ + _widget_prelight = yn; +} + +void +CairoWidget::set_focus_handler (sigc::slot s) { focus_handler = s; }