/** Show a popup window. */ void ShowPopup(int x, int y, const char *text, const PopupMaskType context) { const ScreenType *sp; Assert(text); if(!(settings.popupMask & context)) { return; } if(popup.text) { if(x == popup.x && y == popup.y && !strcmp(popup.text, text)) { // This popup is already shown. return; } Release(popup.text); popup.text = NULL; } if(text[0] == 0) { return; } GetMousePosition(&popup.mx, &popup.my, &popup.mw); popup.text = CopyString(text); popup.height = GetStringHeight(FONT_POPUP) + 2; popup.width = GetStringWidth(FONT_POPUP, popup.text) + 9; sp = GetCurrentScreen(x, y); if(popup.width > sp->width) { popup.width = sp->width; } popup.x = x; if(y + 2 * popup.height + 2 >= sp->height) { popup.y = y - popup.height - 2; } else { popup.y = y + popup.height + 2; } if(popup.width + popup.x > sp->x + sp->width) { popup.x = sp->x + sp->width - popup.width - 2; } if(popup.height + popup.y > sp->y + sp->height) { popup.y = sp->y + sp->height - popup.height - 2; } if(popup.x < 2) { popup.x = 2; } if(popup.y < 2) { popup.y = 2; } if(popup.window == None) { XSetWindowAttributes attr; unsigned long attrMask = 0; attrMask |= CWEventMask; attr.event_mask = ExposureMask | PointerMotionMask | PointerMotionHintMask; attrMask |= CWSaveUnder; attr.save_under = True; attrMask |= CWDontPropagate; attr.do_not_propagate_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask; popup.window = JXCreateWindow(display, rootWindow, popup.x, popup.y, popup.width, popup.height, 0, CopyFromParent, InputOutput, CopyFromParent, attrMask, &attr); SetAtomAtom(popup.window, ATOM_NET_WM_WINDOW_TYPE, ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION); JXMapRaised(display, popup.window); } else { JXMoveResizeWindow(display, popup.window, popup.x, popup.y, popup.width, popup.height); JXFreePixmap(display, popup.pmap); } popup.pmap = JXCreatePixmap(display, popup.window, popup.width, popup.height, rootDepth); JXSetForeground(display, rootGC, colors[COLOR_POPUP_BG]); JXFillRectangle(display, popup.pmap, rootGC, 0, 0, popup.width - 1, popup.height - 1); JXSetForeground(display, rootGC, colors[COLOR_POPUP_OUTLINE]); JXDrawRectangle(display, popup.pmap, rootGC, 0, 0, popup.width - 1, popup.height - 1); RenderString(popup.pmap, FONT_POPUP, COLOR_POPUP_FG, 4, 1, popup.width, popup.text); JXCopyArea(display, popup.pmap, popup.window, rootGC, 0, 0, popup.width, popup.height, 0, 0); }
/** Constrain the size of the client. */ char ConstrainSize(ClientNode *np) { BoundingBox box; const ScreenType *sp; int north, south, east, west; const int oldWidth = np->width; const int oldHeight = np->height; /* First we make sure the window isn't larger than the program allows. * We do this here to avoid moving the window below. */ np->width = Min(np->width, np->maxWidth); np->height = Min(np->height, np->maxHeight); /* Constrain the width if necessary. */ sp = GetCurrentScreen(np->x, np->y); GetScreenBounds(sp, &box); SubtractTrayBounds(GetTrays(), &box, np->state.layer); SubtractStrutBounds(&box, np); GetBorderSize(&np->state, &north, &south, &east, &west); if(np->width + east + west > sp->width) { box.x += west; box.width -= east + west; if(box.width > np->maxWidth) { box.width = np->maxWidth; } if(box.width > np->width) { box.width = np->width; } np->x = box.x; np->width = box.width - (box.width % np->xinc); } /* Constrain the height if necessary. */ if(np->height + north + south > sp->height) { box.y += north; box.height -= north + south; if(box.height > np->maxHeight) { box.height = np->maxHeight; } if(box.height > np->height) { box.height = np->height; } np->y = box.y; np->height = box.height - (box.height % np->yinc); } /* If the program has a minimum constraint, we apply that here. * Note that this could cause the window to overlap something. */ np->width = Max(np->width, np->minWidth); np->height = Max(np->height, np->minHeight); /* Fix the aspect ratio. */ if(np->sizeFlags & PAspect) { if(np->width * np->aspect.miny < np->height * np->aspect.minx) { np->height = (np->width * np->aspect.miny) / np->aspect.minx; } if(np->width * np->aspect.maxy > np->height * np->aspect.maxx) { np->width = (np->height * np->aspect.maxx) / np->aspect.maxy; } } if(np->width != oldWidth || np->height != oldHeight) { return 1; } else { return 0; } }
/** Place a maximized client on the screen. */ void PlaceMaximizedClient(ClientNode *np, MaxFlags flags) { BoundingBox box; const ScreenType *sp; int north, south, east, west; np->oldx = np->x; np->oldy = np->y; np->oldWidth = np->width; np->oldHeight = np->height; np->state.maxFlags = flags; GetBorderSize(&np->state, &north, &south, &east, &west); sp = GetCurrentScreen(np->x + (east + west + np->width) / 2, np->y + (north + south + np->height) / 2); GetScreenBounds(sp, &box); if(!(flags & (MAX_HORIZ | MAX_LEFT | MAX_RIGHT))) { box.x = np->x - west; box.width = np->width + east + west; } if(!(flags & (MAX_VERT | MAX_TOP | MAX_BOTTOM))) { box.y = np->y - north; box.height = np->height + north + south; } SubtractTrayBounds(GetTrays(), &box, np->state.layer); SubtractStrutBounds(&box, np); if(box.width > np->maxWidth) { box.width = np->maxWidth; } if(box.height > np->maxHeight) { box.height = np->maxHeight; } if(np->sizeFlags & PAspect) { if(box.width * np->aspect.miny < box.height * np->aspect.minx) { box.height = (box.width * np->aspect.miny) / np->aspect.minx; } if(box.width * np->aspect.maxy > box.height * np->aspect.maxx) { box.width = (box.height * np->aspect.maxx) / np->aspect.maxy; } } /* Remove window outlines. */ if(flags & (MAX_VERT | MAX_TOP)) { north = Max(0, north - 1); } if(flags & (MAX_VERT | MAX_BOTTOM)) { south = Max(0, south - 1); } if(flags & (MAX_HORIZ | MAX_LEFT)) { west = Max(0, west - 1); } if(flags & (MAX_HORIZ | MAX_RIGHT)) { east = Max(0, east - 1); } /* If maximizing horizontally, update width. */ if(flags & MAX_HORIZ) { np->x = box.x; np->width = box.width; if(!(np->state.status & STAT_IIGNORE)) { np->width -= ((np->width - np->baseWidth) % np->xinc); } } else if(flags & MAX_LEFT) { np->x = box.x; np->width = box.width / 2 - west; if(!(np->state.status & STAT_IIGNORE)) { np->width -= ((np->width - np->baseWidth) % np->xinc); } } else if(flags & MAX_RIGHT) { np->x = box.x + box.width / 2 + west; np->width = box.width / 2 - east; if(!(np->state.status & STAT_IIGNORE)) { np->width -= ((np->width - np->baseWidth) % np->xinc); } } /* If maximizing vertically, update height. */ if(flags & MAX_VERT) { np->y = box.y + north; np->height = box.height - north; if(!(np->state.status & STAT_IIGNORE)) { np->height -= ((np->height - np->baseHeight) % np->yinc); } } else if(flags & MAX_TOP) { np->y = box.y + north; np->height = box.height / 2 - north - south; if(!(np->state.status & STAT_IIGNORE)) { np->height -= ((np->height - np->baseHeight) % np->yinc); } } else if(flags & MAX_BOTTOM) { np->y = box.y + box.height / 2 + north; np->height = box.height / 2 - north - south; if(!(np->state.status & STAT_IIGNORE)) { np->height -= ((np->height - np->baseHeight) % np->yinc); } } }
/** Show the menu associated with a task list item. */ void ShowClientList(TaskBarType *bar, TaskEntry *tp) { Menu *menu; MenuItem *item; ClientEntry *cp; const ScreenType *sp; int x, y; Window w; if(settings.groupTasks) { menu = CreateMenu(); item = CreateMenuItem(MENU_ITEM_NORMAL); item->name = CopyString(_("Close")); item->action.type = MA_CLOSE | MA_GROUP_MASK; item->action.context = tp; item->next = menu->items; menu->items = item; item = CreateMenuItem(MENU_ITEM_NORMAL); item->name = CopyString(_("Minimize")); item->action.type = MA_MINIMIZE | MA_GROUP_MASK; item->action.context = tp; item->next = menu->items; menu->items = item; item = CreateMenuItem(MENU_ITEM_NORMAL); item->name = CopyString(_("Restore")); item->action.type = MA_RESTORE | MA_GROUP_MASK; item->action.context = tp; item->next = menu->items; menu->items = item; item = CreateMenuItem(MENU_ITEM_SUBMENU); item->name = CopyString(_("Send To")); item->action.type = MA_SENDTO_MENU | MA_GROUP_MASK; item->action.context = tp; item->next = menu->items; menu->items = item; /* Load the separator and group actions. */ item = CreateMenuItem(MENU_ITEM_SEPARATOR); item->next = menu->items; menu->items = item; /* Load the clients into the menu. */ for(cp = tp->clients; cp; cp = cp->next) { if(!ShouldFocus(cp->client, 0)) { continue; } item = CreateMenuItem(MENU_ITEM_NORMAL); if(cp->client->state.status & STAT_MINIMIZED) { size_t len = 0; if(cp->client->name) { len = strlen(cp->client->name); } item->name = Allocate(len + 3); item->name[0] = '['; memcpy(&item->name[1], cp->client->name, len); item->name[len + 1] = ']'; item->name[len + 2] = 0; } else { item->name = CopyString(cp->client->name); } item->icon = cp->client->icon ? cp->client->icon : GetDefaultIcon(); item->action.type = MA_EXECUTE; item->action.context = cp->client; item->next = menu->items; menu->items = item; } } else { /* Not grouping clients. */ menu = CreateWindowMenu(tp->clients->client); } /* Initialize and position the menu. */ InitializeMenu(menu); sp = GetCurrentScreen(bar->cp->screenx, bar->cp->screeny); GetMousePosition(&x, &y, &w); if(bar->layout == LAYOUT_HORIZONTAL) { if(bar->cp->screeny + bar->cp->height / 2 < sp->y + sp->height / 2) { /* Bottom of the screen: menus go up. */ y = bar->cp->screeny + bar->cp->height; } else { /* Top of the screen: menus go down. */ y = bar->cp->screeny - menu->height; } x -= menu->width / 2; x = Max(x, sp->x); } else { if(bar->cp->screenx + bar->cp->width / 2 < sp->x + sp->width / 2) { /* Left side: menus go right. */ x = bar->cp->screenx + bar->cp->width; } else { /* Right side: menus go left. */ x = bar->cp->screenx - menu->width; } y -= menu->height / 2; y = Max(y, sp->y); } ShowMenu(menu, RunTaskBarCommand, x, y, 0); DestroyMenu(menu); }