Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wwindow.c
1
2 #include <X11/Xmd.h>
3
4 #include "WINGsP.h"
5
6 #include <X11/Xatom.h>
7
8 typedef struct W_Window {
9         W_Class widgetClass;
10         W_View *view;
11
12         struct W_Window *nextPtr;       /* next in the window list */
13
14         struct W_Window *owner;
15
16         char *title;
17
18         WMPixmap *miniImage;    /* miniwindow */
19         char *miniTitle;
20
21         char *wname;
22
23         WMSize resizeIncrement;
24         WMSize baseSize;
25         WMSize minSize;
26         WMSize maxSize;
27         WMPoint minAspect;
28         WMPoint maxAspect;
29
30         WMPoint upos;
31         WMPoint ppos;
32
33         WMAction *closeAction;
34         void *closeData;
35
36         int level;
37
38         struct {
39                 unsigned style:4;
40                 unsigned configured:1;
41                 unsigned documentEdited:1;
42
43                 unsigned setUPos:1;
44                 unsigned setPPos:1;
45                 unsigned setAspect:1;
46         } flags;
47 } _Window;
48
49 typedef struct {
50         CARD32 flags;
51         CARD32 window_style;
52         CARD32 window_level;
53         CARD32 reserved;
54         Pixmap miniaturize_pixmap;      /* pixmap for miniaturize button */
55         Pixmap close_pixmap;    /* pixmap for close button */
56         Pixmap miniaturize_mask;        /* miniaturize pixmap mask */
57         Pixmap close_mask;      /* close pixmap mask */
58         CARD32 extra_flags;
59 } GNUstepWMAttributes;
60
61 #define GSWindowStyleAttr       (1<<0)
62 #define GSWindowLevelAttr       (1<<1)
63 #define GSMiniaturizePixmapAttr (1<<3)
64 #define GSClosePixmapAttr       (1<<4)
65 #define GSMiniaturizeMaskAttr   (1<<5)
66 #define GSCloseMaskAttr         (1<<6)
67 #define GSExtraFlagsAttr        (1<<7)
68
69 /* extra flags */
70 #define GSDocumentEditedFlag    (1<<0)
71 #define GSNoApplicationIconFlag (1<<5)
72
73 #define WMFHideOtherApplications        10
74 #define WMFHideApplication              12
75
76 static void willResizeWindow(W_ViewDelegate *, WMView *, unsigned *, unsigned *);
77
78 struct W_ViewDelegate _WindowViewDelegate = {
79         NULL,
80         NULL,
81         NULL,
82         NULL,
83         willResizeWindow
84 };
85
86 #define DEFAULT_WIDTH   400
87 #define DEFAULT_HEIGHT  180
88 #define DEFAULT_TITLE   ""
89
90 static void destroyWindow(_Window * win);
91
92 static void handleEvents();
93
94 static void realizeWindow();
95
96 static void realizeObserver(void *self, WMNotification * not)
97 {
98         realizeWindow(self);
99 }
100
101 WMWindow *WMCreatePanelWithStyleForWindow(WMWindow * owner, char *name, int style)
102 {
103         WMWindow *win;
104
105         win = WMCreateWindowWithStyle(owner->view->screen, name, style);
106         win->owner = owner;
107
108         return win;
109 }
110
111 WMWindow *WMCreatePanelForWindow(WMWindow * owner, char *name)
112 {
113         return WMCreatePanelWithStyleForWindow(owner, name,
114                                                WMTitledWindowMask | WMClosableWindowMask | WMResizableWindowMask);
115 }
116
117 void WMChangePanelOwner(WMWindow * win, WMWindow * newOwner)
118 {
119         win->owner = newOwner;
120
121         if (win->view->flags.realized && newOwner) {
122                 XSetTransientForHint(win->view->screen->display, win->view->window, newOwner->view->window);
123         }
124 }
125
126 WMWindow *WMCreateWindow(WMScreen * screen, char *name)
127 {
128         return WMCreateWindowWithStyle(screen, name, WMTitledWindowMask
129                                        | WMClosableWindowMask
130                                        | WMMiniaturizableWindowMask | WMResizableWindowMask);
131 }
132
133 WMWindow *WMCreateWindowWithStyle(WMScreen * screen, char *name, int style)
134 {
135         _Window *win;
136
137         win = wmalloc(sizeof(_Window));
138         memset(win, 0, sizeof(_Window));
139
140         win->widgetClass = WC_Window;
141
142         win->view = W_CreateTopView(screen);
143         if (!win->view) {
144                 wfree(win);
145                 return NULL;
146         }
147         win->view->self = win;
148
149         win->view->delegate = &_WindowViewDelegate;
150
151         win->wname = wstrdup(name);
152
153         /* add to the window list of the screen (application) */
154         win->nextPtr = screen->windowList;
155         screen->windowList = win;
156
157         WMCreateEventHandler(win->view, ExposureMask | StructureNotifyMask
158                              | ClientMessageMask | FocusChangeMask, handleEvents, win);
159
160         W_ResizeView(win->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
161
162         WMAddNotificationObserver(realizeObserver, win, WMViewRealizedNotification, win->view);
163
164         win->flags.style = style;
165
166         win->level = WMNormalWindowLevel;
167
168         /* kluge. Find a better solution */
169         W_SetFocusOfTopLevel(win->view, win->view);
170
171         return win;
172 }
173
174 static void setWindowTitle(WMWindow * win, const char *title)
175 {
176         WMScreen *scr = win->view->screen;
177         XTextProperty property;
178         int result;
179
180         result = XmbTextListToTextProperty(scr->display, (char **)&title, 1, XStdICCTextStyle, &property);
181         if (result == XNoMemory || result == XLocaleNotSupported) {
182                 wwarning("window title conversion error... using STRING encoding");
183                 XStoreName(scr->display, win->view->window, title);
184         } else {
185                 XSetWMName(scr->display, win->view->window, &property);
186                 if (property.value)
187                         XFree(property.value);
188         }
189
190         XChangeProperty(scr->display, win->view->window,
191                         scr->netwmName, scr->utf8String, 8,
192                         PropModeReplace, (unsigned char *)title, strlen(title));
193 }
194
195 static void setMiniwindowTitle(WMWindow * win, const char *title)
196 {
197         WMScreen *scr = win->view->screen;
198         XTextProperty property;
199         int result;
200
201         result = XmbTextListToTextProperty(scr->display, (char **)&title, 1, XStdICCTextStyle, &property);
202         if (result == XNoMemory || result == XLocaleNotSupported) {
203                 wwarning("icon title conversion error..using STRING encoding");
204                 XSetIconName(scr->display, win->view->window, title);
205         } else {
206                 XSetWMIconName(scr->display, win->view->window, &property);
207                 if (property.value)
208                         XFree(property.value);
209         }
210
211         XChangeProperty(scr->display, win->view->window,
212                         scr->netwmIconName, scr->utf8String, 8,
213                         PropModeReplace, (unsigned char *)title, strlen(title));
214 }
215
216 static void setMiniwindow(WMWindow * win, RImage * image)
217 {
218         WMScreen *scr = win->view->screen;
219         CARD32 *data;
220         int x, y;
221         int o;
222
223         if (!image)
224                 return;
225
226         data = wmalloc((image->width * image->height + 2) * sizeof(CARD32));
227
228         o = 0;
229         data[o++] = image->width;
230         data[o++] = image->height;
231
232         for (y = 0; y < image->height; y++) {
233                 for (x = 0; x < image->width; x++) {
234                         CARD32 pixel;
235                         int offs = (x + y * image->width);
236
237                         if (image->format == RRGBFormat)
238                                 pixel =
239                                     image->data[offs * 3] << 16 | image->data[offs * 3 +
240                                                                               1] << 8 | image->data[offs * 3 + 2];
241                         else
242                                 pixel =
243                                     image->data[offs * 4] << 16 | image->data[offs * 4 +
244                                                                               1] << 8 | image->data[offs * 4 +
245                                                                                                     2] | image->
246                                     data[offs * 4 + 3] << 24;
247
248                         data[o++] = pixel;
249                 }
250         }
251
252         XChangeProperty(scr->display, win->view->window, scr->netwmIcon,
253                         XA_CARDINAL, 32, PropModeReplace,
254                         (unsigned char *)data, (image->width * image->height + 2));
255
256         wfree(data);
257 }
258
259 void WMSetWindowTitle(WMWindow * win, char *title)
260 {
261         if (win->title != NULL)
262                 wfree(win->title);
263         if (title != NULL)
264                 win->title = wstrdup(title);
265         else
266                 win->title = NULL;
267
268         if (win->view->flags.realized) {
269                 setWindowTitle(win, title);
270         }
271 }
272
273 void WMSetWindowCloseAction(WMWindow * win, WMAction * action, void *clientData)
274 {
275         Atom *atoms = NULL;
276         Atom *newAtoms;
277         int count;
278         WMScreen *scr = win->view->screen;
279
280         if (win->view->flags.realized) {
281                 if (action && !win->closeAction) {
282                         if (!XGetWMProtocols(scr->display, win->view->window, &atoms, &count)) {
283                                 count = 0;
284                         }
285                         newAtoms = wmalloc((count + 1) * sizeof(Atom));
286                         if (count > 0)
287                                 memcpy(newAtoms, atoms, count * sizeof(Atom));
288                         newAtoms[count++] = scr->deleteWindowAtom;
289                         XSetWMProtocols(scr->display, win->view->window, newAtoms, count);
290                         if (atoms)
291                                 XFree(atoms);
292                         wfree(newAtoms);
293                 } else if (!action && win->closeAction) {
294                         int i, ncount;
295
296                         if (XGetWMProtocols(scr->display, win->view->window, &atoms, &count) && count > 0) {
297                                 newAtoms = wmalloc((count - 1) * sizeof(Atom));
298                                 ncount = 0;
299                                 for (i = 0; i < count; i++) {
300                                         if (atoms[i] != scr->deleteWindowAtom) {
301                                                 newAtoms[i] = atoms[i];
302                                                 ncount++;
303                                         }
304                                 }
305                                 XSetWMProtocols(scr->display, win->view->window, newAtoms, ncount);
306                                 if (atoms)
307                                         XFree(atoms);
308                                 wfree(newAtoms);
309                         }
310                 }
311         }
312         win->closeAction = action;
313         win->closeData = clientData;
314 }
315
316 static void willResizeWindow(W_ViewDelegate * self, WMView * view, unsigned *width, unsigned *height)
317 {
318         WMWindow *win = (WMWindow *) view->self;
319
320         if (win->minSize.width > 0 && win->minSize.height > 0) {
321                 if (*width < win->minSize.width)
322                         *width = win->minSize.width;
323                 if (*height < win->minSize.height)
324                         *height = win->minSize.height;
325         }
326
327         if (win->maxSize.width > 0 && win->maxSize.height > 0) {
328                 if (*width > win->maxSize.width)
329                         *width = win->maxSize.width;
330                 if (*height > win->maxSize.height)
331                         *height = win->maxSize.height;
332         }
333 }
334
335 static void setSizeHints(WMWindow * win)
336 {
337         XSizeHints *hints;
338
339         hints = XAllocSizeHints();
340         if (!hints) {
341                 wwarning("could not allocate memory for window size hints");
342                 return;
343         }
344
345         hints->flags = 0;
346
347         if (win->flags.setPPos) {
348                 hints->flags |= PPosition;
349                 hints->x = win->ppos.x;
350                 hints->y = win->ppos.y;
351         }
352         if (win->flags.setUPos) {
353                 hints->flags |= USPosition;
354                 hints->x = win->upos.x;
355                 hints->y = win->upos.y;
356         }
357         if (win->minSize.width > 0 && win->minSize.height > 0) {
358                 hints->flags |= PMinSize;
359                 hints->min_width = win->minSize.width;
360                 hints->min_height = win->minSize.height;
361         }
362         if (win->maxSize.width > 0 && win->maxSize.height > 0) {
363                 hints->flags |= PMaxSize;
364                 hints->max_width = win->maxSize.width;
365                 hints->max_height = win->maxSize.height;
366         }
367         if (win->baseSize.width > 0 && win->baseSize.height > 0) {
368                 hints->flags |= PBaseSize;
369                 hints->base_width = win->baseSize.width;
370                 hints->base_height = win->baseSize.height;
371         }
372         if (win->resizeIncrement.width > 0 && win->resizeIncrement.height > 0) {
373                 hints->flags |= PResizeInc;
374                 hints->width_inc = win->resizeIncrement.width;
375                 hints->height_inc = win->resizeIncrement.height;
376         }
377         if (win->flags.setAspect) {
378                 hints->flags |= PAspect;
379                 hints->min_aspect.x = win->minAspect.x;
380                 hints->min_aspect.y = win->minAspect.y;
381                 hints->max_aspect.x = win->maxAspect.x;
382                 hints->max_aspect.y = win->maxAspect.y;
383         }
384
385         if (hints->flags) {
386                 XSetWMNormalHints(win->view->screen->display, win->view->window, hints);
387         }
388         XFree(hints);
389 }
390
391 static void writeGNUstepWMAttr(WMScreen * scr, Window window, GNUstepWMAttributes * attr)
392 {
393         unsigned long data[9];
394
395         /* handle idiot compilers where array of CARD32 != struct of CARD32 */
396         data[0] = attr->flags;
397         data[1] = attr->window_style;
398         data[2] = attr->window_level;
399         data[3] = 0;            /* reserved */
400         /* The X protocol says XIDs are 32bit */
401         data[4] = attr->miniaturize_pixmap;
402         data[5] = attr->close_pixmap;
403         data[6] = attr->miniaturize_mask;
404         data[7] = attr->close_mask;
405         data[8] = attr->extra_flags;
406         XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
407                         32, PropModeReplace, (unsigned char *)data, 9);
408 }
409
410 static void setWindowMakerHints(WMWindow * win)
411 {
412         GNUstepWMAttributes attribs;
413         WMScreen *scr = WMWidgetScreen(win);
414
415         memset(&attribs, 0, sizeof(GNUstepWMAttributes));
416         attribs.flags = GSWindowStyleAttr | GSWindowLevelAttr | GSExtraFlagsAttr;
417         attribs.window_style = win->flags.style;
418         attribs.window_level = win->level;
419         if (win->flags.documentEdited)
420                 attribs.extra_flags = GSDocumentEditedFlag;
421         else
422                 attribs.extra_flags = 0;
423
424         writeGNUstepWMAttr(scr, win->view->window, &attribs);
425 }
426
427 static void realizeWindow(WMWindow * win)
428 {
429         XWMHints *hints;
430         XClassHint *classHint;
431         WMScreen *scr = win->view->screen;
432         Atom atoms[4];
433         int count;
434
435         classHint = XAllocClassHint();
436         classHint->res_name = win->wname;
437         classHint->res_class = WMGetApplicationName();
438         XSetClassHint(scr->display, win->view->window, classHint);
439         XFree(classHint);
440
441         hints = XAllocWMHints();
442         hints->flags = 0;
443         if (!scr->aflags.simpleApplication) {
444                 hints->flags |= WindowGroupHint;
445                 hints->window_group = scr->groupLeader;
446         }
447         if (win->miniImage) {
448                 hints->flags |= IconPixmapHint;
449                 hints->icon_pixmap = WMGetPixmapXID(win->miniImage);
450                 hints->icon_mask = WMGetPixmapMaskXID(win->miniImage);
451                 if (hints->icon_mask != None) {
452                         hints->flags |= IconMaskHint;
453                 }
454         }
455         if (hints->flags != 0)
456                 XSetWMHints(scr->display, win->view->window, hints);
457         XFree(hints);
458
459         count = 0;
460         if (win->closeAction) {
461                 atoms[count++] = scr->deleteWindowAtom;
462         }
463
464         if (count > 0)
465                 XSetWMProtocols(scr->display, win->view->window, atoms, count);
466
467         if (win->title || win->miniTitle)
468                 XmbSetWMProperties(scr->display, win->view->window, win->title,
469                                    win->miniTitle, NULL, 0, NULL, NULL, NULL);
470
471         setWindowMakerHints(win);
472
473         setSizeHints(win);
474
475         if (win->owner) {
476                 XSetTransientForHint(scr->display, win->view->window, win->owner->view->window);
477         }
478
479         if (win->title)
480                 setWindowTitle(win, win->title);
481 }
482
483 void WMSetWindowAspectRatio(WMWindow * win, int minX, int minY, int maxX, int maxY)
484 {
485         win->flags.setAspect = 1;
486         win->minAspect.x = minX;
487         win->minAspect.y = minY;
488         win->maxAspect.x = maxX;
489         win->maxAspect.y = maxY;
490         if (win->view->flags.realized)
491                 setSizeHints(win);
492 }
493
494 void WMSetWindowInitialPosition(WMWindow * win, int x, int y)
495 {
496         win->flags.setPPos = 1;
497         win->ppos.x = x;
498         win->ppos.y = y;
499         if (win->view->flags.realized)
500                 setSizeHints(win);
501         WMMoveWidget(win, x, y);
502 }
503
504 void WMSetWindowUserPosition(WMWindow * win, int x, int y)
505 {
506         win->flags.setUPos = 1;
507         win->upos.x = x;
508         win->upos.y = y;
509         if (win->view->flags.realized)
510                 setSizeHints(win);
511         WMMoveWidget(win, x, y);
512 }
513
514 void WMSetWindowMinSize(WMWindow * win, unsigned width, unsigned height)
515 {
516         win->minSize.width = width;
517         win->minSize.height = height;
518         if (win->view->flags.realized)
519                 setSizeHints(win);
520 }
521
522 void WMSetWindowMaxSize(WMWindow * win, unsigned width, unsigned height)
523 {
524         win->maxSize.width = width;
525         win->maxSize.height = height;
526         if (win->view->flags.realized)
527                 setSizeHints(win);
528 }
529
530 void WMSetWindowBaseSize(WMWindow * win, unsigned width, unsigned height)
531 {
532         /* TODO: validate sizes */
533         win->baseSize.width = width;
534         win->baseSize.height = height;
535         if (win->view->flags.realized)
536                 setSizeHints(win);
537 }
538
539 void WMSetWindowResizeIncrements(WMWindow * win, unsigned wIncr, unsigned hIncr)
540 {
541         win->resizeIncrement.width = wIncr;
542         win->resizeIncrement.height = hIncr;
543         if (win->view->flags.realized)
544                 setSizeHints(win);
545 }
546
547 void WMSetWindowLevel(WMWindow * win, int level)
548 {
549         win->level = level;
550         if (win->view->flags.realized)
551                 setWindowMakerHints(win);
552 }
553
554 void WMSetWindowDocumentEdited(WMWindow * win, Bool flag)
555 {
556         flag = ((flag == 0) ? 0 : 1);
557         if (win->flags.documentEdited != flag) {
558                 win->flags.documentEdited = flag;
559                 if (win->view->flags.realized)
560                         setWindowMakerHints(win);
561         }
562 }
563
564 void WMSetWindowMiniwindowImage(WMWindow * win, RImage * image)
565 {
566         if (win->view->flags.realized)
567                 setMiniwindow(win, image);
568 }
569
570 void WMSetWindowMiniwindowPixmap(WMWindow * win, WMPixmap * pixmap)
571 {
572         if ((win->miniImage && !pixmap) || (!win->miniImage && pixmap)) {
573                 if (win->miniImage)
574                         WMReleasePixmap(win->miniImage);
575
576                 if (pixmap)
577                         win->miniImage = WMRetainPixmap(pixmap);
578                 else
579                         win->miniImage = NULL;
580
581                 if (win->view->flags.realized) {
582                         XWMHints *hints;
583
584                         hints = XGetWMHints(win->view->screen->display, win->view->window);
585                         if (!hints) {
586                                 hints = XAllocWMHints();
587                                 if (!hints) {
588                                         wwarning("could not allocate memory for WM hints");
589                                         return;
590                                 }
591                                 hints->flags = 0;
592                         }
593                         if (pixmap) {
594                                 hints->flags |= IconPixmapHint;
595                                 hints->icon_pixmap = WMGetPixmapXID(pixmap);
596                                 hints->icon_mask = WMGetPixmapMaskXID(pixmap);
597                                 if (hints->icon_mask != None) {
598                                         hints->flags |= IconMaskHint;
599                                 }
600                         }
601                         XSetWMHints(win->view->screen->display, win->view->window, hints);
602                         XFree(hints);
603                 }
604         }
605 }
606
607 void WMSetWindowMiniwindowTitle(WMWindow * win, char *title)
608 {
609         if ((win->miniTitle && !title) || (!win->miniTitle && title)
610             || (title && win->miniTitle && strcoll(title, win->miniTitle) != 0)) {
611                 if (win->miniTitle)
612                         wfree(win->miniTitle);
613
614                 if (title)
615                         win->miniTitle = wstrdup(title);
616                 else
617                         win->miniTitle = NULL;
618
619                 if (win->view->flags.realized) {
620                         setMiniwindowTitle(win, title);
621                 }
622         }
623 }
624
625 void WMCloseWindow(WMWindow * win)
626 {
627         WMUnmapWidget(win);
628         /* withdraw the window */
629         if (win->view->flags.realized)
630                 XWithdrawWindow(win->view->screen->display, win->view->window, win->view->screen->screen);
631 }
632
633 static void handleEvents(XEvent * event, void *clientData)
634 {
635         _Window *win = (_Window *) clientData;
636         W_View *view = win->view;
637
638         switch (event->type) {
639         case ClientMessage:
640                 if (event->xclient.message_type == win->view->screen->protocolsAtom
641                     && event->xclient.format == 32
642                     && event->xclient.data.l[0] == win->view->screen->deleteWindowAtom) {
643
644                         if (win->closeAction) {
645                                 (*win->closeAction) (win, win->closeData);
646                         }
647                 }
648                 break;
649                 /*
650                  * was causing windows to ignore commands like closeWindow
651                  * after the windows is iconized/restored or a workspace change
652                  * if this is really needed, put the MapNotify portion too and
653                  * fix the restack bug in wmaker
654                  case UnmapNotify:
655                  WMUnmapWidget(win);
656                  break;
657                  *
658                  case MapNotify:
659                  WMMapWidget(win);
660                  break;
661
662                  */
663         case DestroyNotify:
664                 destroyWindow(win);
665                 break;
666
667         case ConfigureNotify:
668                 if (event->xconfigure.width != view->size.width || event->xconfigure.height != view->size.height) {
669
670                         view->size.width = event->xconfigure.width;
671                         view->size.height = event->xconfigure.height;
672
673                         if (view->flags.notifySizeChanged) {
674                                 WMPostNotificationName(WMViewSizeDidChangeNotification, view, NULL);
675                         }
676                 }
677                 if (event->xconfigure.x != view->pos.x || event->xconfigure.y != view->pos.y) {
678
679                         if (event->xconfigure.send_event) {
680                                 view->pos.x = event->xconfigure.x;
681                                 view->pos.y = event->xconfigure.y;
682                         } else {
683                                 Window foo;
684
685                                 XTranslateCoordinates(view->screen->display,
686                                                       view->window, view->screen->rootWin,
687                                                       event->xconfigure.x, event->xconfigure.y,
688                                                       &view->pos.x, &view->pos.y, &foo);
689                         }
690                 }
691                 break;
692         }
693 }
694
695 static void destroyWindow(_Window * win)
696 {
697         WMScreen *scr = win->view->screen;
698
699         WMRemoveNotificationObserver(win);
700
701         if (scr->windowList == win) {
702                 scr->windowList = scr->windowList->nextPtr;
703         } else {
704                 WMWindow *ptr;
705                 ptr = scr->windowList;
706
707                 if (ptr) {
708                         while (ptr->nextPtr) {
709                                 if (ptr->nextPtr == win) {
710                                         ptr->nextPtr = ptr->nextPtr->nextPtr;
711                                         break;
712                                 }
713                                 ptr = ptr->nextPtr;
714                         }
715                 }
716         }
717
718         if (win->title) {
719                 wfree(win->title);
720         }
721
722         if (win->miniTitle) {
723                 wfree(win->miniTitle);
724         }
725
726         if (win->miniImage) {
727                 WMReleasePixmap(win->miniImage);
728         }
729
730         if (win->wname)
731                 wfree(win->wname);
732
733         wfree(win);
734 }