]> git.saurik.com Git - wxWidgets.git/blob - src/common/toplvcmn.cpp
wxDocument::SaveAs sets m_savedYet flag correctly (patch 494838)
[wxWidgets.git] / src / common / toplvcmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/toplvcmn.cpp
3 // Purpose: common (for all platforms) wxTopLevelWindow functions
4 // Author: Julian Smart, Vadim Zeitlin
5 // Created: 01/02/97
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #ifdef __GNUG__
20 #pragma implementation "toplevelbase.h"
21 #endif
22
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
25
26 #ifdef __BORLANDC__
27 #pragma hdrstop
28 #endif
29
30 #ifndef WX_PRECOMP
31 #include "wx/toplevel.h"
32 #include "wx/dcclient.h"
33 #endif // WX_PRECOMP
34
35 #include "wx/evtloop.h"
36
37 // ----------------------------------------------------------------------------
38 // event table
39 // ----------------------------------------------------------------------------
40
41 BEGIN_EVENT_TABLE(wxTopLevelWindowBase, wxWindow)
42 EVT_CLOSE(wxTopLevelWindowBase::OnCloseWindow)
43 EVT_SIZE(wxTopLevelWindowBase::OnSize)
44 END_EVENT_TABLE()
45
46 // ============================================================================
47 // implementation
48 // ============================================================================
49
50 // FIXME: some platforms don't have wxTopLevelWindow yet
51 #ifdef wxTopLevelWindowNative
52 IMPLEMENT_DYNAMIC_CLASS(wxTopLevelWindow, wxWindow)
53 #endif
54
55 // ----------------------------------------------------------------------------
56 // construction/destruction
57 // ----------------------------------------------------------------------------
58
59 wxTopLevelWindowBase::wxTopLevelWindowBase()
60 {
61 }
62
63 bool wxTopLevelWindowBase::Destroy()
64 {
65 // delayed destruction: the frame will be deleted during the next idle
66 // loop iteration
67 if ( !wxPendingDelete.Member(this) )
68 wxPendingDelete.Append(this);
69
70 return TRUE;
71 }
72
73 // ----------------------------------------------------------------------------
74 // wxTopLevelWindow size management: we exclude the areas taken by
75 // menu/status/toolbars from the client area, so the client area is what's
76 // really available for the frame contents
77 // ----------------------------------------------------------------------------
78
79 void wxTopLevelWindowBase::DoScreenToClient(int *x, int *y) const
80 {
81 wxWindow::DoScreenToClient(x, y);
82
83 // translate the wxWindow client coords to our client coords
84 wxPoint pt(GetClientAreaOrigin());
85 if ( x )
86 *x -= pt.x;
87 if ( y )
88 *y -= pt.y;
89 }
90
91 void wxTopLevelWindowBase::DoClientToScreen(int *x, int *y) const
92 {
93 // our client area origin (0, 0) may be really something like (0, 30) for
94 // wxWindow if we have a toolbar, account for it before translating
95 wxPoint pt(GetClientAreaOrigin());
96 if ( x )
97 *x += pt.x;
98 if ( y )
99 *y += pt.y;
100
101 wxWindow::DoClientToScreen(x, y);
102 }
103
104
105 // ----------------------------------------------------------------------------
106 // event handlers
107 // ----------------------------------------------------------------------------
108
109 // default resizing behaviour - if only ONE subwindow, resize to fill the
110 // whole client area
111 void wxTopLevelWindowBase::OnSize(wxSizeEvent& WXUNUSED(event))
112 {
113 // if we're using constraints - do use them
114 #if wxUSE_CONSTRAINTS
115 if ( GetAutoLayout() )
116 {
117 Layout();
118 }
119 else
120 #endif // wxUSE_CONSTRAINTS
121 {
122 // do we have _exactly_ one child?
123 wxWindow *child = (wxWindow *)NULL;
124 for ( wxWindowList::Node *node = GetChildren().GetFirst();
125 node;
126 node = node->GetNext() )
127 {
128 wxWindow *win = node->GetData();
129
130 // exclude top level and managed windows (status bar isn't
131 // currently in the children list except under wxMac anyhow, but
132 // it makes no harm to test for it)
133 if ( !win->IsTopLevel() && !IsOneOfBars(win) )
134 {
135 if ( child )
136 {
137 return; // it's our second subwindow - nothing to do
138 }
139
140 child = win;
141 }
142 }
143
144 // do we have any children at all?
145 if ( child )
146 {
147 // exactly one child - set it's size to fill the whole frame
148 int clientW, clientH;
149 DoGetClientSize(&clientW, &clientH);
150
151 // for whatever reasons, wxGTK wants to have a small offset - it
152 // probably looks better with it?
153 #ifdef __WXGTK__
154 static const int ofs = 1;
155 #else
156 static const int ofs = 0;
157 #endif
158
159 child->SetSize(ofs, ofs, clientW - 2*ofs, clientH - 2*ofs);
160 }
161 }
162 }
163
164 // The default implementation for the close window event.
165 void wxTopLevelWindowBase::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
166 {
167 Destroy();
168 }
169
170 bool wxTopLevelWindowBase::SendIconizeEvent(bool iconized)
171 {
172 wxIconizeEvent event(GetId(), iconized);
173 event.SetEventObject(this);
174
175 return GetEventHandler()->ProcessEvent(event);
176 }
177
178
179 // ----------------------------------------------------------------------------
180 // interactive manipulation
181 // ----------------------------------------------------------------------------
182
183 #ifdef __WXUNIVERSAL__
184
185 #define wxINTERACTIVE_RESIZE_DIR \
186 (wxINTERACTIVE_RESIZE_W | wxINTERACTIVE_RESIZE_E | \
187 wxINTERACTIVE_RESIZE_S | wxINTERACTIVE_RESIZE_N)
188
189 struct wxInteractiveMoveData
190 {
191 wxTopLevelWindowBase *m_window;
192 wxEventLoop *m_evtLoop;
193 int m_flags;
194 wxRect m_rect;
195 wxRect m_rectOrig;
196 wxPoint m_pos;
197 wxSize m_minSize, m_maxSize;
198 };
199
200 class wxInteractiveMoveHandler : public wxEvtHandler
201 {
202 public:
203 wxInteractiveMoveHandler(wxInteractiveMoveData& data) : m_data(data) {}
204
205 private:
206 DECLARE_EVENT_TABLE()
207 void OnMouseMove(wxMouseEvent& event);
208 void OnMouseDown(wxMouseEvent& event);
209 void OnMouseUp(wxMouseEvent& event);
210 void OnKeyDown(wxKeyEvent& event);
211
212 wxInteractiveMoveData& m_data;
213 };
214
215 BEGIN_EVENT_TABLE(wxInteractiveMoveHandler, wxEvtHandler)
216 EVT_MOTION(wxInteractiveMoveHandler::OnMouseMove)
217 EVT_LEFT_DOWN(wxInteractiveMoveHandler::OnMouseDown)
218 EVT_LEFT_UP(wxInteractiveMoveHandler::OnMouseUp)
219 EVT_KEY_DOWN(wxInteractiveMoveHandler::OnKeyDown)
220 END_EVENT_TABLE()
221
222
223 static inline LINKAGEMODE
224 void wxApplyResize(wxInteractiveMoveData& data, const wxPoint& diff)
225 {
226 if ( data.m_flags & wxINTERACTIVE_RESIZE_W )
227 {
228 data.m_rect.x += diff.x;
229 data.m_rect.width -= diff.x;
230 }
231 else if ( data.m_flags & wxINTERACTIVE_RESIZE_E )
232 {
233 data.m_rect.width += diff.x;
234 }
235 if ( data.m_flags & wxINTERACTIVE_RESIZE_N )
236 {
237 data.m_rect.y += diff.y;
238 data.m_rect.height -= diff.y;
239 }
240 else if ( data.m_flags & wxINTERACTIVE_RESIZE_S )
241 {
242 data.m_rect.height += diff.y;
243 }
244
245 if ( data.m_minSize.x != -1 && data.m_rect.width < data.m_minSize.x )
246 {
247 if ( data.m_flags & wxINTERACTIVE_RESIZE_W )
248 data.m_rect.x -= data.m_minSize.x - data.m_rect.width;
249 data.m_rect.width = data.m_minSize.x;
250 }
251 if ( data.m_maxSize.x != -1 && data.m_rect.width > data.m_maxSize.x )
252 {
253 if ( data.m_flags & wxINTERACTIVE_RESIZE_W )
254 data.m_rect.x -= data.m_minSize.x - data.m_rect.width;
255 data.m_rect.width = data.m_maxSize.x;
256 }
257 if ( data.m_minSize.y != -1 && data.m_rect.height < data.m_minSize.y )
258 {
259 if ( data.m_flags & wxINTERACTIVE_RESIZE_N )
260 data.m_rect.y -= data.m_minSize.y - data.m_rect.height;
261 data.m_rect.height = data.m_minSize.y;
262 }
263 if ( data.m_maxSize.y != -1 && data.m_rect.height > data.m_maxSize.y )
264 {
265 if ( data.m_flags & wxINTERACTIVE_RESIZE_N )
266 data.m_rect.y -= data.m_minSize.y - data.m_rect.height;
267 data.m_rect.height = data.m_maxSize.y;
268 }
269 }
270
271 void wxInteractiveMoveHandler::OnMouseMove(wxMouseEvent& event)
272 {
273 if ( m_data.m_flags & wxINTERACTIVE_WAIT_FOR_INPUT )
274 event.Skip();
275
276 else if ( m_data.m_flags & wxINTERACTIVE_MOVE )
277 {
278 wxPoint diff = wxGetMousePosition() - m_data.m_pos;
279 m_data.m_rect = m_data.m_rectOrig;
280 m_data.m_rect.Offset(diff);
281 m_data.m_window->Move(m_data.m_rect.GetPosition());
282 }
283
284 else if ( m_data.m_flags & wxINTERACTIVE_RESIZE )
285 {
286 wxPoint diff = wxGetMousePosition() - m_data.m_pos;
287 m_data.m_rect = m_data.m_rectOrig;
288 wxApplyResize(m_data, diff);
289 m_data.m_window->SetSize(m_data.m_rect);
290 }
291 }
292
293 void wxInteractiveMoveHandler::OnMouseDown(wxMouseEvent& event)
294 {
295 if ( m_data.m_flags & wxINTERACTIVE_WAIT_FOR_INPUT )
296 {
297 m_data.m_flags &= ~wxINTERACTIVE_WAIT_FOR_INPUT;
298 m_data.m_pos = wxGetMousePosition();
299 }
300 }
301
302 void wxInteractiveMoveHandler::OnKeyDown(wxKeyEvent& event)
303 {
304 if ( m_data.m_flags & wxINTERACTIVE_WAIT_FOR_INPUT )
305 {
306 m_data.m_flags &= ~wxINTERACTIVE_WAIT_FOR_INPUT;
307 m_data.m_pos = wxGetMousePosition();
308 }
309
310 wxPoint diff(-1,-1);
311
312 switch ( event.GetKeyCode() )
313 {
314 case WXK_UP: diff = wxPoint(0, -16); break;
315 case WXK_DOWN: diff = wxPoint(0, 16); break;
316 case WXK_LEFT: diff = wxPoint(-16, 0); break;
317 case WXK_RIGHT: diff = wxPoint(16, 0); break;
318 case WXK_ESCAPE:
319 m_data.m_window->SetSize(m_data.m_rectOrig);
320 m_data.m_evtLoop->Exit();
321 return;
322 case WXK_RETURN:
323 m_data.m_evtLoop->Exit();
324 return;
325 }
326
327 if ( diff.x != -1 )
328 {
329 if ( m_data.m_flags & wxINTERACTIVE_MOVE )
330 {
331 m_data.m_rect.Offset(diff);
332 m_data.m_window->Move(m_data.m_rect.GetPosition());
333 }
334 else /* wxINTERACTIVE_RESIZE */
335 {
336 if ( !(m_data.m_flags & wxINTERACTIVE_RESIZE_DIR) )
337 {
338 if ( diff.y < 0 )
339 m_data.m_flags |= wxINTERACTIVE_RESIZE_N;
340 else if ( diff.y > 0 )
341 m_data.m_flags |= wxINTERACTIVE_RESIZE_S;
342 if ( diff.x < 0 )
343 m_data.m_flags |= wxINTERACTIVE_RESIZE_W;
344 else if ( diff.x > 0 )
345 m_data.m_flags |= wxINTERACTIVE_RESIZE_E;
346 }
347
348 wxApplyResize(m_data, diff);
349 m_data.m_window->SetSize(m_data.m_rect);
350 }
351 }
352 }
353
354 void wxInteractiveMoveHandler::OnMouseUp(wxMouseEvent& event)
355 {
356 m_data.m_evtLoop->Exit();
357 }
358
359
360 void wxTopLevelWindowBase::InteractiveMove(int flags)
361 {
362 wxASSERT_MSG( !((flags & wxINTERACTIVE_MOVE) && (flags & wxINTERACTIVE_RESIZE)),
363 wxT("can't move and resize window at the same time") );
364
365 wxASSERT_MSG( !(flags & wxINTERACTIVE_RESIZE) ||
366 (flags & wxINTERACTIVE_WAIT_FOR_INPUT) ||
367 (flags & wxINTERACTIVE_RESIZE_DIR),
368 wxT("direction of resizing not specified") );
369
370 wxInteractiveMoveData data;
371 wxEventLoop loop;
372 wxWindow *focus = FindFocus();
373
374 // FIXME - display resize cursor if waiting for initial input
375
376 data.m_window = this;
377 data.m_evtLoop = &loop;
378 data.m_flags = flags;
379 data.m_rect = data.m_rectOrig = GetRect();
380 data.m_pos = wxGetMousePosition();
381 data.m_minSize = wxSize(GetMinWidth(), GetMinHeight());
382 data.m_maxSize = wxSize(GetMaxWidth(), GetMaxHeight());
383
384 this->PushEventHandler(new wxInteractiveMoveHandler(data));
385 if ( focus )
386 focus->PushEventHandler(new wxInteractiveMoveHandler(data));
387
388 CaptureMouse();
389 loop.Run();
390 ReleaseMouse();
391
392 this->PopEventHandler(TRUE/*delete*/);
393 if ( focus )
394 focus->PopEventHandler(TRUE/*delete*/);
395 }
396
397 #endif // __WXUNIVERSAL__