wmaker: add core workspace pager functions
[wmaker-crm.git] / src / wsmap.c
blob244df94b55abd7535f4259269444704298f8c526
1 /* wsmap.c - worskpace map
3 * Window Maker window manager
5 * Copyright (c) 2014 Window Maker Team - David Maciejak
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.
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.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "wconfig.h"
24 #include <stdlib.h>
25 #include <stdio.h>
27 #ifdef USE_XSHAPE
28 #include <X11/extensions/shape.h>
29 #endif
31 #include "screen.h"
32 #include "window.h"
33 #include "misc.h"
34 #include "workspace.h"
35 #include "wsmap.h"
37 #include "WINGs/WINGsP.h"
40 static const int WORKSPACE_MAP_RATIO = 10;
41 static const int WORKSPACE_SEPARATOR_WIDTH = 12;
42 static const int mini_workspace_per_line = 5;
45 * Used to store the index of the tenth displayed mini workspace
46 * will be 0 for workspaces number 0 to 9
47 * 1 for workspaces number 10 -> 19
49 int wsmap_bulk_index;
50 WMPixmap *frame_bg_focused;
51 WMPixmap *frame_bg_unfocused;
53 typedef struct {
54 WScreen *scr;
55 WMWindow *win;
56 int xcount, ycount;
57 int wswidth, wsheight;
58 int mini_workspace_width, mini_workspace_height;
59 int edge;
60 int border_width;
61 } WWorkspaceMap;
63 typedef struct {
64 WMButton *workspace_img_button;
65 WMLabel *workspace_label;
66 } W_WorkspaceMap;
68 void wWorkspaceMapUpdate(WScreen *scr)
70 XImage *pimg;
72 pimg = XGetImage(dpy, scr->root_win, 0, 0,
73 scr->scr_width, scr->scr_height,
74 AllPlanes, ZPixmap);
75 if (pimg) {
76 RImage *apercu;
78 apercu = RCreateImageFromXImage(scr->rcontext, pimg, NULL);
79 XDestroyImage(pimg);
81 if (apercu) {
82 RImage *tmp = scr->workspaces[scr->current_workspace]->map;
84 if (tmp)
85 RReleaseImage(tmp);
87 scr->workspaces[scr->current_workspace]->map =
88 RSmoothScaleImage(apercu,
89 scr->scr_width / WORKSPACE_MAP_RATIO,
90 scr->scr_height / WORKSPACE_MAP_RATIO);
91 RReleaseImage(apercu);
96 static void workspace_map_slide(WWorkspaceMap *wsmap)
98 if (wsmap->edge == WD_TOP)
99 SlideWindow(WMWidgetXID(wsmap->win), 0, -1 * wsmap->wsheight, wsmap->xcount, wsmap->ycount);
100 else
101 SlideWindow(WMWidgetXID(wsmap->win), 0, wsmap->scr->scr_height, wsmap->xcount, wsmap->ycount);
104 static void workspace_map_unslide(WWorkspaceMap *wsmap)
106 if (wsmap->edge == WD_TOP)
107 SlideWindow(WMWidgetXID(wsmap->win), wsmap->xcount, wsmap->ycount, 0, -1 * wsmap->wsheight);
108 else
109 SlideWindow(WMWidgetXID(wsmap->win), wsmap->xcount, wsmap->ycount, 0, wsmap->scr->scr_height);
112 static void workspace_map_destroy(WWorkspaceMap *wsmap)
114 workspace_map_unslide(wsmap);
115 WMUnmapWidget(wsmap->win);
117 if (wsmap->win) {
118 Window info_win = wsmap->scr->info_window;
119 XEvent ev;
121 ev.xclient.type = ClientMessage;
122 ev.xclient.message_type = w_global.atom.wm.ignore_focus_events;
123 ev.xclient.format = 32;
124 ev.xclient.data.l[0] = True;
126 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
127 WMUnmapWidget(wsmap->win);
129 ev.xclient.data.l[0] = False;
130 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
131 WMDestroyWidget(wsmap->win);
133 if (frame_bg_focused)
134 WMReleasePixmap(frame_bg_focused);
135 if (frame_bg_unfocused)
136 WMReleasePixmap(frame_bg_unfocused);
138 wfree(wsmap);
141 static void selected_workspace_callback(WMWidget *w, void *data)
143 WWorkspaceMap *wsmap = (WWorkspaceMap *) data;
144 WMButton *click_button = w;
146 if (w && wsmap) {
147 int workspace_id = atoi(WMGetButtonText(click_button));
149 wWorkspaceChange(wsmap->scr, workspace_id);
150 process_workspacemap_event = False;
154 static void set_workspace_map_background_image(WWorkspaceMap *wsmap)
156 Pixmap pixmap, mask;
158 if (wPreferences.wsmbackTexture->any.type == WTEX_PIXMAP) {
159 RImage *tmp = wTextureRenderImage(wPreferences.wsmbackTexture, wsmap->wswidth, wsmap->wsheight, WREL_FLAT);
161 if (!tmp)
162 return;
164 RConvertImageMask(wsmap->scr->rcontext, tmp, &pixmap, &mask, 250);
165 RReleaseImage(tmp);
167 if (!pixmap)
168 return;
170 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(wsmap->win), pixmap);
172 #ifdef USE_XSHAPE
173 if (mask && w_global.xext.shape.supported)
174 XShapeCombineMask(dpy, WMWidgetXID(wsmap->win), ShapeBounding, 0, 0, mask, ShapeSet);
175 #endif
177 if (pixmap)
178 XFreePixmap(dpy, pixmap);
179 if (mask)
180 XFreePixmap(dpy, mask);
184 static void workspace_map_show(WWorkspaceMap *wsmap)
186 WMMapSubwidgets(wsmap->win);
187 WMMapWidget(wsmap->win);
188 workspace_map_slide(wsmap);
191 static WMPixmap *get_frame_background_color(WWorkspaceMap *wsmap, unsigned int width, unsigned int height, int type)
193 RImage *img;
194 WMPixmap *pix;
196 if (!wsmap->scr->window_title_texture[type])
197 return NULL;
199 img = wTextureRenderImage(wsmap->scr->window_title_texture[type], width, height, WREL_FLAT);
200 if (!img)
201 return NULL;
203 pix = WMCreatePixmapFromRImage(wsmap->scr->wmscreen, img, 128);
204 RReleaseImage(img);
206 return pix;
209 static void workspace_map_realize(WWorkspaceMap *wsmap, WMFrame *frame_border, W_WorkspaceMap *wsmap_array)
211 int i, mini_workspace_cnt, general_index;
212 WMPixmap *frame_border_pixmap;
213 WMSize label_size;
215 WMRealizeWidget(wsmap->win);
216 set_workspace_map_background_image(wsmap);
218 frame_border_pixmap = get_frame_background_color(wsmap, wsmap->wswidth, wsmap->border_width, WS_FOCUSED);
219 WMSetWidgetBackgroundPixmap(frame_border, frame_border_pixmap);
220 WMReleasePixmap(frame_border_pixmap);
222 label_size = WMGetViewSize(W_VIEW(wsmap_array[0].workspace_label));
223 frame_bg_focused = get_frame_background_color(wsmap, label_size.width, label_size.height, WS_FOCUSED);
224 frame_bg_unfocused = get_frame_background_color(wsmap, label_size.width, label_size.height, WS_UNFOCUSED);
226 mini_workspace_cnt = (wsmap->scr->workspace_count <= 2 * mini_workspace_per_line) ? wsmap->scr->workspace_count : 2 * mini_workspace_per_line;
227 for (i = 0; i < mini_workspace_cnt; i++) {
228 general_index = i + wsmap_bulk_index * 2 * mini_workspace_per_line;
229 if (general_index == wsmap->scr->current_workspace) {
230 WMSetWidgetBackgroundPixmap(wsmap_array[i].workspace_label, frame_bg_focused);
231 WMSetLabelTextColor(wsmap_array[i].workspace_label, wsmap->scr->window_title_color[WS_FOCUSED]);
232 } else {
233 WMSetWidgetBackgroundPixmap(wsmap_array[i].workspace_label, frame_bg_unfocused);
234 WMSetLabelTextColor(wsmap_array[i].workspace_label, wsmap->scr->window_title_color[WS_UNFOCUSED]);
239 static WMPixmap *enlight_workspace(WScreen *scr, RImage *mini_wkspace_map)
241 RImage *tmp = RCloneImage(mini_wkspace_map);
242 RColor color;
243 WMPixmap *icon;
245 color.red = color.green = color.blue = 0;
246 color.alpha = 160;
247 RLightImage(tmp, &color);
248 icon = WMCreatePixmapFromRImage(scr->wmscreen, tmp, 128);
249 RReleaseImage(tmp);
251 return icon;
254 static WMPixmap *dummy_background_pixmap(WWorkspaceMap *wsmap)
256 RImage *img;
257 WMPixmap *icon;
259 img = RCreateImage(wsmap->wswidth, wsmap->wsheight, 0);
261 if (!img)
262 return NULL;
264 /* the workspace texture is not saved anywhere, so just using the default unfocus color */
265 if (wsmap->scr->window_title_texture[WS_UNFOCUSED]) {
266 RColor frame_bg_color;
268 frame_bg_color.red = wsmap->scr->window_title_texture[WS_UNFOCUSED]->solid.normal.red;
269 frame_bg_color.green = wsmap->scr->window_title_texture[WS_UNFOCUSED]->solid.normal.green;
270 frame_bg_color.blue = wsmap->scr->window_title_texture[WS_UNFOCUSED]->solid.normal.blue;
271 RFillImage(img, &frame_bg_color);
274 icon = WMCreatePixmapFromRImage(wsmap->scr->wmscreen, img, 128);
275 RReleaseImage(img);
277 return icon;
280 static void show_mini_workspace(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array, int max_mini_workspace)
282 int index, space_width;
283 int border_width_adjustement = (wsmap->edge == WD_TOP) ? 0 : wsmap->border_width;
284 int font_height = WMFontHeight(wsmap->scr->info_text_font);
286 if (max_mini_workspace > mini_workspace_per_line)
287 space_width = (wsmap->wswidth - mini_workspace_per_line * wsmap->mini_workspace_width) / (mini_workspace_per_line + 1);
288 else
289 space_width = (wsmap->wswidth - max_mini_workspace * wsmap->mini_workspace_width) / (max_mini_workspace + 1);
291 for (index = 0; index < max_mini_workspace; index++) {
292 int i , j;
294 j = index;
296 if (index >= mini_workspace_per_line) {
297 i = 1;
298 j -= mini_workspace_per_line;
299 } else {
300 i = 0;
302 if (wsmap_array[index].workspace_img_button) {
303 WMResizeWidget(wsmap_array[index].workspace_img_button, wsmap->mini_workspace_width, wsmap->mini_workspace_height);
304 WMMoveWidget(wsmap_array[index].workspace_img_button, j * wsmap->mini_workspace_width + (j + 1) * space_width,
305 border_width_adjustement + WORKSPACE_SEPARATOR_WIDTH +
306 i * (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH) + font_height);
307 WMMapWidget(wsmap_array[index].workspace_img_button);
309 if (wsmap_array[index].workspace_label) {
310 WMResizeWidget(wsmap_array[index].workspace_label, wsmap->mini_workspace_width, font_height);
311 WMMoveWidget(wsmap_array[index].workspace_label, j * wsmap->mini_workspace_width + (j + 1) * space_width,
312 border_width_adjustement + WORKSPACE_SEPARATOR_WIDTH +
313 i * (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH));
314 WMMapWidget(wsmap_array[index].workspace_label);
319 static void hide_mini_workspace(W_WorkspaceMap *wsmap_array, int i)
321 if (wsmap_array[i].workspace_img_button)
322 WMUnmapWidget(wsmap_array[i].workspace_img_button);
323 if (wsmap_array[i].workspace_label)
324 WMUnmapWidget(wsmap_array[i].workspace_label);
327 static WMPixmap *get_mini_workspace(WWorkspaceMap *wsmap, int index)
329 if (!wsmap->scr->workspaces[index]->map)
330 return dummy_background_pixmap(wsmap);
332 if (index == wsmap->scr->current_workspace)
333 return enlight_workspace(wsmap->scr, wsmap->scr->workspaces[index]->map);
335 return WMCreatePixmapFromRImage(wsmap->scr->wmscreen, wsmap->scr->workspaces[index]->map, 128);
338 static void create_mini_workspace(WScreen *scr, WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array)
340 int workspace_index;
341 int mini_workspace_cnt;
342 char name[10];
343 WMButton *mini_workspace_btn;
344 WMPixmap *icon;
346 /* by default display the 10 first mini workspaces */
347 wsmap_bulk_index = 0;
348 mini_workspace_cnt = (scr->workspace_count <= 2 * mini_workspace_per_line) ? scr->workspace_count : 2 * mini_workspace_per_line;
349 for (workspace_index = 0; workspace_index < mini_workspace_cnt; workspace_index++) {
350 mini_workspace_btn = WMCreateButton(wsmap->win, WBTOnOff);
351 WMSetButtonBordered(mini_workspace_btn, 0);
352 WMLabel *workspace_name_label = WMCreateLabel(wsmap->win);
353 WMSetLabelFont(workspace_name_label, scr->info_text_font);
354 WMSetLabelText(workspace_name_label, scr->workspaces[workspace_index]->name);
356 wsmap_array[workspace_index].workspace_img_button = mini_workspace_btn;
357 wsmap_array[workspace_index].workspace_label = workspace_name_label;
359 WMSetButtonImagePosition(mini_workspace_btn, WIPImageOnly);
360 icon = get_mini_workspace(wsmap, workspace_index);
361 if (icon) {
362 WMSetButtonImage(mini_workspace_btn, icon);
363 WMReleasePixmap(icon);
366 snprintf(name, sizeof(name), "%d", workspace_index);
367 WMSetButtonText(mini_workspace_btn, name);
368 WMSetButtonAction(mini_workspace_btn, selected_workspace_callback, wsmap);
370 show_mini_workspace(wsmap, wsmap_array, mini_workspace_cnt);
373 static WWorkspaceMap *create_workspace_map(WScreen *scr, W_WorkspaceMap *wsmap_array, int edge)
375 WWorkspaceMap *wsmap = wmalloc(sizeof(WWorkspaceMap));
377 wsmap->border_width = 5;
378 wsmap->edge = edge;
379 wsmap->mini_workspace_width = scr->scr_width / WORKSPACE_MAP_RATIO;
380 wsmap->mini_workspace_height = scr->scr_height / WORKSPACE_MAP_RATIO;
382 if (scr->workspace_count == 0)
383 return NULL;
385 wsmap->scr = scr;
386 wsmap->win = WMCreateWindow(scr->wmscreen, "wsmap");
387 wsmap->wswidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
388 wsmap->wsheight = WMFontHeight(scr->info_text_font) + (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH) *
389 (scr->workspace_count > mini_workspace_per_line ? 2 : 1);
391 if (wPreferences.wsmbackTexture->any.type == WTEX_SOLID) {
392 WMColor *tmp = WMCreateRGBColor(scr->wmscreen,
393 wPreferences.wsmbackTexture->any.color.red,
394 wPreferences.wsmbackTexture->any.color.green,
395 wPreferences.wsmbackTexture->any.color.blue,
396 False);
397 WMSetWidgetBackgroundColor(wsmap->win, tmp);
398 WMReleaseColor(tmp);
400 WMResizeWidget(wsmap->win, wsmap->wswidth, wsmap->wsheight + wsmap->border_width);
402 WMFrame *framel = WMCreateFrame(wsmap->win);
403 WMResizeWidget(framel, wsmap->wswidth, wsmap->border_width);
404 WMSetFrameRelief(framel, WRSimple);
405 wWorkspaceMapUpdate(scr);
407 wsmap->xcount = 0;
408 if (edge == WD_TOP) {
409 wsmap->ycount = 0;
410 WMMoveWidget(framel, 0, wsmap->wsheight);
411 } else {
412 wsmap->ycount = scr->scr_height - wsmap->wsheight - wsmap->border_width;
413 WMMoveWidget(framel, 0, 0);
416 create_mini_workspace(scr, wsmap, wsmap_array);
417 workspace_map_realize(wsmap, framel, wsmap_array);
419 return wsmap;
422 static void update_mini_workspace(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array, int bulk_of_ten)
424 int local_index, general_index;
425 int mini_workspace_cnt;
426 char name[10];
427 WMPixmap *icon;
429 if (bulk_of_ten == wsmap_bulk_index)
430 return;
432 if (bulk_of_ten < 0)
433 return;
435 if (wsmap->scr->workspace_count <= bulk_of_ten * 2 * mini_workspace_per_line)
436 return;
438 wsmap_bulk_index = bulk_of_ten;
440 mini_workspace_cnt = wsmap->scr->workspace_count - wsmap_bulk_index * 2 * mini_workspace_per_line;
441 if (mini_workspace_cnt > 2 * mini_workspace_per_line)
442 mini_workspace_cnt = 2 * mini_workspace_per_line;
444 for (local_index = 0; local_index < 2 * mini_workspace_per_line; local_index++) {
445 general_index = local_index + wsmap_bulk_index * 2 * mini_workspace_per_line;
446 if (general_index < wsmap->scr->workspace_count) {
447 /* updating label */
448 WMSetLabelText(wsmap_array[local_index].workspace_label, wsmap->scr->workspaces[general_index]->name);
449 snprintf(name, sizeof(name), "%d", general_index);
450 WMSetButtonText(wsmap_array[local_index].workspace_img_button, name);
452 /* updating label background*/
453 if (general_index == wsmap->scr->current_workspace) {
454 WMSetWidgetBackgroundPixmap(wsmap_array[local_index].workspace_label, frame_bg_focused);
455 WMSetLabelTextColor(wsmap_array[local_index].workspace_label, wsmap->scr->window_title_color[WS_FOCUSED]);
456 } else {
457 WMSetWidgetBackgroundPixmap(wsmap_array[local_index].workspace_label, frame_bg_unfocused);
458 WMSetLabelTextColor(wsmap_array[local_index].workspace_label, wsmap->scr->window_title_color[WS_UNFOCUSED]);
461 icon = get_mini_workspace(wsmap, general_index);
462 if (icon) {
463 WMSetButtonImage(wsmap_array[local_index].workspace_img_button, icon);
464 WMReleasePixmap(icon);
466 } else {
467 if (local_index < wsmap->scr->workspace_count)
468 hide_mini_workspace(wsmap_array, local_index);
471 show_mini_workspace(wsmap, wsmap_array, mini_workspace_cnt);
474 static void handle_event(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array)
476 XEvent ev;
477 int modifiers;
478 KeyCode escKey = XKeysymToKeycode(dpy, XK_Escape);
480 XGrabKeyboard(dpy, WMWidgetXID(wsmap->win), False, GrabModeAsync, GrabModeAsync, CurrentTime);
481 XGrabPointer(dpy, WMWidgetXID(wsmap->win), True,
482 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
483 GrabModeAsync, GrabModeAsync, WMWidgetXID(wsmap->win), None, CurrentTime);
485 process_workspacemap_event = True;
486 while (process_workspacemap_event) {
487 WMMaskEvent(dpy, KeyPressMask | KeyReleaseMask | ExposureMask
488 | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask, &ev);
490 if (!wsmap)
491 break;
492 modifiers = ev.xkey.state & w_global.shortcut.modifiers_mask;
494 switch (ev.type) {
495 case KeyPress:
496 if (ev.xkey.keycode == escKey || (wKeyBindings[WKBD_WORKSPACEMAP].keycode != 0 &&
497 wKeyBindings[WKBD_WORKSPACEMAP].keycode == ev.xkey.keycode &&
498 wKeyBindings[WKBD_WORKSPACEMAP].modifier == modifiers)) {
499 process_workspacemap_event = False;
500 } else {
501 KeySym ks;
502 int bulk_id;
504 XLookupString(&ev.xkey, NULL, 16, &ks, NULL);
506 bulk_id = -1;
507 if (ks >= 0x30 && ks <= 0x39)
508 bulk_id = ks - 0x30;
509 else
510 if (ks == XK_Left)
511 bulk_id = wsmap_bulk_index - 1;
512 else if (ks == XK_Right)
513 bulk_id = wsmap_bulk_index + 1;
515 if (bulk_id >= 0)
516 update_mini_workspace(wsmap, wsmap_array, bulk_id);
518 break;
520 case ButtonPress:
521 switch (ev.xbutton.button) {
522 case Button6:
523 update_mini_workspace(wsmap, wsmap_array, wsmap_bulk_index - 1);
524 break;
525 case Button7:
526 update_mini_workspace(wsmap, wsmap_array, wsmap_bulk_index + 1);
527 break;
528 default:
529 WMHandleEvent(&ev);
531 break;
533 default:
534 WMHandleEvent(&ev);
535 break;
539 XUngrabPointer(dpy, CurrentTime);
540 XUngrabKeyboard(dpy, CurrentTime);
542 if (wsmap)
543 workspace_map_destroy(wsmap);
546 static WWorkspaceMap *init_workspace_map(WScreen *scr, W_WorkspaceMap *wsmap_array)
548 WWorkspaceMap *wsmap;
550 wsmap = create_workspace_map(scr, wsmap_array, WD_BOTTOM);
551 return wsmap;
554 void StartWorkspaceMap(WScreen *scr)
556 WWorkspaceMap *wsmap;
557 W_WorkspaceMap wsmap_array[2 * mini_workspace_per_line];
559 /* save the current screen before displaying the workspace map */
560 wWorkspaceMapUpdate(scr);
562 wsmap = init_workspace_map(scr, wsmap_array);
563 if (wsmap) {
564 workspace_map_show(wsmap);
565 handle_event(wsmap, wsmap_array);