1 /* $Id: pager.c,v 1.1 2004/08/28 19:25:46 dannybackx Exp $ */
2 /****************************************************************************
3  * This module is all new
4  * by Rob Nation
5  ****************************************************************************/
6 /***********************************************************************
7  * The rest of it is all my fault -- MLM
8  * mwm - "LessTif Window Manager"
9  ***********************************************************************/
10 
11 #include <LTconfig.h>
12 
13 #include <string.h>
14 
15 #include <Xm/Xm.h>
16 #include <Xm/MwmUtil.h>
17 
18 #include "mwm.h"
19 
20 
21 Bool pagerOn = True;
22 Bool EnablePagerRedraw = True;
23 Bool DoHandlePageing = True;
24 
25 static char *pager_name = "Mwm Pager";
26 
27 static XClassHint classhints =
28 {
29     "pager",
30     "Mwm"
31 };
32 
33 static XSizeHints sizehints =
34 {
35     (PMinSize | PResizeInc | PBaseSize | PWinGravity),
36     0, 0, 100, 100,		/* x, y, width and height */
37     1, 1,			/* Min width and height */
38     0, 0,			/* Max width and height */
39     1, 1,			/* Width and height increments */
40     {0, 0},
41     {0, 0},			/* Aspect ratio - not used */
42     1, 1,			/* base size */
43     (NorthWestGravity)		/* gravity */
44 };
45 
46 /*
47  * draw the lines delimiting the virtual screens
48  */
49 static void
draw_partitions(ScreenInfo * scr)50 draw_partitions(ScreenInfo *scr)
51 {
52     int y, y1, y2, x, x1, x2;
53     int MaxW, MaxH, width, height;
54 
55     MaxW = scr->virt_x_max + scr->d_width;
56     MaxH = scr->virt_y_max + scr->d_height;
57 
58     width = scr->mwm_pager->frame_width -
59 	2 * scr->mwm_pager->boundary_width -
60 	2 * scr->mwm_pager->matte_width;
61     height = scr->mwm_pager->frame_height -
62 	scr->mwm_pager->title_height -
63 	2 * scr->mwm_pager->boundary_width -
64 	2 * scr->mwm_pager->matte_width;
65 
66     x = scr->d_width;
67     y1 = 0;
68     y2 = height;
69     while (x < MaxW)
70     {
71 	x1 = x * width / MaxW;
72 	XDrawLine(dpy, scr->pager_win,
73 		  scr->components[MWM_PAGER].normal_GC, x1, y1, x1, y2);
74 	x += scr->d_width;
75     }
76 
77     y = scr->d_height;
78     x1 = 0;
79     x2 = width;
80     while (y < MaxH)
81     {
82 	y1 = y * height / MaxH;
83 	XDrawLine(dpy, scr->pager_win,
84 		  scr->components[MWM_PAGER].normal_GC, x1, y1, x2, y1);
85 	y += scr->d_height;
86     }
87 }
88 
89 /*
90  * clear the pager area.  This will cause the pager to be redrawn.
91  */
92 void
PAGER_Clear(ScreenInfo * scr)93 PAGER_Clear(ScreenInfo *scr)
94 {
95     if ((scr->mwm_pager) && (EnablePagerRedraw))
96 	XClearArea(dpy, scr->pager_win, 0, 0, scr->mwm_pager->frame_width,
97 		   scr->mwm_pager->frame_height, True);
98 }
99 
100 /*
101  * redraw the pager: we try to be clever - re-draw the pager by causing
102  * an expose event, so that the redraw occurs when the expose arrives.
103  * The advantage is that the number of re-draws will be minimized.
104  */
105 void
PAGER_Redraw(ScreenInfo * scr)106 PAGER_Redraw(ScreenInfo *scr)
107 {
108     MwmWindow *t;
109 
110     if (!scr->mwm_pager)
111 	return;
112 
113     MISC_FlushExpose(scr->pager_win);
114     MISC_FlushExpose(scr->pager_child_win);
115 
116     if (scr->components[MWM_PAGER].f_height > 0)
117     {
118 	if (scr->mwm_highlight != NULL)
119 	{
120 	    if (!(scr->mwm_highlight->flags & STICKY) &&
121 		(scr->mwm_highlight->icon_label != NULL))
122 	    {
123 		MISC_FlushExpose(scr->mwm_highlight->pager_view);
124 		XDrawImageString(dpy, scr->mwm_highlight->pager_view,
125 				 scr->components[MWM_PAGER].normal_GC,
126 				 2, scr->components[MWM_PAGER].f_y + 2,
127 				 scr->mwm_highlight->icon_label,
128 				 strlen(scr->mwm_highlight->icon_label));
129 	    }
130 	}
131 
132 	for (t = scr->mwm_root.next; t != NULL; t = t->next)
133 	{
134 	    if (t != scr->mwm_highlight)
135 	    {
136 		if (!(t->flags & STICKY) &&
137 		    (t->icon_label != NULL))
138 		{
139 		    MISC_FlushExpose(t->pager_view);
140 		    XDrawImageString(dpy, t->pager_view,
141 				     scr->components[MWM_PAGER].normal_GC,
142 				     2, scr->components[MWM_PAGER].f_y + 2,
143 				     t->icon_label, strlen(t->icon_label));
144 		}
145 	    }
146 	}
147     }
148     draw_partitions(scr);
149 }
150 
151 /*
152  * update the child.  MLM -- I'm still not sure what this is.
153  */
154 void
PAGER_UpdateViewPort(ScreenInfo * scr)155 PAGER_UpdateViewPort(ScreenInfo *scr)
156 {
157     int width, height, x1, x2, y1, y2;
158 
159     if ((scr->pager_child_win) && (scr->mwm_pager))
160     {
161 	width = scr->mwm_pager->frame_width -
162 	    2 * scr->mwm_pager->boundary_width -
163 	    2 * scr->mwm_pager->matte_width;
164 	height = scr->mwm_pager->frame_height -
165 	    scr->mwm_pager->title_height -
166 	    2 * scr->mwm_pager->boundary_width -
167 	    2 * scr->mwm_pager->matte_width;
168 	x1 = scr->virt_x * width / (scr->virt_x_max + scr->d_width) + 1;
169 	y1 = scr->virt_y * height / (scr->virt_y_max + scr->d_height) + 1;
170 	x2 = (scr->d_width) * width / (scr->virt_x_max + scr->d_width) - 1;
171 	y2 = (scr->d_height) * height / (scr->virt_y_max + scr->d_height) - 1;
172 	if (x1 == 1)
173 	{
174 	    x1--;
175 	    x2++;
176 	}
177 	if (y1 == 1)
178 	{
179 	    y1--;
180 	    y2++;
181 	}
182 	XMoveResizeWindow(dpy, scr->pager_child_win, x1, y1, x2, y2);
183     }
184 }
185 
186 /*
187  * update the pager view
188  */
189 void
PAGER_UpdateView(ScreenInfo * scr,MwmWindow * t)190 PAGER_UpdateView(ScreenInfo *scr, MwmWindow *t)
191 {
192     unsigned int width, height;
193     int ww, wh;
194     int wx, wy;
195     int MaxH, MaxW;
196 
197     if ((!scr->mwm_pager) || (!pagerOn))
198 	return;
199 
200     width = scr->mwm_pager->frame_width -
201 	2 * scr->mwm_pager->boundary_width -
202 	2 * scr->mwm_pager->matte_width;
203     height = scr->mwm_pager->frame_height -
204 	scr->mwm_pager->title_height -
205 	2 * scr->mwm_pager->boundary_width -
206 	2 * scr->mwm_pager->matte_width;
207 
208     MaxW = scr->virt_x_max + scr->d_width;
209     MaxH = scr->virt_y_max + scr->d_height;
210 
211     if ((!(t->flags & STICKY)) &&
212 	(!((t->flags & ICONIFIED) && (t->flags & ICON_UNMAPPED))) &&
213 	(t->Desk == scr->current_desk))
214     {
215 	if (t->flags & ICONIFIED)
216 	{
217 	    /* show the icon loc */
218 	    wx = (t->icon_x_loc + scr->virt_x) * (int)width / MaxW;;
219 	    wy = (t->icon_y_loc + scr->virt_y) * (int)height / MaxH;
220 	    ww = t->icon_w_width * (int)width / MaxW;
221 	    wh = (t->icon_w_height + t->icon_p_height) * (int)height / MaxH;
222 	}
223 	else
224 	{
225 	    /* show the actual window */
226 	    wx = (t->frame_x + scr->virt_x) * (int)width / MaxW;
227 	    wy = (t->frame_y + scr->virt_y) * (int)height / MaxH;
228 	    ww = t->frame_width * (int)width / MaxW;
229 	    wh = t->frame_height * (int)height / MaxH;
230 	}
231 	if (ww < 2)
232 	    ww = 2;
233 	if (wh < 2)
234 	    wh = 2;
235 	XMoveResizeWindow(dpy, t->pager_view, wx, wy, ww, wh);
236     }
237     else
238     {
239 	/* window is sticky - make sure that the pager_view window is not
240 	 * visible */
241 	XMoveResizeWindow(dpy, t->pager_view, -10, -10, 5, 5);
242     }
243 
244     PAGER_Clear(scr);
245 }
246 
247 /*
248  *  Moves the viewport within thwe virtual desktop
249  */
250 void
PAGER_MoveViewPort(ScreenInfo * scr,int newx,int newy,Boolean grab)251 PAGER_MoveViewPort(ScreenInfo *scr, int newx, int newy, Boolean grab)
252 {
253     MwmWindow *t;
254     int deltax, deltay;
255     XEvent oevent;
256 
257     if (grab)
258 	XGrabServer(dpy);
259 
260     if (newx > scr->virt_x_max)
261 	newx = scr->virt_x_max;
262     if (newy > scr->virt_y_max)
263 	newy = scr->virt_y_max;
264     if (newx < 0)
265 	newx = 0;
266     if (newy < 0)
267 	newy = 0;
268 
269     deltay = scr->virt_y - newy;
270     deltax = scr->virt_x - newx;
271 
272     scr->virt_x = newx;
273     scr->virt_y = newy;
274 
275     if ((deltax != 0) || (deltay != 0))
276     {
277 	for (t = scr->mwm_root.next; t != NULL; t = t->next)
278 	{
279 	    /* If the window is iconified, and sticky Icons is set,
280 	     * then the window should essentially be sticky */
281 	    if (!(t->flags & STICKY))
282 	    {
283 		t->icon_x_loc += deltax;
284 		t->icon_xl_loc += deltax;
285 		t->icon_y_loc += deltay;
286 
287 		if (t->icon_pixmap_w != None)
288 		    XMoveWindow(dpy, t->icon_pixmap_w, t->icon_x_loc,
289 				t->icon_y_loc);
290 		if (t->icon_w != None)
291 		    XMoveWindow(dpy, t->icon_w, t->icon_x_loc,
292 				t->icon_y_loc + t->icon_p_height);
293 
294 		DEC_ConfigureDecorations(scr, t,
295 					 t->frame_x + deltax,
296 					 t->frame_y + deltay,
297 					 t->frame_width, t->frame_height,
298 					 False);
299 	    }
300 	}
301 	for (t = scr->mwm_root.next; t != NULL; t = t->next)
302 	{
303 	    /* If its an icon, and its sticking, autoplace it so
304 	     * that it doesn't wind up on top a a stationary
305 	     * icon */
306 	    if ((t->flags & STICKY) &&
307 		(t->flags & ICONIFIED) && (!(t->flags & ICON_MOVED)) &&
308 		(!(t->flags & ICON_UNMAPPED)))
309 		ICON_AutoPlace(scr, t);
310 	}
311 
312     }
313     /* fix up the viewport indicator */
314     PAGER_UpdateViewPort(scr);
315     PAN_CheckBounds(scr);
316 
317     /* do this with PanFrames too ??? HEDU */
318     while (XCheckTypedEvent(dpy, MotionNotify, &oevent))
319 	MISC_StashEventTime(&oevent);
320     if (grab)
321 	XUngrabServer(dpy);
322 }
323 
324 /*
325  * switch among pages
326  */
327 void
PAGER_SwitchPage(ScreenInfo * scr,Bool align,Bool ChangeFocus,XEvent * event)328 PAGER_SwitchPage(ScreenInfo *scr, Bool align, Bool ChangeFocus, XEvent *event)
329 {
330     int x, y;
331     unsigned int width, height;
332     MwmWindow *tmp_win;
333     Window dumwin;
334 
335     if (!scr->mwm_pager)
336 	return;
337 
338     XTranslateCoordinates(dpy, event->xbutton.window, scr->pager_win,
339 			  event->xbutton.x, event->xbutton.y, &x, &y, &dumwin);
340 
341     width = scr->mwm_pager->frame_width -
342 	2 * scr->mwm_pager->boundary_width -
343 	2 * scr->mwm_pager->matte_width;
344     height = scr->mwm_pager->frame_height -
345 	scr->mwm_pager->title_height -
346 	2 * scr->mwm_pager->boundary_width -
347 	2 * scr->mwm_pager->matte_width;
348 
349     if (x < 0)
350 	x = 0;
351     if (y < 0)
352 	y = 0;
353     x = x * (scr->virt_x_max + scr->d_width) / width;
354     y = y * (scr->virt_y_max + scr->d_height) / height;
355     if (align)
356     {
357 	x = (x / scr->d_width) * scr->d_width;
358 	y = (y / scr->d_height) * scr->d_height;
359     }
360     if (x < 0)
361 	x = 0;
362     if (y < 0)
363 	y = 0;
364     PAGER_MoveViewPort(scr, x, y, True);
365     if ((ChangeFocus) &&
366 	(Mwm.keyboard_focus_policy == XmEXPLICIT &&
367 	 Mwm.auto_key_focus))
368     {
369 	tmp_win = WIN_WindowToStruct(scr, event->xbutton.subwindow);
370 	if (tmp_win)
371 	{
372 	    WIN_Raise(scr, tmp_win);
373 	    WIN_SetFocusInTree(tmp_win);
374 	    WIN_SetFocus(scr, tmp_win->w, tmp_win);
375 	    MISC_SetFocusSequence(scr);
376 	}
377     }
378 }
379 
380 /*
381  * creates the pager window, if needed
382  */
383 void
PAGER_Initialize(ScreenInfo * scr,Position x,Position y)384 PAGER_Initialize(ScreenInfo *scr, Position x, Position y)
385 {
386     XTextProperty name;
387     int width, height, window_x, window_y;
388     unsigned long valuemask;
389     XSetWindowAttributes attributes;
390 
391     width = (scr->virt_x_max + scr->d_width) / scr->virt_scale;
392     height = (scr->virt_y_max + scr->d_height) / scr->virt_scale;
393 
394     if (x >= 0)
395 	window_x = x;
396     else
397     {
398 	sizehints.win_gravity = NorthEastGravity;
399 	window_x = scr->d_width - width + x - 2;
400     }
401 
402     if (y >= 0)
403 	window_y = y;
404     else
405     {
406 	window_y = scr->d_height - height + y - 2;
407 	if (x < 0)
408 	    sizehints.win_gravity = SouthEastGravity;
409 	else
410 	    sizehints.win_gravity = SouthWestGravity;
411     }
412     valuemask = (CWBackPixel | CWBorderPixel | CWEventMask | CWCursor);
413     attributes.background_pixel = scr->components[MWM_PAGER].background;
414     attributes.border_pixel = scr->components[MWM_PAGER].background;
415     attributes.cursor = scr->cursors[DEFAULT_CURS];
416     attributes.event_mask = (ExposureMask | EnterWindowMask | ButtonReleaseMask |
417 			     ButtonMotionMask);
418     sizehints.width = width;
419     sizehints.height = height;
420     sizehints.x = window_x;
421     sizehints.y = window_y;
422 
423     scr->pager_win = XCreateWindow(dpy, scr->root_win, window_x, window_y, width,
424 				   height, (unsigned int)1,
425 				   CopyFromParent, InputOutput,
426 				   (Visual *)CopyFromParent,
427 				   valuemask, &attributes);
428     XSetWMNormalHints(dpy, scr->pager_win, &sizehints);
429     XStringListToTextProperty(&pager_name, 1, &name);
430     XSetWMName(dpy, scr->pager_win, &name);
431     XSetWMIconName(dpy, scr->pager_win, &name);
432     XSetClassHint(dpy, scr->pager_win, &classhints);
433     XFree((char *)name.value);
434 
435     attributes.event_mask = KeyPressMask | ExposureMask;
436     attributes.background_pixel = scr->components[MWM_PAGER].background;
437     attributes.cursor = scr->cursors[DEFAULT_CURS];
438     scr->pager_child_win = XCreateWindow(dpy, scr->pager_win, -10, -10, 10, 10, 0,
439 					 CopyFromParent,
440 					 InputOutput, CopyFromParent,
441 					 CWEventMask | CWBackPixel | CWCursor,
442 					 &attributes);
443     XStoreName(dpy, scr->pager_child_win, "PagerChildWin");
444     XMapRaised(dpy, scr->pager_child_win);
445 }
446 
447 /*
448  * move a window via the pager
449  */
450 void
PAGER_Update(ScreenInfo * scr,XEvent * event)451 PAGER_Update(ScreenInfo *scr, XEvent *event)
452 {
453     MwmWindow *tmp_win;
454     unsigned int width, height;
455     int Xoff, Yoff, x, y, MaxW, MaxH, xl, yt, xl1, yt1;
456     Window target;
457     Bool done, finished = False;
458     XEvent oevent = *event;
459 
460     /* I tried to implement a feature so that when windows which could be
461      * opaque moved in a normal move would get the full size windows moved in
462      * conjunction with the pager version of the window, but it seems to
463      * be buggy on some machines */
464 #ifdef BROKEN_STUFF
465     Bool OpaqueMove = False;
466     int dwidth, dheight;
467 
468 #endif
469     if (!scr->mwm_pager)
470 	return;
471 
472     EnablePagerRedraw = False;
473     target = oevent.xbutton.subwindow;
474     tmp_win = WIN_WindowToStruct(scr, target);
475 
476     if (tmp_win == NULL)
477 	return;
478 
479 
480     MaxW = scr->virt_x_max + scr->d_width;
481     MaxH = scr->virt_y_max + scr->d_height;
482 
483     width = scr->mwm_pager->frame_width -
484 	2 * scr->mwm_pager->boundary_width -
485 	2 * scr->mwm_pager->matte_width;
486     height = scr->mwm_pager->frame_height -
487 	scr->mwm_pager->title_height -
488 	2 * scr->mwm_pager->boundary_width -
489 	2 * scr->mwm_pager->matte_width;
490 
491     XQueryPointer(dpy, target, &JunkRoot, &JunkChild,
492 		  &JunkX, &JunkY, &Xoff, &Yoff, &JunkMask);
493 
494     XQueryPointer(dpy, scr->pager_win, &JunkRoot, &JunkChild,
495 		  &JunkX, &JunkY, &xl, &yt, &JunkMask);
496     if (xl < 0)
497 	xl = 0;
498     if (yt < 0)
499 	yt = 0;
500     if (xl > width)
501 	xl = width;
502     if (yt > height)
503 	yt = height;
504     xl -= Xoff;
505     yt -= Yoff;
506     xl1 = xl;
507     yt1 = yt;
508 
509     while (!finished)
510     {
511 	/* block until there is an interesting event */
512 	XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask |
513 		   PointerMotionMask | ButtonMotionMask | ExposureMask |
514 		   VisibilityChangeMask, &oevent);
515 	MISC_StashEventTime(&oevent);
516 
517 	if (oevent.type == MotionNotify)
518 	    /* discard any extra motion events before a logical release */
519 	    while (XCheckMaskEvent(dpy, PointerMotionMask | ButtonMotionMask |
520 				   ButtonRelease, &oevent))
521 	    {
522 		MISC_StashEventTime(&oevent);
523 		if (oevent.type == ButtonRelease)
524 		    break;
525 	    }
526 
527 	done = False;
528 
529 	/* Handle a limited number of key press events to allow mouseless
530 	 * operation */
531 	if (oevent.type == KeyPress)
532 	    MISC_KeyboardShortcut(scr, &oevent, ButtonRelease);
533 	switch (oevent.type)
534 	{
535 	case ButtonPress:
536 	case KeyPress:
537 	    /* throw away enter and leave events until release */
538 	    done = True;
539 	    break;
540 	case ButtonRelease:
541 	    XQueryPointer(dpy, scr->pager_win, &JunkRoot, &JunkChild,
542 			  &JunkX, &JunkY, &xl, &yt, &JunkMask);
543 	    if (xl < 0)
544 		xl = 0;
545 	    if (yt < 0)
546 		yt = 0;
547 	    if (xl > width)
548 		xl = width;
549 	    if (yt > height)
550 		yt = height;
551 	    xl -= Xoff;
552 	    yt -= Yoff;
553 	    done = True;
554 	    finished = True;
555 	    break;
556 
557 	case MotionNotify:
558 	    XQueryPointer(dpy, scr->pager_win, &JunkRoot, &JunkChild,
559 			  &JunkX, &JunkY, &xl, &yt, &JunkMask);
560 	    if (xl < 0)
561 		xl = 0;
562 	    if (yt < 0)
563 		yt = 0;
564 	    if (xl > width)
565 		xl = width;
566 	    if (yt > height)
567 		yt = height;
568 
569 	    /* redraw the rubberband */
570 	    xl -= Xoff;
571 	    yt -= Yoff;
572 
573 	    done = True;
574 	    break;
575 
576 	default:
577 	    break;
578 	}
579 	if (!done)
580 	{
581 	    EVENT_Dispatch(&oevent);
582 	}
583 	XMoveWindow(dpy, target, xl, yt);
584 	draw_partitions(scr);
585 
586     }
587 
588     x = xl * MaxW / (int)width - scr->virt_x;
589     y = yt * MaxH / (int)height - scr->virt_y;
590 
591     PAGER_UpdateView(scr, tmp_win);
592     if ((xl1 != xl) || (yt1 != yt))
593     {
594 	if ((tmp_win->flags & ICONIFIED))
595 	{
596 	    tmp_win->icon_x_loc = x;
597 	    tmp_win->icon_xl_loc = x -
598 		(tmp_win->icon_w_width - tmp_win->icon_p_width) / 2;
599 	    tmp_win->icon_y_loc = y;
600 	    XMoveWindow(dpy, tmp_win->icon_w, tmp_win->icon_xl_loc, y + tmp_win->icon_p_height);
601 
602 	    if (tmp_win->icon_pixmap_w != None)
603 		XMoveWindow(dpy, tmp_win->icon_pixmap_w, x, y);
604 
605 	    tmp_win->flags |= ICON_MOVED;
606 
607 	}
608 	else
609 	{
610 	    /* show the actual window */
611 	    DEC_ConfigureDecorations(scr, tmp_win, x, y, tmp_win->frame_width, tmp_win->frame_height, False);
612 	}
613     }
614     EnablePagerRedraw = True;
615     PAGER_Clear(scr);
616 }
617