1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/preferences.cpp
3 // Purpose: Native OS X implementation of wxPreferencesEditor.
4 // Author: Vaclav Slavik
6 // Copyright: (c) 2013 Vaclav Slavik <vslavik@fastmail.fm>
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #if wxUSE_PREFERENCES_EDITOR
27 #include "wx/private/preferences.h"
29 #ifdef wxHAS_PREF_EDITOR_NATIVE
32 #include "wx/sharedptr.h"
33 #include "wx/toolbar.h"
34 #include "wx/vector.h"
35 #include "wx/weakref.h"
36 #include "wx/windowid.h"
37 #include "wx/osx/private.h"
39 #import <AppKit/NSWindow.h>
42 wxBitmap wxStockPreferencesPage::GetLargeIcon() const
47 return wxBitmap([NSImage imageNamed:NSImageNamePreferencesGeneral]);
49 return wxBitmap([NSImage imageNamed:NSImageNameAdvanced]);
51 return wxBitmap(); // silence compiler warning
55 class wxCocoaPrefsWindow : public wxFrame
58 wxCocoaPrefsWindow(const wxString& title)
59 : wxFrame(NULL, wxID_ANY, title,
60 wxDefaultPosition, wxDefaultSize,
61 wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX)),
62 m_toolbarRealized(false),
65 m_toolbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
66 wxTB_FLAT | wxTB_TEXT);
67 m_toolbar->SetToolBitmapSize(wxSize(32,32));
68 m_toolbar->OSXSetSelectableTools(true);
69 SetToolBar(m_toolbar);
71 m_toolbar->Bind(wxEVT_TOOL,
72 &wxCocoaPrefsWindow::OnPageChanged, this);
73 Bind(wxEVT_CLOSE_WINDOW, &wxCocoaPrefsWindow::OnClose, this);
76 void AddPage(wxPreferencesPage *page)
78 wxASSERT_MSG( !m_toolbarRealized,
79 "can't add more preferences pages after showing the window" );
81 const wxString title = page->GetName();
82 wxBitmap bmp(page->GetLargeIcon());
83 wxASSERT_MSG( bmp.IsOk(), "OS X requires valid bitmap for preference page" );
85 int toolId = wxIdManager::ReserveId();
86 wxToolBarToolBase *tool = m_toolbar->AddTool(toolId, title, bmp);
88 wxSharedPtr<PageInfo> info(new PageInfo(page));
89 m_pages.push_back(info);
91 tool->SetClientData(info.get());
94 virtual bool Show(bool show)
96 if ( show && !m_toolbarRealized )
99 m_toolbarRealized = true;
101 const wxToolBarToolBase *first = m_toolbar->GetToolByPos(0);
102 wxCHECK_MSG( first, false, "no preferences panels" );
103 OnSelectPageForTool(first);
104 m_toolbar->OSXSelectTool(first->GetId());
107 return wxFrame::Show(show);
110 virtual bool ShouldPreventAppExit() const { return false; }
113 // Native preferences windows resize when the selected panel changes and
114 // the resizing is animated, so we need to override DoMoveWindow.
115 virtual void DoMoveWindow(int x, int y, int width, int height)
117 NSRect r = wxToNSRect(NULL, wxRect(x, y, width, height));
118 NSWindow *win = (NSWindow*)GetWXWindow();
119 [win setFrame:r display:YES animate:YES];
124 void OnSelectPageForTool(const wxToolBarToolBase *tool)
126 PageInfo *info = static_cast<PageInfo*>(tool->GetClientData());
127 wxCHECK_RET( info, "toolbar item lacks client data" );
131 info->win = info->page->CreateWindow(this);
134 // fill the page with data using wxEVT_INIT_DIALOG/TransferDataToWindow:
135 info->win->InitDialog();
138 // When the page changes in a native preferences dialog, the sequence
139 // of events is thus:
141 // 1. the old page is hidden, only gray background remains
143 m_visiblePage->Hide();
144 m_visiblePage = info->win;
146 // 2. window is resized to fix the new page, with animation
147 // (in our case, using overriden DoMoveWindow())
148 SetClientSize(info->win->GetSize());
150 // 3. new page is shown and the title updated.
152 SetTitle(info->page->GetName());
154 // TODO: Preferences window may have some pages resizeable and some
155 // non-resizable on OS X; the whole window is or is not resizable
156 // depending on which page is selected.
158 // We'll need to add wxPreferencesPage::IsResizable() virtual
159 // method to implement this.
162 void OnPageChanged(wxCommandEvent& event)
164 wxToolBarToolBase *tool = m_toolbar->FindById(event.GetId());
165 wxCHECK_RET( tool, "invalid tool ID" );
166 OnSelectPageForTool(tool);
169 void OnClose(wxCloseEvent& e)
171 // Instead of destroying the window, just hide it, it could be
172 // reused again by another invocation of the editor.
177 struct PageInfo : public wxObject
179 PageInfo(wxPreferencesPage *p) : page(p), win(NULL) {}
181 wxSharedPtr<wxPreferencesPage> page;
184 // All pages. Use shared pointer to be able to get pointers to PageInfo structs
185 wxVector< wxSharedPtr<PageInfo> > m_pages;
187 wxToolBar *m_toolbar;
188 bool m_toolbarRealized;
189 wxWindow *m_visiblePage;
193 class wxCocoaPreferencesEditorImpl : public wxPreferencesEditorImpl
196 wxCocoaPreferencesEditorImpl(const wxString& title)
197 : m_win(NULL), m_title(title)
201 virtual ~wxCocoaPreferencesEditorImpl()
203 // m_win may already be destroyed if this destructor is called from
204 // wxApp's destructor. In that case, all windows -- including this
205 // one -- would already be destroyed by now.
210 virtual void AddPage(wxPreferencesPage* page)
212 GetWin()->AddPage(page);
215 virtual void Show(wxWindow* WXUNUSED(parent))
217 // OS X preferences windows don't have parents, they are independent
218 // windows, so we just ignore the 'parent' argument.
219 wxWindow *win = GetWin();
224 virtual void Dismiss()
226 // Don't destroy the window, only hide it, because OS X preferences
227 // window typically remember their state even when closed. Reopening
228 // the window should show it in the exact same state the user left it.
233 // Use this function to access m_win, so that the window is only created on
234 // demand when actually needed.
235 wxCocoaPrefsWindow* GetWin()
239 if ( m_title.empty() )
240 m_title = _("Preferences");
242 m_win = new wxCocoaPrefsWindow(m_title);
248 wxWeakRef<wxCocoaPrefsWindow> m_win;
254 wxPreferencesEditorImpl* wxPreferencesEditorImpl::Create(const wxString& title)
256 return new wxCocoaPreferencesEditorImpl(title);
259 #endif // wxHAS_PREF_EDITOR_NATIVE
261 #endif // wxUSE_PREFERENCES_EDITOR