1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "GxWheel.h"
20
21 #define P_(s) (s) // FIXME -> gettext
22
23 struct _GxWheelPrivate
24 {
25 int last_x;
26 };
27
28 static gboolean gx_wheel_draw (GtkWidget *widget, cairo_t *cr);
29 static void gx_wheel_get_preferred_width (GtkWidget *widget, gint *min_width, gint *natural_width);
30 static void gx_wheel_get_preferred_height (GtkWidget *widget, gint *min_height, gint *natural_height);
31 static void gx_wheel_size_request (GtkWidget *widget, gint *width, gint *height);
32 static gboolean gx_wheel_button_press (GtkWidget *widget, GdkEventButton *event);
33 static gboolean gx_wheel_pointer_motion (GtkWidget *widget, GdkEventMotion *event);
34
G_DEFINE_TYPE_WITH_PRIVATE(GxWheel,gx_wheel,GX_TYPE_REGLER)35 G_DEFINE_TYPE_WITH_PRIVATE(GxWheel, gx_wheel, GX_TYPE_REGLER)
36
37 static void gx_wheel_class_init(GxWheelClass *klass)
38 {
39 GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
40
41 widget_class->draw = gx_wheel_draw;
42 widget_class->get_preferred_width = gx_wheel_get_preferred_width;
43 widget_class->get_preferred_height = gx_wheel_get_preferred_height;
44 widget_class->button_press_event = gx_wheel_button_press;
45 widget_class->motion_notify_event = gx_wheel_pointer_motion;
46 widget_class->enter_notify_event = NULL;
47 widget_class->leave_notify_event = NULL;
48
49 gtk_widget_class_set_css_name(widget_class, "gx-wheel");
50
51 gtk_widget_class_install_style_property(
52 widget_class,
53 g_param_spec_int("framecount",
54 P_("framecount"),
55 P_("Number of frames in the animation specified by the gtkrc"),
56 -1, 250, -1,
57 GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
58 }
59
get_image_dimensions(GtkWidget * widget,GdkPixbuf * pb,GdkRectangle * rect,gint * frame_count)60 static void get_image_dimensions (GtkWidget *widget, GdkPixbuf *pb,
61 GdkRectangle *rect, gint *frame_count)
62 {
63 gtk_widget_style_get (widget, "framecount",
64 frame_count, NULL);
65
66 rect->width = gdk_pixbuf_get_width(pb);
67 rect->height = gdk_pixbuf_get_height(pb);
68
69 if (*frame_count >1)
70 rect->width = (rect->width / *frame_count);
71 if (*frame_count == 0) {// rc directs to assume square frames
72 *frame_count = rect->width / rect->height;
73 rect->width = rect->height;
74
75 }
76 }
77
gx_wheel_draw(GtkWidget * widget,cairo_t * cr)78 static gboolean gx_wheel_draw (GtkWidget *widget, cairo_t *cr)
79 {
80 g_assert(GX_IS_WHEEL(widget));
81 GxRegler *regler = GX_REGLER(widget);
82 GdkRectangle image_rect, value_rect;
83 gint fcount, findex;
84 gdouble wheelstate;
85 gtk_widget_style_get (widget, "framecount", &fcount, NULL);
86
87 GdkPixbuf *wb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
88 "wheel_back", -1,
89 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
90 if (fcount > -1) {
91
92 wheelstate = _gx_regler_get_step_pos(regler, 1);
93 get_image_dimensions (widget, wb, &image_rect, &fcount);
94 _gx_regler_get_positions(regler, &image_rect, &value_rect, false);
95
96 fcount--; // zero based index
97 findex = (int)(fcount * wheelstate);
98 gdk_cairo_set_source_pixbuf (cr, wb, image_rect.x-(image_rect.width * findex), image_rect.y);
99 cairo_rectangle(cr, image_rect.x, image_rect.y,image_rect.width, image_rect.height);
100 cairo_fill(cr);
101 _gx_regler_display_value(regler, cr, &value_rect);
102 } else {
103
104 GdkPixbuf *ws = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
105 "wheel_fringe", -1,
106 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
107 GdkPixbuf *wp = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
108 "wheel_pointer", -1,
109 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
110
111 image_rect.width = gdk_pixbuf_get_width(wb);
112 image_rect.height = gdk_pixbuf_get_height(wb);
113
114 gint step = gdk_pixbuf_get_width(ws) / 2;
115 wheelstate = _gx_regler_get_step_pos(regler, step);
116 _gx_regler_get_positions(regler, &image_rect, &value_rect, false);
117 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
118 int smoth_pointer = 0;
119 if (wheelstate > (gtk_adjustment_get_upper(adj) - gtk_adjustment_get_lower(adj))) {
120 smoth_pointer = -4;
121 }
122 gdk_cairo_set_source_pixbuf(cr, wb, image_rect.x, image_rect.y);
123 cairo_paint(cr);
124 //- ((int)(wheelstate) + image_rect.width)
125 gdk_cairo_set_source_pixbuf(cr, ws, image_rect.x+wheelstate*0.6-4*image_rect.width, image_rect.y);
126 cairo_rectangle(cr, image_rect.x, image_rect.y, image_rect.width, image_rect.height);
127 cairo_fill(cr);
128 gdk_cairo_set_source_pixbuf(cr, wp, image_rect.x+smoth_pointer+wheelstate*0.4, image_rect.y);
129 cairo_rectangle(cr, image_rect.x+smoth_pointer+wheelstate*0.4, image_rect.y,
130 gdk_pixbuf_get_width(wp), image_rect.height);
131 cairo_fill(cr);
132 _gx_regler_display_value(regler, cr, &value_rect);
133
134 g_clear_object(&ws);
135 g_clear_object(&wp);
136 }
137
138 g_clear_object(&wb);
139 return TRUE;
140 }
141
gx_wheel_get_preferred_width(GtkWidget * widget,gint * min_width,gint * natural_width)142 static void gx_wheel_get_preferred_width (GtkWidget *widget, gint *min_width, gint *natural_width)
143 {
144 gint width, height;
145 gx_wheel_size_request(widget, &width, &height);
146
147 if (min_width) {
148 *min_width = width;
149 }
150 if (natural_width) {
151 *natural_width = width;
152 }
153 }
154
gx_wheel_get_preferred_height(GtkWidget * widget,gint * min_height,gint * natural_height)155 static void gx_wheel_get_preferred_height (GtkWidget *widget, gint *min_height, gint *natural_height)
156 {
157 gint width, height;
158 gx_wheel_size_request(widget, &width, &height);
159
160 if (min_height) {
161 *min_height = height;
162 }
163 if (natural_height) {
164 *natural_height = height;
165 }
166 }
167
gx_wheel_size_request(GtkWidget * widget,gint * width,gint * height)168 static void gx_wheel_size_request (GtkWidget *widget, gint *width, gint *height)
169 {
170 g_assert(GX_IS_WHEEL(widget));
171 gint fcount;
172 GdkRectangle rect;
173
174 GdkPixbuf *wb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
175 "wheel_back", -1,
176 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
177 get_image_dimensions (widget, wb, &rect, &fcount);
178 *width = rect.width;
179 *height = rect.height;
180 _gx_regler_calc_size_request(GX_REGLER(widget), width, height, TRUE);
181 g_clear_object(&wb);
182 }
183
wheel_set_from_pointer(GtkWidget * widget,gdouble x,gdouble y,gboolean drag,int state,int button,GdkEventButton * event)184 static gboolean wheel_set_from_pointer(GtkWidget *widget, gdouble x, gdouble y, gboolean drag, int state, int button, GdkEventButton *event)
185 {
186 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
187 GdkPixbuf *wb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
188 "wheel_back", -1,
189 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
190 GdkRectangle image_rect, value_rect;
191 GxWheel *wheel = GX_WHEEL(widget);
192 GxWheelPrivate *priv = wheel->priv;
193 gint fcount;
194 get_image_dimensions (widget, wb, &image_rect, &fcount);
195 GtkAllocation allocation;
196 gtk_widget_get_allocation(widget, &allocation);
197 x += allocation.x;
198 y += allocation.y;
199
200 _gx_regler_get_positions(GX_REGLER(widget), &image_rect, &value_rect, false);
201 if (!drag) {
202 GdkRectangle *rect = NULL;
203 if (_approx_in_rectangle(x, y, &image_rect)) {
204 if (button == 3) {
205 rect = &image_rect;
206 }
207 } else if (_approx_in_rectangle(x, y, &value_rect)) {
208 if (button == 1 || button == 3) {
209 rect = &value_rect;
210 } else {
211 return FALSE;
212 }
213 } else {
214 return FALSE;
215 }
216 if (rect) {
217 gboolean ret;
218 g_signal_emit_by_name(GX_REGLER(widget), "value-entry", rect, event, &ret);
219 return FALSE;
220 }
221 }
222
223 double value;
224 gdouble lower = gtk_adjustment_get_lower(adj);
225 gdouble upper = gtk_adjustment_get_upper(adj);
226 gdouble adj_value = gtk_adjustment_get_value(adj);
227 if (!drag) {
228 priv->last_x = x;
229 if (event && event->type == GDK_2BUTTON_PRESS) {
230 const int frame = 5;
231 value = lower + ((x - (image_rect.x+frame)) * (upper - lower)) / (image_rect.width-2*frame);
232 gtk_range_set_value(GTK_RANGE(widget), value);
233 }
234 return TRUE;
235 }
236 int mode = ((state & GDK_CONTROL_MASK) == 0);
237 const double scaling = 0.01;
238 double scal = (mode ? scaling : scaling*0.1);
239 value = adj_value + (((x - priv->last_x) * scal) * (upper - lower));
240 priv->last_x = x;
241 if (adj_value != value)
242 gtk_range_set_value(GTK_RANGE(widget), value);
243 g_clear_object(&wb);
244 return TRUE;
245 }
246
gx_wheel_button_press(GtkWidget * widget,GdkEventButton * event)247 static gboolean gx_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
248 {
249 g_assert(GX_IS_WHEEL(widget));
250 if (event->button != 1 && event->button != 3) {
251 return FALSE;
252 }
253 gtk_widget_grab_focus(widget);
254 if (wheel_set_from_pointer(widget, event->x, event->y, FALSE, event->state, event->button, event)) {
255 gtk_grab_add(widget);
256 }
257 return FALSE;
258 }
259
260 /****************************************************************
261 ** set the value from mouse movement
262 */
263
gx_wheel_pointer_motion(GtkWidget * widget,GdkEventMotion * event)264 static gboolean gx_wheel_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
265 {
266 g_assert(GX_IS_WHEEL(widget));
267 gdk_event_request_motions (event);
268 if (!gtk_widget_has_grab(widget)) {
269 return FALSE;
270 }
271 wheel_set_from_pointer(widget, event->x, event->y, TRUE, event->state, 0, NULL);
272 return FALSE;
273 }
274
gx_wheel_init(GxWheel * wheel)275 static void gx_wheel_init(GxWheel *wheel)
276 {
277 wheel->priv = (GxWheelPrivate*)gx_wheel_get_instance_private(wheel);
278 }
279