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