1 /*
2 * (SLIK) SimpLIstic sKin functions
3 * (C) 2005 John Ellis
4 *
5 * Author: John Ellis
6 *
7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
10 */
11
12 #include "ui2_includes.h"
13 #include "ui2_typedefs.h"
14 #include "ui2_skin.h"
15
16 #include <math.h>
17
18 #include "ui2_button.h"
19 #include "ui2_display.h"
20 #include "ui2_main.h"
21 #include "ui2_util.h"
22 #include "ui2_widget.h"
23 #include "ui_pixbuf_ops.h"
24
25 #include <gdk/gdkkeysyms.h> /* for key values */
26
27
28 /*
29 *-------------
30 * background
31 *-------------
32 */
33
skin_back_setup(SkinData * skin)34 static void skin_back_setup(SkinData *skin)
35 {
36 GdkPixbuf *s;
37 GdkPixbuf *d;
38 gint w, h;
39
40 if (!skin->real_overlay) return;
41
42 if (skin->overlay &&
43 (skin->width != gdk_pixbuf_get_width(skin->overlay) ||
44 skin->height != gdk_pixbuf_get_height(skin->overlay)))
45 {
46 gdk_pixbuf_unref(skin->overlay);
47 skin->overlay = NULL;
48 }
49
50 if (!skin->overlay)
51 {
52 skin->overlay = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
53 gdk_pixbuf_get_has_alpha(skin->real_overlay), 8,
54 skin->width, skin->height);
55 }
56
57 s = skin->real_overlay;
58 w = gdk_pixbuf_get_width(s);
59 h = gdk_pixbuf_get_height(s);
60
61 d = skin->overlay;
62
63 if (skin->has_border)
64 {
65 pixbuf_copy_fill_border(s, d,
66 0, 0,
67 gdk_pixbuf_get_width(d), gdk_pixbuf_get_height(d),
68 skin->border_left, skin->border_left_stretch,
69 skin->border_right, skin->border_right_stretch,
70 skin->border_top, skin->border_top_stretch,
71 skin->border_bottom, skin->border_bottom_stretch,
72 skin->stretch, TRUE);
73 }
74 else
75 {
76 pixbuf_copy_fill(s, 0, 0, w, h,
77 d, 0, 0, skin->width, skin->height,
78 skin->stretch, TRUE);
79 }
80
81 if (skin->mask_buffer) g_object_unref(skin->mask_buffer);
82 skin->mask_buffer = NULL;
83 if (skin->mask)
84 {
85 gdk_pixbuf_render_pixmap_and_mask(skin->mask, NULL, &skin->mask_buffer, 128);
86 }
87 else
88 {
89 gdk_pixbuf_render_pixmap_and_mask(skin->overlay, NULL, &skin->mask_buffer, 1);
90 }
91 }
92
skin_back_pass2(SkinData * skin,UIData * ui)93 static void skin_back_pass2(SkinData *skin, UIData *ui)
94 {
95 guint8 alpha;
96
97 if (!skin->real_overlay || !skin->overlay) return;
98
99 alpha = (slik_transparency_force) ? slik_transparency_force_a : 255;
100
101 if (skin->has_border)
102 {
103 pixbuf_copy_fill_border_alpha(skin->real_overlay, skin->overlay,
104 0, 0,
105 gdk_pixbuf_get_width(skin->overlay), gdk_pixbuf_get_height(skin->overlay),
106 skin->border_left, skin->border_left_stretch,
107 skin->border_right, skin->border_right_stretch,
108 skin->border_top, skin->border_top_stretch,
109 skin->border_bottom, skin->border_bottom_stretch,
110 skin->stretch, alpha);
111 }
112 else
113 {
114 gint w, h;
115
116 w = gdk_pixbuf_get_width(skin->real_overlay);
117 h = gdk_pixbuf_get_height(skin->real_overlay);
118 pixbuf_copy_fill_alpha(skin->real_overlay, 0, 0, w, h,
119 skin->overlay, 0, 0, skin->width, skin->height,
120 skin->stretch, alpha);
121 }
122 }
123
124 /*
125 *-------------
126 * skin side keys
127 *-------------
128 */
129
skin_register_widget(SkinData * skin,const gchar * key,const gchar * text_id,WidgetType type,gpointer widget)130 WidgetData *skin_register_widget(SkinData *skin, const gchar *key, const gchar *text_id, WidgetType type, gpointer widget)
131 {
132 WidgetData *wd;
133
134 if (key == NULL)
135 {
136 printf("Attempt to register skin widget with NULL key!\n");
137 return NULL;
138 }
139 if (debug_mode) printf("skin registering widget \"%s\" (%d)\n", key, type);
140 wd = ui_widget_new(key, text_id, type, widget);
141 skin->widget_list = g_list_append(skin->widget_list, wd);
142
143 return wd;
144 }
145
skin_register_free_all(SkinData * skin)146 void skin_register_free_all(SkinData *skin)
147 {
148 while(skin->widget_list)
149 {
150 WidgetData *wd = skin->widget_list->data;
151 skin->widget_list = g_list_remove(skin->widget_list, wd);
152
153 ui_widget_free(wd);
154 }
155 }
156
skin_widget_get_by_key(SkinData * skin,const gchar * key,WidgetType type)157 WidgetData *skin_widget_get_by_key(SkinData *skin, const gchar *key, WidgetType type)
158 {
159 GList *work;
160
161 if (!key) return NULL;
162
163 work = skin->widget_list;
164 while(work)
165 {
166 WidgetData *wd = work->data;
167
168 if (wd->type == type && strcmp(key, wd->key) == 0) return wd;
169
170 work = work->next;
171 }
172 return NULL;
173 }
174
skin_widget_get_by_text_id(SkinData * skin,const gchar * text_id)175 WidgetData *skin_widget_get_by_text_id(SkinData *skin, const gchar *text_id)
176 {
177 GList *work;
178
179 if (!text_id) return NULL;
180
181 work = skin->widget_list;
182 while(work)
183 {
184 WidgetData *wd = work->data;
185
186 if (wd->text_id && strcmp(text_id, wd->text_id) == 0) return wd;
187
188 work = work->next;
189 }
190 return NULL;
191 }
192
skin_widget_get_by_widget(SkinData * skin,gpointer widget)193 WidgetData *skin_widget_get_by_widget(SkinData *skin, gpointer widget)
194 {
195 GList *work;
196
197 if (!widget || !skin) return NULL;
198
199 work = skin->widget_list;
200 while (work)
201 {
202 WidgetData *wd = work->data;
203
204 if (wd->widget == widget) return wd;
205
206 work = work->next;
207 }
208 return NULL;
209 }
210
211
skin_widget_for_each_do(UIData * ui,const gchar * key,WidgetType type,void (* func)(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui),gpointer data)212 static gint skin_widget_for_each_do(UIData *ui, const gchar *key, WidgetType type,
213 void (*func)(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui),
214 gpointer data)
215 {
216 gint count = 0;
217 GList *work;
218
219 if (!ui->skin) return 0;
220
221 work = ui->skin->widget_list;
222 while (work)
223 {
224 WidgetData *wd = work->data;
225
226 if (wd->type == type && wd->in_bounds && !wd->hidden && strcmp(key, wd->key) == 0)
227 {
228 func(wd, data, ui->skin->pixbuf, ui);
229 count++;
230 }
231
232 work = work->next;
233 }
234
235 return count;
236 }
237
skin_widget_for_each_key(UIData * ui,const gchar * key,WidgetType type,void (* func)(WidgetData * wd,gpointer data,GdkPixbuf * pb,UIData * ui),gpointer data)238 gint skin_widget_for_each_key(UIData *ui, const gchar *key, WidgetType type,
239 void (*func)(WidgetData *wd, gpointer data, GdkPixbuf *pb, UIData *ui),
240 gpointer data)
241 {
242 gint count;
243 GList *work;
244 UIData *parent;
245
246 if (!func || !key) return 0;
247
248 if (ui->parent)
249 {
250 parent = ui->parent;
251 }
252 else
253 {
254 parent = ui;
255 }
256
257 count = skin_widget_for_each_do(ui, key, type, func, data);
258
259 /* obey private keys */
260 if (ui_registered_key_is_private(ui, key, type))
261 {
262 return count;
263 }
264
265 /*
266 * Children keys override the parent's,
267 * so if at least one hit, do not propogate up to parent.
268 */
269 if (parent != ui && count > 0) return count;
270
271 if (parent != ui)
272 {
273 count += skin_widget_for_each_do(parent, key, type, func, data);
274 }
275
276 work = parent->children;
277 while (work)
278 {
279 UIData *child;
280
281 child = work->data;
282
283 if (child != ui)
284 {
285 count += skin_widget_for_each_do(child, key, type, func, data);
286 }
287
288 work = work->next;
289 }
290
291 /* editor updates (only parent can have an editor) */
292 if (parent->edit)
293 {
294 skin_widget_for_each_key(parent->edit, key, type, func, data);
295 }
296
297 return count;
298 }
299
skin_widget_for_each_key_simple(SkinData * skin,const gchar * key,WidgetType type,void (* func)(WidgetData * wd,gpointer data),gpointer data)300 gint skin_widget_for_each_key_simple(SkinData *skin, const gchar *key, WidgetType type,
301 void (*func)(WidgetData *wd, gpointer data),
302 gpointer data)
303 {
304 gint count = 0;
305 GList *work;
306
307 if (!func) return 0;
308
309 work = skin->widget_list;
310 while (work)
311 {
312 WidgetData *wd = work->data;
313
314 if (wd->type == type && (!key || strcmp(key, wd->key) == 0))
315 {
316 func(wd, data);
317 count++;
318 }
319
320 work = work->next;
321 }
322
323 return count;
324 }
325
326 /*
327 *-------------
328 * app data
329 *-------------
330 */
331
skin_set_data(SkinData * skin,const gchar * key,const gchar * data)332 void skin_set_data(SkinData *skin, const gchar *key, const gchar *data)
333 {
334 if (!skin) return;
335
336 skin->data_list = widget_data_list_set(skin->data_list, key, data);
337 }
338
skin_get_data(SkinData * skin,const gchar * key)339 const gchar *skin_get_data(SkinData *skin, const gchar *key)
340 {
341 GList *work;
342
343 if (!skin || !key) return NULL;
344
345 work = widget_data_list_find(skin->data_list, key);
346 if (work && work->next)
347 {
348 work = work->next;
349 return work->data;
350 }
351
352 return NULL;
353 }
354
355 /*
356 *-------------
357 * skin stuff
358 *-------------
359 */
360
skin_new(void)361 SkinData *skin_new(void)
362 {
363 SkinData *skin;
364
365 skin = g_new0(SkinData, 1);
366 skin->widget_list = NULL;
367 skin->transparent = FALSE;
368 skin->ui = NULL;
369
370 skin->background_filename = NULL;
371 skin->background_mask_filename = NULL;
372 skin->focus_filename = NULL;
373
374 skin->focus_overlay = NULL;
375 skin->focus_stretch = FALSE;
376 skin->focus_has_border = FALSE;
377 skin->focus_border_left = 0;
378 skin->focus_border_right = 0;
379 skin->focus_border_top = 0;
380 skin->focus_border_bottom = 0;
381 skin->focus_anchor_right = FALSE;
382 skin->focus_anchor_bottom = FALSE;
383
384 skin->focus_box_filled = FALSE;
385 skin->focus_box_alpha = 128;
386 skin->focus_box_r = 255;
387 skin->focus_box_g = 0;
388 skin->focus_box_b = 0;
389
390 skin->scratch_pixbuf = NULL;
391 skin->scratch_pixmap = NULL;
392
393 skin->resize_idle_id = -1;
394
395 return skin;
396 }
397
skin_free(SkinData * skin)398 void skin_free(SkinData *skin)
399 {
400 GList *work;
401
402 if (!skin) return;
403
404 if (skin->resize_idle_id != -1) g_source_remove(skin->resize_idle_id);
405
406 while (skin->widget_list)
407 {
408 WidgetData *wd = skin->widget_list->data;
409 skin->widget_list = g_list_remove(skin->widget_list, wd);
410 ui_widget_free(wd);
411 }
412
413 work = skin->data_list;
414 while (work)
415 {
416 g_free(work->data);
417 work = work->next;
418 }
419 g_list_free(skin->data_list);
420
421 if (skin->overlay) gdk_pixbuf_unref(skin->overlay);
422 if (skin->pixbuf) gdk_pixbuf_unref(skin->pixbuf);
423 if (skin->buffer) g_object_unref(skin->buffer);
424 if (skin->mask) gdk_pixbuf_unref(skin->mask);
425 if (skin->mask_buffer) g_object_unref(skin->mask_buffer);
426
427 if (skin->real_overlay) gdk_pixbuf_unref(skin->real_overlay);
428
429 if (skin->focus_overlay) gdk_pixbuf_unref(skin->focus_overlay);
430
431 if (skin->scratch_pixbuf) gdk_pixbuf_unref(skin->scratch_pixbuf);
432 if (skin->scratch_pixmap) g_object_unref(skin->scratch_pixmap);
433
434 g_free(skin->background_filename);
435 g_free(skin->background_mask_filename);
436 g_free(skin->focus_filename);
437
438 g_free(skin);
439 }
440
skin_get_scratch(SkinData * skin,gint w,gint h,GdkPixbuf ** pixbuf,GdkPixmap ** pixmap)441 void skin_get_scratch(SkinData *skin, gint w, gint h, GdkPixbuf **pixbuf, GdkPixmap **pixmap)
442 {
443 gint old_w, old_h;
444
445 if (skin->scratch_pixbuf)
446 {
447 old_w = gdk_pixbuf_get_width(skin->scratch_pixbuf);
448 old_h = gdk_pixbuf_get_height(skin->scratch_pixbuf);
449 }
450 else
451 {
452 old_w = 0;
453 old_h = 0;
454 }
455
456 if (w > old_w || h > old_h)
457 {
458 /* scratch buffer dimensions can only grow,
459 * to reduce needing to recreate for each widget size
460 */
461 if (old_w > w) w = old_w;
462 if (old_h > h) h = old_h;
463
464 if (skin->scratch_pixbuf) gdk_pixbuf_unref(skin->scratch_pixbuf);
465 skin->scratch_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
466
467 if (skin->scratch_pixmap) g_object_unref(skin->scratch_pixmap);
468 skin->scratch_pixmap = gdk_pixmap_new(skin->ui->display->window, w, h, -1);
469 }
470
471 if (pixbuf) *pixbuf = skin->scratch_pixbuf;
472 if (pixmap) *pixmap = skin->scratch_pixmap;
473 }
474
skin_get_buffer(SkinData * skin)475 GdkPixmap *skin_get_buffer(SkinData *skin)
476 {
477 return skin->buffer;
478 }
479
skin_get_pixbuf(SkinData * skin)480 GdkPixbuf *skin_get_pixbuf(SkinData *skin)
481 {
482 return skin->pixbuf;
483 }
484
skin_get_ui(SkinData * skin)485 UIData *skin_get_ui(SkinData *skin)
486 {
487 if (!skin) return NULL;
488 return skin->ui;
489 }
490
skin_sync_back_on_widgets(SkinData * skin,UIData * ui)491 static void skin_sync_back_on_widgets(SkinData *skin, UIData *ui)
492 {
493 gint priority;
494
495 for (priority = UI_WIDGET_PRIORITY_HIGH; priority >= 0; priority--)
496 {
497 GList *work;
498
499 work = skin->widget_list;
500 while (work)
501 {
502 WidgetData *wd = work->data;
503 work = work->next;
504
505 if (wd->od->priority == priority) ui_widget_sync_back(ui, wd);
506 }
507 }
508 }
509
skin_sync_back(SkinData * skin,UIData * ui)510 void skin_sync_back(SkinData *skin, UIData *ui)
511 {
512 gint pass2 = FALSE;
513
514 if (!skin || !skin->overlay || !skin->pixbuf) return;
515
516 if (ui->back_func &&
517 (gdk_pixbuf_get_has_alpha(skin->overlay) || slik_transparency_force) &&
518 ui->back_func(ui, skin->overlay, ui->back_data))
519 {
520 pass2 = TRUE;
521 #if 0
522 if (slik_transparency_force)
523 {
524 pixbuf_copy_area_alpha(skin->overlay, 0, 0, pb, 0, 0, skin->width, skin->height,
525 slik_transparency_force_a);
526 }
527 else
528 {
529 pixbuf_copy_area_alpha(skin->overlay, 0, 0, pb, 0, 0, skin->width, skin->height, 255);
530 }
531 #endif
532 }
533 else if (skin->transparent || slik_transparency_force)
534 {
535 gint x, y;
536
537 if (ui->window)
538 {
539 gdk_window_get_position(ui->window->window, &x, &y);
540 }
541 else
542 {
543 gdk_window_get_position(ui->display->window, &x, &y);
544 }
545
546 util_pixbuf_fill_from_root_window(skin->overlay, x, y, FALSE);
547
548 pass2 = TRUE;
549 #if 0
550 if (slik_transparency_force)
551 {
552 pixbuf_copy_area_alpha(skin->overlay, 0, 0, pb, 0, 0, skin->width, skin->height,
553 slik_transparency_force_a);
554 }
555 else
556 {
557 pixbuf_copy_area_alpha(skin->overlay, 0, 0, pb, 0, 0, skin->width, skin->height, 255);
558 }
559 #endif
560 }
561 #if 0
562 else
563 {
564 pixbuf_copy_area(skin->overlay, 0, 0, pb, 0, 0, skin->width, skin->height, FALSE);
565 }
566 #endif
567
568 if (pass2) skin_back_pass2(skin, ui);
569
570 skin_sync_back_on_widgets(skin, ui);
571 pixbuf_copy_area(skin->overlay, 0, 0, skin->pixbuf, 0, 0, skin->width, skin->height, FALSE);
572 }
573
skin_set_underlay(SkinData * skin,UIData * ui,GdkPixbuf * pb)574 void skin_set_underlay(SkinData *skin, UIData *ui, GdkPixbuf *pb)
575 {
576 if (!skin || !skin->pixbuf || !pb) return;
577
578 skin_back_setup(skin);
579 pixbuf_copy_area(pb, 0, 0, skin->overlay, 0, 0, skin->width, skin->height, FALSE);
580 #if 0
581 pixbuf_copy_area_alpha(skin->overlay, 0, 0, skin->pixbuf, 0, 0, skin->width, skin->height, 255);
582 #endif
583 skin_back_pass2(skin, ui);
584 skin_sync_back_on_widgets(skin, ui);
585 pixbuf_copy_area(skin->overlay, 0, 0, skin->pixbuf, 0, 0, skin->width, skin->height, FALSE);
586 }
587
skin_sync_buffer(SkinData * skin,UIData * ui)588 void skin_sync_buffer(SkinData *skin, UIData *ui)
589 {
590 if (!skin) return;
591
592 skin_back_setup(skin);
593
594 if (!GTK_WIDGET_REALIZED(ui->display)) gtk_widget_realize(ui->display);
595
596 if (skin->buffer) g_object_unref(skin->buffer);
597 skin->buffer = gdk_pixmap_new(ui->display->window, skin->width, skin->height, -1);
598
599 if (skin->pixbuf) gdk_pixbuf_unref(skin->pixbuf);
600 skin->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, skin->width, skin->height);
601
602 skin_sync_back(skin, ui);
603 }
604
skin_debug_print_registered_keys(SkinData * skin)605 void skin_debug_print_registered_keys(SkinData *skin)
606 {
607 GList *work;
608
609 if (!skin)
610 {
611 printf("no skin\n");
612 return;
613 }
614
615 printf("SKIN widget keys (%3d):\n", g_list_length(skin->widget_list));
616 printf("-[key]----------------[in bounds]-[type]-----[text id]--[data]-\n");
617
618 work = skin->widget_list;
619 while(work)
620 {
621 WidgetData *wd;
622 const gchar *data;
623
624 wd = work->data;
625 work = work->next;
626
627 data = ui_widget_get_data(wd, "data");
628
629 printf("%-30s %1d %-10s %-10s %s\n", wd->key, wd->in_bounds, ui_widget_type_to_text(wd->type), (wd->text_id) ? wd->text_id : "", (data) ? data : "");
630 }
631 }
632
skin_widget_check_bounds(SkinData * skin,WidgetData * wd)633 void skin_widget_check_bounds(SkinData *skin, WidgetData *wd)
634 {
635 gint x, y, w, h;
636
637 if (wd->od && wd->od->is_visible &&
638 ui_widget_get_geometry(wd, &x, &y, &w, &h))
639 {
640 if (x < 0 || y < 0 ||
641 x + w > skin->width || y + h > skin->height)
642 {
643 wd->in_bounds = FALSE;
644 }
645 else
646 {
647 wd->in_bounds = TRUE;
648 }
649 }
650 }
651
skin_check_bounds(SkinData * skin)652 void skin_check_bounds(SkinData *skin)
653 {
654 GList *work;
655
656 if (!skin) return;
657
658 /* standard bounds check */
659 work = skin->widget_list;
660 while (work)
661 {
662 WidgetData *wd = work->data;
663 skin_widget_check_bounds(skin, wd);
664 work = work->next;
665 }
666
667 /* process exclusive widget overlap, disabling "covered" widgets */
668 work = skin->widget_list;
669 while (work)
670 {
671 WidgetData *awd;
672 gint x, y, w, h;
673
674 awd = work->data;
675
676 if (awd->exclusive && awd->in_bounds &&
677 ui_widget_get_geometry(awd, &x, &y, &w, &h))
678 {
679 GList *check;
680
681 check = skin->widget_list;
682 while (check)
683 {
684 WidgetData *bwd = check->data;
685
686 if (bwd != awd && bwd->in_bounds &&
687 bwd->od->priority <= awd->od->priority &&
688 ui_widget_contacts_area(bwd, x, y, w, h))
689 {
690 bwd->in_bounds = FALSE;
691 }
692
693 check = check->next;
694 }
695 }
696
697 work = work->next;
698 }
699 }
700
701 /* this also takes on the work of calling set_size */
skin_move_anchored(SkinData * skin,gint move_w,gint move_h)702 static void skin_move_anchored(SkinData *skin, gint move_w, gint move_h)
703 {
704 GList *work;
705
706 work = skin->widget_list;
707 while(work)
708 {
709 WidgetData *wd = work->data;
710 gint x, y;
711
712 if ((wd->anchor_right || wd->anchor_bottom) &&
713 ui_widget_get_geometry(wd, &x, &y, NULL, NULL))
714 {
715 if (wd->anchor_right) x += move_w;
716 if (wd->anchor_bottom) y += move_h;
717 ui_widget_set_coord(skin->ui, wd, x, y, FALSE);
718 }
719 ui_widget_set_size(skin->ui, wd, move_w, move_h, FALSE);
720
721 work = work->next;
722 }
723 }
724
skin_set_size(SkinData * skin,gint w,gint h)725 void skin_set_size(SkinData *skin, gint w, gint h)
726 {
727 gint dw, dh;
728
729 if (skin->ui)
730 {
731 printf("warning: skin_set_size called with a ui, use skin_resize instead\n");
732 return;
733 }
734
735 if (w > SKIN_SIZE_MAX) w = SKIN_SIZE_MAX;
736 if (h > SKIN_SIZE_MAX) h = SKIN_SIZE_MAX;
737 if (w < SKIN_SIZE_MIN) w = SKIN_SIZE_MIN;
738 if (h < SKIN_SIZE_MIN) h = SKIN_SIZE_MIN;
739
740 if (w == skin->width && h == skin->height) return;
741 if (w < 4 || h < 4) return;
742
743 dw = w - skin->width;
744 dh = h - skin->height;
745
746 skin->width = w;
747 skin->height = h;
748
749 skin_move_anchored(skin, dw, dh);
750 }
751
skin_resize(UIData * ui,gint w,gint h)752 void skin_resize(UIData *ui, gint w, gint h)
753 {
754 SkinData *skin;
755 gint x, y;
756 gint dw, dh;
757
758 skin = ui->skin;
759
760 if (skin->resize_idle_id != -1)
761 {
762 g_source_remove(skin->resize_idle_id);
763 skin->resize_idle_id = -1;
764 }
765
766 if (w > SKIN_SIZE_MAX) w = SKIN_SIZE_MAX;
767 if (h > SKIN_SIZE_MAX) h = SKIN_SIZE_MAX;
768 if (w < SKIN_SIZE_MIN) w = SKIN_SIZE_MIN;
769 if (h < SKIN_SIZE_MIN) h = SKIN_SIZE_MIN;
770
771 /* keyboard resize expects this to be in sync */
772 skin->resize_idle_width = w;
773 skin->resize_idle_height = h;
774
775 if (w == skin->width && h == skin->height) return;
776 if (w < 4 || h < 4) return;
777
778 x = y = 0;
779
780 if (ui->window && ui->window->window) gdk_window_get_position(ui->window->window, &x, &y);
781
782 dw = w - skin->width;
783 dh = h - skin->height;
784
785 skin->width = w;
786 skin->height = h;
787
788 skin_move_anchored(skin, dw, dh);
789
790 ui_display_sync_all(ui);
791
792 if (ui->window && ui->window->window) gdk_window_move(ui->window->window, x, y);
793 }
794
795 /*
796 *-------------
797 * skin widget init (after setting to a ui)
798 *-------------
799 */
800
skin_widgets_init(SkinData * skin,UIData * ui)801 void skin_widgets_init(SkinData *skin, UIData *ui)
802 {
803 GList *work;
804
805 if (!skin || !ui) return;
806
807 work = skin->widget_list;
808 while (work)
809 {
810 WidgetData *wd = work->data;
811
812 if (wd->od && wd->od->func_init)
813 {
814 wd->od->func_init(wd->widget, wd->key, ui);
815 }
816
817 work = work->next;
818 }
819 }
820
821 /*
822 *-------------
823 * skin resizing button handling
824 *-------------
825 */
826
827 static void (*skin_size_func_motion)(gpointer, const gchar *, gint, gint, GdkPixbuf *, UIData *) = NULL;
828 static gint (*skin_size_func_key)(gpointer widget, const gchar *key, GdkEventKey *event, GdkPixbuf *pb, UIData *ui) = NULL;
829
skin_resize_idle_cb(gpointer data)830 static gint skin_resize_idle_cb(gpointer data)
831 {
832 SkinData *skin = data;
833
834 if (skin->resize_idle_id == -1) return FALSE;
835
836 skin->resize_idle_id = -1;
837
838 skin->ui->press_x += skin->resize_idle_width - skin->width;
839 skin->ui->press_y += skin->resize_idle_height - skin->height;
840
841 skin_resize(skin->ui, skin->resize_idle_width, skin->resize_idle_height);
842
843 return FALSE;
844 }
845
skin_resize_real_motion(gpointer widget,const gchar * key,gint x,gint y,GdkPixbuf * pb,UIData * ui)846 static void skin_resize_real_motion(gpointer widget, const gchar *key, gint x, gint y, GdkPixbuf *pb, UIData *ui)
847 {
848 ButtonData *button = widget;
849
850 if (button->active && ui->skin->sizeable && ui->skin->real_overlay)
851 {
852 SkinData *skin = ui->skin;
853 gint mx, my;
854 gint nw, nh;
855 gint w, h;
856
857 mx = x - ui->press_x;
858 my = y - ui->press_y;
859
860 /* sanity check */
861 if (skin->width_inc < 1) skin->width_inc = 1;
862 if (skin->height_inc < 1) skin->height_inc = 1;
863
864 /* lock to increments */
865 mx = (gint)floor((float)mx / skin->width_inc + 0.5) * skin->width_inc;
866 my = (gint)floor((float)my / skin->height_inc + 0.5) * skin->height_inc;
867
868 /* compute new size */
869 nw = skin->width + mx;
870 nh = skin->height + my;
871
872 /* clamp to valid range */
873 if (nw < skin->width_min) nw = skin->width_min;
874 if (nw > skin->width_max) nw = skin->width_max;
875 if (nh < skin->height_min) nh = skin->height_min;
876 if (nh > skin->height_max) nh = skin->height_max;
877
878 w = nw - skin->width;
879 h = nh - skin->height;
880
881 if ((w != 0 || h != 0) &&
882 button->x + w >= 0 &&
883 button->y + h >= 0)
884 {
885 /* resize it */
886 skin->resize_idle_width = nw;
887 skin->resize_idle_height = nh;
888 if (skin->resize_idle_id == -1) skin->resize_idle_id = g_idle_add(skin_resize_idle_cb, skin);
889 }
890 }
891
892 /* pass it on to the button (at this time not necessary,
893 * but the button motion API may change in the future */
894 if (skin_size_func_motion) skin_size_func_motion(widget, key, x, y, ui->skin->pixbuf, ui);
895 }
896
skin_resize_real_key(gpointer widget,const gchar * key,GdkEventKey * event,GdkPixbuf * pb,UIData * ui)897 static gint skin_resize_real_key(gpointer widget, const gchar *key, GdkEventKey *event, GdkPixbuf *pb, UIData *ui)
898 {
899 SkinData *skin;
900 gint nw, nh;
901 gint ret = TRUE;
902
903 if (!ui->skin->sizeable || !ui->skin->real_overlay ||
904 !(event->state & GDK_SHIFT_MASK))
905 {
906 if (skin_size_func_key)
907 {
908 /* pass key press on to button */
909 return skin_size_func_key(widget, key, event, ui->skin->pixbuf, ui);
910 }
911 return FALSE;
912 }
913
914 skin = ui->skin;
915
916 if (skin->resize_idle_width < 4 || skin->resize_idle_height < 4)
917 {
918 /* probably default skin size, hence no skin_resize has been called yet */
919 nw = skin->width;
920 nh = skin->height;
921 }
922 else
923 {
924 nw = skin->resize_idle_width;
925 nh = skin->resize_idle_height;
926 }
927
928 switch (event->keyval)
929 {
930 case GDK_Up:
931 case GDK_KP_Up:
932 nh -= skin->height_inc;
933 break;
934 case GDK_Down:
935 case GDK_KP_Down:
936 nh += skin->height_inc;
937 break;
938 case GDK_Left:
939 case GDK_KP_Left:
940 nw -= skin->width_inc;
941 break;
942 case GDK_Right:
943 case GDK_KP_Right:
944 nw += skin->width_inc;
945 break;
946 case GDK_Page_Up:
947 case GDK_KP_Page_Up:
948 nw = skin->width_min;
949 nh = skin->height_min;
950 break;
951 case GDK_Page_Down:
952 case GDK_KP_Page_Down:
953 nw = skin->width_max;
954 nh = skin->height_max;
955 break;
956 case GDK_Home:
957 case GDK_KP_Home:
958 nw = skin->width_def;
959 nh = skin->height_def;
960 break;
961 default:
962 ret = FALSE;
963 break;
964 }
965
966 if (ret)
967 {
968 if (nw < skin->width_min) nw = skin->width_min;
969 if (nw > skin->width_max) nw = skin->width_max;
970 if (nh < skin->height_min) nh = skin->height_min;
971 if (nh > skin->height_max) nh = skin->height_max;
972
973 if (nw != skin->resize_idle_width || nh != skin->resize_idle_height)
974 {
975 skin->resize_idle_width = nw;
976 skin->resize_idle_height = nh;
977 if (skin->resize_idle_id == -1) skin->resize_idle_id = g_idle_add(skin_resize_idle_cb, skin);
978 }
979 }
980
981 /* if not handled, pass it on to the button */
982 if (!ret && skin_size_func_key)
983 {
984 return skin_size_func_key(widget, key, event, ui->skin->pixbuf, ui);
985 }
986
987 return ret;
988 }
989
skin_resize_setup_cb(WidgetData * wd,gpointer data)990 static void skin_resize_setup_cb(WidgetData *wd, gpointer data)
991 {
992 static WidgetObjectData *od = NULL;
993
994 /* since this intercepts func_motion, we need to copy the original button od,
995 * set our own func_motion, and set the WidgetData to use the copy
996 */
997 if (!od)
998 {
999 od = ui_widget_object_copy(ui_widget_object_by_type(button_type_id()));
1000
1001 skin_size_func_motion = od->func_motion;
1002 od->func_motion = skin_resize_real_motion;
1003
1004 skin_size_func_key = od->func_focus_key_event;
1005 od->func_focus_key_event = skin_resize_real_key;
1006 }
1007
1008 if (od && wd->od)
1009 {
1010 wd->od = od;
1011
1012 /* this button always anchors bottom, right */
1013 wd->anchor_right = TRUE;
1014 wd->anchor_bottom = TRUE;
1015 }
1016 }
1017
1018 /* this overrides things like the skin resize button */
skin_finalize(SkinData * skin)1019 void skin_finalize(SkinData *skin)
1020 {
1021 skin_widget_for_each_key_simple(skin, "skin_size", button_type_id(),
1022 skin_resize_setup_cb, skin);
1023 }
1024