Update Serbian translation from master branch
[wmaker-crm.git] / src / wsmap.c
blob6f30e36a82262bb300090f5de0c1a4e1185fe18a
1 /* wsmap.c - worskpace map
3 * Window Maker window manager
5 * Copyright (c) 2014-2023 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 static int wsmap_bulk_index;
50 static WMPixmap *frame_bg_focused;
51 static 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 *mini_preview;
78 mini_preview = RCreateImageFromXImage(scr->rcontext, pimg, NULL);
79 XDestroyImage(pimg);
81 if (mini_preview) {
82 RImage *tmp = scr->workspaces[scr->current_workspace]->map;
84 if (tmp)
85 RReleaseImage(tmp);
87 scr->workspaces[scr->current_workspace]->map =
88 RSmoothScaleImage(mini_preview,
89 scr->scr_width / WORKSPACE_MAP_RATIO,
90 scr->scr_height / WORKSPACE_MAP_RATIO);
91 RReleaseImage(mini_preview);
96 static void workspace_map_slide(WWorkspaceMap *wsmap)
98 if (wsmap->edge == WD_TOP)
99 slide_window(WMWidgetXID(wsmap->win), 0, -1 * wsmap->wsheight, wsmap->xcount, wsmap->ycount);
100 else
101 slide_window(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 slide_window(WMWidgetXID(wsmap->win), wsmap->xcount, wsmap->ycount, 0, -1 * wsmap->wsheight);
108 else
109 slide_window(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 w_global.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->mini_workspace_width, wsmap->mini_workspace_height, 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 NULL;
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 unsigned short workspace_index;
341 int mini_workspace_cnt;
342 char name[6];
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), "%hu", 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;
377 if (scr->workspace_count == 0)
378 return NULL;
380 wsmap = wmalloc(sizeof(*wsmap));
382 wsmap->border_width = 5;
383 wsmap->edge = edge;
384 wsmap->mini_workspace_width = scr->scr_width / WORKSPACE_MAP_RATIO;
385 wsmap->mini_workspace_height = scr->scr_height / WORKSPACE_MAP_RATIO;
387 wsmap->scr = scr;
388 wsmap->win = WMCreateWindow(scr->wmscreen, "wsmap");
389 wsmap->wswidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
390 wsmap->wsheight = WMFontHeight(scr->info_text_font) + (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH) *
391 (scr->workspace_count > mini_workspace_per_line ? 2 : 1);
393 if (wPreferences.wsmbackTexture->any.type == WTEX_SOLID) {
394 WMColor *tmp = WMCreateRGBColor(scr->wmscreen,
395 wPreferences.wsmbackTexture->any.color.red,
396 wPreferences.wsmbackTexture->any.color.green,
397 wPreferences.wsmbackTexture->any.color.blue,
398 False);
399 WMSetWidgetBackgroundColor(wsmap->win, tmp);
400 WMReleaseColor(tmp);
402 WMResizeWidget(wsmap->win, wsmap->wswidth, wsmap->wsheight + wsmap->border_width);
404 WMFrame *framel = WMCreateFrame(wsmap->win);
405 WMResizeWidget(framel, wsmap->wswidth, wsmap->border_width);
406 WMSetFrameRelief(framel, WRSimple);
407 wWorkspaceMapUpdate(scr);
409 wsmap->xcount = 0;
410 if (edge == WD_TOP) {
411 wsmap->ycount = 0;
412 WMMoveWidget(framel, 0, wsmap->wsheight);
413 } else {
414 wsmap->ycount = scr->scr_height - wsmap->wsheight - wsmap->border_width;
415 WMMoveWidget(framel, 0, 0);
418 create_mini_workspace(scr, wsmap, wsmap_array);
419 workspace_map_realize(wsmap, framel, wsmap_array);
421 return wsmap;
424 static void update_mini_workspace(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array, int bulk_of_ten)
426 unsigned short local_index, general_index;
427 int mini_workspace_cnt;
428 char name[6];
429 WMPixmap *icon;
431 if (bulk_of_ten == wsmap_bulk_index)
432 return;
434 if (bulk_of_ten < 0)
435 return;
437 if (wsmap->scr->workspace_count <= bulk_of_ten * 2 * mini_workspace_per_line)
438 return;
440 wsmap_bulk_index = bulk_of_ten;
442 mini_workspace_cnt = wsmap->scr->workspace_count - wsmap_bulk_index * 2 * mini_workspace_per_line;
443 if (mini_workspace_cnt > 2 * mini_workspace_per_line)
444 mini_workspace_cnt = 2 * mini_workspace_per_line;
446 for (local_index = 0; local_index < 2 * mini_workspace_per_line; local_index++) {
447 general_index = local_index + wsmap_bulk_index * 2 * mini_workspace_per_line;
448 if (general_index < wsmap->scr->workspace_count) {
449 /* updating label */
450 WMSetLabelText(wsmap_array[local_index].workspace_label, wsmap->scr->workspaces[general_index]->name);
451 snprintf(name, sizeof(name), "%hu", general_index);
452 WMSetButtonText(wsmap_array[local_index].workspace_img_button, name);
454 /* updating label background*/
455 if (general_index == wsmap->scr->current_workspace) {
456 WMSetWidgetBackgroundPixmap(wsmap_array[local_index].workspace_label, frame_bg_focused);
457 WMSetLabelTextColor(wsmap_array[local_index].workspace_label, wsmap->scr->window_title_color[WS_FOCUSED]);
458 } else {
459 WMSetWidgetBackgroundPixmap(wsmap_array[local_index].workspace_label, frame_bg_unfocused);
460 WMSetLabelTextColor(wsmap_array[local_index].workspace_label, wsmap->scr->window_title_color[WS_UNFOCUSED]);
463 icon = get_mini_workspace(wsmap, general_index);
464 if (icon) {
465 WMSetButtonImage(wsmap_array[local_index].workspace_img_button, icon);
466 WMReleasePixmap(icon);
468 } else {
469 if (local_index < wsmap->scr->workspace_count)
470 hide_mini_workspace(wsmap_array, local_index);
473 show_mini_workspace(wsmap, wsmap_array, mini_workspace_cnt);
476 static void handle_event(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array)
478 XEvent ev;
479 int modifiers;
480 KeyCode escKey = XKeysymToKeycode(dpy, XK_Escape);
482 XGrabKeyboard(dpy, WMWidgetXID(wsmap->win), False, GrabModeAsync, GrabModeAsync, CurrentTime);
483 XGrabPointer(dpy, WMWidgetXID(wsmap->win), True,
484 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
485 GrabModeAsync, GrabModeAsync, WMWidgetXID(wsmap->win), None, CurrentTime);
487 w_global.process_workspacemap_event = True;
488 while (w_global.process_workspacemap_event) {
489 WMMaskEvent(dpy, KeyPressMask | KeyReleaseMask | ExposureMask | PointerMotionMask |
490 ButtonPressMask | ButtonReleaseMask | EnterWindowMask | FocusChangeMask, &ev);
492 modifiers = ev.xkey.state & w_global.shortcut.modifiers_mask;
494 switch (ev.type) {
495 WMScreen *wmscr;
496 WMColor *black;
498 case KeyPress:
499 if (ev.xkey.keycode == escKey || (wKeyBindings[WKBD_WORKSPACEMAP].keycode != 0 &&
500 wKeyBindings[WKBD_WORKSPACEMAP].keycode == ev.xkey.keycode &&
501 wKeyBindings[WKBD_WORKSPACEMAP].modifier == modifiers)) {
502 w_global.process_workspacemap_event = False;
503 } else {
504 KeySym ks;
505 int bulk_id;
507 XLookupString(&ev.xkey, NULL, 16, &ks, NULL);
509 bulk_id = -1;
510 if (ks >= 0x30 && ks <= 0x39)
511 bulk_id = ks - 0x30;
512 else
513 if (ks == XK_Left)
514 bulk_id = wsmap_bulk_index - 1;
515 else if (ks == XK_Right)
516 bulk_id = wsmap_bulk_index + 1;
518 if (bulk_id >= 0)
519 update_mini_workspace(wsmap, wsmap_array, bulk_id);
521 break;
523 case ButtonPress:
524 switch (ev.xbutton.button) {
525 case Button6:
526 update_mini_workspace(wsmap, wsmap_array, wsmap_bulk_index - 1);
527 break;
528 case Button7:
529 update_mini_workspace(wsmap, wsmap_array, wsmap_bulk_index + 1);
530 break;
531 default:
532 WMHandleEvent(&ev);
534 break;
536 case FocusIn:
537 wmscr = wsmap->scr->wmscreen;
538 black = WMBlackColor(wmscr);
539 const char *text = "?";
540 WMFont *bold = WMBoldSystemFontOfSize(wmscr, wsmap->mini_workspace_width / 3);
541 int x = (wsmap->mini_workspace_width / 2) - (WMWidthOfString(bold, text, strlen(text)) / 2);
542 int y = (wsmap->mini_workspace_height / 2) - (WMFontHeight(bold) / 2);
543 WMPixmap *icon = dummy_background_pixmap(wsmap);
545 if (icon) {
546 int i;
548 WMDrawString(wmscr, WMGetPixmapXID(icon),
549 black, bold, x, y, text, strlen(text));
550 for (i = 0; i < wsmap->scr->workspace_count; i++) {
551 if (!wsmap->scr->workspaces[i]->map)
552 WMSetButtonImage(wsmap_array[i].workspace_img_button, icon);
554 WMReleasePixmap(icon);
556 WMHandleEvent(&ev);
557 break;
559 default:
560 WMHandleEvent(&ev);
561 break;
565 XUngrabPointer(dpy, CurrentTime);
566 XUngrabKeyboard(dpy, CurrentTime);
568 workspace_map_destroy(wsmap);
571 static WWorkspaceMap *init_workspace_map(WScreen *scr, W_WorkspaceMap *wsmap_array)
573 WWorkspaceMap *wsmap;
575 wsmap = create_workspace_map(scr, wsmap_array, WD_BOTTOM);
576 return wsmap;
579 void StartWorkspaceMap(WScreen *scr)
581 WWorkspaceMap *wsmap;
582 W_WorkspaceMap wsmap_array[2 * mini_workspace_per_line];
584 /* save the current screen before displaying the workspace map */
585 wWorkspaceMapUpdate(scr);
587 wsmap = init_workspace_map(scr, wsmap_array);
588 if (wsmap) {
589 workspace_map_show(wsmap);
590 handle_event(wsmap, wsmap_array);