]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/preferences.mm
Allow unsetting wxMenuItem as start of radio group too.
[wxWidgets.git] / src / osx / cocoa / preferences.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/preferences.cpp
3 // Purpose: Native OS X implementation of wxPreferencesEditor.
4 // Author: Vaclav Slavik
5 // Created: 2013-02-19
6 // RCS-ID: $Id$
7 // Copyright: (c) 2013 Vaclav Slavik <vslavik@fastmail.fm>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_PREFERENCES_EDITOR
27
28 #include "wx/private/preferences.h"
29
30 #ifdef wxHAS_PREF_EDITOR_NATIVE
31
32 #include "wx/frame.h"
33 #include "wx/sharedptr.h"
34 #include "wx/toolbar.h"
35 #include "wx/vector.h"
36 #include "wx/weakref.h"
37 #include "wx/windowid.h"
38 #include "wx/osx/private.h"
39
40 #import <AppKit/NSWindow.h>
41
42
43 wxBitmap wxStockPreferencesPage::GetLargeIcon() const
44 {
45 switch ( m_kind )
46 {
47 case Kind_General:
48 return wxBitmap([NSImage imageNamed:NSImageNamePreferencesGeneral]);
49 case Kind_Advanced:
50 return wxBitmap([NSImage imageNamed:NSImageNameAdvanced]);
51 }
52 return wxBitmap(); // silence compiler warning
53 }
54
55
56 class wxCocoaPrefsWindow : public wxFrame
57 {
58 public:
59 wxCocoaPrefsWindow(const wxString& title)
60 : wxFrame(NULL, wxID_ANY, title,
61 wxDefaultPosition, wxDefaultSize,
62 wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX)),
63 m_toolbarRealized(false),
64 m_visiblePage(NULL)
65 {
66 m_toolbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
67 wxTB_FLAT | wxTB_TEXT);
68 m_toolbar->SetToolBitmapSize(wxSize(32,32));
69 m_toolbar->OSXSetSelectableTools(true);
70 SetToolBar(m_toolbar);
71
72 m_toolbar->Bind(wxEVT_TOOL,
73 &wxCocoaPrefsWindow::OnPageChanged, this);
74 Bind(wxEVT_CLOSE_WINDOW, &wxCocoaPrefsWindow::OnClose, this);
75 }
76
77 void AddPage(wxPreferencesPage *page)
78 {
79 wxASSERT_MSG( !m_toolbarRealized,
80 "can't add more preferences pages after showing the window" );
81
82 const wxString title = page->GetName();
83 wxBitmap bmp(page->GetLargeIcon());
84 wxASSERT_MSG( bmp.IsOk(), "OS X requires valid bitmap for preference page" );
85
86 int toolId = wxIdManager::ReserveId();
87 wxToolBarToolBase *tool = m_toolbar->AddTool(toolId, title, bmp);
88
89 wxSharedPtr<PageInfo> info(new PageInfo(page));
90 m_pages.push_back(info);
91
92 tool->SetClientData(info.get());
93 }
94
95 virtual bool Show(bool show)
96 {
97 if ( show && !m_toolbarRealized )
98 {
99 m_toolbar->Realize();
100 m_toolbarRealized = true;
101
102 const wxToolBarToolBase *first = m_toolbar->GetToolByPos(0);
103 wxCHECK_MSG( first, false, "no preferences panels" );
104 OnSelectPageForTool(first);
105 m_toolbar->OSXSelectTool(first->GetId());
106 }
107
108 return wxFrame::Show(show);
109 }
110
111 virtual bool ShouldPreventAppExit() const { return false; }
112
113 protected:
114 // Native preferences windows resize when the selected panel changes and
115 // the resizing is animated, so we need to override DoMoveWindow.
116 virtual void DoMoveWindow(int x, int y, int width, int height)
117 {
118 NSRect r = wxToNSRect(NULL, wxRect(x, y, width, height));
119 NSWindow *win = (NSWindow*)GetWXWindow();
120 [win setFrame:r display:YES animate:YES];
121 }
122
123
124 private:
125 void OnSelectPageForTool(const wxToolBarToolBase *tool)
126 {
127 PageInfo *info = static_cast<PageInfo*>(tool->GetClientData());
128 wxCHECK_RET( info, "toolbar item lacks client data" );
129
130 if ( !info->win )
131 {
132 info->win = info->page->CreateWindow(this);
133 info->win->Hide();
134 info->win->Fit();
135 // fill the page with data using wxEVT_INIT_DIALOG/TransferDataToWindow:
136 info->win->InitDialog();
137 }
138
139 // When the page changes in a native preferences dialog, the sequence
140 // of events is thus:
141
142 // 1. the old page is hidden, only gray background remains
143 if ( m_visiblePage )
144 m_visiblePage->Hide();
145 m_visiblePage = info->win;
146
147 // 2. window is resized to fix the new page, with animation
148 // (in our case, using overriden DoMoveWindow())
149 SetClientSize(info->win->GetSize());
150
151 // 3. new page is shown and the title updated.
152 info->win->Show();
153 SetTitle(info->page->GetName());
154
155 // TODO: Preferences window may have some pages resizeable and some
156 // non-resizable on OS X; the whole window is or is not resizable
157 // depending on which page is selected.
158 //
159 // We'll need to add wxPreferencesPage::IsResizable() virtual
160 // method to implement this.
161 }
162
163 void OnPageChanged(wxCommandEvent& event)
164 {
165 wxToolBarToolBase *tool = m_toolbar->FindById(event.GetId());
166 wxCHECK_RET( tool, "invalid tool ID" );
167 OnSelectPageForTool(tool);
168 }
169
170 void OnClose(wxCloseEvent& e)
171 {
172 // Instead of destroying the window, just hide it, it could be
173 // reused again by another invocation of the editor.
174 Hide();
175 }
176
177 private:
178 struct PageInfo : public wxObject
179 {
180 PageInfo(wxPreferencesPage *p) : page(p), win(NULL) {}
181
182 wxSharedPtr<wxPreferencesPage> page;
183 wxWindow *win;
184 };
185 // All pages. Use shared pointer to be able to get pointers to PageInfo structs
186 wxVector< wxSharedPtr<PageInfo> > m_pages;
187
188 wxToolBar *m_toolbar;
189 bool m_toolbarRealized;
190 wxWindow *m_visiblePage;
191 };
192
193
194 class wxCocoaPreferencesEditorImpl : public wxPreferencesEditorImpl
195 {
196 public:
197 wxCocoaPreferencesEditorImpl(const wxString& title)
198 : m_win(NULL), m_title(title)
199 {
200 }
201
202 virtual ~wxCocoaPreferencesEditorImpl()
203 {
204 // m_win may already be destroyed if this destructor is called from
205 // wxApp's destructor. In that case, all windows -- including this
206 // one -- would already be destroyed by now.
207 if ( m_win )
208 m_win->Destroy();
209 }
210
211 virtual void AddPage(wxPreferencesPage* page)
212 {
213 GetWin()->AddPage(page);
214 }
215
216 virtual void Show(wxWindow* WXUNUSED(parent))
217 {
218 // OS X preferences windows don't have parents, they are independent
219 // windows, so we just ignore the 'parent' argument.
220 wxWindow *win = GetWin();
221 win->Show();
222 win->Raise();
223 }
224
225 virtual void Dismiss()
226 {
227 // Don't destroy the window, only hide it, because OS X preferences
228 // window typically remember their state even when closed. Reopening
229 // the window should show it in the exact same state the user left it.
230 GetWin()->Hide();
231 }
232
233 private:
234 // Use this function to access m_win, so that the window is only created on
235 // demand when actually needed.
236 wxCocoaPrefsWindow* GetWin()
237 {
238 if ( !m_win )
239 {
240 if ( m_title.empty() )
241 m_title = _("Preferences");
242
243 m_win = new wxCocoaPrefsWindow(m_title);
244 }
245
246 return m_win;
247 }
248
249 wxWeakRef<wxCocoaPrefsWindow> m_win;
250
251 wxString m_title;
252 };
253
254 /*static*/
255 wxPreferencesEditorImpl* wxPreferencesEditorImpl::Create(const wxString& title)
256 {
257 return new wxCocoaPreferencesEditorImpl(title);
258 }
259
260 #endif // wxHAS_PREF_EDITOR_NATIVE
261
262 #endif // wxUSE_PREFERENCES_EDITOR