]> git.saurik.com Git - wxWidgets.git/blob - src/generic/splitter.cpp
wxSetCursor() bug with splitters corrected
[wxWidgets.git] / src / generic / splitter.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: splitter.cpp
3 // Purpose: wxSplitterWindow implementation
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "splitter.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/wx.h"
25 #endif
26
27 #include <math.h>
28 #include <stdlib.h>
29
30 #include "wx/string.h"
31 #include "wx/splitter.h"
32 #include "wx/dcscreen.h"
33
34 #if !USE_SHARED_LIBRARY
35 IMPLEMENT_DYNAMIC_CLASS(wxSplitterWindow, wxWindow)
36
37 BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
38 EVT_PAINT(wxSplitterWindow::OnPaint)
39 EVT_SIZE(wxSplitterWindow::OnSize)
40 EVT_MOUSE_EVENTS(wxSplitterWindow::OnMouseEvent)
41 END_EVENT_TABLE()
42 #endif
43
44 wxSplitterWindow::wxSplitterWindow()
45 {
46 m_splitMode = wxSPLIT_VERTICAL;
47 m_windowOne = (wxWindow *) NULL;
48 m_windowTwo = (wxWindow *) NULL;
49 m_dragMode = wxSPLIT_DRAG_NONE;
50 m_oldX = 0;
51 m_oldY = 0;
52 m_firstX = 0;
53 m_firstY = 0;
54 m_sashSize = 7;
55 m_borderSize = 2;
56 m_sashPosition = 0;
57 m_sashCursorWE = (wxCursor *) NULL;
58 m_sashCursorNS = (wxCursor *) NULL;
59 m_sashTrackerPen = (wxPen *) NULL;
60 m_lightShadowPen = (wxPen *) NULL;
61 m_mediumShadowPen = (wxPen *) NULL;
62 m_darkShadowPen = (wxPen *) NULL;
63 m_faceBrush = (wxBrush *) NULL;
64 m_facePen = (wxPen *) NULL;
65 m_hilightPen = (wxPen *) NULL;
66 m_minimumPaneSize = 0;
67 }
68
69 wxSplitterWindow::wxSplitterWindow(wxWindow *parent, wxWindowID id,
70 const wxPoint& pos,
71 const wxSize& size,
72 long style,
73 const wxString& name)
74 : wxWindow(parent, id, pos, size, style, name)
75 {
76 m_splitMode = wxSPLIT_VERTICAL;
77 m_windowOne = (wxWindow *) NULL;
78 m_windowTwo = (wxWindow *) NULL;
79 m_dragMode = wxSPLIT_DRAG_NONE;
80 m_oldX = 0;
81 m_oldY = 0;
82 m_firstX = 0;
83 m_firstY = 0;
84 m_sashSize = 7;
85 m_borderSize = 2;
86 m_sashPosition = 0;
87 m_minimumPaneSize = 0;
88 m_sashCursorWE = new wxCursor(wxCURSOR_SIZEWE);
89 m_sashCursorNS = new wxCursor(wxCURSOR_SIZENS);
90 m_sashTrackerPen = new wxPen(*wxBLACK, 2, wxSOLID);
91 m_lightShadowPen = (wxPen *) NULL;
92 m_mediumShadowPen = (wxPen *) NULL;
93 m_darkShadowPen = (wxPen *) NULL;
94 m_faceBrush = (wxBrush *) NULL;
95 m_facePen = (wxPen *) NULL;
96 m_hilightPen = (wxPen *) NULL;
97
98 if ( style & wxSP_3D )
99 {
100 m_borderSize = 2;
101 m_sashSize = 7;
102 }
103 else if ( style & wxSP_BORDER )
104 {
105 m_borderSize = 1;
106 m_sashSize = 3;
107 }
108 else
109 {
110 m_borderSize = 0;
111 m_sashSize = 3;
112 }
113
114 // Eventually, we'll respond to colour change messages
115 InitColours();
116
117 // For debugging purposes, to see the background.
118 // SetBackground(wxBLUE_BRUSH);
119 }
120
121 wxSplitterWindow::~wxSplitterWindow()
122 {
123 delete m_sashCursorWE;
124 delete m_sashCursorNS;
125 delete m_sashTrackerPen;
126 delete m_lightShadowPen;
127 delete m_darkShadowPen;
128 delete m_mediumShadowPen;
129 delete m_hilightPen;
130 delete m_facePen;
131 delete m_faceBrush;
132 }
133
134 void wxSplitterWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
135 {
136 wxPaintDC dc(this);
137
138 if ( m_borderSize > 0 )
139 DrawBorders(dc);
140 DrawSash(dc);
141 }
142
143 void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
144 {
145 long x, y;
146 event.Position(&x, &y);
147
148 // reset the cursor
149 SetCursor(wxCursor());
150
151 if (event.LeftDown())
152 {
153 if ( SashHitTest(x, y) )
154 {
155 CaptureMouse();
156
157 m_dragMode = wxSPLIT_DRAG_DRAGGING;
158
159 DrawSashTracker(x, y);
160 m_oldX = x;
161 m_oldY = y;
162 return;
163 }
164 }
165 else if (event.LeftUp() && m_dragMode == wxSPLIT_DRAG_DRAGGING)
166 {
167 // We can stop dragging now and see what we've got.
168 m_dragMode = wxSPLIT_DRAG_NONE;
169 ReleaseMouse();
170
171 // Erase old tracker
172 DrawSashTracker(m_oldX, m_oldY);
173
174 int w, h;
175 GetClientSize(&w, &h);
176 if ( m_splitMode == wxSPLIT_VERTICAL )
177 {
178 if ( !OnSashPositionChange(x) )
179 return;
180
181 if ( x <= 4 )
182 {
183 // We remove the first window from the view
184 wxWindow *removedWindow = m_windowOne;
185 m_windowOne = m_windowTwo;
186 m_windowTwo = (wxWindow *) NULL;
187
188 OnUnsplit(removedWindow);
189 m_sashPosition = 0;
190 }
191 else if ( x >= (w - 4) )
192 {
193 // We remove the second window from the view
194 wxWindow *removedWindow = m_windowTwo;
195 m_windowTwo = (wxWindow *) NULL;
196 OnUnsplit(removedWindow);
197 m_sashPosition = 0;
198 }
199 else
200 {
201 m_sashPosition = x;
202 }
203 }
204 else // m_splitMode == wxSPLIT_VERTICAL
205 {
206 if ( !OnSashPositionChange(y) )
207 return;
208
209 if ( y <= 4 )
210 {
211 // We remove the first window from the view
212 wxWindow *removedWindow = m_windowOne;
213 m_windowOne = m_windowTwo;
214 m_windowTwo = (wxWindow *) NULL;
215
216 OnUnsplit(removedWindow);
217 m_sashPosition = 0;
218 }
219 else if ( y >= (h - 4) )
220 {
221 // We remove the second window from the view
222 wxWindow *removedWindow = m_windowTwo;
223 m_windowTwo = (wxWindow *) NULL;
224 OnUnsplit(removedWindow);
225 m_sashPosition = 0;
226 }
227 else
228 {
229 m_sashPosition = y;
230 }
231 } // m_splitMode == wxSPLIT_VERTICAL
232 SizeWindows();
233 } // left up && dragging
234 else if (event.Moving() && !event.Dragging())
235 {
236 // Just change the cursor if required
237 if ( SashHitTest(x, y) )
238 {
239 if ( m_splitMode == wxSPLIT_VERTICAL )
240 {
241 SetCursor(*m_sashCursorWE);
242 }
243 else
244 {
245 SetCursor(*m_sashCursorNS);
246 }
247 }
248 }
249 else if (event.Dragging() && (m_dragMode == wxSPLIT_DRAG_DRAGGING))
250 {
251 // Erase old tracker
252 DrawSashTracker(m_oldX, m_oldY);
253
254 // Draw new one
255 DrawSashTracker(x, y);
256
257 m_oldX = x;
258 m_oldY = y;
259 }
260 else if ( event.LeftDClick() )
261 {
262 OnDoubleClickSash(x, y);
263 }
264 }
265
266 void wxSplitterWindow::OnSize(wxSizeEvent& WXUNUSED(event))
267 {
268 int cw, ch;
269 GetClientSize( &cw, &ch );
270 if ( m_windowTwo )
271 {
272 if ( m_splitMode == wxSPLIT_VERTICAL )
273 {
274 if ( m_sashPosition >= (cw - 5) )
275 m_sashPosition = wxMax(10, cw - 40);
276 }
277 if ( m_splitMode == wxSPLIT_HORIZONTAL )
278 {
279 if ( m_sashPosition >= (ch - 5) )
280 m_sashPosition = wxMax(10, ch - 40);
281 }
282 }
283 SizeWindows();
284 }
285
286 bool wxSplitterWindow::SashHitTest(int x, int y, int tolerance)
287 {
288 if ( m_windowTwo == NULL || m_sashPosition == 0)
289 return FALSE; // No sash
290
291 if ( m_splitMode == wxSPLIT_VERTICAL )
292 {
293 if ( (x >= m_sashPosition - tolerance) && (x <= m_sashPosition + m_sashSize + tolerance) )
294 return TRUE;
295 else
296 return FALSE;
297 }
298 else
299 {
300 if ( (y >= (m_sashPosition- tolerance)) && (y <= (m_sashPosition + m_sashSize + tolerance)) )
301 return TRUE;
302 else
303 return FALSE;
304 }
305
306 return FALSE;
307 }
308
309 // Draw 3D effect borders
310 void wxSplitterWindow::DrawBorders(wxDC& dc)
311 {
312 int w, h;
313 GetClientSize(&w, &h);
314
315 if ( GetWindowStyleFlag() & wxSP_3D )
316 {
317 dc.SetPen(*m_mediumShadowPen);
318 dc.DrawLine(0, 0, w-1, 0);
319 dc.DrawLine(0, 0, 0, h - 1);
320
321 dc.SetPen(*m_darkShadowPen);
322 dc.DrawLine(1, 1, w-2, 1);
323 dc.DrawLine(1, 1, 1, h-2);
324
325 dc.SetPen(*m_hilightPen);
326 dc.DrawLine(0, h-1, w-1, h-1);
327 dc.DrawLine(w-1, 0, w-1, h); // Surely the maximum y pos. should be h - 1.
328 /// Anyway, h is required for MSW.
329
330 dc.SetPen(*m_lightShadowPen);
331 dc.DrawLine(w-2, 1, w-2, h-2); // Right hand side
332 dc.DrawLine(1, h-2, w-1, h-2); // Bottom
333 }
334 else if ( GetWindowStyleFlag() & wxSP_BORDER )
335 {
336 dc.SetBrush(*wxTRANSPARENT_BRUSH);
337 dc.SetPen(*wxBLACK_PEN);
338 dc.DrawRectangle(0, 0, w-1, h-1);
339 }
340
341 dc.SetPen(wxNullPen);
342 dc.SetBrush(wxNullBrush);
343 }
344
345 // Draw the sash
346 void wxSplitterWindow::DrawSash(wxDC& dc)
347 {
348 if ( m_sashPosition == 0 || !m_windowTwo)
349 return;
350
351 int w, h;
352 GetClientSize(&w, &h);
353
354 if ( GetWindowStyleFlag() & wxSP_3D )
355 {
356 if ( m_splitMode == wxSPLIT_VERTICAL )
357 {
358 dc.SetPen(*m_facePen);
359 dc.SetBrush(*m_faceBrush);
360 dc.DrawRectangle(m_sashPosition + 2, 0, m_sashSize - 4, h);
361
362 dc.SetBrush(*wxTRANSPARENT_BRUSH);
363
364 dc.SetPen(*m_lightShadowPen);
365 dc.DrawLine(m_sashPosition, 1, m_sashPosition, h-2);
366
367 dc.SetPen(*m_hilightPen);
368 dc.DrawLine(m_sashPosition+1, 0, m_sashPosition+1, h);
369
370 dc.SetPen(*m_mediumShadowPen);
371 dc.DrawLine(m_sashPosition+m_sashSize-2, 1, m_sashPosition+m_sashSize-2, h-1);
372
373 dc.SetPen(*m_darkShadowPen);
374 dc.DrawLine(m_sashPosition+m_sashSize-1, 2, m_sashPosition+m_sashSize-1, h-2);
375 }
376 else
377 {
378 dc.SetPen(*m_facePen);
379 dc.SetBrush(*m_faceBrush);
380 dc.DrawRectangle(0, m_sashPosition + 2, w, m_sashSize - 4);
381
382 dc.SetBrush(*wxTRANSPARENT_BRUSH);
383
384 dc.SetPen(*m_lightShadowPen);
385 dc.DrawLine(1, m_sashPosition, w-2, m_sashPosition);
386
387 dc.SetPen(*m_hilightPen);
388 dc.DrawLine(0, m_sashPosition+1, w, m_sashPosition+1);
389
390 dc.SetPen(*m_mediumShadowPen);
391 dc.DrawLine(1, m_sashPosition+m_sashSize-2, w-1, m_sashPosition+m_sashSize-2);
392
393 dc.SetPen(*m_darkShadowPen);
394 dc.DrawLine(2, m_sashPosition+m_sashSize-1, w-2, m_sashPosition+m_sashSize-1);
395 }
396 }
397 else
398 {
399 if ( m_splitMode == wxSPLIT_VERTICAL )
400 {
401 dc.SetPen(*wxBLACK_PEN);
402 dc.SetBrush(*wxBLACK_BRUSH);
403 int h1 = h-1;
404 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER )
405 h1 += 1; // Not sure why this is necessary...
406 dc.DrawRectangle(m_sashPosition, 0, m_sashSize, h1);
407 }
408 else
409 {
410 dc.SetPen(*wxBLACK_PEN);
411 dc.SetBrush(*wxBLACK_BRUSH);
412 int w1 = w-1;
413 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER )
414 w1 ++;
415
416 dc.DrawRectangle(0, m_sashPosition, w1, m_sashSize);
417 }
418
419 }
420
421 dc.SetPen(wxNullPen);
422 dc.SetBrush(wxNullBrush);
423 }
424
425 // Draw the sash tracker (for whilst moving the sash)
426 void wxSplitterWindow::DrawSashTracker(int x, int y)
427 {
428 int w, h;
429 GetClientSize(&w, &h);
430
431 wxScreenDC screenDC;
432 int x1, y1;
433 int x2, y2;
434
435 if ( m_splitMode == wxSPLIT_VERTICAL )
436 {
437 x1 = x; y1 = 2;
438 x2 = x; y2 = h-2;
439
440 if ( x1 > w )
441 {
442 x1 = w; x2 = w;
443 }
444 else if ( x1 < 0 )
445 {
446 x1 = 0; x2 = 0;
447 }
448 }
449 else
450 {
451 x1 = 2; y1 = y;
452 x2 = w-2; y2 = y;
453
454 if ( y1 > h )
455 {
456 y1 = h;
457 y2 = h;
458 }
459 else if ( y1 < 0 )
460 {
461 y1 = 0;
462 y2 = 0;
463 }
464 }
465
466 ClientToScreen(&x1, &y1);
467 ClientToScreen(&x2, &y2);
468
469 screenDC.SetLogicalFunction(wxXOR);
470 screenDC.SetPen(*m_sashTrackerPen);
471 screenDC.SetBrush(*wxTRANSPARENT_BRUSH);
472
473 screenDC.DrawLine(x1, y1, x2, y2);
474
475 screenDC.SetLogicalFunction(wxCOPY);
476
477 screenDC.SetPen(wxNullPen);
478 screenDC.SetBrush(wxNullBrush);
479 }
480
481 // Position and size subwindows.
482 // Note that the border size applies to each subwindow, not
483 // including the edges next to the sash.
484 void wxSplitterWindow::SizeWindows()
485 {
486 int w, h;
487 GetClientSize(&w, &h);
488
489 if ( m_windowOne && !m_windowTwo )
490 {
491 m_windowOne->SetSize(m_borderSize, m_borderSize, w - 2*m_borderSize, h - 2*m_borderSize);
492 }
493 else if ( m_windowOne && m_windowTwo )
494 {
495 if (m_splitMode == wxSPLIT_VERTICAL)
496 {
497 int x1 = m_borderSize;
498 int y1 = m_borderSize;
499 int w1 = m_sashPosition - m_borderSize;
500 int h1 = h - 2*m_borderSize;
501
502 int x2 = m_sashPosition + m_sashSize;
503 int y2 = m_borderSize;
504 int w2 = w - 2*m_borderSize - m_sashSize - w1;
505 int h2 = h - 2*m_borderSize;
506
507 m_windowOne->SetSize(x1, y1, w1, h1);
508 m_windowTwo->SetSize(x2, y2, w2, h2);
509 }
510 else
511 {
512 m_windowOne->SetSize(m_borderSize, m_borderSize,
513 w - 2*m_borderSize, m_sashPosition - m_borderSize);
514 m_windowTwo->SetSize(m_borderSize, m_sashPosition + m_sashSize,
515 w - 2*m_borderSize, h - 2*m_borderSize - m_sashSize - (m_sashPosition - m_borderSize));
516 }
517 }
518 wxClientDC dc(this);
519 DrawBorders(dc);
520 DrawSash(dc);
521 }
522
523 // Set pane for unsplit window
524 void wxSplitterWindow::Initialize(wxWindow *window)
525 {
526 m_windowOne = window;
527 m_windowTwo = (wxWindow *) NULL;
528 m_sashPosition = 0;
529 }
530
531 // Associates the given window with window 2, drawing the appropriate sash
532 // and changing the split mode.
533 // Does nothing and returns FALSE if the window is already split.
534 bool wxSplitterWindow::SplitVertically(wxWindow *window1, wxWindow *window2, int sashPosition)
535 {
536 if ( IsSplit() )
537 return FALSE;
538
539 int w, h;
540 GetClientSize(&w, &h);
541
542 m_splitMode = wxSPLIT_VERTICAL;
543 m_windowOne = window1;
544 m_windowTwo = window2;
545 if ( sashPosition > 0 )
546 m_sashPosition = sashPosition;
547 else if ( sashPosition < 0 )
548 m_sashPosition = w - sashPosition;
549 else // default
550 m_sashPosition = w/2;
551
552 SizeWindows();
553
554 return TRUE;
555 }
556
557 bool wxSplitterWindow::SplitHorizontally(wxWindow *window1, wxWindow *window2, int sashPosition)
558 {
559 if ( IsSplit() )
560 return FALSE;
561
562 int w, h;
563 GetClientSize(&w, &h);
564
565 m_splitMode = wxSPLIT_HORIZONTAL;
566 m_windowOne = window1;
567 m_windowTwo = window2;
568 if ( sashPosition > 0 )
569 m_sashPosition = sashPosition;
570 else if ( sashPosition < 0 )
571 m_sashPosition = h - sashPosition;
572 else // default
573 m_sashPosition = h/2;
574
575 SizeWindows();
576
577 return TRUE;
578 }
579
580
581 // Remove the specified (or second) window from the view
582 // Doesn't actually delete the window.
583 bool wxSplitterWindow::Unsplit(wxWindow *toRemove)
584 {
585 if ( ! IsSplit() )
586 return FALSE;
587
588 wxWindow *win = NULL;
589 if ( toRemove == NULL || toRemove == m_windowTwo)
590 {
591 win = m_windowTwo ;
592 m_windowTwo = (wxWindow *) NULL;
593 }
594 else if ( toRemove == m_windowOne )
595 {
596 win = m_windowOne ;
597 m_windowOne = m_windowTwo;
598 m_windowTwo = (wxWindow *) NULL;
599 }
600 else
601 {
602 wxFAIL_MSG("splitter: attempt to remove a non-existent window");
603
604 return FALSE;
605 }
606
607 OnUnsplit(win);
608 m_sashPosition = 0;
609 SizeWindows();
610
611 return TRUE;
612 }
613
614 // Replace a window with another one
615 bool wxSplitterWindow::ReplaceWindow(wxWindow *winOld, wxWindow *winNew)
616 {
617 wxCHECK_MSG( winOld, FALSE, "use one of Split() functions instead" );
618 wxCHECK_MSG( winNew, FALSE, "use Unsplit() functions instead" );
619
620 if ( winOld == m_windowTwo )
621 {
622 m_windowTwo = winNew;
623 }
624 else if ( winOld == m_windowOne )
625 {
626 m_windowOne = winNew;
627 }
628 else
629 {
630 wxFAIL_MSG("splitter: attempt to replace a non-existent window");
631
632 return FALSE;
633 }
634
635 SizeWindows();
636
637 return TRUE;
638 }
639
640 void wxSplitterWindow::SetSashPosition(int position, bool redraw)
641 {
642 m_sashPosition = position;
643
644 if ( redraw )
645 {
646 SizeWindows();
647 }
648 }
649
650 bool wxSplitterWindow::OnSashPositionChange(int newSashPosition)
651 {
652 // is the left/upper pane too small?
653 if ( newSashPosition < m_minimumPaneSize )
654 return NULL;
655
656 // is the right/lower pane too small?
657 int w, h;
658 GetClientSize(&w, &h);
659
660 if ( m_splitMode == wxSPLIT_VERTICAL )
661 {
662 if ( w - newSashPosition < m_minimumPaneSize )
663 return FALSE;
664 }
665 else // m_splitMode = wxSPLIT_HORIZONTAL
666 {
667 if ( h - newSashPosition < m_minimumPaneSize )
668 return FALSE;
669 }
670
671 // it's ok to move sash
672 return TRUE;
673 }
674
675 // Called when the sash is double-clicked.
676 // The default behaviour is to remove the sash if the
677 // minimum pane size is zero.
678 void wxSplitterWindow::OnDoubleClickSash(int WXUNUSED(x), int WXUNUSED(y) )
679 {
680 if ( GetMinimumPaneSize() == 0 )
681 {
682 Unsplit();
683 }
684 }
685
686 // Initialize colours
687 void wxSplitterWindow::InitColours()
688 {
689 wxDELETE( m_facePen );
690 wxDELETE( m_faceBrush );
691 wxDELETE( m_mediumShadowPen );
692 wxDELETE( m_darkShadowPen );
693 wxDELETE( m_lightShadowPen );
694 wxDELETE( m_hilightPen );
695
696 // Shadow colours
697 #if defined(__WIN95__)
698 wxColour faceColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
699 m_facePen = new wxPen(faceColour, 1, wxSOLID);
700 m_faceBrush = new wxBrush(faceColour, wxSOLID);
701
702 wxColour mediumShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW));
703 m_mediumShadowPen = new wxPen(mediumShadowColour, 1, wxSOLID);
704
705 wxColour darkShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW));
706 m_darkShadowPen = new wxPen(darkShadowColour, 1, wxSOLID);
707
708 wxColour lightShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT));
709 m_lightShadowPen = new wxPen(lightShadowColour, 1, wxSOLID);
710
711 wxColour hilightColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT));
712 m_hilightPen = new wxPen(hilightColour, 1, wxSOLID);
713 #else // !Win32
714 m_facePen = new wxPen("LIGHT GREY", 1, wxSOLID);
715 m_faceBrush = new wxBrush("LIGHT GREY", wxSOLID);
716 m_mediumShadowPen = new wxPen("GREY", 1, wxSOLID);
717 m_darkShadowPen = new wxPen("BLACK", 1, wxSOLID);
718 m_lightShadowPen = new wxPen("LIGHT GREY", 1, wxSOLID);
719 m_hilightPen = new wxPen("WHITE", 1, wxSOLID);
720 #endif // Win32/!Win32
721 }
722