mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-24 15:12:32 +01:00
- Also tested the backward compatibility ability of the WINGs proplist code which seems to work quite well. Starting with this moment, Window Maker no longer needs libPropList and is now using the better and much more robust proplist code from WINGs. Also the WINGs based proplist code is actively maintained while the old libPropList code is practically dead and flawed by the fact that it borrowed concepts from the UserDefaults which conflicted with the retain/release mechanism, making some problems that libPropList had, practically unsolvable without a complete redesign (which can be found in the more robust WINGs code).
1090 lines
28 KiB
C
1090 lines
28 KiB
C
/* workspace.c- Workspace management
|
|
*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1997, 1998 Alfredo K. Kojima
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*/
|
|
#include "wconfig.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#ifdef SHAPE
|
|
#include <X11/extensions/shape.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "WindowMaker.h"
|
|
#include "wcore.h"
|
|
#include "framewin.h"
|
|
#include "window.h"
|
|
#include "icon.h"
|
|
#include "funcs.h"
|
|
#include "menu.h"
|
|
#include "application.h"
|
|
#include "dock.h"
|
|
#include "actions.h"
|
|
#include "workspace.h"
|
|
#include "appicon.h"
|
|
#ifdef KWM_HINTS
|
|
#include "kwm.h"
|
|
#endif
|
|
|
|
#include <WINGs/WUtil.h>
|
|
|
|
|
|
extern WPreferences wPreferences;
|
|
extern XContext wWinContext;
|
|
|
|
|
|
static WMPropList *dWorkspaces=NULL;
|
|
static WMPropList *dClip, *dName;
|
|
|
|
#ifdef VIRTUAL_DESKTOP
|
|
static BOOL initVDesk = False;
|
|
#endif
|
|
|
|
static void
|
|
make_keys()
|
|
{
|
|
if (dWorkspaces!=NULL)
|
|
return;
|
|
|
|
dWorkspaces = WMCreatePLString("Workspaces");
|
|
dName = WMCreatePLString("Name");
|
|
dClip = WMCreatePLString("Clip");
|
|
}
|
|
|
|
|
|
void
|
|
wWorkspaceMake(WScreen *scr, int count)
|
|
{
|
|
while (count>0) {
|
|
wWorkspaceNew(scr);
|
|
count--;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
wWorkspaceNew(WScreen *scr)
|
|
{
|
|
WWorkspace *wspace, **list;
|
|
int i;
|
|
|
|
if (scr->workspace_count < MAX_WORKSPACES) {
|
|
scr->workspace_count++;
|
|
|
|
wspace = wmalloc(sizeof(WWorkspace));
|
|
wspace->name = NULL;
|
|
|
|
#ifdef KWM_HINTS
|
|
if (scr->flags.kwm_syncing_count) {
|
|
wspace->name = wKWMGetWorkspaceName(scr, scr->workspace_count-1);
|
|
}
|
|
#endif
|
|
if (!wspace->name) {
|
|
wspace->name = wmalloc(strlen(_("Workspace %i"))+8);
|
|
sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
|
|
}
|
|
|
|
|
|
if (!wPreferences.flags.noclip) {
|
|
wspace->clip = wDockCreate(scr, WM_CLIP);
|
|
} else
|
|
wspace->clip = NULL;
|
|
|
|
list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
|
|
|
|
for (i=0; i<scr->workspace_count-1; i++) {
|
|
list[i] = scr->workspaces[i];
|
|
}
|
|
list[i] = wspace;
|
|
if (scr->workspaces)
|
|
wfree(scr->workspaces);
|
|
scr->workspaces = list;
|
|
|
|
wWorkspaceMenuUpdate(scr, scr->workspace_menu);
|
|
wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
|
|
#ifdef VIRTUAL_DESKTOP
|
|
wspace->view_x = wspace->view_y = 0;
|
|
wspace->height = scr->scr_height;
|
|
wspace->width = scr->scr_width;
|
|
#endif
|
|
WMPostNotificationName(WMNWorkspaceCreated, scr,
|
|
(void*)(scr->workspace_count-1));
|
|
XFlush(dpy);
|
|
|
|
return scr->workspace_count-1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
Bool
|
|
wWorkspaceDelete(WScreen *scr, int workspace)
|
|
{
|
|
WWindow *tmp;
|
|
WWorkspace **list;
|
|
int i, j;
|
|
|
|
|
|
if (workspace<=0)
|
|
return False;
|
|
|
|
/* verify if workspace is in use by some window */
|
|
tmp = scr->focused_window;
|
|
while (tmp) {
|
|
if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace==workspace)
|
|
return False;
|
|
tmp = tmp->prev;
|
|
}
|
|
|
|
if (!wPreferences.flags.noclip) {
|
|
wDockDestroy(scr->workspaces[workspace]->clip);
|
|
scr->workspaces[workspace]->clip = NULL;
|
|
}
|
|
|
|
list = wmalloc(sizeof(WWorkspace*)*(scr->workspace_count-1));
|
|
j = 0;
|
|
for (i=0; i<scr->workspace_count; i++) {
|
|
if (i!=workspace)
|
|
list[j++] = scr->workspaces[i];
|
|
else {
|
|
if (scr->workspaces[i]->name)
|
|
wfree(scr->workspaces[i]->name);
|
|
wfree(scr->workspaces[i]);
|
|
}
|
|
}
|
|
wfree(scr->workspaces);
|
|
scr->workspaces = list;
|
|
|
|
scr->workspace_count--;
|
|
|
|
|
|
/* update menu */
|
|
wWorkspaceMenuUpdate(scr, scr->workspace_menu);
|
|
/* clip workspace menu */
|
|
wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
|
|
|
|
/* update also window menu */
|
|
if (scr->workspace_submenu) {
|
|
WMenu *menu = scr->workspace_submenu;
|
|
|
|
i = menu->entry_no;
|
|
while (i>scr->workspace_count)
|
|
wMenuRemoveItem(menu, --i);
|
|
wMenuRealize(menu);
|
|
}
|
|
/* and clip menu */
|
|
if (scr->clip_submenu) {
|
|
WMenu *menu = scr->clip_submenu;
|
|
|
|
i = menu->entry_no;
|
|
while (i>scr->workspace_count)
|
|
wMenuRemoveItem(menu, --i);
|
|
wMenuRealize(menu);
|
|
}
|
|
|
|
WMPostNotificationName(WMNWorkspaceDestroyed, scr,
|
|
(void*)(scr->workspace_count-1));
|
|
|
|
if (scr->current_workspace >= scr->workspace_count)
|
|
wWorkspaceChange(scr, scr->workspace_count-1);
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
typedef struct WorkspaceNameData {
|
|
int count;
|
|
RImage *back;
|
|
RImage *text;
|
|
time_t timeout;
|
|
} WorkspaceNameData;
|
|
|
|
|
|
|
|
static void
|
|
hideWorkpaceName(void *data)
|
|
{
|
|
WScreen *scr = (WScreen*)data;
|
|
|
|
if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
|
|
|| time(NULL) > scr->workspace_name_data->timeout) {
|
|
XUnmapWindow(dpy, scr->workspace_name);
|
|
|
|
if (scr->workspace_name_data) {
|
|
RReleaseImage(scr->workspace_name_data->back);
|
|
RReleaseImage(scr->workspace_name_data->text);
|
|
wfree(scr->workspace_name_data);
|
|
|
|
scr->workspace_name_data = NULL;
|
|
}
|
|
scr->workspace_name_timer = NULL;
|
|
} else {
|
|
RImage *img = RCloneImage(scr->workspace_name_data->back);
|
|
Pixmap pix;
|
|
|
|
scr->workspace_name_timer =
|
|
WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkpaceName,
|
|
scr);
|
|
|
|
RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
|
|
scr->workspace_name_data->count*255/10);
|
|
|
|
RConvertImage(scr->rcontext, img, &pix);
|
|
|
|
RReleaseImage(img);
|
|
|
|
XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
|
|
XClearWindow(dpy, scr->workspace_name);
|
|
XFreePixmap(dpy, pix);
|
|
XFlush(dpy);
|
|
|
|
scr->workspace_name_data->count--;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
showWorkspaceName(WScreen *scr, int workspace)
|
|
{
|
|
WorkspaceNameData *data;
|
|
RXImage *ximg;
|
|
Pixmap text, mask;
|
|
int w, h;
|
|
int px, py;
|
|
char *name = scr->workspaces[workspace]->name;
|
|
int len = strlen(name);
|
|
int x, y;
|
|
|
|
if (wPreferences.workspace_name_display_position == WD_NONE
|
|
|| scr->workspace_count < 2)
|
|
return;
|
|
|
|
if (scr->workspace_name_timer) {
|
|
WMDeleteTimerHandler(scr->workspace_name_timer);
|
|
XUnmapWindow(dpy, scr->workspace_name);
|
|
XFlush(dpy);
|
|
}
|
|
scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
|
|
hideWorkpaceName, scr);
|
|
|
|
if (scr->workspace_name_data) {
|
|
RReleaseImage(scr->workspace_name_data->back);
|
|
RReleaseImage(scr->workspace_name_data->text);
|
|
wfree(scr->workspace_name_data);
|
|
}
|
|
|
|
data = wmalloc(sizeof(WorkspaceNameData));
|
|
|
|
w = WMWidthOfString(scr->workspace_name_font, name, len);
|
|
h = WMFontHeight(scr->workspace_name_font);
|
|
|
|
switch (wPreferences.workspace_name_display_position) {
|
|
case WD_TOP:
|
|
px = (scr->scr_width - (w+4))/2;
|
|
py = 0;
|
|
break;
|
|
case WD_BOTTOM:
|
|
px = (scr->scr_width - (w+4))/2;
|
|
py = scr->scr_height - (h+4);
|
|
break;
|
|
case WD_TOPLEFT:
|
|
px = 0;
|
|
py = 0;
|
|
break;
|
|
case WD_TOPRIGHT:
|
|
px = scr->scr_width - (w+4);
|
|
py = 0;
|
|
break;
|
|
case WD_BOTTOMLEFT:
|
|
px = 0;
|
|
py = scr->scr_height - (h+4);
|
|
break;
|
|
case WD_BOTTOMRIGHT:
|
|
px = scr->scr_width - (w+4);
|
|
py = scr->scr_height - (h+4);
|
|
break;
|
|
case WD_CENTER:
|
|
default:
|
|
px = (scr->scr_width - (w+4))/2;
|
|
py = (scr->scr_height - (h+4))/2;
|
|
break;
|
|
}
|
|
XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
|
|
XMoveWindow(dpy, scr->workspace_name, px, py);
|
|
|
|
text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
|
|
mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
|
|
|
|
XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
|
|
XFillRectangle(dpy, text, scr->draw_gc, 0, 0, w+4, h+4);
|
|
|
|
XSetForeground(dpy, scr->mono_gc, 0);
|
|
XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);
|
|
|
|
XSetForeground(dpy, scr->mono_gc, 1);
|
|
for (x = 0; x <= 4; x++) {
|
|
for (y = 0; y <= 4; y++) {
|
|
WMDrawString(scr->wmscreen, mask, scr->mono_gc,
|
|
scr->workspace_name_font, x, y, name, len);
|
|
}
|
|
}
|
|
|
|
XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
|
|
WMDrawString(scr->wmscreen, text, scr->draw_gc, scr->workspace_name_font,
|
|
2, 2, scr->workspaces[workspace]->name,
|
|
strlen(scr->workspaces[workspace]->name));
|
|
#ifdef SHAPE
|
|
XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
|
|
ShapeSet);
|
|
#endif
|
|
XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
|
|
XClearWindow(dpy, scr->workspace_name);
|
|
|
|
data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
|
|
|
|
XFreePixmap(dpy, text);
|
|
XFreePixmap(dpy, mask);
|
|
|
|
if (!data->text) {
|
|
XMapRaised(dpy, scr->workspace_name);
|
|
XFlush(dpy);
|
|
|
|
goto erro;
|
|
}
|
|
|
|
ximg = RGetXImage(scr->rcontext, scr->root_win, px, py,
|
|
data->text->width, data->text->height);
|
|
|
|
if (!ximg || !ximg->image) {
|
|
goto erro;
|
|
}
|
|
|
|
XMapRaised(dpy, scr->workspace_name);
|
|
XFlush(dpy);
|
|
|
|
data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
|
|
RDestroyXImage(scr->rcontext, ximg);
|
|
|
|
if (!data->back) {
|
|
goto erro;
|
|
}
|
|
|
|
data->count = 10;
|
|
|
|
/* set a timeout for the effect */
|
|
data->timeout = time(NULL) + 2 +
|
|
(WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
|
|
|
|
scr->workspace_name_data = data;
|
|
|
|
|
|
return;
|
|
|
|
erro:
|
|
if (scr->workspace_name_timer)
|
|
WMDeleteTimerHandler(scr->workspace_name_timer);
|
|
|
|
if (data->text)
|
|
RReleaseImage(data->text);
|
|
if (data->back)
|
|
RReleaseImage(data->back);
|
|
wfree(data);
|
|
|
|
scr->workspace_name_data = NULL;
|
|
|
|
scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
|
|
10*WORKSPACE_NAME_FADE_DELAY,
|
|
hideWorkpaceName, scr);
|
|
}
|
|
|
|
|
|
void
|
|
wWorkspaceChange(WScreen *scr, int workspace)
|
|
{
|
|
if (scr->flags.startup || scr->flags.startup2) {
|
|
return;
|
|
}
|
|
|
|
if (workspace != scr->current_workspace) {
|
|
wWorkspaceForceChange(scr, workspace);
|
|
} /*else {
|
|
showWorkspaceName(scr, workspace);
|
|
}*/
|
|
}
|
|
|
|
|
|
void
|
|
wWorkspaceRelativeChange(WScreen *scr, int amount)
|
|
{
|
|
int w;
|
|
|
|
w = scr->current_workspace + amount;
|
|
|
|
if (amount < 0) {
|
|
|
|
if (w >= 0)
|
|
wWorkspaceChange(scr, w);
|
|
else if (wPreferences.ws_cycle)
|
|
wWorkspaceChange(scr, scr->workspace_count + w);
|
|
|
|
} else if (amount > 0) {
|
|
|
|
if (w < scr->workspace_count)
|
|
wWorkspaceChange(scr, w);
|
|
else if (wPreferences.ws_advance)
|
|
wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
|
|
else if (wPreferences.ws_cycle)
|
|
wWorkspaceChange(scr, w % scr->workspace_count);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
wWorkspaceForceChange(WScreen *scr, int workspace)
|
|
{
|
|
WWindow *tmp, *foc=NULL, *foc2=NULL;
|
|
|
|
if (workspace >= MAX_WORKSPACES || workspace < 0)
|
|
return;
|
|
|
|
SendHelperMessage(scr, 'C', workspace+1, NULL);
|
|
|
|
if (workspace > scr->workspace_count-1) {
|
|
wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
|
|
}
|
|
|
|
wClipUpdateForWorkspaceChange(scr, workspace);
|
|
|
|
scr->current_workspace = workspace;
|
|
|
|
wWorkspaceMenuUpdate(scr, scr->workspace_menu);
|
|
|
|
wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
|
|
|
|
if ((tmp = scr->focused_window)!= NULL) {
|
|
if ((IS_OMNIPRESENT(tmp) && !WFLAGP(tmp, skip_window_list))
|
|
|| tmp->flags.changing_workspace) {
|
|
foc = tmp;
|
|
}
|
|
|
|
/* foc2 = tmp; will fix annoyance with gnome panel
|
|
* but will create annoyance for every other application
|
|
*/
|
|
|
|
while (tmp) {
|
|
if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
|
|
/* unmap windows not on this workspace */
|
|
if ((tmp->flags.mapped||tmp->flags.shaded)
|
|
&& !IS_OMNIPRESENT(tmp)
|
|
&& !tmp->flags.changing_workspace) {
|
|
|
|
wWindowUnmap(tmp);
|
|
}
|
|
/* also unmap miniwindows not on this workspace */
|
|
if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
|
|
tmp->icon && !IS_OMNIPRESENT(tmp)) {
|
|
|
|
XUnmapWindow(dpy, tmp->icon->core->window);
|
|
tmp->icon->mapped = 0;
|
|
}
|
|
/* update current workspace of omnipresent windows */
|
|
if (IS_OMNIPRESENT(tmp)) {
|
|
WApplication *wapp = wApplicationOf(tmp->main_window);
|
|
|
|
tmp->frame->workspace = workspace;
|
|
|
|
if (wapp) {
|
|
wapp->last_workspace = workspace;
|
|
}
|
|
if (!foc2)
|
|
foc2 = tmp;
|
|
}
|
|
} else {
|
|
/* change selected windows' workspace */
|
|
if (tmp->flags.selected) {
|
|
wWindowChangeWorkspace(tmp, workspace);
|
|
if (!tmp->flags.miniaturized && !foc) {
|
|
foc = tmp;
|
|
}
|
|
} else {
|
|
if (!tmp->flags.hidden) {
|
|
if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
|
|
/* remap windows that are on this workspace */
|
|
wWindowMap(tmp);
|
|
if (!foc && !WFLAGP(tmp, skip_window_list))
|
|
foc = tmp;
|
|
}
|
|
/* Also map miniwindow if not omnipresent */
|
|
if (!wPreferences.sticky_icons &&
|
|
tmp->flags.miniaturized &&
|
|
!IS_OMNIPRESENT(tmp) && tmp->icon) {
|
|
tmp->icon->mapped = 1;
|
|
XMapWindow(dpy, tmp->icon->core->window);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tmp = tmp->prev;
|
|
}
|
|
|
|
if (!foc)
|
|
foc = foc2;
|
|
|
|
if (scr->focused_window->flags.mapped && !foc) {
|
|
foc = scr->focused_window;
|
|
}
|
|
if (wPreferences.focus_mode == WKF_CLICK) {
|
|
wSetFocusTo(scr, foc);
|
|
} else {
|
|
unsigned int mask;
|
|
int foo;
|
|
Window bar, win;
|
|
WWindow *tmp;
|
|
|
|
tmp = NULL;
|
|
if (XQueryPointer(dpy, scr->root_win, &bar, &win,
|
|
&foo, &foo, &foo, &foo, &mask)) {
|
|
tmp = wWindowFor(win);
|
|
}
|
|
if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
|
|
wSetFocusTo(scr, foc);
|
|
} else {
|
|
wSetFocusTo(scr, tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We need to always arrange icons when changing workspace, even if
|
|
* no autoarrange icons, because else the icons in different workspaces
|
|
* can be superposed.
|
|
* This can be avoided if appicons are also workspace specific.
|
|
*/
|
|
if (!wPreferences.sticky_icons)
|
|
wArrangeIcons(scr, False);
|
|
|
|
if (scr->dock)
|
|
wAppIconPaint(scr->dock->icon_array[0]);
|
|
|
|
if (scr->clip_icon) {
|
|
if (scr->workspaces[workspace]->clip->auto_collapse ||
|
|
scr->workspaces[workspace]->clip->auto_raise_lower) {
|
|
/* to handle enter notify. This will also */
|
|
XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
|
|
XMapWindow(dpy, scr->clip_icon->icon->core->window);
|
|
} else {
|
|
wClipIconPaint(scr->clip_icon);
|
|
}
|
|
}
|
|
|
|
showWorkspaceName(scr, workspace);
|
|
|
|
WMPostNotificationName(WMNWorkspaceChanged, scr, (void*)workspace);
|
|
|
|
/* XSync(dpy, False); */
|
|
}
|
|
|
|
#ifdef VIRTUAL_DESKTOP
|
|
/* TODO:
|
|
* 1) Allow border around each window so the scrolling
|
|
* won't just stop at the border.
|
|
* 2) Make pager.
|
|
*/
|
|
|
|
void wWorkspaceManageEdge(WScreen *scr)
|
|
{
|
|
int w;
|
|
int vmask;
|
|
XSetWindowAttributes attribs;
|
|
|
|
/* puts("wWorkspaceManageEdge()"); */
|
|
if (wPreferences.vedge_thickness) {
|
|
initVDesk = True;
|
|
for (w = 0; w < scr->workspace_count; w++) {
|
|
/* puts("reset workspace"); */
|
|
wWorkspaceSetViewPort(scr, w, 0, 0);
|
|
}
|
|
|
|
vmask = CWEventMask|CWOverrideRedirect;
|
|
attribs.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask);
|
|
attribs.override_redirect = True;
|
|
scr->virtual_edge_u =
|
|
XCreateWindow(dpy, scr->root_win, 0, 0,
|
|
scr->scr_width, wPreferences.vedge_thickness, 0,
|
|
CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
|
|
scr->virtual_edge_d =
|
|
XCreateWindow(dpy, scr->root_win, 0, scr->scr_height-wPreferences.vedge_thickness,
|
|
scr->scr_width, wPreferences.vedge_thickness, 0,
|
|
CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
|
|
scr->virtual_edge_l =
|
|
XCreateWindow(dpy, scr->root_win, 0, 0,
|
|
wPreferences.vedge_thickness, scr->scr_height, 0,
|
|
CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
|
|
scr->virtual_edge_r =
|
|
XCreateWindow(dpy, scr->root_win, scr->scr_width-wPreferences.vedge_thickness, 0,
|
|
wPreferences.vedge_thickness, scr->scr_height, 0,
|
|
CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
|
|
XMapWindow(dpy, scr->virtual_edge_u);
|
|
XMapWindow(dpy, scr->virtual_edge_d);
|
|
XMapWindow(dpy, scr->virtual_edge_l);
|
|
XMapWindow(dpy, scr->virtual_edge_r);
|
|
wWorkspaceRaiseEdge(scr);
|
|
}
|
|
}
|
|
|
|
void wWorkspaceRaiseEdge(WScreen *scr)
|
|
{
|
|
puts("raise edge");
|
|
if (wPreferences.vedge_thickness && initVDesk) {
|
|
XRaiseWindow(dpy, scr->virtual_edge_u);
|
|
XRaiseWindow(dpy, scr->virtual_edge_d);
|
|
XRaiseWindow(dpy, scr->virtual_edge_l);
|
|
XRaiseWindow(dpy, scr->virtual_edge_r);
|
|
}
|
|
}
|
|
|
|
void wWorkspaceLowerEdge(WScreen *scr)
|
|
{
|
|
puts("lower edge");
|
|
if (wPreferences.vedge_thickness && initVDesk) {
|
|
XLowerWindow(dpy, scr->virtual_edge_u);
|
|
XLowerWindow(dpy, scr->virtual_edge_d);
|
|
XLowerWindow(dpy, scr->virtual_edge_l);
|
|
XLowerWindow(dpy, scr->virtual_edge_r);
|
|
}
|
|
}
|
|
|
|
void wWorkspaceResizeViewPort(WScreen *scr, int workspace, int width, int height)
|
|
{
|
|
scr->workspaces[workspace]->width = WMAX(width,scr->scr_width);
|
|
scr->workspaces[workspace]->height = WMAX(height,scr->scr_height);
|
|
}
|
|
|
|
void updateWorkspaceGeometry(WScreen *scr, int workspace, int *view_x, int *view_y) {
|
|
int most_left, most_right, most_top, most_bottom;
|
|
WWindow *wwin;
|
|
|
|
/* adjust workspace layout */
|
|
wwin = scr->focused_window;
|
|
most_right = scr->scr_width;
|
|
most_bottom = scr->scr_height;
|
|
most_left = 0;
|
|
most_top = 0;
|
|
for(;wwin; wwin = wwin->prev) {
|
|
if (wwin->frame->workspace == workspace) {
|
|
if (!wwin->flags.miniaturized
|
|
&& !wwin->flags.hidden) {
|
|
if (wwin->frame_x < most_left) { /* record positions, should this be cached? */
|
|
most_left = wwin->frame_x;
|
|
}
|
|
if ((int)wwin->frame_x + (int)wwin->frame->core->width > most_right) {
|
|
most_right = wwin->frame_x + wwin->frame->core->width;
|
|
}
|
|
if (wwin->frame_y < most_top) {
|
|
most_top = wwin->frame_y;
|
|
}
|
|
if (wwin->frame_y + wwin->frame->core->height > most_bottom) {
|
|
most_bottom = wwin->frame_y + wwin->frame->core->height;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
scr->workspaces[workspace]->width = WMAX(most_right, scr->scr_width) - WMIN(most_left, 0);
|
|
scr->workspaces[workspace]->height = WMAX(most_bottom, scr->scr_height) - WMIN(most_top, 0);
|
|
|
|
*view_x += -most_left - scr->workspaces[workspace]->view_x;
|
|
scr->workspaces[workspace]->view_x = -most_left;
|
|
|
|
*view_y += -most_top - scr->workspaces[workspace]->view_y;
|
|
scr->workspaces[workspace]->view_y = -most_top;
|
|
|
|
}
|
|
|
|
typedef struct _delay_configure {
|
|
WWindow *wwin;
|
|
int delay_count;
|
|
} _delay_configure;
|
|
|
|
void _sendConfigureNotify (_delay_configure *delay) {
|
|
WWindow *wwin;
|
|
|
|
delay->delay_count--;
|
|
if (!delay->delay_count) {
|
|
for (wwin = delay->wwin; wwin; wwin = wwin->prev) {
|
|
wWindowSynthConfigureNotify(wwin);
|
|
}
|
|
}
|
|
}
|
|
|
|
Bool wWorkspaceSetViewPort(WScreen *scr, int workspace, int view_x, int view_y)
|
|
{
|
|
Bool adjust_flag = False;
|
|
int diff_x, diff_y;
|
|
static _delay_configure delay_configure = {NULL, 0};
|
|
WWindow *wwin;
|
|
|
|
/*printf("wWorkspaceSetViewPort %d %d\n", view_x, view_y);*/
|
|
|
|
updateWorkspaceGeometry(scr, workspace, &view_x, &view_y);
|
|
|
|
if (view_x + scr->scr_width > scr->workspaces[workspace]->width) {
|
|
/* puts("right edge of vdesk"); */
|
|
view_x = scr->workspaces[workspace]->width - scr->scr_width;
|
|
}
|
|
if (view_x < 0) {
|
|
/* puts("left edge of vdesk"); */
|
|
view_x = 0;
|
|
}
|
|
if (view_y + scr->scr_height > scr->workspaces[workspace]->height) {
|
|
/* puts("right edge of vdesk"); */
|
|
view_y = scr->workspaces[workspace]->height - scr->scr_height;
|
|
}
|
|
if (view_y < 0) {
|
|
/* puts("left edge of vdesk"); */
|
|
view_y = 0;
|
|
}
|
|
|
|
diff_x = scr->workspaces[workspace]->view_x - view_x;
|
|
diff_y = scr->workspaces[workspace]->view_y - view_y;
|
|
if (!diff_x && !diff_y)
|
|
return False;
|
|
|
|
scr->workspaces[workspace]->view_x = view_x;
|
|
scr->workspaces[workspace]->view_y = view_y;
|
|
|
|
|
|
for (wwin = scr->focused_window; wwin; wwin = wwin->prev) {
|
|
if (wwin->frame->workspace == workspace) {
|
|
wWindowMove(wwin, wwin->frame_x + diff_x, wwin->frame_y + diff_y);
|
|
}
|
|
}
|
|
if (1) { /* if delay*/
|
|
delay_configure.delay_count++;
|
|
delay_configure.wwin = scr->focused_window;
|
|
WMAddTimerHandler(200, (WMCallback *)_sendConfigureNotify, &delay_configure);
|
|
}
|
|
|
|
return adjust_flag;
|
|
}
|
|
|
|
void wWorkspaceGetViewPosition(WScreen *scr, int workspace, int *view_x, int *view_y) {
|
|
if (view_x) *view_x = scr->workspaces[workspace]->view_x;
|
|
if (view_y) *view_y = scr->workspaces[workspace]->view_y;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
switchWSCommand(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
deleteWSCommand(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
wWorkspaceDelete(menu->frame->screen_ptr,
|
|
menu->frame->screen_ptr->workspace_count-1);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
newWSCommand(WMenu *menu, WMenuEntry *foo)
|
|
{
|
|
int ws;
|
|
|
|
ws = wWorkspaceNew(menu->frame->screen_ptr);
|
|
/* autochange workspace*/
|
|
if (ws>=0)
|
|
wWorkspaceChange(menu->frame->screen_ptr, ws);
|
|
|
|
|
|
/*
|
|
if (ws<9) {
|
|
int kcode;
|
|
if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
|
|
kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
|
|
entry->rtext =
|
|
wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
|
|
}
|
|
}*/
|
|
}
|
|
|
|
|
|
static char*
|
|
cropline(char *line)
|
|
{
|
|
char *start, *end;
|
|
|
|
if (strlen(line)==0)
|
|
return line;
|
|
|
|
start = line;
|
|
end = &(line[strlen(line)])-1;
|
|
while (isspace(*line) && *line!=0) line++;
|
|
while (isspace(*end) && end!=line) {
|
|
*end=0;
|
|
end--;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
|
|
void
|
|
wWorkspaceRename(WScreen *scr, int workspace, char *name)
|
|
{
|
|
char buf[MAX_WORKSPACENAME_WIDTH+1];
|
|
char *tmp;
|
|
|
|
if (workspace >= scr->workspace_count)
|
|
return;
|
|
|
|
/* trim white spaces */
|
|
tmp = cropline(name);
|
|
|
|
if (strlen(tmp)==0) {
|
|
sprintf(buf, _("Workspace %i"), workspace+1);
|
|
} else {
|
|
strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
|
|
}
|
|
buf[MAX_WORKSPACENAME_WIDTH] = 0;
|
|
|
|
/* update workspace */
|
|
wfree(scr->workspaces[workspace]->name);
|
|
scr->workspaces[workspace]->name = wstrdup(buf);
|
|
|
|
if (scr->clip_ws_menu) {
|
|
if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
|
|
wfree(scr->clip_ws_menu->entries[workspace+2]->text);
|
|
scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
|
|
wMenuRealize(scr->clip_ws_menu);
|
|
}
|
|
}
|
|
if (scr->workspace_menu) {
|
|
if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
|
|
wfree(scr->workspace_menu->entries[workspace+2]->text);
|
|
scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
|
|
wMenuRealize(scr->workspace_menu);
|
|
}
|
|
}
|
|
|
|
if (scr->clip_icon)
|
|
wClipIconPaint(scr->clip_icon);
|
|
|
|
WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)workspace);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* callback for when menu entry is edited */
|
|
static void
|
|
onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
char *tmp;
|
|
|
|
tmp = entry->text;
|
|
wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
|
|
}
|
|
|
|
|
|
WMenu*
|
|
wWorkspaceMenuMake(WScreen *scr, Bool titled)
|
|
{
|
|
WMenu *wsmenu;
|
|
|
|
wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
|
|
if (!wsmenu) {
|
|
wwarning(_("could not create Workspace menu"));
|
|
return NULL;
|
|
}
|
|
|
|
/* callback to be called when an entry is edited */
|
|
wsmenu->on_edit = onMenuEntryEdited;
|
|
|
|
wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
|
|
wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
|
|
|
|
return wsmenu;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
|
|
{
|
|
int i;
|
|
long ws;
|
|
char title[MAX_WORKSPACENAME_WIDTH+1];
|
|
WMenuEntry *entry;
|
|
int tmp;
|
|
|
|
if (!menu)
|
|
return;
|
|
|
|
if (menu->entry_no < scr->workspace_count+2) {
|
|
/* new workspace(s) added */
|
|
i = scr->workspace_count-(menu->entry_no-2);
|
|
ws = menu->entry_no - 2;
|
|
while (i>0) {
|
|
strcpy(title, scr->workspaces[ws]->name);
|
|
|
|
entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.editable = 1;
|
|
|
|
i--;
|
|
ws++;
|
|
}
|
|
} else if (menu->entry_no > scr->workspace_count+2) {
|
|
/* removed workspace(s) */
|
|
for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
|
|
wMenuRemoveItem(menu, i);
|
|
}
|
|
}
|
|
wMenuRealize(menu);
|
|
|
|
for (i=0; i<scr->workspace_count; i++) {
|
|
menu->entries[i+2]->flags.indicator_on = 0;
|
|
}
|
|
menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
|
|
|
|
/* don't let user destroy current workspace */
|
|
if (scr->current_workspace == scr->workspace_count-1) {
|
|
wMenuSetEnabled(menu, 1, False);
|
|
} else {
|
|
wMenuSetEnabled(menu, 1, True);
|
|
}
|
|
|
|
tmp = menu->frame->top_width + 5;
|
|
/* if menu got unreachable, bring it to a visible place */
|
|
if (menu->frame_x < tmp - (int)menu->frame->core->width)
|
|
wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
|
|
void
|
|
wWorkspaceSaveState(WScreen *scr, WMPropList *old_state)
|
|
{
|
|
WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
|
|
int i;
|
|
|
|
make_keys();
|
|
|
|
old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
|
|
parr = WMCreatePLArray(NULL);
|
|
for (i=0; i < scr->workspace_count; i++) {
|
|
pstr = WMCreatePLString(scr->workspaces[i]->name);
|
|
wks_state = WMCreatePLDictionary(dName, pstr, NULL);
|
|
WMReleasePropList(pstr);
|
|
if (!wPreferences.flags.noclip) {
|
|
pstr = wClipSaveWorkspaceState(scr, i);
|
|
WMPutInPLDictionary(wks_state, dClip, pstr);
|
|
WMReleasePropList(pstr);
|
|
} else if (old_wks_state!=NULL) {
|
|
if ((foo = WMGetFromPLArray(old_wks_state, i))!=NULL) {
|
|
if ((bar = WMGetFromPLDictionary(foo, dClip))!=NULL) {
|
|
WMPutInPLDictionary(wks_state, dClip, bar);
|
|
}
|
|
}
|
|
}
|
|
WMAddToPLArray(parr, wks_state);
|
|
WMReleasePropList(wks_state);
|
|
}
|
|
WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
|
|
WMReleasePropList(parr);
|
|
}
|
|
|
|
|
|
void
|
|
wWorkspaceRestoreState(WScreen *scr)
|
|
{
|
|
WMPropList *parr, *pstr, *wks_state, *clip_state;
|
|
int i, j, wscount;
|
|
|
|
make_keys();
|
|
|
|
parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
|
|
|
|
if (!parr)
|
|
return;
|
|
|
|
wscount = scr->workspace_count;
|
|
for (i=0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
|
|
wks_state = WMGetFromPLArray(parr, i);
|
|
if (WMIsPLDictionary(wks_state))
|
|
pstr = WMGetFromPLDictionary(wks_state, dName);
|
|
else
|
|
pstr = wks_state;
|
|
if (i >= scr->workspace_count)
|
|
wWorkspaceNew(scr);
|
|
if (scr->workspace_menu) {
|
|
wfree(scr->workspace_menu->entries[i+2]->text);
|
|
scr->workspace_menu->entries[i+2]->text = wstrdup(WMGetFromPLString(pstr));
|
|
scr->workspace_menu->flags.realized = 0;
|
|
}
|
|
wfree(scr->workspaces[i]->name);
|
|
scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
|
|
if (!wPreferences.flags.noclip) {
|
|
clip_state = WMGetFromPLDictionary(wks_state, dClip);
|
|
if (scr->workspaces[i]->clip)
|
|
wDockDestroy(scr->workspaces[i]->clip);
|
|
scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
|
|
WM_CLIP);
|
|
if (i>0)
|
|
wDockHideIcons(scr->workspaces[i]->clip);
|
|
|
|
/* We set the global icons here, because scr->workspaces[i]->clip
|
|
* was not valid in wDockRestoreState().
|
|
* There we only set icon->omnipresent to know which icons we
|
|
* need to set here.
|
|
*/
|
|
for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
|
|
WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
|
|
|
|
if (aicon && aicon->omnipresent) {
|
|
aicon->omnipresent = 0;
|
|
wClipMakeIconOmnipresent(aicon, True);
|
|
XMapWindow(dpy, aicon->icon->core->window);
|
|
}
|
|
}
|
|
}
|
|
|
|
WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)i);
|
|
}
|
|
}
|
|
|
|
|