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 "GxLevelSlider.h"
20 #include "log_meter.h"
21 
22 #define P_(s) (s)   // FIXME -> gettext
23 
24 static gboolean gx_level_slider_draw (GtkWidget *widget, cairo_t *cr);
25 static void gx_level_slider_get_preferred_width (GtkWidget *widget, gint *min_width, gint *natural_width);
26 static void gx_level_slider_get_preferred_height (GtkWidget *widget, gint *min_height, gint *natural_height);
27 static void gx_level_slider_size_request (GtkWidget *widget, gint *width, gint *height);
28 static gboolean gx_level_slider_button_press (GtkWidget *widget, GdkEventButton *event);
29 static gboolean gx_level_slider_pointer_motion (GtkWidget *widget, GdkEventMotion *event);
30 static const char *get_stock_id(GtkWidget *widget);
31 
G_DEFINE_TYPE(GxLevelSlider,gx_level_slider,GX_TYPE_VSLIDER)32 G_DEFINE_TYPE(GxLevelSlider, gx_level_slider, GX_TYPE_VSLIDER)
33 
34 static void gx_level_slider_class_init(GxLevelSliderClass *klass)
35 {
36 	GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
37 
38 	widget_class->draw = gx_level_slider_draw;
39 	widget_class->get_preferred_width = gx_level_slider_get_preferred_width;
40 	widget_class->get_preferred_height = gx_level_slider_get_preferred_height;
41 	widget_class->button_press_event = gx_level_slider_button_press;
42 	widget_class->motion_notify_event = gx_level_slider_pointer_motion;
43 	widget_class->enter_notify_event = NULL;
44 	widget_class->leave_notify_event = NULL;
45 	klass->parent_class.stock_id = "levelslider";
46 	gtk_widget_class_install_style_property(
47 		widget_class,
48 		g_param_spec_int("slider-width",P_("size of slider"),
49 		                   P_("Height of movable part of vslider"),
50 		                 0, 100, 5, GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
51 	gtk_widget_class_install_style_property(
52 		GTK_WIDGET_CLASS(klass),
53 		g_param_spec_string("icon-name",
54 		                    P_("Icon Name"),
55 		                    P_("Icon to use as Slider"),
56 		                    NULL,
57 		                    GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
58 	gtk_widget_class_set_css_name(widget_class, "gx-level-slider");
59 }
60 
get_stock_id(GtkWidget * widget)61 static const char *get_stock_id(GtkWidget *widget)
62 {
63 	gchar *icon;
64 	gtk_widget_style_get(widget, "icon-name", &icon, NULL);
65 	if (icon) {
66 		return icon;
67 	} else {
68 		return GX_LEVEL_SLIDER_CLASS(
69 			GTK_WIDGET_GET_CLASS(widget))->parent_class.stock_id;
70 	}
71 }
72 
gx_level_slider_get_preferred_width(GtkWidget * widget,gint * min_width,gint * natural_width)73 static void gx_level_slider_get_preferred_width (GtkWidget *widget, gint *min_width, gint *natural_width)
74 {
75   gint width, height;
76   gx_level_slider_size_request(widget, &width, &height);
77 
78   if (min_width) {
79     *min_width = width;
80   }
81   if (natural_width) {
82     *natural_width = width;
83   }
84 }
85 
gx_level_slider_get_preferred_height(GtkWidget * widget,gint * min_height,gint * natural_height)86 static void gx_level_slider_get_preferred_height (GtkWidget *widget, gint *min_height, gint *natural_height)
87 {
88   gint width, height;
89   gx_level_slider_size_request(widget, &width, &height);
90 
91   if (min_height) {
92     *min_height = height;
93   }
94   if (natural_height) {
95     *natural_height = height;
96   }
97 }
98 
99 
gx_level_slider_size_request(GtkWidget * widget,gint * width,gint * height)100 static void gx_level_slider_size_request (GtkWidget *widget, gint *width, gint *height)
101 {
102 	g_assert(GX_IS_LEVEL_SLIDER(widget));
103 	gint slider_height;
104 	gtk_widget_style_get(widget, "slider-width", &slider_height, NULL);
105 	GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
106 											 get_stock_id(widget), -1,
107 											 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
108 	*width = gdk_pixbuf_get_width(pb);
109 	*height = (gdk_pixbuf_get_height(pb) + slider_height) / 2;
110 	_gx_regler_calc_size_request(GX_REGLER(widget), width, height, FALSE);
111 	g_clear_object(&pb);
112 }
113 
level_slider_expose(GtkWidget * widget,cairo_t * cr,GdkRectangle * rect,gdouble sliderstate,GdkPixbuf * image)114 static void level_slider_expose(
115 	GtkWidget *widget, cairo_t *cr, GdkRectangle *rect, gdouble sliderstate, GdkPixbuf *image)
116 {
117 	GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
118 	sliderstate = rect->height * log_meter(gtk_adjustment_get_value(adj));
119 	gdk_cairo_set_source_pixbuf(cr, image, rect->x, rect->y - (gint)sliderstate);
120 	cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height);
121 	cairo_fill(cr);
122 }
123 
gx_level_slider_draw(GtkWidget * widget,cairo_t * cr)124 static gboolean gx_level_slider_draw(GtkWidget *widget, cairo_t *cr)
125 {
126 	g_assert(GX_IS_LEVEL_SLIDER(widget));
127 	gint slider_height;
128 	GdkRectangle image_rect, value_rect;
129 	GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
130 											 get_stock_id(widget), -1,
131 											 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
132 	gtk_widget_style_get(widget, "slider-width", &slider_height, NULL);
133 	image_rect.width = gdk_pixbuf_get_width(pb);
134 	image_rect.height = (gdk_pixbuf_get_height(pb) + slider_height) / 2;
135 	gdouble sliderstate = _gx_regler_get_step_pos(GX_REGLER(widget), image_rect.height-slider_height);
136 	_gx_regler_get_positions(GX_REGLER(widget), &image_rect, &value_rect, false);
137 	level_slider_expose(widget, cr, &image_rect, sliderstate, pb);
138 	_gx_regler_simple_display_value(GX_REGLER(widget), cr, &value_rect);
139 	g_clear_object(&pb);
140 	return FALSE;
141 }
142 
get_width_height(GtkWidget * widget,GdkRectangle * r)143 static inline void get_width_height(GtkWidget *widget, GdkRectangle *r)
144 {
145 	GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
146 											 get_stock_id(widget), -1,
147 											 GTK_ICON_LOOKUP_GENERIC_FALLBACK, nullptr);
148 	r->width = gdk_pixbuf_get_width(pb);
149 	r->height = gdk_pixbuf_get_height(pb);
150 	g_clear_object(&pb);
151 }
152 
slider_set_from_pointer(GtkWidget * widget,int state,gdouble x,gdouble y,gboolean drag,gint button,GdkEventButton * event)153 static gboolean slider_set_from_pointer(GtkWidget *widget, int state, gdouble x, gdouble y, gboolean drag, gint button, GdkEventButton *event)
154 {
155 	GdkRectangle image_rect, value_rect;
156 	gint slider_height;
157 	gtk_widget_style_get(widget, "slider-width", &slider_height, NULL);
158 	get_width_height(widget, &image_rect);
159 	image_rect.height = (image_rect.height + slider_height) / 2;
160 	_gx_regler_get_positions(GX_REGLER(widget), &image_rect, &value_rect, false);
161 	if (!drag && !_approx_in_rectangle(x, y, &image_rect)) {
162 		return FALSE;
163 	}
164 	if (button == 3) {
165 		gboolean ret;
166 		g_signal_emit_by_name(GX_REGLER(widget), "value-entry", &image_rect, event, &ret);
167 		return FALSE;
168 	}
169 	static double last_y = 2e20;
170 
171 	GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
172 	double slidery = image_rect.height;
173 	double posy = slidery - y + image_rect.y;
174 	double value;
175 	if (!drag) {
176 		last_y = posy;
177 		if (event && event->type == GDK_2BUTTON_PRESS) {
178 		    gtk_range_set_value(GTK_RANGE(widget), log_meter_inv(posy/image_rect.height));
179 		}
180 		return TRUE;
181 	}
182     double sc = 0.005;
183     if (state & GDK_CONTROL_MASK) {
184         sc = 0.0005;
185     }
186 	value = (posy - last_y) * sc;
187 	last_y = posy;
188 	gtk_range_set_value(GTK_RANGE(widget), gtk_adjustment_get_value(adj) + value * (gtk_adjustment_get_upper(adj) - gtk_adjustment_get_lower(adj)));
189 	return TRUE;
190 }
191 
gx_level_slider_button_press(GtkWidget * widget,GdkEventButton * event)192 static gboolean gx_level_slider_button_press (GtkWidget *widget, GdkEventButton *event)
193 {
194 	g_assert(GX_IS_LEVEL_SLIDER(widget));
195 	if (event->button != 1 && event->button != 3) {
196 		return FALSE;
197 	}
198 	gtk_widget_grab_focus(widget);
199 	if (slider_set_from_pointer(widget, event->state, event->x, event->y, FALSE, event->button, event)) {
200 		gtk_grab_add(widget);
201 	}
202 	return FALSE;
203 }
204 
gx_level_slider_pointer_motion(GtkWidget * widget,GdkEventMotion * event)205 static gboolean gx_level_slider_pointer_motion(GtkWidget *widget, GdkEventMotion *event)
206 {
207 	g_assert(GX_IS_LEVEL_SLIDER(widget));
208 	if (!gtk_widget_has_grab(widget)) {
209 		return FALSE;
210 	}
211 	gdk_event_request_motions (event);
212 	slider_set_from_pointer(widget, event->state, event->x, event->y, TRUE, 0, NULL);
213 	return FALSE;
214 }
215 
216 
gx_level_slider_init(GxLevelSlider * level_slider)217 static void gx_level_slider_init(GxLevelSlider *level_slider)
218 {
219 	gtk_widget_set_name (GTK_WIDGET(level_slider),"rack_slider");
220 }
221