wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / osx / cocoa / msgdlg.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/cocoa/msgdlg.mm
3 // Purpose:     wxMessageDialog
4 // Author:      Stefan Csomor
5 // Modified by:
6 // Created:     04/01/98
7 // Copyright:   (c) Stefan Csomor
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #include "wx/msgdlg.h"
14
15 #ifndef WX_PRECOMP
16     #include "wx/intl.h"
17     #include "wx/app.h"
18 #endif
19
20 #include "wx/control.h"
21 #include "wx/thread.h"
22 #include "wx/evtloop.h"
23 #include "wx/modalhook.h"
24 #include "wx/osx/private.h"
25
26
27 IMPLEMENT_CLASS(wxMessageDialog, wxDialog)
28
29
30 namespace 
31 {
32     NSAlertStyle GetAlertStyleFromWXStyle( long style )
33     {
34         NSAlertStyle alertType = NSWarningAlertStyle;
35         if (style & wxICON_EXCLAMATION)
36             alertType = NSCriticalAlertStyle;
37         else if (style & wxICON_HAND)
38             alertType = NSWarningAlertStyle;
39         else if (style & wxICON_INFORMATION)
40             alertType = NSInformationalAlertStyle;
41         else if (style & wxICON_QUESTION)
42             alertType = NSInformationalAlertStyle;
43         return alertType;
44     }
45 }
46
47 wxMessageDialog::wxMessageDialog(wxWindow *parent,
48                                  const wxString& message,
49                                  const wxString& caption,
50                                  long style,
51                                  const wxPoint& WXUNUSED(pos))
52                : wxMessageDialogBase(parent, message, caption, style)
53 {
54     m_sheetDelegate = [[ModalDialogDelegate alloc] init];
55     [(ModalDialogDelegate*)m_sheetDelegate setImplementation: this];
56 }
57
58 wxMessageDialog::~wxMessageDialog()
59 {
60     [m_sheetDelegate release];
61 }
62
63 int wxMessageDialog::ShowModal()
64 {
65     WX_HOOK_MODAL_DIALOG();
66
67     wxCFEventLoopPauseIdleEvents pause;
68     
69     int resultbutton = wxID_CANCEL;
70
71     const long style = GetMessageDialogStyle();
72
73     wxASSERT_MSG( (style & 0x3F) != wxYES, wxT("this style is not supported on Mac") );
74
75     // work out what to display
76     // if the extended text is empty then we use the caption as the title
77     // and the message as the text (for backwards compatibility)
78     // but if the extended message is not empty then we use the message as the title
79     // and the extended message as the text because that makes more sense
80
81     wxString msgtitle,msgtext;
82     if(m_extendedMessage.IsEmpty())
83     {
84         msgtitle = m_caption;
85         msgtext  = m_message;
86     }
87     else
88     {
89         msgtitle = m_message;
90         msgtext  = m_extendedMessage;
91     }
92
93
94     if ( !wxIsMainThread() )
95     {
96         CFStringRef defaultButtonTitle = NULL;
97         CFStringRef alternateButtonTitle = NULL;
98         CFStringRef otherButtonTitle = NULL;
99
100         wxCFStringRef cfTitle( msgtitle, GetFont().GetEncoding() );
101         wxCFStringRef cfText( msgtext, GetFont().GetEncoding() );
102
103         wxCFStringRef cfNoString( wxControl::GetLabelText(GetNoLabel()), GetFont().GetEncoding() );
104         wxCFStringRef cfYesString( wxControl::GetLabelText(GetYesLabel()), GetFont().GetEncoding() );
105         wxCFStringRef cfOKString( wxControl::GetLabelText(GetOKLabel()), GetFont().GetEncoding()) ;
106         wxCFStringRef cfCancelString( wxControl::GetLabelText(GetCancelLabel()), GetFont().GetEncoding() );
107
108         NSAlertStyle alertType = GetAlertStyleFromWXStyle(style);
109                 
110         int m_buttonId[4] = { 0, 0, 0, wxID_CANCEL /* time-out */ };
111
112         if (style & wxYES_NO)
113         {
114             if ( style & wxNO_DEFAULT )
115             {
116                 defaultButtonTitle = cfNoString;
117                 alternateButtonTitle = cfYesString;
118                 m_buttonId[0] = wxID_NO;
119                 m_buttonId[1] = wxID_YES;
120             }
121             else
122             {
123                 defaultButtonTitle = cfYesString;
124                 alternateButtonTitle = cfNoString;
125                 m_buttonId[0] = wxID_YES;
126                 m_buttonId[1] = wxID_NO;
127             }
128             if (style & wxCANCEL)
129             {
130                 otherButtonTitle = cfCancelString;
131                 m_buttonId[2] = wxID_CANCEL;
132             }
133         }
134         else
135         {
136             // the MSW implementation even shows an OK button if it is not specified, we'll do the same
137             m_buttonId[0] = wxID_OK;
138             // using null as default title does not work on earlier systems
139             defaultButtonTitle = cfOKString;
140             if (style & wxCANCEL)
141             {
142                 alternateButtonTitle = cfCancelString;
143                 m_buttonId[1] = wxID_CANCEL;
144             }
145         }
146
147         wxASSERT_MSG( !(style & wxHELP), "wxHELP not supported in non-GUI thread" );
148
149         CFOptionFlags exitButton;
150         OSStatus err = CFUserNotificationDisplayAlert(
151             0, alertType, NULL, NULL, NULL, cfTitle, cfText,
152             defaultButtonTitle, alternateButtonTitle, otherButtonTitle, &exitButton );
153         if (err == noErr)
154             resultbutton = m_buttonId[exitButton];
155         SetReturnCode(resultbutton);
156     }
157     else
158     {
159         NSAlert* alert = (NSAlert*)ConstructNSAlert();
160
161         int button = -1;
162         button = [alert runModal];
163         [alert release];
164         ModalFinishedCallback(alert, button);
165     }
166
167     return GetReturnCode();
168 }
169
170 void wxMessageDialog::ShowWindowModal()
171 {
172     NSAlert* alert = (NSAlert*)ConstructNSAlert();
173
174     wxNonOwnedWindow* parentWindow = NULL;
175
176     m_modality = wxDIALOG_MODALITY_WINDOW_MODAL;
177
178     if (GetParent())
179         parentWindow = dynamic_cast<wxNonOwnedWindow*>(wxGetTopLevelParent(GetParent()));
180
181     wxASSERT_MSG(parentWindow, "Window modal display requires parent.");
182
183     if (parentWindow)
184     {
185         NSWindow* nativeParent = parentWindow->GetWXWindow();
186         [alert beginSheetModalForWindow: nativeParent modalDelegate: m_sheetDelegate
187             didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
188             contextInfo: nil];
189     }
190 }
191
192 void wxMessageDialog::ModalFinishedCallback(void* WXUNUSED(panel), int resultCode)
193 {
194     int resultbutton = wxID_CANCEL;
195     if ( resultCode < NSAlertFirstButtonReturn )
196         resultbutton = wxID_CANCEL;
197     else
198     {
199         if ( resultCode - NSAlertFirstButtonReturn < m_buttonCount )
200             resultbutton = m_buttonId[ resultCode - NSAlertFirstButtonReturn ];
201         else
202             resultbutton = wxID_CANCEL;
203     }
204     SetReturnCode(resultbutton);
205     
206     if (GetModality() == wxDIALOG_MODALITY_WINDOW_MODAL)
207         SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED  );
208 }
209
210 void* wxMessageDialog::ConstructNSAlert()
211 {
212     const long style = GetMessageDialogStyle();
213
214     wxASSERT_MSG( (style & 0x3F) != wxYES, wxT("this style is not supported on Mac") );
215
216     // work out what to display
217     // if the extended text is empty then we use the caption as the title
218     // and the message as the text (for backwards compatibility)
219     // but if the extended message is not empty then we use the message as the title
220     // and the extended message as the text because that makes more sense
221
222     wxString msgtitle,msgtext;
223     if(m_extendedMessage.IsEmpty())
224     {
225         msgtitle = m_caption;
226         msgtext  = m_message;
227     }
228     else
229     {
230         msgtitle = m_message;
231         msgtext  = m_extendedMessage;
232     }
233
234     NSAlert* alert = [[NSAlert alloc] init];
235     NSAlertStyle alertType = GetAlertStyleFromWXStyle(style);
236
237     wxCFStringRef cfNoString( wxControl::GetLabelText(GetNoLabel()), GetFont().GetEncoding() );
238     wxCFStringRef cfYesString( wxControl::GetLabelText(GetYesLabel()), GetFont().GetEncoding() );
239     wxCFStringRef cfOKString( wxControl::GetLabelText(GetOKLabel()), GetFont().GetEncoding() );
240     wxCFStringRef cfCancelString( wxControl::GetLabelText(GetCancelLabel()), GetFont().GetEncoding() );
241
242     wxCFStringRef cfTitle( msgtitle, GetFont().GetEncoding() );
243     wxCFStringRef cfText( msgtext, GetFont().GetEncoding() );
244
245     [alert setMessageText:cfTitle.AsNSString()];
246     [alert setInformativeText:cfText.AsNSString()];
247     [alert setAlertStyle:alertType];
248
249     m_buttonCount = 0;
250
251     if (style & wxYES_NO)
252     {
253         if ( style & wxNO_DEFAULT )
254         {
255             [alert addButtonWithTitle:cfNoString.AsNSString()];
256             m_buttonId[ m_buttonCount++ ] = wxID_NO;
257             [alert addButtonWithTitle:cfYesString.AsNSString()];
258             m_buttonId[ m_buttonCount++ ] = wxID_YES;
259         }
260         else
261         {
262             [alert addButtonWithTitle:cfYesString.AsNSString()];
263             m_buttonId[ m_buttonCount++ ] = wxID_YES;
264             [alert addButtonWithTitle:cfNoString.AsNSString()];
265             m_buttonId[ m_buttonCount++ ] = wxID_NO;
266         }
267
268         if (style & wxCANCEL)
269         {
270             [alert addButtonWithTitle:cfCancelString.AsNSString()];
271             m_buttonId[ m_buttonCount++ ] = wxID_CANCEL;
272         }
273     }
274     // the MSW implementation even shows an OK button if it is not specified, we'll do the same
275     else
276     {
277         if ( style & wxCANCEL_DEFAULT )
278         {
279             [alert addButtonWithTitle:cfCancelString.AsNSString()];
280             m_buttonId[ m_buttonCount++ ] = wxID_CANCEL;
281
282             [alert addButtonWithTitle:cfOKString.AsNSString()];
283             m_buttonId[ m_buttonCount++ ] = wxID_OK;
284         }
285         else 
286         {
287             [alert addButtonWithTitle:cfOKString.AsNSString()];
288             m_buttonId[ m_buttonCount++ ] = wxID_OK;
289             if (style & wxCANCEL)
290             {
291                 [alert addButtonWithTitle:cfCancelString.AsNSString()];
292                 m_buttonId[ m_buttonCount++ ] = wxID_CANCEL;
293             }
294         }
295
296     }
297
298     if ( style & wxHELP )
299     {
300         wxCFStringRef cfHelpString( GetHelpLabel(), GetFont().GetEncoding() );
301         [alert addButtonWithTitle:cfHelpString.AsNSString()];
302         m_buttonId[ m_buttonCount++ ] = wxID_HELP;
303     }
304
305     wxASSERT_MSG( m_buttonCount <= WXSIZEOF(m_buttonId), "Too many buttons" );
306
307     return alert;
308 }