]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus_cycle_popup.c
wip: Add config_parser.c which will provide a nice means to specify config variables...
[dana/openbox.git] / openbox / focus_cycle_popup.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    focus_cycle_popup.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "focus_cycle_popup.h"
21 #include "focus_cycle.h"
22 #include "popup.h"
23 #include "client.h"
24 #include "client_set.h"
25 #include "screen.h"
26 #include "focus.h"
27 #include "openbox.h"
28 #include "config.h"
29 #include "window.h"
30 #include "event.h"
31 #include "obrender/render.h"
32
33 #include <X11/Xlib.h>
34 #include <glib.h>
35
36 /* Size of the icons, which can appear inside or outside of a hilite box */
37 #define ICON_SIZE (gint)config_theme_window_list_icon_size
38 /* Size of the hilite box around a window's icon */
39 #define HILITE_SIZE (ICON_SIZE + 2*HILITE_OFFSET)
40 /* Width of the outer ring around the hilite box */
41 #define HILITE_WIDTH 2
42 /* Space between the outer ring around the hilite box and the icon inside it */
43 #define HILITE_MARGIN 1
44 /* Total distance from the edge of the hilite box to the icon inside it */
45 #define HILITE_OFFSET (HILITE_WIDTH + HILITE_MARGIN)
46 /* Margin area around the outside of the dialog */
47 #define OUTSIDE_BORDER 3
48 /* Margin area around the text */
49 #define TEXT_BORDER 2
50 /* Scroll the list-mode list when the cursor gets within this many rows of the
51    top or bottom */
52 #define SCROLL_MARGIN 4
53
54 typedef struct _ObFocusCyclePopup       ObFocusCyclePopup;
55 typedef struct _ObFocusCyclePopupTarget ObFocusCyclePopupTarget;
56
57 struct _ObFocusCyclePopupTarget
58 {
59     ObClient *client;
60     RrImage *icon;
61     gchar *text;
62     Window iconwin;
63     /* This is used when the popup is in list mode */
64     Window textwin;
65 };
66
67 struct _ObFocusCyclePopup
68 {
69     ObWindow obwin;
70     Window bg;
71
72     /* This is used when the popup is in icon mode */
73     Window icon_mode_text;
74
75     Window list_mode_up;
76     Window list_mode_down;
77
78     GList *targets;
79     gint n_targets;
80
81     const ObFocusCyclePopupTarget *last_target;
82
83     gint maxtextw;
84
85     /* How are the list is scrolled, in scroll mode */
86     gint scroll;
87
88     RrAppearance *a_bg;
89     RrAppearance *a_text;
90     RrAppearance *a_hilite_text;
91     RrAppearance *a_icon;
92     RrAppearance *a_arrow;
93
94     gboolean mapped;
95     ObFocusCyclePopupMode mode;
96 };
97
98 /*! This popup shows all possible windows */
99 static ObFocusCyclePopup popup;
100 /*! This popup shows a single window */
101 static ObIconPopup *single_popup;
102
103 static gchar   *popup_get_name (ObClient *c);
104 static gboolean popup_setup    (ObFocusCyclePopup *p,
105                                 const ObClientSet *set,
106                                 gboolean create_targets,
107                                 gboolean refresh_targets,
108                                 gboolean linear);
109 static void     popup_render   (ObFocusCyclePopup *p,
110                                 const ObClient *c);
111
112 static Window create_window(Window parent, guint bwidth, gulong mask,
113                             XSetWindowAttributes *attr)
114 {
115     return XCreateWindow(obt_display, parent, 0, 0, 1, 1, bwidth,
116                          RrDepth(ob_rr_inst), InputOutput,
117                          RrVisual(ob_rr_inst), mask, attr);
118 }
119
120 void focus_cycle_popup_startup(gboolean reconfig)
121 {
122     XSetWindowAttributes attrib;
123     RrPixel32 *p;
124
125     single_popup = icon_popup_new();
126
127     popup.obwin.type = OB_WINDOW_CLASS_INTERNAL;
128     popup.a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg);
129     popup.a_hilite_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
130     popup.a_text = RrAppearanceCopy(ob_rr_theme->osd_unhilite_label);
131     popup.a_icon = RrAppearanceCopy(ob_rr_theme->a_clear);
132     popup.a_arrow = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
133
134     popup.a_hilite_text->surface.parent = popup.a_bg;
135     popup.a_text->surface.parent = popup.a_bg;
136     popup.a_icon->surface.parent = popup.a_bg;
137
138     popup.a_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
139     popup.a_hilite_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
140
141     /* 2 textures. texture[0] is the icon.  texture[1] is the hilight, and
142        may or may not be used */
143     RrAppearanceAddTextures(popup.a_icon, 2);
144
145     RrAppearanceClearTextures(popup.a_icon);
146     popup.a_icon->texture[0].type = RR_TEXTURE_IMAGE;
147
148     RrAppearanceClearTextures(popup.a_arrow);
149     popup.a_arrow->texture[0].type = RR_TEXTURE_MASK;
150     popup.a_arrow->texture[0].data.mask.color =
151         ob_rr_theme->osd_text_active_color;
152
153     attrib.override_redirect = True;
154     attrib.border_pixel=RrColorPixel(ob_rr_theme->osd_border_color);
155     popup.bg = create_window(obt_root(ob_screen), ob_rr_theme->obwidth,
156                              CWOverrideRedirect | CWBorderPixel, &attrib);
157
158     /* create the text window used for the icon-mode popup */
159     popup.icon_mode_text = create_window(popup.bg, 0, 0, NULL);
160
161     /* create the windows for the up and down arrows */
162     popup.list_mode_up = create_window(popup.bg, 0, 0, NULL);
163     popup.list_mode_down = create_window(popup.bg, 0, 0, NULL);
164
165     popup.targets = NULL;
166     popup.n_targets = 0;
167     popup.last_target = NULL;
168
169     /* set up the hilite texture for the icon */
170     popup.a_icon->texture[1].data.rgba.width = HILITE_SIZE;
171     popup.a_icon->texture[1].data.rgba.height = HILITE_SIZE;
172     popup.a_icon->texture[1].data.rgba.alpha = 0xff;
173     p = g_new(RrPixel32, HILITE_SIZE * HILITE_SIZE);
174     popup.a_icon->texture[1].data.rgba.data = p;
175
176     /* create the hilite under the target icon */
177     {
178         RrPixel32 color;
179         RrColor *tc;
180         gint x, y, o;
181
182         tc = ob_rr_theme->osd_text_active_color;
183         color = ((tc->r & 0xff) << RrDefaultRedOffset) +
184             ((tc->g & 0xff) << RrDefaultGreenOffset) +
185             ((tc->b & 0xff) << RrDefaultBlueOffset);
186
187         o = 0;
188         for (x = 0; x < HILITE_SIZE; x++)
189             for (y = 0; y < HILITE_SIZE; y++) {
190                 guchar a;
191
192                 if (x < HILITE_WIDTH ||
193                     x >= HILITE_SIZE - HILITE_WIDTH ||
194                     y < HILITE_WIDTH ||
195                     y >= HILITE_SIZE - HILITE_WIDTH)
196                 {
197                     /* the border of the target */
198                     a = 0x88;
199                 } else {
200                     /* the background of the target */
201                     a = 0x22;
202                 }
203
204                 p[o++] = color + (a << RrDefaultAlphaOffset);
205             }
206     }
207
208     stacking_add(INTERNAL_AS_WINDOW(&popup));
209     window_add(&popup.bg, INTERNAL_AS_WINDOW(&popup));
210 }
211
212 void focus_cycle_popup_shutdown(gboolean reconfig)
213 {
214     icon_popup_free(single_popup);
215
216     window_remove(popup.bg);
217     stacking_remove(INTERNAL_AS_WINDOW(&popup));
218
219     while(popup.targets) {
220         ObFocusCyclePopupTarget *t = popup.targets->data;
221
222         RrImageUnref(t->icon);
223         g_free(t->text);
224         XDestroyWindow(obt_display, t->iconwin);
225         XDestroyWindow(obt_display, t->textwin);
226         g_slice_free(ObFocusCyclePopupTarget, t);
227
228         popup.targets = g_list_delete_link(popup.targets, popup.targets);
229     }
230
231     g_free(popup.a_icon->texture[1].data.rgba.data);
232     popup.a_icon->texture[1].data.rgba.data = NULL;
233
234     XDestroyWindow(obt_display, popup.list_mode_up);
235     XDestroyWindow(obt_display, popup.list_mode_down);
236     XDestroyWindow(obt_display, popup.icon_mode_text);
237     XDestroyWindow(obt_display, popup.bg);
238
239     RrAppearanceFree(popup.a_arrow);
240     RrAppearanceFree(popup.a_icon);
241     RrAppearanceFree(popup.a_hilite_text);
242     RrAppearanceFree(popup.a_text);
243     RrAppearanceFree(popup.a_bg);
244 }
245
246 static void popup_target_free(ObFocusCyclePopupTarget *t)
247 {
248     RrImageUnref(t->icon);
249     g_free(t->text);
250     XDestroyWindow(obt_display, t->iconwin);
251     XDestroyWindow(obt_display, t->textwin);
252     g_slice_free(ObFocusCyclePopupTarget, t);
253 }
254
255 static gboolean popup_setup(ObFocusCyclePopup *p, const ObClientSet *set,
256                             gboolean create_targets, gboolean refresh_targets,
257                             gboolean linear)
258 {
259     gint maxwidth, n;
260     GList *it;
261     GList *rtargets; /* old targets for refresh */
262     GList *rtlast;
263     gboolean change;
264
265     if (refresh_targets) {
266         rtargets = p->targets;
267         rtlast = g_list_last(rtargets);
268         p->targets = NULL;
269         p->n_targets = 0;
270         change = FALSE;
271     }
272     else {
273         rtargets = rtlast = NULL;
274         change = TRUE;
275     }
276
277     g_assert(p->targets == NULL);
278     g_assert(p->n_targets == 0);
279
280     /* make its width to be the width of all the possible titles */
281
282     /* build a list of all the valid focus targets and measure their strings,
283        and count them */
284     maxwidth = 0;
285     n = 0;
286     for (it = g_list_last(linear ? client_list : focus_order);
287          it;
288          it = g_list_previous(it))
289     {
290         ObClient *ft = it->data;
291
292         if (client_set_contains(set, ft) && focus_cycle_valid(ft)) {
293             GList *rit;
294
295             /* reuse the target if possible during refresh */
296             for (rit = rtlast; rit; rit = g_list_previous(rit)) {
297                 ObFocusCyclePopupTarget *t = rit->data;
298                 if (t->client == ft) {
299                     if (rit == rtlast)
300                         rtlast = g_list_previous(rit);
301                     rtargets = g_list_remove_link(rtargets, rit);
302
303                     p->targets = g_list_concat(rit, p->targets);
304                     ++n;
305
306                     if (rit != rtlast)
307                         change = TRUE; /* order changed */
308                     break;
309                 }
310             }
311
312             if (!rit) {
313                 gchar *text = popup_get_name(ft);
314
315                 /* measure */
316                 p->a_text->texture[0].data.text.string = text;
317                 maxwidth = MAX(maxwidth, RrMinWidth(p->a_text));
318
319                 if (!create_targets) {
320                     g_free(text);
321                 } else {
322                     ObFocusCyclePopupTarget *t =
323                         g_slice_new(ObFocusCyclePopupTarget);
324
325                     t->client = ft;
326                     t->text = text;
327                     t->icon = client_icon(t->client);
328                     RrImageRef(t->icon); /* own the icon so it won't go away */
329                     t->iconwin = create_window(p->bg, 0, 0, NULL);
330                     t->textwin = create_window(p->bg, 0, 0, NULL);
331
332                     p->targets = g_list_prepend(p->targets, t);
333                     ++n;
334
335                     change = TRUE; /* added a window */
336                 }
337             }
338         }
339     }
340
341     if (rtargets) {
342         change = TRUE; /* removed a window */
343
344         while (rtargets) {
345             popup_target_free(rtargets->data);
346             rtargets = g_list_delete_link(rtargets, rtargets);
347         }
348     }
349
350     p->n_targets = n;
351     if (refresh_targets)
352         /* don't shrink when refreshing */
353         p->maxtextw = MAX(p->maxtextw, maxwidth);
354     else
355         p->maxtextw = maxwidth;
356
357     return change;
358 }
359
360 static void popup_cleanup(void)
361 {
362     while(popup.targets) {
363         popup_target_free(popup.targets->data);
364         popup.targets = g_list_delete_link(popup.targets, popup.targets);
365     }
366     popup.n_targets = 0;
367     popup.last_target = NULL;
368 }
369
370 static gchar *popup_get_name(ObClient *c)
371 {
372     ObClient *p;
373     gchar *title;
374     const gchar *desk = NULL;
375     gchar *ret;
376
377     /* find our highest direct parent */
378     p = client_search_top_direct_parent(c);
379
380     if (c->desktop != DESKTOP_ALL && c->desktop != screen_desktop)
381         desk = screen_desktop_names[c->desktop];
382
383     title = c->iconic ? c->icon_title : c->title;
384
385     /* use the transient's parent's title/icon if we don't have one */
386     if (p != c && title[0] == '\0')
387         title = p->iconic ? p->icon_title : p->title;
388
389     if (desk)
390         ret = g_strdup_printf("%s [%s]", title, desk);
391     else
392         ret = g_strdup(title);
393
394     return ret;
395 }
396
397 static void popup_render(ObFocusCyclePopup *p, const ObClient *c)
398 {
399     gint ml, mt, mr, mb;
400     gint l, t, r, b;
401     gint x, y, w, h;
402     const Rect *screen_area = NULL;
403     gint i;
404     GList *it;
405     const ObFocusCyclePopupTarget *newtarget;
406     ObFocusCyclePopupMode mode = p->mode;
407     gint icons_per_row;
408     gint icon_rows;
409     gint textw, texth;
410     gint selected_pos;
411     gint last_scroll;
412
413     /* vars for icon mode */
414     gint icon_mode_textx;
415     gint icon_mode_texty;
416     gint icons_center_x;
417
418     /* vars for list mode */
419     gint list_mode_icon_column_w = HILITE_SIZE + OUTSIDE_BORDER;
420     gint up_arrow_x, down_arrow_x;
421     gint up_arrow_y, down_arrow_y;
422     gboolean showing_arrows = FALSE;
423
424     g_assert(mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ||
425              mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST);
426
427     screen_area = screen_physical_area_primary(FALSE);
428
429     /* get the outside margins */
430     RrMargins(p->a_bg, &ml, &mt, &mr, &mb);
431
432     /* get our outside borders */
433     l = ml + OUTSIDE_BORDER;
434     r = mr + OUTSIDE_BORDER;
435     t = mt + OUTSIDE_BORDER;
436     b = mb + OUTSIDE_BORDER;
437
438     /* get the width from the text and keep it within limits */
439     w = l + r + p->maxtextw;
440     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
441         /* when in list mode, there are icons down the side */
442         w += list_mode_icon_column_w;
443     w = MIN(w, MAX(screen_area->width/3, POPUP_WIDTH)); /* max width */
444     w = MAX(w, POPUP_WIDTH); /* min width */
445
446     /* get the text height */
447     texth = RrMinHeight(p->a_hilite_text);
448     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
449         texth = MAX(MAX(texth, RrMinHeight(p->a_text)), HILITE_SIZE);
450     else
451         texth += TEXT_BORDER * 2;
452
453     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) {
454         /* how many icons will fit in that row? make the width fit that */
455         w -= l + r;
456         icons_per_row = (w + HILITE_SIZE - 1) / HILITE_SIZE;
457         w = icons_per_row * HILITE_SIZE + l + r;
458
459         /* how many rows do we need? */
460         icon_rows = (p->n_targets-1) / icons_per_row + 1;
461     } else {
462         /* in list mode, there is one column of icons.. */
463         icons_per_row = 1;
464         /* maximum is 80% of the screen height */
465         icon_rows = MIN(p->n_targets,
466                         (4*screen_area->height/5) /* 80% of the screen */
467                         /
468                         MAX(HILITE_SIZE, texth)); /* height of each row */
469         /* but make sure there is always one */
470         icon_rows = MAX(icon_rows, 1);
471     }
472
473     /* get the text width */
474     textw = w - l - r;
475     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
476         /* leave space on the side for the icons */
477         textw -= list_mode_icon_column_w;
478
479     if (!p->mapped)
480         /* reset the scrolling when the dialog is first shown */
481         p->scroll = 0;
482
483     /* find the height of the dialog */
484     h = t + b + (icon_rows * MAX(HILITE_SIZE, texth));
485     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS)
486         /* in icon mode the text sits below the icons, so make some space */
487         h += OUTSIDE_BORDER + texth;
488
489     /* find the focused target */
490     newtarget = NULL;
491     for (i = 0, it = p->targets; it; ++i, it = g_list_next(it)) {
492         const ObFocusCyclePopupTarget *target = it->data;
493         if (target->client == c) {
494             /* save the target */
495             newtarget = target;
496             break;
497         }
498     }
499     selected_pos = i;
500     g_assert(newtarget != NULL);
501
502     /* scroll the list if needed */
503     last_scroll = p->scroll;
504     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) {
505         const gint top = p->scroll + SCROLL_MARGIN;
506         const gint bottom = p->scroll + icon_rows - SCROLL_MARGIN;
507         const gint min_scroll = 0;
508         const gint max_scroll = p->n_targets - icon_rows;
509
510         if (top - selected_pos >= 0) {
511             p->scroll -= top - selected_pos + 1;
512             p->scroll = MAX(p->scroll, min_scroll);
513         } else if (selected_pos - bottom >= 0) {
514             p->scroll += selected_pos - bottom + 1;
515             p->scroll = MIN(p->scroll, max_scroll);
516         }
517     }
518
519     /* show the scroll arrows when appropriate */
520     if (p->scroll && mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) {
521         XMapWindow(obt_display, p->list_mode_up);
522         showing_arrows = TRUE;
523     } else
524         XUnmapWindow(obt_display, p->list_mode_up);
525
526     if (p->scroll < p->n_targets - icon_rows &&
527         mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
528     {
529         XMapWindow(obt_display, p->list_mode_down);
530         showing_arrows = TRUE;
531     } else
532         XUnmapWindow(obt_display, p->list_mode_down);
533
534     /* make space for the arrows */
535     if (showing_arrows)
536         h += ob_rr_theme->up_arrow_mask->height + OUTSIDE_BORDER
537             + ob_rr_theme->down_arrow_mask->height + OUTSIDE_BORDER;
538
539     /* center the icons if there is less than one row */
540     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS && icon_rows == 1)
541         icons_center_x = (w - p->n_targets * HILITE_SIZE) / 2;
542     else
543         icons_center_x = 0;
544
545     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) {
546         /* get the position of the text */
547         icon_mode_textx = l;
548         icon_mode_texty = h - texth - b;
549     }
550
551     /* find the position for the popup (include the outer borders) */
552     x = screen_area->x + (screen_area->width -
553                           (w + ob_rr_theme->obwidth * 2)) / 2;
554     y = screen_area->y + (screen_area->height -
555                           (h + ob_rr_theme->obwidth * 2)) / 2;
556
557     if (!p->mapped) {
558         /* position the background but don't draw it */
559         XMoveResizeWindow(obt_display, p->bg, x, y, w, h);
560
561         if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) {
562             /* position the text */
563             XMoveResizeWindow(obt_display, p->icon_mode_text,
564                               icon_mode_textx, icon_mode_texty, textw, texth);
565             XMapWindow(obt_display, popup.icon_mode_text);
566         } else {
567             XUnmapWindow(obt_display, popup.icon_mode_text);
568
569             up_arrow_x = (w - ob_rr_theme->up_arrow_mask->width) / 2;
570             up_arrow_y = t;
571
572             down_arrow_x = (w - ob_rr_theme->down_arrow_mask->width) / 2;
573             down_arrow_y = h - b - ob_rr_theme->down_arrow_mask->height;
574
575             /* position the arrows */
576             XMoveResizeWindow(obt_display, p->list_mode_up,
577                               up_arrow_x, up_arrow_y,
578                               ob_rr_theme->up_arrow_mask->width,
579                               ob_rr_theme->up_arrow_mask->height);
580             XMoveResizeWindow(obt_display, p->list_mode_down,
581                               down_arrow_x, down_arrow_y,
582                               ob_rr_theme->down_arrow_mask->width,
583                               ob_rr_theme->down_arrow_mask->height);
584         }
585     }
586
587     /* * * draw everything * * */
588
589     /* draw the background */
590     if (!p->mapped)
591         RrPaint(p->a_bg, p->bg, w, h);
592
593     /* draw the scroll arrows */
594     if (!p->mapped && mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) {
595         p->a_arrow->texture[0].data.mask.mask =
596             ob_rr_theme->up_arrow_mask;
597         p->a_arrow->surface.parent = p->a_bg;
598         p->a_arrow->surface.parentx = up_arrow_x;
599         p->a_arrow->surface.parenty = up_arrow_y;
600         RrPaint(p->a_arrow, p->list_mode_up, 
601                 ob_rr_theme->up_arrow_mask->width,
602                 ob_rr_theme->up_arrow_mask->height);
603
604         p->a_arrow->texture[0].data.mask.mask =
605             ob_rr_theme->down_arrow_mask;
606         p->a_arrow->surface.parent = p->a_bg;
607         p->a_arrow->surface.parentx = down_arrow_x;
608         p->a_arrow->surface.parenty = down_arrow_y;
609         RrPaint(p->a_arrow, p->list_mode_down, 
610                 ob_rr_theme->down_arrow_mask->width,
611                 ob_rr_theme->down_arrow_mask->height);
612     }
613
614     /* draw the icons and text */
615     for (i = 0, it = p->targets; it; ++i, it = g_list_next(it)) {
616         const ObFocusCyclePopupTarget *target = it->data;
617
618         /* have to redraw the targetted icon and last targetted icon
619          * to update the hilite */
620         if (!p->mapped || newtarget == target || p->last_target == target ||
621             last_scroll != p->scroll)
622         {
623             /* row and column start from 0 */
624             const gint row = i / icons_per_row - p->scroll;
625             const gint col = i % icons_per_row;
626             gint iconx, icony;
627             gint list_mode_textx, list_mode_texty;
628             RrAppearance *text;
629
630             /* find the coordinates for the icon */
631             iconx = icons_center_x + l + (col * HILITE_SIZE);
632             icony = t + (showing_arrows ? ob_rr_theme->up_arrow_mask->height
633                                           + OUTSIDE_BORDER
634                          : 0)
635                 + (row * MAX(texth, HILITE_SIZE))
636                 + MAX(texth - HILITE_SIZE, 0) / 2;
637
638             /* find the dimensions of the text box */
639             list_mode_textx = iconx + HILITE_SIZE + TEXT_BORDER;
640             list_mode_texty = icony;
641
642             /* position the icon */
643             XMoveResizeWindow(obt_display, target->iconwin,
644                               iconx, icony, HILITE_SIZE, HILITE_SIZE);
645
646             /* position the text */
647             if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
648                 XMoveResizeWindow(obt_display, target->textwin,
649                                   list_mode_textx, list_mode_texty,
650                                   textw, texth);
651
652             /* show/hide the right windows */
653             if (row >= 0 && row < icon_rows) {
654                 XMapWindow(obt_display, target->iconwin);
655                 if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
656                     XMapWindow(obt_display, target->textwin);
657                 else
658                     XUnmapWindow(obt_display, target->textwin);
659             } else {
660                 XUnmapWindow(obt_display, target->textwin);
661                 if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
662                     XUnmapWindow(obt_display, target->iconwin);
663                 else
664                     XMapWindow(obt_display, target->iconwin);
665             }
666
667             /* get the icon from the client */
668             p->a_icon->texture[0].data.image.twidth = ICON_SIZE;
669             p->a_icon->texture[0].data.image.theight = ICON_SIZE;
670             p->a_icon->texture[0].data.image.tx = HILITE_OFFSET;
671             p->a_icon->texture[0].data.image.ty = HILITE_OFFSET;
672             p->a_icon->texture[0].data.image.alpha =
673                 target->client->iconic ? OB_ICONIC_ALPHA : 0xff;
674             p->a_icon->texture[0].data.image.image = target->icon;
675
676             /* Draw the hilite? */
677             p->a_icon->texture[1].type = (target == newtarget) ?
678                 RR_TEXTURE_RGBA : RR_TEXTURE_NONE;
679
680             /* draw the icon */
681             p->a_icon->surface.parentx = iconx;
682             p->a_icon->surface.parenty = icony;
683             RrPaint(p->a_icon, target->iconwin, HILITE_SIZE, HILITE_SIZE);
684
685             /* draw the text */
686             if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST ||
687                 target == newtarget)
688             {
689                 text = (target == newtarget) ? p->a_hilite_text : p->a_text;
690                 text->texture[0].data.text.string = target->text;
691                 text->surface.parentx =
692                     mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ?
693                     icon_mode_textx : list_mode_textx;
694                 text->surface.parenty =
695                     mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ?
696                     icon_mode_texty : list_mode_texty;
697                 RrPaint(text,
698                         (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ?
699                          p->icon_mode_text : target->textwin),
700                         textw, texth);
701             }
702         }
703     }
704
705     p->last_target = newtarget;
706
707     XFlush(obt_display);
708 }
709
710 void focus_cycle_popup_show(const ObClientSet *set, ObClient *c,
711                             ObFocusCyclePopupMode mode, gboolean linear)
712 {
713     g_assert(c != NULL);
714
715     if (mode == OB_FOCUS_CYCLE_POPUP_MODE_NONE) {
716         focus_cycle_popup_hide();
717         return;
718     }
719
720     /* do this stuff only when the dialog is first showing */
721     if (!popup.mapped) {
722         popup_setup(&popup, set, TRUE, FALSE, linear);
723         /* this is fixed once the dialog is shown */
724         popup.mode = mode;
725     }
726     g_assert(popup.targets != NULL);
727
728     popup_render(&popup, c);
729
730     if (!popup.mapped) {
731         /* show the dialog */
732         XMapWindow(obt_display, popup.bg);
733         XFlush(obt_display);
734         popup.mapped = TRUE;
735         screen_hide_desktop_popup();
736     }
737 }
738
739 void focus_cycle_popup_hide(void)
740 {
741     gulong ignore_start;
742
743     ignore_start = event_start_ignore_all_enters();
744
745     XUnmapWindow(obt_display, popup.bg);
746     XFlush(obt_display);
747
748     event_end_ignore_all_enters(ignore_start);
749
750     popup.mapped = FALSE;
751
752     popup_cleanup();
753 }
754
755 void focus_cycle_popup_single_show(struct _ObClient *c)
756 {
757     gchar *text;
758
759     g_assert(c != NULL);
760
761     /* do this stuff only when the dialog is first showing */
762     if (!single_popup->popup->mapped) {
763         const Rect *a;
764
765         /* position the popup */
766         a = screen_physical_area_primary(FALSE);
767         icon_popup_position(single_popup, CenterGravity,
768                             a->x + a->width / 2, a->y + a->height / 2);
769         icon_popup_height(single_popup, POPUP_HEIGHT);
770         icon_popup_min_width(single_popup, POPUP_WIDTH);
771         icon_popup_max_width(single_popup, MAX(a->width/3, POPUP_WIDTH));
772         icon_popup_text_width(single_popup, popup.maxtextw);
773     }
774
775     text = popup_get_name(c);
776     icon_popup_show(single_popup, text, client_icon(c));
777     g_free(text);
778     screen_hide_desktop_popup();
779 }
780
781 void focus_cycle_popup_single_hide(void)
782 {
783     icon_popup_hide(single_popup);
784 }
785
786 gboolean focus_cycle_popup_is_showing(ObClient *c)
787 {
788     if (popup.mapped) {
789         GList *it;
790
791         for (it = popup.targets; it; it = g_list_next(it)) {
792             ObFocusCyclePopupTarget *t = it->data;
793             if (t->client == c)
794                 return TRUE;
795         }
796     }
797     return FALSE;
798 }
799
800 static ObClient* popup_revert(ObClient *target)
801 {
802     GList *it, *itt;
803
804     for (it = popup.targets; it; it = g_list_next(it)) {
805         ObFocusCyclePopupTarget *t = it->data;
806         if (t->client == target) {
807             /* move to a previous window if possible */
808             for (itt = it->prev; itt; itt = g_list_previous(itt)) {
809                 ObFocusCyclePopupTarget *t2 = itt->data;
810                 if (focus_cycle_valid(t2->client))
811                     return t2->client;
812             }
813
814             /* otherwise move to a following window if possible */
815             for (itt = it->next; itt; itt = g_list_next(itt)) {
816                 ObFocusCyclePopupTarget *t2 = itt->data;
817                 if (focus_cycle_valid(t2->client))
818                     return t2->client;
819             }
820
821             /* otherwise, we can't go anywhere there is nowhere valid to go */
822             return NULL;
823         }
824     }
825     return NULL;
826 }
827
828 ObClient* focus_cycle_popup_refresh(const ObClientSet *set,
829                                     ObClient *target,
830                                     gboolean redraw,
831                                     gboolean linear)
832 {
833     if (!popup.mapped) return NULL;
834
835     if (!focus_cycle_valid(target))
836         target = popup_revert(target);
837
838     redraw = popup_setup(&popup, set, TRUE, TRUE, linear) && redraw;
839
840     if (!target && popup.targets)
841         target = ((ObFocusCyclePopupTarget*)popup.targets->data)->client;
842
843     if (target && redraw) {
844         popup.mapped = FALSE;
845         popup_render(&popup, target);
846         XFlush(obt_display);
847         popup.mapped = TRUE;
848     }
849
850     return target;
851 }