/** Determine if a client is allowed focus. */ char ShouldFocus(const ClientNode *np, char current) { /* Only display clients on the current desktop or clients that are sticky. */ if(!settings.listAllTasks || current) { if(!IsClientOnCurrentDesktop(np)) { return 0; } } /* Don't display a client if it doesn't want to be displayed. */ if(np->state.status & STAT_NOLIST) { return 0; } /* Don't display a client on the tray if it has an owner. */ if(np->owner != None) { return 0; } if(!(np->state.status & (STAT_MAPPED | STAT_MINIMIZED | STAT_SHADED))) { return 0; } return 1; }
/** Check if all clients in this grou are on the top of their layer. */ char IsGroupOnTop(const TaskEntry *entry) { ClientEntry *cp; int layer; for(layer = FIRST_LAYER; layer <= LAST_LAYER; layer++) { ClientNode *np; char foundOther = 0; for(np = nodes[layer]; np; np = np->next) { char found = 0; if(!IsClientOnCurrentDesktop(np)) { continue; } for(cp = entry->clients; cp; cp = cp->next) { if(np == cp->client) { if(foundOther) { return 0; } found = 1; break; } } foundOther = !found; } } return 1; }
/** Draw a specific task bar. */ void Render(const TaskBarType *bp) { TaskEntry *tp; char *displayName; ButtonNode button; int x, y; if(JUNLIKELY(shouldExit)) { return; } ClearTrayDrawable(bp->cp); if(!taskEntries) { UpdateSpecificTray(bp->cp->tray, bp->cp); return; } ResetButton(&button, bp->cp->pixmap); button.border = settings.trayDecorations == DECO_MOTIF; button.font = FONT_TASKLIST; button.height = bp->itemHeight; button.width = bp->itemWidth; button.text = NULL; x = 0; y = 0; for(tp = taskEntries; tp; tp = tp->next) { if(!ShouldShowEntry(tp)) { continue; } /* Check for an active or urgent window and count clients. */ ClientEntry *cp; unsigned clientCount = 0; button.type = BUTTON_TASK; for(cp = tp->clients; cp; cp = cp->next) { if(ShouldFocus(cp->client, 0)) { const char flash = (cp->client->state.status & STAT_FLASH) != 0; const char active = (cp->client->state.status & STAT_ACTIVE) && IsClientOnCurrentDesktop(cp->client); if(flash || active) { if(button.type == BUTTON_TASK) { button.type = BUTTON_TASK_ACTIVE; } else { button.type = BUTTON_TASK; } } clientCount += 1; } } button.x = x; button.y = y; if(!tp->clients->client->icon) { button.icon = GetDefaultIcon(); } else { button.icon = tp->clients->client->icon; } displayName = NULL; if(tp->clients->client->className && settings.groupTasks) { if(clientCount != 1) { const size_t len = strlen(tp->clients->client->className) + 16; displayName = Allocate(len); snprintf(displayName, len, "%s (%u)", tp->clients->client->className, clientCount); button.text = displayName; } else { button.text = tp->clients->client->className; } } else { button.text = tp->clients->client->name; } DrawButton(&button); if(displayName) { Release(displayName); } if(bp->layout == LAYOUT_HORIZONTAL) { x += bp->itemWidth; } else { y += bp->itemHeight; } } UpdateSpecificTray(bp->cp->tray, bp->cp); }
/** Raise all clients in a group and focus the top-most. */ void FocusGroup(const TaskEntry *tp) { const char *className = tp->clients->client->className; ClientNode **toRestore; const ClientEntry *cp; unsigned restoreCount; int i; char shouldSwitch; /* If there is no class name, then there will only be one client. */ if(!className || !settings.groupTasks) { if(!(tp->clients->client->state.status & STAT_STICKY)) { ChangeDesktop(tp->clients->client->state.desktop); } RestoreClient(tp->clients->client, 1); FocusClient(tp->clients->client); return; } /* If there is a client in the group on this desktop, * then we remain on the same desktop. */ shouldSwitch = 1; for(cp = tp->clients; cp; cp = cp->next) { if(IsClientOnCurrentDesktop(cp->client)) { shouldSwitch = 0; break; } } /* Switch to the desktop of the top-most client in the group. */ if(shouldSwitch) { for(i = 0; i < LAYER_COUNT; i++) { ClientNode *np; for(np = nodes[i]; np; np = np->next) { if(np->className && !strcmp(np->className, className)) { if(ShouldFocus(np, 0)) { if(!(np->state.status & STAT_STICKY)) { ChangeDesktop(np->state.desktop); } break; } } } } } /* Build up the list of clients to restore in correct order. */ toRestore = AllocateStack(sizeof(ClientNode*) * clientCount); restoreCount = 0; for(i = 0; i < LAYER_COUNT; i++) { ClientNode *np; for(np = nodes[i]; np; np = np->next) { if(!ShouldFocus(np, 1)) { continue; } if(np->className && !strcmp(np->className, className)) { toRestore[restoreCount] = np; restoreCount += 1; } } } Assert(restoreCount <= clientCount); for(i = restoreCount - 1; i >= 0; i--) { RestoreClient(toRestore[i], 1); } for(i = 0; i < restoreCount; i++) { if(toRestore[i]->state.status & (STAT_CANFOCUS | STAT_TAKEFOCUS)) { FocusClient(toRestore[i]); break; } } ReleaseStack(toRestore); }
/** Process a task list button event. */ void ProcessTaskButtonEvent(TrayComponentType *cp, int x, int y, int mask) { TaskBarType *bar = (TaskBarType*)cp->object; TaskEntry *entry = GetEntry(bar, x, y); if(entry) { ClientEntry *cp; ClientNode *focused = NULL; char onTop = 0; char hasActive = 0; switch(mask) { case Button1: /* Raise or minimize items in this group. */ for(cp = entry->clients; cp; cp = cp->next) { int layer; char foundTop = 0; if(cp->client->state.status & STAT_MINIMIZED) { continue; } else if(!ShouldFocus(cp->client, 0)) { continue; } for(layer = LAST_LAYER; layer >= FIRST_LAYER; layer--) { ClientNode *np; for(np = nodes[layer]; np; np = np->next) { if(np->state.status & STAT_MINIMIZED) { continue; } else if(!ShouldFocus(np, 0)) { continue; } if(np == cp->client) { const char isActive = (np->state.status & STAT_ACTIVE) && IsClientOnCurrentDesktop(np); onTop = onTop || !foundTop; if(isActive) { focused = np; } if(!(cp->client->state.status & (STAT_CANFOCUS | STAT_TAKEFOCUS)) || isActive) { hasActive = 1; } } if(hasActive && onTop) { goto FoundActiveAndTop; } foundTop = 1; } } } FoundActiveAndTop: if(hasActive && onTop) { ClientNode *nextClient = NULL; int i; /* Try to find a client on a different desktop. */ for(i = 0; i < settings.desktopCount - 1; i++) { const int target = (currentDesktop + i + 1) % settings.desktopCount; for(cp = entry->clients; cp; cp = cp->next) { ClientNode *np = cp->client; if(!ShouldFocus(np, 0)) { continue; } else if(np->state.status & STAT_STICKY) { continue; } else if(np->state.desktop == target) { if(!nextClient || np->state.status & STAT_ACTIVE) { nextClient = np; } } } if(nextClient) { break; } } /* Focus the next client or minimize the current group. */ if(nextClient) { ChangeDesktop(nextClient->state.desktop); RestoreClient(nextClient, 1); } else { MinimizeGroup(entry); } } else { FocusGroup(entry); if(focused) { FocusClient(focused); } } break; case Button3: ShowClientList(bar, entry); break; case Button4: FocusPrevious(); break; case Button5: FocusNext(); break; default: break; } } }