1 /* High Contrast - a cairo based GTK+ engine
2 * Copyright (C) 2003 Sun Microsystems Inc.
3 * Copyright (C) 2006 Andrew Johnson <acjgenius@earthlink.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Project contact: <gnome-themes-list@gnome.org>
20 *
21 */
22
23
24 #include "hc_gtk2_engine.h"
25 #include "hc_gtk2_support.h"
26 #include "hc_gtk2_drawing.h"
27
28 void
hc_simple_border_gap_clip(cairo_t * canvas,gint border_thickness,gint x,gint y,gint width,gint height,GtkPositionType gap_side,gint gap_pos,gint gap_size)29 hc_simple_border_gap_clip(cairo_t *canvas,
30 gint border_thickness,
31
32 gint x,
33 gint y,
34 gint width,
35 gint height,
36
37 GtkPositionType gap_side,
38 gint gap_pos,
39 gint gap_size)
40 {
41 switch (gap_side)
42 {
43 default:
44 case GTK_POS_TOP:
45 cairo_move_to(canvas, x, y);
46 cairo_line_to(canvas, x, y + height);
47 cairo_line_to(canvas, x + width, y + height);
48 cairo_line_to(canvas, x + width, y);
49 cairo_line_to(canvas, x + gap_pos + gap_size, y);
50 cairo_line_to(canvas, x + gap_pos + gap_size, y + border_thickness + 1);
51 cairo_line_to(canvas, x + gap_pos, y + border_thickness + 1);
52 cairo_line_to(canvas, x + gap_pos, y);
53 cairo_close_path(canvas);
54 break;
55
56 case GTK_POS_LEFT:
57 cairo_move_to(canvas, x, y);
58 cairo_line_to(canvas, x + width, y);
59 cairo_line_to(canvas, x + width, y + height);
60 cairo_line_to(canvas, x, y + height);
61 cairo_line_to(canvas, x, y + gap_pos + gap_size);
62 cairo_line_to(canvas, x + border_thickness + 1, y + gap_pos + gap_size);
63 cairo_line_to(canvas, x + border_thickness + 1, y + gap_pos);
64 cairo_line_to(canvas, x, y + gap_pos);
65 cairo_close_path(canvas);
66 break;
67
68 case GTK_POS_BOTTOM:
69 cairo_move_to(canvas, x + width, y + height);
70 cairo_line_to(canvas, x + width, y);
71 cairo_line_to(canvas, x, y);
72 cairo_line_to(canvas, x, y + height);
73 cairo_line_to(canvas, x + gap_pos, y + height);
74 cairo_line_to(canvas, x + gap_pos, y + height - border_thickness - 1);
75 cairo_line_to(canvas, x + gap_pos + gap_size, y + height - border_thickness - 1);
76 cairo_line_to(canvas, x + gap_pos + gap_size, y + height);
77 cairo_close_path(canvas);
78 break;
79
80 case GTK_POS_RIGHT:
81 cairo_line_to(canvas, x + width, y);
82 cairo_line_to(canvas, x, y);
83 cairo_line_to(canvas, x, y + height);
84 cairo_line_to(canvas, x + width, y + height);
85 cairo_line_to(canvas, x + width, y + gap_pos + gap_size);
86 cairo_line_to(canvas, x + width - border_thickness - 1, y + gap_pos + gap_size);
87 cairo_line_to(canvas, x + width - border_thickness - 1, y + gap_pos);
88 cairo_line_to(canvas, x + width, y + gap_pos);
89 cairo_close_path(canvas);
90 break;
91 }
92
93 cairo_clip(canvas);
94 }
95
96 /***********************************************
97 * do_hc_draw_arrow -
98 *
99 * A simple routine to draw a hc style
100 * arrow using the passed Color.
101 *
102 * Taken in part from smooth, it was based on
103 * XFCE's & CleanIce draw arrow routines,
104 * both which were based on ThinIce's.
105 ***********************************************/
106 void
do_hc_draw_arrow(cairo_t * canvas,CairoColor * color,GtkArrowType arrow_type,gboolean fill,gint x,gint y,gint width,gint height)107 do_hc_draw_arrow (cairo_t *canvas,
108 CairoColor * color,
109 GtkArrowType arrow_type,
110 gboolean fill,
111 gint x,
112 gint y,
113 gint width,
114 gint height)
115 {
116 gint aw = width, ah = height;
117 GdkPoint points[3];
118
119 switch (arrow_type)
120 {
121 case GTK_ARROW_UP:
122 case GTK_ARROW_DOWN:
123 {
124 gdouble tmp=((aw+1)/2) - ((height%2)?1:0);
125
126 if (tmp > ah)
127 {
128 aw = 2*ah - 1 - ((height%2)?1:0);
129 ah = (aw+1)/2;
130 }
131 else
132 {
133 ah = (gint) tmp;
134 aw = 2*ah - 1;
135 }
136
137 if ((aw < 5) || (ah < 3))
138 {
139 aw = 5;
140 ah = 3;
141 }
142
143 x += (width - aw) / 2 ;
144 y += (height - ah) / 2;
145 width = aw;
146 height = ah;
147
148 width += width % 2 - 1;
149
150 points[0].x = x;
151 points[1].x = x + width - 1;
152 points[2].x = x + ((height - 1) - (height - (1 + width / 2)));
153
154 points[0].y = points[1].y = y;
155 points[2].y = y + height - 1;
156
157 if (arrow_type == GTK_ARROW_UP)
158 {
159 gint flip = points[1].y;
160
161 points[0].y = points[1].y = points[2].y;
162 points[2].y = flip;
163 }
164 }
165 break;
166
167 case GTK_ARROW_LEFT:
168 case GTK_ARROW_RIGHT:
169 {
170 gdouble tmp=((ah+1)/2) - ((width%2)?1:0);
171
172 if (tmp > aw)
173 {
174 ah = 2*aw - 1 - ((width%2)?1:0);
175 aw = (ah+1)/2;
176 }
177 else
178 {
179 aw = (gint) tmp;
180 ah = 2*aw - 1;
181 }
182
183 if ((ah < 5) || (aw < 3))
184 {
185 ah = 5;
186 aw = 3;
187 }
188
189 x += (width - aw) / 2 ;
190 y += (height - ah) / 2;
191 width = aw;
192 height = ah;
193
194 height += height % 2 - 1;
195
196 points[0].y = y;
197 points[1].y = y + height - 1;
198 points[2].y = y + ((width - 1) - (width - (1 + height / 2)));
199
200 points[0].x = points[1].x = x;
201 points[2].x = x + width - 1;
202
203 if (arrow_type == GTK_ARROW_LEFT)
204 {
205 gint flip = points[0].x;
206
207 points[0].x = points[1].x = points[2].x;
208 points[2].x = flip;
209 }
210 }
211 break;
212
213 default:
214 {
215 return;
216 }
217 }
218
219 cairo_save(canvas);
220
221 ge_cairo_set_color(canvas, color);
222 cairo_set_line_width (canvas, 0.5);
223
224 cairo_move_to(canvas, points[0].x + 0.5, points[0].y + 0.5);
225 cairo_line_to(canvas, points[1].x + 0.5, points[1].y + 0.5);
226 cairo_line_to(canvas, points[2].x + 0.5, points[2].y + 0.5);
227 cairo_close_path(canvas);
228
229 if (fill)
230 {
231 cairo_stroke_preserve(canvas);
232
233 cairo_fill(canvas);
234 }
235 else
236 {
237 cairo_stroke(canvas);
238 }
239
240 cairo_restore(canvas);
241 }
242
do_hc_draw_line(cairo_t * cr,CairoColor * color,gdouble thickness,gdouble x1,gdouble y1,gdouble x2,gdouble y2)243 void do_hc_draw_line (cairo_t *cr,
244 CairoColor *color,
245 gdouble thickness,
246 gdouble x1,
247 gdouble y1,
248 gdouble x2,
249 gdouble y2)
250 {
251 cairo_save(cr);
252
253 ge_cairo_set_color(cr, color);
254 cairo_set_line_width (cr, thickness);
255 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
256
257 cairo_move_to(cr, x1, y1);
258 cairo_line_to(cr, x2, y2);
259
260 cairo_stroke(cr);
261
262 cairo_restore(cr);
263 }
264
265 void
do_hc_draw_dot(cairo_t * canvas,CairoColor * light,CairoColor * dark,gint x,gint y)266 do_hc_draw_dot (cairo_t *canvas,
267 CairoColor * light,
268 CairoColor * dark,
269 gint x,
270 gint y)
271 {
272 ge_cairo_set_color (canvas, dark);
273 cairo_rectangle (canvas, x - 1, y - 1, 1, 1);
274 cairo_rectangle (canvas, x - 1, y, 1, 1);
275 cairo_rectangle (canvas, x, y - 1, 1, 1);
276 cairo_fill (canvas);
277
278 ge_cairo_set_color (canvas, light);
279 cairo_rectangle (canvas, x + 1, y + 1, 1, 1);
280 cairo_rectangle (canvas, x + 1, y, 1, 1);
281 cairo_rectangle (canvas, x, y + 1, 1, 1);
282 cairo_fill (canvas);
283 }
284
285 /***********************************************/
286 /* MenuShell/MenuBar Item Prelight Workaround */
287 /***********************************************/
288
289 /***********************************************
290 * hc_gtk2_engine_hack_menu_shell_style_set -
291 *
292 * Style set signal to ensure menushell signals
293 * get cleaned up if the theme changes
294 ***********************************************/
295 static gboolean
hc_gtk2_engine_hack_menu_shell_style_set(GtkWidget * widget,GtkStyle * previous_style,gpointer user_data)296 hc_gtk2_engine_hack_menu_shell_style_set(GtkWidget *widget,
297 GtkStyle *previous_style,
298 gpointer user_data)
299 {
300 hc_gtk2_engine_hack_menu_shell_cleanup_signals(widget);
301
302 return FALSE;
303 }
304
305 /***********************************************
306 * hc_gtk2_engine_hack_menu_shell_destroy -
307 *
308 * Destroy signal to ensure menushell signals
309 * get cleaned if it is destroyed
310 ***********************************************/
311 static gboolean
hc_gtk2_engine_hack_menu_shell_destroy(GtkWidget * widget,GdkEvent * event,gpointer user_data)312 hc_gtk2_engine_hack_menu_shell_destroy(GtkWidget *widget,
313 GdkEvent *event,
314 gpointer user_data)
315 {
316 hc_gtk2_engine_hack_menu_shell_cleanup_signals(widget);
317
318 return FALSE;
319 }
320
321 /***********************************************
322 * hc_gtk2_engine_hack_menu_shell_motion -
323 *
324 * Motion signal to ensure menushell items
325 * prelight state changes on mouse move.
326 ***********************************************/
327 static gboolean
hc_gtk2_engine_hack_menu_shell_motion(GtkWidget * widget,GdkEventMotion * event,gpointer user_data)328 hc_gtk2_engine_hack_menu_shell_motion(GtkWidget *widget,
329 GdkEventMotion *event,
330 gpointer user_data)
331 {
332 if (GE_IS_MENU_SHELL(widget))
333 {
334 gint pointer_x, pointer_y;
335 GdkModifierType pointer_mask;
336 GList *children = NULL, *child = NULL;
337
338 gdk_window_get_pointer(widget->window, &pointer_x, &pointer_y, &pointer_mask);
339
340 if (GE_IS_CONTAINER(widget))
341 {
342 children = gtk_container_get_children(GTK_CONTAINER(widget));
343
344 for (child = g_list_first(children); child; child = g_list_next(child))
345 {
346 if ((child->data) && GE_IS_WIDGET(child->data) &&
347 (GTK_WIDGET_STATE(GTK_WIDGET(child->data)) != GTK_STATE_INSENSITIVE))
348 {
349 if ((pointer_x >= GTK_WIDGET(child->data)->allocation.x) &&
350 (pointer_y >= GTK_WIDGET(child->data)->allocation.y) &&
351 (pointer_x < (GTK_WIDGET(child->data)->allocation.x +
352 GTK_WIDGET(child->data)->allocation.width)) &&
353 (pointer_y < (GTK_WIDGET(child->data)->allocation.y +
354 GTK_WIDGET(child->data)->allocation.height)))
355 {
356 gtk_widget_set_state (GTK_WIDGET(child->data), GTK_STATE_PRELIGHT);
357 }
358 else
359 {
360 gtk_widget_set_state (GTK_WIDGET(child->data), GTK_STATE_NORMAL);
361 }
362 }
363 }
364
365 if (children)
366 g_list_free(children);
367 }
368 }
369
370 return FALSE;
371 }
372
373 /***********************************************
374 * hc_gtk2_engine_hack_menu_shell_leave -
375 *
376 * Leave signal to ensure menushell items
377 * normal state on mouse leave.
378 ***********************************************/
379 static gboolean
hc_gtk2_engine_hack_menu_shell_leave(GtkWidget * widget,GdkEventCrossing * event,gpointer user_data)380 hc_gtk2_engine_hack_menu_shell_leave(GtkWidget *widget,
381 GdkEventCrossing *event,
382 gpointer user_data)
383 {
384 if (GE_IS_MENU_SHELL(widget))
385 {
386 GList *children = NULL, *child = NULL;
387
388 if (GE_IS_CONTAINER(widget))
389 {
390 children = gtk_container_get_children(GTK_CONTAINER(widget));
391
392 for (child = g_list_first(children); child; child = g_list_next(child))
393 {
394 if ((child->data) && GE_IS_MENU_ITEM(child->data) &&
395 (GTK_WIDGET_STATE(GTK_WIDGET(child->data)) != GTK_STATE_INSENSITIVE))
396 {
397 if ((!GE_IS_MENU(GTK_MENU_ITEM(child->data)->submenu)) ||
398 (!(GTK_WIDGET_REALIZED(GTK_MENU_ITEM(child->data)->submenu) &&
399 GTK_WIDGET_VISIBLE(GTK_MENU_ITEM(child->data)->submenu) &&
400 GTK_WIDGET_REALIZED(GTK_MENU(GTK_MENU_ITEM(child->data)->submenu)->toplevel) &&
401 GTK_WIDGET_VISIBLE(GTK_MENU(GTK_MENU_ITEM(child->data)->submenu)->toplevel))))
402 {
403 gtk_widget_set_state (GTK_WIDGET(child->data), GTK_STATE_NORMAL);
404 }
405 }
406 }
407
408 if (children)
409 g_list_free(children);
410 }
411 }
412
413 return FALSE;
414 }
415
416 /***********************************************
417 * hc_gtk2_engine_menu_shell_setup_signals -
418 *
419 * Setup Menu Shell with signals to ensure
420 * prelight works on items
421 ***********************************************/
422 void
hc_gtk2_engine_hack_menu_shell_setup_signals(GtkWidget * widget)423 hc_gtk2_engine_hack_menu_shell_setup_signals(GtkWidget *widget)
424 {
425 if (GE_IS_MENU_BAR(widget))
426 {
427 gint id = 0;
428
429 if (!g_object_get_data(G_OBJECT(widget), "HC_MENU_SHELL_HACK_SET"))
430 {
431 id = g_signal_connect(G_OBJECT(widget), "motion-notify-event",
432 (GCallback)hc_gtk2_engine_hack_menu_shell_motion,
433 NULL);
434
435 g_object_set_data(G_OBJECT(widget), "HC_MENU_SHELL_MOTION_ID", (gpointer)id);
436
437 id = g_signal_connect(G_OBJECT(widget), "leave-notify-event",
438 (GCallback)hc_gtk2_engine_hack_menu_shell_leave,
439 NULL);
440 g_object_set_data(G_OBJECT(widget), "HC_MENU_SHELL_LEAVE_ID", (gpointer)id);
441
442 id = g_signal_connect(G_OBJECT(widget), "destroy-event",
443 (GCallback)hc_gtk2_engine_hack_menu_shell_destroy,
444 NULL);
445 g_object_set_data(G_OBJECT(widget), "HC_MENU_SHELL_DESTROY_ID", (gpointer)id);
446
447 g_object_set_data(G_OBJECT(widget), "HC_MENU_SHELL_HACK_SET", (gpointer)1);
448
449 id = g_signal_connect(G_OBJECT(widget), "style-set",
450 (GCallback)hc_gtk2_engine_hack_menu_shell_style_set,
451 NULL);
452 g_object_set_data(G_OBJECT(widget), "HC_MENU_SHELL_STYLE_SET_ID", (gpointer)id);
453 }
454 }
455 }
456
457 /***********************************************
458 * hc_gtk2_engine_hack_menu_shell_cleanuo_signals -
459 *
460 * Cleanup/remove Menu Shell signals
461 ***********************************************/
462 void
hc_gtk2_engine_hack_menu_shell_cleanup_signals(GtkWidget * widget)463 hc_gtk2_engine_hack_menu_shell_cleanup_signals(GtkWidget *widget)
464 {
465 if (GE_IS_MENU_BAR(widget))
466 {
467 gint id = 0;
468
469 id = (gint)g_object_steal_data (G_OBJECT(widget), "HC_MENU_SHELL_MOTION_ID");
470 g_signal_handler_disconnect(G_OBJECT(widget), id);
471
472 id = (gint)g_object_steal_data (G_OBJECT(widget), "HC_MENU_SHELL_LEAVE_ID");
473 g_signal_handler_disconnect(G_OBJECT(widget), id);
474
475 id = (gint)g_object_steal_data (G_OBJECT(widget), "HC_MENU_SHELL_DESTROY_ID");
476 g_signal_handler_disconnect(G_OBJECT(widget), id);
477
478 id = (gint)g_object_steal_data (G_OBJECT(widget), "HC_MENU_SHELL_STYLE_SET_ID");
479 g_signal_handler_disconnect(G_OBJECT(widget), id);
480
481 g_object_steal_data (G_OBJECT(widget), "HC_MENU_SHELL_HACK_SET");
482 }
483 }
484