1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/mac/carbon/mdi.cpp
3// Purpose:     MDI classes
4// Author:      Stefan Csomor
5// Modified by:
6// Created:     1998-01-01
7// RCS-ID:      $Id: mdi.cpp 53039 2008-04-06 09:31:01Z CE $
8// Copyright:   (c) Stefan Csomor
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
14#if wxUSE_MDI
15
16#include "wx/mdi.h"
17
18#ifndef WX_PRECOMP
19    #include "wx/log.h"
20    #include "wx/menu.h"
21    #include "wx/settings.h"
22    #include "wx/statusbr.h"
23#endif
24
25#include "wx/mac/private.h"
26#include "wx/mac/uma.h"
27
28extern wxWindowList wxModelessWindows;
29
30IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame, wxFrame)
31IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame, wxFrame)
32IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
33
34BEGIN_EVENT_TABLE(wxMDIParentFrame, wxFrame)
35    EVT_ACTIVATE(wxMDIParentFrame::OnActivate)
36    EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged)
37END_EVENT_TABLE()
38
39BEGIN_EVENT_TABLE(wxMDIClientWindow, wxWindow)
40    EVT_SCROLL(wxMDIClientWindow::OnScroll)
41END_EVENT_TABLE()
42
43static const wxChar *TRACE_MDI = _T("mdi");
44
45static const int IDM_WINDOWTILEHOR  = 4001;
46static const int IDM_WINDOWCASCADE = 4002;
47static const int IDM_WINDOWICONS = 4003;
48static const int IDM_WINDOWNEXT = 4004;
49static const int IDM_WINDOWTILEVERT = 4005;
50
51// ----------------------------------------------------------------------------
52// Parent frame
53// ----------------------------------------------------------------------------
54
55void wxMDIParentFrame::Init()
56{
57    m_clientWindow = NULL;
58    m_currentChild = NULL;
59    m_windowMenu = (wxMenu*) NULL;
60    m_parentFrameActive = true;
61    m_shouldBeShown = false;
62}
63
64bool wxMDIParentFrame::Create(wxWindow *parent,
65    wxWindowID id,
66    const wxString& title,
67    const wxPoint& pos,
68    const wxSize& size,
69    long style,
70    const wxString& name)
71{
72    // this style can be used to prevent a window from having the standard MDI
73    // "Window" menu
74    if ( style & wxFRAME_NO_WINDOW_MENU )
75    {
76        m_windowMenu = (wxMenu *)NULL;
77        style -= wxFRAME_NO_WINDOW_MENU ;
78    }
79    else // normal case: we have the window menu, so construct it
80    {
81        m_windowMenu = new wxMenu;
82
83        m_windowMenu->Append(IDM_WINDOWCASCADE, wxT("&Cascade"));
84        m_windowMenu->Append(IDM_WINDOWTILEHOR, wxT("Tile &Horizontally"));
85        m_windowMenu->Append(IDM_WINDOWTILEVERT, wxT("Tile &Vertically"));
86        m_windowMenu->AppendSeparator();
87        m_windowMenu->Append(IDM_WINDOWICONS, wxT("&Arrange Icons"));
88        m_windowMenu->Append(IDM_WINDOWNEXT, wxT("&Next"));
89    }
90
91    wxFrame::Create( parent , id , title , pos , size , style , name ) ;
92    m_parentFrameActive = true;
93
94    OnCreateClient();
95
96    return true;
97}
98
99wxMDIParentFrame::~wxMDIParentFrame()
100{
101    DestroyChildren();
102
103    // already deleted by DestroyChildren()
104    m_clientWindow = NULL ;
105
106    delete m_windowMenu;
107}
108
109void wxMDIParentFrame::SetMenuBar(wxMenuBar *menu_bar)
110{
111    wxFrame::SetMenuBar( menu_bar ) ;
112}
113
114void wxMDIParentFrame::GetRectForTopLevelChildren(int *x, int *y, int *w, int *h)
115{
116    if (x)
117        *x = 0;
118    if (y)
119        *y = 0;
120
121    wxDisplaySize(w, h);
122}
123
124void wxMDIParentFrame::AddChild(wxWindowBase *child)
125{
126    // moved this to front, so that we don't run into unset m_parent problems later
127    wxFrame::AddChild(child);
128
129    if ( !m_currentChild )
130    {
131        m_currentChild = wxDynamicCast(child, wxMDIChildFrame);
132
133        if ( m_currentChild && IsShown() && !ShouldBeVisible() )
134        {
135            // we shouldn't remain visible any more
136            wxFrame::Show(false);
137            m_shouldBeShown = true;
138        }
139    }
140}
141
142void wxMDIParentFrame::RemoveChild(wxWindowBase *child)
143{
144    if ( child == m_currentChild )
145    {
146        // the current child isn't active any more, try to find another one
147        m_currentChild = NULL;
148
149        for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
150              node;
151              node = node->GetNext() )
152        {
153            wxMDIChildFrame *
154                childCur = wxDynamicCast(node->GetData(), wxMDIChildFrame);
155            if ( childCur != child )
156            {
157                m_currentChild = childCur;
158                break;
159            }
160        }
161    }
162
163    wxFrame::RemoveChild(child);
164
165    // if there are no more children left we need to show the frame if we
166    // hadn't shown it before because there were active children and it was
167    // useless (note that we have to do it after fully removing the child, i.e.
168    // after calling the base class RemoveChild() as otherwise we risk to touch
169    // pointer to the child being deleted)
170    if ( !m_currentChild && m_shouldBeShown && !IsShown() )
171    {
172        // we have to show it, but at least move it out of sight and make it of
173        // smallest possible size (unfortunately (0, 0) doesn't work so that it
174        // doesn't appear in expose
175        SetSize(-10000, -10000, 1, 1);
176        Show();
177    }
178}
179
180void wxMDIParentFrame::MacActivate(long timestamp, bool activating)
181{
182    wxLogTrace(TRACE_MDI, wxT("MDI PARENT=%p MacActivate(0x%08lx,%s)"), this, timestamp, activating ? wxT("ACTIV") : wxT("deact"));
183
184    if (activating)
185    {
186        if (s_macDeactivateWindow && s_macDeactivateWindow->GetParent() == this)
187        {
188            wxLogTrace(TRACE_MDI, wxT("child had been scheduled for deactivation, rehighlighting"));
189
190            UMAHighlightAndActivateWindow((WindowRef)s_macDeactivateWindow->MacGetWindowRef(), true);
191
192            wxLogTrace(TRACE_MDI, wxT("finished highliting child"));
193
194            s_macDeactivateWindow = NULL;
195        }
196        else if (s_macDeactivateWindow == this)
197        {
198            wxLogTrace(TRACE_MDI, wxT("Avoided deactivation/activation of this=%p"), this);
199
200            s_macDeactivateWindow = NULL;
201        }
202        else // window to deactivate is NULL or is not us or one of our kids
203        {
204            // activate kid instead
205            if (m_currentChild)
206                m_currentChild->MacActivate(timestamp, activating);
207            else
208                wxFrame::MacActivate(timestamp, activating);
209        }
210    }
211    else
212    {
213        // We were scheduled for deactivation, and now we do it.
214        if (s_macDeactivateWindow == this)
215        {
216            s_macDeactivateWindow = NULL;
217            if (m_currentChild)
218                m_currentChild->MacActivate(timestamp, activating);
219            wxFrame::MacActivate(timestamp, activating);
220        }
221        else // schedule ourselves for deactivation
222        {
223            if (s_macDeactivateWindow)
224                wxLogTrace(TRACE_MDI, wxT("window=%p SHOULD have been deactivated, oh well!"), s_macDeactivateWindow);
225            wxLogTrace(TRACE_MDI, wxT("Scheduling delayed MDI Parent deactivation"));
226
227            s_macDeactivateWindow = this;
228        }
229    }
230}
231
232void wxMDIParentFrame::OnActivate(wxActivateEvent& event)
233{
234    event.Skip();
235}
236
237// Returns the active MDI child window
238wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
239{
240    return m_currentChild ;
241}
242
243// Create the client window class (don't Create the window,
244// just return a new class)
245wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
246{
247    m_clientWindow = new wxMDIClientWindow( this );
248
249    return m_clientWindow;
250}
251
252// Responds to colour changes, and passes event on to children.
253void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent& event)
254{
255    // TODO
256
257    // Propagate the event to the non-top-level children
258    wxFrame::OnSysColourChanged(event);
259}
260
261// MDI operations
262void wxMDIParentFrame::Cascade()
263{
264    // TODO
265}
266
267void wxMDIParentFrame::Tile(wxOrientation WXUNUSED(orient))
268{
269    // TODO
270}
271
272void wxMDIParentFrame::ArrangeIcons()
273{
274    // TODO
275}
276
277void wxMDIParentFrame::ActivateNext()
278{
279    // TODO
280}
281
282void wxMDIParentFrame::ActivatePrevious()
283{
284    // TODO
285}
286
287bool wxMDIParentFrame::ShouldBeVisible() const
288{
289    for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
290          node;
291          node = node->GetNext() )
292    {
293        wxWindow *win = node->GetData();
294
295        if ( win->IsShown()
296                && !wxDynamicCast(win, wxMDIChildFrame)
297#if wxUSE_STATUSBAR
298                && win != (wxWindow*) GetStatusBar()
299#endif
300                && win != GetClientWindow() )
301        {
302            // if we have a non-MDI child, do remain visible so that it could
303            // be used
304            return true;
305        }
306    }
307
308    return false;
309}
310
311bool wxMDIParentFrame::Show( bool show )
312{
313    m_shouldBeShown = false;
314
315    // don't really show the MDI frame unless it has any children other than
316    // MDI children as it is pretty useless in this case
317
318    if ( show )
319    {
320        if ( !ShouldBeVisible() && m_currentChild )
321        {
322            // don't make the window visible now but remember that we should
323            // have had done it
324            m_shouldBeShown = true;
325
326            return false;
327        }
328    }
329
330    return wxFrame::Show(show);
331}
332
333// ----------------------------------------------------------------------------
334// Child frame
335// ----------------------------------------------------------------------------
336
337wxMDIChildFrame::wxMDIChildFrame()
338{
339    Init() ;
340}
341void wxMDIChildFrame::Init()
342{
343}
344
345bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
346                             wxWindowID id,
347                             const wxString& title,
348                             const wxPoint& pos,
349                             const wxSize& size,
350                             long style,
351                             const wxString& name)
352{
353    SetName(name);
354
355    if ( id == wxID_ANY )
356        m_windowId = (int)NewControlId();
357    else
358        m_windowId = id;
359
360    if (parent)
361        parent->AddChild(this);
362
363    MacCreateRealWindow( title, pos , size , MacRemoveBordersFromStyle(style) , name ) ;
364
365    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
366
367    wxModelessWindows.Append(this);
368
369    return true;
370}
371
372wxMDIChildFrame::~wxMDIChildFrame()
373{
374    DestroyChildren();
375}
376
377void wxMDIChildFrame::SetMenuBar(wxMenuBar *menu_bar)
378{
379    return wxFrame::SetMenuBar( menu_bar ) ;
380}
381
382void wxMDIChildFrame::MacActivate(long timestamp, bool activating)
383{
384    wxLogTrace(TRACE_MDI, wxT("MDI child=%p  MacActivate(0x%08lx,%s)"),this, timestamp, activating ? wxT("ACTIV") : wxT("deact"));
385
386    wxMDIParentFrame *mdiparent = wxDynamicCast(m_parent, wxMDIParentFrame);
387    wxASSERT(mdiparent);
388
389    if (activating)
390    {
391        if (s_macDeactivateWindow == m_parent)
392        {
393            wxLogTrace(TRACE_MDI, wxT("parent had been scheduled for deactivation, rehighlighting"));
394
395            UMAHighlightAndActivateWindow((WindowRef)s_macDeactivateWindow->MacGetWindowRef(), true);
396
397            wxLogTrace(TRACE_MDI, wxT("finished highliting parent"));
398
399            s_macDeactivateWindow = NULL;
400        }
401        else if ((mdiparent->m_currentChild == this) || !s_macDeactivateWindow)
402            mdiparent->wxFrame::MacActivate(timestamp, activating);
403
404        if (mdiparent->m_currentChild && mdiparent->m_currentChild != this)
405            mdiparent->m_currentChild->wxFrame::MacActivate(timestamp, false);
406        mdiparent->m_currentChild = this;
407
408        if (s_macDeactivateWindow == this)
409        {
410            wxLogTrace(TRACE_MDI, wxT("Avoided deactivation/activation of this=%p"), this);
411
412            s_macDeactivateWindow = NULL;
413        }
414        else
415            wxFrame::MacActivate(timestamp, activating);
416    }
417    else
418    {
419        // We were scheduled for deactivation, and now we do it.
420        if (s_macDeactivateWindow == this)
421        {
422            s_macDeactivateWindow = NULL;
423            wxFrame::MacActivate(timestamp, activating);
424            if (mdiparent->m_currentChild == this)
425                mdiparent->wxFrame::MacActivate(timestamp, activating);
426        }
427        else // schedule ourselves for deactivation
428        {
429            if (s_macDeactivateWindow)
430                wxLogTrace(TRACE_MDI, wxT("window=%p SHOULD have been deactivated, oh well!"), s_macDeactivateWindow);
431            wxLogTrace(TRACE_MDI, wxT("Scheduling delayed deactivation"));
432
433            s_macDeactivateWindow = this;
434        }
435    }
436}
437
438// MDI operations
439void wxMDIChildFrame::Maximize()
440{
441    wxFrame::Maximize() ;
442}
443
444void wxMDIChildFrame::Restore()
445{
446    wxFrame::Restore() ;
447}
448
449void wxMDIChildFrame::Activate()
450{
451    Raise();
452}
453
454//-----------------------------------------------------------------------------
455// wxMDIClientWindow
456//-----------------------------------------------------------------------------
457
458wxMDIClientWindow::wxMDIClientWindow()
459{
460}
461
462wxMDIClientWindow::~wxMDIClientWindow()
463{
464    DestroyChildren();
465}
466
467bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
468{
469    if ( !wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style) )
470        return false;
471
472    wxModelessWindows.Append(this);
473
474    return true;
475}
476
477// Get size *available for subwindows* i.e. excluding menu bar.
478void wxMDIClientWindow::DoGetClientSize(int *x, int *y) const
479{
480    wxDisplaySize( x , y ) ;
481}
482
483// Explicitly call default scroll behaviour
484void wxMDIClientWindow::OnScroll(wxScrollEvent& event)
485{
486}
487
488#endif // wxUSE_MDI
489