1 /*
2  * pwm/frame.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2001.
5  *
6  * You may distribute and modify this program under the terms of either
7  * the Clarified Artistic License or the GNU GPL, version 2 or later.
8  */
9 
10 #include <string.h>
11 
12 #include "common.h"
13 #include "frame.h"
14 #include "frameid.h"
15 #include "clientwin.h"
16 #include "screen.h"
17 #include "draw.h"
18 #include "property.h"
19 #include "winobj.h"
20 #include "focus.h"
21 #include "event.h"
22 #include "binding.h"
23 #include "moveres.h"
24 #include "placement.h"
25 #include "winlist.h"
26 
27 
28 #define IS_CURRENT_FRAME(FRAME) ((WWinObj*)(FRAME)==wglobal.current_winobj)
29 
30 
31 static bool do_frame_detach_clientwin(WFrame *frame, WClientWin *cwin,
32 									  WFrame *transient_dest);
33 static void check_minmax_size(WFrame *frame, WClientWin *cwin);
34 static void fit_clientwin(WFrame *frame, WClientWin *cwin);
35 static void move_transients_to(WFrame *dest, WFrame *src,
36 							   Window transient_for);
37 static void hideshow_transients(WFrame *dest,
38 								Window show_for, Window hide_for);
39 
40 
41 /* */
42 
43 
clientwin_to_frame_size(int iw,int ih,int flags,int * fw,int * fh)44 void clientwin_to_frame_size(int iw, int ih, int flags, int *fw, int *fh)
45 {
46 	if(!(flags&WFRAME_NO_BORDER)){
47 		iw+=2*CF_BORDER_WIDTH;
48 		ih+=2*CF_BORDER_WIDTH;
49 	}
50 
51 	if(!(flags&WFRAME_NO_BAR))
52 		ih+=GRDATA->bar_height;
53 
54 	*fw=iw;
55 	*fh=ih;
56 }
57 
58 
59 /*
60  * Frame creation
61  */
62 
63 
create_frame(int x,int y,int iw,int ih,int id,int flags)64 WFrame* create_frame(int x, int y, int iw, int ih, int id, int flags)
65 {
66 	WFrame *frame;
67 	int w, h;
68 	Window frame_win, bar_win;
69 
70 	frame=ALLOC(WFrame);
71 
72 	if(frame==NULL){
73 		warn_err();
74 		return NULL;
75 	}
76 
77 	WTHING_INIT(frame, WTHING_FRAME);
78 
79 	flags&=WFRAME_NO_BAR|WFRAME_NO_BORDER;
80 	flags|=WFRAME_HIDDEN|WFRAME_SHADE;
81 
82 	frame->flags=flags;
83 	frame->frame_id=(id==0 ? new_frame_id() : use_frame_id(id));
84 	frame->frame_iw=iw;
85 	frame->frame_ih=ih;
86 	frame->frame_ix=frame->frame_iy=(flags&WFRAME_NO_BORDER ? 0 :
87 									 CF_BORDER_WIDTH);
88 	frame->min_w=CF_WIN_MIN_WIDTH;
89 	frame->min_h=CF_WIN_MIN_HEIGHT;
90 	frame->max_w=-1;
91 	frame->max_h=-1;
92 	frame->cwin_count=0;
93 
94 	clientwin_to_frame_size(iw, ih, flags, &w, &h);
95 
96 	frame->x=x;
97 	frame->y=y;
98 	frame->w=w;
99 	frame->h=h;
100 	frame->bar_w=frame->tab_w=frame->w;
101 	frame->bar_h=(flags&WFRAME_NO_BAR ? 0 : GRDATA->bar_height);
102 
103 #ifdef CF_WANT_TRANSPARENT_TERMS
104 	frame_win=XCreateWindow(wglobal.dpy, SCREEN->root,
105 							FRAME_X(frame), FRAME_Y(frame),
106 							FRAME_W(frame), FRAME_H(frame),
107 							0, CopyFromParent, InputOutput, CopyFromParent,
108 							0, NULL);
109 #else
110 	frame_win=create_simple_window(FRAME_X(frame), FRAME_Y(frame),
111 								   FRAME_W(frame), FRAME_H(frame),
112 								   GRDATA->base_colors.pixels[WCG_PIX_BG]);
113 #endif
114 	bar_win=create_simple_window(BAR_X(frame), BAR_Y(frame),
115 								 BAR_W(frame), GRDATA->bar_height,
116 								 GRDATA->tab_colors.pixels[WCG_PIX_BG]);
117 
118 	frame->frame_win=frame_win;
119 	frame->bar_win=bar_win;
120 
121 	XSelectInput(wglobal.dpy, frame_win, FRAME_MASK);
122 	XSaveContext(wglobal.dpy, frame_win, wglobal.win_context, (XPointer)frame);
123 
124 	XSelectInput(wglobal.dpy, bar_win, BAR_MASK);
125 	XSaveContext(wglobal.dpy, bar_win, wglobal.win_context, (XPointer)frame);
126 
127 	grab_bindings(frame_win, ACTX_WINDOW|ACTX_GLOBAL);
128 
129 	return frame;
130 }
131 
132 
create_add_frame_simple(int root_x,int root_y,int iiw,int iih)133 WFrame *create_add_frame_simple(int root_x, int root_y, int iiw, int iih)
134 {
135 	WFrame *frame;
136 
137 	frame=create_frame(root_x, root_y, iiw, iih, 0, 0);
138 
139 	if(frame!=NULL)
140 		add_winobj((WWinObj*)frame, WORKSPACE_CURRENT, LVL_NORMAL);
141 
142 	return frame;
143 }
144 
145 
146 /*
147  * Frame destroy stuff
148  */
149 
150 
destroy_frame(WFrame * frame)151 void destroy_frame(WFrame *frame)
152 {
153 	unlink_winobj_d((WWinObj*)frame);
154 
155 	XDeleteContext(wglobal.dpy, frame->frame_win, wglobal.win_context);
156 	XDeleteContext(wglobal.dpy, frame->bar_win, wglobal.win_context);
157 
158 	XDestroyWindow(wglobal.dpy, frame->frame_win);
159 	XDestroyWindow(wglobal.dpy, frame->bar_win);
160 
161 	free_frame_id(frame->frame_id);
162 
163 	free_thing((WThing*)frame);
164 }
165 
166 
167 /*
168  * Clientwin switching stuff
169  */
170 
171 
do_frame_switch_clientwin(WFrame * frame,WClientWin * cwin,bool simple)172 static void do_frame_switch_clientwin(WFrame *frame, WClientWin *cwin,
173 									  bool simple)
174 {
175 	if(!WFRAME_IS_NORMAL(frame)){
176 		frame->current_cwin=cwin;
177 		if(!simple && WFRAME_IS_SHADE(frame))
178 			draw_frame_bar(frame, TRUE);
179 
180 		update_winlist();
181 		return;
182 	}
183 
184 #if 0
185 	fit_clientwin(frame, cwin);
186 #endif
187 	show_clientwin(cwin);
188 
189 	if(!simple)
190 		hide_clientwin(frame->current_cwin);
191 
192 	hideshow_transients(frame, cwin->client_win,
193 						simple ? None : frame->current_cwin->client_win);
194 
195 	frame->current_cwin=cwin;
196 
197 	if(IS_CURRENT_FRAME(frame))
198 		set_focus_weak((WThing*)cwin);
199 
200 	if(!simple)
201 		draw_frame_bar(frame, TRUE);
202 
203 	update_winlist();
204 }
205 
206 
frame_switch_clientwin(WFrame * frame,WClientWin * cwin)207 void frame_switch_clientwin(WFrame *frame, WClientWin *cwin)
208 {
209 	if(cwin!=frame->current_cwin)
210 		do_frame_switch_clientwin(frame, cwin, FALSE);
211 }
212 
213 
frame_switch_nth(WFrame * frame,int num)214 void frame_switch_nth(WFrame *frame, int num)
215 {
216 	WThing *thing;
217 
218 	thing=nth_subthing((WThing*)frame, num);
219 
220 	if(thing!=NULL)
221 		frame_switch_clientwin(frame, (WClientWin*)thing);
222 }
223 
224 
frame_switch_rot(WFrame * frame,int rotcnt)225 void frame_switch_rot(WFrame *frame, int rotcnt)
226 {
227 	WThing *thing;
228 
229 	if(frame->current_cwin==NULL)
230 		thing=nth_subthing((WThing*)frame, rotcnt);
231 	else
232 		thing=nth_thing((WThing*)(frame->current_cwin), rotcnt);
233 
234 	if(thing!=NULL)
235 		frame_switch_clientwin(frame, (WClientWin*)thing);
236 }
237 
238 
239 /*
240  * Frame state
241  */
242 
243 
set_frame_state(WFrame * frame,int stateflags)244 void set_frame_state(WFrame *frame, int stateflags)
245 {
246 	int changes;
247 	Window show_for=None, hide_for=None;
248 	/*
249 	if(WFRAME_IS_NO_BAR(frame))
250 		stateflags&=~WFRAME_HIDDEN;
251 
252 	if(WFRAME_IS_NO_BORDER(frame))
253 		stateflags&=~WFRAME_SHADE;
254 	*/
255 	changes=(stateflags^frame->flags)&(WFRAME_HIDDEN|WFRAME_SHADE);
256 
257 	if(changes==0)
258 		return;
259 
260 	if(WFRAME_IS_NORMAL(frame)){
261 		hide_clientwin(frame->current_cwin);
262 		hide_for=frame->current_cwin->client_win;
263 	}
264 
265 	frame->flags^=changes;
266 
267 	if(stateflags&WFRAME_HIDDEN){
268 		/* hide it */
269 		if(changes&WFRAME_HIDDEN)
270 			unmap_winobj((WWinObj*)frame);
271 	}else if(stateflags&WFRAME_SHADE){
272 		/* shade it */
273 		if(WWINOBJ_IS_MAPPED(frame))
274 			XUnmapWindow(wglobal.dpy, frame->frame_win);
275 		else
276 			map_winobj((WWinObj*)frame);
277 
278 		if(IS_CURRENT_FRAME(frame))
279 			set_focus_weak((WThing*)frame);
280 	}else{
281 		/* show it */
282 		if(WWINOBJ_IS_MAPPED(frame))
283 			XMapWindow(wglobal.dpy, frame->frame_win);
284 		else
285 			map_winobj((WWinObj*)frame);
286 
287 		show_clientwin(frame->current_cwin);
288 		show_for=frame->current_cwin->client_win;
289 
290 		if(IS_CURRENT_FRAME(frame))
291 			set_focus_weak((WThing*)frame);
292 	}
293 
294 	hideshow_transients(frame, show_for, hide_for);
295 }
296 
297 
298 /*
299  * Focus
300  */
301 
302 
activate_frame(WFrame * frame)303 void activate_frame(WFrame *frame)
304 {
305 	draw_frame_bar(frame, TRUE);
306 	draw_frame_frame(frame, TRUE);
307 }
308 
309 
deactivate_frame(WFrame * frame)310 void deactivate_frame(WFrame *frame)
311 {
312 	draw_frame_bar(frame, TRUE);
313 	draw_frame_frame(frame, TRUE);
314 }
315 
316 
317 /* */
318 
319 
restack_transient(WFrame * frame,Window transient_for)320 static void restack_transient(WFrame *frame, Window transient_for)
321 {
322 	WClientWin *cwin=find_clientwin(transient_for);
323 
324 	if(cwin==NULL || !CWIN_HAS_FRAME(cwin))
325 		return;
326 
327 	restack_winobj_above((WWinObj*)frame, (WWinObj*)CWIN_FRAME(cwin),
328 						 FALSE);
329 }
330 
331 
frame_reconf_clientwin(WFrame * frame,WClientWin * cwin)332 static void frame_reconf_clientwin(WFrame* frame, WClientWin *cwin)
333 {
334 	int x, y;
335 
336 	x=FRAME_X(frame)+frame->frame_ix;
337 	y=FRAME_Y(frame)+frame->frame_iy;
338 #ifndef CF_CWIN_TOPLEFT
339 	x+=(frame->frame_iw-cwin->client_w)/2;
340 	y+=(frame->frame_ih-cwin->client_h)/2;
341 #endif
342 	clientwin_reconf_at(cwin, x, y);
343 }
344 
345 
346 /*
347  * Attach new window to a frame.
348  */
349 
do_frame_attach_clientwin(WFrame * frame,WClientWin * cwin)350 static void do_frame_attach_clientwin(WFrame *frame, WClientWin *cwin)
351 {
352 	int tw=frame->frame_iw, th=frame->frame_ih;
353 
354 	link_thing((WThing*)frame, (WThing*)cwin);
355 	frame->cwin_count++;
356 
357 	set_integer_property(cwin->client_win, wglobal.atom_frame_id,
358 						 frame->frame_id);
359 	set_integer_property(cwin->client_win, wglobal.atom_workspace_num,
360 						 frame->workspace);
361 
362 	check_minmax_size(frame, cwin);
363 	if(frame->frame_iw!=cwin->client_w || frame->frame_ih!=cwin->client_h)
364 		frame_clientwin_resize(frame, cwin, frame->frame_iw, frame->frame_ih,
365 							   FALSE);
366 	else
367 		frame_recalc_bar(frame);
368 
369 	if(cwin->state!=IconicState && WFRAME_IS_SHADE(frame)){
370 		frame_switch_clientwin(frame, cwin);
371 		set_frame_state(frame, frame->flags&~WFRAME_SHADE);
372 	}else if(frame->cwin_count==1){
373 		if(cwin->state==IconicState)
374 			set_frame_state(frame, frame->flags|WFRAME_SHADE);
375 		frame_switch_clientwin(frame, cwin);
376 	}
377 
378 	if(!WFRAME_IS_NORMAL(frame) || cwin!=frame->current_cwin)
379 		hide_clientwin(cwin);
380 }
381 
382 
frame_attach_clientwin(WFrame * frame,WClientWin * cwin)383 bool frame_attach_clientwin(WFrame *frame, WClientWin *cwin)
384 {
385 	WFrame *oframe=NULL;
386 	WWinObj *tmp;
387 
388 	if(CWIN_HAS_FRAME(cwin)){
389 		if(CWIN_FRAME(cwin)==frame)
390 			return TRUE;
391 
392 		oframe=CWIN_FRAME(cwin);
393 	}
394 
395 	if(frame->cwin_count==1 && frame->stack_above!=NULL){
396 		/* Adding new window to a transient-stacked frame ->
397 		 * stack normally.
398 		 */
399 		tmp=(WWinObj*)frame;
400 		do{
401 			tmp=tmp->stack_above;
402 		}while(tmp->stack_above!=NULL);
403 
404 		restack_winobj((WWinObj*)frame, tmp->stack_lvl, FALSE);
405 	}
406 
407 #ifndef CF_NO_WILD_WINDOWS
408 	if(frame->cwin_count==0 && CWIN_IS_WILD(cwin))
409 		frame_set_decor(frame, WFRAME_NO_DECOR);
410 	else
411 		frame_set_decor(frame, 0);
412 #endif
413 
414 	/* Reparent and attach the window */
415 	XSelectInput(wglobal.dpy, cwin->client_win,
416 				 cwin->event_mask&~StructureNotifyMask);
417 	XReparentWindow(wglobal.dpy, cwin->client_win, frame->frame_win,
418 					frame->frame_ix, frame->frame_iy);
419 	XSelectInput(wglobal.dpy, cwin->client_win, cwin->event_mask);
420 
421 	/* Tk apps need this or else their menus will be initially fsck'd up */
422 	frame_reconf_clientwin(frame, cwin);
423 
424 	if(oframe!=NULL){
425 		if(!do_frame_detach_clientwin(oframe, cwin, frame))
426 			oframe=NULL;
427 	}
428 
429 	do_frame_attach_clientwin(frame, cwin);
430 
431 	if(frame->cwin_count==1 && cwin->transient_for!=None){
432 		/* Adding a new transient window to an empty frame? */
433 		restack_transient(frame, cwin->transient_for);
434 	}
435 
436 	if(oframe!=NULL && oframe->cwin_count==1){
437 		cwin=first_clientwin((WThing*)oframe);
438 		assert(cwin!=NULL);
439 
440 		if(cwin->transient_for!=None)
441 			restack_transient(oframe, cwin->transient_for);
442 
443 #ifndef CF_NO_WILD_WINDOWS
444 		if(CWIN_IS_WILD(cwin))
445 			frame_set_decor(oframe, WFRAME_NO_DECOR);
446 #endif
447 	}
448 
449 	update_winlist();
450 
451 	return TRUE;
452 }
453 
454 
455 /*
456  * Detach a window from a frame
457  */
458 
do_frame_detach_clientwin(WFrame * frame,WClientWin * cwin,WFrame * transient_dest)459 static bool do_frame_detach_clientwin(WFrame *frame, WClientWin *cwin,
460 									  WFrame *transient_dest)
461 {
462 	WClientWin *next;
463 	int nw, nh;
464 
465 #if 0
466 	next=prev_clientwin(cwin);
467 	if(next==NULL)
468 		next=next_clientwin(cwin);
469 #else
470 	next=next_clientwin(cwin);
471 	if(next==NULL)
472 		next=prev_clientwin(cwin);
473 #endif
474 	move_transients_to(transient_dest, frame, cwin->client_win);
475 
476 	unlink_thing((WThing*)cwin);
477 	frame->cwin_count--;
478 
479 	if(frame->cwin_count==0){
480 		if(!(frame->flags&WTHING_SUBDEST))
481 			destroy_frame(frame);
482 		return FALSE;
483 	}
484 
485 	assert(next!=NULL && next!=cwin);
486 
487 	nw=frame->frame_iw;
488 	nh=frame->frame_ih;
489 
490 	frame_recalc_minmax(frame);
491 
492 	if(!FRAME_MAXW_UNSET(frame) && frame->frame_iw>frame->max_w)
493 		nw=frame->max_w;
494 	if(!FRAME_MAXH_UNSET(frame) && frame->frame_ih>frame->max_h)
495 		nh=frame->max_h;
496 
497 	if(cwin==frame->current_cwin)
498 		do_frame_switch_clientwin(frame, next, TRUE);
499 
500 	if(nw!=frame->frame_iw || nh!=frame->frame_ih)
501 		set_frame_size(frame, nw, nh);
502 	else
503 		frame_recalc_bar(frame);
504 
505 	return TRUE;
506 }
507 
508 
frame_detach_clientwin(WFrame * frame,WClientWin * cwin,int x,int y)509 void frame_detach_clientwin(WFrame *frame, WClientWin *cwin, int x, int y)
510 {
511 	XSelectInput(wglobal.dpy, cwin->client_win,
512 				 cwin->event_mask&~StructureNotifyMask);
513 	XReparentWindow(wglobal.dpy, cwin->client_win, SCREEN->root, x, y);
514 	XSelectInput(wglobal.dpy, cwin->client_win, cwin->event_mask);
515 
516 	if(do_frame_detach_clientwin(frame, cwin, NULL) && frame->cwin_count==1){
517 		cwin=first_clientwin((WThing*)frame);
518 		assert(cwin!=NULL);
519 
520 		if(cwin->transient_for!=None)
521 			restack_transient(frame, cwin->transient_for);
522 
523 #ifndef CF_NO_WILD_WINDOWS
524 		if(CWIN_IS_WILD(cwin))
525 			frame_set_decor(frame, WFRAME_NO_DECOR);
526 #endif
527 	}
528 
529 	update_winlist();
530 }
531 
532 
533 /*
534  * Move and resize
535  */
536 
537 
do_set_frame_size(WFrame * frame,int iw,int ih)538 static void do_set_frame_size(WFrame *frame, int iw, int ih)
539 {
540 	frame->frame_iw=iw;
541 	frame->frame_ih=ih;
542 
543 	clientwin_to_frame_size(iw, ih, frame->flags, &(frame->w), &(frame->h));
544 
545 	XResizeWindow(wglobal.dpy, frame->frame_win,
546 				  FRAME_W(frame), FRAME_H(frame));
547 
548 	frame_recalc_bar(frame);
549 }
550 
551 
set_frame_size(WFrame * frame,int iw,int ih)552 void set_frame_size(WFrame *frame, int iw, int ih)
553 {
554 	WClientWin *cwin;
555 
556 	do_set_frame_size(frame, iw, ih);
557 
558 	frame->flags&=~WFRAME_MAX_BOTH;
559 
560 	cwin=first_clientwin((WThing*)frame);
561 
562 	while(cwin!=NULL){
563 		fit_clientwin(frame, cwin);
564 		cwin=next_clientwin(cwin);
565 	}
566 }
567 
568 
set_frame_pos(WFrame * frame,int x,int y)569 void set_frame_pos(WFrame *frame, int x, int y)
570 {
571 	WClientWin *cwin;
572 
573 	frame->x=x;
574 	frame->y=y;
575 	XMoveWindow(wglobal.dpy, frame->bar_win, BAR_X(frame), BAR_Y(frame));
576 	XMoveWindow(wglobal.dpy, frame->frame_win, FRAME_X(frame), FRAME_Y(frame));
577 
578 	cwin=first_clientwin((WThing*)frame);
579 	while(cwin!=NULL){
580 		frame_reconf_clientwin(frame, cwin);
581 		cwin=next_clientwin(cwin);
582 	}
583 }
584 
585 
check_minmax_size(WFrame * frame,WClientWin * cwin)586 static void check_minmax_size(WFrame *frame, WClientWin *cwin)
587 {
588 	if(cwin->size_hints.flags&PMinSize){
589 		if(cwin->size_hints.min_width>frame->min_w)
590 			frame->min_w=cwin->size_hints.min_width;
591 		if(cwin->size_hints.min_height>frame->min_h)
592 			frame->min_h=cwin->size_hints.min_height;
593 	}
594 
595 	if(cwin->size_hints.flags&PMaxSize){
596 		if(frame->max_w!=0 && cwin->size_hints.max_width>frame->max_w)
597 			frame->max_w=cwin->size_hints.max_width;
598 		if(frame->max_h!=0 && cwin->size_hints.max_height>frame->max_h)
599 			frame->max_h=cwin->size_hints.max_height;
600 	}else{
601 		frame->max_w=0;
602 		frame->max_h=0;
603 	}
604 
605 	if(!FRAME_MAXW_UNSET(frame) && frame->min_w>frame->max_w)
606 		frame->max_w=frame->min_w;
607 
608 	if(!FRAME_MAXH_UNSET(frame) && frame->min_h>frame->max_h)
609 		frame->max_h=frame->min_h;
610 }
611 
612 
frame_recalc_minmax(WFrame * frame)613 void frame_recalc_minmax(WFrame *frame)
614 {
615 	WClientWin *cwin=first_clientwin((WThing*)frame);
616 
617 	frame->min_w=CF_WIN_MIN_WIDTH;
618 	frame->min_h=CF_WIN_MIN_HEIGHT;
619 	frame->max_w=-1;
620 	frame->max_h=-1;
621 
622 	while(cwin!=NULL){
623 		check_minmax_size(frame, cwin);
624 		cwin=next_clientwin(cwin);
625 	}
626 }
627 
628 
do_resize_clientwin(WFrame * frame,WClientWin * cwin,int w,int h)629 static void do_resize_clientwin(WFrame *frame, WClientWin *cwin, int w, int h)
630 {
631 #ifndef CF_CWIN_TOPLEFT
632 	int x, y;
633 #endif
634 
635 	if(w!=cwin->client_w || h!=cwin->client_h){
636 		cwin->client_w=w;
637 		cwin->client_h=h;
638 		XResizeWindow(wglobal.dpy, cwin->client_win, w, h);
639 	}
640 
641 #ifndef CF_CWIN_TOPLEFT
642 	x=frame->frame_ix+(frame->frame_iw-cwin->client_w)/2;
643 	y=frame->frame_iy+(frame->frame_ih-cwin->client_h)/2;
644 	XMoveWindow(wglobal.dpy, cwin->client_win, x, y);
645 	clientwin_reconf_at(cwin, FRAME_X(frame)+x, FRAME_Y(frame)+y);
646 #endif
647 }
648 
649 
fit_clientwin(WFrame * frame,WClientWin * cwin)650 static void fit_clientwin(WFrame *frame, WClientWin *cwin)
651 {
652 	int w=frame->frame_iw;
653 	int h=frame->frame_ih;
654 
655 	calc_size(frame, cwin, FALSE, &w, &h);
656 	do_resize_clientwin(frame, cwin, w, h);
657 }
658 
659 
660 /* This is called when a client wants its window resized.
661  */
frame_clientwin_resize(WFrame * frame,WClientWin * cwin,int w,int h,bool dmax)662 void frame_clientwin_resize(WFrame *frame, WClientWin *cwin, int w, int h,
663 							bool dmax)
664 {
665 	int fw, fh;
666 	WClientWin *tmp;
667 	int tmpf=frame->flags;
668 
669 	calc_size(frame, cwin, FALSE, &w, &h);
670 
671 	fw=w;
672 	fh=h;
673 
674 	if(frame->cwin_count>1){
675 		/* Don't resize the frame smaller then the biggest window */
676 		for(tmp=first_clientwin((WThing*)frame);
677 			tmp!=NULL;
678 			tmp=next_clientwin(tmp)){
679 
680 			if(tmp==cwin)
681 				continue;
682 			if(tmp->client_w>fw)
683 				fw=tmp->client_w;
684 			if(tmp->client_h>fh)
685 				fh=tmp->client_h;
686 		}
687 	}
688 
689 
690 	do_set_frame_size(frame, fw, fh);
691 
692 	if(!dmax)
693 		frame->flags=tmpf;
694 
695 	/* Resize all the windows */
696 	tmp=first_clientwin((WThing*)frame);
697 	while(tmp!=NULL){
698 		if(tmp==cwin)
699 			do_resize_clientwin(frame, tmp, w, h);
700 		else
701 			fit_clientwin(frame, tmp);
702 		tmp=next_clientwin(tmp);
703 	}
704 }
705 
706 
707 /* Possibly take dock into account? */
708 #define MAX_WIDTH (SCREEN->width)
709 #define MAX_HEIGHT (SCREEN->height)
710 
711 
maximize_frame(WFrame * frame,int flags)712 static void maximize_frame(WFrame *frame, int flags)
713 {
714 	int ow, oh;
715 	int ox, oy;
716 	int w, h;
717 	int x, y;
718 
719 	ox=frame->x;
720 	oy=frame->y;
721 	ow=frame->frame_iw;
722 	oh=frame->frame_ih;
723 
724 	if(flags&WFRAME_MAX_HORIZ){
725 		w=MAX_WIDTH-frame->frame_ix*2;
726 		x=0;
727 
728 		frame->saved_iw=ow;
729 		frame->saved_x=ox;
730 	}else{
731 		x=ox;
732 		w=ow;
733 	}
734 
735 	if(flags&WFRAME_MAX_VERT){
736 		h=MAX_HEIGHT-frame->frame_iy*2-frame->bar_h;
737 		y=0;
738 
739 		frame->saved_ih=oh;
740 		frame->saved_y=oy;
741 	}else{
742 		y=oy;
743 		h=oh;
744 	}
745 
746 	flags|=frame->flags;
747 
748 	calc_size(frame, frame->current_cwin, frame->cwin_count!=1, &w, &h);
749 
750 	set_frame_pos(frame, x, y);
751 	set_frame_size(frame, w, h);
752 
753 	frame->flags=flags;
754 }
755 
756 
unmaximize_frame(WFrame * frame,int flags)757 static void unmaximize_frame(WFrame *frame, int flags)
758 {
759 	int x, y;
760 	int w, h;
761 
762 	if(flags&WFRAME_MAX_HORIZ){
763 		x=frame->saved_x;
764 		w=frame->saved_iw;
765 	}else{
766 		x=frame->x;
767 		w=frame->frame_iw;
768 	}
769 
770 	if(flags&WFRAME_MAX_VERT){
771 		y=frame->saved_y;
772 		h=frame->saved_ih;
773 	}else{
774 		y=frame->y;
775 		h=frame->frame_ih;
776 	}
777 
778 	flags=frame->flags&~flags;
779 
780 	calc_size(frame, frame->current_cwin, frame->cwin_count!=1, &w, &h);
781 
782 	set_frame_size(frame, w, h);
783 	set_frame_pos(frame, x, y);
784 
785 	frame->flags=flags;
786 }
787 
788 
frame_do_toggle_maximize(WFrame * frame,int mask)789 static void frame_do_toggle_maximize(WFrame *frame, int mask)
790 {
791 	int f=mask&~frame->flags;
792 
793 	if(f==0)
794 		unmaximize_frame(frame, mask&frame->flags);
795 	else
796 		maximize_frame(frame, f);
797 }
798 
799 
frame_toggle_maximize(WFrame * frame,int mask)800 void frame_toggle_maximize(WFrame *frame, int mask)
801 {
802 	mask&=WFRAME_MAX_BOTH;
803 
804 	if(mask==0)
805 		mask=WFRAME_MAX_BOTH;
806 
807 	frame_do_toggle_maximize(frame, mask);
808 }
809 
810 
811 /*
812  * Transient stuff
813  */
814 
815 
hideshow_transients(WFrame * frame,Window show_for,Window hide_for)816 static void hideshow_transients(WFrame *frame,
817 								Window show_for, Window hide_for)
818 {
819 	WWinObj *obj;
820 	WClientWin *cwin;
821 
822 	if(hide_for==show_for)
823 		return;
824 
825 	obj=frame->stack_above_list;
826 
827 	while(obj!=NULL){
828 		if(obj->type!=WTHING_FRAME)
829 			goto cont;
830 
831 		/* There should only be exactly one client window if it is
832 		 * a transient. */
833 		cwin=first_clientwin((WThing*)obj);
834 
835 		if(cwin==NULL)
836 			continue;
837 
838 		if(cwin->transient_for==hide_for && hide_for!=None)
839 			set_frame_state((WFrame*)obj, obj->flags|WFRAME_HIDDEN);
840 		else if(cwin->transient_for==show_for && show_for!=None)
841 			set_frame_state((WFrame*)obj, obj->flags&~WFRAME_HIDDEN);
842 	cont:
843 		obj=traverse_winobjs(obj, (WWinObj*)frame);
844 	}
845 }
846 
847 
move_transients_to(WFrame * dest,WFrame * src,Window transient_for)848 static void move_transients_to(WFrame *dest, WFrame *src, Window transient_for)
849 {
850 	WWinObj *obj, *next;
851 	WClientWin *cwin=NULL;
852 
853 	for(obj=src->stack_above_list; obj!=NULL; obj=next){
854 		next=obj->stack_next;
855 
856 		if(obj->type!=WTHING_FRAME)
857 			continue;
858 
859 		/* A window is being attached to a frame containing its
860 		 * transient windows - this should be taken care of elsewhere
861 		 */
862 		assert((WFrame*)obj!=dest);
863 
864 		/* There should only be exactly one client window if it is
865 		 * a transient.
866 		 */
867 		cwin=first_clientwin((WThing*)obj);
868 		if(cwin==NULL)
869 			continue;
870 
871 		if(cwin->transient_for!=transient_for)
872 			continue;
873 
874 		if(dest==NULL){
875 			restack_winobj(obj, src->stack_lvl, FALSE /*TRUE*/);
876 			set_frame_state((WFrame*)obj, obj->flags&~WFRAME_HIDDEN);
877 		}else{
878 			restack_winobj_above(obj, (WWinObj*)dest, TRUE);
879 			/* Hide now, no need to run through the list again later */
880 			if(dest->cwin_count>0)
881 				set_frame_state((WFrame*)obj, obj->flags|WFRAME_HIDDEN);
882 		}
883 
884 	}
885 }
886 
887 
888 /*
889  * Tagged-clientwin stuff
890  */
891 
892 
do_attach_tagged(WFrame * frame)893 static void do_attach_tagged(WFrame *frame)
894 {
895 	WClientWin *cwin;
896 	bool newframe=FALSE;
897 	int x, y;
898 
899 	cwin=SCREEN->clientwin_list;
900 
901 	for(; cwin!=NULL; cwin=cwin->s_cwin_next){
902 		if(!(cwin->flags&CWIN_TAGGED))
903 			continue;
904 
905 		cwin->flags&=~CWIN_TAGGED;
906 
907 		if(frame==NULL){
908 			frame=create_add_frame_simple(0, 0,
909 										  cwin->client_w, cwin->client_h);
910 			if(frame==NULL)
911 				return;
912 			newframe=TRUE;
913 		}
914 		frame_attach_clientwin(frame, cwin);
915 	}
916 
917 	if(newframe){
918 		calc_placement(frame->w, frame->h, frame->workspace, &x, &y);
919 		set_frame_pos(frame, x, y);
920 		set_frame_state(frame, 0);
921 	}
922 }
923 
924 
frame_attach_tagged(WFrame * frame)925 void frame_attach_tagged(WFrame *frame)
926 {
927 	do_attach_tagged(frame);
928 }
929 
930 
join_tagged()931 void join_tagged()
932 {
933 	do_attach_tagged(NULL);
934 }
935 
936 
937 /*
938  * Misc.
939  */
940 
941 
frame_recalc_bar(WFrame * frame)942 void frame_recalc_bar(WFrame *frame)
943 {
944 	WClientWin *cwin;
945 	const char *str;
946 	int maxw=0;
947 	int tmaxw=0;
948 	int w, tmp;
949 	int n=0;
950 
951 	if(frame->flags&WFRAME_NO_BAR)
952 		return;
953 
954 	for(cwin=first_clientwin((WThing*)frame);
955 		cwin!=NULL;
956 		cwin=next_clientwin(cwin)){
957 
958 		if(cwin->state!=NormalState && cwin->state!=IconicState)
959 			continue;
960 
961 		str=clientwin_full_label(cwin);
962 
963 		w=XTextWidth(GRDATA->font, str, strlen(str));
964 
965 		if(w>tmaxw)
966 			tmaxw=w;
967 
968 		n++;
969 	}
970 
971 	assert(n!=0);
972 
973 	maxw=CF_BAR_MAX_WIDTH_Q*frame->w;
974 	if(maxw<CF_BAR_MIN_WIDTH && frame->w>=CF_BAR_MIN_WIDTH)
975 		maxw=CF_BAR_MIN_WIDTH;
976 
977 	w=(tmaxw+CF_TAB_MIN_TEXT_X_OFF*2);
978 	tmp=maxw-w*n;	/* tmp=bar max width - min space for full-label tabs */
979 
980 	if(tmp>0){
981 		/* No label truncation needed. Good. See how much can be padded. */
982 		tmp/=n*2;
983 
984 		if(tmp>CF_TAB_MAX_TEXT_X_OFF)
985 			tmp=CF_TAB_MAX_TEXT_X_OFF;
986 
987 		w=tmaxw+tmp*2;
988 
989 		if(w<CF_TAB_MIN_WIDTH){
990 			w=CF_TAB_MIN_WIDTH;
991 			if(w*n>maxw)
992 				w=maxw/n;
993 		}
994 	}else{
995 		/* Some labels must be truncated. */
996 		w=maxw/n;
997 	}
998 
999 	tmaxw=w-2*CF_TAB_MIN_TEXT_X_OFF;
1000 
1001 	/* Make the labels */
1002 	for(cwin=first_clientwin((WThing*)frame);
1003 		cwin!=NULL;
1004 		cwin=next_clientwin(cwin)){
1005 
1006 		if(cwin->state!=NormalState && cwin->state!=IconicState)
1007 			continue;
1008 
1009 		clientwin_make_label(cwin, tmaxw);
1010 	}
1011 
1012 	frame->tab_w=w;
1013 
1014 	if(frame->bar_w==n*w){
1015 		draw_frame_bar(frame, TRUE);
1016 	}else{
1017 		frame->bar_w=n*w;
1018 		XResizeWindow(wglobal.dpy, frame->bar_win, frame->bar_w, frame->bar_h);
1019 	}
1020 }
1021 
1022 
1023 /*
1024  * Shade
1025  */
1026 
1027 
frame_toggle_shade(WFrame * frame)1028 void frame_toggle_shade(WFrame *frame)
1029 {
1030 	set_frame_state(frame, frame->flags^WFRAME_SHADE);
1031 }
1032 
1033 
frame_toggle_sticky(WFrame * frame)1034 void frame_toggle_sticky(WFrame *frame)
1035 {
1036 	int ws=WORKSPACE_STICKY;
1037 
1038 	if(WWINOBJ_IS_STICKY(frame))
1039 		ws=WORKSPACE_CURRENT;
1040 
1041 	move_to_workspace((WWinObj*)frame, ws);
1042 
1043 	/*draw_frame_bar(frame, TRUE);*/
1044 }
1045 
1046 
1047 /*
1048  * Decor toggle
1049  */
1050 
frame_set_decor(WFrame * frame,int decorflags)1051 void frame_set_decor(WFrame *frame, int decorflags)
1052 {
1053 	int flagchg;
1054 	int w, h, x, y;
1055 	WClientWin *cwin;
1056 
1057 	flagchg=(frame->flags&WFRAME_NO_DECOR)^decorflags;
1058 
1059 	if(flagchg==0)
1060 		return;
1061 
1062 	if(flagchg&WFRAME_NO_BAR){
1063 		if(frame->flags&WFRAME_NO_BAR){
1064 			XMapWindow(wglobal.dpy, frame->bar_win);
1065 			frame->bar_h=GRDATA->bar_height;
1066 		}else{
1067 			if(WFRAME_IS_SHADE(frame))
1068 				set_frame_state(frame, frame->flags&~WFRAME_SHADE);
1069 			XUnmapWindow(wglobal.dpy, frame->bar_win);
1070 			frame->bar_h=0;
1071 		}
1072 	}
1073 
1074 	if(flagchg&WFRAME_NO_BORDER){
1075 		if(frame->flags&WFRAME_NO_BORDER){
1076 			frame->frame_ix=frame->frame_iy=CF_BORDER_WIDTH;
1077 		}else{
1078 			frame->frame_ix=frame->frame_iy=0;
1079 		}
1080 	}
1081 
1082 	frame->flags^=flagchg;
1083 
1084 	w=frame->frame_iw+2*frame->frame_ix;
1085 	h=frame->frame_ih+2*frame->frame_iy;
1086 	frame->w=w;
1087 	frame->h=h+frame->bar_h;
1088 
1089 	XMoveResizeWindow(wglobal.dpy, frame->frame_win,
1090 					  frame->x, frame->y+frame->bar_h, w, h);
1091 
1092 	if(flagchg&WFRAME_NO_BAR && !(decorflags&WFRAME_NO_BAR))
1093 		frame_recalc_bar(frame);
1094 
1095 	cwin=first_clientwin((WThing*)frame);
1096 	while(cwin!=NULL){
1097 		x=frame->frame_ix;
1098 		y=frame->frame_iy;
1099 #ifndef CF_CWIN_TOPLEFT
1100 		x+=(frame->frame_iw-cwin->client_w)/2;
1101 		y+=(frame->frame_ih-cwin->client_h)/2;
1102 #endif
1103 		XMoveWindow(wglobal.dpy, cwin->client_win, x, y);
1104 		cwin=next_clientwin(cwin);
1105 	}
1106 }
1107 
1108 
frame_toggle_decor(WFrame * frame)1109 void frame_toggle_decor(WFrame *frame)
1110 {
1111 	int decorflags=0;
1112 
1113 	if((frame->flags&WFRAME_NO_DECOR)==0)
1114 		decorflags=WFRAME_NO_DECOR;
1115 
1116 	frame_set_decor(frame, decorflags);
1117 }
1118 
1119