add a focus handling callback so that all button press events on CairoWidgets will...
[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         cairo_t* cr = gdk_cairo_create (get_window ()->gobj());
68         cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
69         cairo_clip_preserve (cr);
70
71         /* paint expose area the color of the parent window bg
72         */
73         
74         Gdk::Color bg (get_parent_bg());
75         
76         cairo_set_source_rgb (cr, bg.get_red_p(), bg.get_green_p(), bg.get_blue_p());
77         cairo_fill (cr);
78
79         cairo_rectangle_t expose_area;
80         expose_area.x = ev->area.x;
81         expose_area.y = ev->area.y;
82         expose_area.width = ev->area.width;
83         expose_area.height = ev->area.height;
84
85         render (cr, &expose_area);
86
87         cairo_destroy (cr);
88
89         return true;
90 }
91
92 /** Marks the widget as dirty, so that render () will be called on
93  *  the next GTK expose event.
94  */
95
96 void
97 CairoWidget::set_dirty ()
98 {
99         ENSURE_GUI_THREAD (*this, &CairoWidget::set_dirty);
100         queue_draw ();
101 }
102
103 /** Handle a size allocation.
104  *  @param alloc GTK allocation.
105  */
106 void
107 CairoWidget::on_size_allocate (Gtk::Allocation& alloc)
108 {
109         Gtk::EventBox::on_size_allocate (alloc);
110
111         set_dirty ();
112 }
113
114 Gdk::Color
115 CairoWidget::get_parent_bg ()
116 {
117         Widget* parent;
118
119         parent = get_parent ();
120
121         while (parent) {
122                 void* p = g_object_get_data (G_OBJECT(parent->gobj()), has_cairo_widget_background_info);
123
124                 if (p) {
125                         Glib::RefPtr<Gtk::Style> style = parent->get_style();
126                         if (_current_parent != parent) {
127                                 if (_parent_style_change) _parent_style_change.disconnect();
128                                 _current_parent = parent;
129                                 _parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &CairoWidget::on_style_changed));
130                         }
131                         return style->get_bg (get_state());
132                 }
133
134                 if (!parent->get_has_window()) {
135                         parent = parent->get_parent();
136                 } else {
137                         break;
138                 }
139         }
140
141         if (parent && parent->get_has_window()) {
142                 if (_current_parent != parent) {
143                         if (_parent_style_change) _parent_style_change.disconnect();
144                         _current_parent = parent;
145                         _parent_style_change = parent->signal_style_changed().connect (mem_fun (*this, &CairoWidget::on_style_changed));
146                 }
147                 return parent->get_style ()->get_bg (parent->get_state());
148         }
149
150         return get_style ()->get_bg (get_state());
151 }
152
153 void
154 CairoWidget::set_active_state (Gtkmm2ext::ActiveState s)
155 {
156         if (_active_state != s) {
157                 _active_state = s;
158                 StateChanged ();
159         }
160 }
161
162 void
163 CairoWidget::set_visual_state (Gtkmm2ext::VisualState s)
164 {
165         if (_visual_state != s) {
166                 _visual_state = s;
167                 StateChanged ();
168         }
169 }
170
171 void
172 CairoWidget::set_active (bool yn)
173 {
174         /* this is an API simplification for buttons
175            that only use the Active and Normal states.
176         */
177
178         if (yn) {
179                 set_active_state (Gtkmm2ext::ExplicitActive);
180         } else {
181                 unset_active_state ();
182         }
183 }
184
185 void
186 CairoWidget::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
187 {
188         queue_draw();
189 }
190
191 void
192 CairoWidget::on_state_changed (Gtk::StateType)
193 {
194         /* this will catch GTK-level state changes from calls like
195            ::set_sensitive()
196         */
197
198         if (get_state() == Gtk::STATE_INSENSITIVE) {
199                 set_visual_state (Gtkmm2ext::VisualState (visual_state() | Gtkmm2ext::Insensitive));
200         } else {
201                 set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive));
202         }
203
204         queue_draw ();
205 }
206
207 void
208 CairoWidget::set_draw_background (bool yn)
209 {
210         _need_bg = yn;
211 }
212
213 void
214 CairoWidget::provide_background_for_cairo_widget (Gtk::Widget& w, const Gdk::Color& bg)
215 {
216         /* set up @w to be able to provide bg information to
217            any CairoWidgets that are packed inside it.
218         */
219
220         w.modify_bg (Gtk::STATE_NORMAL, bg);
221         w.modify_bg (Gtk::STATE_INSENSITIVE, bg);
222         w.modify_bg (Gtk::STATE_ACTIVE, bg);
223         w.modify_bg (Gtk::STATE_SELECTED, bg);
224
225         g_object_set_data (G_OBJECT(w.gobj()), has_cairo_widget_background_info, (void*) 0xfeedface);
226 }
227
228 void
229 CairoWidget::set_flat_buttons (bool yn)
230 {
231         _flat_buttons = yn;
232 }
233
234 void
235 CairoWidget::set_focus_handler (sigc::slot<void> s)
236 {
237         focus_handler = s;
238 }