1 /* gAlan - Graphical Audio Language
2 * Copyright (C) 1999 Tony Garnock-Jones
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <gtk/gtkmain.h>
22 #include <gtk/gtksignal.h>
24 #include "gtkslider.h"
27 # define M_PI 3.14159265358979323846 /* pi */
30 #define SCROLL_DELAY_LENGTH 300
31 #define SLIDER_WIDTH 32
32 #define DEFAULT_SLIDER_SIZE 100 /* travel distance, in pixels */
36 #define STATE_PRESSED 1
37 #define STATE_DRAGGING 2
40 static char * slider_xpm
[] = {
136 ". + @ @ # # $ $ % & * * & & = - @ ; > ; > , ' ) ",
137 ". @ ! ~ { { ] ! ] , ^ ^ / ] ! ! ( _ : _ < [ } | ",
138 "1 % 2 3 } } 4 5 4 4 6 7 8 3 8 8 9 9 9 } } 0 a b ",
139 "c d e f f f f f f f f f f f f f f f f f f g h i ",
140 "j ] : k k k k k k k k k k k k k k k k k k l m n ",
141 "+ { o p q d d r r s t u t t r d v v w x v y h z ",
142 "j ) A B C D D E E F G H C H C I E J J e f K L M ",
143 "l N O P Q R S T U h V V W W X X Y Z W U ` ...` "};
145 /* Forward declarations */
147 static void gtk_slider_class_init(GtkSliderClass
*klass
);
148 static void gtk_slider_init(GtkSlider
*slider
);
149 static void gtk_slider_destroy(GtkObject
*object
);
150 static void gtk_slider_realize(GtkWidget
*widget
);
151 static void gtk_slider_size_request(GtkWidget
*widget
, GtkRequisition
*requisition
);
152 static void gtk_slider_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
);
153 static gint
gtk_slider_expose(GtkWidget
*widget
, GdkEventExpose
*event
);
154 static gint
gtk_slider_button_press(GtkWidget
*widget
, GdkEventButton
*event
);
155 static gint
gtk_slider_button_release(GtkWidget
*widget
, GdkEventButton
*event
);
156 static gint
gtk_slider_motion_notify(GtkWidget
*widget
, GdkEventMotion
*event
);
157 static gint
gtk_slider_timer(GtkSlider
*slider
);
159 static void gtk_slider_update_mouse_update(GtkSlider
*slider
);
160 static void gtk_slider_update_mouse(GtkSlider
*slider
, gint x
, gint y
);
161 static void gtk_slider_update_mouse_abs(GtkSlider
*slider
, gint x
, gint y
);
162 static void gtk_slider_update(GtkSlider
*slider
);
163 static void gtk_slider_adjustment_changed(GtkAdjustment
*adjustment
, gpointer data
);
164 static void gtk_slider_adjustment_value_changed(GtkAdjustment
*adjustment
, gpointer data
);
168 static GtkWidgetClass
*parent_class
= NULL
;
170 guint
gtk_slider_get_type(void) {
171 static guint slider_type
= 0;
174 GtkTypeInfo slider_info
= {
177 sizeof (GtkSliderClass
),
178 (GtkClassInitFunc
) gtk_slider_class_init
,
179 (GtkObjectInitFunc
) gtk_slider_init
,
184 slider_type
= gtk_type_unique(gtk_widget_get_type(), &slider_info
);
190 static void gtk_slider_class_init (GtkSliderClass
*class) {
191 GtkObjectClass
*object_class
;
192 GtkWidgetClass
*widget_class
;
194 object_class
= (GtkObjectClass
*) class;
195 widget_class
= (GtkWidgetClass
*) class;
197 parent_class
= gtk_type_class(gtk_widget_get_type());
199 object_class
->destroy
= gtk_slider_destroy
;
201 widget_class
->realize
= gtk_slider_realize
;
202 widget_class
->expose_event
= gtk_slider_expose
;
203 widget_class
->size_request
= gtk_slider_size_request
;
204 widget_class
->size_allocate
= gtk_slider_size_allocate
;
205 widget_class
->button_press_event
= gtk_slider_button_press
;
206 widget_class
->button_release_event
= gtk_slider_button_release
;
207 widget_class
->motion_notify_event
= gtk_slider_motion_notify
;
210 static void gtk_slider_init (GtkSlider
*slider
) {
211 slider
->policy
= GTK_UPDATE_CONTINUOUS
;
212 slider
->state
= STATE_IDLE
;
213 slider
->saved_x
= slider
->saved_y
= 0;
215 slider
->pixmap
= NULL
;
216 slider
->size
= DEFAULT_SLIDER_SIZE
;
217 slider
->old_value
= 0.0;
218 slider
->old_lower
= 0.0;
219 slider
->old_upper
= 0.0;
220 slider
->adjustment
= NULL
;
223 GtkWidget
*gtk_slider_new(GtkAdjustment
*adjustment
, gint size
) {
226 slider
= gtk_type_new(gtk_slider_get_type());
229 adjustment
= (GtkAdjustment
*) gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
233 size
= DEFAULT_SLIDER_SIZE
;
237 gtk_slider_set_adjustment(slider
, adjustment
);
239 return GTK_WIDGET(slider
);
242 static void gtk_slider_destroy(GtkObject
*object
) {
245 g_return_if_fail(object
!= NULL
);
246 g_return_if_fail(GTK_IS_SLIDER(object
));
248 slider
= GTK_SLIDER(object
);
250 if (slider
->adjustment
) {
251 gtk_object_unref(GTK_OBJECT(slider
->adjustment
));
252 slider
->adjustment
= NULL
;
255 if (slider
->pixmap
) {
256 gdk_pixmap_unref(slider
->pixmap
);
257 slider
->pixmap
= NULL
;
260 if (GTK_OBJECT_CLASS(parent_class
)->destroy
)
261 (*GTK_OBJECT_CLASS(parent_class
)->destroy
)(object
);
264 GtkAdjustment
* gtk_slider_get_adjustment(GtkSlider
*slider
) {
265 g_return_val_if_fail(slider
!= NULL
, NULL
);
266 g_return_val_if_fail(GTK_IS_SLIDER(slider
), NULL
);
268 return slider
->adjustment
;
271 void gtk_slider_set_update_policy(GtkSlider
*slider
, GtkUpdateType policy
) {
272 g_return_if_fail (slider
!= NULL
);
273 g_return_if_fail (GTK_IS_SLIDER (slider
));
275 slider
->policy
= policy
;
278 void gtk_slider_set_adjustment(GtkSlider
*slider
, GtkAdjustment
*adjustment
) {
279 g_return_if_fail (slider
!= NULL
);
280 g_return_if_fail (GTK_IS_SLIDER (slider
));
282 if (slider
->adjustment
) {
283 gtk_signal_disconnect_by_data(GTK_OBJECT(slider
->adjustment
), (gpointer
)slider
);
284 gtk_object_unref(GTK_OBJECT(slider
->adjustment
));
287 slider
->adjustment
= adjustment
;
288 gtk_object_ref(GTK_OBJECT(slider
->adjustment
));
289 gtk_object_sink(GTK_OBJECT( slider
-> adjustment
) );
291 gtk_signal_connect(GTK_OBJECT(adjustment
), "changed",
292 GTK_SIGNAL_FUNC(gtk_slider_adjustment_changed
), (gpointer
) slider
);
293 gtk_signal_connect(GTK_OBJECT(adjustment
), "value_changed",
294 GTK_SIGNAL_FUNC(gtk_slider_adjustment_value_changed
), (gpointer
) slider
);
296 slider
->old_value
= adjustment
->value
;
297 slider
->old_lower
= adjustment
->lower
;
298 slider
->old_upper
= adjustment
->upper
;
300 gtk_slider_update(slider
);
303 static void gtk_slider_realize(GtkWidget
*widget
) {
305 GdkWindowAttr attributes
;
306 gint attributes_mask
;
309 g_return_if_fail(widget
!= NULL
);
310 g_return_if_fail(GTK_IS_SLIDER(widget
));
312 GTK_WIDGET_SET_FLAGS(widget
, GTK_REALIZED
);
313 slider
= GTK_SLIDER(widget
);
315 attributes
.x
= widget
->allocation
.x
;
316 attributes
.y
= widget
->allocation
.y
;
317 attributes
.width
= widget
->allocation
.width
;
318 attributes
.height
= widget
->allocation
.height
;
319 attributes
.wclass
= GDK_INPUT_OUTPUT
;
320 attributes
.window_type
= GDK_WINDOW_CHILD
;
321 attributes
.event_mask
=
322 gtk_widget_get_events (widget
) |
323 GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
|
324 GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
|
325 GDK_POINTER_MOTION_HINT_MASK
;
326 attributes
.visual
= gtk_widget_get_visual(widget
);
327 attributes
.colormap
= gtk_widget_get_colormap(widget
);
329 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
330 widget
->window
= gdk_window_new(widget
->parent
->window
, &attributes
, attributes_mask
);
332 widget
->style
= gtk_style_attach(widget
->parent
->style
, widget
->window
);
334 gdk_window_set_user_data(widget
->window
, widget
);
336 slider
->pixmap
= gdk_pixmap_colormap_create_from_xpm_d(widget
->window
, gdk_colormap_get_system(), &mask
,
337 &widget
->style
->bg
[GTK_STATE_NORMAL
],
340 gtk_style_set_background(widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
343 static void gtk_slider_size_request (GtkWidget
*widget
, GtkRequisition
*requisition
) {
344 requisition
->width
= SLIDER_WIDTH
;
345 requisition
->height
= GTK_SLIDER(widget
)->size
+ SLIDER_GAP
* 2;
348 static void gtk_slider_size_allocate (GtkWidget
*widget
, GtkAllocation
*allocation
) {
351 g_return_if_fail(widget
!= NULL
);
352 g_return_if_fail(GTK_IS_SLIDER(widget
));
353 g_return_if_fail(allocation
!= NULL
);
355 widget
->allocation
= *allocation
;
356 slider
= GTK_SLIDER(widget
);
358 if (GTK_WIDGET_REALIZED(widget
)) {
359 gdk_window_move_resize(widget
->window
,
360 allocation
->x
, allocation
->y
,
361 allocation
->width
, allocation
->height
);
365 static gint
gtk_slider_expose(GtkWidget
*widget
, GdkEventExpose
*event
) {
369 g_return_val_if_fail(widget
!= NULL
, FALSE
);
370 g_return_val_if_fail(GTK_IS_SLIDER(widget
), FALSE
);
371 g_return_val_if_fail(event
!= NULL
, FALSE
);
373 if (event
->count
> 0)
376 slider
= GTK_SLIDER(widget
);
378 gdk_window_clear_area(widget
->window
, 0, 0, widget
->allocation
.width
, widget
->allocation
.height
);
380 // gdk_draw_rectangle(widget->window, widget->style->bg_gc[widget->state], TRUE,
381 // 0, 0, widget->allocation.width, widget->allocation.height);
383 gdk_draw_line(widget
->window
, widget
->style
->black_gc
,
384 SLIDER_WIDTH
>>1, SLIDER_GAP
, SLIDER_WIDTH
>>1, SLIDER_GAP
+ slider
->size
);
386 dy
= slider
->adjustment
->upper
- slider
->adjustment
->lower
;
389 dy
= (slider
->adjustment
->value
- slider
->adjustment
->lower
) / dy
;
390 dy
= MIN(MAX(dy
, 0), 1);
391 dy
= (1 - dy
) * slider
->size
+ SLIDER_GAP
;
393 gdk_draw_pixmap(widget
->window
, widget
->style
->bg_gc
[widget
->state
], slider
->pixmap
,
394 0, 0, (SLIDER_WIDTH
>>1)-12, dy
-4, 24, 8);
400 static gint
gtk_slider_button_press(GtkWidget
*widget
, GdkEventButton
*event
) {
403 g_return_val_if_fail(widget
!= NULL
, FALSE
);
404 g_return_val_if_fail(GTK_IS_SLIDER(widget
), FALSE
);
405 g_return_val_if_fail(event
!= NULL
, FALSE
);
407 slider
= GTK_SLIDER(widget
);
409 switch (slider
->state
) {
411 switch (event
->button
) {
414 gtk_slider_update_mouse_abs(slider
, event
->x
, event
->y
);
420 gtk_grab_add(widget
);
421 slider
->state
= STATE_PRESSED
;
422 slider
->saved_x
= event
->x
;
423 slider
->saved_y
= event
->y
;
438 static gint
gtk_slider_button_release(GtkWidget
*widget
, GdkEventButton
*event
) {
441 g_return_val_if_fail(widget
!= NULL
, FALSE
);
442 g_return_val_if_fail(GTK_IS_SLIDER(widget
), FALSE
);
443 g_return_val_if_fail(event
!= NULL
, FALSE
);
445 slider
= GTK_SLIDER(widget
);
447 switch (slider
->state
) {
449 gtk_grab_remove(widget
);
450 slider
->state
= STATE_IDLE
;
452 switch (event
->button
) {
454 slider
->adjustment
->value
-= slider
->adjustment
->page_increment
;
455 gtk_signal_emit_by_name(GTK_OBJECT(slider
->adjustment
), "value_changed");
459 slider
->adjustment
->value
+= slider
->adjustment
->page_increment
;
460 gtk_signal_emit_by_name(GTK_OBJECT(slider
->adjustment
), "value_changed");
469 gtk_grab_remove(widget
);
470 slider
->state
= STATE_IDLE
;
472 if (slider
->policy
!= GTK_UPDATE_CONTINUOUS
&& slider
->old_value
!= slider
->adjustment
->value
)
473 gtk_signal_emit_by_name(GTK_OBJECT(slider
->adjustment
), "value_changed");
484 static gint
gtk_slider_motion_notify(GtkWidget
*widget
, GdkEventMotion
*event
) {
486 GdkModifierType mods
;
489 g_return_val_if_fail(widget
!= NULL
, FALSE
);
490 g_return_val_if_fail(GTK_IS_SLIDER(widget
), FALSE
);
491 g_return_val_if_fail(event
!= NULL
, FALSE
);
493 slider
= GTK_SLIDER(widget
);
498 if (event
->is_hint
|| (event
->window
!= widget
->window
))
499 gdk_window_get_pointer(widget
->window
, &x
, &y
, &mods
);
501 switch (slider
->state
) {
503 slider
->state
= STATE_DRAGGING
;
507 if (mods
& GDK_BUTTON1_MASK
) {
508 gtk_slider_update_mouse(slider
, x
, y
);
510 } else if (mods
& GDK_BUTTON3_MASK
) {
511 gtk_slider_update_mouse_abs(slider
, x
, y
);
523 static gint
gtk_slider_timer(GtkSlider
*slider
) {
524 g_return_val_if_fail(slider
!= NULL
, FALSE
);
525 g_return_val_if_fail(GTK_IS_SLIDER(slider
), FALSE
);
527 if (slider
->policy
== GTK_UPDATE_DELAYED
)
528 gtk_signal_emit_by_name(GTK_OBJECT(slider
->adjustment
), "value_changed");
530 return FALSE
; /* don't keep running this timer */
533 static void gtk_slider_update_mouse_update(GtkSlider
*slider
) {
534 if (slider
->policy
== GTK_UPDATE_CONTINUOUS
)
535 gtk_signal_emit_by_name(GTK_OBJECT(slider
->adjustment
), "value_changed");
537 gtk_widget_draw(GTK_WIDGET(slider
), NULL
);
539 if (slider
->policy
== GTK_UPDATE_DELAYED
) {
541 gtk_timeout_remove(slider
->timer
);
543 slider
->timer
= gtk_timeout_add (SCROLL_DELAY_LENGTH
, (GtkFunction
) gtk_slider_timer
,
549 static void gtk_slider_update_mouse(GtkSlider
*slider
, gint x
, gint y
) {
553 g_return_if_fail(slider
!= NULL
);
554 g_return_if_fail(GTK_IS_SLIDER(slider
));
556 old_value
= slider
->adjustment
->value
;
558 dv
= (slider
->saved_y
- y
) * slider
->adjustment
->step_increment
;
562 slider
->adjustment
->value
+= dv
;
564 if (slider
->adjustment
->value
!= old_value
)
565 gtk_slider_update_mouse_update(slider
);
568 static void gtk_slider_update_mouse_abs(GtkSlider
*slider
, gint x
, gint y
) {
572 g_return_if_fail(slider
!= NULL
);
573 g_return_if_fail(GTK_IS_SLIDER(slider
));
575 old_value
= slider
->adjustment
->value
;
578 y
= slider
->size
- y
;
580 dy
= y
/ ((gfloat
) slider
->size
);
581 dy
*= slider
->adjustment
->upper
- slider
->adjustment
->lower
;
582 dy
+= slider
->adjustment
->lower
;
584 slider
->adjustment
->value
= dy
;
586 if (slider
->adjustment
->value
!= old_value
)
587 gtk_slider_update_mouse_update(slider
);
590 static void gtk_slider_update(GtkSlider
*slider
) {
593 g_return_if_fail(slider
!= NULL
);
594 g_return_if_fail(GTK_IS_SLIDER (slider
));
596 new_value
= slider
->adjustment
->value
;
598 if (new_value
< slider
->adjustment
->lower
)
599 new_value
= slider
->adjustment
->lower
;
601 if (new_value
> slider
->adjustment
->upper
)
602 new_value
= slider
->adjustment
->upper
;
604 if (new_value
!= slider
->adjustment
->value
) {
605 slider
->adjustment
->value
= new_value
;
606 gtk_signal_emit_by_name(GTK_OBJECT(slider
->adjustment
), "value_changed");
609 gtk_widget_draw(GTK_WIDGET(slider
), NULL
);
612 static void gtk_slider_adjustment_changed(GtkAdjustment
*adjustment
, gpointer data
) {
615 g_return_if_fail(adjustment
!= NULL
);
616 g_return_if_fail(data
!= NULL
);
618 slider
= GTK_SLIDER(data
);
620 if ((slider
->old_value
!= adjustment
->value
) ||
621 (slider
->old_lower
!= adjustment
->lower
) ||
622 (slider
->old_upper
!= adjustment
->upper
)) {
623 gtk_slider_update (slider
);
625 slider
->old_value
= adjustment
->value
;
626 slider
->old_lower
= adjustment
->lower
;
627 slider
->old_upper
= adjustment
->upper
;
631 static void gtk_slider_adjustment_value_changed (GtkAdjustment
*adjustment
, gpointer data
) {
634 g_return_if_fail(adjustment
!= NULL
);
635 g_return_if_fail(data
!= NULL
);
637 slider
= GTK_SLIDER(data
);
639 if (slider
->old_value
!= adjustment
->value
) {
640 gtk_slider_update (slider
);
642 slider
->old_value
= adjustment
->value
;