]> git.saurik.com Git - wxWidgets.git/blame - src/aui/framemanager.cpp
Give MSVS project files DOS line endings.
[wxWidgets.git] / src / aui / framemanager.cpp
CommitLineData
50acee04 1///////////////////////////////////////////////////////////////////////////////
be66f18e 2// Name: src/aui/framemanager.cpp
50acee04
JS
3// Purpose: wxaui: wx advanced user interface - docking window manager
4// Author: Benjamin I. Williams
5// Modified by:
6// Created: 2005-05-17
be66f18e 7// RCS-ID: $Id$
50acee04
JS
8// Copyright: (C) Copyright 2005-2006, Kirix Corporation, All Rights Reserved
9// Licence: wxWindows Library Licence, Version 3.1
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#if wxUSE_AUI
27
28#include "wx/aui/framemanager.h"
29#include "wx/aui/dockart.h"
30#include "wx/aui/floatpane.h"
31
32#ifndef WX_PRECOMP
be66f18e
WS
33 #include "wx/settings.h"
34 #include "wx/app.h"
35 #include "wx/dcclient.h"
36 #include "wx/dcscreen.h"
4e3e485b 37 #include "wx/toolbar.h"
59cf2e49 38 #include "wx/mdi.h"
155ecd4c 39 #include "wx/image.h"
50acee04
JS
40#endif
41
50acee04
JS
42WX_CHECK_BUILD_OPTIONS("wxAUI")
43
44#include "wx/arrimpl.cpp"
45WX_DECLARE_OBJARRAY(wxRect, wxAuiRectArray);
46WX_DEFINE_OBJARRAY(wxAuiRectArray)
47WX_DEFINE_OBJARRAY(wxDockUIPartArray)
48WX_DEFINE_OBJARRAY(wxDockInfoArray)
49WX_DEFINE_OBJARRAY(wxPaneButtonArray)
50WX_DEFINE_OBJARRAY(wxPaneInfoArray)
51
52wxPaneInfo wxNullPaneInfo;
53wxDockInfo wxNullDockInfo;
54DEFINE_EVENT_TYPE(wxEVT_AUI_PANEBUTTON)
58754643 55DEFINE_EVENT_TYPE(wxEVT_AUI_PANECLOSE)
50acee04
JS
56
57#ifdef __WXMAC__
58 // a few defines to avoid nameclashes
59 #define __MAC_OS_X_MEMORY_MANAGER_CLEAN__ 1
60 #define __AIFF__
61 #include "wx/mac/private.h"
62#endif
63
af672b5a
RD
64IMPLEMENT_DYNAMIC_CLASS(wxFrameManagerEvent, wxEvent)
65
50acee04
JS
66
67// -- static utility functions --
68
69static wxBitmap wxPaneCreateStippleBitmap()
70{
71 unsigned char data[] = { 0,0,0,192,192,192, 192,192,192,0,0,0 };
72 wxImage img(2,2,data,true);
73 return wxBitmap(img);
74}
75
76static void DrawResizeHint(wxDC& dc, const wxRect& rect)
77{
78 wxBitmap stipple = wxPaneCreateStippleBitmap();
79 wxBrush brush(stipple);
be66f18e 80 dc.SetBrush(brush);
50acee04
JS
81 dc.SetPen(*wxTRANSPARENT_PEN);
82
83 dc.SetLogicalFunction(wxXOR);
84 dc.DrawRectangle(rect);
85}
86
50acee04 87
81cc4eb2 88// on supported windows systems (Win2000 and greater, Mac), this function
50acee04
JS
89// will make a frame window transparent by a certain amount
90static void MakeWindowTransparent(wxWindow* wnd, int amount)
91{
81cc4eb2 92#if defined(__WXMSW__)
50acee04
JS
93 // this API call is not in all SDKs, only the newer ones, so
94 // we will runtime bind this
95 typedef DWORD (WINAPI *PSETLAYEREDWINDOWATTR)(HWND, DWORD, BYTE, DWORD);
96 static PSETLAYEREDWINDOWATTR pSetLayeredWindowAttributes = NULL;
97 static HMODULE h = NULL;
98 HWND hwnd = (HWND)wnd->GetHWND();
be66f18e 99
50acee04
JS
100 if (!h)
101 h = LoadLibrary(_T("user32"));
be66f18e 102
50acee04
JS
103 if (!pSetLayeredWindowAttributes)
104 {
105 pSetLayeredWindowAttributes =
99ae6124
WS
106 (PSETLAYEREDWINDOWATTR)GetProcAddress(h,
107#ifdef __WXWINCE__
108 wxT("SetLayeredWindowAttributes")
109#else
110 "SetLayeredWindowAttributes"
111#endif
112 );
50acee04 113 }
be66f18e 114
50acee04
JS
115 if (pSetLayeredWindowAttributes == NULL)
116 return;
be66f18e 117
50acee04
JS
118 LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
119 if (0 == (exstyle & 0x80000) /*WS_EX_LAYERED*/)
be66f18e
WS
120 SetWindowLong(hwnd, GWL_EXSTYLE, exstyle | 0x80000 /*WS_EX_LAYERED*/);
121
122 pSetLayeredWindowAttributes(hwnd, 0, (BYTE)amount, 2 /*LWA_ALPHA*/);
50acee04 123
81cc4eb2
RD
124#elif defined(__WXMAC__)
125
126 WindowRef handle = GetControlOwner((OpaqueControlRef*)wnd->GetHandle());
127 SetWindowAlpha(handle, float(amount)/ 255.0);
128
129#else
2713fedc
RD
130 wxUnusedVar(wnd);
131 wxUnusedVar(amount);
50acee04 132#endif
81cc4eb2
RD
133}
134
50acee04
JS
135
136
137// CopyDocksAndPanes() - this utility function creates copies of
138// the dock and pane info. wxDockInfo's usually contain pointers
139// to wxPaneInfo classes, thus this function is necessary to reliably
140// reconstruct that relationship in the new dock info and pane info arrays
141
142static void CopyDocksAndPanes(wxDockInfoArray& dest_docks,
143 wxPaneInfoArray& dest_panes,
144 const wxDockInfoArray& src_docks,
145 const wxPaneInfoArray& src_panes)
146{
147 dest_docks = src_docks;
148 dest_panes = src_panes;
149 int i, j, k, dock_count, pc1, pc2;
150 for (i = 0, dock_count = dest_docks.GetCount(); i < dock_count; ++i)
151 {
152 wxDockInfo& dock = dest_docks.Item(i);
153 for (j = 0, pc1 = dock.panes.GetCount(); j < pc1; ++j)
154 for (k = 0, pc2 = src_panes.GetCount(); k < pc2; ++k)
155 if (dock.panes.Item(j) == &src_panes.Item(k))
156 dock.panes.Item(j) = &dest_panes.Item(k);
157 }
158}
159
160// GetMaxLayer() is an internal function which returns
161// the highest layer inside the specified dock
162static int GetMaxLayer(const wxDockInfoArray& docks, int dock_direction)
163{
164 int i, dock_count, max_layer = 0;
165 for (i = 0, dock_count = docks.GetCount(); i < dock_count; ++i)
166 {
167 wxDockInfo& dock = docks.Item(i);
168 if (dock.dock_direction == dock_direction &&
169 dock.dock_layer > max_layer && !dock.fixed)
170 max_layer = dock.dock_layer;
171 }
172 return max_layer;
173}
174
175
176// GetMaxRow() is an internal function which returns
177// the highest layer inside the specified dock
178static int GetMaxRow(const wxPaneInfoArray& panes, int direction, int layer)
179{
180 int i, pane_count, max_row = 0;
181 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
182 {
183 wxPaneInfo& pane = panes.Item(i);
184 if (pane.dock_direction == direction &&
be66f18e 185 pane.dock_layer == layer &&
50acee04
JS
186 pane.dock_row > max_row)
187 max_row = pane.dock_row;
188 }
189 return max_row;
190}
191
192
193
194// DoInsertDockLayer() is an internal function that inserts a new dock
195// layer by incrementing all existing dock layer values by one
196static void DoInsertDockLayer(wxPaneInfoArray& panes,
197 int dock_direction,
198 int dock_layer)
199{
200 int i, pane_count;
201 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
202 {
203 wxPaneInfo& pane = panes.Item(i);
204 if (!pane.IsFloating() &&
205 pane.dock_direction == dock_direction &&
206 pane.dock_layer >= dock_layer)
207 pane.dock_layer++;
208 }
209}
210
211// DoInsertDockLayer() is an internal function that inserts a new dock
212// row by incrementing all existing dock row values by one
213static void DoInsertDockRow(wxPaneInfoArray& panes,
214 int dock_direction,
215 int dock_layer,
216 int dock_row)
217{
218 int i, pane_count;
219 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
220 {
221 wxPaneInfo& pane = panes.Item(i);
222 if (!pane.IsFloating() &&
223 pane.dock_direction == dock_direction &&
224 pane.dock_layer == dock_layer &&
225 pane.dock_row >= dock_row)
226 pane.dock_row++;
227 }
228}
229
be66f18e 230// DoInsertDockLayer() is an internal function that inserts a space for
50acee04
JS
231// another dock pane by incrementing all existing dock row values by one
232static void DoInsertPane(wxPaneInfoArray& panes,
233 int dock_direction,
234 int dock_layer,
235 int dock_row,
236 int dock_pos)
237{
238 int i, pane_count;
239 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
240 {
241 wxPaneInfo& pane = panes.Item(i);
242 if (!pane.IsFloating() &&
243 pane.dock_direction == dock_direction &&
244 pane.dock_layer == dock_layer &&
245 pane.dock_row == dock_row &&
246 pane.dock_pos >= dock_pos)
247 pane.dock_pos++;
248 }
249}
250
251// FindDocks() is an internal function that returns a list of docks which meet
252// the specified conditions in the parameters and returns a sorted array
253// (sorted by layer and then row)
254static void FindDocks(wxDockInfoArray& docks,
255 int dock_direction,
256 int dock_layer,
257 int dock_row,
258 wxDockInfoPtrArray& arr)
259{
260 int begin_layer = dock_layer;
261 int end_layer = dock_layer;
262 int begin_row = dock_row;
263 int end_row = dock_row;
264 int dock_count = docks.GetCount();
265 int layer, row, i, max_row = 0, max_layer = 0;
266
267 // discover the maximum dock layer and the max row
268 for (i = 0; i < dock_count; ++i)
269 {
270 max_row = wxMax(max_row, docks.Item(i).dock_row);
271 max_layer = wxMax(max_layer, docks.Item(i).dock_layer);
272 }
be66f18e 273
50acee04
JS
274 // if no dock layer was specified, search all dock layers
275 if (dock_layer == -1)
276 {
277 begin_layer = 0;
278 end_layer = max_layer;
279 }
be66f18e 280
50acee04
JS
281 // if no dock row was specified, search all dock row
282 if (dock_row == -1)
283 {
284 begin_row = 0;
285 end_row = max_row;
286 }
287
288 arr.Clear();
289
290 for (layer = begin_layer; layer <= end_layer; ++layer)
291 for (row = begin_row; row <= end_row; ++row)
292 for (i = 0; i < dock_count; ++i)
293 {
294 wxDockInfo& d = docks.Item(i);
295 if (dock_direction == -1 || dock_direction == d.dock_direction)
296 {
297 if (d.dock_layer == layer && d.dock_row == row)
298 arr.Add(&d);
299 }
300 }
301}
302
303// FindPaneInDock() looks up a specified window pointer inside a dock.
304// If found, the corresponding wxPaneInfo pointer is returned, otherwise NULL.
305static wxPaneInfo* FindPaneInDock(const wxDockInfo& dock, wxWindow* window)
306{
307 int i, count = dock.panes.GetCount();
308 for (i = 0; i < count; ++i)
309 {
310 wxPaneInfo* p = dock.panes.Item(i);
311 if (p->window == window)
312 return p;
313 }
314 return NULL;
315}
316
317// RemovePaneFromDocks() removes a pane window from all docks
318// with a possible exception specified by parameter "except"
319static void RemovePaneFromDocks(wxDockInfoArray& docks,
320 wxPaneInfo& pane,
321 wxDockInfo* except = NULL)
322{
323 int i, dock_count;
324 for (i = 0, dock_count = docks.GetCount(); i < dock_count; ++i)
325 {
326 wxDockInfo& d = docks.Item(i);
327 if (&d == except)
328 continue;
329 wxPaneInfo* pi = FindPaneInDock(d, pane.window);
330 if (pi)
331 d.panes.Remove(pi);
332 }
333}
334
335// RenumberDockRows() takes a dock and assigns sequential numbers
336// to existing rows. Basically it takes out the gaps; so if a
337// dock has rows with numbers 0,2,5, they will become 0,1,2
338static void RenumberDockRows(wxDockInfoPtrArray& docks)
339{
340 int i, dock_count, j, pane_count;
341 for (i = 0, dock_count = docks.GetCount(); i < dock_count; ++i)
342 {
343 wxDockInfo& dock = *docks.Item(i);
344 dock.dock_row = i;
345 for (j = 0, pane_count = dock.panes.GetCount(); j < pane_count; ++j)
346 dock.panes.Item(j)->dock_row = i;
347 }
348}
349
350
cc0196c7
BW
351// SetActivePane() sets the active pane, as well as cycles through
352// every other pane and makes sure that all others' active flags
353// are turned off
50acee04
JS
354static void SetActivePane(wxPaneInfoArray& panes, wxWindow* active_pane)
355{
356 int i, pane_count;
357 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
358 {
359 wxPaneInfo& pane = panes.Item(i);
360 pane.state &= ~wxPaneInfo::optionActive;
361 if (pane.window == active_pane)
362 pane.state |= wxPaneInfo::optionActive;
363 }
364}
365
366
367// this function is used to sort panes by dock position
368static int PaneSortFunc(wxPaneInfo** p1, wxPaneInfo** p2)
369{
370 return ((*p1)->dock_pos < (*p2)->dock_pos) ? -1 : 1;
371}
372
373
374// -- wxFrameManager class implementation --
375
376
377BEGIN_EVENT_TABLE(wxFrameManager, wxEvtHandler)
378 EVT_AUI_PANEBUTTON(wxFrameManager::OnPaneButton)
379 EVT_PAINT(wxFrameManager::OnPaint)
380 EVT_ERASE_BACKGROUND(wxFrameManager::OnEraseBackground)
381 EVT_SIZE(wxFrameManager::OnSize)
382 EVT_SET_CURSOR(wxFrameManager::OnSetCursor)
383 EVT_LEFT_DOWN(wxFrameManager::OnLeftDown)
384 EVT_LEFT_UP(wxFrameManager::OnLeftUp)
385 EVT_MOTION(wxFrameManager::OnMotion)
386 EVT_LEAVE_WINDOW(wxFrameManager::OnLeaveWindow)
387 EVT_CHILD_FOCUS(wxFrameManager::OnChildFocus)
388 EVT_TIMER(101, wxFrameManager::OnHintFadeTimer)
389END_EVENT_TABLE()
390
391
392wxFrameManager::wxFrameManager(wxFrame* frame, unsigned int flags)
393{
394 m_action = actionNone;
395 m_last_mouse_move = wxPoint();
396 m_hover_button = NULL;
397 m_art = new wxDefaultDockArt;
398 m_hint_wnd = NULL;
399 m_flags = flags;
400
401 if (frame)
402 {
403 SetFrame(frame);
404 }
405}
406
407wxFrameManager::~wxFrameManager()
408{
409 delete m_art;
410}
411
412// GetPane() looks up a wxPaneInfo structure based
413// on the supplied window pointer. Upon failure, GetPane()
414// returns an empty wxPaneInfo, a condition which can be checked
415// by calling wxPaneInfo::IsOk().
416//
417// The pane info's structure may then be modified. Once a pane's
418// info is modified, wxFrameManager::Update() must be called to
419// realize the changes in the UI.
420
421wxPaneInfo& wxFrameManager::GetPane(wxWindow* window)
422{
423 int i, pane_count;
424 for (i = 0, pane_count = m_panes.GetCount(); i < pane_count; ++i)
425 {
426 wxPaneInfo& p = m_panes.Item(i);
427 if (p.window == window)
428 return p;
429 }
430 return wxNullPaneInfo;
431}
432
433// this version of GetPane() looks up a pane based on a
434// 'pane name', see above comment for more info
435wxPaneInfo& wxFrameManager::GetPane(const wxString& name)
436{
437 int i, pane_count;
438 for (i = 0, pane_count = m_panes.GetCount(); i < pane_count; ++i)
439 {
440 wxPaneInfo& p = m_panes.Item(i);
441 if (p.name == name)
442 return p;
443 }
444 return wxNullPaneInfo;
445}
446
447// GetAllPanes() returns a reference to all the pane info structures
448wxPaneInfoArray& wxFrameManager::GetAllPanes()
449{
450 return m_panes;
451}
452
453// HitTest() is an internal function which determines
454// which UI item the specified coordinates are over
455// (x,y) specify a position in client coordinates
456wxDockUIPart* wxFrameManager::HitTest(int x, int y)
457{
458 wxDockUIPart* result = NULL;
459
460 int i, part_count;
461 for (i = 0, part_count = m_uiparts.GetCount(); i < part_count; ++i)
462 {
463 wxDockUIPart* item = &m_uiparts.Item(i);
be66f18e
WS
464
465 // we are not interested in typeDock, because this space
50acee04
JS
466 // isn't used to draw anything, just for measurements;
467 // besides, the entire dock area is covered with other
468 // rectangles, which we are interested in.
469 if (item->type == wxDockUIPart::typeDock)
470 continue;
be66f18e 471
50acee04
JS
472 // if we already have a hit on a more specific item, we are not
473 // interested in a pane hit. If, however, we don't already have
474 // a hit, returning a pane hit is necessary for some operations
475 if ((item->type == wxDockUIPart::typePane ||
476 item->type == wxDockUIPart::typePaneBorder) && result)
477 continue;
be66f18e 478
50acee04
JS
479 // if the point is inside the rectangle, we have a hit
480 if (item->rect.Inside(x,y))
481 result = item;
482 }
be66f18e 483
50acee04
JS
484 return result;
485}
486
487
488// SetFlags() and GetFlags() allow the owner to set various
489// options which are global to wxFrameManager
490void wxFrameManager::SetFlags(unsigned int flags)
491{
492 m_flags = flags;
493}
494
495unsigned int wxFrameManager::GetFlags() const
496{
497 return m_flags;
498}
499
500
501// SetFrame() is usually called once when the frame
502// manager class is being initialized. "frame" specifies
503// the frame which should be managed by the frame mananger
504void wxFrameManager::SetFrame(wxFrame* frame)
505{
506 wxASSERT_MSG(frame, wxT("specified frame must be non-NULL"));
507
508 m_frame = frame;
509 m_frame->PushEventHandler(this);
510
511#if wxUSE_MDI
512 // if the owner is going to manage an MDI parent frame,
513 // we need to add the MDI client window as the default
514 // center pane
515
516 if (frame->IsKindOf(CLASSINFO(wxMDIParentFrame)))
517 {
518 wxMDIParentFrame* mdi_frame = (wxMDIParentFrame*)frame;
9db1b540 519 wxWindow* client_window = mdi_frame->GetClientWindow();
50acee04
JS
520
521 wxASSERT_MSG(client_window, wxT("Client window is NULL!"));
522
523 AddPane(client_window,
524 wxPaneInfo().Name(wxT("mdiclient")).
525 CenterPane().PaneBorder(false));
526 }
527#endif
528}
529
530
531// UnInit() must be called, usually in the destructor
532// of the frame class. If it is not called, usually this
533// will result in a crash upon program exit
534void wxFrameManager::UnInit()
535{
536 m_frame->RemoveEventHandler(this);
537}
538
539// GetFrame() returns the frame pointer being managed by wxFrameManager
540wxFrame* wxFrameManager::GetFrame() const
541{
542 return m_frame;
543}
544
545wxDockArt* wxFrameManager::GetArtProvider() const
546{
547 return m_art;
548}
549
550void wxFrameManager::ProcessMgrEvent(wxFrameManagerEvent& event)
551{
552 // first, give the owner frame a chance to override
553 if (m_frame)
554 {
555 if (m_frame->ProcessEvent(event))
556 return;
557 }
558
559 ProcessEvent(event);
560}
561
562// SetArtProvider() instructs wxFrameManager to use the
563// specified art provider for all drawing calls. This allows
cc0196c7
BW
564// plugable look-and-feel features. The pointer that is
565// passed to this method subsequently belongs to wxFrameManager,
566// and is deleted in the frame manager destructor
50acee04
JS
567void wxFrameManager::SetArtProvider(wxDockArt* art_provider)
568{
569 // delete the last art provider, if any
570 delete m_art;
be66f18e 571
50acee04
JS
572 // assign the new art provider
573 m_art = art_provider;
574}
575
cc0196c7 576
50acee04
JS
577bool wxFrameManager::AddPane(wxWindow* window, const wxPaneInfo& pane_info)
578{
579 // check if the pane has a valid window
580 if (!window)
581 return false;
582
583 // check if the pane already exists
584 if (GetPane(pane_info.window).IsOk())
585 return false;
586
587 m_panes.Add(pane_info);
588
589 wxPaneInfo& pinfo = m_panes.Last();
590
591 // set the pane window
592 pinfo.window = window;
be66f18e 593
50acee04 594 // if the pane's name identifier is blank, create a random string
be66f18e 595 if (pinfo.name.empty())
50acee04 596 {
0e9c9923 597 pinfo.name.Printf(wxT("%08lx%08x%08x%08lx"),
50acee04
JS
598 ((unsigned long)pinfo.window) & 0xffffffff,
599 (unsigned int)time(NULL),
99ae6124
WS
600#ifdef __WXWINCE__
601 (unsigned int)GetTickCount(),
602#else
0e9c9923 603 (unsigned int)clock(),
99ae6124 604#endif
0e9c9923 605 (unsigned long)m_panes.GetCount());
50acee04 606 }
be66f18e 607
50acee04
JS
608 // set initial proportion (if not already set)
609 if (pinfo.dock_proportion == 0)
610 pinfo.dock_proportion = 100000;
611
612 if (pinfo.HasCloseButton() &&
613 pinfo.buttons.size() == 0)
614 {
615 wxPaneButton button;
616 button.button_id = wxPaneInfo::buttonClose;
617 pinfo.buttons.Add(button);
618 }
be66f18e 619
50acee04
JS
620 if (pinfo.best_size == wxDefaultSize &&
621 pinfo.window)
622 {
623 pinfo.best_size = pinfo.window->GetClientSize();
624
625 if (pinfo.window->IsKindOf(CLASSINFO(wxToolBar)))
626 {
627 // GetClientSize() doesn't get the best size for
628 // a toolbar under some newer versions of wxWidgets,
629 // so use GetBestSize()
630 pinfo.best_size = pinfo.window->GetBestSize();
be66f18e 631
50acee04
JS
632 // for some reason, wxToolBar::GetBestSize() is returning
633 // a size that is a pixel shy of the correct amount.
634 // I believe this to be the correct action, until
635 // wxToolBar::GetBestSize() is fixed. Is this assumption
636 // correct?
637 pinfo.best_size.y++;
638 }
be66f18e 639
50acee04
JS
640 if (pinfo.min_size != wxDefaultSize)
641 {
642 if (pinfo.best_size.x < pinfo.min_size.x)
643 pinfo.best_size.x = pinfo.min_size.x;
644 if (pinfo.best_size.y < pinfo.min_size.y)
645 pinfo.best_size.y = pinfo.min_size.y;
646 }
647 }
648
649 return true;
650}
651
652bool wxFrameManager::AddPane(wxWindow* window,
653 int direction,
654 const wxString& caption)
655{
656 wxPaneInfo pinfo;
657 pinfo.Caption(caption);
658 switch (direction)
659 {
660 case wxTOP: pinfo.Top(); break;
661 case wxBOTTOM: pinfo.Bottom(); break;
662 case wxLEFT: pinfo.Left(); break;
663 case wxRIGHT: pinfo.Right(); break;
664 case wxCENTER: pinfo.CenterPane(); break;
665 }
666 return AddPane(window, pinfo);
667}
668
669bool wxFrameManager::InsertPane(wxWindow* window, const wxPaneInfo& pane_info,
670 int insert_level)
671{
672 // shift the panes around, depending on the insert level
673 switch (insert_level)
674 {
675 case wxAUI_INSERT_PANE:
676 DoInsertPane(m_panes,
677 pane_info.dock_direction,
678 pane_info.dock_layer,
679 pane_info.dock_row,
680 pane_info.dock_pos);
681 break;
682 case wxAUI_INSERT_ROW:
683 DoInsertDockRow(m_panes,
684 pane_info.dock_direction,
685 pane_info.dock_layer,
686 pane_info.dock_row);
687 break;
688 case wxAUI_INSERT_DOCK:
689 DoInsertDockLayer(m_panes,
690 pane_info.dock_direction,
691 pane_info.dock_layer);
692 break;
693 }
be66f18e 694
50acee04
JS
695 // if the window already exists, we are basically just moving/inserting the
696 // existing window. If it doesn't exist, we need to add it and insert it
697 wxPaneInfo& existing_pane = GetPane(window);
698 if (!existing_pane.IsOk())
699 {
700 return AddPane(window, pane_info);
701 }
696978ee 702 else
50acee04
JS
703 {
704 if (pane_info.IsFloating())
705 {
706 existing_pane.Float();
707 if (pane_info.floating_pos != wxDefaultPosition)
708 existing_pane.FloatingPosition(pane_info.floating_pos);
709 if (pane_info.floating_size != wxDefaultSize)
710 existing_pane.FloatingSize(pane_info.floating_size);
711 }
712 else
713 {
714 existing_pane.Direction(pane_info.dock_direction);
715 existing_pane.Layer(pane_info.dock_layer);
716 existing_pane.Row(pane_info.dock_row);
717 existing_pane.Position(pane_info.dock_pos);
718 }
719 }
720
721 return true;
722}
723
be66f18e 724
cc0196c7
BW
725// DetachPane() removes a pane from the frame manager. This
726// method will not destroy the window that is removed.
50acee04
JS
727bool wxFrameManager::DetachPane(wxWindow* window)
728{
729 int i, count;
730 for (i = 0, count = m_panes.GetCount(); i < count; ++i)
731 {
be66f18e 732 wxPaneInfo& p = m_panes.Item(i);
50acee04
JS
733 if (p.window == window)
734 {
735 if (p.frame)
736 {
737 // we have a floating frame which is being detached. We need to
738 // reparent it to m_frame and destroy the floating frame
739
740 // reduce flicker
741 p.window->SetSize(1,1);
742 p.frame->Show(false);
743
744 // reparent to m_frame and destroy the pane
745 p.window->Reparent(m_frame);
746 p.frame->SetSizer(NULL);
747 p.frame->Destroy();
748 p.frame = NULL;
749 }
750 m_panes.RemoveAt(i);
751 return true;
752 }
753 }
754 return false;
755}
756
757
758// EscapeDelimiters() changes ";" into "\;" and "|" into "\|"
759// in the input string. This is an internal functions which is
760// used for saving perspectives
761static wxString EscapeDelimiters(const wxString& s)
762{
763 wxString result;
be66f18e 764 result.Alloc(s.length());
50acee04
JS
765 const wxChar* ch = s.c_str();
766 while (*ch)
767 {
768 if (*ch == wxT(';') || *ch == wxT('|'))
769 result += wxT('\\');
770 result += *ch;
771 ++ch;
772 }
773 return result;
774}
775
776
777// SavePerspective() saves all pane information as a single string.
778// This string may later be fed into LoadPerspective() to restore
779// all pane settings. This save and load mechanism allows an
780// exact pane configuration to be saved and restored at a later time
781
782wxString wxFrameManager::SavePerspective()
783{
784 wxString result;
785 result.Alloc(500);
786 result = wxT("layout1|");
787
788 int pane_i, pane_count = m_panes.GetCount();
789 for (pane_i = 0; pane_i < pane_count; ++pane_i)
790 {
791 wxPaneInfo& pane = m_panes.Item(pane_i);
be66f18e 792
50acee04
JS
793 result += wxT("name=");
794 result += EscapeDelimiters(pane.name);
795 result += wxT(";");
be66f18e 796
50acee04
JS
797 result += wxT("caption=");
798 result += EscapeDelimiters(pane.caption);
799 result += wxT(";");
be66f18e 800
50acee04
JS
801 result += wxString::Format(wxT("state=%u;"), pane.state);
802 result += wxString::Format(wxT("dir=%d;"), pane.dock_direction);
803 result += wxString::Format(wxT("layer=%d;"), pane.dock_layer);
804 result += wxString::Format(wxT("row=%d;"), pane.dock_row);
805 result += wxString::Format(wxT("pos=%d;"), pane.dock_pos);
806 result += wxString::Format(wxT("prop=%d;"), pane.dock_proportion);
807 result += wxString::Format(wxT("bestw=%d;"), pane.best_size.x);
808 result += wxString::Format(wxT("besth=%d;"), pane.best_size.y);
809 result += wxString::Format(wxT("minw=%d;"), pane.min_size.x);
810 result += wxString::Format(wxT("minh=%d;"), pane.min_size.y);
811 result += wxString::Format(wxT("maxw=%d;"), pane.max_size.x);
812 result += wxString::Format(wxT("maxh=%d;"), pane.max_size.y);
813 result += wxString::Format(wxT("floatx=%d;"), pane.floating_pos.x);
814 result += wxString::Format(wxT("floaty=%d;"), pane.floating_pos.y);
815 result += wxString::Format(wxT("floatw=%d;"), pane.floating_size.x);
816 result += wxString::Format(wxT("floath=%d"), pane.floating_size.y);
817 result += wxT("|");
818 }
be66f18e 819
50acee04
JS
820 int dock_i, dock_count = m_docks.GetCount();
821 for (dock_i = 0; dock_i < dock_count; ++dock_i)
822 {
823 wxDockInfo& dock = m_docks.Item(dock_i);
be66f18e 824
50acee04
JS
825 result += wxString::Format(wxT("dock_size(%d,%d,%d)=%d|"),
826 dock.dock_direction, dock.dock_layer,
827 dock.dock_row, dock.size);
828 }
be66f18e 829
50acee04
JS
830 return result;
831}
832
833// LoadPerspective() loads a layout which was saved with SavePerspective()
834// If the "update" flag parameter is true, the GUI will immediately be updated
835
836bool wxFrameManager::LoadPerspective(const wxString& layout, bool update)
837{
838 wxString input = layout;
839 wxString part;
be66f18e 840
50acee04
JS
841 // check layout string version
842 part = input.BeforeFirst(wxT('|'));
843 input = input.AfterFirst(wxT('|'));
844 part.Trim(true);
845 part.Trim(false);
846 if (part != wxT("layout1"))
847 return false;
be66f18e
WS
848
849
50acee04
JS
850 // mark all panes currently managed as docked and hidden
851 int pane_i, pane_count = m_panes.GetCount();
852 for (pane_i = 0; pane_i < pane_count; ++pane_i)
853 m_panes.Item(pane_i).Dock().Hide();
854
855 // clear out the dock array; this will be reconstructed
856 m_docks.Clear();
be66f18e 857
50acee04
JS
858 // replace escaped characters so we can
859 // split up the string easily
860 input.Replace(wxT("\\|"), wxT("\a"));
861 input.Replace(wxT("\\;"), wxT("\b"));
be66f18e 862
50acee04
JS
863 while (1)
864 {
865 wxPaneInfo pane;
866
867 wxString pane_part = input.BeforeFirst(wxT('|'));
868 input = input.AfterFirst(wxT('|'));
869 pane_part.Trim(true);
870
871 // if the string is empty, we're done parsing
be66f18e 872 if (pane_part.empty())
50acee04
JS
873 break;
874
875
876 if (pane_part.Left(9) == wxT("dock_size"))
877 {
878 wxString val_name = pane_part.BeforeFirst(wxT('='));
879 wxString value = pane_part.AfterFirst(wxT('='));
be66f18e 880
50acee04
JS
881 long dir, layer, row, size;
882 wxString piece = val_name.AfterFirst(wxT('('));
883 piece = piece.BeforeLast(wxT(')'));
884 piece.BeforeFirst(wxT(',')).ToLong(&dir);
885 piece = piece.AfterFirst(wxT(','));
886 piece.BeforeFirst(wxT(',')).ToLong(&layer);
887 piece.AfterFirst(wxT(',')).ToLong(&row);
888 value.ToLong(&size);
be66f18e 889
50acee04
JS
890 wxDockInfo dock;
891 dock.dock_direction = dir;
892 dock.dock_layer = layer;
893 dock.dock_row = row;
894 dock.size = size;
895 m_docks.Add(dock);
896 continue;
897 }
898
899 while (1)
900 {
901 wxString val_part = pane_part.BeforeFirst(wxT(';'));
902 pane_part = pane_part.AfterFirst(wxT(';'));
903 wxString val_name = val_part.BeforeFirst(wxT('='));
904 wxString value = val_part.AfterFirst(wxT('='));
905 val_name.MakeLower();
906 val_name.Trim(true);
907 val_name.Trim(false);
908 value.Trim(true);
909 value.Trim(false);
be66f18e
WS
910
911 if (val_name.empty())
50acee04
JS
912 break;
913
914 if (val_name == wxT("name"))
915 pane.name = value;
916 else if (val_name == wxT("caption"))
917 pane.caption = value;
918 else if (val_name == wxT("state"))
919 pane.state = (unsigned int)wxAtoi(value.c_str());
920 else if (val_name == wxT("dir"))
921 pane.dock_direction = wxAtoi(value.c_str());
922 else if (val_name == wxT("layer"))
923 pane.dock_layer = wxAtoi(value.c_str());
924 else if (val_name == wxT("row"))
925 pane.dock_row = wxAtoi(value.c_str());
926 else if (val_name == wxT("pos"))
927 pane.dock_pos = wxAtoi(value.c_str());
928 else if (val_name == wxT("prop"))
929 pane.dock_proportion = wxAtoi(value.c_str());
930 else if (val_name == wxT("bestw"))
931 pane.best_size.x = wxAtoi(value.c_str());
932 else if (val_name == wxT("besth"))
933 pane.best_size.y = wxAtoi(value.c_str());
934 else if (val_name == wxT("minw"))
935 pane.min_size.x = wxAtoi(value.c_str());
936 else if (val_name == wxT("minh"))
937 pane.min_size.y = wxAtoi(value.c_str());
938 else if (val_name == wxT("maxw"))
939 pane.max_size.x = wxAtoi(value.c_str());
940 else if (val_name == wxT("maxh"))
941 pane.max_size.y = wxAtoi(value.c_str());
942 else if (val_name == wxT("floatx"))
943 pane.floating_pos.x = wxAtoi(value.c_str());
944 else if (val_name == wxT("floaty"))
945 pane.floating_pos.y = wxAtoi(value.c_str());
946 else if (val_name == wxT("floatw"))
947 pane.floating_size.x = wxAtoi(value.c_str());
948 else if (val_name == wxT("floath"))
949 pane.floating_size.y = wxAtoi(value.c_str());
950 else {
951 wxFAIL_MSG(wxT("Bad Perspective String"));
952 }
953 }
be66f18e 954
50acee04
JS
955 // replace escaped characters so we can
956 // split up the string easily
957 pane.name.Replace(wxT("\a"), wxT("|"));
958 pane.name.Replace(wxT("\b"), wxT(";"));
959 pane.caption.Replace(wxT("\a"), wxT("|"));
960 pane.caption.Replace(wxT("\b"), wxT(";"));
be66f18e 961
50acee04
JS
962 wxPaneInfo& p = GetPane(pane.name);
963 if (!p.IsOk())
964 {
965 // the pane window couldn't be found
966 // in the existing layout
967 return false;
968 }
be66f18e 969
50acee04
JS
970 pane.window = p.window;
971 pane.frame = p.frame;
972 pane.buttons = p.buttons;
973 p = pane;
974 }
be66f18e 975
50acee04
JS
976 if (update)
977 Update();
978
979 return true;
980}
981
982
983void wxFrameManager::GetPanePositionsAndSizes(wxDockInfo& dock,
984 wxArrayInt& positions,
985 wxArrayInt& sizes)
986{
987 int caption_size = m_art->GetMetric(wxAUI_ART_CAPTION_SIZE);
988 int pane_border_size = m_art->GetMetric(wxAUI_ART_PANE_BORDER_SIZE);
989 int gripper_size = m_art->GetMetric(wxAUI_ART_GRIPPER_SIZE);
990
991 positions.Empty();
992 sizes.Empty();
993
994 int offset, action_pane = -1;
995 int pane_i, pane_count = dock.panes.GetCount();
996
997 // find the pane marked as our action pane
998 for (pane_i = 0; pane_i < pane_count; ++pane_i)
999 {
1000 wxPaneInfo& pane = *(dock.panes.Item(pane_i));
1001
1002 if (pane.state & wxPaneInfo::actionPane)
1003 {
1004 wxASSERT_MSG(action_pane==-1, wxT("Too many fixed action panes"));
1005 action_pane = pane_i;
1006 }
1007 }
be66f18e 1008
50acee04
JS
1009 // set up each panes default position, and
1010 // determine the size (width or height, depending
1011 // on the dock's orientation) of each pane
1012 for (pane_i = 0; pane_i < pane_count; ++pane_i)
1013 {
1014 wxPaneInfo& pane = *(dock.panes.Item(pane_i));
1015 positions.Add(pane.dock_pos);
1016 int size = 0;
be66f18e 1017
50acee04
JS
1018 if (pane.HasBorder())
1019 size += (pane_border_size*2);
be66f18e 1020
50acee04
JS
1021 if (dock.IsHorizontal())
1022 {
1023 if (pane.HasGripper() && !pane.HasGripperTop())
1024 size += gripper_size;
1025 size += pane.best_size.x;
1026 }
1027 else
1028 {
1029 if (pane.HasGripper() && pane.HasGripperTop())
1030 size += gripper_size;
1031
1032 if (pane.HasCaption())
be66f18e 1033 size += caption_size;
50acee04
JS
1034 size += pane.best_size.y;
1035 }
be66f18e 1036
50acee04
JS
1037 sizes.Add(size);
1038 }
1039
1040 // if there is no action pane, just return the default
1041 // positions (as specified in pane.pane_pos)
1042 if (action_pane == -1)
1043 return;
1044
1045 offset = 0;
1046 for (pane_i = action_pane-1; pane_i >= 0; --pane_i)
1047 {
1048 int amount = positions[pane_i+1] - (positions[pane_i] + sizes[pane_i]);
1049
1050 if (amount >= 0)
1051 offset += amount;
1052 else
1053 positions[pane_i] -= -amount;
1054
1055 offset += sizes[pane_i];
1056 }
be66f18e 1057
50acee04
JS
1058 // if the dock mode is fixed, make sure none of the panes
1059 // overlap; we will bump panes that overlap
1060 offset = 0;
1061 for (pane_i = action_pane; pane_i < pane_count; ++pane_i)
1062 {
1063 int amount = positions[pane_i] - offset;
1064 if (amount >= 0)
1065 offset += amount;
1066 else
1067 positions[pane_i] += -amount;
1068
1069 offset += sizes[pane_i];
1070 }
1071}
1072
1073
1074void wxFrameManager::LayoutAddPane(wxSizer* cont,
1075 wxDockInfo& dock,
1076 wxPaneInfo& pane,
1077 wxDockUIPartArray& uiparts,
1078 bool spacer_only)
be66f18e 1079{
50acee04
JS
1080 wxDockUIPart part;
1081 wxSizerItem* sizer_item;
1082
1083 int caption_size = m_art->GetMetric(wxAUI_ART_CAPTION_SIZE);
1084 int gripper_size = m_art->GetMetric(wxAUI_ART_GRIPPER_SIZE);
1085 int pane_border_size = m_art->GetMetric(wxAUI_ART_PANE_BORDER_SIZE);
1086 int pane_button_size = m_art->GetMetric(wxAUI_ART_PANE_BUTTON_SIZE);
1087
1088 // find out the orientation of the item (orientation for panes
1089 // is the same as the dock's orientation)
1090 int orientation;
1091 if (dock.IsHorizontal())
1092 orientation = wxHORIZONTAL;
1093 else
1094 orientation = wxVERTICAL;
1095
1096 // this variable will store the proportion
1097 // value that the pane will receive
1098 int pane_proportion = pane.dock_proportion;
1099
1100 wxBoxSizer* horz_pane_sizer = new wxBoxSizer(wxHORIZONTAL);
1101 wxBoxSizer* vert_pane_sizer = new wxBoxSizer(wxVERTICAL);
1102
1103 if (pane.HasGripper())
1104 {
1105 if (pane.HasGripperTop())
1106 sizer_item = vert_pane_sizer ->Add(1, gripper_size, 0, wxEXPAND);
be66f18e 1107 else
50acee04
JS
1108 sizer_item = horz_pane_sizer ->Add(gripper_size, 1, 0, wxEXPAND);
1109
1110 part.type = wxDockUIPart::typeGripper;
1111 part.dock = &dock;
1112 part.pane = &pane;
1113 part.button = NULL;
1114 part.orientation = orientation;
1115 part.cont_sizer = horz_pane_sizer;
1116 part.sizer_item = sizer_item;
1117 uiparts.Add(part);
1118 }
1119
1120 if (pane.HasCaption())
1121 {
1122 // create the caption sizer
1123 wxBoxSizer* caption_sizer = new wxBoxSizer(wxHORIZONTAL);
1124
1125 sizer_item = caption_sizer->Add(1, caption_size, 1, wxEXPAND);
1126
1127 part.type = wxDockUIPart::typeCaption;
1128 part.dock = &dock;
1129 part.pane = &pane;
1130 part.button = NULL;
1131 part.orientation = orientation;
1132 part.cont_sizer = vert_pane_sizer;
1133 part.sizer_item = sizer_item;
1134 int caption_part_idx = uiparts.GetCount();
1135 uiparts.Add(part);
1136
1137 // add pane buttons to the caption
1138 int i, button_count;
1139 for (i = 0, button_count = pane.buttons.GetCount();
1140 i < button_count; ++i)
1141 {
1142 wxPaneButton& button = pane.buttons.Item(i);
1143
1144 sizer_item = caption_sizer->Add(pane_button_size,
1145 caption_size,
1146 0, wxEXPAND);
1147
1148 part.type = wxDockUIPart::typePaneButton;
1149 part.dock = &dock;
1150 part.pane = &pane;
1151 part.button = &button;
1152 part.orientation = orientation;
1153 part.cont_sizer = caption_sizer;
1154 part.sizer_item = sizer_item;
1155 uiparts.Add(part);
1156 }
1157
1158 // add the caption sizer
1159 sizer_item = vert_pane_sizer->Add(caption_sizer, 0, wxEXPAND);
1160
1161 uiparts.Item(caption_part_idx).sizer_item = sizer_item;
1162 }
1163
1164 // add the pane window itself
1165 if (spacer_only)
1166 {
1167 sizer_item = vert_pane_sizer->Add(1, 1, 1, wxEXPAND);
1168 }
696978ee 1169 else
50acee04
JS
1170 {
1171 sizer_item = vert_pane_sizer->Add(pane.window, 1, wxEXPAND);
5c62cb6c
AB
1172 // Don't do this because it breaks the pane size in floating windows
1173 // vert_pane_sizer->SetItemMinSize(pane.window, 1, 1);
50acee04
JS
1174 }
1175
1176 part.type = wxDockUIPart::typePane;
1177 part.dock = &dock;
1178 part.pane = &pane;
1179 part.button = NULL;
1180 part.orientation = orientation;
1181 part.cont_sizer = vert_pane_sizer;
1182 part.sizer_item = sizer_item;
1183 uiparts.Add(part);
1184
1185
1186 // determine if the pane should have a minimum size; if the pane is
1187 // non-resizable (fixed) then we must set a minimum size. Alternitavely,
1188 // if the pane.min_size is set, we must use that value as well
be66f18e 1189
50acee04
JS
1190 wxSize min_size = pane.min_size;
1191 if (pane.IsFixed())
1192 {
1193 if (min_size == wxDefaultSize)
1194 {
1195 min_size = pane.best_size;
1196 pane_proportion = 0;
1197 }
1198 }
be66f18e 1199
50acee04
JS
1200 if (min_size != wxDefaultSize)
1201 {
1202 vert_pane_sizer->SetItemMinSize(
1203 vert_pane_sizer->GetChildren().GetCount()-1,
1204 min_size.x, min_size.y);
1205 }
1206
1207
1208 // add the verticle sizer (caption, pane window) to the
1209 // horizontal sizer (gripper, verticle sizer)
1210 horz_pane_sizer->Add(vert_pane_sizer, 1, wxEXPAND);
1211
1212 // finally, add the pane sizer to the dock sizer
1213
1214 if (pane.HasBorder())
1215 {
1216 // allowing space for the pane's border
1217 sizer_item = cont->Add(horz_pane_sizer, pane_proportion,
1218 wxEXPAND | wxALL, pane_border_size);
1219
1220 part.type = wxDockUIPart::typePaneBorder;
1221 part.dock = &dock;
1222 part.pane = &pane;
1223 part.button = NULL;
1224 part.orientation = orientation;
1225 part.cont_sizer = cont;
1226 part.sizer_item = sizer_item;
1227 uiparts.Add(part);
1228 }
696978ee 1229 else
50acee04
JS
1230 {
1231 sizer_item = cont->Add(horz_pane_sizer, pane_proportion, wxEXPAND);
1232 }
1233}
1234
1235void wxFrameManager::LayoutAddDock(wxSizer* cont,
1236 wxDockInfo& dock,
1237 wxDockUIPartArray& uiparts,
1238 bool spacer_only)
1239{
1240 wxSizerItem* sizer_item;
1241 wxDockUIPart part;
1242
1243 int sash_size = m_art->GetMetric(wxAUI_ART_SASH_SIZE);
1244 int orientation = dock.IsHorizontal() ? wxHORIZONTAL : wxVERTICAL;
1245
1246 // resizable bottom and right docks have a sash before them
1247 if (!dock.fixed && (dock.dock_direction == wxAUI_DOCK_BOTTOM ||
1248 dock.dock_direction == wxAUI_DOCK_RIGHT))
1249 {
1250 sizer_item = cont->Add(sash_size, sash_size, 0, wxEXPAND);
1251
1252 part.type = wxDockUIPart::typeDockSizer;
1253 part.orientation = orientation;
1254 part.dock = &dock;
1255 part.pane = NULL;
1256 part.button = NULL;
1257 part.cont_sizer = cont;
1258 part.sizer_item = sizer_item;
1259 uiparts.Add(part);
1260 }
1261
1262 // create the sizer for the dock
1263 wxSizer* dock_sizer = new wxBoxSizer(orientation);
1264
1265 // add each pane to the dock
1266 int pane_i, pane_count = dock.panes.GetCount();
1267
1268 if (dock.fixed)
1269 {
1270 wxArrayInt pane_positions, pane_sizes;
be66f18e 1271
50acee04
JS
1272 // figure out the real pane positions we will
1273 // use, without modifying the each pane's pane_pos member
1274 GetPanePositionsAndSizes(dock, pane_positions, pane_sizes);
1275
1276 int offset = 0;
1277 for (pane_i = 0; pane_i < pane_count; ++pane_i)
1278 {
1279 wxPaneInfo& pane = *(dock.panes.Item(pane_i));
1280 int pane_pos = pane_positions.Item(pane_i);
1281
1282 int amount = pane_pos - offset;
1283 if (amount > 0)
1284 {
1285 if (dock.IsVertical())
1286 sizer_item = dock_sizer->Add(1, amount, 0, wxEXPAND);
1287 else
1288 sizer_item = dock_sizer->Add(amount, 1, 0, wxEXPAND);
1289
1290 part.type = wxDockUIPart::typeBackground;
1291 part.dock = &dock;
1292 part.pane = NULL;
1293 part.button = NULL;
1294 part.orientation = (orientation==wxHORIZONTAL) ? wxVERTICAL:wxHORIZONTAL;
1295 part.cont_sizer = dock_sizer;
1296 part.sizer_item = sizer_item;
1297 uiparts.Add(part);
1298
1299 offset += amount;
1300 }
1301
1302 LayoutAddPane(dock_sizer, dock, pane, uiparts, spacer_only);
1303
1304 offset += pane_sizes.Item(pane_i);
1305 }
1306
1307 // at the end add a very small stretchable background area
1308 sizer_item = dock_sizer->Add(1,1, 1, wxEXPAND);
1309
1310 part.type = wxDockUIPart::typeBackground;
1311 part.dock = &dock;
1312 part.pane = NULL;
1313 part.button = NULL;
1314 part.orientation = orientation;
1315 part.cont_sizer = dock_sizer;
1316 part.sizer_item = sizer_item;
1317 uiparts.Add(part);
1318 }
696978ee 1319 else
50acee04
JS
1320 {
1321 for (pane_i = 0; pane_i < pane_count; ++pane_i)
1322 {
1323 wxPaneInfo& pane = *(dock.panes.Item(pane_i));
1324
1325 // if this is not the first pane being added,
1326 // we need to add a pane sizer
1327 if (pane_i > 0)
1328 {
1329 sizer_item = dock_sizer->Add(sash_size, sash_size, 0, wxEXPAND);
1330
1331 part.type = wxDockUIPart::typePaneSizer;
1332 part.dock = &dock;
1333 part.pane = dock.panes.Item(pane_i-1);
1334 part.button = NULL;
1335 part.orientation = (orientation==wxHORIZONTAL) ? wxVERTICAL:wxHORIZONTAL;
1336 part.cont_sizer = dock_sizer;
1337 part.sizer_item = sizer_item;
1338 uiparts.Add(part);
1339 }
1340
1341 LayoutAddPane(dock_sizer, dock, pane, uiparts, spacer_only);
1342 }
1343 }
1344
1345 if (dock.dock_direction == wxAUI_DOCK_CENTER)
1346 sizer_item = cont->Add(dock_sizer, 1, wxEXPAND);
1347 else
1348 sizer_item = cont->Add(dock_sizer, 0, wxEXPAND);
1349
1350 part.type = wxDockUIPart::typeDock;
1351 part.dock = &dock;
1352 part.pane = NULL;
1353 part.button = NULL;
1354 part.orientation = orientation;
1355 part.cont_sizer = cont;
1356 part.sizer_item = sizer_item;
1357 uiparts.Add(part);
1358
1359 if (dock.IsHorizontal())
1360 cont->SetItemMinSize(dock_sizer, 0, dock.size);
1361 else
1362 cont->SetItemMinSize(dock_sizer, dock.size, 0);
1363
1364 // top and left docks have a sash after them
1365 if (!dock.fixed && (dock.dock_direction == wxAUI_DOCK_TOP ||
1366 dock.dock_direction == wxAUI_DOCK_LEFT))
1367 {
1368 sizer_item = cont->Add(sash_size, sash_size, 0, wxEXPAND);
1369
1370 part.type = wxDockUIPart::typeDockSizer;
1371 part.dock = &dock;
1372 part.pane = NULL;
1373 part.button = NULL;
1374 part.orientation = orientation;
1375 part.cont_sizer = cont;
1376 part.sizer_item = sizer_item;
1377 uiparts.Add(part);
1378 }
1379}
1380
1381wxSizer* wxFrameManager::LayoutAll(wxPaneInfoArray& panes,
1382 wxDockInfoArray& docks,
1383 wxDockUIPartArray& uiparts,
1384 bool spacer_only)
1385{
1386 wxBoxSizer* container = new wxBoxSizer(wxVERTICAL);
1387
1388 int pane_border_size = m_art->GetMetric(wxAUI_ART_PANE_BORDER_SIZE);
1389 int caption_size = m_art->GetMetric(wxAUI_ART_CAPTION_SIZE);
1390 wxSize cli_size = m_frame->GetClientSize();
1391 int i, dock_count, pane_count;
be66f18e 1392
50acee04
JS
1393
1394 // empty all docks out
1395 for (i = 0, dock_count = docks.GetCount(); i < dock_count; ++i)
1396 docks.Item(i).panes.Empty();
be66f18e 1397
50acee04
JS
1398 // iterate through all known panes, filing each
1399 // of them into the appropriate dock. If the
1400 // pane does not exist in the dock, add it
1401 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
1402 {
1403 wxPaneInfo& p = panes.Item(i);
1404
1405 // find any docks in this layer
1406 wxDockInfo* dock;
1407 wxDockInfoPtrArray arr;
1408 FindDocks(docks, p.dock_direction, p.dock_layer, p.dock_row, arr);
1409
1410 if (arr.GetCount() > 0)
1411 {
1412 dock = arr.Item(0);
1413 }
1414 else
1415 {
1416 // dock was not found, so we need to create a new one
1417 wxDockInfo d;
1418 d.dock_direction = p.dock_direction;
1419 d.dock_layer = p.dock_layer;
1420 d.dock_row = p.dock_row;
1421 docks.Add(d);
1422 dock = &docks.Last();
1423 }
1424
1425
1426 if (p.IsDocked() && p.IsShown())
1427 {
1428 // remove the pane from any existing docks except this one
1429 RemovePaneFromDocks(docks, p, dock);
1430
1431 // pane needs to be added to the dock,
be66f18e 1432 // if it doesn't already exist
50acee04
JS
1433 if (!FindPaneInDock(*dock, p.window))
1434 dock->panes.Add(&p);
1435 }
1436 else
1437 {
1438 // remove the pane from any existing docks
1439 RemovePaneFromDocks(docks, p);
1440 }
1441
1442 }
1443
1444 // remove any empty docks
1445 for (i = docks.GetCount()-1; i >= 0; --i)
1446 {
1447 if (docks.Item(i).panes.GetCount() == 0)
1448 docks.RemoveAt(i);
1449 }
1450
1451 // configure the docks further
1452 for (i = 0, dock_count = docks.GetCount(); i < dock_count; ++i)
1453 {
1454 wxDockInfo& dock = docks.Item(i);
1455 int j, dock_pane_count = dock.panes.GetCount();
be66f18e 1456
50acee04
JS
1457 // sort the dock pane array by the pane's
1458 // dock position (dock_pos), in ascending order
1459 dock.panes.Sort(PaneSortFunc);
1460
1461 // for newly created docks, set up their initial size
1462 if (dock.size == 0)
1463 {
1464 int size = 0;
1465
1466 for (j = 0; j < dock_pane_count; ++j)
1467 {
1468 wxPaneInfo& pane = *dock.panes.Item(j);
1469 wxSize pane_size = pane.best_size;
1470 if (pane_size == wxDefaultSize)
1471 pane_size = pane.min_size;
1472 if (pane_size == wxDefaultSize)
1473 pane_size = pane.window->GetSize();
be66f18e 1474
50acee04
JS
1475 if (dock.IsHorizontal())
1476 size = wxMax(pane_size.y, size);
1477 else
1478 size = wxMax(pane_size.x, size);
1479 }
be66f18e 1480
50acee04
JS
1481 // add space for the border (two times), but only
1482 // if at least one pane inside the dock has a pane border
1483 for (j = 0; j < dock_pane_count; ++j)
1484 {
1485 if (dock.panes.Item(j)->HasBorder())
1486 {
1487 size += (pane_border_size*2);
1488 break;
1489 }
1490 }
1491
1492 // if pane is on the top or bottom, add the caption height,
1493 // but only if at least one pane inside the dock has a caption
1494 if (dock.IsHorizontal())
1495 {
1496 for (j = 0; j < dock_pane_count; ++j)
1497 {
1498 if (dock.panes.Item(j)->HasCaption())
1499 {
1500 size += caption_size;
1501 break;
1502 }
1503 }
1504 }
1505
1506 // new dock's size may not be more than 1/3 of the frame size
1507 if (dock.IsHorizontal())
1508 size = wxMin(size, cli_size.y/3);
1509 else
1510 size = wxMin(size, cli_size.x/3);
1511
1512 if (size < 10)
1513 size = 10;
1514 dock.size = size;
1515 }
1516
1517
1518 // determine the dock's minimum size
1519 bool plus_border = false;
1520 bool plus_caption = false;
1521 int dock_min_size = 0;
1522 for (j = 0; j < dock_pane_count; ++j)
1523 {
1524 wxPaneInfo& pane = *dock.panes.Item(j);
1525 if (pane.min_size != wxDefaultSize)
1526 {
1527 if (pane.HasBorder())
1528 plus_border = true;
1529 if (pane.HasCaption())
1530 plus_caption = true;
1531 if (dock.IsHorizontal())
1532 {
1533 if (pane.min_size.y > dock_min_size)
1534 dock_min_size = pane.min_size.y;
1535 }
1536 else
1537 {
1538 if (pane.min_size.x > dock_min_size)
1539 dock_min_size = pane.min_size.x;
1540 }
1541 }
1542 }
be66f18e 1543
50acee04
JS
1544 if (plus_border)
1545 dock_min_size += (pane_border_size*2);
1546 if (plus_caption && dock.IsHorizontal())
1547 dock_min_size += (caption_size);
be66f18e 1548
50acee04 1549 dock.min_size = dock_min_size;
be66f18e
WS
1550
1551
50acee04
JS
1552 // if the pane's current size is less than it's
1553 // minimum, increase the dock's size to it's minimum
1554 if (dock.size < dock.min_size)
1555 dock.size = dock.min_size;
1556
1557
1558 // determine the dock's mode (fixed or proportional);
1559 // determine whether the dock has only toolbars
1560 bool action_pane_marked = false;
1561 dock.fixed = true;
1562 dock.toolbar = true;
1563 for (j = 0; j < dock_pane_count; ++j)
1564 {
1565 wxPaneInfo& pane = *dock.panes.Item(j);
1566 if (!pane.IsFixed())
1567 dock.fixed = false;
1568 if (!pane.IsToolbar())
1569 dock.toolbar = false;
1570 if (pane.state & wxPaneInfo::actionPane)
1571 action_pane_marked = true;
1572 }
1573
1574
1575 // if the dock mode is proportional and not fixed-pixel,
1576 // reassign the dock_pos to the sequential 0, 1, 2, 3;
1577 // e.g. remove gaps like 1, 2, 30, 500
1578 if (!dock.fixed)
1579 {
1580 for (j = 0; j < dock_pane_count; ++j)
1581 {
1582 wxPaneInfo& pane = *dock.panes.Item(j);
1583 pane.dock_pos = j;
1584 }
1585 }
1586
1587 // if the dock mode is fixed, and none of the panes
1588 // are being moved right now, make sure the panes
1589 // do not overlap each other. If they do, we will
1590 // adjust the panes' positions
1591 if (dock.fixed && !action_pane_marked)
1592 {
1593 wxArrayInt pane_positions, pane_sizes;
1594 GetPanePositionsAndSizes(dock, pane_positions, pane_sizes);
be66f18e 1595
50acee04
JS
1596 int offset = 0;
1597 for (j = 0; j < dock_pane_count; ++j)
1598 {
1599 wxPaneInfo& pane = *(dock.panes.Item(j));
1600 pane.dock_pos = pane_positions[j];
1601
1602 int amount = pane.dock_pos - offset;
1603 if (amount >= 0)
1604 offset += amount;
1605 else
1606 pane.dock_pos += -amount;
1607
1608 offset += pane_sizes[j];
1609 }
1610 }
1611 }
be66f18e 1612
50acee04
JS
1613 // discover the maximum dock layer
1614 int max_layer = 0;
1615 for (i = 0; i < dock_count; ++i)
1616 max_layer = wxMax(max_layer, docks.Item(i).dock_layer);
be66f18e 1617
50acee04
JS
1618
1619 // clear out uiparts
1620 uiparts.Empty();
1621
1622 // create a bunch of box sizers,
1623 // from the innermost level outwards.
1624 wxSizer* cont = NULL;
1625 wxSizer* middle = NULL;
1626 int layer = 0;
1627 int row, row_count;
1628
1629 for (layer = 0; layer <= max_layer; ++layer)
1630 {
1631 wxDockInfoPtrArray arr;
1632
1633 // find any docks in this layer
1634 FindDocks(docks, -1, layer, -1, arr);
1635
1636 // if there aren't any, skip to the next layer
1637 if (arr.IsEmpty())
1638 continue;
1639
1640 wxSizer* old_cont = cont;
1641
1642 // create a container which will hold this layer's
1643 // docks (top, bottom, left, right)
1644 cont = new wxBoxSizer(wxVERTICAL);
1645
1646
1647 // find any top docks in this layer
1648 FindDocks(docks, wxAUI_DOCK_TOP, layer, -1, arr);
1649 RenumberDockRows(arr);
1650 if (!arr.IsEmpty())
1651 {
1652 for (row = 0, row_count = arr.GetCount(); row < row_count; ++row)
1653 LayoutAddDock(cont, *arr.Item(row), uiparts, spacer_only);
1654 }
1655
be66f18e 1656
50acee04
JS
1657 // fill out the middle layer (which consists
1658 // of left docks, content area and right docks)
be66f18e 1659
50acee04
JS
1660 middle = new wxBoxSizer(wxHORIZONTAL);
1661
1662 // find any left docks in this layer
1663 FindDocks(docks, wxAUI_DOCK_LEFT, layer, -1, arr);
1664 RenumberDockRows(arr);
1665 if (!arr.IsEmpty())
1666 {
1667 for (row = 0, row_count = arr.GetCount(); row < row_count; ++row)
1668 LayoutAddDock(middle, *arr.Item(row), uiparts, spacer_only);
1669 }
1670
1671 // add content dock (or previous layer's sizer
1672 // to the middle
1673 if (!old_cont)
1674 {
1675 // find any center docks
1676 FindDocks(docks, wxAUI_DOCK_CENTER, -1, -1, arr);
1677 if (!arr.IsEmpty())
1678 {
1679 for (row = 0,row_count = arr.GetCount(); row<row_count; ++row)
1680 LayoutAddDock(middle, *arr.Item(row), uiparts, spacer_only);
1681 }
1682 else
1683 {
1684 // there are no center docks, add a background area
1685 wxSizerItem* sizer_item = middle->Add(1,1, 1, wxEXPAND);
1686 wxDockUIPart part;
1687 part.type = wxDockUIPart::typeBackground;
1688 part.pane = NULL;
1689 part.dock = NULL;
1690 part.button = NULL;
1691 part.cont_sizer = middle;
1692 part.sizer_item = sizer_item;
1693 uiparts.Add(part);
1694 }
1695 }
1696 else
1697 {
1698 middle->Add(old_cont, 1, wxEXPAND);
1699 }
1700
1701 // find any right docks in this layer
1702 FindDocks(docks, wxAUI_DOCK_RIGHT, layer, -1, arr);
1703 RenumberDockRows(arr);
1704 if (!arr.IsEmpty())
1705 {
1706 for (row = arr.GetCount()-1; row >= 0; --row)
1707 LayoutAddDock(middle, *arr.Item(row), uiparts, spacer_only);
1708 }
1709
1710 cont->Add(middle, 1, wxEXPAND);
1711
1712
1713
1714 // find any bottom docks in this layer
1715 FindDocks(docks, wxAUI_DOCK_BOTTOM, layer, -1, arr);
1716 RenumberDockRows(arr);
1717 if (!arr.IsEmpty())
1718 {
1719 for (row = arr.GetCount()-1; row >= 0; --row)
1720 LayoutAddDock(cont, *arr.Item(row), uiparts, spacer_only);
1721 }
1722
1723 }
1724
1725 if (!cont)
1726 {
1727 // no sizer available, because there are no docks,
1728 // therefore we will create a simple background area
1729 cont = new wxBoxSizer(wxVERTICAL);
1730 wxSizerItem* sizer_item = cont->Add(1,1, 1, wxEXPAND);
1731 wxDockUIPart part;
1732 part.type = wxDockUIPart::typeBackground;
1733 part.pane = NULL;
1734 part.dock = NULL;
1735 part.button = NULL;
1736 part.cont_sizer = middle;
1737 part.sizer_item = sizer_item;
1738 uiparts.Add(part);
1739 }
1740
1741 container->Add(cont, 1, wxEXPAND);
1742 return container;
1743}
1744
1745
1746// Update() updates the layout. Whenever changes are made to
1747// one or more panes, this function should be called. It is the
1748// external entry point for running the layout engine.
1749
1750void wxFrameManager::Update()
1751{
1752 wxSizer* sizer;
1753 int i, pane_count = m_panes.GetCount();
1754
1755 // delete old sizer first
1756 m_frame->SetSizer(NULL);
1757
1758 // destroy floating panes which have been
1759 // redocked or are becoming non-floating
1760 for (i = 0; i < pane_count; ++i)
1761 {
1762 wxPaneInfo& p = m_panes.Item(i);
1763
1764 if (!p.IsFloating() && p.frame)
1765 {
1766 // because the pane is no longer in a floating, we need to
1767 // reparent it to m_frame and destroy the floating frame
be66f18e 1768
50acee04
JS
1769 // reduce flicker
1770 p.window->SetSize(1,1);
1771 p.frame->Show(false);
be66f18e 1772
50acee04
JS
1773 // reparent to m_frame and destroy the pane
1774 p.window->Reparent(m_frame);
1775 p.frame->SetSizer(NULL);
1776 p.frame->Destroy();
1777 p.frame = NULL;
1778 }
1779 }
1780
1781
1782 // create a layout for all of the panes
1783 sizer = LayoutAll(m_panes, m_docks, m_uiparts, false);
1784
1785 // hide or show panes as necessary,
1786 // and float panes as necessary
1787 for (i = 0; i < pane_count; ++i)
1788 {
1789 wxPaneInfo& p = m_panes.Item(i);
1790
1791 if (p.IsFloating())
1792 {
1793 if (p.frame == NULL)
1794 {
1795 // we need to create a frame for this
1796 // pane, which has recently been floated
1797 wxFloatingPane* frame = new wxFloatingPane(m_frame,
e5ab82d3
AB
1798 this,
1799 p);
be66f18e 1800
81cc4eb2 1801 // on MSW and Mac, if the owner desires transparent dragging, and
50acee04 1802 // the dragging is happening right now, then the floating
be66f18e 1803 // window should have this style by default
50acee04
JS
1804 if (m_action == actionDragFloatingPane &&
1805 (m_flags & wxAUI_MGR_TRANSPARENT_DRAG))
1806 MakeWindowTransparent(frame, 150);
be66f18e 1807
50acee04
JS
1808 frame->SetPaneWindow(p);
1809 p.frame = frame;
1810
1811 if (p.IsShown())
1812 {
1813 frame->Show();
1814 }
1815 }
1816 else
1817 {
1818 // frame already exists, make sure it's position
1819 // and size reflect the information in wxPaneInfo
1820 if (p.frame->GetPosition() != p.floating_pos)
1821 {
1822 p.frame->SetSize(p.floating_pos.x, p.floating_pos.y,
a1b3b608
WS
1823 wxDefaultCoord, wxDefaultCoord,
1824 wxSIZE_USE_EXISTING);
50acee04
JS
1825 //p.frame->Move(p.floating_pos.x, p.floating_pos.y);
1826 }
1827
1828 p.frame->Show(p.IsShown());
1829 }
1830 }
1831 else
1832 {
1833 p.window->Show(p.IsShown());
1834 }
1835
1836 // if "active panes" are no longer allowed, clear
1837 // any optionActive values from the pane states
1838 if ((m_flags & wxAUI_MGR_ALLOW_ACTIVE_PANE) == 0)
1839 {
1840 p.state &= ~wxPaneInfo::optionActive;
1841 }
1842 }
1843
1844
1845 // keep track of the old window rectangles so we can
1846 // refresh those windows whose rect has changed
1847 wxAuiRectArray old_pane_rects;
1848 for (i = 0; i < pane_count; ++i)
1849 {
1850 wxRect r;
1851 wxPaneInfo& p = m_panes.Item(i);
1852
1853 if (p.window && p.IsShown() && p.IsDocked())
1854 r = p.rect;
1855
1856 old_pane_rects.Add(r);
1857 }
1858
1859
1860
1861
1862 // apply the new sizer
1863 m_frame->SetSizer(sizer);
1864 m_frame->SetAutoLayout(false);
1865 DoFrameLayout();
1866
1867
1868
1869 // now that the frame layout is done, we need to check
1870 // the new pane rectangles against the old rectangles that
1871 // we saved a few lines above here. If the rectangles have
1872 // changed, the corresponding panes must also be updated
1873 for (i = 0; i < pane_count; ++i)
1874 {
1875 wxPaneInfo& p = m_panes.Item(i);
1876 if (p.window && p.window->IsShown() && p.IsDocked())
1877 {
1878 if (p.rect != old_pane_rects[i])
1879 {
1880 p.window->Refresh();
1881 p.window->Update();
1882 }
1883 }
1884 }
1885
1886
1887 Repaint();
be66f18e 1888
50acee04 1889 // set frame's minimum size
be66f18e 1890
50acee04
JS
1891/*
1892 // N.B. More work needs to be done on frame minimum sizes;
1893 // this is some intresting code that imposes the minimum size,
1894 // but we may want to include a more flexible mechanism or
1895 // options for multiple minimum-size modes, e.g. strict or lax
1896 wxSize min_size = sizer->GetMinSize();
1897 wxSize frame_size = m_frame->GetSize();
1898 wxSize client_size = m_frame->GetClientSize();
1899
1900 wxSize minframe_size(min_size.x+frame_size.x-client_size.x,
1901 min_size.y+frame_size.y-client_size.y );
be66f18e 1902
50acee04 1903 m_frame->SetMinSize(minframe_size);
be66f18e 1904
50acee04
JS
1905 if (frame_size.x < minframe_size.x ||
1906 frame_size.y < minframe_size.y)
1907 sizer->Fit(m_frame);
1908*/
1909}
1910
1911
1912// DoFrameLayout() is an internal function which invokes wxSizer::Layout
1913// on the frame's main sizer, then measures all the various UI items
1914// and updates their internal rectangles. This should always be called
1915// instead of calling m_frame->Layout() directly
1916
1917void wxFrameManager::DoFrameLayout()
1918{
1919 m_frame->Layout();
be66f18e 1920
50acee04
JS
1921 int i, part_count;
1922 for (i = 0, part_count = m_uiparts.GetCount(); i < part_count; ++i)
1923 {
1924 wxDockUIPart& part = m_uiparts.Item(i);
1925
1926 // get the rectangle of the UI part
1927 // originally, this code looked like this:
1928 // part.rect = wxRect(part.sizer_item->GetPosition(),
1929 // part.sizer_item->GetSize());
1930 // this worked quite well, with one exception: the mdi
be66f18e 1931 // client window had a "deferred" size variable
50acee04
JS
1932 // that returned the wrong size. It looks like
1933 // a bug in wx, because the former size of the window
1934 // was being returned. So, we will retrieve the part's
1935 // rectangle via other means
1936
1937
1938 part.rect = part.sizer_item->GetRect();
1939 int flag = part.sizer_item->GetFlag();
1940 int border = part.sizer_item->GetBorder();
1941 if (flag & wxTOP)
1942 {
1943 part.rect.y -= border;
1944 part.rect.height += border;
1945 }
1946 if (flag & wxLEFT)
1947 {
1948 part.rect.x -= border;
1949 part.rect.width += border;
1950 }
1951 if (flag & wxBOTTOM)
1952 part.rect.height += border;
1953 if (flag & wxRIGHT)
1954 part.rect.width += border;
1955
1956
1957 if (part.type == wxDockUIPart::typeDock)
1958 part.dock->rect = part.rect;
1959 if (part.type == wxDockUIPart::typePane)
1960 part.pane->rect = part.rect;
1961 }
1962}
1963
1964// GetPanePart() looks up the pane the pane border UI part (or the regular
1965// pane part if there is no border). This allows the caller to get the exact
1966// rectangle of the pane in question, including decorations like
1967// caption and border (if any).
1968
1969wxDockUIPart* wxFrameManager::GetPanePart(wxWindow* wnd)
1970{
1971 int i, part_count;
1972 for (i = 0, part_count = m_uiparts.GetCount(); i < part_count; ++i)
1973 {
1974 wxDockUIPart& part = m_uiparts.Item(i);
1975 if (part.type == wxDockUIPart::typePaneBorder &&
1976 part.pane && part.pane->window == wnd)
1977 return &part;
1978 }
1979 for (i = 0, part_count = m_uiparts.GetCount(); i < part_count; ++i)
1980 {
1981 wxDockUIPart& part = m_uiparts.Item(i);
1982 if (part.type == wxDockUIPart::typePane &&
1983 part.pane && part.pane->window == wnd)
1984 return &part;
1985 }
1986 return NULL;
1987}
1988
1989
1990
1991// GetDockPixelOffset() is an internal function which returns
1992// a dock's offset in pixels from the left side of the window
1993// (for horizontal docks) or from the top of the window (for
1994// vertical docks). This value is necessary for calculating
1995// fixel-pane/toolbar offsets when they are dragged.
1996
1997int wxFrameManager::GetDockPixelOffset(wxPaneInfo& test)
1998{
1999 // the only way to accurately calculate the dock's
2000 // offset is to actually run a theoretical layout
be66f18e 2001
50acee04
JS
2002 int i, part_count, dock_count;
2003 wxDockInfoArray docks;
2004 wxPaneInfoArray panes;
2005 wxDockUIPartArray uiparts;
2006 CopyDocksAndPanes(docks, panes, m_docks, m_panes);
2007 panes.Add(test);
2008
2009 wxSizer* sizer = LayoutAll(panes, docks, uiparts, true);
2010 wxSize client_size = m_frame->GetClientSize();
2011 sizer->SetDimension(0, 0, client_size.x, client_size.y);
2012 sizer->Layout();
2013
2014 for (i = 0, part_count = uiparts.GetCount(); i < part_count; ++i)
2015 {
2016 wxDockUIPart& part = uiparts.Item(i);
2017 part.rect = wxRect(part.sizer_item->GetPosition(),
2018 part.sizer_item->GetSize());
2019 if (part.type == wxDockUIPart::typeDock)
2020 part.dock->rect = part.rect;
2021 }
be66f18e 2022
50acee04 2023 delete sizer;
be66f18e 2024
50acee04
JS
2025 for (i = 0, dock_count = docks.GetCount(); i < dock_count; ++i)
2026 {
2027 wxDockInfo& dock = docks.Item(i);
2028 if (test.dock_direction == dock.dock_direction &&
2029 test.dock_layer==dock.dock_layer && test.dock_row==dock.dock_row)
2030 {
2031 if (dock.IsVertical())
2032 return dock.rect.y;
2033 else
2034 return dock.rect.x;
2035 }
2036 }
2037
2038 return 0;
2039}
2040
2041
2042
2043// ProcessDockResult() is a utility function used by DoDrop() - it checks
2044// if a dock operation is allowed, the new dock position is copied into
2045// the target info. If the operation was allowed, the function returns true.
2046
2047static bool ProcessDockResult(wxPaneInfo& target,
2048 const wxPaneInfo& new_pos)
2049{
2050 bool allowed = false;
2051 switch (new_pos.dock_direction)
2052 {
2053 case wxAUI_DOCK_TOP: allowed = target.IsTopDockable(); break;
2054 case wxAUI_DOCK_BOTTOM: allowed = target.IsBottomDockable(); break;
2055 case wxAUI_DOCK_LEFT: allowed = target.IsLeftDockable(); break;
2056 case wxAUI_DOCK_RIGHT: allowed = target.IsRightDockable(); break;
2057 }
2058
2059 if (allowed)
2060 target = new_pos;
2061
2062 return allowed;
2063}
2064
2065
2066// DoDrop() is an important function. It basically takes a mouse position,
2067// and determines where the pane's new position would be. If the pane is to be
2068// dropped, it performs the drop operation using the specified dock and pane
2069// arrays. By specifying copied dock and pane arrays when calling, a "what-if"
2070// scenario can be performed, giving precise coordinates for drop hints.
2071// If, however, wxFrameManager:m_docks and wxFrameManager::m_panes are specified
2072// as parameters, the changes will be made to the main state arrays
2073
2074const int auiInsertRowPixels = 10;
2075const int auiNewRowPixels = 40;
2076const int auiLayerInsertPixels = 40;
2077const int auiLayerInsertOffset = 5;
2078
2079bool wxFrameManager::DoDrop(wxDockInfoArray& docks,
2080 wxPaneInfoArray& panes,
2081 wxPaneInfo& target,
2082 const wxPoint& pt,
2083 const wxPoint& offset)
2084{
2085 wxSize cli_size = m_frame->GetClientSize();
2086
2087 wxPaneInfo drop = target;
2088
2089
2090 // The result should always be shown
2091 drop.Show();
be66f18e 2092
50acee04
JS
2093
2094 // Check to see if the pane has been dragged outside of the window
2095 // (or near to the outside of the window), if so, dock it along the edge
2096
2097
2098 int layer_insert_offset = auiLayerInsertOffset;
2099 if (target.IsToolbar())
2100 layer_insert_offset = 0;
be66f18e 2101
50acee04
JS
2102 if (pt.x < layer_insert_offset &&
2103 pt.x > layer_insert_offset-auiLayerInsertPixels)
2104 {
2105 int new_layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_LEFT),
2106 GetMaxLayer(docks, wxAUI_DOCK_BOTTOM)),
be66f18e 2107 GetMaxLayer(docks, wxAUI_DOCK_TOP)) + 1;
50acee04
JS
2108 drop.Dock().Left().
2109 Layer(new_layer).
2110 Row(0).
2111 Position(pt.y - GetDockPixelOffset(drop) - offset.y);
2112 return ProcessDockResult(target, drop);
2113 }
696978ee
WS
2114 else if (pt.y < layer_insert_offset &&
2115 pt.y > layer_insert_offset-auiLayerInsertPixels)
50acee04
JS
2116 {
2117 int new_layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_TOP),
2118 GetMaxLayer(docks, wxAUI_DOCK_LEFT)),
2119 GetMaxLayer(docks, wxAUI_DOCK_RIGHT)) + 1;
2120 drop.Dock().Top().
2121 Layer(new_layer).
2122 Row(0).
2123 Position(pt.x - GetDockPixelOffset(drop) - offset.x);
2124 return ProcessDockResult(target, drop);
2125 }
696978ee
WS
2126 else if (pt.x >= cli_size.x - layer_insert_offset &&
2127 pt.x < cli_size.x - layer_insert_offset + auiLayerInsertPixels)
50acee04
JS
2128 {
2129 int new_layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_RIGHT),
2130 GetMaxLayer(docks, wxAUI_DOCK_TOP)),
be66f18e 2131 GetMaxLayer(docks, wxAUI_DOCK_BOTTOM)) + 1;
50acee04
JS
2132 drop.Dock().Right().
2133 Layer(new_layer).
2134 Row(0).
2135 Position(pt.y - GetDockPixelOffset(drop) - offset.y);
2136 return ProcessDockResult(target, drop);
2137 }
696978ee
WS
2138 else if (pt.y >= cli_size.y - layer_insert_offset &&
2139 pt.y < cli_size.y - layer_insert_offset + auiLayerInsertPixels)
50acee04
JS
2140 {
2141 int new_layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_BOTTOM),
2142 GetMaxLayer(docks, wxAUI_DOCK_LEFT)),
2143 GetMaxLayer(docks, wxAUI_DOCK_RIGHT)) + 1;
2144 drop.Dock().Bottom().
2145 Layer(new_layer).
2146 Row(0).
2147 Position(pt.x - GetDockPixelOffset(drop) - offset.x);
2148 return ProcessDockResult(target, drop);
2149 }
2150
2151
2152 wxDockUIPart* part = HitTest(pt.x, pt.y);
2153
2154
2155 if (drop.IsToolbar())
2156 {
2157 if (!part || !part->dock)
2158 return false;
be66f18e 2159
50acee04
JS
2160
2161 // calculate the offset from where the dock begins
2162 // to the point where the user dropped the pane
2163 int dock_drop_offset = 0;
2164 if (part->dock->IsHorizontal())
2165 dock_drop_offset = pt.x - part->dock->rect.x - offset.x;
2166 else
2167 dock_drop_offset = pt.y - part->dock->rect.y - offset.y;
2168
2169
2170 // toolbars may only be moved in and to fixed-pane docks,
2171 // otherwise we will try to float the pane. Also, the pane
2172 // should float if being dragged over center pane windows
2173 if (!part->dock->fixed || part->dock->dock_direction == wxAUI_DOCK_CENTER)
2174 {
2175 if ((m_flags & wxAUI_MGR_ALLOW_FLOATING) &&
2176 (drop.IsFloatable() ||
2177 (part->dock->dock_direction != wxAUI_DOCK_CENTER &&
2178 part->dock->dock_direction != wxAUI_DOCK_NONE)))
2179 {
2180 drop.Float();
2181 }
be66f18e 2182
50acee04
JS
2183 return ProcessDockResult(target, drop);
2184 }
be66f18e 2185
50acee04
JS
2186 drop.Dock().
2187 Direction(part->dock->dock_direction).
2188 Layer(part->dock->dock_layer).
2189 Row(part->dock->dock_row).
2190 Position(dock_drop_offset);
2191
2192 if ((
2193 ((pt.y < part->dock->rect.y + 2) && part->dock->IsHorizontal()) ||
2194 ((pt.x < part->dock->rect.x + 2) && part->dock->IsVertical())
2195 ) && part->dock->panes.GetCount() > 1)
2196 {
2197 int row = drop.dock_row;
2198 DoInsertDockRow(panes, part->dock->dock_direction,
2199 part->dock->dock_layer,
2200 part->dock->dock_row);
2201 drop.dock_row = row;
2202 }
be66f18e 2203
50acee04
JS
2204 if ((
2205 ((pt.y > part->dock->rect.y + part->dock->rect.height - 2 ) && part->dock->IsHorizontal()) ||
2206 ((pt.x > part->dock->rect.x + part->dock->rect.width - 2 ) && part->dock->IsVertical())
2207 ) && part->dock->panes.GetCount() > 1)
2208 {
2209 DoInsertDockRow(panes, part->dock->dock_direction,
2210 part->dock->dock_layer,
2211 part->dock->dock_row+1);
2212 drop.dock_row = part->dock->dock_row+1;
2213 }
2214
2215 return ProcessDockResult(target, drop);
2216 }
2217
2218
2219
be66f18e 2220
50acee04
JS
2221 if (!part)
2222 return false;
2223
2224 if (part->type == wxDockUIPart::typePaneBorder ||
2225 part->type == wxDockUIPart::typeCaption ||
2226 part->type == wxDockUIPart::typeGripper ||
2227 part->type == wxDockUIPart::typePaneButton ||
2228 part->type == wxDockUIPart::typePane ||
2229 part->type == wxDockUIPart::typePaneSizer ||
2230 part->type == wxDockUIPart::typeDockSizer ||
2231 part->type == wxDockUIPart::typeBackground)
2232 {
2233 if (part->type == wxDockUIPart::typeDockSizer)
2234 {
2235 if (part->dock->panes.GetCount() != 1)
2236 return false;
2237 part = GetPanePart(part->dock->panes.Item(0)->window);
2238 if (!part)
2239 return false;
2240 }
2241
2242
2243
2244 // If a normal frame is being dragged over a toolbar, insert it
2245 // along the edge under the toolbar, but over all other panes.
2246 // (this could be done much better, but somehow factoring this
2247 // calculation with the one at the beginning of this function)
2248 if (part->dock && part->dock->toolbar)
2249 {
2250 int layer = 0;
2251
2252 switch (part->dock->dock_direction)
2253 {
2254 case wxAUI_DOCK_LEFT:
2255 layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_LEFT),
2256 GetMaxLayer(docks, wxAUI_DOCK_BOTTOM)),
2257 GetMaxLayer(docks, wxAUI_DOCK_TOP));
2258 break;
2259 case wxAUI_DOCK_TOP:
2260 layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_TOP),
2261 GetMaxLayer(docks, wxAUI_DOCK_LEFT)),
2262 GetMaxLayer(docks, wxAUI_DOCK_RIGHT));
2263 break;
2264 case wxAUI_DOCK_RIGHT:
2265 layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_RIGHT),
2266 GetMaxLayer(docks, wxAUI_DOCK_TOP)),
2267 GetMaxLayer(docks, wxAUI_DOCK_BOTTOM));
2268 break;
2269 case wxAUI_DOCK_BOTTOM:
2270 layer = wxMax(wxMax(GetMaxLayer(docks, wxAUI_DOCK_BOTTOM),
2271 GetMaxLayer(docks, wxAUI_DOCK_LEFT)),
2272 GetMaxLayer(docks, wxAUI_DOCK_RIGHT));
2273 break;
2274 }
2275
2276 DoInsertDockRow(panes, part->dock->dock_direction,
2277 layer, 0);
2278 drop.Dock().
2279 Direction(part->dock->dock_direction).
2280 Layer(layer).Row(0).Position(0);
2281 return ProcessDockResult(target, drop);
2282 }
2283
2284
2285 if (!part->pane)
2286 return false;
2287
2288 part = GetPanePart(part->pane->window);
2289 if (!part)
2290 return false;
be66f18e 2291
50acee04
JS
2292 bool insert_dock_row = false;
2293 int insert_row = part->pane->dock_row;
2294 int insert_dir = part->pane->dock_direction;
2295 int insert_layer = part->pane->dock_layer;
be66f18e 2296
50acee04
JS
2297 switch (part->pane->dock_direction)
2298 {
2299 case wxAUI_DOCK_TOP:
2300 if (pt.y >= part->rect.y &&
2301 pt.y < part->rect.y+auiInsertRowPixels)
2302 insert_dock_row = true;
2303 break;
2304 case wxAUI_DOCK_BOTTOM:
2305 if (pt.y > part->rect.y+part->rect.height-auiInsertRowPixels &&
2306 pt.y <= part->rect.y + part->rect.height)
2307 insert_dock_row = true;
2308 break;
2309 case wxAUI_DOCK_LEFT:
2310 if (pt.x >= part->rect.x &&
2311 pt.x < part->rect.x+auiInsertRowPixels)
2312 insert_dock_row = true;
2313 break;
2314 case wxAUI_DOCK_RIGHT:
2315 if (pt.x > part->rect.x+part->rect.width-auiInsertRowPixels &&
2316 pt.x <= part->rect.x+part->rect.width)
2317 insert_dock_row = true;
2318 break;
2319 case wxAUI_DOCK_CENTER:
2320 {
2321 // "new row pixels" will be set to the default, but
2322 // must never exceed 20% of the window size
2323 int new_row_pixels_x = auiNewRowPixels;
2324 int new_row_pixels_y = auiNewRowPixels;
2325
2326 if (new_row_pixels_x > (part->rect.width*20)/100)
2327 new_row_pixels_x = (part->rect.width*20)/100;
2328
2329 if (new_row_pixels_y > (part->rect.height*20)/100)
2330 new_row_pixels_y = (part->rect.height*20)/100;
2331
be66f18e 2332
50acee04
JS
2333 // determine if the mouse pointer is in a location that
2334 // will cause a new row to be inserted. The hot spot positions
2335 // are along the borders of the center pane
2336
2337 insert_layer = 0;
2338 insert_dock_row = true;
2339 if (pt.x >= part->rect.x &&
2340 pt.x < part->rect.x+new_row_pixels_x)
2341 insert_dir = wxAUI_DOCK_LEFT;
2342 else
2343 if (pt.y >= part->rect.y &&
2344 pt.y < part->rect.y+new_row_pixels_y)
2345 insert_dir = wxAUI_DOCK_TOP;
2346 else
2347 if (pt.x >= part->rect.x + part->rect.width-new_row_pixels_x &&
2348 pt.x < part->rect.x + part->rect.width)
2349 insert_dir = wxAUI_DOCK_RIGHT;
2350 else
2351 if (pt.y >= part->rect.y+ part->rect.height-new_row_pixels_y &&
2352 pt.y < part->rect.y + part->rect.height)
2353 insert_dir = wxAUI_DOCK_BOTTOM;
2354 else
2355 return false;
2356
2357 insert_row = GetMaxRow(panes, insert_dir, insert_layer) + 1;
2358 }
2359 }
2360
2361 if (insert_dock_row)
be66f18e 2362 {
50acee04
JS
2363 DoInsertDockRow(panes, insert_dir, insert_layer, insert_row);
2364 drop.Dock().Direction(insert_dir).
2365 Layer(insert_layer).
2366 Row(insert_row).
2367 Position(0);
2368 return ProcessDockResult(target, drop);
2369 }
be66f18e 2370
50acee04
JS
2371 // determine the mouse offset and the pane size, both in the
2372 // direction of the dock itself, and perpendicular to the dock
be66f18e 2373
50acee04 2374 int offset, size;
be66f18e 2375
50acee04
JS
2376 if (part->orientation == wxVERTICAL)
2377 {
2378 offset = pt.y - part->rect.y;
be66f18e 2379 size = part->rect.GetHeight();
50acee04
JS
2380 }
2381 else
2382 {
2383 offset = pt.x - part->rect.x;
2384 size = part->rect.GetWidth();
2385 }
be66f18e 2386
50acee04 2387 int drop_position = part->pane->dock_pos;
be66f18e 2388
50acee04
JS
2389 // if we are in the top/left part of the pane,
2390 // insert the pane before the pane being hovered over
2391 if (offset <= size/2)
2392 {
2393 drop_position = part->pane->dock_pos;
2394 DoInsertPane(panes,
2395 part->pane->dock_direction,
2396 part->pane->dock_layer,
2397 part->pane->dock_row,
2398 part->pane->dock_pos);
2399 }
2400
2401 // if we are in the bottom/right part of the pane,
2402 // insert the pane before the pane being hovered over
2403 if (offset > size/2)
2404 {
2405 drop_position = part->pane->dock_pos+1;
2406 DoInsertPane(panes,
2407 part->pane->dock_direction,
2408 part->pane->dock_layer,
2409 part->pane->dock_row,
2410 part->pane->dock_pos+1);
2411 }
2412
2413 drop.Dock().
2414 Direction(part->dock->dock_direction).
2415 Layer(part->dock->dock_layer).
2416 Row(part->dock->dock_row).
2417 Position(drop_position);
2418 return ProcessDockResult(target, drop);
2419 }
2420
2421 return false;
2422}
2423
2424
2425void wxFrameManager::OnHintFadeTimer(wxTimerEvent& WXUNUSED(event))
2426{
50acee04
JS
2427 if (!m_hint_wnd || m_hint_fadeamt >= 50)
2428 {
2429 m_hint_fadetimer.Stop();
2430 return;
2431 }
be66f18e 2432
50acee04
JS
2433 m_hint_fadeamt += 5;
2434 MakeWindowTransparent(m_hint_wnd, m_hint_fadeamt);
50acee04
JS
2435}
2436
2437void wxFrameManager::ShowHint(const wxRect& rect)
2438{
0b2bbd17 2439#if defined(__WXMSW__) || defined(__WXMAC__)
50acee04
JS
2440 // First, determine if the operating system can handle transparency.
2441 // Transparency is available on Win2000 and above
be66f18e 2442
50acee04
JS
2443 static int os_type = -1;
2444 static int ver_major = -1;
be66f18e 2445
50acee04
JS
2446 if (os_type == -1)
2447 os_type = ::wxGetOsVersion(&ver_major);
2448
2449 // If the transparent flag is set, and the OS supports it,
2450 // go ahead and use a transparent hint
be66f18e 2451
81cc4eb2
RD
2452 if ((m_flags & wxAUI_MGR_TRANSPARENT_HINT) != 0
2453#ifdef __WXMSW__
2454 && os_type == wxWINDOWS_NT && ver_major >= 5
2455#endif
2456 )
50acee04
JS
2457 {
2458 if (m_last_hint == rect)
2459 return;
2460 m_last_hint = rect;
be66f18e 2461
50acee04
JS
2462 int initial_fade = 50;
2463 if (m_flags & wxAUI_MGR_TRANSPARENT_HINT_FADE)
2464 initial_fade = 0;
be66f18e 2465
50acee04
JS
2466 if (m_hint_wnd == NULL)
2467 {
2468 wxPoint pt = rect.GetPosition();
2469 wxSize size = rect.GetSize();
81cc4eb2 2470#if defined(__WXMSW__)
50acee04
JS
2471 m_hint_wnd = new wxFrame(m_frame, -1, wxEmptyString, pt, size,
2472 wxFRAME_TOOL_WINDOW |
2473 wxFRAME_FLOAT_ON_PARENT |
2474 wxFRAME_NO_TASKBAR |
2475 wxNO_BORDER);
2476
2477 MakeWindowTransparent(m_hint_wnd, initial_fade);
2478 m_hint_wnd->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION));
81cc4eb2
RD
2479#elif defined(__WXMAC__)
2480 // Using a miniframe with float and tool styles keeps the parent
2481 // frame activated and highlighted as such...
2482 m_hint_wnd = new wxMiniFrame(m_frame, -1, wxEmptyString, pt, size,
2483 wxFRAME_FLOAT_ON_PARENT
2484 | wxFRAME_TOOL_WINDOW
2485 | wxCAPTION );
2486
2487 // Can't set the bg colour of a Frame in wxMac
2488 wxPanel* p = new wxPanel(m_hint_wnd);
2489
2490 // The default wxSYS_COLOUR_ACTIVECAPTION colour is a light silver
2491 // color that is really hard to see, especially transparent.
2492 // Until a better system color is decided upon we'll just use
2493 // blue.
2494 p->SetBackgroundColour(*wxBLUE);
2495#endif
50acee04
JS
2496 m_hint_wnd->Show();
2497
2498 // if we are dragging a floating pane, set the focus
2499 // back to that floating pane (otherwise it becomes unfocused)
2500 if (m_action == actionDragFloatingPane && m_action_window)
2501 m_action_window->SetFocus();
2502
2503 }
81cc4eb2 2504 else
50acee04 2505 {
50acee04 2506 MakeWindowTransparent(m_hint_wnd, initial_fade);
b836b6ea 2507 m_hint_wnd->SetSize(rect);
81cc4eb2 2508 m_hint_wnd->Raise();
50acee04 2509 }
be66f18e 2510
50acee04
JS
2511 if (m_flags & wxAUI_MGR_TRANSPARENT_HINT_FADE)
2512 {
2513 // start fade in timer
2514 m_hint_fadeamt = 0;
2515 m_hint_fadetimer.SetOwner(this, 101);
2516 m_hint_fadetimer.Start(5);
2517 }
be66f18e 2518
50acee04
JS
2519 return;
2520 }
0b2bbd17
RD
2521#endif
2522
50acee04
JS
2523 if (m_last_hint != rect)
2524 {
2525 // remove the last hint rectangle
2526 m_last_hint = rect;
2527 m_frame->Refresh();
2528 m_frame->Update();
2529 }
be66f18e 2530
50acee04
JS
2531 wxScreenDC screendc;
2532 wxRegion clip(1, 1, 10000, 10000);
2533
2534 // clip all floating windows, so we don't draw over them
2535 int i, pane_count;
2536 for (i = 0, pane_count = m_panes.GetCount(); i < pane_count; ++i)
2537 {
2538 wxPaneInfo& pane = m_panes.Item(i);
2539
2540 if (pane.IsFloating() &&
2541 pane.frame->IsShown())
2542 {
2543 wxRect rect = pane.frame->GetRect();
2544 #ifdef __WXGTK__
2545 // wxGTK returns the client size, not the whole frame size
2546 rect.width += 15;
2547 rect.height += 35;
2548 rect.Inflate(5);
2549 #endif
2550
2551 clip.Subtract(rect);
2552 }
2553 }
2554
2555 screendc.SetClippingRegion(clip);
2556
2557 wxBitmap stipple = wxPaneCreateStippleBitmap();
2558 wxBrush brush(stipple);
2559 screendc.SetBrush(brush);
2560 screendc.SetPen(*wxTRANSPARENT_PEN);
2561
2562 screendc.DrawRectangle(rect.x, rect.y, 5, rect.height);
2563 screendc.DrawRectangle(rect.x+5, rect.y, rect.width-10, 5);
2564 screendc.DrawRectangle(rect.x+rect.width-5, rect.y, 5, rect.height);
2565 screendc.DrawRectangle(rect.x+5, rect.y+rect.height-5, rect.width-10, 5);
2566}
2567
2568void wxFrameManager::HideHint()
be66f18e 2569{
81cc4eb2 2570 // hides a transparent window hint, if there is one
50acee04
JS
2571 if (m_hint_wnd)
2572 {
2573 MakeWindowTransparent(m_hint_wnd, 0);
2574 m_hint_fadetimer.Stop();
2575 m_last_hint = wxRect();
2576 return;
2577 }
be66f18e 2578
50acee04
JS
2579 // hides a painted hint by redrawing the frame window
2580 if (!m_last_hint.IsEmpty())
2581 {
2582 m_frame->Refresh();
2583 m_frame->Update();
2584 m_last_hint = wxRect();
2585 }
2586}
2587
2588
2589
2590// DrawHintRect() draws a drop hint rectangle. First calls DoDrop() to
2591// determine the exact position the pane would be at were if dropped. If
2592// the pame would indeed become docked at the specified drop point,
2593// DrawHintRect() then calls ShowHint() to indicate this drop rectangle.
2594// "pane_window" is the window pointer of the pane being dragged, pt is
2595// the mouse position, in client coordinates
2596void wxFrameManager::DrawHintRect(wxWindow* pane_window,
2597 const wxPoint& pt,
2598 const wxPoint& offset)
2599{
2600 wxRect rect;
2601
2602 // we need to paint a hint rectangle; to find out the exact hint rectangle,
2603 // we will create a new temporary layout and then measure the resulting
2604 // rectangle; we will create a copy of the docking structures (m_dock)
2605 // so that we don't modify the real thing on screen
2606
2607 int i, pane_count, part_count;
2608 wxDockInfoArray docks;
2609 wxPaneInfoArray panes;
2610 wxDockUIPartArray uiparts;
2611 wxPaneInfo hint = GetPane(pane_window);
2612 hint.name = wxT("__HINT__");
2613
2614 if (!hint.IsOk())
2615 return;
2616
2617 CopyDocksAndPanes(docks, panes, m_docks, m_panes);
2618
2619 // remove any pane already there which bears the same window;
2620 // this happens when you are moving a pane around in a dock
2621 for (i = 0, pane_count = panes.GetCount(); i < pane_count; ++i)
2622 {
2623 if (panes.Item(i).window == pane_window)
2624 {
2625 RemovePaneFromDocks(docks, panes.Item(i));
2626 panes.RemoveAt(i);
2627 break;
2628 }
2629 }
2630
2631 // find out where the new pane would be
2632 if (!DoDrop(docks, panes, hint, pt, offset))
2633 {
2634 HideHint();
2635 return;
2636 }
2637
2638 panes.Add(hint);
2639
2640 wxSizer* sizer = LayoutAll(panes, docks, uiparts, true);
2641 wxSize client_size = m_frame->GetClientSize();
2642 sizer->SetDimension(0, 0, client_size.x, client_size.y);
2643 sizer->Layout();
2644
2645 for (i = 0, part_count = uiparts.GetCount();
2646 i < part_count; ++i)
2647 {
2648 wxDockUIPart& part = uiparts.Item(i);
2649
2650 if (part.type == wxDockUIPart::typePaneBorder &&
2651 part.pane && part.pane->name == wxT("__HINT__"))
2652 {
2653 rect = wxRect(part.sizer_item->GetPosition(),
2654 part.sizer_item->GetSize());
2655 break;
2656 }
2657 }
be66f18e 2658
50acee04
JS
2659 delete sizer;
2660
2661 if (rect.IsEmpty())
2662 {
2663 HideHint();
2664 return;
2665 }
2666
2667 // actually show the hint rectangle on the screen
2668 m_frame->ClientToScreen(&rect.x, &rect.y);
2669 ShowHint(rect);
2670}
2671
2672void wxFrameManager::OnFloatingPaneMoveStart(wxWindow* wnd)
2673{
2674 // try to find the pane
2675 wxPaneInfo& pane = GetPane(wnd);
2676 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
be66f18e 2677
50acee04
JS
2678 if (m_flags & wxAUI_MGR_TRANSPARENT_DRAG)
2679 MakeWindowTransparent(pane.frame, 150);
50acee04
JS
2680}
2681
2682void wxFrameManager::OnFloatingPaneMoving(wxWindow* wnd)
2683{
2684 // try to find the pane
2685 wxPaneInfo& pane = GetPane(wnd);
2686 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
be66f18e 2687
50acee04
JS
2688 wxPoint pt = ::wxGetMousePosition();
2689 wxPoint client_pt = m_frame->ScreenToClient(pt);
be66f18e 2690
50acee04
JS
2691 // calculate the offset from the upper left-hand corner
2692 // of the frame to the mouse pointer
2693 wxPoint frame_pos = pane.frame->GetPosition();
2694 wxPoint action_offset(pt.x-frame_pos.x, pt.y-frame_pos.y);
2695
2696 // no hint for toolbar floating windows
2697 if (pane.IsToolbar() && m_action == actionDragFloatingPane)
2698 {
2699 if (m_action == actionDragFloatingPane)
2700 {
2701 wxDockInfoArray docks;
2702 wxPaneInfoArray panes;
2703 wxDockUIPartArray uiparts;
2704 wxPaneInfo hint = pane;
be66f18e 2705
50acee04
JS
2706 CopyDocksAndPanes(docks, panes, m_docks, m_panes);
2707
2708 // find out where the new pane would be
2709 if (!DoDrop(docks, panes, hint, client_pt))
2710 return;
2711 if (hint.IsFloating())
2712 return;
be66f18e 2713
50acee04
JS
2714 pane = hint;
2715 m_action = actionDragToolbarPane;
2716 m_action_window = pane.window;
be66f18e 2717
50acee04
JS
2718 Update();
2719 }
be66f18e 2720
50acee04
JS
2721 return;
2722 }
2723
2724
2725 // if a key modifier is pressed while dragging the frame,
2726 // don't dock the window
2727 if (wxGetKeyState(WXK_CONTROL) || wxGetKeyState(WXK_ALT))
2728 {
2729 HideHint();
2730 return;
2731 }
2732
2733
2734 DrawHintRect(wnd, client_pt, action_offset);
2735
be66f18e 2736#ifdef __WXGTK__
50acee04
JS
2737 // this cleans up some screen artifacts that are caused on GTK because
2738 // we aren't getting the exact size of the window (see comment
2739 // in DrawHintRect)
2740 //Refresh();
be66f18e
WS
2741#endif
2742
2743
50acee04
JS
2744 // reduces flicker
2745 m_frame->Update();
2746}
2747
2748void wxFrameManager::OnFloatingPaneMoved(wxWindow* wnd)
2749{
2750 // try to find the pane
2751 wxPaneInfo& pane = GetPane(wnd);
2752 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
be66f18e 2753
50acee04
JS
2754 wxPoint pt = ::wxGetMousePosition();
2755 wxPoint client_pt = m_frame->ScreenToClient(pt);
be66f18e 2756
50acee04
JS
2757 // calculate the offset from the upper left-hand corner
2758 // of the frame to the mouse pointer
2759 wxPoint frame_pos = pane.frame->GetPosition();
2760 wxPoint action_offset(pt.x-frame_pos.x, pt.y-frame_pos.y);
be66f18e 2761
50acee04
JS
2762
2763 // if a key modifier is pressed while dragging the frame,
2764 // don't dock the window
2765 if (wxGetKeyState(WXK_CONTROL) || wxGetKeyState(WXK_ALT))
2766 {
2767 HideHint();
2768 return;
2769 }
2770
2771
2772 // do the drop calculation
2773 DoDrop(m_docks, m_panes, pane, client_pt, action_offset);
2774
2775 // if the pane is still floating, update it's floating
2776 // position (that we store)
2777 if (pane.IsFloating())
2778 {
2779 pane.floating_pos = pane.frame->GetPosition();
be66f18e 2780
50acee04
JS
2781 if (m_flags & wxAUI_MGR_TRANSPARENT_DRAG)
2782 MakeWindowTransparent(pane.frame, 255);
50acee04 2783 }
be66f18e 2784
50acee04 2785 Update();
be66f18e 2786
50acee04
JS
2787 HideHint();
2788}
2789
2790void wxFrameManager::OnFloatingPaneResized(wxWindow* wnd, const wxSize& size)
2791{
2792 // try to find the pane
2793 wxPaneInfo& pane = GetPane(wnd);
2794 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
be66f18e 2795
50acee04
JS
2796 pane.floating_size = size;
2797}
2798
58754643
BW
2799
2800void wxFrameManager::OnFloatingPaneClosed(wxWindow* wnd, wxCloseEvent& evt)
50acee04
JS
2801{
2802 // try to find the pane
2803 wxPaneInfo& pane = GetPane(wnd);
2804 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
2805
58754643
BW
2806
2807 // fire pane close event
2808 wxFrameManagerEvent e(wxEVT_AUI_PANECLOSE);
2809 e.SetPane(&pane);
2810 e.SetCanVeto(evt.CanVeto());
2811 ProcessMgrEvent(e);
a1b3b608 2812
58754643
BW
2813 if (e.GetVeto())
2814 {
2815 evt.Veto();
2816 return;
2817 }
2818 else
2819 {
2820 // reparent the pane window back to us and
2821 // prepare the frame window for destruction
2822 pane.window->Show(false);
2823 pane.window->Reparent(m_frame);
2824 pane.frame = NULL;
2825 pane.Hide();
2826 }
50acee04
JS
2827}
2828
58754643
BW
2829
2830
50acee04
JS
2831void wxFrameManager::OnFloatingPaneActivated(wxWindow* wnd)
2832{
2833 if (GetFlags() & wxAUI_MGR_ALLOW_ACTIVE_PANE)
2834 {
2835 // try to find the pane
8f964392 2836 wxASSERT_MSG(GetPane(wnd).IsOk(), wxT("Pane window not found"));
50acee04
JS
2837
2838 SetActivePane(m_panes, wnd);
2839 Repaint();
2840 }
2841}
2842
2843// Render() draws all of the pane captions, sashes,
2844// backgrounds, captions, grippers, pane borders and buttons.
2845// It renders the entire user interface.
2846
2847void wxFrameManager::Render(wxDC* dc)
2848{
2849#ifdef __WXMAC__
2850 dc->Clear() ;
2851#endif
2852 int i, part_count;
2853 for (i = 0, part_count = m_uiparts.GetCount();
2854 i < part_count; ++i)
2855 {
2856 wxDockUIPart& part = m_uiparts.Item(i);
2857
2858 // don't draw hidden pane items
2859 if (part.sizer_item && !part.sizer_item->IsShown())
2860 continue;
be66f18e 2861
50acee04
JS
2862 switch (part.type)
2863 {
2864 case wxDockUIPart::typeDockSizer:
2865 case wxDockUIPart::typePaneSizer:
2866 m_art->DrawSash(*dc, part.orientation, part.rect);
2867 break;
2868 case wxDockUIPart::typeBackground:
2869 m_art->DrawBackground(*dc, part.orientation, part.rect);
2870 break;
2871 case wxDockUIPart::typeCaption:
2872 m_art->DrawCaption(*dc, part.pane->caption, part.rect, *part.pane);
2873 break;
2874 case wxDockUIPart::typeGripper:
2875 m_art->DrawGripper(*dc, part.rect, *part.pane);
be66f18e 2876 break;
50acee04
JS
2877 case wxDockUIPart::typePaneBorder:
2878 m_art->DrawBorder(*dc, part.rect, *part.pane);
2879 break;
2880 case wxDockUIPart::typePaneButton:
2881 m_art->DrawPaneButton(*dc, part.button->button_id,
2882 wxAUI_BUTTON_STATE_NORMAL, part.rect, *part.pane);
2883 break;
2884 }
2885 }
2886}
2887
2888void wxFrameManager::Repaint(wxDC* dc)
2889{
2890#ifdef __WXMAC__
2891 if ( dc == NULL )
2892 {
2893 m_frame->Refresh() ;
2894 m_frame->Update() ;
2895 return ;
2896 }
2897#endif
2898 int w, h;
2899 m_frame->GetClientSize(&w, &h);
2900
2901 // figure out which dc to use; if one
2902 // has been specified, use it, otherwise
2903 // make a client dc
2904 wxClientDC* client_dc = NULL;
2905 if (!dc)
2906 {
2907 client_dc = new wxClientDC(m_frame);
2908 dc = client_dc;
2909 }
2910
2911 // if the frame has a toolbar, the client area
2912 // origin will not be (0,0).
2913 wxPoint pt = m_frame->GetClientAreaOrigin();
2914 if (pt.x != 0 || pt.y != 0)
2915 dc->SetDeviceOrigin(pt.x, pt.y);
2916
2917 // render all the items
2918 Render(dc);
2919
2920 // if we created a client_dc, delete it
2921 if (client_dc)
2922 delete client_dc;
2923}
2924
2925void wxFrameManager::OnPaint(wxPaintEvent& WXUNUSED(event))
2926{
2927 wxPaintDC dc(m_frame);
2928 Repaint(&dc);
2929}
2930
2931void wxFrameManager::OnEraseBackground(wxEraseEvent& event)
2932{
2933#ifdef __WXMAC__
2934 event.Skip() ;
2935#else
2936 wxUnusedVar(event);
2937#endif
2938}
2939
2940void wxFrameManager::OnSize(wxSizeEvent& WXUNUSED(event))
2941{
2942 if (m_frame)
2943 {
2944 DoFrameLayout();
2945 Repaint();
2946 }
2947}
2948
2949
2950void wxFrameManager::OnSetCursor(wxSetCursorEvent& event)
2951{
2952 // determine cursor
2953 wxDockUIPart* part = HitTest(event.GetX(), event.GetY());
2954 wxCursor cursor = wxNullCursor;
2955
2956 if (part)
2957 {
2958 if (part->type == wxDockUIPart::typeDockSizer ||
2959 part->type == wxDockUIPart::typePaneSizer)
2960 {
2961 // a dock may not be resized if it has a single
2962 // pane which is not resizable
2963 if (part->type == wxDockUIPart::typeDockSizer && part->dock &&
2964 part->dock->panes.GetCount() == 1 &&
2965 part->dock->panes.Item(0)->IsFixed())
2966 return;
2967
2968 // panes that may not be resized do not get a sizing cursor
2969 if (part->pane && part->pane->IsFixed())
2970 return;
2971
2972 if (part->orientation == wxVERTICAL)
2973 cursor = wxCursor(wxCURSOR_SIZEWE);
2974 else
2975 cursor = wxCursor(wxCURSOR_SIZENS);
2976 }
2977 else if (part->type == wxDockUIPart::typeGripper)
2978 {
2979 cursor = wxCursor(wxCURSOR_SIZING);
2980 }
2981 }
be66f18e 2982
50acee04
JS
2983 event.SetCursor(cursor);
2984}
2985
2986
2987
2988void wxFrameManager::UpdateButtonOnScreen(wxDockUIPart* button_ui_part,
2989 const wxMouseEvent& event)
2990{
2991 wxDockUIPart* hit_test = HitTest(event.GetX(), event.GetY());
2992
2993 int state = wxAUI_BUTTON_STATE_NORMAL;
be66f18e 2994
50acee04
JS
2995 if (hit_test == button_ui_part)
2996 {
2997 if (event.LeftDown())
2998 state = wxAUI_BUTTON_STATE_PRESSED;
2999 else
3000 state = wxAUI_BUTTON_STATE_HOVER;
3001 }
696978ee 3002 else
50acee04
JS
3003 {
3004 if (event.LeftDown())
3005 state = wxAUI_BUTTON_STATE_HOVER;
3006 }
3007
3008 // now repaint the button with hover state
3009 wxClientDC cdc(m_frame);
3010
3011 // if the frame has a toolbar, the client area
3012 // origin will not be (0,0).
3013 wxPoint pt = m_frame->GetClientAreaOrigin();
3014 if (pt.x != 0 || pt.y != 0)
3015 cdc.SetDeviceOrigin(pt.x, pt.y);
3016
3017 m_art->DrawPaneButton(cdc,
3018 button_ui_part->button->button_id,
3019 state,
3020 button_ui_part->rect,
3021 *hit_test->pane);
3022}
3023
3024void wxFrameManager::OnLeftDown(wxMouseEvent& event)
3025{
3026 wxDockUIPart* part = HitTest(event.GetX(), event.GetY());
3027 if (part)
3028 {
3029 if (part->dock && part->dock->dock_direction == wxAUI_DOCK_CENTER)
3030 return;
3031
3032 if (part->type == wxDockUIPart::typeDockSizer ||
3033 part->type == wxDockUIPart::typePaneSizer)
3034 {
3035 // a dock may not be resized if it has a single
3036 // pane which is not resizable
3037 if (part->type == wxDockUIPart::typeDockSizer && part->dock &&
3038 part->dock->panes.GetCount() == 1 &&
3039 part->dock->panes.Item(0)->IsFixed())
3040 return;
3041
3042 // panes that may not be resized should be ignored here
3043 if (part->pane && part->pane->IsFixed())
3044 return;
3045
3046 m_action = actionResize;
3047 m_action_part = part;
3048 m_action_hintrect = wxRect();
3049 m_action_start = wxPoint(event.m_x, event.m_y);
3050 m_action_offset = wxPoint(event.m_x - part->rect.x,
3051 event.m_y - part->rect.y);
3052 m_frame->CaptureMouse();
3053 }
3054 else if (part->type == wxDockUIPart::typePaneButton)
3055 {
3056 m_action = actionClickButton;
3057 m_action_part = part;
3058 m_action_start = wxPoint(event.m_x, event.m_y);
3059 m_frame->CaptureMouse();
3060
3061 UpdateButtonOnScreen(part, event);
3062 }
3063 else if (part->type == wxDockUIPart::typeCaption ||
3064 part->type == wxDockUIPart::typeGripper)
3065 {
3066 if (GetFlags() & wxAUI_MGR_ALLOW_ACTIVE_PANE)
3067 {
3068 // set the caption as active
3069 SetActivePane(m_panes, part->pane->window);
3070 Repaint();
3071 }
3072
3073 m_action = actionClickCaption;
3074 m_action_part = part;
3075 m_action_start = wxPoint(event.m_x, event.m_y);
3076 m_action_offset = wxPoint(event.m_x - part->rect.x,
3077 event.m_y - part->rect.y);
3078 m_frame->CaptureMouse();
3079 }
3080#ifdef __WXMAC__
3081 else
3082 {
3083 event.Skip();
3084 }
3085#endif
3086 }
3087#ifdef __WXMAC__
3088 else
3089 {
3090 event.Skip();
3091 }
3092#else
3093 event.Skip();
3094#endif
3095}
3096
3097
3098void wxFrameManager::OnLeftUp(wxMouseEvent& event)
3099{
3100 if (m_action == actionResize)
3101 {
3102 m_frame->ReleaseMouse();
3103
3104 // get rid of the hint rectangle
3105 wxScreenDC dc;
3106 DrawResizeHint(dc, m_action_hintrect);
3107
3108 // resize the dock or the pane
3109 if (m_action_part && m_action_part->type==wxDockUIPart::typeDockSizer)
3110 {
3111 wxRect& rect = m_action_part->dock->rect;
3112
3113 wxPoint new_pos(event.m_x - m_action_offset.x,
3114 event.m_y - m_action_offset.y);
3115
3116 switch (m_action_part->dock->dock_direction)
3117 {
3118 case wxAUI_DOCK_LEFT:
3119 m_action_part->dock->size = new_pos.x - rect.x;
3120 break;
3121 case wxAUI_DOCK_TOP:
3122 m_action_part->dock->size = new_pos.y - rect.y;
3123 break;
3124 case wxAUI_DOCK_RIGHT:
3125 m_action_part->dock->size = rect.x + rect.width -
3126 new_pos.x - m_action_part->rect.GetWidth();
3127 break;
3128 case wxAUI_DOCK_BOTTOM:
3129 m_action_part->dock->size = rect.y + rect.height -
3130 new_pos.y - m_action_part->rect.GetHeight();
3131 break;
3132 }
3133
3134 Update();
3135 Repaint(NULL);
3136 }
3137 else if (m_action_part &&
3138 m_action_part->type == wxDockUIPart::typePaneSizer)
3139 {
3140 wxDockInfo& dock = *m_action_part->dock;
3141 wxPaneInfo& pane = *m_action_part->pane;
3142
3143 int total_proportion = 0;
3144 int dock_pixels = 0;
3145 int new_pixsize = 0;
3146
3147 int caption_size = m_art->GetMetric(wxAUI_ART_CAPTION_SIZE);
3148 int pane_border_size = m_art->GetMetric(wxAUI_ART_PANE_BORDER_SIZE);
3149 int sash_size = m_art->GetMetric(wxAUI_ART_SASH_SIZE);
3150
3151 wxPoint new_pos(event.m_x - m_action_offset.x,
3152 event.m_y - m_action_offset.y);
3153
3154 // determine the pane rectangle by getting the pane part
3155 wxDockUIPart* pane_part = GetPanePart(pane.window);
3156 wxASSERT_MSG(pane_part,
3157 wxT("Pane border part not found -- shouldn't happen"));
3158
3159 // determine the new pixel size that the user wants;
3160 // this will help us recalculate the pane's proportion
3161 if (dock.IsHorizontal())
3162 new_pixsize = new_pos.x - pane_part->rect.x;
3163 else
3164 new_pixsize = new_pos.y - pane_part->rect.y;
3165
3166 // determine the size of the dock, based on orientation
3167 if (dock.IsHorizontal())
3168 dock_pixels = dock.rect.GetWidth();
3169 else
3170 dock_pixels = dock.rect.GetHeight();
3171
3172 // determine the total proportion of all resizable panes,
3173 // and the total size of the dock minus the size of all
3174 // the fixed panes
3175 int i, dock_pane_count = dock.panes.GetCount();
3176 int pane_position = -1;
3177 for (i = 0; i < dock_pane_count; ++i)
3178 {
3179 wxPaneInfo& p = *dock.panes.Item(i);
3180 if (p.window == pane.window)
3181 pane_position = i;
be66f18e 3182
50acee04
JS
3183 // while we're at it, subtract the pane sash
3184 // width from the dock width, because this would
3185 // skew our proportion calculations
3186 if (i > 0)
3187 dock_pixels -= sash_size;
be66f18e 3188
50acee04
JS
3189 // also, the whole size (including decorations) of
3190 // all fixed panes must also be subtracted, because they
3191 // are not part of the proportion calculation
3192 if (p.IsFixed())
3193 {
3194 if (dock.IsHorizontal())
3195 dock_pixels -= p.best_size.x;
3196 else
3197 dock_pixels -= p.best_size.y;
3198 }
3199 else
3200 {
3201 total_proportion += p.dock_proportion;
3202 }
3203 }
be66f18e 3204
50acee04
JS
3205 // find a pane in our dock to 'steal' space from or to 'give'
3206 // space to -- this is essentially what is done when a pane is
3207 // resized; the pane should usually be the first non-fixed pane
3208 // to the right of the action pane
3209 int borrow_pane = -1;
3210 for (i = pane_position+1; i < dock_pane_count; ++i)
3211 {
3212 wxPaneInfo& p = *dock.panes.Item(i);
3213 if (!p.IsFixed())
3214 {
3215 borrow_pane = i;
3216 break;
3217 }
3218 }
be66f18e
WS
3219
3220
50acee04
JS
3221 // demand that the pane being resized is found in this dock
3222 // (this assert really never should be raised)
3223 wxASSERT_MSG(pane_position != -1, wxT("Pane not found in dock"));
be66f18e 3224
50acee04
JS
3225 // prevent division by zero
3226 if (dock_pixels == 0 || total_proportion == 0 || borrow_pane == -1)
3227 {
3228 m_action = actionNone;
3229 return;
3230 }
3231
3232 // calculate the new proportion of the pane
3233 int new_proportion = (new_pixsize*total_proportion)/dock_pixels;
be66f18e 3234
50acee04
JS
3235 // default minimum size
3236 int min_size = 0;
be66f18e 3237
50acee04
JS
3238 // check against the pane's minimum size, if specified. please note
3239 // that this is not enough to ensure that the minimum size will
3240 // not be violated, because the whole frame might later be shrunk,
3241 // causing the size of the pane to violate it's minimum size
3242 if (pane.min_size.IsFullySpecified())
3243 {
3244 min_size = 0;
be66f18e 3245
50acee04
JS
3246 if (pane.HasBorder())
3247 min_size += (pane_border_size*2);
3248
3249 // calculate minimum size with decorations (border,caption)
3250 if (pane_part->orientation == wxVERTICAL)
3251 {
3252 min_size += pane.min_size.y;
3253 if (pane.HasCaption())
3254 min_size += caption_size;
3255 }
3256 else
3257 {
3258 min_size += pane.min_size.x;
3259 }
3260 }
be66f18e
WS
3261
3262
50acee04
JS
3263 // for some reason, an arithmatic error somewhere is causing
3264 // the proportion calculations to always be off by 1 pixel;
3265 // for now we will add the 1 pixel on, but we really should
3266 // determine what's causing this.
3267 min_size++;
3268
3269 int min_proportion = (min_size*total_proportion)/dock_pixels;
be66f18e 3270
50acee04
JS
3271 if (new_proportion < min_proportion)
3272 new_proportion = min_proportion;
be66f18e
WS
3273
3274
3275
50acee04
JS
3276 int prop_diff = new_proportion - pane.dock_proportion;
3277
3278 // borrow the space from our neighbor pane to the
3279 // right or bottom (depending on orientation)
3280 dock.panes.Item(borrow_pane)->dock_proportion -= prop_diff;
3281 pane.dock_proportion = new_proportion;
be66f18e 3282
50acee04
JS
3283 // repaint
3284 Update();
3285 Repaint(NULL);
3286 }
3287 }
696978ee 3288 else if (m_action == actionClickButton)
50acee04
JS
3289 {
3290 m_hover_button = NULL;
be66f18e 3291 m_frame->ReleaseMouse();
50acee04
JS
3292 UpdateButtonOnScreen(m_action_part, event);
3293
3294 // make sure we're still over the item that was originally clicked
3295 if (m_action_part == HitTest(event.GetX(), event.GetY()))
be66f18e 3296 {
50acee04
JS
3297 // fire button-click event
3298 wxFrameManagerEvent e(wxEVT_AUI_PANEBUTTON);
3299 e.SetPane(m_action_part->pane);
3300 e.SetButton(m_action_part->button->button_id);
3301 ProcessMgrEvent(e);
3302 }
3303 }
696978ee 3304 else if (m_action == actionClickCaption)
50acee04
JS
3305 {
3306 m_frame->ReleaseMouse();
3307 }
696978ee 3308 else if (m_action == actionDragFloatingPane)
50acee04
JS
3309 {
3310 m_frame->ReleaseMouse();
3311 }
696978ee 3312 else if (m_action == actionDragToolbarPane)
50acee04
JS
3313 {
3314 m_frame->ReleaseMouse();
3315
3316 wxPaneInfo& pane = GetPane(m_action_window);
3317 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
be66f18e 3318
50acee04
JS
3319 // save the new positions
3320 wxDockInfoPtrArray docks;
3321 FindDocks(m_docks, pane.dock_direction,
3322 pane.dock_layer, pane.dock_row, docks);
3323 if (docks.GetCount() == 1)
3324 {
3325 wxDockInfo& dock = *docks.Item(0);
be66f18e 3326
50acee04
JS
3327 wxArrayInt pane_positions, pane_sizes;
3328 GetPanePositionsAndSizes(dock, pane_positions, pane_sizes);
be66f18e 3329
50acee04
JS
3330 int i, dock_pane_count = dock.panes.GetCount();
3331 for (i = 0; i < dock_pane_count; ++i)
3332 dock.panes.Item(i)->dock_pos = pane_positions[i];
3333 }
be66f18e 3334
50acee04
JS
3335 pane.state &= ~wxPaneInfo::actionPane;
3336 Update();
3337 }
696978ee 3338 else
50acee04
JS
3339 {
3340 event.Skip();
3341 }
3342
3343 m_action = actionNone;
3344 m_last_mouse_move = wxPoint(); // see comment in OnMotion()
3345}
3346
3347
3348void wxFrameManager::OnMotion(wxMouseEvent& event)
3349{
3350 // sometimes when Update() is called from inside this method,
3351 // a spurious mouse move event is generated; this check will make
3352 // sure that only real mouse moves will get anywhere in this method;
3353 // this appears to be a bug somewhere, and I don't know where the
3354 // mouse move event is being generated. only verified on MSW
be66f18e 3355
50acee04
JS
3356 wxPoint mouse_pos = event.GetPosition();
3357 if (m_last_mouse_move == mouse_pos)
3358 return;
3359 m_last_mouse_move = mouse_pos;
3360
be66f18e 3361
50acee04
JS
3362 if (m_action == actionResize)
3363 {
3364 wxPoint pos = m_action_part->rect.GetPosition();
3365 if (m_action_part->orientation == wxHORIZONTAL)
3366 pos.y = wxMax(0, event.m_y - m_action_offset.y);
3367 else
3368 pos.x = wxMax(0, event.m_x - m_action_offset.x);
3369
3370 wxRect rect(m_frame->ClientToScreen(pos),
3371 m_action_part->rect.GetSize());
3372
3373 wxScreenDC dc;
3374 if (!m_action_hintrect.IsEmpty())
3375 DrawResizeHint(dc, m_action_hintrect);
3376 DrawResizeHint(dc, rect);
3377 m_action_hintrect = rect;
3378 }
696978ee 3379 else if (m_action == actionClickCaption)
50acee04
JS
3380 {
3381 int drag_x_threshold = wxSystemSettings::GetMetric(wxSYS_DRAG_X);
3382 int drag_y_threshold = wxSystemSettings::GetMetric(wxSYS_DRAG_Y);
be66f18e 3383
50acee04
JS
3384 // caption has been clicked. we need to check if the mouse
3385 // is now being dragged. if it is, we need to change the
3386 // mouse action to 'drag'
3387 if (abs(event.m_x - m_action_start.x) > drag_x_threshold ||
be66f18e 3388 abs(event.m_y - m_action_start.y) > drag_y_threshold)
50acee04
JS
3389 {
3390 wxPaneInfo* pane_info = m_action_part->pane;
3391
3392 if (!pane_info->IsToolbar())
3393 {
3394 if ((m_flags & wxAUI_MGR_ALLOW_FLOATING) &&
be66f18e 3395 pane_info->IsFloatable())
50acee04
JS
3396 {
3397 m_action = actionDragFloatingPane;
3398
3399 // set initial float position
3400 wxPoint pt = m_frame->ClientToScreen(event.GetPosition());
3401 pane_info->floating_pos = wxPoint(pt.x - m_action_offset.x,
3402 pt.y - m_action_offset.y);
3403 // float the window
3404 pane_info->Float();
3405 Update();
3406
3407 m_action_window = pane_info->frame;
be66f18e 3408
50acee04
JS
3409 // action offset is used here to make it feel "natural" to the user
3410 // to drag a docked pane and suddenly have it become a floating frame.
3411 // Sometimes, however, the offset where the user clicked on the docked
3412 // caption is bigger than the width of the floating frame itself, so
3413 // in that case we need to set the action offset to a sensible value
3414 wxSize frame_size = m_action_window->GetSize();
3415 if (frame_size.x <= m_action_offset.x)
3416 m_action_offset.x = 30;
3417 }
3418 }
3419 else
3420 {
3421 m_action = actionDragToolbarPane;
3422 m_action_window = pane_info->window;
3423 }
3424 }
3425 }
696978ee 3426 else if (m_action == actionDragFloatingPane)
50acee04
JS
3427 {
3428 wxPoint pt = m_frame->ClientToScreen(event.GetPosition());
3429 m_action_window->Move(pt.x - m_action_offset.x,
3430 pt.y - m_action_offset.y);
3431 }
696978ee 3432 else if (m_action == actionDragToolbarPane)
50acee04
JS
3433 {
3434 wxPaneInfo& pane = GetPane(m_action_window);
3435 wxASSERT_MSG(pane.IsOk(), wxT("Pane window not found"));
3436
3437 pane.state |= wxPaneInfo::actionPane;
3438
3439 wxPoint pt = event.GetPosition();
3440 DoDrop(m_docks, m_panes, pane, pt, m_action_offset);
be66f18e 3441
50acee04
JS
3442 // if DoDrop() decided to float the pane, set up
3443 // the floating pane's initial position
3444 if (pane.IsFloating())
3445 {
3446 wxPoint pt = m_frame->ClientToScreen(event.GetPosition());
3447 pane.floating_pos = wxPoint(pt.x - m_action_offset.x,
3448 pt.y - m_action_offset.y);
3449 }
be66f18e 3450
50acee04
JS
3451 // this will do the actiual move operation;
3452 // in the case that the pane has been floated,
3453 // this call will create the floating pane
3454 // and do the reparenting
3455 Update();
be66f18e 3456
50acee04
JS
3457 // if the pane has been floated, change the mouse
3458 // action actionDragFloatingPane so that subsequent
3459 // EVT_MOTION() events will move the floating pane
3460 if (pane.IsFloating())
3461 {
3462 pane.state &= ~wxPaneInfo::actionPane;
3463 m_action = actionDragFloatingPane;
3464 m_action_window = pane.frame;
3465 }
be66f18e
WS
3466 }
3467 else
50acee04
JS
3468 {
3469 wxDockUIPart* part = HitTest(event.GetX(), event.GetY());
3470 if (part && part->type == wxDockUIPart::typePaneButton)
3471 {
3472 if (part != m_hover_button)
3473 {
3474 // make the old button normal
3475 if (m_hover_button)
3476 UpdateButtonOnScreen(m_hover_button, event);
3477
3478 // mouse is over a button, so repaint the
3479 // button in hover mode
3480 UpdateButtonOnScreen(part, event);
3481 m_hover_button = part;
3482 }
3483 }
3484 else
3485 {
3486 if (m_hover_button)
3487 {
3488 m_hover_button = NULL;
3489 Repaint();
3490 }
3491 else
3492 {
3493 event.Skip();
3494 }
3495 }
3496 }
3497}
3498
3499void wxFrameManager::OnLeaveWindow(wxMouseEvent& WXUNUSED(event))
3500{
3501 if (m_hover_button)
3502 {
3503 m_hover_button = NULL;
3504 Repaint();
3505 }
3506}
3507
3508void wxFrameManager::OnChildFocus(wxChildFocusEvent& event)
3509{
be66f18e
WS
3510 // when a child pane has it's focus set, we should change the
3511 // pane's active state to reflect this. (this is only true if
50acee04
JS
3512 // active panes are allowed by the owner)
3513 if (GetFlags() & wxAUI_MGR_ALLOW_ACTIVE_PANE)
3514 {
3515 if (GetPane(event.GetWindow()).IsOk())
3516 {
3517 SetActivePane(m_panes, event.GetWindow());
3518 m_frame->Refresh();
3519 }
3520 }
3521}
3522
3523
3524// OnPaneButton() is an event handler that is called
3525// when a pane button has been pressed.
58754643 3526void wxFrameManager::OnPaneButton(wxFrameManagerEvent& evt)
50acee04 3527{
58754643 3528 wxASSERT_MSG(evt.pane, wxT("Pane Info passed to wxFrameManager::OnPaneButton must be non-null"));
a1b3b608 3529
58754643 3530 wxPaneInfo& pane = *(evt.pane);
be66f18e 3531
58754643 3532 if (evt.button == wxPaneInfo::buttonClose)
50acee04 3533 {
e092fb2e 3534 // fire pane close event
58754643
BW
3535 wxFrameManagerEvent e(wxEVT_AUI_PANECLOSE);
3536 e.SetPane(evt.pane);
3537 ProcessMgrEvent(e);
a1b3b608 3538
58754643 3539 if (!e.GetVeto())
cf08f19a 3540 {
58754643
BW
3541 pane.Hide();
3542 Update();
cf08f19a 3543 }
50acee04 3544 }
58754643 3545 else if (evt.button == wxPaneInfo::buttonPin)
50acee04
JS
3546 {
3547 if ((m_flags & wxAUI_MGR_ALLOW_FLOATING) &&
3548 pane.IsFloatable())
3549 pane.Float();
3550 Update();
3551 }
3552}
3553
3554#endif // wxUSE_AUI