src/gtkblist.c

changeset 11692
30e8a4a1b965
parent 11643
f04408721780
child 11709
da6e9cd5f6d9
equal deleted inserted replaced
11691:d46ace30ec7e 11692:30e8a4a1b965
56 56
57 #include <gdk/gdkkeysyms.h> 57 #include <gdk/gdkkeysyms.h>
58 #include <gtk/gtk.h> 58 #include <gtk/gtk.h>
59 #include <gdk/gdk.h> 59 #include <gdk/gdk.h>
60 60
61 /* if someone explicitly asked for drop shadows, we also need to make
62 sure that their environment can support it. If not, tough */
63 #ifdef WANT_DROP_SHADOW
64 # if !GTK_CHECK_VERSION(2,2,0) || (defined(__APPLE__) && defined(__MACH__))
65 # undef WANT_DROP_SHADOW
66 # endif
67 #endif
68
69 typedef struct 61 typedef struct
70 { 62 {
71 GaimAccount *account; 63 GaimAccount *account;
72 64
73 GtkWidget *window; 65 GtkWidget *window;
138 130
139 struct _gaim_gtk_blist_node { 131 struct _gaim_gtk_blist_node {
140 GtkTreeRowReference *row; 132 GtkTreeRowReference *row;
141 gboolean contact_expanded; 133 gboolean contact_expanded;
142 }; 134 };
143
144
145 #ifdef WANT_DROP_SHADOW
146 /**************************** Weird drop shadow stuff *******************/
147 /* This is based on a patch for drop shadows in GTK+ menus available at
148 * http://www.xfce.org/gtkmenu-shadow/
149 */
150
151 enum side {
152 EAST_SIDE,
153 SOUTH_SIDE
154 };
155
156 static const double shadow_strip_l[5] = {
157 .937, .831, .670, .478, .180
158 };
159
160 static const double bottom_left_corner[25] = {
161 1.00, .682, .423, .333, .258,
162 1.00, .898, .800, .682, .584,
163 1.00, .937, .874, .800, .737,
164 1.00, .968, .937, .898, .866,
165 1.00, .988, .976, .960, .945
166 };
167
168 static const double bottom_right_corner[25] = {
169 .258, .584, .737, .866, .945,
170 .584, .682, .800, .898, .960,
171 .737, .800, .874, .937, .976,
172 .866, .898, .937, .968, .988,
173 .945, .960, .976, .988, .996
174 };
175
176 static const double top_right_corner[25] = {
177 1.00, 1.00, 1.00, 1.00, 1.00,
178 .686, .898, .937, .968, .988,
179 .423, .803, .874, .937, .976,
180 .333, .686, .800, .898, .960,
181 .258, .584, .737, .866, .945
182 };
183
184 static const double top_left_corner[25] = {
185 .988, .968, .937, .898, .498,
186 .976, .937, .874, .803, .423,
187 .960, .898, .800, .686, .333,
188 .945, .866, .737, .584, .258,
189 .941, .847, .698, .521, .215
190 };
191
192
193 static gboolean xcomposite_is_present()
194 {
195 static gboolean result = FALSE;
196 #ifndef _WIN32
197 static gboolean known = FALSE;
198 int i, j, k;
199
200 if (!known) {
201 /* I don't actually care about versions/etc. */
202 if (XQueryExtension(GDK_DISPLAY(), "Composite", &i, &j, &k) == True)
203 result = TRUE;
204 known = TRUE;
205 }
206 #endif
207
208 return result;
209 }
210
211 static GdkPixbuf *
212 get_pixbuf(GtkWidget *menu, int x, int y, int width, int height)
213 {
214 GdkPixbuf *dest, *src;
215 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(menu));
216 GdkWindow *root = gdk_screen_get_root_window (screen);
217 gint screen_height = gdk_screen_get_height (screen);
218 gint screen_width = gdk_screen_get_width (screen);
219 gint original_width = width;
220 gint original_height = height;
221
222 if (x < 0) {
223 width += x;
224 x = 0;
225 }
226
227 if (y < 0) {
228 height += y;
229 y = 0;
230 }
231
232 if (x + width > screen_width) {
233 width = screen_width - x;
234 }
235
236 if (y + height > screen_height) {
237 height = screen_height - y;
238 }
239
240 if (width <= 0 || height <= 0)
241 return NULL;
242
243 dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
244 original_width, original_height);
245 src = gdk_pixbuf_get_from_drawable(NULL, root, NULL, x, y, 0, 0,
246 width, height);
247 gdk_pixbuf_copy_area (src, 0, 0, width, height, dest, 0, 0);
248
249 g_object_unref (G_OBJECT (src));
250
251 return dest;
252 }
253
254 static void
255 shadow_paint(GaimGtkBuddyList *blist, GdkRectangle *area, enum side shadow)
256 {
257 gint width, height;
258 GdkGC *gc = gtkblist->tipwindow->style->black_gc;
259
260 switch (shadow) {
261 case EAST_SIDE:
262 if (gtkblist->east != NULL) {
263 if (area)
264 gdk_gc_set_clip_rectangle (gc, area);
265
266 width = gdk_pixbuf_get_width (gtkblist->east);
267 height = gdk_pixbuf_get_height (gtkblist->east);
268
269 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->east_shadow), gc,
270 gtkblist->east, 0, 0, 0, 0, width, height,
271 GDK_RGB_DITHER_NONE, 0, 0);
272
273 if (area)
274 gdk_gc_set_clip_rectangle (gc, NULL);
275 }
276 break;
277
278 case SOUTH_SIDE:
279 if (blist->south != NULL) {
280 if (area)
281 gdk_gc_set_clip_rectangle (gc, area);
282
283 width = gdk_pixbuf_get_width (gtkblist->south);
284 height = gdk_pixbuf_get_height (gtkblist->south);
285
286 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->south_shadow), gc,
287 gtkblist->south, 0, 0, 0, 0, width, height,
288 GDK_RGB_DITHER_NONE, 0, 0);
289
290 if (area)
291 gdk_gc_set_clip_rectangle (gc, NULL);
292 }
293 break;
294 }
295 }
296
297 static void
298 pixbuf_add_shadow (GdkPixbuf *pb, enum side shadow)
299 {
300 gint width, rowstride, height;
301 gint i;
302 guchar *pixels, *p;
303
304 width = gdk_pixbuf_get_width (pb);
305 height = gdk_pixbuf_get_height (pb);
306 rowstride = gdk_pixbuf_get_rowstride (pb);
307 pixels = gdk_pixbuf_get_pixels (pb);
308
309 switch (shadow) {
310 case EAST_SIDE:
311 if (height > 5) {
312 for (i = 0; i < width; i++) {
313 gint j, k;
314
315 p = pixels + (i * rowstride);
316 for (j = 0, k = 0; j < 3 * width; j += 3, k++) {
317 p[j] = (guchar) (p[j] * top_right_corner [i * width + k]);
318 p[j + 1] = (guchar) (p[j + 1] * top_right_corner [i * width + k]);
319 p[j + 2] = (guchar) (p[j + 2] * top_right_corner [i * width + k]);
320 }
321 }
322
323 i = 5;
324 } else {
325 i = 0;
326 }
327
328 for (; i < height; i++) {
329 gint j, k;
330
331 p = pixels + (i * rowstride);
332 for (j = 0, k = 0; j < 3 * width; j += 3, k++) {
333 p[j] = (guchar) (p[j] * shadow_strip_l[width - 1 - k]);
334 p[j + 1] = (guchar) (p[j + 1] * shadow_strip_l[width - 1 - k]);
335 p[j + 2] = (guchar) (p[j + 2] * shadow_strip_l[width - 1 - k]);
336 }
337 }
338 break;
339
340 case SOUTH_SIDE:
341 for (i = 0; i < height; i++) {
342 gint j, k;
343
344 p = pixels + (i * rowstride);
345 for (j = 0, k = 0; j < 3 * height; j += 3, k++) {
346 p[j] = (guchar) (p[j] * bottom_left_corner[i * height + k]);
347 p[j + 1] = (guchar) (p[j + 1] * bottom_left_corner[i * height + k]);
348 p[j + 2] = (guchar) (p[j + 2] * bottom_left_corner[i * height + k]);
349 }
350
351 p = pixels + (i * rowstride) + 3 * height;
352 for (j = 0, k = 0; j < (width * 3) - (6 * height); j += 3, k++) {
353 p[j] = (guchar) (p[j] * bottom_right_corner [i * height]);
354 p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner [i * height]);
355 p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner [i * height]);
356 }
357
358 p = pixels + (i * rowstride) + ((width * 3) - (3 * height));
359 for (j = 0, k = 0; j < 3 * height; j += 3, k++) {
360 p[j] = (guchar) (p[j] * bottom_right_corner[i * height + k]);
361 p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner[i * height + k]);
362 p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner[i * height + k]);
363 }
364 }
365 break;
366 }
367 }
368
369 static gboolean
370 map_shadow_windows (gpointer data)
371 {
372 GaimGtkBuddyList *blist = (GaimGtkBuddyList*)data;
373 GtkWidget *widget = blist->tipwindow;
374 GdkPixbuf *pixbuf;
375 int x, y;
376
377 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
378 pixbuf = get_pixbuf(widget,
379 x + widget->allocation.width, y,
380 5, widget->allocation.height + 5);
381 if (pixbuf != NULL)
382 {
383 pixbuf_add_shadow (pixbuf, EAST_SIDE);
384 if (blist->east != NULL)
385 {
386 g_object_unref (G_OBJECT (blist->east));
387 }
388 blist->east = pixbuf;
389 }
390
391 pixbuf = get_pixbuf (widget,
392 x, y + widget->allocation.height,
393 widget->allocation.width + 5, 5);
394 if (pixbuf != NULL)
395 {
396 pixbuf_add_shadow (pixbuf, SOUTH_SIDE);
397 if (blist->south != NULL)
398 {
399 g_object_unref (G_OBJECT (blist->south));
400 }
401 blist->south = pixbuf;
402 }
403
404 gdk_window_move_resize (blist->east_shadow,
405 x + widget->allocation.width, MAX(0,y),
406 5, MIN(widget->allocation.height, gdk_screen_height()));
407
408 gdk_window_move_resize (blist->south_shadow,
409 MAX(0,x), y + widget->allocation.height,
410 MIN(widget->allocation.width + 5, gdk_screen_width()), 5);
411
412 gdk_window_show (blist->east_shadow);
413 gdk_window_show (blist->south_shadow);
414 shadow_paint(blist, NULL, EAST_SIDE);
415 shadow_paint(blist, NULL, SOUTH_SIDE);
416
417 return FALSE;
418 }
419
420 /**************** END WEIRD DROP SHADOW STUFF ***********************************/
421 #endif /* ifdef WANT_DROP_SHADOW */
422 135
423 136
424 static char dim_grey_string[8] = ""; 137 static char dim_grey_string[8] = "";
425 static char *dim_grey() 138 static char *dim_grey()
426 { 139 {
2321 g_object_unref (avatar); 2034 g_object_unref (avatar);
2322 2035
2323 g_object_unref (pixbuf); 2036 g_object_unref (pixbuf);
2324 g_object_unref (layout); 2037 g_object_unref (layout);
2325 2038
2326 #ifdef WANT_DROP_SHADOW
2327 if (!xcomposite_is_present()) {
2328 shadow_paint(gtkblist, NULL, EAST_SIDE);
2329 shadow_paint(gtkblist, NULL, SOUTH_SIDE);
2330 }
2331 #endif
2332
2333 return; 2039 return;
2334 } 2040 }
2335 2041
2336 static void gaim_gtk_blist_tooltip_destroy() 2042 static void gaim_gtk_blist_tooltip_destroy()
2337 { 2043 {
2341 if (gtkblist->tipwindow == NULL) 2047 if (gtkblist->tipwindow == NULL)
2342 return; 2048 return;
2343 2049
2344 gtk_widget_destroy(gtkblist->tipwindow); 2050 gtk_widget_destroy(gtkblist->tipwindow);
2345 gtkblist->tipwindow = NULL; 2051 gtkblist->tipwindow = NULL;
2346
2347 #ifdef WANT_DROP_SHADOW
2348 if (!xcomposite_is_present()) {
2349 gdk_window_set_user_data (gtkblist->east_shadow, NULL);
2350 gdk_window_destroy (gtkblist->east_shadow);
2351 gtkblist->east_shadow = NULL;
2352
2353 gdk_window_set_user_data (gtkblist->south_shadow, NULL);
2354 gdk_window_destroy (gtkblist->south_shadow);
2355 gtkblist->south_shadow = NULL;
2356 }
2357 #endif
2358 } 2052 }
2359 2053
2360 static gboolean gaim_gtk_blist_expand_timeout(GtkWidget *tv) 2054 static gboolean gaim_gtk_blist_expand_timeout(GtkWidget *tv)
2361 { 2055 {
2362 GtkTreePath *path; 2056 GtkTreePath *path;
2411 #endif 2105 #endif
2412 PangoLayout *layout; 2106 PangoLayout *layout;
2413 gboolean tooltip_top = FALSE; 2107 gboolean tooltip_top = FALSE;
2414 struct _gaim_gtk_blist_node *gtknode; 2108 struct _gaim_gtk_blist_node *gtknode;
2415 GdkRectangle mon_size; 2109 GdkRectangle mon_size;
2416 #ifdef WANT_DROP_SHADOW
2417 GdkWindowAttr attr;
2418 #endif
2419 2110
2420 /* 2111 /*
2421 * Attempt to free the previous tooltip. I have a feeling 2112 * Attempt to free the previous tooltip. I have a feeling
2422 * this is never needed... but just in case. 2113 * this is never needed... but just in case.
2423 */ 2114 */
2446 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); 2137 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE);
2447 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); 2138 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips");
2448 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", 2139 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event",
2449 G_CALLBACK(gaim_gtk_blist_paint_tip), node); 2140 G_CALLBACK(gaim_gtk_blist_paint_tip), node);
2450 gtk_widget_ensure_style (gtkblist->tipwindow); 2141 gtk_widget_ensure_style (gtkblist->tipwindow);
2451
2452 #ifdef WANT_DROP_SHADOW
2453 if (!xcomposite_is_present()) {
2454 attr.window_type = GDK_WINDOW_TEMP;
2455 attr.override_redirect = TRUE;
2456 attr.x = gtkblist->tipwindow->allocation.x;
2457 attr.y = gtkblist->tipwindow->allocation.y;
2458 attr.width = gtkblist->tipwindow->allocation.width;
2459 attr.height = gtkblist->tipwindow->allocation.height;
2460 attr.wclass = GDK_INPUT_OUTPUT;
2461 attr.visual = gtk_widget_get_visual (gtkblist->window);
2462 attr.colormap = gtk_widget_get_colormap (gtkblist->window);
2463
2464 attr.event_mask = gtk_widget_get_events (gtkblist->tipwindow);
2465
2466 attr.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
2467 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
2468 gtkblist->east_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr,
2469 GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP);
2470 gdk_window_set_user_data (gtkblist->east_shadow, gtkblist->tipwindow);
2471 gdk_window_set_back_pixmap (gtkblist->east_shadow, NULL, FALSE);
2472
2473 gtkblist->south_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr,
2474 GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP);
2475 gdk_window_set_user_data (gtkblist->south_shadow, gtkblist->tipwindow);
2476 gdk_window_set_back_pixmap (gtkblist->south_shadow, NULL, FALSE);
2477 }
2478 #endif /* ifdef WANT_DROP_SHADOW */
2479 2142
2480 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); 2143 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL);
2481 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); 2144 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
2482 pango_layout_set_width(layout, 300000); 2145 pango_layout_set_width(layout, 300000);
2483 pango_layout_set_markup(layout, gtkblist->tooltiptext, strlen(gtkblist->tooltiptext)); 2146 pango_layout_set_markup(layout, gtkblist->tooltiptext, strlen(gtkblist->tooltiptext));
2551 2214
2552 g_object_unref (layout); 2215 g_object_unref (layout);
2553 gtk_widget_set_size_request(gtkblist->tipwindow, w, h); 2216 gtk_widget_set_size_request(gtkblist->tipwindow, w, h);
2554 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); 2217 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y);
2555 gtk_widget_show(gtkblist->tipwindow); 2218 gtk_widget_show(gtkblist->tipwindow);
2556
2557 #ifdef WANT_DROP_SHADOW
2558 if (!xcomposite_is_present()) {
2559 map_shadow_windows(gtkblist);
2560 }
2561 #endif
2562 2219
2563 return FALSE; 2220 return FALSE;
2564 } 2221 }
2565 2222
2566 static gboolean gaim_gtk_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context, 2223 static gboolean gaim_gtk_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context,

mercurial