1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/settings.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Modified by: Mart Raudsepp (GetMetric)
6 // Id: $Id: settings.cpp 67017 2011-02-25 09:37:28Z JS $
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #include "wx/settings.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/cmndata.h"
18 #include "wx/toplevel.h"
19 #endif
20
21 #include "wx/fontutil.h"
22
23 #include <gtk/gtkversion.h>
24 #if GTK_CHECK_VERSION(2, 9, 0)
25 // gtk_object_sink
26 #undef GTK_DISABLE_DEPRECATED
27 #endif
28 #include <gtk/gtk.h>
29 #include <gdk/gdkx.h>
30
31 #include <X11/Xatom.h>
32
33 // ----------------------------------------------------------------------------
34 // wxSystemObjects
35 // ----------------------------------------------------------------------------
36
37 struct wxSystemObjects
38 {
39 wxColour m_colBtnFace,
40 m_colBtnShadow,
41 m_colBtnHighlight,
42 m_colHighlight,
43 m_colHighlightText,
44 m_colListBox,
45 m_colWindow,
46 m_colBtnText,
47 m_colMenuItemHighlight,
48 m_colTooltip,
49 m_colTooltipText,
50 m_colMenubarBg,
51 m_colListBoxText,
52 m_colListBoxUnfocusedText;
53
54 wxFont m_fontSystem;
55 };
56
57 static wxSystemObjects gs_objects;
58
wxClearGtkSystemObjects()59 void wxClearGtkSystemObjects()
60 {
61 gs_objects.m_colBtnFace = wxColour();
62 gs_objects.m_colBtnShadow = wxColour();
63 gs_objects.m_colBtnHighlight = wxColour();
64 gs_objects.m_colHighlightText = wxColour();
65 gs_objects.m_colListBox = wxColour();
66 gs_objects.m_colWindow = wxColour();
67 gs_objects.m_colBtnText = wxColour();
68 gs_objects.m_colMenuItemHighlight = wxColour();
69 gs_objects.m_colTooltip = wxColour();
70 gs_objects.m_colTooltipText = wxColour();
71 gs_objects.m_colMenubarBg = wxColour();
72 gs_objects.m_fontSystem = wxNullFont;
73 gs_objects.m_colListBoxText = wxColour();
74 }
75
76 // ----------------------------------------------------------------------------
77 // wxSystemSettings implementation
78 // ----------------------------------------------------------------------------
79
80 // kind of widget to use in GetColourFromGTKWidget
81 enum wxGtkWidgetType
82 {
83 wxGTK_BUTTON,
84 wxGTK_LIST,
85 wxGTK_MENUITEM,
86 wxGTK_TEXTCTRL,
87 wxGTK_MENUBAR,
88 };
89
90 // the colour we need
91 enum wxGtkColourType
92 {
93 wxGTK_FG,
94 wxGTK_BG,
95 wxGTK_BASE,
96 wxGTK_TEXT
97 };
98
99 // wxSystemSettings::GetColour() helper: get the colours from a GTK+
100 // widget style, return true if we did get them
GetColourFromGTKWidget(GdkColor & gdkColor,wxGtkWidgetType type=wxGTK_BUTTON,GtkStateType state=GTK_STATE_NORMAL,wxGtkColourType colour=wxGTK_BG)101 static bool GetColourFromGTKWidget(GdkColor& gdkColor,
102 wxGtkWidgetType type = wxGTK_BUTTON,
103 GtkStateType state = GTK_STATE_NORMAL,
104 wxGtkColourType colour = wxGTK_BG)
105 {
106 GtkWidget *widget;
107 switch ( type )
108 {
109 default:
110 wxFAIL_MSG( _T("unexpected GTK widget type") );
111 // fall through
112
113 case wxGTK_BUTTON:
114 widget = gtk_button_new();
115 break;
116
117 case wxGTK_TEXTCTRL:
118 widget = gtk_text_view_new();
119 break;
120
121 case wxGTK_LIST:
122 widget = gtk_tree_view_new_with_model(
123 (GtkTreeModel*)gtk_list_store_new(1, G_TYPE_INT));
124 break;
125
126 case wxGTK_MENUITEM:
127 widget = gtk_menu_item_new();
128 break;
129
130 case wxGTK_MENUBAR:
131 widget = gtk_menu_bar_new();
132 break;
133 }
134
135 GtkStyle *def = gtk_rc_get_style( widget );
136 if ( !def )
137 def = gtk_widget_get_default_style();
138
139 const bool ok = def != NULL;
140 if (ok)
141 {
142 switch ( colour )
143 {
144 default:
145 wxFAIL_MSG( _T("unexpected GTK colour type") );
146 // fall through
147
148 case wxGTK_FG:
149 gdkColor = def->fg[state];
150 break;
151
152 case wxGTK_BG:
153 gdkColor = def->bg[state];
154 break;
155
156 case wxGTK_BASE:
157 gdkColor = def->base[state];
158 break;
159
160 case wxGTK_TEXT:
161 gdkColor = def->text[state];
162 break;
163 }
164 }
165
166 gtk_object_sink((GtkObject*)widget);
167
168 return ok;
169 }
170
GetTooltipColors()171 static void GetTooltipColors()
172 {
173 GtkWidget* widget = gtk_window_new(GTK_WINDOW_POPUP);
174 const char* name = "gtk-tooltip";
175 if (gtk_check_version(2, 11, 0))
176 name = "gtk-tooltips";
177 gtk_widget_set_name(widget, name);
178 gtk_widget_ensure_style(widget);
179
180 GdkColor c = widget->style->bg[GTK_STATE_NORMAL];
181 gs_objects.m_colTooltip = wxColor(c);
182 c = widget->style->fg[GTK_STATE_NORMAL];
183 gs_objects.m_colTooltipText = wxColor(c);
184
185 gtk_widget_destroy(widget);
186 }
187
GetColour(wxSystemColour index)188 wxColour wxSystemSettingsNative::GetColour( wxSystemColour index )
189 {
190 wxColor color;
191 GdkColor gdkColor;
192 switch (index)
193 {
194 case wxSYS_COLOUR_SCROLLBAR:
195 case wxSYS_COLOUR_BACKGROUND:
196 case wxSYS_COLOUR_INACTIVECAPTION:
197 case wxSYS_COLOUR_MENU:
198 case wxSYS_COLOUR_WINDOWFRAME:
199 case wxSYS_COLOUR_ACTIVEBORDER:
200 case wxSYS_COLOUR_INACTIVEBORDER:
201 case wxSYS_COLOUR_BTNFACE:
202 case wxSYS_COLOUR_3DLIGHT:
203 if (!gs_objects.m_colBtnFace.Ok())
204 {
205 gdkColor.red =
206 gdkColor.green = 0;
207 gdkColor.blue = 0x9c40;
208 GetColourFromGTKWidget(gdkColor);
209 gs_objects.m_colBtnFace = wxColor(gdkColor);
210 }
211 color = gs_objects.m_colBtnFace;
212 break;
213
214 case wxSYS_COLOUR_WINDOW:
215 if (!gs_objects.m_colWindow.Ok())
216 {
217 gdkColor.red =
218 gdkColor.green =
219 gdkColor.blue = 0xFFFF;
220 GetColourFromGTKWidget(gdkColor, wxGTK_TEXTCTRL, GTK_STATE_NORMAL, wxGTK_BASE);
221 gs_objects.m_colWindow = wxColor(gdkColor);
222 }
223 color = gs_objects.m_colWindow;
224 break;
225
226
227 case wxSYS_COLOUR_MENUBAR:
228 if (!gs_objects.m_colMenubarBg.Ok())
229 {
230 gdkColor.red =
231 gdkColor.green = 0;
232 gdkColor.blue = 0x9c40;
233 GetColourFromGTKWidget(gdkColor,wxGTK_MENUBAR);
234 gs_objects.m_colMenubarBg = wxColor(gdkColor);
235 }
236 color = gs_objects.m_colMenubarBg;
237 break;
238
239 case wxSYS_COLOUR_3DDKSHADOW:
240 color = *wxBLACK;
241 break;
242
243 case wxSYS_COLOUR_GRAYTEXT:
244 case wxSYS_COLOUR_BTNSHADOW:
245 //case wxSYS_COLOUR_3DSHADOW:
246 if (!gs_objects.m_colBtnShadow.Ok())
247 {
248 wxColour faceColour(GetColour(wxSYS_COLOUR_3DFACE));
249 gs_objects.m_colBtnShadow =
250 wxColour((unsigned char) (faceColour.Red() * 2 / 3),
251 (unsigned char) (faceColour.Green() * 2 / 3),
252 (unsigned char) (faceColour.Blue() * 2 / 3));
253 }
254 color = gs_objects.m_colBtnShadow;
255 break;
256
257 case wxSYS_COLOUR_3DHIGHLIGHT:
258 //case wxSYS_COLOUR_BTNHIGHLIGHT:
259 color = *wxWHITE;
260 break;
261
262 case wxSYS_COLOUR_HIGHLIGHT:
263 if (!gs_objects.m_colHighlight.Ok())
264 {
265 gdkColor.red =
266 gdkColor.green = 0;
267 gdkColor.blue = 0x9c40;
268 GetColourFromGTKWidget(
269 gdkColor, wxGTK_BUTTON, GTK_STATE_SELECTED);
270 gs_objects.m_colHighlight = wxColour(gdkColor);
271 }
272 color = gs_objects.m_colHighlight;
273 break;
274
275 case wxSYS_COLOUR_LISTBOX:
276 if (!gs_objects.m_colListBox.Ok())
277 {
278 if ( GetColourFromGTKWidget(gdkColor,
279 wxGTK_LIST,
280 GTK_STATE_NORMAL,
281 wxGTK_BASE) )
282 {
283 gs_objects.m_colListBox = wxColour(gdkColor);
284 }
285 else
286 {
287 gs_objects.m_colListBox = *wxWHITE;
288 }
289 }
290 color = gs_objects.m_colListBox;
291 break;
292
293 case wxSYS_COLOUR_LISTBOXTEXT:
294 if (!gs_objects.m_colListBoxText.Ok())
295 {
296 if ( GetColourFromGTKWidget(gdkColor,
297 wxGTK_LIST,
298 GTK_STATE_NORMAL,
299 wxGTK_TEXT) )
300 {
301 gs_objects.m_colListBoxText = wxColour(gdkColor);
302 }
303 else
304 {
305 gs_objects.m_colListBoxText = GetColour(wxSYS_COLOUR_WINDOWTEXT);
306 }
307 }
308 color = gs_objects.m_colListBoxText;
309 break;
310
311 case wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT:
312 {
313 // This is for the text in a list control (or tree) when the
314 // item is selected, but not focused
315 if (!gs_objects.m_colListBoxUnfocusedText.Ok())
316 {
317 if (GetColourFromGTKWidget(gdkColor, wxGTK_LIST, GTK_STATE_ACTIVE, wxGTK_TEXT))
318 gs_objects.m_colListBoxUnfocusedText = wxColour(gdkColor);
319 else
320 gs_objects.m_colListBoxUnfocusedText = GetColour(wxSYS_COLOUR_WINDOWTEXT);
321 }
322 color = gs_objects.m_colListBoxUnfocusedText;
323 break;
324 }
325 case wxSYS_COLOUR_MENUTEXT:
326 case wxSYS_COLOUR_WINDOWTEXT:
327 case wxSYS_COLOUR_CAPTIONTEXT:
328 case wxSYS_COLOUR_INACTIVECAPTIONTEXT:
329 case wxSYS_COLOUR_BTNTEXT:
330 if (!gs_objects.m_colBtnText.Ok())
331 {
332 gdkColor.red =
333 gdkColor.green =
334 gdkColor.blue = 0;
335 GetColourFromGTKWidget(
336 gdkColor, wxGTK_BUTTON, GTK_STATE_NORMAL, wxGTK_FG);
337 gs_objects.m_colBtnText = wxColour(gdkColor);
338 }
339 color = gs_objects.m_colBtnText;
340 break;
341
342 case wxSYS_COLOUR_INFOBK:
343 if (!gs_objects.m_colTooltip.Ok()) {
344 GetTooltipColors();
345 }
346 color = gs_objects.m_colTooltip;
347 break;
348
349 case wxSYS_COLOUR_INFOTEXT:
350 if (!gs_objects.m_colTooltipText.Ok()) {
351 GetTooltipColors();
352 }
353 color = gs_objects.m_colTooltipText;
354 break;
355
356 case wxSYS_COLOUR_HIGHLIGHTTEXT:
357 if (!gs_objects.m_colHighlightText.Ok())
358 {
359 gdkColor.red =
360 gdkColor.green =
361 gdkColor.blue = 0;
362 GetColourFromGTKWidget(
363 gdkColor, wxGTK_BUTTON, GTK_STATE_SELECTED, wxGTK_FG);
364 gs_objects.m_colHighlightText = wxColour(gdkColor);
365 }
366 color = gs_objects.m_colHighlightText;
367 break;
368
369 case wxSYS_COLOUR_APPWORKSPACE:
370 color = *wxWHITE; // ?
371 break;
372
373 case wxSYS_COLOUR_ACTIVECAPTION:
374 case wxSYS_COLOUR_MENUHILIGHT:
375 if (!gs_objects.m_colMenuItemHighlight.Ok())
376 {
377 gdkColor.red =
378 gdkColor.green =
379 gdkColor.blue = 0;
380 GetColourFromGTKWidget(
381 gdkColor, wxGTK_MENUITEM, GTK_STATE_SELECTED, wxGTK_BG);
382 gs_objects.m_colMenuItemHighlight = wxColour(gdkColor);
383 }
384 color = gs_objects.m_colMenuItemHighlight;
385 break;
386
387 case wxSYS_COLOUR_HOTLIGHT:
388 case wxSYS_COLOUR_GRADIENTACTIVECAPTION:
389 case wxSYS_COLOUR_GRADIENTINACTIVECAPTION:
390 // TODO
391 color = *wxBLACK;
392 break;
393
394 case wxSYS_COLOUR_MAX:
395 default:
396 wxFAIL_MSG( _T("unknown system colour index") );
397 color = *wxWHITE;
398 break;
399 }
400
401 return color;
402 }
403
GetFont(wxSystemFont index)404 wxFont wxSystemSettingsNative::GetFont( wxSystemFont index )
405 {
406 wxFont font;
407 switch (index)
408 {
409 case wxSYS_OEM_FIXED_FONT:
410 case wxSYS_ANSI_FIXED_FONT:
411 case wxSYS_SYSTEM_FIXED_FONT:
412 font = *wxNORMAL_FONT;
413 break;
414
415 case wxSYS_ANSI_VAR_FONT:
416 case wxSYS_SYSTEM_FONT:
417 case wxSYS_DEVICE_DEFAULT_FONT:
418 case wxSYS_DEFAULT_GUI_FONT:
419 if (!gs_objects.m_fontSystem.Ok())
420 {
421 GtkWidget *widget = gtk_button_new();
422 GtkStyle *def = gtk_rc_get_style( widget );
423 if ( !def || !def->font_desc )
424 def = gtk_widget_get_default_style();
425 if ( def && def->font_desc )
426 {
427 wxNativeFontInfo info;
428 info.description =
429 pango_font_description_copy(def->font_desc);
430 gs_objects.m_fontSystem = wxFont(info);
431 }
432 else
433 {
434 GtkSettings *settings = gtk_settings_get_default();
435 gchar *font_name = NULL;
436 g_object_get ( settings,
437 "gtk-font-name",
438 &font_name,
439 NULL);
440 if (!font_name)
441 gs_objects.m_fontSystem = wxFont( 12, wxSWISS, wxNORMAL, wxNORMAL );
442 else
443 gs_objects.m_fontSystem = wxFont(wxString::FromAscii(font_name));
444 g_free (font_name);
445 }
446 gtk_object_sink((GtkObject*)widget);
447 }
448 font = gs_objects.m_fontSystem;
449 break;
450
451 default:
452 break;
453 }
454 return font;
455 }
456
wxXGetWindowProperty(GdkWindow * window,Atom & type,int & format,gulong & nitems,guchar * & data)457 static bool wxXGetWindowProperty(GdkWindow* window, Atom& type, int& format, gulong& nitems, guchar*& data)
458 {
459 bool success = false;
460 #if GTK_CHECK_VERSION(2, 2, 0)
461 if (gtk_check_version(2, 2, 0) == NULL)
462 {
463 gulong bytes_after;
464 success = XGetWindowProperty(
465 GDK_DISPLAY_XDISPLAY(gdk_drawable_get_display(window)),
466 GDK_WINDOW_XWINDOW(window),
467 gdk_x11_get_xatom_by_name_for_display(
468 gdk_drawable_get_display(window),
469 "_NET_FRAME_EXTENTS"),
470 0, // left, right, top, bottom, CARDINAL[4]/32
471 G_MAXLONG, // size of long
472 false, // do not delete property
473 XA_CARDINAL, // 32 bit
474 &type, &format, &nitems, &bytes_after, &data
475 ) == Success;
476 }
477 #endif
478 return success;
479 }
480
GetMetric(wxSystemMetric index,wxWindow * win)481 int wxSystemSettingsNative::GetMetric( wxSystemMetric index, wxWindow* win )
482 {
483 guchar *data = NULL;
484 Atom type;
485 int format;
486 gulong nitems;
487 GdkWindow *window = NULL;
488 if(win && GTK_WIDGET_REALIZED(win->GetHandle()))
489 window = win->GetHandle()->window;
490
491 switch (index)
492 {
493 case wxSYS_BORDER_X:
494 case wxSYS_BORDER_Y:
495 case wxSYS_EDGE_X:
496 case wxSYS_EDGE_Y:
497 case wxSYS_FRAMESIZE_X:
498 case wxSYS_FRAMESIZE_Y:
499 // If a window is specified/realized, and it is a toplevel window, we can query from wm.
500 // The returned border thickness is outside the client area in that case.
501 if (window)
502 {
503 wxTopLevelWindow *tlw = wxDynamicCast(win, wxTopLevelWindow);
504 if (!tlw)
505 return -1; // not a tlw, not sure how to approach
506 else
507 {
508 // Check if wm supports frame extents - we can't know
509 // the border widths if it does not.
510 #if GTK_CHECK_VERSION(2,2,0)
511 if (!gtk_check_version(2,2,0))
512 {
513 if (!gdk_x11_screen_supports_net_wm_hint(
514 gdk_drawable_get_screen(window),
515 gdk_atom_intern("_NET_FRAME_EXTENTS", false) ) )
516 return -1;
517 }
518 else
519 #endif
520 {
521 if (!gdk_net_wm_supports(gdk_atom_intern("_NET_FRAME_EXTENTS", false)))
522 return -1;
523 }
524
525 // Get the frame extents from the windowmanager.
526 // In most cases the top extent is the titlebar, so we use the bottom extent
527 // for the heights.
528 if (wxXGetWindowProperty(window, type, format, nitems, data))
529 {
530 int border_return = -1;
531
532 if ((type == XA_CARDINAL) && (format == 32) && (nitems >= 4) && (data))
533 {
534 switch(index)
535 {
536 case wxSYS_BORDER_X:
537 case wxSYS_EDGE_X:
538 case wxSYS_FRAMESIZE_X:
539 border_return = ((long*)data)[1]; // width of right extent
540 break;
541 default:
542 border_return = ((long*)data)[3]; // height of bottom extent
543 break;
544 }
545 }
546
547 if (data)
548 XFree(data);
549
550 return border_return;
551 }
552 }
553 }
554
555 return -1; // no window specified
556
557 case wxSYS_CURSOR_X:
558 case wxSYS_CURSOR_Y:
559 #ifdef __WXGTK24__
560 if (!gtk_check_version(2,4,0))
561 {
562 if (window)
563 return gdk_display_get_default_cursor_size(gdk_drawable_get_display(window));
564 else
565 return gdk_display_get_default_cursor_size(gdk_display_get_default());
566 }
567 else
568 #endif
569 return 16;
570
571 case wxSYS_DCLICK_X:
572 case wxSYS_DCLICK_Y:
573 gint dclick_distance;
574 #if GTK_CHECK_VERSION(2,2,0)
575 if (window && !gtk_check_version(2,2,0))
576 g_object_get(gtk_settings_get_for_screen(gdk_drawable_get_screen(window)),
577 "gtk-double-click-distance", &dclick_distance, NULL);
578 else
579 #endif
580 g_object_get(gtk_settings_get_default(),
581 "gtk-double-click-distance", &dclick_distance, NULL);
582
583 return dclick_distance * 2;
584
585 case wxSYS_DRAG_X:
586 case wxSYS_DRAG_Y:
587 gint drag_threshold;
588 #if GTK_CHECK_VERSION(2,2,0)
589 if (window && !gtk_check_version(2,2,0))
590 {
591 g_object_get(
592 gtk_settings_get_for_screen(gdk_drawable_get_screen(window)),
593 "gtk-dnd-drag-threshold",
594 &drag_threshold, NULL);
595 }
596 else
597 #endif
598 {
599 g_object_get(gtk_settings_get_default(),
600 "gtk-dnd-drag-threshold", &drag_threshold, NULL);
601 }
602
603 // The correct thing here would be to double the value
604 // since that is what the API wants. But the values
605 // are much bigger under GNOME than under Windows and
606 // just seem to much in many cases to be useful.
607 // drag_threshold *= 2;
608
609 return drag_threshold;
610
611 // MBN: ditto for icons
612 case wxSYS_ICON_X: return 32;
613 case wxSYS_ICON_Y: return 32;
614
615 case wxSYS_SCREEN_X:
616 #if GTK_CHECK_VERSION(2,2,0)
617 if (window && !gtk_check_version(2,2,0))
618 return gdk_screen_get_width(gdk_drawable_get_screen(window));
619 else
620 #endif
621 return gdk_screen_width();
622
623 case wxSYS_SCREEN_Y:
624 #if GTK_CHECK_VERSION(2,2,0)
625 if (window && !gtk_check_version(2,2,0))
626 return gdk_screen_get_height(gdk_drawable_get_screen(window));
627 else
628 #endif
629 return gdk_screen_height();
630
631 case wxSYS_HSCROLL_Y: return 15;
632 case wxSYS_VSCROLL_X: return 15;
633
634 case wxSYS_CAPTION_Y:
635 if (!window)
636 // No realized window specified, and no implementation for that case yet.
637 return -1;
638
639 // Check if wm supports frame extents - we can't know the caption height if it does not.
640 #if GTK_CHECK_VERSION(2,2,0)
641 if (!gtk_check_version(2,2,0))
642 {
643 if (!gdk_x11_screen_supports_net_wm_hint(
644 gdk_drawable_get_screen(window),
645 gdk_atom_intern("_NET_FRAME_EXTENTS", false) ) )
646 return -1;
647 }
648 else
649 #endif
650 {
651 if (!gdk_net_wm_supports(gdk_atom_intern("_NET_FRAME_EXTENTS", false)))
652 return -1;
653 }
654
655 wxASSERT_MSG( wxDynamicCast(win, wxTopLevelWindow),
656 wxT("Asking for caption height of a non toplevel window") );
657
658 // Get the height of the top windowmanager border.
659 // This is the titlebar in most cases. The titlebar might be elsewhere, and
660 // we could check which is the thickest wm border to decide on which side the
661 // titlebar is, but this might lead to interesting behaviours in used code.
662 // Reconsider when we have a way to report to the user on which side it is.
663 if (wxXGetWindowProperty(window, type, format, nitems, data))
664 {
665 int caption_height = -1;
666
667 if ((type == XA_CARDINAL) && (format == 32) && (nitems >= 3) && (data))
668 {
669 caption_height = ((long*)data)[2]; // top frame extent
670 }
671
672 if (data)
673 XFree(data);
674
675 return caption_height;
676 }
677
678 // Try a default approach without a window pointer, if possible
679 // ...
680
681 return -1;
682
683 case wxSYS_PENWINDOWS_PRESENT:
684 // No MS Windows for Pen computing extension available in X11 based gtk+.
685 return 0;
686
687 default:
688 return -1; // metric is unknown
689 }
690 }
691
HasFeature(wxSystemFeature index)692 bool wxSystemSettingsNative::HasFeature(wxSystemFeature index)
693 {
694 switch (index)
695 {
696 case wxSYS_CAN_ICONIZE_FRAME:
697 return false;
698
699 case wxSYS_CAN_DRAW_FRAME_DECORATIONS:
700 return true;
701
702 default:
703 return false;
704 }
705 }
706