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