1/*
2 *  Notes - panel plugin for Xfce Desktop Environment
3 *  Copyright (c) 2010  Mike Massonnet <mmassonnet@xfce.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 Library General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20const double M_PI = 3.14159265358979323846;
21
22namespace Xnp {
23
24	public abstract class IconButton : Gtk.EventBox {
25
26		protected bool active = false;
27
28		public signal void clicked ();
29
30		construct {
31			((Gtk.Widget)this).name = "notes-icon-button";
32			set_visible_window (false);
33			set_above_child (true);
34			set_size_request (22, 22);
35
36			enter_notify_event.connect (on_enter_notify_event);
37			leave_notify_event.connect (on_leave_notify_event);
38			button_release_event.connect (on_button_release_event);
39		}
40
41		protected abstract void draw_icon (Cairo.Context cr, int width, int height);
42
43		protected void set_widget_source_color (Cairo.Context cr) {
44			var style_context = get_style_context ();
45
46			if (sensitive && active) {
47				Gdk.cairo_set_source_rgba (cr, style_context.get_color (Gtk.StateFlags.PRELIGHT));
48			}
49			else if (sensitive && !active)
50				Gdk.cairo_set_source_rgba (cr, style_context.get_color (Gtk.StateFlags.NORMAL));
51			else if (!sensitive)
52				Gdk.cairo_set_source_rgba (cr, style_context.get_color (Gtk.StateFlags.INSENSITIVE));
53		}
54
55		public override void add (Gtk.Widget widget) {
56			warning ("This object doesn't allow packing child widgets.");
57		}
58
59		public override bool draw (Cairo.Context cr) {
60			int width = get_allocated_width ();
61			int height = get_allocated_height ();
62			var style_context = get_style_context ();
63
64			style_context.save ();
65			style_context.add_class (Gtk.STYLE_CLASS_BUTTON);
66			style_context.render_frame (cr, 0, 0, width, height);
67			style_context.render_background (cr, 0, 0, width, height);
68			style_context.restore ();
69
70			cr.save ();
71			cr.translate (2, 2);
72			draw_icon (cr, width - 4, height - 4);
73			cr.restore ();
74
75			return false;
76		}
77
78		private bool on_enter_notify_event (Gdk.EventCrossing event) {
79			active = true;
80			get_window ().invalidate_rect (null, false);
81			return false;
82		}
83
84		private bool on_leave_notify_event (Gdk.EventCrossing event) {
85			active = false;
86			get_window ().invalidate_rect (null, false);
87			return false;
88		}
89
90		private bool on_button_release_event (Gdk.EventButton event) {
91			if (event.button != 1)
92				return false;
93
94			int cur_x = (int)event.x;
95			int cur_y = (int)event.y;
96			int width, height;
97			get_size_request (out width, out height);
98
99			if (cur_x >= 0 && cur_x < width && cur_y >= 0 && cur_y < height)
100				clicked ();
101
102			return false;
103		}
104
105	}
106
107	public enum TitleBarButtonType {
108		EMPTY,
109		CLOSE,
110		LEFT_ARROW,
111		RIGHT_ARROW,
112		REFRESH,
113	}
114
115	public class TitleBarButton : IconButton {
116
117		public TitleBarButtonType icon_type { default = TitleBarButtonType.EMPTY; get; construct set; }
118
119		public TitleBarButton (TitleBarButtonType icon_type) {
120			Object (icon_type: icon_type);
121		}
122
123		protected override void draw_icon (Cairo.Context cr, int width, int height) {
124			switch (icon_type) {
125				case TitleBarButtonType.CLOSE:
126					draw_close_button (cr, width, height);
127					break;
128				case TitleBarButtonType.LEFT_ARROW:
129					draw_left_arrow_button (cr, width, height);
130					break;
131				case TitleBarButtonType.RIGHT_ARROW:
132					draw_right_arrow_button (cr, width, height);
133					break;
134				case TitleBarButtonType.REFRESH:
135					draw_refresh_button (cr, width, height);
136					break;
137				default:
138					break;
139			}
140		}
141
142		private void draw_left_arrow_button (Cairo.Context cr, int width, int height) {
143			int border = 4;
144			int x1 = border;
145			int x2 = width - border;
146			int y1 = border;
147			int y2 = height - border;
148			if (x2 <= x1 || y2 <= y1) {
149				return;
150			}
151
152			cr.set_line_cap (Cairo.LineCap.ROUND);
153
154			for (int i = 0; i < 2; i++) {
155				if (i == 0) {
156					cr.set_source_rgba (1, 1, 1, active ? 0.4 : 0.2);
157					cr.set_line_width (4);
158				}
159				else {
160					set_widget_source_color (cr);
161					cr.set_line_width (2.66);
162				}
163				cr.move_to (x1, height / 2);
164				cr.line_to (x2, height / 2);
165				cr.move_to (width / 2, y1);
166				cr.line_to (x1, height / 2);
167				cr.line_to (width / 2, y2);
168				cr.stroke ();
169			}
170		}
171
172		private void draw_right_arrow_button (Cairo.Context cr, int width, int height) {
173			int border = 4;
174			int x1 = border;
175			int x2 = width - border;
176			int y1 = border;
177			int y2 = height - border;
178			if (x2 <= x1 || y2 <= y1) {
179				return;
180			}
181
182			cr.set_line_cap (Cairo.LineCap.ROUND);
183
184			for (int i = 0; i < 2; i++) {
185				if (i == 0) {
186					cr.set_source_rgba (1, 1, 1, active ? 0.4 : 0.2);
187					cr.set_line_width (4);
188				}
189				else {
190					set_widget_source_color (cr);
191					cr.set_line_width (2.66);
192				}
193				cr.move_to (x1, height / 2);
194				cr.line_to (x2, height / 2);
195				cr.move_to (width / 2, y1);
196				cr.line_to (x2, height / 2);
197				cr.line_to (width / 2, y2);
198				cr.stroke ();
199			}
200		}
201
202		private void draw_close_button (Cairo.Context cr, int width, int height) {
203			int border = 4;
204			int x1 = border;
205			int x2 = width - border;
206			int y1 = border;
207			int y2 = height - border;
208			if (x2 <= x1 || y2 <= y1) {
209				return;
210			}
211
212			cr.set_line_cap (Cairo.LineCap.ROUND);
213
214			for (int i = 0; i < 2; i++) {
215				if (i == 0) {
216					cr.set_source_rgba (1, 1, 1, active ? 0.4 : 0.2);
217					cr.set_line_width (4);
218				}
219				else {
220					set_widget_source_color (cr);
221					cr.set_line_width (2.66);
222				}
223				cr.move_to (x1, y1);
224				cr.line_to (x2, y2);
225				cr.move_to (x2, y1);
226				cr.line_to (x1, y2);
227				cr.stroke ();
228			}
229		}
230
231		private void draw_refresh_button (Cairo.Context cr, int width, int height) {
232			int border = 6;
233			int x1 = border;
234			int x2 = width - border;
235			int y1 = border;
236			int y2 = height - border;
237			if (x2 <= x1 || y2 <= y1) {
238				return;
239			}
240
241			cr.set_line_cap (Cairo.LineCap.ROUND);
242
243			for (int j = 0; j < 2; j++) {
244				for (int i = 0; i < 2; i++) {
245					if (i == 0) {
246						cr.set_source_rgba (1, 1, 1, active ? 0.4 : 0.2);
247						cr.set_line_width (4);
248					}
249					else {
250						set_widget_source_color (cr);
251						cr.set_line_width (2.44);
252					}
253					cr.save ();
254					cr.translate (x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2);
255					if (j == 0) {
256						cr.rotate (-M_PI / 16.0);
257					}
258					else {
259						cr.rotate ((15.0 * M_PI) / 16.0);
260					}
261					cr.arc (0, 0, x2 - x1, (5.0 * M_PI) / 16.0, M_PI);
262					var r = (x2 - x1) / 2.0;
263					cr.line_to (-r * 2.0, (3.0 * r) / 2.0);
264					cr.move_to (-r * 2.0, 0.0);
265					cr.line_to (-r, r / 2.0);
266					cr.stroke ();
267					cr.restore ();
268				}
269			}
270		}
271	}
272
273}
274