allow to use cairo-image/software surface for canvas & cairowidgets
[ardour.git] / libs / gtkmm2ext / cairo_widget.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "gtkmm2ext/cairo_widget.h"
21 #include "gtkmm2ext/gui_thread.h"
22
23 #include "i18n.h"
24
25 static const char* has_cairo_widget_background_info = "has_cairo_widget_background_info";
26
27 bool CairoWidget::_flat_buttons = false;
28
29 static void noop() { }
30 sigc::slot<void> CairoWidget::focus_handler (sigc::ptr_fun (noop));
31
32 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 (?)
33 {
34         float r = col.get_red_p ();
35         float g = col.get_green_p ();
36         float b = col.get_blue_p ();
37         
38         cairo_set_source_rgba(cr, r, g, b, a);
39 }
40
41 CairoWidget::CairoWidget ()
42         : _active_state (Gtkmm2ext::Off)
43         , _visual_state (Gtkmm2ext::NoVisualState)
44         , _need_bg (true)
45         , _grabbed (false)
46         , _name_proxy (this, X_("name"))
47         , _current_parent (0)
48 {
49         _name_proxy.connect (sigc::mem_fun (*this, &CairoWidget::on_name_changed));
50 }
51
52 CairoWidget::~CairoWidget ()
53 {
54         if (_parent_style_change) _parent_style_change.disconnect();
55 }
56
57 bool
58 CairoWidget::on_button_press_event (GdkEventButton*)
59 {
60         focus_handler();
61         return false;
62 }
63
64 bool
65 CairoWidget::on_expose_event (GdkEventExpose *ev)
66 {
67 #ifdef USE_CAIRO_IMAGE_SURFACE
68
69         if (!image_surface) {
70                 image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
71         }
72
73         Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (image_surface);
74 #else
75         Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context ();
76 #endif
77
78         cr->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
79         cr->clip_preserve ();
80
81         /* paint expose area the color of the parent window bg
82         */
83         
84         Gdk::Color bg (get_parent_bg());
85         
86         cr->set_source_rgb (bg.get_red_p(), bg.get_green_p(), bg.get_blue_p());
87         cr->fill ();
88
89         cairo_rectangle_t expose_area;
90         expose_area.x = ev->area.x;
91         expose_area.y = ev->area.y;
92         expose_area.width = ev->area.width;
93         expose_area.height = ev->area.height;
94
95         render (cr->cobj(), &expose_area);
96
97 #ifdef USE_CAIRO_IMAGE_SURFACE
98         image_surface->flush();
99         /* now blit our private surface back to the GDK one */
100
101         Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
102
103         cairo_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
104         cairo_context->clip ();
105         cairo_context->set_source (image_surface, 0, 0);
106         cairo_context->set_operator (Cairo::OPERATOR_SOURCE);
107         cairo_context->paint ();
108 #endif
109
110         return true;
111 }
112
113 /** Marks the widget as dirty, so that render () will be called on
114  *  the next GTK expose event.
115  */
116
117 void
118 CairoWidget::set_dirty ()
119 {
120         ENSURE_GUI_THREAD (*this, &CairoWidget::set_dirty);
121         queue_draw ();
122 }
123
124 /** Handle a size allocation.
125  *  @param alloc GTK allocation.
126  */
127 void
128 CairoWidget::on_size_allocate (Gtk::Allocation& alloc)
129 {
130         Gtk::EventBox::on_size_allocate (alloc);
131
132 #ifdef USE_CAIRO_IMAGE_SURFACE
133         image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, alloc.get_width(), alloc.get_height());
134 #endif
135
136         set_dirty ();
137 }
138
139 Gdk::Color
140 CairoWidget::get_parent_bg ()
141 {
142         Widget* parent;
143
144         parent = get_parent ();
145
146         while (parent) {
147                 void* p = g_object_get_data (G_OBJECT(parent->gobj()), has_cairo_widget_background_info);
148
149                 if (p) {
150                         Glib::RefPtr<Gtk::Style> style = parent->get_style();
151                         if (_current_parent != parent) {
152                                 if (_parent_style_change) _parent_style_change.disconnect();
153                                 _current_parent = parent;
154                                 _parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &CairoWidget::on_style_changed));
155                         }
156                         return style->get_bg (get_state());
157                 }
158
159                 if (!parent->get_has_window()) {
160                         parent = parent->get_parent();
161                 } else {
162                         break;
163                 }
164         }
165
166         if (parent && parent->get_has_window()) {
167                 if (_current_parent != parent) {
168                         if (_parent_style_change) _parent_style_change.disconnect();
169                         _current_parent = parent;
170                         _parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &CairoWidget::on_style_changed));
171                 }
172                 return parent->get_style ()->get_bg (parent->get_state());
173         }
174
175         return get_style ()->get_bg (get_state());
176 }
177
178 void
179 CairoWidget::set_active_state (Gtkmm2ext::ActiveState s)
180 {
181         if (_active_state != s) {
182                 _active_state = s;
183                 StateChanged ();
184         }
185 }
186
187 void
188 CairoWidget::set_visual_state (Gtkmm2ext::VisualState s)
189 {
190         if (_visual_state != s) {
191                 _visual_state = s;
192                 StateChanged ();
193         }
194 }
195
196 void
197 CairoWidget::set_active (bool yn)
198 {
199         /* this is an API simplification for buttons
200            that only use the Active and Normal states.
201         */
202
203         if (yn) {
204                 set_active_state (Gtkmm2ext::ExplicitActive);
205         } else {
206                 unset_active_state ();
207         }
208 }
209
210 void
211 CairoWidget::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
212 {
213         queue_draw();
214 }
215
216 void
217 CairoWidget::on_state_changed (Gtk::StateType)
218 {
219         /* this will catch GTK-level state changes from calls like
220            ::set_sensitive()
221         */
222
223         if (get_state() == Gtk::STATE_INSENSITIVE) {
224                 set_visual_state (Gtkmm2ext::VisualState (visual_state() | Gtkmm2ext::Insensitive));
225         } else {
226                 set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive));
227         }
228
229         queue_draw ();
230 }
231
232 void
233 CairoWidget::set_draw_background (bool yn)
234 {
235         _need_bg = yn;
236 }
237
238 void
239 CairoWidget::provide_background_for_cairo_widget (Gtk::Widget& w, const Gdk::Color& bg)
240 {
241         /* set up @w to be able to provide bg information to
242            any CairoWidgets that are packed inside it.
243         */
244
245         w.modify_bg (Gtk::STATE_NORMAL, bg);
246         w.modify_bg (Gtk::STATE_INSENSITIVE, bg);
247         w.modify_bg (Gtk::STATE_ACTIVE, bg);
248         w.modify_bg (Gtk::STATE_SELECTED, bg);
249
250         g_object_set_data (G_OBJECT(w.gobj()), has_cairo_widget_background_info, (void*) 0xfeedface);
251 }
252
253 void
254 CairoWidget::set_flat_buttons (bool yn)
255 {
256         _flat_buttons = yn;
257 }
258
259 void
260 CairoWidget::set_focus_handler (sigc::slot<void> s)
261 {
262         focus_handler = s;
263 }