mirror of
https://github.com/gryf/wmaker.git
synced 2026-06-18 08:25:23 +02:00
26a296db23
This patch is adding vim-like window marking, like in i3. A window can be dynamically assigned a mark label. Then a marked window can be pulled or jumped to. Or the current focused window can be swapped with a marked window. The mark label appears in the Window List in between the window title and the workspace name. Those new options in WindowMaker conf file are used to control the actions: MarkSetKey, MarkUnsetKey, MarkBringKey, MarkJumpKey and MarkSwapKey. Those actions are set to None by default.
475 lines
13 KiB
C
475 lines
13 KiB
C
/*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1997 Shige Abe
|
|
* Copyright (c) 1997-2003 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "wconfig.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
|
|
#include "WindowMaker.h"
|
|
#include "window.h"
|
|
#include "actions.h"
|
|
#include "client.h"
|
|
#include "misc.h"
|
|
#include "stacking.h"
|
|
#include "workspace.h"
|
|
#include "framewin.h"
|
|
#include "switchmenu.h"
|
|
|
|
#define IS_GNUSTEP_MENU(w) ((w)->wm_gnustep_attr && \
|
|
((w)->wm_gnustep_attr->flags & GSWindowLevelAttr) && \
|
|
((w)->wm_gnustep_attr->window_level == WMMainMenuWindowLevel || \
|
|
(w)->wm_gnustep_attr->window_level == WMSubmenuWindowLevel))
|
|
|
|
#define MAX_RTEXT_LENGTH (MAX_WORKSPACENAME_WIDTH + MAX_SHORTCUT_LENGTH + 16)
|
|
|
|
static int initialized = 0;
|
|
static void observer(void *self, WMNotification * notif);
|
|
static void wsobserver(void *self, WMNotification * notif);
|
|
|
|
static WPixmap *switchMenuIconForWindow(WScreen *scr, WWindow *wwin)
|
|
{
|
|
RImage *image = NULL;
|
|
WPixmap *pix;
|
|
WApplication *wapp;
|
|
int max_size;
|
|
|
|
if (!scr || !wwin)
|
|
return NULL;
|
|
|
|
max_size = WMFontHeight(scr->menu_entry_font) + 2;
|
|
if (max_size < 12)
|
|
max_size = 12;
|
|
|
|
/* Prefer the actual appicon image when available */
|
|
wapp = wApplicationOf(wwin->main_window);
|
|
if (wapp && wapp->app_icon && wapp->app_icon->icon && wapp->app_icon->icon->file_image) {
|
|
image = RRetainImage(wapp->app_icon->icon->file_image);
|
|
}
|
|
|
|
/* Fall back to _NET_WM_ICON, then the default icon */
|
|
if (!image && !WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
|
|
image = RRetainImage(wwin->net_icon_image);
|
|
if (!image)
|
|
image = get_icon_image(scr, wwin->wm_instance, wwin->wm_class, max_size);
|
|
|
|
if (!image)
|
|
return NULL;
|
|
|
|
image = wIconValidateIconSize(image, max_size);
|
|
if (!image)
|
|
return NULL;
|
|
|
|
pix = wmalloc(sizeof(WPixmap));
|
|
memset(pix, 0, sizeof(WPixmap));
|
|
RConvertImageMask(scr->rcontext, image, &pix->image, &pix->mask, 128);
|
|
pix->width = image->width;
|
|
pix->height = image->height;
|
|
pix->depth = scr->w_depth;
|
|
|
|
RReleaseImage(image);
|
|
return pix;
|
|
}
|
|
|
|
/*
|
|
* FocusWindow
|
|
*
|
|
* - Needs to check if already in the right workspace before
|
|
* calling wChangeWorkspace?
|
|
*
|
|
* Order:
|
|
* Switch to correct workspace
|
|
* Unshade if shaded
|
|
* If iconified then deiconify else focus/raise.
|
|
*/
|
|
static void focusWindow(WMenu * menu, WMenuEntry * entry)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) menu;
|
|
|
|
wwin = (WWindow *) entry->clientdata;
|
|
wWindowSingleFocus(wwin);
|
|
}
|
|
|
|
void InitializeSwitchMenu(void)
|
|
{
|
|
if (!initialized) {
|
|
initialized = 1;
|
|
|
|
WMAddNotificationObserver(observer, NULL, WMNManaged, NULL);
|
|
WMAddNotificationObserver(observer, NULL, WMNUnmanaged, NULL);
|
|
WMAddNotificationObserver(observer, NULL, WMNChangedWorkspace, NULL);
|
|
WMAddNotificationObserver(observer, NULL, WMNChangedState, NULL);
|
|
WMAddNotificationObserver(observer, NULL, WMNChangedFocus, NULL);
|
|
WMAddNotificationObserver(observer, NULL, WMNChangedStacking, NULL);
|
|
WMAddNotificationObserver(observer, NULL, WMNChangedName, NULL);
|
|
|
|
WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceChanged, NULL);
|
|
WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceNameChanged, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Open switch menu
|
|
*
|
|
*/
|
|
void OpenSwitchMenu(WScreen * scr, int x, int y, int keyboard)
|
|
{
|
|
WMenu *switchmenu = scr->switch_menu;
|
|
WWindow *wwin;
|
|
|
|
if (switchmenu) {
|
|
if (switchmenu->flags.mapped) {
|
|
if (!switchmenu->flags.buttoned) {
|
|
wMenuUnmap(switchmenu);
|
|
} else {
|
|
wRaiseFrame(switchmenu->frame->core);
|
|
|
|
if (keyboard)
|
|
wMenuMapAt(switchmenu, 0, 0, True);
|
|
else
|
|
wMenuMapCopyAt(switchmenu, x - switchmenu->frame->core->width / 2, y);
|
|
}
|
|
} else {
|
|
if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
|
|
y = y - switchmenu->frame->core->height / 2;
|
|
}
|
|
wMenuMapAt(switchmenu, x - switchmenu->frame->core->width / 2, y, keyboard);
|
|
}
|
|
return;
|
|
}
|
|
switchmenu = wMenuCreate(scr, _("Windows"), True);
|
|
scr->switch_menu = switchmenu;
|
|
|
|
wwin = scr->focused_window;
|
|
while (wwin) {
|
|
UpdateSwitchMenu(scr, wwin, ACTION_ADD);
|
|
|
|
wwin = wwin->prev;
|
|
}
|
|
|
|
if (switchmenu) {
|
|
int newx, newy;
|
|
|
|
if (!switchmenu->flags.realized)
|
|
wMenuRealize(switchmenu);
|
|
|
|
if (keyboard && x == 0 && y == 0) {
|
|
newx = newy = 0;
|
|
} else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
|
|
newx = x - switchmenu->frame->core->width / 2;
|
|
newy = y - switchmenu->frame->core->height / 2;
|
|
} else {
|
|
newx = x - switchmenu->frame->core->width / 2;
|
|
newy = y;
|
|
}
|
|
wMenuMapAt(switchmenu, newx, newy, keyboard);
|
|
}
|
|
}
|
|
|
|
static int menuIndexForWindow(WMenu * menu, WWindow * wwin, int old_pos)
|
|
{
|
|
int idx, move_down;
|
|
|
|
if (menu->entry_no <= old_pos)
|
|
return -1;
|
|
|
|
for (idx = 0, move_down = 0; idx < menu->entry_no; idx++) {
|
|
WWindow *tw = (WWindow *) menu->entries[idx]->clientdata;
|
|
|
|
/* Is the window moving down in the menu? If so, we'll need to
|
|
adjust its new index by 1. */
|
|
if (tw == wwin)
|
|
move_down = 1;
|
|
|
|
if (IS_OMNIPRESENT(tw) || (tw != wwin &&
|
|
tw->frame->workspace >= wwin->frame->workspace))
|
|
return idx - move_down;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
static void fillRtext(char *buf, size_t bufsz, WWindow *wwin, WScreen *scr)
|
|
{
|
|
char *mlbl = wwin->mark_key_label ? GetShortcutString(wwin->mark_key_label) : NULL;
|
|
|
|
if (IS_OMNIPRESENT(wwin)) {
|
|
if (mlbl)
|
|
snprintf(buf, bufsz, "[%s] [*]", mlbl);
|
|
else
|
|
snprintf(buf, bufsz, "[*]");
|
|
} else {
|
|
if (mlbl)
|
|
snprintf(buf, bufsz, "[%s] [%s]", mlbl,
|
|
scr->workspaces[wwin->frame->workspace]->name);
|
|
else
|
|
snprintf(buf, bufsz, "[%s]",
|
|
scr->workspaces[wwin->frame->workspace]->name);
|
|
}
|
|
wfree(mlbl);
|
|
}
|
|
|
|
/*
|
|
* Update switch menu
|
|
*/
|
|
void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
|
|
{
|
|
WMenu *switchmenu = scr->switch_menu;
|
|
WMenuEntry *entry;
|
|
char title[MAX_MENU_TEXT_LENGTH + 6];
|
|
int len = sizeof(title);
|
|
int i;
|
|
int checkVisibility = 0;
|
|
|
|
if (!wwin->screen_ptr->switch_menu)
|
|
return;
|
|
/*
|
|
* This menu is updated under the following conditions:
|
|
*
|
|
* 1. When a window is created.
|
|
* 2. When a window is destroyed.
|
|
*
|
|
* 3. When a window changes its title.
|
|
* 4. When a window changes its workspace.
|
|
*/
|
|
if (action == ACTION_ADD) {
|
|
char *t;
|
|
int idx;
|
|
|
|
if (wwin->flags.internal_window || WFLAGP(wwin, skip_window_list) || IS_GNUSTEP_MENU(wwin)) {
|
|
return;
|
|
}
|
|
|
|
if (wwin->frame->title)
|
|
snprintf(title, len, "%s", wwin->frame->title);
|
|
else
|
|
snprintf(title, len, "%s", DEF_WINDOW_TITLE);
|
|
t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH);
|
|
|
|
if (IS_OMNIPRESENT(wwin))
|
|
idx = -1;
|
|
else {
|
|
idx = menuIndexForWindow(switchmenu, wwin, 0);
|
|
}
|
|
|
|
entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
|
|
wfree(t);
|
|
|
|
entry->icon = switchMenuIconForWindow(scr, wwin);
|
|
|
|
entry->flags.indicator = 1;
|
|
entry->rtext = wmalloc(MAX_RTEXT_LENGTH);
|
|
fillRtext(entry->rtext, MAX_RTEXT_LENGTH, wwin, scr);
|
|
|
|
if (wwin->flags.hidden) {
|
|
entry->flags.indicator_type = MI_HIDDEN;
|
|
entry->flags.indicator_on = 1;
|
|
} else if (wwin->flags.miniaturized) {
|
|
entry->flags.indicator_type = MI_MINIWINDOW;
|
|
entry->flags.indicator_on = 1;
|
|
} else if (wwin->flags.focused) {
|
|
entry->flags.indicator_type = MI_DIAMOND;
|
|
entry->flags.indicator_on = 1;
|
|
} else if (wwin->flags.shaded) {
|
|
entry->flags.indicator_type = MI_SHADED;
|
|
entry->flags.indicator_on = 1;
|
|
}
|
|
|
|
wMenuRealize(switchmenu);
|
|
checkVisibility = 1;
|
|
} else {
|
|
char *t;
|
|
for (i = 0; i < switchmenu->entry_no; i++) {
|
|
entry = switchmenu->entries[i];
|
|
/* this is the entry that was changed */
|
|
if (entry->clientdata == wwin) {
|
|
switch (action) {
|
|
case ACTION_REMOVE:
|
|
wMenuRemoveItem(switchmenu, i);
|
|
wMenuRealize(switchmenu);
|
|
checkVisibility = 1;
|
|
break;
|
|
|
|
case ACTION_CHANGE:
|
|
if (entry->text)
|
|
wfree(entry->text);
|
|
|
|
if (wwin->frame->title)
|
|
snprintf(title, MAX_MENU_TEXT_LENGTH, "%s", wwin->frame->title);
|
|
else
|
|
snprintf(title, MAX_MENU_TEXT_LENGTH, "%s", DEF_WINDOW_TITLE);
|
|
|
|
t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH);
|
|
entry->text = t;
|
|
|
|
fillRtext(entry->rtext, MAX_RTEXT_LENGTH, wwin, scr);
|
|
|
|
wMenuRealize(switchmenu);
|
|
checkVisibility = 1;
|
|
break;
|
|
|
|
case ACTION_CHANGE_WORKSPACE:
|
|
if (entry->rtext) {
|
|
int idx = -1;
|
|
char *t, *rt;
|
|
WPixmap *ipix;
|
|
int it, ion;
|
|
|
|
fillRtext(entry->rtext, MAX_RTEXT_LENGTH, wwin, scr);
|
|
|
|
rt = entry->rtext;
|
|
entry->rtext = NULL;
|
|
ipix = entry->icon;
|
|
entry->icon = NULL;
|
|
t = entry->text;
|
|
entry->text = NULL;
|
|
|
|
it = entry->flags.indicator_type;
|
|
ion = entry->flags.indicator_on;
|
|
|
|
if (!IS_OMNIPRESENT(wwin) && idx < 0) {
|
|
idx = menuIndexForWindow(switchmenu, wwin, i);
|
|
}
|
|
|
|
wMenuRemoveItem(switchmenu, i);
|
|
|
|
entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
|
|
wfree(t);
|
|
entry->rtext = rt;
|
|
entry->icon = ipix;
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_type = it;
|
|
entry->flags.indicator_on = ion;
|
|
}
|
|
wMenuRealize(switchmenu);
|
|
checkVisibility = 1;
|
|
break;
|
|
|
|
case ACTION_CHANGE_STATE:
|
|
if (wwin->flags.hidden) {
|
|
entry->flags.indicator_type = MI_HIDDEN;
|
|
entry->flags.indicator_on = 1;
|
|
} else if (wwin->flags.miniaturized) {
|
|
entry->flags.indicator_type = MI_MINIWINDOW;
|
|
entry->flags.indicator_on = 1;
|
|
} else if (wwin->flags.shaded && !wwin->flags.focused) {
|
|
entry->flags.indicator_type = MI_SHADED;
|
|
entry->flags.indicator_on = 1;
|
|
} else {
|
|
entry->flags.indicator_on = wwin->flags.focused;
|
|
entry->flags.indicator_type = MI_DIAMOND;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (checkVisibility) {
|
|
int tmp;
|
|
|
|
tmp = switchmenu->frame->top_width + 5;
|
|
/* if menu got unreachable, bring it to a visible place */
|
|
if (switchmenu->frame_x < tmp - (int)switchmenu->frame->core->width) {
|
|
wMenuMove(switchmenu, tmp - (int)switchmenu->frame->core->width,
|
|
switchmenu->frame_y, False);
|
|
}
|
|
}
|
|
wMenuPaint(switchmenu);
|
|
}
|
|
|
|
static void UpdateSwitchMenuWorkspace(WScreen *scr, int workspace)
|
|
{
|
|
WMenu *menu = scr->switch_menu;
|
|
int i;
|
|
WWindow *wwin;
|
|
|
|
if (!menu)
|
|
return;
|
|
|
|
for (i = 0; i < menu->entry_no; i++) {
|
|
wwin = (WWindow *) menu->entries[i]->clientdata;
|
|
|
|
if (wwin->frame->workspace == workspace && !IS_OMNIPRESENT(wwin)) {
|
|
fillRtext(menu->entries[i]->rtext, MAX_RTEXT_LENGTH, wwin, scr);
|
|
menu->flags.realized = 0;
|
|
}
|
|
}
|
|
if (!menu->flags.realized)
|
|
wMenuRealize(menu);
|
|
}
|
|
|
|
static void observer(void *self, WMNotification * notif)
|
|
{
|
|
WWindow *wwin = (WWindow *) WMGetNotificationObject(notif);
|
|
const char *name = WMGetNotificationName(notif);
|
|
void *data = WMGetNotificationClientData(notif);
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) self;
|
|
|
|
if (!wwin)
|
|
return;
|
|
|
|
if (strcmp(name, WMNManaged) == 0)
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD);
|
|
else if (strcmp(name, WMNUnmanaged) == 0)
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_REMOVE);
|
|
else if (strcmp(name, WMNChangedWorkspace) == 0)
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE);
|
|
else if (strcmp(name, WMNChangedFocus) == 0)
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
|
|
else if (strcmp(name, WMNChangedName) == 0)
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE);
|
|
else if (strcmp(name, WMNChangedState) == 0) {
|
|
if (strcmp((char *)data, "omnipresent") == 0) {
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE);
|
|
} else {
|
|
UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void wsobserver(void *self, WMNotification * notif)
|
|
{
|
|
WScreen *scr = (WScreen *) WMGetNotificationObject(notif);
|
|
const char *name = WMGetNotificationName(notif);
|
|
void *data = WMGetNotificationClientData(notif);
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) self;
|
|
|
|
if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
|
|
UpdateSwitchMenuWorkspace(scr, (uintptr_t)data);
|
|
} else if (strcmp(name, WMNWorkspaceChanged) == 0) {
|
|
|
|
}
|
|
}
|