| |
1 /* GTK - The GIMP Toolkit |
| |
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
| |
3 * |
| |
4 * This library is free software; you can redistribute it and/or |
| |
5 * modify it under the terms of the GNU Library General Public |
| |
6 * License as published by the Free Software Foundation; either |
| |
7 * version 2 of the License, or (at your option) any later version. |
| |
8 * |
| |
9 * This library 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 GNU |
| |
12 * Library General Public License for more details. |
| |
13 * |
| |
14 * You should have received a copy of the GNU Library General Public |
| |
15 * License along with this library; if not, write to the |
| |
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| |
17 * Boston, MA 02111-1307, USA. |
| |
18 */ |
| |
19 |
| |
20 /* |
| |
21 * GtkTicker Copyright 2000 Syd Logan |
| |
22 */ |
| |
23 |
| |
24 #include "gtkticker.h" |
| |
25 #include <gtk/gtk.h> |
| |
26 |
| |
27 static void gtk_ticker_compute_offsets (GtkTicker *ticker); |
| |
28 static void gtk_ticker_class_init (GtkTickerClass *klass); |
| |
29 static void gtk_ticker_init (GtkTicker *ticker); |
| |
30 static void gtk_ticker_map (GtkWidget *widget); |
| |
31 static void gtk_ticker_realize (GtkWidget *widget); |
| |
32 static void gtk_ticker_size_request (GtkWidget *widget, |
| |
33 GtkRequisition *requisition); |
| |
34 static void gtk_ticker_size_allocate (GtkWidget *widget, |
| |
35 GtkAllocation *allocation); |
| |
36 static void gtk_ticker_add_real (GtkContainer *container, |
| |
37 GtkWidget *widget); |
| |
38 static void gtk_ticker_remove_real (GtkContainer *container, |
| |
39 GtkWidget *widget); |
| |
40 static void gtk_ticker_forall (GtkContainer *container, |
| |
41 gboolean include_internals, |
| |
42 GtkCallback callback, |
| |
43 gpointer callback_data); |
| |
44 static GtkType gtk_ticker_child_type (GtkContainer *container); |
| |
45 |
| |
46 |
| |
47 static GtkContainerClass *parent_class = NULL; |
| |
48 |
| |
49 |
| |
50 GType gtk_ticker_get_type (void) |
| |
51 { |
| |
52 static GType ticker_type = 0; |
| |
53 |
| |
54 ticker_type = g_type_from_name("GtkTicker"); |
| |
55 |
| |
56 if (!ticker_type) |
| |
57 { |
| |
58 static const GTypeInfo ticker_info = |
| |
59 { |
| |
60 sizeof(GtkTickerClass), |
| |
61 NULL, |
| |
62 NULL, |
| |
63 (GClassInitFunc) gtk_ticker_class_init, |
| |
64 NULL, |
| |
65 NULL, |
| |
66 sizeof(GtkTicker), |
| |
67 0, |
| |
68 (GInstanceInitFunc) gtk_ticker_init, |
| |
69 NULL |
| |
70 }; |
| |
71 |
| |
72 ticker_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTicker", |
| |
73 &ticker_info, 0); |
| |
74 } |
| |
75 |
| |
76 /* kludge to re-initialise the class if it's already registered */ |
| |
77 else if (parent_class == NULL) { |
| |
78 gtk_ticker_class_init((GtkTickerClass *)g_type_class_peek(ticker_type)); |
| |
79 } |
| |
80 |
| |
81 return ticker_type; |
| |
82 } |
| |
83 |
| |
84 static void gtk_ticker_finalize(GObject *object) { |
| |
85 gtk_ticker_stop_scroll(GTK_TICKER(object)); |
| |
86 |
| |
87 G_OBJECT_CLASS(parent_class)->finalize(object); |
| |
88 } |
| |
89 |
| |
90 static void gtk_ticker_class_init (GtkTickerClass *class) |
| |
91 { |
| |
92 GObjectClass *gobject_class; |
| |
93 GtkWidgetClass *widget_class; |
| |
94 GtkContainerClass *container_class; |
| |
95 |
| |
96 gobject_class = (GObjectClass*) class; |
| |
97 widget_class = (GtkWidgetClass*) class; |
| |
98 container_class = (GtkContainerClass*) class; |
| |
99 |
| |
100 parent_class = gtk_type_class (GTK_TYPE_CONTAINER); |
| |
101 |
| |
102 gobject_class->finalize = gtk_ticker_finalize; |
| |
103 |
| |
104 widget_class->map = gtk_ticker_map; |
| |
105 widget_class->realize = gtk_ticker_realize; |
| |
106 widget_class->size_request = gtk_ticker_size_request; |
| |
107 widget_class->size_allocate = gtk_ticker_size_allocate; |
| |
108 |
| |
109 container_class->add = gtk_ticker_add_real; |
| |
110 container_class->remove = gtk_ticker_remove_real; |
| |
111 container_class->forall = gtk_ticker_forall; |
| |
112 container_class->child_type = gtk_ticker_child_type; |
| |
113 } |
| |
114 |
| |
115 static GtkType gtk_ticker_child_type (GtkContainer *container) |
| |
116 { |
| |
117 return GTK_TYPE_WIDGET; |
| |
118 } |
| |
119 |
| |
120 static void gtk_ticker_init (GtkTicker *ticker) |
| |
121 { |
| |
122 GTK_WIDGET_UNSET_FLAGS (ticker, GTK_NO_WINDOW); |
| |
123 |
| |
124 ticker->interval = (guint) 200; |
| |
125 ticker->scootch = (guint) 2; |
| |
126 ticker->children = NULL; |
| |
127 ticker->timer = 0; |
| |
128 ticker->dirty = TRUE; |
| |
129 } |
| |
130 |
| |
131 GtkWidget* gtk_ticker_new (void) |
| |
132 { |
| |
133 return GTK_WIDGET(g_object_new(GTK_TYPE_TICKER, NULL)); |
| |
134 } |
| |
135 |
| |
136 static void gtk_ticker_put (GtkTicker *ticker, GtkWidget *widget) |
| |
137 { |
| |
138 GtkTickerChild *child_info; |
| |
139 |
| |
140 g_return_if_fail (ticker != NULL); |
| |
141 g_return_if_fail (GTK_IS_TICKER (ticker)); |
| |
142 g_return_if_fail (widget != NULL); |
| |
143 |
| |
144 child_info = g_new(GtkTickerChild, 1); |
| |
145 child_info->widget = widget; |
| |
146 child_info->x = 0; |
| |
147 |
| |
148 gtk_widget_set_parent(widget, GTK_WIDGET (ticker)); |
| |
149 |
| |
150 ticker->children = g_list_append (ticker->children, child_info); |
| |
151 |
| |
152 if (GTK_WIDGET_REALIZED (ticker)) |
| |
153 gtk_widget_realize (widget); |
| |
154 |
| |
155 if (GTK_WIDGET_VISIBLE (ticker) && GTK_WIDGET_VISIBLE (widget)) |
| |
156 { |
| |
157 if (GTK_WIDGET_MAPPED (ticker)) |
| |
158 gtk_widget_map (widget); |
| |
159 |
| |
160 gtk_widget_queue_resize (GTK_WIDGET (ticker)); |
| |
161 } |
| |
162 } |
| |
163 |
| |
164 void gtk_ticker_set_interval (GtkTicker *ticker, gint interval) |
| |
165 { |
| |
166 g_return_if_fail (ticker != NULL); |
| |
167 g_return_if_fail (GTK_IS_TICKER (ticker)); |
| |
168 |
| |
169 if ( interval < 0 ) |
| |
170 interval = 200; |
| |
171 ticker->interval = interval; |
| |
172 } |
| |
173 |
| |
174 guint gtk_ticker_get_interval (GtkTicker *ticker) |
| |
175 { |
| |
176 g_return_val_if_fail (ticker != NULL, -1); |
| |
177 g_return_val_if_fail (GTK_IS_TICKER (ticker), -1); |
| |
178 |
| |
179 return ticker->interval; |
| |
180 } |
| |
181 |
| |
182 void gtk_ticker_set_scootch (GtkTicker *ticker, gint scootch) |
| |
183 { |
| |
184 g_return_if_fail (ticker != NULL); |
| |
185 g_return_if_fail (GTK_IS_TICKER (ticker)); |
| |
186 |
| |
187 if (scootch <= 0) |
| |
188 scootch = 2; |
| |
189 ticker->scootch = scootch; |
| |
190 ticker->dirty = TRUE; |
| |
191 } |
| |
192 |
| |
193 guint gtk_ticker_get_scootch (GtkTicker *ticker ) |
| |
194 { |
| |
195 g_return_val_if_fail (ticker != NULL, -1); |
| |
196 g_return_val_if_fail (GTK_IS_TICKER (ticker), -1); |
| |
197 |
| |
198 return ticker->scootch; |
| |
199 } |
| |
200 |
| |
201 void gtk_ticker_set_spacing (GtkTicker *ticker, gint spacing ) |
| |
202 { |
| |
203 g_return_if_fail (ticker != NULL); |
| |
204 g_return_if_fail (GTK_IS_TICKER (ticker)); |
| |
205 |
| |
206 if ( spacing < 0 ) |
| |
207 spacing = 0; |
| |
208 ticker->spacing = spacing; |
| |
209 ticker->dirty = TRUE; |
| |
210 } |
| |
211 |
| |
212 static int ticker_timeout(gpointer data) |
| |
213 { |
| |
214 GtkTicker *ticker = (GtkTicker *) data; |
| |
215 |
| |
216 if (GTK_WIDGET_VISIBLE (ticker)) |
| |
217 gtk_widget_queue_resize (GTK_WIDGET (ticker)); |
| |
218 |
| |
219 return( TRUE ); |
| |
220 } |
| |
221 |
| |
222 void gtk_ticker_start_scroll(GtkTicker *ticker) |
| |
223 { |
| |
224 g_return_if_fail (ticker != NULL); |
| |
225 g_return_if_fail (GTK_IS_TICKER (ticker)); |
| |
226 if ( ticker->timer != 0 ) |
| |
227 return; |
| |
228 ticker->timer = g_timeout_add(ticker->interval, ticker_timeout, ticker); |
| |
229 } |
| |
230 |
| |
231 void gtk_ticker_stop_scroll(GtkTicker *ticker) |
| |
232 { |
| |
233 g_return_if_fail (ticker != NULL); |
| |
234 g_return_if_fail (GTK_IS_TICKER (ticker)); |
| |
235 if ( ticker->timer == 0 ) |
| |
236 return; |
| |
237 g_source_remove(ticker->timer); |
| |
238 ticker->timer = 0; |
| |
239 } |
| |
240 |
| |
241 guint gtk_ticker_get_spacing (GtkTicker *ticker ) |
| |
242 { |
| |
243 g_return_val_if_fail (ticker != NULL, -1); |
| |
244 g_return_val_if_fail (GTK_IS_TICKER (ticker), -1); |
| |
245 |
| |
246 return ticker->spacing; |
| |
247 } |
| |
248 |
| |
249 static void gtk_ticker_map (GtkWidget *widget) |
| |
250 { |
| |
251 GtkTicker *ticker; |
| |
252 GtkTickerChild *child; |
| |
253 GList *children; |
| |
254 |
| |
255 g_return_if_fail (widget != NULL); |
| |
256 g_return_if_fail (GTK_IS_TICKER (widget)); |
| |
257 |
| |
258 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); |
| |
259 ticker = GTK_TICKER (widget); |
| |
260 |
| |
261 children = ticker->children; |
| |
262 while (children) |
| |
263 { |
| |
264 child = children->data; |
| |
265 children = children->next; |
| |
266 |
| |
267 if (GTK_WIDGET_VISIBLE (child->widget) && |
| |
268 !GTK_WIDGET_MAPPED (child->widget)) |
| |
269 gtk_widget_map (child->widget); |
| |
270 } |
| |
271 |
| |
272 gdk_window_show (widget->window); |
| |
273 } |
| |
274 |
| |
275 static void gtk_ticker_realize (GtkWidget *widget) |
| |
276 { |
| |
277 GdkWindowAttr attributes; |
| |
278 gint attributes_mask; |
| |
279 |
| |
280 g_return_if_fail (widget != NULL); |
| |
281 g_return_if_fail (GTK_IS_TICKER (widget)); |
| |
282 |
| |
283 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); |
| |
284 |
| |
285 attributes.window_type = GDK_WINDOW_CHILD; |
| |
286 attributes.x = widget->allocation.x; |
| |
287 attributes.y = widget->allocation.y; |
| |
288 attributes.width = widget->allocation.width; |
| |
289 attributes.height = widget->allocation.height; |
| |
290 attributes.wclass = GDK_INPUT_OUTPUT; |
| |
291 attributes.visual = gtk_widget_get_visual (widget); |
| |
292 attributes.colormap = gtk_widget_get_colormap (widget); |
| |
293 attributes.event_mask = gtk_widget_get_events (widget); |
| |
294 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; |
| |
295 |
| |
296 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; |
| |
297 |
| |
298 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), |
| |
299 &attributes, attributes_mask); |
| |
300 gdk_window_set_user_data (widget->window, widget); |
| |
301 |
| |
302 widget->style = gtk_style_attach (widget->style, widget->window); |
| |
303 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); |
| |
304 } |
| |
305 |
| |
306 static void gtk_ticker_size_request (GtkWidget *widget, GtkRequisition *requisition) |
| |
307 { |
| |
308 GtkTicker *ticker; |
| |
309 GtkTickerChild *child; |
| |
310 GList *children; |
| |
311 GtkRequisition child_requisition; |
| |
312 |
| |
313 g_return_if_fail (widget != NULL); |
| |
314 g_return_if_fail (GTK_IS_TICKER (widget)); |
| |
315 g_return_if_fail (requisition != NULL); |
| |
316 |
| |
317 ticker = GTK_TICKER (widget); |
| |
318 requisition->width = 0; |
| |
319 requisition->height = 0; |
| |
320 |
| |
321 children = ticker->children; |
| |
322 while (children) |
| |
323 { |
| |
324 child = children->data; |
| |
325 children = children->next; |
| |
326 |
| |
327 if (GTK_WIDGET_VISIBLE (child->widget)) |
| |
328 { |
| |
329 gtk_widget_size_request (child->widget, &child_requisition); |
| |
330 |
| |
331 requisition->height = MAX (requisition->height, |
| |
332 child_requisition.height); |
| |
333 requisition->width += child_requisition.width + ticker->spacing; |
| |
334 } |
| |
335 } |
| |
336 if ( requisition->width > ticker->spacing ) |
| |
337 requisition->width -= ticker->spacing; |
| |
338 |
| |
339 requisition->height += GTK_CONTAINER (ticker)->border_width * 2; |
| |
340 requisition->width += GTK_CONTAINER (ticker)->border_width * 2; |
| |
341 } |
| |
342 |
| |
343 static void gtk_ticker_compute_offsets (GtkTicker *ticker) |
| |
344 { |
| |
345 GtkTickerChild *child; |
| |
346 GtkRequisition child_requisition; |
| |
347 GList *children; |
| |
348 guint16 border_width; |
| |
349 |
| |
350 g_return_if_fail (ticker != NULL); |
| |
351 g_return_if_fail (GTK_IS_TICKER(ticker)); |
| |
352 |
| |
353 border_width = GTK_CONTAINER (ticker)->border_width; |
| |
354 |
| |
355 ticker->width = GTK_WIDGET(ticker)->allocation.width; |
| |
356 ticker->total = 0; |
| |
357 children = ticker->children; |
| |
358 while (children) { |
| |
359 child = children->data; |
| |
360 |
| |
361 child->x = 0; |
| |
362 if (GTK_WIDGET_VISIBLE (child->widget)) { |
| |
363 gtk_widget_get_child_requisition (child->widget, &child_requisition); |
| |
364 child->offset = ticker->total; |
| |
365 ticker->total += |
| |
366 child_requisition.width + border_width + ticker->spacing; |
| |
367 } |
| |
368 children = children->next; |
| |
369 } |
| |
370 ticker->dirty = FALSE; |
| |
371 } |
| |
372 |
| |
373 static void gtk_ticker_size_allocate (GtkWidget *widget, |
| |
374 GtkAllocation *allocation) |
| |
375 { |
| |
376 GtkTicker *ticker; |
| |
377 GtkTickerChild *child; |
| |
378 GtkAllocation child_allocation; |
| |
379 GtkRequisition child_requisition; |
| |
380 GList *children; |
| |
381 guint16 border_width; |
| |
382 |
| |
383 g_return_if_fail (widget != NULL); |
| |
384 g_return_if_fail (GTK_IS_TICKER(widget)); |
| |
385 g_return_if_fail (allocation != NULL); |
| |
386 |
| |
387 ticker = GTK_TICKER (widget); |
| |
388 |
| |
389 if ( GTK_WIDGET(ticker)->allocation.width != ticker->width ) |
| |
390 ticker->dirty = TRUE; |
| |
391 |
| |
392 if ( ticker->dirty == TRUE ) { |
| |
393 gtk_ticker_compute_offsets( ticker ); |
| |
394 } |
| |
395 |
| |
396 widget->allocation = *allocation; |
| |
397 if (GTK_WIDGET_REALIZED (widget)) |
| |
398 gdk_window_move_resize (widget->window, |
| |
399 allocation->x, |
| |
400 allocation->y, |
| |
401 allocation->width, |
| |
402 allocation->height); |
| |
403 |
| |
404 border_width = GTK_CONTAINER (ticker)->border_width; |
| |
405 |
| |
406 children = ticker->children; |
| |
407 while (children) |
| |
408 { |
| |
409 child = children->data; |
| |
410 child->x -= ticker->scootch; |
| |
411 |
| |
412 if (GTK_WIDGET_VISIBLE (child->widget)) { |
| |
413 gtk_widget_get_child_requisition (child->widget, &child_requisition); |
| |
414 child_allocation.width = child_requisition.width; |
| |
415 child_allocation.x = child->offset + border_width + child->x; |
| |
416 if ( ( child_allocation.x + child_allocation.width ) < GTK_WIDGET(ticker)->allocation.x ) { |
| |
417 if ( ticker->total >= GTK_WIDGET(ticker)->allocation.width ) { |
| |
418 child->x += GTK_WIDGET(ticker)->allocation.x + GTK_WIDGET(ticker)->allocation.width + ( ticker->total - ( GTK_WIDGET(ticker)->allocation.x + GTK_WIDGET(ticker)->allocation.width ) ); |
| |
419 } |
| |
420 else { |
| |
421 child->x += GTK_WIDGET(ticker)->allocation.x + GTK_WIDGET(ticker)->allocation.width; |
| |
422 } |
| |
423 } |
| |
424 child_allocation.y = border_width; |
| |
425 child_allocation.height = child_requisition.height; |
| |
426 gtk_widget_size_allocate (child->widget, &child_allocation); |
| |
427 } |
| |
428 children = children->next; |
| |
429 } |
| |
430 } |
| |
431 |
| |
432 void gtk_ticker_add(GtkTicker *ticker, GtkWidget *widget) |
| |
433 { |
| |
434 gtk_ticker_add_real( GTK_CONTAINER( ticker ), widget ); |
| |
435 ticker->dirty = TRUE; |
| |
436 } |
| |
437 |
| |
438 void gtk_ticker_remove(GtkTicker *ticker, GtkWidget *widget) |
| |
439 { |
| |
440 gtk_ticker_remove_real( GTK_CONTAINER( ticker ), widget ); |
| |
441 ticker->dirty = TRUE; |
| |
442 } |
| |
443 |
| |
444 static void gtk_ticker_add_real(GtkContainer *container, GtkWidget *widget) |
| |
445 { |
| |
446 g_return_if_fail (container != NULL); |
| |
447 g_return_if_fail (GTK_IS_TICKER (container)); |
| |
448 g_return_if_fail (widget != NULL); |
| |
449 |
| |
450 gtk_ticker_put(GTK_TICKER (container), widget); |
| |
451 } |
| |
452 |
| |
453 static void gtk_ticker_remove_real(GtkContainer *container, GtkWidget *widget) |
| |
454 { |
| |
455 GtkTicker *ticker; |
| |
456 GtkTickerChild *child; |
| |
457 GList *children; |
| |
458 |
| |
459 g_return_if_fail (container != NULL); |
| |
460 g_return_if_fail (GTK_IS_TICKER (container)); |
| |
461 g_return_if_fail (widget != NULL); |
| |
462 |
| |
463 ticker = GTK_TICKER (container); |
| |
464 |
| |
465 children = ticker->children; |
| |
466 while (children) |
| |
467 { |
| |
468 child = children->data; |
| |
469 |
| |
470 if (child->widget == widget) |
| |
471 { |
| |
472 gboolean was_visible = GTK_WIDGET_VISIBLE (widget); |
| |
473 |
| |
474 gtk_widget_unparent (widget); |
| |
475 |
| |
476 ticker->children = g_list_remove_link (ticker->children, children); |
| |
477 g_list_free (children); |
| |
478 g_free (child); |
| |
479 |
| |
480 if (was_visible && GTK_WIDGET_VISIBLE (container)) |
| |
481 gtk_widget_queue_resize (GTK_WIDGET (container)); |
| |
482 |
| |
483 break; |
| |
484 } |
| |
485 |
| |
486 children = children->next; |
| |
487 } |
| |
488 } |
| |
489 |
| |
490 static void gtk_ticker_forall (GtkContainer *container, |
| |
491 gboolean include_internals, |
| |
492 GtkCallback callback, |
| |
493 gpointer callback_data) |
| |
494 { |
| |
495 GtkTicker *ticker; |
| |
496 GtkTickerChild *child; |
| |
497 GList *children; |
| |
498 |
| |
499 g_return_if_fail (container != NULL); |
| |
500 g_return_if_fail (GTK_IS_TICKER (container)); |
| |
501 g_return_if_fail (callback != NULL); |
| |
502 |
| |
503 ticker = GTK_TICKER (container); |
| |
504 |
| |
505 children = ticker->children; |
| |
506 while (children) |
| |
507 { |
| |
508 child = children->data; |
| |
509 children = children->next; |
| |
510 |
| |
511 (* callback) (child->widget, callback_data); |
| |
512 } |
| |
513 } |
| |
514 |