1 /*
2  * Copyright (C) 2010-2007 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <string>
21 #include <iostream>
22 
23 
24 #include <gtkmm/main.h>
25 
26 #include "widgets/stateful_button.h"
27 
28 using namespace Gtk;
29 using namespace Glib;
30 using namespace ArdourWidgets;
31 using namespace std;
32 
StateButton()33 StateButton::StateButton ()
34         : visual_state (0)
35         , _self_managed (false)
36         , _is_realized (false)
37         , style_changing (false)
38         , state_before_prelight (Gtk::STATE_NORMAL)
39         , is_toggle (false)
40 {
41 }
42 
43 void
set_visual_state(int n)44 StateButton::set_visual_state (int n)
45 {
46 	if (!_is_realized) {
47 		/* not yet realized */
48 		visual_state = n;
49 		return;
50 	}
51 
52 	if (n == visual_state) {
53 		return;
54 	}
55 
56 	string name = get_widget_name ();
57 	name = name.substr (0, name.find_last_of ('-'));
58 
59 	switch (n) {
60 	case 0:
61 		/* relax */
62 		break;
63 	case 1:
64                 name += "-active";
65 		break;
66 
67 	case 2:
68 		name += "-alternate";
69 		break;
70 
71         case 3:
72                 name += "-alternate2";
73                 break;
74 	}
75 
76 	set_widget_name (name);
77 	visual_state = n;
78 }
79 
80 void
avoid_prelight_on_style_changed(const Glib::RefPtr<Gtk::Style> &,GtkWidget * widget)81 StateButton::avoid_prelight_on_style_changed (const Glib::RefPtr<Gtk::Style>& /* old_style */,  GtkWidget* widget)
82 {
83         /* don't go into an endless recursive loop if we're changing
84            the style in response to an existing style change.
85         */
86 
87         if (style_changing) {
88                 return;
89         }
90 
91         if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT) {
92 
93                 /* avoid PRELIGHT: make sure that the prelight colors in this new style match
94                    the colors of the new style in whatever state we were in
95                    before we switched to prelight.
96                 */
97 
98                 GtkRcStyle* rcstyle = gtk_widget_get_modifier_style (widget);
99                 GtkStyle* style = gtk_widget_get_style (widget);
100 
101                 rcstyle->fg[GTK_STATE_PRELIGHT] = style->fg[state_before_prelight];
102                 rcstyle->bg[GTK_STATE_PRELIGHT] = style->bg[state_before_prelight];
103                 rcstyle->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags) (GTK_RC_FG|GTK_RC_BG);
104 
105                 style_changing = true;
106                 g_object_ref (rcstyle);
107                 gtk_widget_modify_style (widget, rcstyle);
108 
109                 Widget* child = get_child_widget();
110                 if (child) {
111                         gtk_widget_modify_style (GTK_WIDGET(child->gobj()), rcstyle);
112                 }
113 
114 
115                 g_object_unref (rcstyle);
116                 style_changing = false;
117         }
118 }
119 
120 void
avoid_prelight_on_state_changed(Gtk::StateType old_state,GtkWidget * widget)121 StateButton::avoid_prelight_on_state_changed (Gtk::StateType old_state, GtkWidget* widget)
122 {
123         GtkStateType state = gtk_widget_get_state (widget);
124 
125         if (state == GTK_STATE_PRELIGHT) {
126 
127                 state_before_prelight = old_state;
128 
129 
130                 /* avoid PRELIGHT when currently ACTIVE:
131                    if we just went into PRELIGHT, make sure that the colors
132                    match those of whatever state we were in before.
133                 */
134 
135                 GtkRcStyle* rcstyle = gtk_widget_get_modifier_style (widget);
136                 GtkStyle* style = gtk_widget_get_style (widget);
137 
138                 rcstyle->fg[GTK_STATE_PRELIGHT] = style->fg[old_state];
139                 rcstyle->bg[GTK_STATE_PRELIGHT] = style->bg[old_state];
140                 rcstyle->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags) (GTK_RC_FG|GTK_RC_BG);
141 
142                 g_object_ref (rcstyle);
143                 gtk_widget_modify_style (widget, rcstyle);
144 
145                 Widget* child = get_child_widget ();
146 
147                 if (child) {
148                         gtk_widget_modify_style (GTK_WIDGET(child->gobj()), rcstyle);
149                 }
150 
151                 g_object_unref (rcstyle);
152 
153         }
154 }
155 
156 /* ----------------------------------------------------------------- */
157 
StatefulToggleButton()158 StatefulToggleButton::StatefulToggleButton ()
159 {
160         is_toggle = true;
161 }
162 
StatefulToggleButton(const std::string & label)163 StatefulToggleButton::StatefulToggleButton (const std::string& label)
164         : ToggleButton (label)
165 {
166         is_toggle = true;
167 }
168 
169 void
on_realize()170 StatefulToggleButton::on_realize ()
171 {
172 	ToggleButton::on_realize ();
173 
174 	_is_realized = true;
175 	visual_state++; // to force transition
176 	set_visual_state (visual_state - 1);
177 }
178 
179 void
on_realize()180 StatefulButton::on_realize ()
181 {
182 	Button::on_realize ();
183 
184 	_is_realized = true;
185 	visual_state++; // to force transition
186 	set_visual_state (visual_state - 1);
187 }
188 
189 void
on_toggled()190 StatefulToggleButton::on_toggled ()
191 {
192 	if (!_self_managed) {
193 		if (get_active()) {
194                         set_state (Gtk::STATE_ACTIVE);
195 		} else {
196                         set_state (Gtk::STATE_NORMAL);
197 		}
198 	}
199 }
200 
201 
202 void
on_style_changed(const Glib::RefPtr<Gtk::Style> & style)203 StatefulToggleButton::on_style_changed (const Glib::RefPtr<Gtk::Style>& style)
204 {
205         avoid_prelight_on_style_changed (style, GTK_WIDGET(gobj()));
206         Button::on_style_changed (style);
207 }
208 
209 void
on_state_changed(Gtk::StateType old_state)210 StatefulToggleButton::on_state_changed (Gtk::StateType old_state)
211 {
212         avoid_prelight_on_state_changed (old_state, GTK_WIDGET(gobj()));
213         Button::on_state_changed (old_state);
214 }
215 
216 Widget*
get_child_widget()217 StatefulToggleButton::get_child_widget ()
218 {
219         return get_child();
220 }
221 
222 void
set_widget_name(const std::string & name)223 StatefulToggleButton::set_widget_name (const std::string& name)
224 {
225 	set_name (name);
226 	Widget* w = get_child();
227 
228 	if (w) {
229 		w->set_name (name);
230 	}
231 }
232 
233 /*--------------------------------------------- */
234 
StatefulButton()235 StatefulButton::StatefulButton ()
236 {
237 }
238 
StatefulButton(const std::string & label)239 StatefulButton::StatefulButton (const std::string& label)
240         : Button (label)
241 {
242 }
243 
244 void
on_style_changed(const Glib::RefPtr<Gtk::Style> & style)245 StatefulButton::on_style_changed (const Glib::RefPtr<Gtk::Style>& style)
246 {
247         avoid_prelight_on_style_changed (style, GTK_WIDGET(gobj()));
248         Button::on_style_changed (style);
249 }
250 
251 void
on_state_changed(Gtk::StateType old_state)252 StatefulButton::on_state_changed (Gtk::StateType old_state)
253 {
254         avoid_prelight_on_state_changed (old_state, GTK_WIDGET(gobj()));
255         Button::on_state_changed (old_state);
256 }
257 
258 Widget*
get_child_widget()259 StatefulButton::get_child_widget ()
260 {
261         return get_child();
262 }
263 
264 void
set_widget_name(const std::string & name)265 StatefulButton::set_widget_name (const std::string& name)
266 {
267 	set_name (name);
268 	Widget* w = get_child();
269 
270 	if (w) {
271 		w->set_name (name);
272 	}
273 }
274