98c7a307ca1b03a26d8665474865ff93be874474
[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 bool CairoWidget::_widget_prelight = true;
29
30 static void noop() { }
31 sigc::slot<void> CairoWidget::focus_handler (sigc::ptr_fun (noop));
32
33 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 (?)
34 {
35         float r = col.get_red_p ();
36         float g = col.get_green_p ();
37         float b = col.get_blue_p ();
38         
39         cairo_set_source_rgba(cr, r, g, b, a);
40 }
41
42 CairoWidget::CairoWidget ()
43         : _active_state (Gtkmm2ext::Off)
44         , _visual_state (Gtkmm2ext::NoVisualState)
45         , _need_bg (true)
46         , _grabbed (false)
47         , _name_proxy (this, X_("name"))
48         , _current_parent (0)
49 {
50         _name_proxy.connect (sigc::mem_fun (*this, &CairoWidget::on_name_changed));
51 }
52
53 CairoWidget::~CairoWidget ()
54 {
55         if (_parent_style_change) _parent_style_change.disconnect();
56 }
57
58 bool
59 CairoWidget::on_button_press_event (GdkEventButton*)
60 {
61         focus_handler();
62         return false;
63 }
64
65 bool
66 CairoWidget::on_expose_event (GdkEventExpose *ev)
67 {
68 #ifdef USE_CAIRO_IMAGE_SURFACE
69
70         if (!image_surface) {
71                 image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
72         }
73
74         Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (image_surface);
75 #else
76         Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context ();
77 #endif
78
79         cr->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
80         cr->clip_preserve ();
81
82         /* paint expose area the color of the parent window bg
83         */
84         
85         Gdk::Color bg (get_parent_bg());
86         
87         cr->set_source_rgb (bg.get_red_p(), bg.get_green_p(), bg.get_blue_p());
88         cr->fill ();
89
90         cairo_rectangle_t expose_area;
91         expose_area.x = ev->area.x;
92         expose_area.y = ev->area.y;
93         expose_area.width = ev->area.width;
94         expose_area.height = ev->area.height;
95
96         render (cr->cobj(), &expose_area);
97
98 #ifdef USE_CAIRO_IMAGE_SURFACE
99         image_surface->flush();
100         /* now blit our private surface back to the GDK one */
101
102         Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
103
104         cairo_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
105         cairo_context->clip ();
106         cairo_context->set_source (image_surface, 0, 0);
107         cairo_context->set_operator (Cairo::OPERATOR_SOURCE);
108         cairo_context->paint ();
109 #endif
110
111         return true;
112 }
113
114 /** Marks the widget as dirty, so that render () will be called on
115  *  the next GTK expose event.
116  */
117
118 void
119 CairoWidget::set_dirty ()
120 {
121         ENSURE_GUI_THREAD (*this, &CairoWidget::set_dirty);
122         queue_draw ();
123 }
124
125 /** Handle a size allocation.
126  *  @param alloc GTK allocation.
127  */
128 void
129 CairoWidget::on_size_allocate (Gtk::Allocation& alloc)
130 {
131         Gtk::EventBox::on_size_allocate (alloc);
132
133 #ifdef USE_CAIRO_IMAGE_SURFACE
134         image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, alloc.get_width(), alloc.get_height());
135 #endif
136
137         set_dirty ();
138 }
139
140 Gdk::Color
141 CairoWidget::get_parent_bg ()
142 {
143         Widget* parent;
144
145         parent = get_parent ();
146
147         while (parent) {
148                 void* p = g_object_get_data (G_OBJECT(parent->gobj()), has_cairo_widget_background_info);
149
150                 if (p) {
151                         Glib::RefPtr<Gtk::Style> style = parent->get_style();
152                         if (_current_parent != parent) {
153                                 if (_parent_style_change) _parent_style_change.disconnect();
154                                 _current_parent = parent;
155                                 _parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &CairoWidget::on_style_changed));
156                         }
157                         return style->get_bg (get_state());
158                 }
159
160                 if (!parent->get_has_window()) {
161                         parent = parent->get_parent();
162                 } else {
163                         break;
164                 }
165         }
166
167         if (parent && parent->get_has_window()) {
168                 if (_current_parent != parent) {
169                         if (_parent_style_change) _parent_style_change.disconnect();
170                         _current_parent = parent;
171                         _parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &CairoWidget::on_style_changed));
172                 }
173                 return parent->get_style ()->get_bg (parent->get_state());
174         }
175
176         return get_style ()->get_bg (get_state());
177 }
178
179 void
180 CairoWidget::set_active_state (Gtkmm2ext::ActiveState s)
181 {
182         if (_active_state != s) {
183                 _active_state = s;
184                 StateChanged ();
185         }
186 }
187
188 void
189 CairoWidget::set_visual_state (Gtkmm2ext::VisualState s)
190 {
191         if (_visual_state != s) {
192                 _visual_state = s;
193                 StateChanged ();
194         }
195 }
196
197 void
198 CairoWidget::set_active (bool yn)
199 {
200         /* this is an API simplification for buttons
201            that only use the Active and Normal states.
202         */
203
204         if (yn) {
205                 set_active_state (Gtkmm2ext::ExplicitActive);
206         } else {
207                 unset_active_state ();
208         }
209 }
210
211 void
212 CairoWidget::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
213 {
214         queue_draw();
215 }
216
217 void
218 CairoWidget::on_state_changed (Gtk::StateType)
219 {
220         /* this will catch GTK-level state changes from calls like
221            ::set_sensitive()
222         */
223
224         if (get_state() == Gtk::STATE_INSENSITIVE) {
225                 set_visual_state (Gtkmm2ext::VisualState (visual_state() | Gtkmm2ext::Insensitive));
226         } else {
227                 set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive));
228         }
229
230         queue_draw ();
231 }
232
233 void
234 CairoWidget::set_draw_background (bool yn)
235 {
236         _need_bg = yn;
237 }
238
239 void
240 CairoWidget::provide_background_for_cairo_widget (Gtk::Widget& w, const Gdk::Color& bg)
241 {
242         /* set up @w to be able to provide bg information to
243            any CairoWidgets that are packed inside it.
244         */
245
246         w.modify_bg (Gtk::STATE_NORMAL, bg);
247         w.modify_bg (Gtk::STATE_INSENSITIVE, bg);
248         w.modify_bg (Gtk::STATE_ACTIVE, bg);
249         w.modify_bg (Gtk::STATE_SELECTED, bg);
250
251         g_object_set_data (G_OBJECT(w.gobj()), has_cairo_widget_background_info, (void*) 0xfeedface);
252 }
253
254 void
255 CairoWidget::set_flat_buttons (bool yn)
256 {
257         _flat_buttons = yn;
258 }
259
260 void
261 CairoWidget::set_widget_prelight (bool yn)
262 {
263         _widget_prelight = yn;
264 }
265
266 void
267 CairoWidget::set_focus_handler (sigc::slot<void> s)
268 {
269         focus_handler = s;
270 }