shod

mouse-based window manager that can tile windows inside floating containers
Log | Files | Refs | README | LICENSE

xcontainer.c (51456B)


      1 #include "shod.h"
      2 
      3 #define DIV                     15      /* see containerplace() for details */
      4 
      5 /* get next focused container after old on given monitor and desktop */
      6 struct Container *
      7 getnextfocused(struct Monitor *mon, int desk)
      8 {
      9 	struct Container *c;
     10 
     11 	TAILQ_FOREACH(c, &wm.focusq, entry)
     12 		if (containerisvisible(c, mon, desk))
     13 			return c;
     14 	return NULL;
     15 }
     16 
     17 /* snap to edge */
     18 static void
     19 snaptoedge(int *x, int *y, int w, int h)
     20 {
     21 	struct Container *c;
     22 
     23 	if (config.snap <= 0)
     24 		return;
     25 	if (abs(*y - wm.selmon->wy) < config.snap)
     26 		*y = wm.selmon->wy;
     27 	if (abs(*y + h - wm.selmon->wy - wm.selmon->wh) < config.snap)
     28 		*y = wm.selmon->wy + wm.selmon->wh - h;
     29 	if (abs(*x - wm.selmon->wx) < config.snap)
     30 		*x = wm.selmon->wx;
     31 	if (abs(*x + w - wm.selmon->wx - wm.selmon->ww) < config.snap)
     32 		*x = wm.selmon->wx + wm.selmon->ww - w;
     33 	TAILQ_FOREACH(c, &wm.focusq, entry) {
     34 		if (!c->isminimized && containerisvisible(c, wm.selmon, wm.selmon->seldesk)) {
     35 			if (*x + w >= c->x && *x <= c->x + c->w) {
     36 				if (abs(*y + h - c->y) < config.snap) {
     37 					*y = c->y - h;
     38 				}
     39 				if (abs(*y - c->y) < config.snap) {
     40 					*y = c->y;
     41 				}
     42 				if (abs(*y + h - c->y - c->h) < config.snap) {
     43 					*y = c->y + c->h - h;
     44 				}
     45 				if (abs(*y - c->y - c->h) < config.snap) {
     46 					*y = c->y + c->h;
     47 				}
     48 			}
     49 			if (*y + h >= c->y && *y <= c->y + c->h) {
     50 				if (abs(*x + w - c->x) < config.snap) {
     51 					*x = c->x - w;
     52 				}
     53 				if (abs(*x - c->x) < config.snap) {
     54 					*x = c->x;
     55 				}
     56 				if (abs(*x + w - c->x - c->w) < config.snap) {
     57 					*x = c->x + c->w - w;
     58 				}
     59 				if (abs(*x - c->x - c->w) < config.snap) {
     60 					*x = c->x + c->w;
     61 				}
     62 			}
     63 		}
     64 	}
     65 }
     66 
     67 /* increment number of clients */
     68 static void
     69 clientsincr(void)
     70 {
     71 	wm.nclients++;
     72 }
     73 
     74 /* decrement number of clients */
     75 static void
     76 clientsdecr(void)
     77 {
     78 	wm.nclients--;
     79 }
     80 
     81 /* get decoration style (and state) of container */
     82 static int
     83 containergetstyle(struct Container *c)
     84 {
     85 	return (c == wm.focused) ? FOCUSED : UNFOCUSED;
     86 }
     87 
     88 /* get tab decoration style */
     89 static int
     90 tabgetstyle(struct Tab *t)
     91 {
     92 	if (t == NULL)
     93 		return UNFOCUSED;
     94 	if (t->isurgent)
     95 		return URGENT;
     96 	if (t->row->col->c == wm.focused)
     97 		return FOCUSED;
     98 	return UNFOCUSED;
     99 }
    100 
    101 /* clear window urgency */
    102 static void
    103 tabclearurgency(struct Tab *tab)
    104 {
    105 	XWMHints wmh = {0};
    106 
    107 	XSetWMHints(dpy, tab->obj.win, &wmh);
    108 	tab->isurgent = 0;
    109 }
    110 
    111 /* commit tab size and position */
    112 static void
    113 tabmoveresize(struct Tab *t)
    114 {
    115 	XMoveResizeWindow(dpy, t->title, t->x, 0, t->w, config.titlewidth);
    116 	if (t->ptw != t->w) {
    117 		tabdecorate(t, 0);
    118 	}
    119 	winnotify(t->obj.win, t->row->col->c->x + t->row->col->x, t->row->col->c->y + t->row->y + config.titlewidth, t->winw, t->winh);
    120 }
    121 
    122 /* commit titlebar size and position */
    123 static void
    124 titlebarmoveresize(struct Row *row, int x, int y, int w)
    125 {
    126 	XMoveResizeWindow(dpy, row->bar, x, y, w, config.titlewidth);
    127 	XMoveWindow(dpy, row->bl, 0, 0);
    128 	XMoveWindow(dpy, row->br, w - config.titlewidth, 0);
    129 }
    130 
    131 /* calculate size of dialogs of a tab */
    132 static void
    133 dialogcalcsize(struct Dialog *dial)
    134 {
    135 	struct Tab *tab;
    136 
    137 	tab = dial->tab;
    138 	dial->w = max(1, min(dial->maxw, tab->winw - 2 * config.borderwidth));
    139 	dial->h = max(1, min(dial->maxh, tab->winh - 2 * config.borderwidth));
    140 	dial->x = tab->winw / 2 - dial->w / 2;
    141 	dial->y = tab->winh / 2 - dial->h / 2;
    142 }
    143 
    144 /* create new dialog */
    145 static struct Dialog *
    146 dialognew(Window win, int maxw, int maxh, int ignoreunmap)
    147 {
    148 	struct Dialog *dial;
    149 
    150 	dial = emalloc(sizeof(*dial));
    151 	*dial = (struct Dialog){
    152 		.pix = None,
    153 		.maxw = maxw,
    154 		.maxh = maxh,
    155 		.ignoreunmap = ignoreunmap,
    156 		.obj.win = win,
    157 		.obj.type = TYPE_DIALOG,
    158 	};
    159 	dial->frame = XCreateWindow(dpy, root, 0, 0, maxw, maxh, 0, depth, CopyFromParent, visual, clientmask, &clientswa);
    160 	XReparentWindow(dpy, dial->obj.win, dial->frame, 0, 0);
    161 	XMapWindow(dpy, dial->obj.win);
    162 	return dial;
    163 }
    164 
    165 /* calculate position and width of tabs of a row */
    166 static void
    167 rowcalctabs(struct Row *row)
    168 {
    169 	struct Object *p, *q;
    170 	struct Dialog *d;
    171 	struct Tab *t;
    172 	int i, x;
    173 
    174 	if (TAILQ_EMPTY(&row->tabq))
    175 		return;
    176 	x = config.titlewidth;
    177 	i = 0;
    178 	TAILQ_FOREACH(p, &row->tabq, entry) {
    179 		t = (struct Tab *)p;
    180 		t->winh = max(1, row->h - config.titlewidth);
    181 		t->winw = row->col->w;
    182 		t->w = max(1, ((i + 1) * (t->winw - 2 * config.titlewidth) / row->ntabs) - (i * (t->winw - 2 * config.titlewidth) / row->ntabs));
    183 		t->x = x;
    184 		x += t->w;
    185 		TAILQ_FOREACH(q, &t->dialq, entry) {
    186 			d = (struct Dialog *)q;
    187 			dialogcalcsize(d);
    188 		}
    189 		i++;
    190 	}
    191 }
    192 
    193 /* calculate position and height of rows of a column */
    194 static void
    195 colcalcrows(struct Column *col, int recalcfact)
    196 {
    197 	struct Container *c;
    198 	struct Row *row;
    199 	int i, y, h, sumh;
    200 	int content;
    201 	int recalc;
    202 
    203 	c = col->c;
    204 
    205 	if (TAILQ_EMPTY(&col->rowq))
    206 		return;
    207 	if (col->c->isfullscreen) {
    208 		TAILQ_FOREACH(row, &col->rowq, entry) {
    209 			row->y = -config.titlewidth;
    210 			row->h = col->c->h + config.titlewidth;
    211 			rowcalctabs(row);
    212 		}
    213 		return;
    214 	}
    215 
    216 	/* check if rows sum up the height of the container */
    217 	content = columncontentheight(col);
    218 	sumh = 0;
    219 	recalc = 0;
    220 	TAILQ_FOREACH(row, &col->rowq, entry) {
    221 		if (!recalcfact) {
    222 			if (TAILQ_NEXT(row, entry) == NULL) {
    223 				row->h = content - sumh + config.titlewidth;
    224 			} else {
    225 				row->h = row->fact * content + config.titlewidth;
    226 			}
    227 			if (row->h < config.titlewidth) {
    228 				recalc = 1;
    229 			}
    230 		}
    231 		sumh += row->h - config.titlewidth;
    232 	}
    233 	if (sumh != content)
    234 		recalc = 1;
    235 
    236 	h = col->c->h - 2 * c->b - (col->nrows - 1) * config.divwidth;
    237 	y = c->b;
    238 	i = 0;
    239 	TAILQ_FOREACH(row, &col->rowq, entry) {
    240 		if (recalc)
    241 			row->h = max(config.titlewidth, ((i + 1) * h / col->nrows) - (i * h / col->nrows));
    242 		if (recalc || recalcfact)
    243 			row->fact = (double)(row->h - config.titlewidth) / (double)(content);
    244 		row->y = y;
    245 		y += row->h + config.divwidth;
    246 		rowcalctabs(row);
    247 		i++;
    248 	}
    249 }
    250 
    251 /* create new tab */
    252 static struct Tab *
    253 tabnew(Window win, Window leader, int ignoreunmap)
    254 {
    255 	struct Tab *tab;
    256 
    257 	tab = emalloc(sizeof(*tab));
    258 	*tab = (struct Tab){
    259 		.ignoreunmap = ignoreunmap,
    260 		.pix = None,
    261 		.pixtitle = None,
    262 		.title = None,
    263 		.leader = leader,
    264 		.obj.win = win,
    265 		.obj.type = TYPE_NORMAL,
    266 	};
    267 	TAILQ_INIT(&tab->dialq);
    268 	((struct Object *)tab)->type = TYPE_NORMAL;
    269 	tab->frame = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, depth, CopyFromParent, visual, clientmask, &clientswa),
    270 	XReparentWindow(dpy, tab->obj.win, tab->frame, 0, 0);
    271 	XMapWindow(dpy, tab->obj.win);
    272 	icccmwmstate(win, NormalState);
    273 	clientsincr();
    274 	return tab;
    275 }
    276 
    277 /* remove tab from row's tab queue */
    278 static void
    279 tabremove(struct Row *row, struct Tab *tab)
    280 {
    281 	if (row->seltab == tab) {
    282 		row->seltab = (struct Tab *)TAILQ_PREV((struct Object *)tab, Queue, entry);
    283 		if (row->seltab == NULL) {
    284 			row->seltab = (struct Tab *)TAILQ_NEXT((struct Object *)tab, entry);
    285 		}
    286 	}
    287 	row->ntabs--;
    288 	TAILQ_REMOVE(&row->tabq, (struct Object *)tab, entry);
    289 	tab->row = NULL;
    290 }
    291 
    292 /* delete tab */
    293 static void
    294 tabdel(struct Tab *tab)
    295 {
    296 	struct Dialog *dial;
    297 
    298 	while ((dial = (struct Dialog *)TAILQ_FIRST(&tab->dialq)) != NULL) {
    299 		XDestroyWindow(dpy, dial->obj.win);
    300 		unmanagedialog((struct Object *)dial, 0);
    301 	}
    302 	tabremove(tab->row, tab);
    303 	if (tab->pixtitle != None)
    304 		XFreePixmap(dpy, tab->pixtitle);
    305 	if (tab->pix != None)
    306 		XFreePixmap(dpy, tab->pix);
    307 	icccmdeletestate(tab->obj.win);
    308 	XReparentWindow(dpy, tab->obj.win, root, 0, 0);
    309 	XDestroyWindow(dpy, tab->title);
    310 	XDestroyWindow(dpy, tab->frame);
    311 	clientsdecr();
    312 	free(tab->name);
    313 	free(tab);
    314 }
    315 
    316 /* create new row */
    317 struct Row *
    318 rownew(void)
    319 {
    320 	struct Row *row;
    321 
    322 	row = emalloc(sizeof(*row));
    323 	*row = (struct Row){
    324 		.pixbar = None,
    325 		.isunmapped = 0,
    326 	};
    327 	TAILQ_INIT(&row->tabq);
    328 	row->frame = XCreateWindow(dpy, root, 0, 0, 1, 1, 0,
    329 	                           depth, CopyFromParent, visual,
    330 	                           clientmask, &clientswa);
    331 	row->bar = XCreateWindow(dpy, root, 0, 0, 1, 1, 0,
    332 	                         depth, CopyFromParent, visual,
    333 	                         clientmask, &clientswa);
    334 	row->bl = XCreateWindow(dpy, row->bar, 0, 0, config.titlewidth, config.titlewidth, 0,
    335 	                        depth, CopyFromParent, visual,
    336 	                        clientmask, &clientswa);
    337 	row->pixbl = XCreatePixmap(dpy, row->bl, config.titlewidth, config.titlewidth, depth);
    338 	row->br = XCreateWindow(dpy, row->bar, 0, 0, config.titlewidth, config.titlewidth, 0,
    339 	                        depth, CopyFromParent, visual,
    340 	                        clientmask, &clientswa);
    341 	row->div = XCreateWindow(dpy, root, 0, 0, 1, 1, 0,
    342 	                         CopyFromParent, InputOnly, CopyFromParent, CWCursor,
    343 	                         &(XSetWindowAttributes){.cursor = wm.cursors[CURSOR_V]});
    344 	row->pixbr = XCreatePixmap(dpy, row->bl, config.titlewidth, config.titlewidth, depth);
    345 	XMapWindow(dpy, row->bl);
    346 	XMapWindow(dpy, row->br);
    347 	XDefineCursor(dpy, row->bl, wm.cursors[CURSOR_HAND]);
    348 	XDefineCursor(dpy, row->br, wm.cursors[CURSOR_PIRATE]);
    349 	return row;
    350 }
    351 
    352 /* detach row from column */
    353 static void
    354 rowdetach(struct Row *row, int recalc)
    355 {
    356 	struct Column *col;
    357 
    358 	col = row->col;
    359 	if (col->selrow == row) {
    360 		col->selrow = TAILQ_PREV(row, RowQueue, entry);
    361 		if (col->selrow == NULL) {
    362 			col->selrow = TAILQ_NEXT(row, entry);
    363 		}
    364 	}
    365 	col->nrows--;
    366 	TAILQ_REMOVE(&col->rowq, row, entry);
    367 	if (recalc) {
    368 		colcalcrows(row->col, 1);
    369 	}
    370 }
    371 
    372 /* delete row */
    373 static void
    374 rowdel(struct Row *row)
    375 {
    376 	struct Tab *tab;
    377 
    378 	while ((tab = (struct Tab *)TAILQ_FIRST(&row->tabq)) != NULL)
    379 		tabdel(tab);
    380 	rowdetach(row, 1);
    381 	XDestroyWindow(dpy, row->frame);
    382 	XDestroyWindow(dpy, row->bar);
    383 	XDestroyWindow(dpy, row->bl);
    384 	XDestroyWindow(dpy, row->br);
    385 	XDestroyWindow(dpy, row->div);
    386 	if (row->pixbar != None)
    387 		XFreePixmap(dpy, row->pixbar);
    388 	XFreePixmap(dpy, row->pixbl);
    389 	XFreePixmap(dpy, row->pixbr);
    390 	free(row);
    391 }
    392 
    393 /* create new column */
    394 static struct Column *
    395 colnew(void)
    396 {
    397 	struct Column *col;
    398 
    399 	col = emalloc(sizeof(*col));
    400 	*col = (struct Column){ 0 };
    401 	TAILQ_INIT(&col->rowq);
    402 	col->div = XCreateWindow(dpy, root, 0, 0, 1, 1, 0,
    403 	                         CopyFromParent, InputOnly, CopyFromParent, CWCursor,
    404 	                         &(XSetWindowAttributes){.cursor = wm.cursors[CURSOR_H]});
    405 	return col;
    406 }
    407 
    408 /* detach column from container */
    409 static void
    410 coldetach(struct Column *col)
    411 {
    412 	struct Container *c;
    413 
    414 	c = col->c;
    415 	if (c->selcol == col) {
    416 		c->selcol = TAILQ_PREV(col, ColumnQueue, entry);
    417 		if (c->selcol == NULL) {
    418 			c->selcol = TAILQ_NEXT(col, entry);
    419 		}
    420 	}
    421 	c->ncols--;
    422 	TAILQ_REMOVE(&c->colq, col, entry);
    423 	containercalccols(col->c);
    424 }
    425 
    426 /* delete column */
    427 static void
    428 coldel(struct Column *col)
    429 {
    430 	struct Row *row;
    431 
    432 	while ((row = TAILQ_FIRST(&col->rowq)) != NULL)
    433 		rowdel(row);
    434 	coldetach(col);
    435 	XDestroyWindow(dpy, col->div);
    436 	free(col);
    437 }
    438 
    439 /* add row to column */
    440 static void
    441 coladdrow(struct Column *col, struct Row *row, struct Row *prev)
    442 {
    443 	struct Container *c;
    444 	struct Column *oldcol;
    445 
    446 	c = col->c;
    447 	oldcol = row->col;
    448 	row->col = col;
    449 	col->selrow = row;
    450 	col->nrows++;
    451 	if (prev == NULL || TAILQ_EMPTY(&col->rowq))
    452 		TAILQ_INSERT_HEAD(&col->rowq, row, entry);
    453 	else
    454 		TAILQ_INSERT_AFTER(&col->rowq, prev, row, entry);
    455 	colcalcrows(col, 1);    /* set row->y, row->h, etc */
    456 	XReparentWindow(dpy, row->div, c->frame, col->x + col->w, c->b);
    457 	XReparentWindow(dpy, row->bar, c->frame, col->x, row->y);
    458 	XReparentWindow(dpy, row->frame, c->frame, col->x, row->y);
    459 	XMapWindow(dpy, row->bar);
    460 	XMapWindow(dpy, row->frame);
    461 	if (oldcol != NULL && oldcol->nrows == 0) {
    462 		coldel(oldcol);
    463 	}
    464 }
    465 
    466 /* add tab to row */
    467 static void
    468 rowaddtab(struct Row *row, struct Tab *tab, struct Tab *prev)
    469 {
    470 	struct Row *oldrow;
    471 
    472 	oldrow = tab->row;
    473 	tab->row = row;
    474 	row->seltab = tab;
    475 	row->ntabs++;
    476 	if (prev == NULL || TAILQ_EMPTY(&row->tabq))
    477 		TAILQ_INSERT_HEAD(&row->tabq, (struct Object *)tab, entry);
    478 	else
    479 		TAILQ_INSERT_AFTER(&row->tabq, (struct Object *)prev, (struct Object *)tab, entry);
    480 	rowcalctabs(row);               /* set tab->x, tab->w, etc */
    481 	if (tab->title == None) {
    482 		tab->title = XCreateWindow(dpy, row->bar, tab->x, 0, tab->w, config.titlewidth, 0,
    483 		                         depth, CopyFromParent, visual,
    484 		                         clientmask, &clientswa);
    485 	} else {
    486 		XReparentWindow(dpy, tab->title, row->bar, tab->x, 0);
    487 	}
    488 	XReparentWindow(dpy, tab->frame, row->frame, 0, 0);
    489 	XMapWindow(dpy, tab->frame);
    490 	XMapWindow(dpy, tab->title);
    491 	if (oldrow != NULL) {           /* deal with the row this tab came from */
    492 		if (oldrow->ntabs == 0) {
    493 			rowdel(oldrow);
    494 		} else {
    495 			rowcalctabs(oldrow);
    496 		}
    497 	}
    498 }
    499 
    500 /* decorate dialog window */
    501 static void
    502 dialogdecorate(struct Dialog *d)
    503 {
    504 	int fullw, fullh;       /* size of dialog window + borders */
    505 
    506 	fullw = d->w + 2 * config.borderwidth;
    507 	fullh = d->h + 2 * config.borderwidth;
    508 
    509 	/* (re)create pixmap */
    510 	if (d->pw != fullw || d->ph != fullh || d->pix == None)
    511 		pixmapnew(&d->pix, d->frame, fullw, fullh);
    512 	d->pw = fullw;
    513 	d->ph = fullh;
    514 
    515 	drawborders(d->pix, fullw, fullh, tabgetstyle(d->tab));
    516 	drawcommit(d->pix, d->frame);
    517 }
    518 
    519 /* get focused fullscreen window in given monitor and desktop */
    520 static struct Container *
    521 getfullscreen(struct Monitor *mon, int desk)
    522 {
    523 	struct Container *c;
    524 
    525 	for (c = &wm.layers[LAYER_FULLSCREEN]; !ISDUMMY(c); c = TAILQ_NEXT(c, raiseentry))
    526 		if (containerisvisible(c, mon, desk))
    527 			return c;
    528 	return NULL;
    529 }
    530 
    531 /* add container into head of focus queue */
    532 static void
    533 containerinsertfocus(struct Container *c)
    534 {
    535 	TAILQ_INSERT_HEAD(&wm.focusq, c, entry);
    536 }
    537 
    538 /* add container into head of focus queue */
    539 static void
    540 containerinsertraise(struct Container *c)
    541 {
    542 	int layer;
    543 
    544 	layer = LAYER_NORMAL;
    545 	if (c->isfullscreen)
    546 		layer = LAYER_FULLSCREEN;
    547 	else if (c->abovebelow > 0)
    548 		layer = LAYER_ABOVE;
    549 	else if (c->abovebelow < 0)
    550 		layer = LAYER_BELOW;
    551 	TAILQ_INSERT_AFTER(&wm.stackq, &wm.layers[layer], c, raiseentry);
    552 }
    553 
    554 /* remove container from the focus list */
    555 static void
    556 containerdelfocus(struct Container *c)
    557 {
    558 	TAILQ_REMOVE(&wm.focusq, c, entry);
    559 }
    560 
    561 /* put container on beginning of focus list */
    562 static void
    563 containeraddfocus(struct Container *c)
    564 {
    565 	if (c == NULL || c->isminimized)
    566 		return;
    567 	containerdelfocus(c);
    568 	containerinsertfocus(c);
    569 }
    570 
    571 /* remove container from the raise list */
    572 static void
    573 containerdelraise(struct Container *c)
    574 {
    575 	TAILQ_REMOVE(&wm.stackq, c, raiseentry);
    576 }
    577 
    578 /* hide container */
    579 void
    580 containerhide(struct Container *c, int hide)
    581 {
    582 	struct Object *t, *d;
    583 
    584 	if (c == NULL)
    585 		return;
    586 	c->ishidden = hide;
    587 	if (hide) {
    588 		XUnmapWindow(dpy, c->frame);
    589 	} else {
    590 		XMapWindow(dpy, c->frame);
    591 	}
    592 	TAB_FOREACH_BEGIN(c, t) {
    593 		icccmwmstate(t->win, (hide ? IconicState : NormalState));
    594 		TAILQ_FOREACH(d, &((struct Tab *)t)->dialq, entry) {
    595 			icccmwmstate(d->win, (hide ? IconicState : NormalState));
    596 		}
    597 	}TAB_FOREACH_END
    598 }
    599 
    600 /* add column to container */
    601 static void
    602 containeraddcol(struct Container *c, struct Column *col, struct Column *prev)
    603 {
    604 	struct Container *oldc;
    605 
    606 	oldc = col->c;
    607 	col->c = c;
    608 	c->selcol = col;
    609 	c->ncols++;
    610 	if (prev == NULL || TAILQ_EMPTY(&c->colq))
    611 		TAILQ_INSERT_HEAD(&c->colq, col, entry);
    612 	else
    613 		TAILQ_INSERT_AFTER(&c->colq, prev, col, entry);
    614 	XReparentWindow(dpy, col->div, c->frame, 0, 0);
    615 	containercalccols(c);
    616 	if (oldc != NULL && oldc->ncols == 0) {
    617 		containerdel(oldc);
    618 	}
    619 }
    620 
    621 /* send container to desktop and raise it; return nonzero if it was actually sent anywhere */
    622 static int
    623 containersendtodesk(struct Container *c, struct Monitor *mon, unsigned long desk)
    624 {
    625 	void containerstick(struct Container *c, int stick);
    626 
    627 	if (c == NULL || c->isminimized)
    628 		return 0;
    629 	if (desk == 0xFFFFFFFF) {
    630 		containerstick(c, ADD);
    631 	} else if ((int)desk < config.ndesktops) {
    632 		c->desk = (int)desk;
    633 		if (c->mon != mon)
    634 			containerplace(c, mon, desk, 1);
    635 		c->mon = mon;
    636 		c->issticky = 0;
    637 		if ((int)desk != mon->seldesk)  /* container was sent to invisible desktop */
    638 			containerhide(c, 1);
    639 		else
    640 			containerhide(c, 0);
    641 		containerraise(c, c->isfullscreen, c->abovebelow);
    642 	} else {
    643 		return 0;
    644 	}
    645 	ewmhsetwmdesktop(c);
    646 	ewmhsetstate(c);
    647 	return 1;
    648 }
    649 
    650 /* make a container occupy the whole monitor */
    651 static void
    652 containerfullscreen(struct Container *c, int fullscreen)
    653 {
    654 	if (fullscreen != REMOVE && !c->isfullscreen)
    655 		containerraise(c, 1, c->abovebelow);
    656 	else if (fullscreen != ADD && c->isfullscreen)
    657 		containerraise(c, 0, c->abovebelow);
    658 	else
    659 		return;
    660 	containercalccols(c);
    661 	containermoveresize(c, 1);
    662 	containerredecorate(c, NULL, NULL, 0);
    663 	ewmhsetstate(c);
    664 }
    665 
    666 /* maximize a container on the monitor */
    667 static void
    668 containermaximize(struct Container *c, int maximize)
    669 {
    670 	if (maximize != REMOVE && !c->ismaximized)
    671 		c->ismaximized = 1;
    672 	else if (maximize != ADD && c->ismaximized)
    673 		c->ismaximized = 0;
    674 	else
    675 		return;
    676 	containercalccols(c);
    677 	containermoveresize(c, 1);
    678 	containerredecorate(c, NULL, NULL, 0);
    679 }
    680 
    681 /* minimize container; optionally focus another container */
    682 static void
    683 containerminimize(struct Container *c, int minimize, int focus)
    684 {
    685 	struct Container *tofocus;
    686 
    687 	if (minimize != REMOVE && !c->isminimized) {
    688 		c->isminimized = 1;
    689 		containerhide(c, 1);
    690 		if (focus) {
    691 			if ((tofocus = getnextfocused(c->mon, c->desk)) != NULL) {
    692 				tabfocus(tofocus->selcol->selrow->seltab, 0);
    693 				containerraise(c, c->isfullscreen, c->abovebelow);
    694 			} else {
    695 				tabfocus(NULL, 0);
    696 			}
    697 		}
    698 	} else if (minimize != ADD && c->isminimized) {
    699 		(void)containersendtodesk(c, wm.selmon, wm.selmon->seldesk);
    700 		containermoveresize(c, 1);
    701 		tabfocus(c->selcol->selrow->seltab, 0);
    702 		containerraise(c, c->isfullscreen, c->abovebelow);
    703 	}
    704 }
    705 
    706 /* shade container title bar */
    707 static void
    708 containershade(struct Container *c, int shade)
    709 {
    710 	if (shade != REMOVE && !c->isshaded) {
    711 		c->isshaded = 1;
    712 		XDefineCursor(dpy, c->curswin[BORDER_NW], wm.cursors[CURSOR_W]);
    713 		XDefineCursor(dpy, c->curswin[BORDER_SW], wm.cursors[CURSOR_W]);
    714 		XDefineCursor(dpy, c->curswin[BORDER_NE], wm.cursors[CURSOR_E]);
    715 		XDefineCursor(dpy, c->curswin[BORDER_SE], wm.cursors[CURSOR_E]);
    716 	} else if (shade != ADD && c->isshaded) {
    717 		c->isshaded = 0;
    718 		XDefineCursor(dpy, c->curswin[BORDER_NW], wm.cursors[CURSOR_NW]);
    719 		XDefineCursor(dpy, c->curswin[BORDER_SW], wm.cursors[CURSOR_SW]);
    720 		XDefineCursor(dpy, c->curswin[BORDER_NE], wm.cursors[CURSOR_NE]);
    721 		XDefineCursor(dpy, c->curswin[BORDER_SE], wm.cursors[CURSOR_SE]);
    722 	} else {
    723 		return;
    724 	}
    725 	containercalccols(c);
    726 	containermoveresize(c, 1);
    727 	containerredecorate(c, NULL, NULL, 0);
    728 	if (c == wm.focused) {
    729 		tabfocus(c->selcol->selrow->seltab, 0);
    730 	}
    731 }
    732 
    733 /* stick a container on the monitor */
    734 void
    735 containerstick(struct Container *c, int stick)
    736 {
    737 	if (stick != REMOVE && !c->issticky) {
    738 		c->issticky = 1;
    739 		ewmhsetwmdesktop(c);
    740 	} else if (stick != ADD && c->issticky) {
    741 		c->issticky = 0;
    742 		(void)containersendtodesk(c, c->mon, c->mon->seldesk);
    743 	} else {
    744 		return;
    745 	}
    746 }
    747 
    748 /* raise container above others */
    749 static void
    750 containerabove(struct Container *c, int above)
    751 {
    752 	if (above != REMOVE && c->abovebelow != 1)
    753 		containerraise(c, c->isfullscreen, 1);
    754 	else if (above != ADD && c->abovebelow != 0)
    755 		containerraise(c, c->isfullscreen, 0);
    756 	else
    757 		return;
    758 }
    759 
    760 /* lower container below others */
    761 static void
    762 containerbelow(struct Container *c, int below)
    763 {
    764 	if (below != REMOVE && c->abovebelow != -1)
    765 		containerraise(c, c->isfullscreen, -1);
    766 	else if (below != ADD && c->abovebelow != 0)
    767 		containerraise(c, c->isfullscreen, 0);
    768 	else
    769 		return;
    770 }
    771 
    772 /* create new container */
    773 struct Container *
    774 containernew(int x, int y, int w, int h, int state)
    775 {
    776 	struct Container *c;
    777 	int i;
    778 
    779 	x -= config.borderwidth,
    780 	y -= config.borderwidth,
    781 	w += 2 * config.borderwidth,
    782 	h += 2 * config.borderwidth + config.titlewidth,
    783 	c = emalloc(sizeof *c);
    784 	*c = (struct Container) {
    785 		.x  = x, .y  = y, .w  = w, .h  = h,
    786 		.nx = x, .ny = y, .nw = w, .nh = h,
    787 		.b = config.borderwidth,
    788 		.pix = None,
    789 		.isfullscreen = (state & FULLSCREEN),
    790 		.ismaximized = (state & MAXIMIZED),
    791 		.isminimized = (state & MINIMIZED),
    792 		.issticky = (state & STICKY),
    793 		.isshaded = (state & SHADED),
    794 		.ishidden = 0,
    795 		.isobscured = 0,
    796 		.abovebelow = (state & ABOVE) ? +1 : (state & BELOW) ? -1 : 0,
    797 	};
    798 	TAILQ_INIT(&c->colq);
    799 	c->frame = XCreateWindow(dpy, root, c->x, c->y, c->w, c->h, 0, depth, InputOutput, visual, clientmask, &clientswa);
    800 	c->curswin[BORDER_N] = XCreateWindow(
    801 		dpy, c->frame, 0, 0, 1, 1, 0,
    802 		CopyFromParent, InputOnly, CopyFromParent,
    803 		CWCursor,
    804 		&(XSetWindowAttributes){
    805 			.cursor = wm.cursors[CURSOR_N],
    806 		}
    807 	);
    808 	c->curswin[BORDER_S] = XCreateWindow(
    809 		dpy, c->frame, 0, 0, 1, 1, 0,
    810 		CopyFromParent, InputOnly, CopyFromParent,
    811 		CWCursor,
    812 		&(XSetWindowAttributes){
    813 			.cursor = wm.cursors[CURSOR_S],
    814 		}
    815 	);
    816 	c->curswin[BORDER_W] = XCreateWindow(
    817 		dpy, c->frame, 0, 0, 1, 1, 0,
    818 		CopyFromParent, InputOnly, CopyFromParent,
    819 		CWCursor,
    820 		&(XSetWindowAttributes){
    821 			.cursor = wm.cursors[CURSOR_W],
    822 		}
    823 	);
    824 	c->curswin[BORDER_E] = XCreateWindow(
    825 		dpy, c->frame, 0, 0, 1, 1, 0,
    826 		CopyFromParent, InputOnly, CopyFromParent,
    827 		CWCursor,
    828 		&(XSetWindowAttributes){
    829 			.cursor = wm.cursors[CURSOR_E],
    830 		}
    831 	);
    832 	c->curswin[BORDER_NW] = XCreateWindow(
    833 		dpy, c->frame, 0, 0, 1, 1, 0,
    834 		CopyFromParent, InputOnly, CopyFromParent,
    835 		CWCursor,
    836 		&(XSetWindowAttributes){
    837 			.cursor = c->isshaded ? wm.cursors[CURSOR_W] : wm.cursors[CURSOR_NW],
    838 		}
    839 	);
    840 	c->curswin[BORDER_SW] = XCreateWindow(
    841 		dpy, c->frame, 0, 0, 1, 1, 0,
    842 		CopyFromParent, InputOnly, CopyFromParent,
    843 		CWCursor,
    844 		&(XSetWindowAttributes){
    845 			.cursor = c->isshaded ? wm.cursors[CURSOR_W] : wm.cursors[CURSOR_SW],
    846 		}
    847 	);
    848 	c->curswin[BORDER_NE] = XCreateWindow(
    849 		dpy, c->frame, 0, 0, 1, 1, 0,
    850 		CopyFromParent, InputOnly, CopyFromParent,
    851 		CWCursor,
    852 		&(XSetWindowAttributes){
    853 			.cursor = c->isshaded ? wm.cursors[CURSOR_E] : wm.cursors[CURSOR_NE],
    854 		}
    855 	);
    856 	c->curswin[BORDER_SE] = XCreateWindow(
    857 		dpy, c->frame, 0, 0, 1, 1, 0,
    858 		CopyFromParent, InputOnly, CopyFromParent,
    859 		CWCursor,
    860 		&(XSetWindowAttributes){
    861 			.cursor = c->isshaded ? wm.cursors[CURSOR_E] : wm.cursors[CURSOR_SE],
    862 		}
    863 	);
    864 	for (i = 0; i < BORDER_LAST; i++)
    865 		XMapWindow(dpy, c->curswin[i]);
    866 	containerinsertfocus(c);
    867 	containerinsertraise(c);
    868 	return c;
    869 }
    870 
    871 /* delete container */
    872 void
    873 containerdel(struct Container *c)
    874 {
    875 	struct Column *col;
    876 	int i;
    877 
    878 	containerdelfocus(c);
    879 	containerdelraise(c);
    880 	if (wm.focused == c)
    881 		wm.focused = NULL;
    882 	TAILQ_REMOVE(&wm.focusq, c, entry);
    883 	while ((col = TAILQ_FIRST(&c->colq)) != NULL)
    884 		coldel(col);
    885 	if (c->pix != None)
    886 		XFreePixmap(dpy, c->pix);
    887 	XDestroyWindow(dpy, c->frame);
    888 	for (i = 0; i < BORDER_LAST; i++)
    889 		XDestroyWindow(dpy, c->curswin[i]);
    890 	free(c);
    891 }
    892 
    893 /* commit container size and position */
    894 void
    895 containermoveresize(struct Container *c, int checkstack)
    896 {
    897 	struct Object *t, *d;
    898 	struct Column *col;
    899 	struct Row *row;
    900 	struct Tab *tab;
    901 	struct Dialog *dial;
    902 	int rowy;
    903 	int isshaded;
    904 
    905 	if (c == NULL)
    906 		return;
    907 	XMoveResizeWindow(dpy, c->frame, c->x, c->y, c->w, c->h);
    908 	XMoveResizeWindow(dpy, c->curswin[BORDER_N], config.corner, 0, c->w - 2 * config.corner, c->b);
    909 	XMoveResizeWindow(dpy, c->curswin[BORDER_S], config.corner, c->h - c->b, c->w - 2 * config.corner, c->b);
    910 	XMoveResizeWindow(dpy, c->curswin[BORDER_W], 0, config.corner, c->b, c->h - 2 * config.corner);
    911 	XMoveResizeWindow(dpy, c->curswin[BORDER_E], c->w - c->b, config.corner, c->b, c->h - 2 * config.corner);
    912 	XMoveResizeWindow(dpy, c->curswin[BORDER_NW], 0, 0, config.corner, config.corner);
    913 	XMoveResizeWindow(dpy, c->curswin[BORDER_NE], c->w - config.corner, 0, config.corner, config.corner);
    914 	XMoveResizeWindow(dpy, c->curswin[BORDER_SW], 0, c->h - config.corner, config.corner, config.corner);
    915 	XMoveResizeWindow(dpy, c->curswin[BORDER_SE], c->w - config.corner, c->h - config.corner, config.corner, config.corner);
    916 	isshaded = containerisshaded(c);
    917 	TAILQ_FOREACH(col, &c->colq, entry) {
    918 		rowy = c->b;
    919 		if (TAILQ_NEXT(col, entry) != NULL) {
    920 			XMoveResizeWindow(dpy, col->div, col->x + col->w, c->b, config.divwidth, c->h - 2 * c->b);
    921 			XMapWindow(dpy, col->div);
    922 		} else {
    923 			XUnmapWindow(dpy, col->div);
    924 		}
    925 		TAILQ_FOREACH(row, &col->rowq, entry) {
    926 			if (!isshaded) {
    927 				if (TAILQ_NEXT(row, entry) != NULL) {
    928 					XMoveResizeWindow(dpy, row->div, col->x, row->y + row->h, col->w, config.divwidth);
    929 					XMapWindow(dpy, row->div);
    930 				}
    931 				titlebarmoveresize(row, col->x, row->y, col->w);
    932 				if (row->h - config.titlewidth > 0) {
    933 					XMoveResizeWindow(dpy, row->frame, col->x, row->y + config.titlewidth, col->w, row->h - config.titlewidth);
    934 					XMapWindow(dpy, row->frame);
    935 					row->isunmapped = 0;
    936 				} else {
    937 					XUnmapWindow(dpy, row->frame);
    938 					row->isunmapped = 1;
    939 				}
    940 			} else {
    941 				titlebarmoveresize(row, col->x, rowy, col->w);
    942 				XUnmapWindow(dpy, row->frame);
    943 				XUnmapWindow(dpy, row->div);
    944 				row->isunmapped = 1;
    945 			}
    946 			rowy += config.titlewidth;
    947 			TAILQ_FOREACH(t, &row->tabq, entry) {
    948 				tab = (struct Tab *)t;
    949 				XMoveResizeWindow(dpy, tab->frame, 0, 0, tab->winw, tab->winh);
    950 				TAILQ_FOREACH(d, &tab->dialq, entry) {
    951 					dial = (struct Dialog *)d;
    952 					dialogmoveresize(dial);
    953 					ewmhsetframeextents(dial->obj.win, c->b, 0);
    954 				}
    955 				XResizeWindow(dpy, tab->obj.win, tab->winw, tab->winh);
    956 				ewmhsetframeextents(tab->obj.win, c->b, TITLEWIDTH(c));
    957 				tabmoveresize(tab);
    958 			}
    959 		}
    960 	}
    961 	if (!config.disablehidden && checkstack) {
    962 		wm.setclientlist = 1;
    963 	}
    964 }
    965 
    966 /* draw decoration on container frame */
    967 void
    968 containerdecorate(struct Container *c, struct Column *cdiv, struct Row *rdiv, int recursive, enum Octant o)
    969 {
    970 	struct Column *col;
    971 	struct Row *row;
    972 	struct Object *t, *d;
    973 	int style;
    974 	int isshaded;
    975 
    976 	if (c == NULL)
    977 		return;
    978 	isshaded = containerisshaded(c);
    979 	style = containergetstyle(c);
    980 
    981 	/* (re)create pixmap */
    982 	if (c->pw != c->w || c->ph != c->h || c->pix == None)
    983 		pixmapnew(&c->pix, c->frame, c->w, c->h);
    984 	c->pw = c->w;
    985 	c->ph = c->h;
    986 
    987 	/* draw background */
    988 	drawbackground(c->pix, 0, 0, c->w, c->h, style);
    989 
    990 	if (c->b > 0)
    991 		drawframe(c->pix, isshaded, c->w, c->h, o, style);
    992 
    993 	TAILQ_FOREACH(col, &c->colq, entry) {
    994 		/* draw column division */
    995 		if (TAILQ_NEXT(col, entry) != NULL)
    996 			drawshadow(c->pix, col->x + col->w, c->b, config.divwidth, c->h - 2 * c->b, style, col == cdiv);
    997 		TAILQ_FOREACH(row, &col->rowq, entry) {
    998 			/* draw row division */
    999 			if (TAILQ_NEXT(row, entry) != NULL)
   1000 				drawshadow(c->pix, col->x, row->y + row->h, col->w, config.divwidth, style, row == rdiv);
   1001 
   1002 			/* (re)create titlebar pixmap */
   1003 			if (row->pw != col->w || row->pixbar == None)
   1004 				pixmapnew(&row->pixbar, row->bar, col->w, config.titlewidth);
   1005 			row->pw = col->w;
   1006 
   1007 			/* draw background of titlebar pixmap */
   1008 			drawbackground(row->pixbar, 0, 0, col->w, config.titlewidth, style);
   1009 			drawcommit(row->pixbar, row->bar);
   1010 
   1011 			/* draw buttons */
   1012 			buttonleftdecorate(row->bl, row->pixbl, style, 0);
   1013 			buttonrightdecorate(row->br, row->pixbr, style, 0);
   1014 
   1015 			/* decorate tabs, if necessary */
   1016 			if (recursive) {
   1017 				TAILQ_FOREACH(t, &row->tabq, entry) {
   1018 					tabdecorate((struct Tab *)t, 0);
   1019 					TAILQ_FOREACH(d, &((struct Tab *)t)->dialq, entry) {
   1020 						dialogdecorate((struct Dialog *)d);
   1021 					}
   1022 				}
   1023 			}
   1024 		}
   1025 	}
   1026 
   1027 	drawcommit(c->pix, c->frame);
   1028 }
   1029 
   1030 /* check if container needs to be redecorated and redecorate it */
   1031 void
   1032 containerredecorate(struct Container *c, struct Column *cdiv, struct Row *rdiv, enum Octant o)
   1033 {
   1034 	if (c->pw != c->w || c->ph != c->h) {
   1035 		containerdecorate(c, cdiv, rdiv, 0, o);
   1036 	}
   1037 }
   1038 
   1039 /* calculate position and width of columns of a container */
   1040 void
   1041 containercalccols(struct Container *c)
   1042 {
   1043 	struct Column *col;
   1044 	int i, x, w;
   1045 	int sumw;
   1046 	int content;
   1047 	int recalc;
   1048 
   1049 	if (c->isfullscreen) {
   1050 		c->x = c->mon->mx;
   1051 		c->y = c->mon->my;
   1052 		c->w = c->mon->mw;
   1053 		c->h = c->mon->mh;
   1054 		c->b = 0;
   1055 		TAILQ_FOREACH(col, &c->colq, entry) {
   1056 			col->x = 0;
   1057 			col->w = c->w;
   1058 			colcalcrows(col, 0);
   1059 		}
   1060 		return;
   1061 	} else if (c->ismaximized) {
   1062 		c->x = c->mon->wx;
   1063 		c->y = c->mon->wy;
   1064 		c->w = c->mon->ww;
   1065 		c->h = c->mon->wh;
   1066 		c->b = config.borderwidth;
   1067 	} else {
   1068 		c->x = c->nx;
   1069 		c->y = c->ny;
   1070 		c->w = c->nw;
   1071 		c->h = c->nh;
   1072 		c->b = config.borderwidth;
   1073 	}
   1074 	if (containerisshaded(c)) {
   1075 		c->h = 0;
   1076 	}
   1077 
   1078 	/* check if columns sum up the width of the container */
   1079 	content = containercontentwidth(c);
   1080 	sumw = 0;
   1081 	recalc = 0;
   1082 	TAILQ_FOREACH(col, &c->colq, entry) {
   1083 		if (TAILQ_NEXT(col, entry) == NULL) {
   1084 			col->w = content - sumw;
   1085 		} else {
   1086 			col->w = col->fact * content;
   1087 		}
   1088 		if (col->w == 0) {
   1089 			recalc = 1;
   1090 		}
   1091 		sumw += col->w;
   1092 	}
   1093 	if (sumw != content)
   1094 		recalc = 1;
   1095 
   1096 	w = c->w - 2 * c->b - (c->ncols - 1) * config.divwidth;
   1097 	x = c->b;
   1098 	i = 0;
   1099 	TAILQ_FOREACH(col, &c->colq, entry) {
   1100 		if (containerisshaded(c))
   1101 			c->h = max(c->h, col->nrows * config.titlewidth);
   1102 		if (recalc)
   1103 			col->w = max(1, ((i + 1) * w / c->ncols) - (i * w / c->ncols));
   1104 		if (recalc)
   1105 			col->fact = (double)col->w/(double)c->w;
   1106 		col->x = x;
   1107 		x += col->w + config.divwidth;
   1108 		colcalcrows(col, 0);
   1109 		i++;
   1110 	}
   1111 	if (containerisshaded(c)) {
   1112 		c->h += 2 * c->b;
   1113 	}
   1114 }
   1115 
   1116 /* send container to desktop and focus another on the original desktop */
   1117 void
   1118 containersendtodeskandfocus(struct Container *c, struct Monitor *mon, unsigned long d)
   1119 {
   1120 	struct Monitor *prevmon;
   1121 	int prevdesk, desk;
   1122 
   1123 	if (c == NULL)
   1124 		return;
   1125 	prevmon = c->mon;
   1126 	prevdesk = c->desk;
   1127 	desk = d;
   1128 
   1129 	/* is it necessary to send the container to the desktop */
   1130 	if (c->mon != mon || c->desk != desk) {
   1131 		/*
   1132 		 * Container sent to a desktop which is not the same
   1133 		 * as the one it was originally at.
   1134 		 */
   1135 		if (!containersendtodesk(c, mon, d)) {
   1136 			/*
   1137 			 * container could not be sent to given desktop;
   1138 			 */
   1139 			return;
   1140 		}
   1141 	}
   1142 
   1143 	/* is it necessary to focus something? */
   1144 	if (mon == wm.selmon && desk == wm.selmon->seldesk) {
   1145 		/*
   1146 		 * Container sent to the focused desktop.
   1147 		 * Focus it, if visible.
   1148 		 */
   1149 		if (containerisvisible(c, mon, wm.selmon->seldesk)) {
   1150 			tabfocus(c->selcol->selrow->seltab, 0);
   1151 		}
   1152 	} else if (prevmon == wm.selmon && prevdesk == wm.selmon->seldesk) {
   1153 		/*
   1154 		 * Container moved from the focused desktop.
   1155 		 * Focus the next visible container, if existing;
   1156 		 * or nothing, if there's no visible container.
   1157 		 */
   1158 		if ((c = getnextfocused(mon, prevdesk)) != NULL) {
   1159 			tabfocus(c->selcol->selrow->seltab, 0);
   1160 		} else {
   1161 			tabfocus(NULL, 0);
   1162 		}
   1163 	}
   1164 }
   1165 
   1166 /* move container x pixels to the right and y pixels down */
   1167 void
   1168 containermove(struct Container *c, int x, int y, int relative)
   1169 {
   1170 	struct Monitor *monto;
   1171 	struct Object *t;
   1172 	struct Tab *tab;
   1173 
   1174 	if (c == NULL || c->isminimized || c->ismaximized || c->isfullscreen)
   1175 		return;
   1176 	if (relative) {
   1177 		c->nx += x;
   1178 		c->ny += y;
   1179 	} else {
   1180 		c->nx = x;
   1181 		c->ny = y;
   1182 	}
   1183 	c->x = c->nx;
   1184 	c->y = c->ny;
   1185 	snaptoedge(&c->x, &c->y, c->w, c->h);
   1186 	XMoveWindow(dpy, c->frame, c->x, c->y);
   1187 	TAB_FOREACH_BEGIN(c, t){
   1188 		tab = (struct Tab *)t;
   1189 		winnotify(tab->obj.win, c->x + col->x, c->y + row->y + config.titlewidth, tab->winw, tab->winh);
   1190 	}TAB_FOREACH_END
   1191 	if (!c->issticky) {
   1192 		monto = getmon(c->nx + c->nw / 2, c->ny + c->nh / 2);
   1193 		if (monto != NULL && monto != c->mon) {
   1194 			c->mon = monto;
   1195 			if (wm.focused == c) {
   1196 				deskupdate(monto, monto->seldesk);
   1197 			}
   1198 		}
   1199 	}
   1200 }
   1201 
   1202 /* raise container */
   1203 void
   1204 containerraise(struct Container *c, int isfullscreen, int abovebelow)
   1205 {
   1206 	struct Tab *tab;
   1207 	struct Menu *menu;
   1208 	struct Object *obj;
   1209 	Window wins[2];
   1210 	int layer;
   1211 
   1212 	if (c == NULL || c->isminimized)
   1213 		return;
   1214 	containerdelraise(c);
   1215 	wins[1] = c->frame;
   1216 	layer = LAYER_NORMAL;
   1217 	if (isfullscreen)
   1218 		layer = LAYER_FULLSCREEN;
   1219 	else if (abovebelow > 0)
   1220 		layer = LAYER_ABOVE;
   1221 	else if (abovebelow < 0)
   1222 		layer = LAYER_BELOW;
   1223 	TAILQ_INSERT_AFTER(&wm.stackq, &wm.layers[layer], c, raiseentry);
   1224 	wins[0] = wm.layers[layer].frame;
   1225 	c->isfullscreen = isfullscreen;
   1226 	c->abovebelow = abovebelow;
   1227 	XRestackWindows(dpy, wins, 2);
   1228 	tab = c->selcol->selrow->seltab;
   1229 
   1230 	/* raise any menu for the container */
   1231 	wins[0] = wm.layers[LAYER_MENU].frame;
   1232 	TAILQ_FOREACH(obj, &wm.menuq, entry) {
   1233 		menu = ((struct Menu *)obj);
   1234 		if (!istabformenu(tab, menu))
   1235 			continue;
   1236 		menu = (struct Menu *)obj;
   1237 		wins[1] = menu->frame;
   1238 		XRestackWindows(dpy, wins, 2);
   1239 		wins[0] = menu->frame;
   1240 	}
   1241 	wm.setclientlist = 1;
   1242 }
   1243 
   1244 /* configure container size and position */
   1245 void
   1246 containerconfigure(struct Container *c, unsigned int valuemask, XWindowChanges *wc)
   1247 {
   1248 	if (c == NULL || c->isminimized || c->isfullscreen || c->ismaximized)
   1249 		return;
   1250 	if (valuemask & CWX)
   1251 		c->nx = wc->x;
   1252 	if (valuemask & CWY)
   1253 		c->ny = wc->y;
   1254 	if ((valuemask & CWWidth) && wc->width >= wm.minsize)
   1255 		c->nw = wc->width;
   1256 	if ((valuemask & CWHeight) && wc->height >= wm.minsize)
   1257 		c->nh = wc->height;
   1258 	containercalccols(c);
   1259 	containermoveresize(c, 1);
   1260 	containerredecorate(c, NULL, NULL, 0);
   1261 }
   1262 
   1263 /* set container state from client message */
   1264 void
   1265 containersetstate(struct Tab *tab, Atom *props, unsigned long set)
   1266 {
   1267 	struct Container *c;
   1268 
   1269 	if (tab == NULL)
   1270 		return;
   1271 	c = tab->row->col->c;
   1272 	if (props[0] == atoms[_NET_WM_STATE_MAXIMIZED_VERT] ||
   1273 	    props[0] == atoms[_NET_WM_STATE_MAXIMIZED_HORZ] ||
   1274 	    props[1] == atoms[_NET_WM_STATE_MAXIMIZED_VERT] ||
   1275 	    props[1] == atoms[_NET_WM_STATE_MAXIMIZED_HORZ])
   1276 		containermaximize(c, set);
   1277 	if (props[0] == atoms[_NET_WM_STATE_FULLSCREEN] ||
   1278 	    props[1] == atoms[_NET_WM_STATE_FULLSCREEN])
   1279 		containerfullscreen(c, set);
   1280 	if (props[0] == atoms[_NET_WM_STATE_SHADED] ||
   1281 	    props[1] == atoms[_NET_WM_STATE_SHADED])
   1282 		containershade(c, set);
   1283 	if (props[0] == atoms[_NET_WM_STATE_STICKY] ||
   1284 	    props[1] == atoms[_NET_WM_STATE_STICKY])
   1285 		containerstick(c, set);
   1286 	if (props[0] == atoms[_NET_WM_STATE_HIDDEN] ||
   1287 	    props[1] == atoms[_NET_WM_STATE_HIDDEN])
   1288 		containerminimize(c, set, (c == wm.focused));
   1289 	if (props[0] == atoms[_NET_WM_STATE_ABOVE] ||
   1290 	    props[1] == atoms[_NET_WM_STATE_ABOVE])
   1291 		containerabove(c, set);
   1292 	if (props[0] == atoms[_NET_WM_STATE_BELOW] ||
   1293 	    props[1] == atoms[_NET_WM_STATE_BELOW])
   1294 		containerbelow(c, set);
   1295 	if (props[0] == atoms[_NET_WM_STATE_DEMANDS_ATTENTION] ||
   1296 	    props[1] == atoms[_NET_WM_STATE_DEMANDS_ATTENTION])
   1297 		tabupdateurgency(tab, set == ADD || (set == TOGGLE && !tab->isurgent));
   1298 	if (props[0] == atoms[_SHOD_WM_STATE_STRETCHED] ||
   1299 	    props[1] == atoms[_SHOD_WM_STATE_STRETCHED]) {
   1300 		rowstretch(tab->row->col, tab->row);
   1301 		tabfocus(tab->row->seltab, 0);
   1302 	}
   1303 	ewmhsetstate(c);
   1304 }
   1305 
   1306 /* fill placement grid for given rectangle */
   1307 static void
   1308 fillgrid(struct Monitor *mon, int x, int y, int w, int h, int grid[DIV][DIV])
   1309 {
   1310 	int i, j;
   1311 	int ha, hb, wa, wb;
   1312 	int ya, yb, xa, xb;
   1313 
   1314 	for (i = 0; i < DIV; i++) {
   1315 		for (j = 0; j < DIV; j++) {
   1316 			ha = mon->wy + (mon->wh * i)/DIV;
   1317 			hb = mon->wy + (mon->wh * (i + 1))/DIV;
   1318 			wa = mon->wx + (mon->ww * j)/DIV;
   1319 			wb = mon->wx + (mon->ww * (j + 1))/DIV;
   1320 			ya = y;
   1321 			yb = y + h;
   1322 			xa = x;
   1323 			xb = x + w;
   1324 			if (ya <= hb && ha <= yb && xa <= wb && wa <= xb) {
   1325 				if (ya < ha && yb > hb)
   1326 					grid[i][j]++;
   1327 				if (xa < wa && xb > wb)
   1328 					grid[i][j]++;
   1329 				grid[i][j]++;
   1330 			}
   1331 		}
   1332 	}
   1333 }
   1334 
   1335 /* find best position to place a container on screen */
   1336 void
   1337 containerplace(struct Container *c, struct Monitor *mon, int desk, int userplaced)
   1338 {
   1339 	struct Container *tmp;
   1340 	struct Object *obj;
   1341 	struct Menu *menu;
   1342 	int grid[DIV][DIV] = {{0}, {0}};
   1343 	int lowest;
   1344 	int i, j, k, w, h;
   1345 	int subx, suby;         /* position of the larger subregion */
   1346 	int subw, subh;         /* larger subregion width and height */
   1347 
   1348 	if (desk < 0 || desk >= config.ndesktops || c == NULL || c->isminimized)
   1349 		return;
   1350 
   1351 	fitmonitor(mon, &c->nx, &c->ny, &c->nw, &c->nh, 1.0);
   1352 
   1353 	/* if the user placed the window, we should not re-place it */
   1354 	if (userplaced)
   1355 		return;
   1356 
   1357 	/*
   1358 	 * The container area is the region of the screen where containers live,
   1359 	 * that is, the area of the monitor not occupied by bars or the dock; it
   1360 	 * corresponds to the region occupied by a maximized container.
   1361 	 *
   1362 	 * Shod tries to find an empty region on the container area or a region
   1363 	 * with few containers in it to place a new container.  To do that, shod
   1364 	 * cuts the container area in DIV divisions horizontally and vertically,
   1365 	 * creating DIV*DIV regions; shod then counts how many containers are on
   1366 	 * each region; and places the new container on those regions with few
   1367 	 * containers over them.
   1368 	 *
   1369 	 * After some trial and error, I found out that a DIV equals to 15 is
   1370 	 * optimal.  It is not too low to provide a incorrect placement, nor too
   1371 	 * high to take so much computer time.
   1372 	 */
   1373 
   1374 	/* increment cells of grid a window is in */
   1375 	TAILQ_FOREACH(tmp, &wm.focusq, entry) {
   1376 		if (tmp != c && containerisvisible(tmp, c->mon, c->desk)) {
   1377 			fillgrid(mon, tmp->nx, tmp->ny, tmp->nw, tmp->nh, grid);
   1378 		}
   1379 	}
   1380 	TAILQ_FOREACH(obj, &wm.menuq, entry) {
   1381 		menu = ((struct Menu *)obj);
   1382 		if (istabformenu(c->selcol->selrow->seltab, menu)) {
   1383 			fillgrid(mon, menu->x, menu->y, menu->w, menu->h, grid);
   1384 		}
   1385 	}
   1386 
   1387 	/* find biggest region in grid with less windows in it */
   1388 	lowest = INT_MAX;
   1389 	subx = suby = 0;
   1390 	subw = subh = 0;
   1391 	for (i = 0; i < DIV; i++) {
   1392 		for (j = 0; j < DIV; j++) {
   1393 			if (grid[i][j] > lowest)
   1394 				continue;
   1395 			else if (grid[i][j] < lowest) {
   1396 				lowest = grid[i][j];
   1397 				subw = subh = 0;
   1398 			}
   1399 			for (w = 0; j+w < DIV && grid[i][j + w] == lowest; w++)
   1400 				;
   1401 			for (h = 1; i+h < DIV && grid[i + h][j] == lowest; h++) {
   1402 				for (k = 0; k < w && grid[i + h][j + k] == lowest; k++)
   1403 					;
   1404 				if (k < w) {
   1405 					h--;
   1406 					break;
   1407 				}
   1408 			}
   1409 			if (w * h > subw * subh) {
   1410 				subw = w;
   1411 				subh = h;
   1412 				suby = i;
   1413 				subx = j;
   1414 			}
   1415 		}
   1416 	}
   1417 	subx = subx * mon->ww / DIV;
   1418 	suby = suby * mon->wh / DIV;
   1419 	subw = subw * mon->ww / DIV;
   1420 	subh = subh * mon->wh / DIV;
   1421 	c->nx = min(mon->wx + mon->ww - c->nw, max(mon->wx, mon->wx + subx + subw / 2 - c->nw / 2));
   1422 	c->ny = min(mon->wy + mon->wh - c->nh, max(mon->wy, mon->wy + suby + subh / 2 - c->nh / 2));
   1423 	containercalccols(c);
   1424 }
   1425 
   1426 /* check whether container is sticky or is on given desktop */
   1427 int
   1428 containerisvisible(struct Container *c, struct Monitor *mon, int desk)
   1429 {
   1430 	return !c->isminimized && c->mon == mon && (c->issticky || c->desk == desk);
   1431 }
   1432 
   1433 /* check if container can be shaded */
   1434 int
   1435 containerisshaded(struct Container *c)
   1436 {
   1437 	return c->isshaded && !c->isfullscreen;
   1438 }
   1439 
   1440 /* attach tab into row; return nonzero if an attachment was performed */
   1441 int
   1442 tabattach(struct Container *c, struct Tab *det, int x, int y)
   1443 {
   1444 	enum { CREATTAB = 0x0, CREATROW = 0x1, CREATCOL = 0x2 };
   1445 	struct Column *col, *ncol;
   1446 	struct Row *row, *nrow;
   1447 	struct Tab *tab;
   1448 	struct Object *obj;
   1449 	int flag;
   1450 
   1451 	if (c == NULL)
   1452 		return 0;
   1453 	flag = CREATTAB;
   1454 	col = NULL;
   1455 	row = NULL;
   1456 	tab = NULL;
   1457 	if (x < config.borderwidth) {
   1458 		flag = CREATCOL | CREATROW;
   1459 		goto found;
   1460 	}
   1461 	if (x >= c->w - config.borderwidth) {
   1462 		flag = CREATCOL | CREATROW;
   1463 		col = TAILQ_LAST(&c->colq, ColumnQueue);
   1464 		goto found;
   1465 	}
   1466 	TAILQ_FOREACH(col, &c->colq, entry) {
   1467 		if (TAILQ_NEXT(col, entry) != NULL && x >= col->x + col->w && x < col->x + col->w + config.divwidth) {
   1468 			flag = CREATCOL | CREATROW;
   1469 			goto found;
   1470 		}
   1471 		if (x >= col->x && x < col->x + col->w) {
   1472 			if (y < config.borderwidth) {
   1473 				flag = CREATROW;
   1474 				goto found;
   1475 			}
   1476 			if (y >= c->h - config.borderwidth) {
   1477 				flag = CREATROW;
   1478 				row = TAILQ_LAST(&col->rowq, RowQueue);
   1479 				goto found;
   1480 			}
   1481 			TAILQ_FOREACH(row, &col->rowq, entry) {
   1482 				if (y > row->y && y <= row->y + config.titlewidth) {
   1483 					TAILQ_FOREACH_REVERSE(obj, &row->tabq, Queue, entry) {
   1484 						tab = (struct Tab *)obj;
   1485 						if (x > col->x + tab->x + tab->w / 2) {
   1486 							flag = CREATTAB;
   1487 							goto found;
   1488 						}
   1489 					}
   1490 					tab = NULL;
   1491 					goto found;
   1492 				}
   1493 				if (TAILQ_NEXT(row, entry) != NULL && y >= row->y + row->h && y < row->y + row->h + config.divwidth) {
   1494 					flag = CREATROW;
   1495 					goto found;
   1496 				}
   1497 			}
   1498 		}
   1499 	}
   1500 	return 0;
   1501 found:
   1502 	ncol = NULL;
   1503 	nrow = NULL;
   1504 	if (flag & CREATCOL) {
   1505 		ncol = colnew();
   1506 		containeraddcol(c, ncol, col);
   1507 		col = ncol;
   1508 	}
   1509 	if (flag & CREATROW) {
   1510 		nrow = rownew();
   1511 		coladdrow(col, nrow, row);
   1512 		row = nrow;
   1513 	}
   1514 	rowaddtab(row, det, tab);
   1515 	if (ncol != NULL)
   1516 		containercalccols(c);
   1517 	else if (nrow != NULL)
   1518 		colcalcrows(col, 1);
   1519 	else
   1520 		rowcalctabs(row);
   1521 	tabfocus(det, 0);
   1522 	containerraise(c, c->isfullscreen, c->abovebelow);
   1523 	XMapSubwindows(dpy, c->frame);
   1524 	/* no need to call shodgrouptab() and shodgroupcontainer(); tabfocus() already calls them */
   1525 	ewmhsetdesktop(det->obj.win, c->desk);
   1526 	containermoveresize(c, 0);
   1527 	containerredecorate(c, NULL, NULL, 0);
   1528 	return 1;
   1529 }
   1530 
   1531 /* delete row, then column if empty, then container if empty */
   1532 void
   1533 containerdelrow(struct Row *row)
   1534 {
   1535 	struct Container *c;
   1536 	struct Column *col;
   1537 	int recalc, redraw;
   1538 
   1539 	col = row->col;
   1540 	c = col->c;
   1541 	recalc = 1;
   1542 	redraw = 0;
   1543 	if (row->ntabs == 0) {
   1544 		rowdel(row);
   1545 		redraw = 1;
   1546 	}
   1547 	if (col->nrows == 0) {
   1548 		coldel(col);
   1549 		redraw = 1;
   1550 	}
   1551 	if (c->ncols == 0) {
   1552 		containerdel(c);
   1553 		recalc = 0;
   1554 	}
   1555 	if (recalc) {
   1556 		containercalccols(c);
   1557 		containermoveresize(c, 0);
   1558 		shodgrouptab(c);
   1559 		shodgroupcontainer(c);
   1560 		if (redraw) {
   1561 			containerdecorate(c, NULL, NULL, 0, 0);
   1562 		}
   1563 	}
   1564 }
   1565 
   1566 /* temporarily raise prev/next container and return it; also unhide it if hidden */
   1567 struct Container *
   1568 containerraisetemp(struct Container *prevc, int backward)
   1569 {
   1570 	struct Container *newc;
   1571 	Window wins[2];
   1572 
   1573 	if (prevc == NULL)
   1574 		return NULL;
   1575 	if (backward) {
   1576 		for (newc = prevc; newc != NULL; newc = TAILQ_PREV(newc, ContainerQueue, entry)) {
   1577 			if (newc != prevc &&
   1578 			    newc->mon == prevc->mon &&
   1579 			    containerisvisible(newc, prevc->mon, prevc->desk)) {
   1580 				break;
   1581 			}
   1582 		}
   1583 		if (newc == NULL) {
   1584 			TAILQ_FOREACH_REVERSE(newc, &wm.focusq, ContainerQueue, entry) {
   1585 				if (newc != prevc &&
   1586 				    newc->mon == prevc->mon &&
   1587 				    containerisvisible(newc, prevc->mon, prevc->desk)) {
   1588 					break;
   1589 				}
   1590 			}
   1591 		}
   1592 	} else {
   1593 		for (newc = prevc; newc != NULL; newc = TAILQ_NEXT(newc, entry)) {
   1594 			if (newc != prevc &&
   1595 			    newc->mon == prevc->mon &&
   1596 			    containerisvisible(newc, prevc->mon, prevc->desk)) {
   1597 				break;
   1598 			}
   1599 		}
   1600 		if (newc == NULL) {
   1601 			TAILQ_FOREACH(newc, &wm.focusq, entry) {
   1602 				if (newc != prevc &&
   1603 				    newc->mon == prevc->mon &&
   1604 				    containerisvisible(newc, prevc->mon, prevc->desk)) {
   1605 					break;
   1606 				}
   1607 			}
   1608 		}
   1609 	}
   1610 	if (newc == NULL)
   1611 		newc = prevc;
   1612 	if (newc->ishidden)
   1613 		XMapWindow(dpy, newc->frame);
   1614 	/* we save the Z-axis position of the container with wm.restackwin */
   1615 	wins[0] = newc->frame;
   1616 	wins[1] = wm.restackwin;
   1617 	XRestackWindows(dpy, wins, 2);
   1618 	XRaiseWindow(dpy, newc->frame);
   1619 	wm.focused = newc;
   1620 	containerdecorate(newc, NULL, NULL, 1, 0);
   1621 	ewmhsetactivewindow(newc->selcol->selrow->seltab->obj.win);
   1622 	return newc;
   1623 }
   1624 
   1625 /* revert container to its previous position after temporarily raised */
   1626 void
   1627 containerbacktoplace(struct Container *c, int restack)
   1628 {
   1629 	Window wins[2];
   1630 
   1631 	if (c == NULL)
   1632 		return;
   1633 	wm.focused = NULL;
   1634 	containerdecorate(c, NULL, NULL, 1, 0);
   1635 	if (restack) {
   1636 		wins[0] = wm.restackwin;
   1637 		wins[1] = c->frame;
   1638 		XRestackWindows(dpy, wins, 2);
   1639 	}
   1640 	if (c->ishidden)
   1641 		XUnmapWindow(dpy, c->frame);
   1642 	XFlush(dpy);
   1643 }
   1644 
   1645 /* detach tab from row, placing it at x,y */
   1646 void
   1647 tabdetach(struct Tab *tab, int x, int y)
   1648 {
   1649 	struct Row *row;
   1650 
   1651 	row = tab->row;
   1652 	tabremove(row, tab);
   1653 	tab->ignoreunmap = IGNOREUNMAP;
   1654 	XReparentWindow(dpy, tab->title, root, x, y);
   1655 	rowcalctabs(row);
   1656 }
   1657 
   1658 /* focus tab */
   1659 void
   1660 tabfocus(struct Tab *tab, int gotodesk)
   1661 {
   1662 	struct Container *c;
   1663 	struct Dialog *dial;
   1664 
   1665 	wm.prevfocused = wm.focused;
   1666 	if (tab == NULL) {
   1667 		wm.focused = NULL;
   1668 		XSetInputFocus(dpy, wm.focuswin, RevertToParent, CurrentTime);
   1669 		ewmhsetactivewindow(None);
   1670 	} else {
   1671 		c = tab->row->col->c;
   1672 		if (!c->isfullscreen && getfullscreen(c->mon, c->desk) != NULL)
   1673 			return;         /* we should not focus a client below a fullscreen client */
   1674 		wm.focused = c;
   1675 		tab->row->seltab = tab;
   1676 		tab->row->col->selrow = tab->row;
   1677 		tab->row->col->c->selcol = tab->row->col;
   1678 		if (gotodesk)
   1679 			deskupdate(c->mon, c->issticky ? c->mon->seldesk : c->desk);
   1680 		if (tab->row->fact == 0.0)
   1681 			rowstretch(tab->row->col, tab->row);
   1682 		XRaiseWindow(dpy, tab->row->frame);
   1683 		XRaiseWindow(dpy, tab->frame);
   1684 		if (c->isshaded || tab->row->isunmapped) {
   1685 			XSetInputFocus(dpy, tab->row->bar, RevertToParent, CurrentTime);
   1686 		} else if (!TAILQ_EMPTY(&tab->dialq)) {
   1687 			dial = (struct Dialog *)TAILQ_FIRST(&tab->dialq);
   1688 			XRaiseWindow(dpy, dial->frame);
   1689 			XSetInputFocus(dpy, dial->obj.win, RevertToParent, CurrentTime);
   1690 		} else {
   1691 			XSetInputFocus(dpy, tab->obj.win, RevertToParent, CurrentTime);
   1692 		}
   1693 		ewmhsetactivewindow(tab->obj.win);
   1694 		tabclearurgency(tab);
   1695 		containeraddfocus(c);
   1696 		containerdecorate(c, NULL, NULL, 1, 0);
   1697 		c->isminimized = 0;
   1698 		containerhide(c, 0);
   1699 		shodgrouptab(c);
   1700 		shodgroupcontainer(c);
   1701 		ewmhsetstate(c);
   1702 	}
   1703 	if (wm.prevfocused != NULL && wm.prevfocused != wm.focused) {
   1704 		containerdecorate(wm.prevfocused, NULL, NULL, 1, 0);
   1705 		ewmhsetstate(wm.prevfocused);
   1706 	}
   1707 	menuupdate();
   1708 }
   1709 
   1710 /* decorate tab */
   1711 void
   1712 tabdecorate(struct Tab *t, int pressed)
   1713 {
   1714 	int style;
   1715 	int drawlines = 0;
   1716 
   1717 	style = tabgetstyle(t);
   1718 	if (t->row != NULL && t != t->row->col->c->selcol->selrow->seltab) {
   1719 		pressed = 0;
   1720 		drawlines = 0;
   1721 	} else if (t->row != NULL && pressed) {
   1722 		pressed = 1;
   1723 		drawlines = 1;
   1724 	} else {
   1725 		pressed = 0;
   1726 		drawlines = 1;
   1727 	}
   1728 
   1729 	/* (re)create pixmap */
   1730 	if (t->ptw != t->w || t->pixtitle == None)
   1731 		pixmapnew(&t->pixtitle, t->title, t->w, config.titlewidth);
   1732 	if (t->pw != t->winw || t->ph != t->winh || t->pix == None)
   1733 		pixmapnew(&t->pix, t->frame, t->winw, t->winh);
   1734 	t->ptw = t->w;
   1735 	t->pw = t->winw;
   1736 	t->ph = t->winh;
   1737 
   1738 	/* draw background */
   1739 	drawbackground(t->pixtitle, 0, 0, t->w, config.titlewidth, style);
   1740 
   1741 	/* draw shadows */
   1742 	drawshadow(t->pixtitle, 0, 0, t->w, config.titlewidth, style, pressed);
   1743 
   1744 	/* write tab title */
   1745 	if (t->name != NULL)
   1746 		drawtitle(t->pixtitle, t->name, t->w, drawlines, style, pressed, 0);
   1747 
   1748 	/* draw frame background */
   1749 	drawbackground(t->pix, 0, 0, t->winw, t->winh, style);
   1750 
   1751 	drawcommit(t->pixtitle, t->title);
   1752 	drawcommit(t->pix, t->frame);
   1753 }
   1754 
   1755 /* update tab urgency */
   1756 void
   1757 tabupdateurgency(struct Tab *t, int isurgent)
   1758 {
   1759 	int prev;
   1760 
   1761 	prev = t->isurgent;
   1762 	if (wm.focused != NULL && t == wm.focused->selcol->selrow->seltab)
   1763 		t->isurgent = False;
   1764 	else
   1765 		t->isurgent = isurgent;
   1766 	if (prev != t->isurgent) {
   1767 		tabdecorate(t, 0);
   1768 	}
   1769 }
   1770 
   1771 /* stretch row */
   1772 void
   1773 rowstretch(struct Column *col, struct Row *row)
   1774 {
   1775 	struct Row *r;
   1776 	double fact;
   1777 	int refact;
   1778 
   1779 	fact = 1.0 / (double)col->nrows;
   1780 	refact = (row->fact == 1.0);
   1781 	TAILQ_FOREACH(r, &col->rowq, entry) {
   1782 		if (refact) {
   1783 			r->fact = fact;
   1784 		} else if (r == row) {
   1785 			r->fact = 1.0;
   1786 		} else {
   1787 			r->fact = 0.0;
   1788 		}
   1789 	}
   1790 	colcalcrows(col, 0);
   1791 	containermoveresize(col->c, 0);
   1792 }
   1793 
   1794 /* configure dialog window */
   1795 void
   1796 dialogconfigure(struct Dialog *d, unsigned int valuemask, XWindowChanges *wc)
   1797 {
   1798 	if (d == NULL)
   1799 		return;
   1800 	if (valuemask & CWWidth)
   1801 		d->maxw = wc->width;
   1802 	if (valuemask & CWHeight)
   1803 		d->maxh = wc->height;
   1804 	dialogmoveresize(d);
   1805 }
   1806 
   1807 /* commit dialog size and position */
   1808 void
   1809 dialogmoveresize(struct Dialog *dial)
   1810 {
   1811 	struct Container *c;
   1812 	int dx, dy, dw, dh;
   1813 
   1814 	dialogcalcsize(dial);
   1815 	c = dial->tab->row->col->c;
   1816 	dx = dial->x - config.borderwidth;
   1817 	dy = dial->y - config.borderwidth;
   1818 	dw = dial->w + 2 * config.borderwidth;
   1819 	dh = dial->h + 2 * config.borderwidth;
   1820 	XMoveResizeWindow(dpy, dial->frame, dx, dy, dw, dh);
   1821 	XMoveResizeWindow(dpy, dial->obj.win, config.borderwidth, config.borderwidth, dial->w, dial->h);
   1822 	winnotify(dial->obj.win, c->x + dial->tab->row->col->x + dial->x, c->y + dial->tab->row->y + dial->y, dial->w, dial->h);
   1823 	if (dial->pw != dw || dial->ph != dh) {
   1824 		dialogdecorate(dial);
   1825 	}
   1826 }
   1827 
   1828 /* create container for tab */
   1829 void
   1830 containernewwithtab(struct Tab *tab, struct Monitor *mon, int desk, XRectangle rect, int state)
   1831 {
   1832 	struct Container *c;
   1833 	struct Column *col;
   1834 	struct Row *row;
   1835 
   1836 	if (tab == NULL)
   1837 		return;
   1838 	c = containernew(rect.x, rect.y, rect.width, rect.height, state);
   1839 	c->mon = mon;
   1840 	c->desk = desk;
   1841 	row = rownew();
   1842 	col = colnew();
   1843 	containeraddcol(c, col, NULL);
   1844 	coladdrow(col, row, NULL);
   1845 	rowaddtab(row, tab, NULL);
   1846 	containerredecorate(c, NULL, NULL, 0);
   1847 	XMapSubwindows(dpy, c->frame);
   1848 	containerplace(c, mon, desk, (state & USERPLACED));
   1849 	containermoveresize(c, 0);
   1850 	if (containerisvisible(c, wm.selmon, wm.selmon->seldesk)) {
   1851 		containerraise(c, c->isfullscreen, c->abovebelow);
   1852 		containerhide(c, 0);
   1853 		tabfocus(tab, 0);
   1854 	}
   1855 	/* no need to call shodgrouptab() and shodgroupcontainer(); tabfocus() already calls them */
   1856 	ewmhsetwmdesktop(c);
   1857 	wm.setclientlist = 1;
   1858 }
   1859 
   1860 /* create container for tab */
   1861 void
   1862 managecontainer(struct Tab *prev, struct Monitor *mon, int desk, Window win, Window leader, XRectangle rect, int state, int ignoreunmap)
   1863 {
   1864 	struct Tab *tab;
   1865 	struct Container *c;
   1866 	struct Row *row;
   1867 
   1868 	tab = tabnew(win, leader, ignoreunmap);
   1869 	winupdatetitle(tab->obj.win, &tab->name);
   1870 	if (prev == NULL) {
   1871 		containernewwithtab(tab, mon, desk, rect, state);
   1872 	} else {
   1873 		row = prev->row;
   1874 		c = row->col->c;
   1875 		rowaddtab(row, tab, prev);
   1876 		rowcalctabs(row);
   1877 		ewmhsetdesktop(win, c->desk);
   1878 		wm.setclientlist = 1;
   1879 		containermoveresize(c, 0);
   1880 		containerredecorate(c, NULL, NULL, 0);
   1881 		XMapSubwindows(dpy, c->frame);
   1882 		if (wm.focused == c) {
   1883 			tabfocus(tab, 1);
   1884 		}
   1885 	}
   1886 }
   1887 
   1888 /* create container for tab */
   1889 void
   1890 managedialog(struct Tab *tab, struct Monitor *mon, int desk, Window win, Window leader, XRectangle rect, int state, int ignoreunmap)
   1891 {
   1892 	struct Dialog *dial;
   1893 
   1894 	(void)mon;
   1895 	(void)desk;
   1896 	(void)leader;
   1897 	(void)state;
   1898 	dial = dialognew(win, rect.width, rect.height, ignoreunmap);
   1899 	dial->tab = tab;
   1900 	TAILQ_INSERT_HEAD(&tab->dialq, (struct Object *)dial, entry);
   1901 	XReparentWindow(dpy, dial->frame, tab->frame, 0, 0);
   1902 	icccmwmstate(dial->obj.win, NormalState);
   1903 	dialogmoveresize(dial);
   1904 	XMapRaised(dpy, dial->frame);
   1905 	if (wm.focused != NULL && wm.focused->selcol->selrow->seltab == tab)
   1906 		tabfocus(tab, 0);
   1907 	wm.setclientlist = 1;
   1908 }
   1909 
   1910 /* unmanage tab (and delete its row if it is the only tab); return whether deletion occurred */
   1911 int
   1912 unmanagecontainer(struct Object *obj, int ignoreunmap)
   1913 {
   1914 	struct Container *c, *next;
   1915 	struct Column *col;
   1916 	struct Row *row;
   1917 	struct Tab *t;
   1918 	struct Monitor *mon;
   1919 	int desk;
   1920 	int moveresize;
   1921 	int focus;
   1922 
   1923 	t = (struct Tab *)obj;
   1924 	if (ignoreunmap && t->ignoreunmap) {
   1925 		t->ignoreunmap--;
   1926 		return 0;
   1927 	}
   1928 	row = t->row;
   1929 	col = row->col;
   1930 	c = col->c;
   1931 	desk = c->desk;
   1932 	mon = c->mon;
   1933 	moveresize = 1;
   1934 	next = c;
   1935 	tabdel(t);
   1936 	focus = (c == wm.focused);
   1937 	if (row->ntabs == 0) {
   1938 		rowdel(row);
   1939 		if (col->nrows == 0) {
   1940 			coldel(col);
   1941 			if (c->ncols == 0) {
   1942 				containerdel(c);
   1943 				next = getnextfocused(mon, desk);
   1944 				moveresize = 0;
   1945 			}
   1946 		}
   1947 	}
   1948 	if (moveresize) {
   1949 		containercalccols(c);
   1950 		containermoveresize(c, 0);
   1951 		containerredecorate(c, NULL, NULL, 0);
   1952 		shodgrouptab(c);
   1953 		shodgroupcontainer(c);
   1954 	}
   1955 	if (focus) {
   1956 		tabfocus((next != NULL) ? next->selcol->selrow->seltab : NULL, 0);
   1957 	}
   1958 	return 1;
   1959 }
   1960 
   1961 /* delete dialog; return whether dialog was deleted */
   1962 int
   1963 unmanagedialog(struct Object *obj, int ignoreunmap)
   1964 {
   1965 	struct Dialog *dial;
   1966 
   1967 	dial = (struct Dialog *)obj;
   1968 	if (ignoreunmap && dial->ignoreunmap) {
   1969 		dial->ignoreunmap--;
   1970 		return 0;
   1971 	}
   1972 	TAILQ_REMOVE(&dial->tab->dialq, (struct Object *)dial, entry);
   1973 	if (dial->pix != None)
   1974 		XFreePixmap(dpy, dial->pix);
   1975 	icccmdeletestate(dial->obj.win);
   1976 	XReparentWindow(dpy, dial->obj.win, root, 0, 0);
   1977 	XDestroyWindow(dpy, dial->frame);
   1978 	free(dial);
   1979 	return 1;
   1980 }
   1981 
   1982 /* get height of column without borders, divisors, title bars, etc */
   1983 int
   1984 columncontentheight(struct Column *col)
   1985 {
   1986 	return col->c->h - col->nrows * config.titlewidth
   1987 	       - (col->nrows - 1) * config.divwidth - 2 * col->c->b;
   1988 }
   1989 
   1990 /* get width of container without borders, divisors, etc */
   1991 int
   1992 containercontentwidth(struct Container *c)
   1993 {
   1994 	return c->w - (c->ncols - 1) * config.divwidth - 2 * c->b;
   1995 }