1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 Copyright (C) 2018-2019 EXL <exlmotodev@gmail.com>
5
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13
14 1. The origin of this software must not be misrepresented; you must not
15 claim that you wrote the original software. If you use this software
16 in a product, an acknowledgment in the product documentation would be
17 appreciated but is not required.
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source distribution.
21 */
22
23 #include "../../SDL_internal.h"
24
25 #if SDL_VIDEO_DRIVER_HAIKU
26
27 #include "SDL_messagebox.h"
28
29 /* For application signature. */
30 #include "../../main/haiku/SDL_BeApp.h"
31
32 #include <Alert.h>
33 #include <Application.h>
34 #include <Button.h>
35 #include <Font.h>
36 #include <Layout.h>
37 #include <String.h>
38 #include <TextView.h>
39 #include <View.h>
40 #include <Window.h>
41
42 #include <InterfaceDefs.h>
43 #include <SupportDefs.h>
44 #include <GraphicsDefs.h>
45
46 #include <new>
47 #include <vector>
48 #include <algorithm>
49
50 enum
51 {
52 G_CLOSE_BUTTON_ID = -1,
53 G_DEFAULT_BUTTON_ID = 0,
54 G_MAX_STRING_LENGTH_BYTES = 120
55 };
56
57 class HAIKU_SDL_MessageBox : public BAlert
58 {
59 float fComputedMessageBoxWidth;
60
61 BTextView *fMessageBoxTextView;
62
63 int fCloseButton;
64 int fDefaultButton;
65
66 bool fCustomColorScheme;
67 bool fThereIsLongLine;
68 rgb_color fTextColor;
69
70 const char *fTitle;
71 const char *HAIKU_SDL_DefTitle;
72 const char *HAIKU_SDL_DefMessage;
73 const char *HAIKU_SDL_DefButton;
74
75 std::vector<const SDL_MessageBoxButtonData *> fButtons;
76
77 static bool
SortButtonsPredicate(const SDL_MessageBoxButtonData * aButtonLeft,const SDL_MessageBoxButtonData * aButtonRight)78 SortButtonsPredicate(const SDL_MessageBoxButtonData *aButtonLeft,
79 const SDL_MessageBoxButtonData *aButtonRight)
80 {
81 return aButtonLeft->buttonid < aButtonRight->buttonid;
82 }
83
84 alert_type
ConvertMessageBoxType(const SDL_MessageBoxFlags aWindowType) const85 ConvertMessageBoxType(const SDL_MessageBoxFlags aWindowType) const
86 {
87 switch (aWindowType)
88 {
89 default:
90 case SDL_MESSAGEBOX_WARNING:
91 {
92 return B_WARNING_ALERT;
93 }
94 case SDL_MESSAGEBOX_ERROR:
95 {
96 return B_STOP_ALERT;
97 }
98 case SDL_MESSAGEBOX_INFORMATION:
99 {
100 return B_INFO_ALERT;
101 }
102 }
103 }
104
105 rgb_color
ConvertColorType(const SDL_MessageBoxColor * aColor) const106 ConvertColorType(const SDL_MessageBoxColor *aColor) const
107 {
108 rgb_color color = { aColor->r, aColor->g, aColor->b, color.alpha = 255 };
109 return color;
110 }
111
112 int32
GetLeftPanelWidth(void) const113 GetLeftPanelWidth(void) const
114 {
115 // See file "haiku/src/kits/interface/Alert.cpp" for this magic numbers.
116 // IconStripeWidth = 30 * Scale
117 // IconSize = 32 * Scale
118 // Scale = max_c(1, ((int32)be_plain_font->Size() + 15) / 16)
119 // RealWidth = (IconStripeWidth * Scale) + (IconSize * Scale)
120
121 int32 scale = max_c(1, ((int32)be_plain_font->Size() + 15) / 16);
122 return (30 * scale) + (32 * scale);
123 }
124
125 void
UpdateTextViewWidth(void)126 UpdateTextViewWidth(void)
127 {
128 fComputedMessageBoxWidth = fMessageBoxTextView->PreferredSize().Width() + GetLeftPanelWidth();
129 }
130
131 void
ParseSdlMessageBoxData(const SDL_MessageBoxData * aMessageBoxData)132 ParseSdlMessageBoxData(const SDL_MessageBoxData *aMessageBoxData)
133 {
134 if (aMessageBoxData == NULL)
135 {
136 SetTitle(HAIKU_SDL_DefTitle);
137 SetMessageText(HAIKU_SDL_DefMessage);
138 AddButton(HAIKU_SDL_DefButton);
139 return;
140 }
141
142 if (aMessageBoxData->numbuttons <= 0)
143 {
144 AddButton(HAIKU_SDL_DefButton);
145 }
146 else
147 {
148 AddSdlButtons(aMessageBoxData->buttons, aMessageBoxData->numbuttons);
149 }
150
151 if (aMessageBoxData->colorScheme != NULL)
152 {
153 fCustomColorScheme = true;
154 ApplyAndParseColorScheme(aMessageBoxData->colorScheme);
155 }
156
157 (aMessageBoxData->title != NULL) ?
158 SetTitle(aMessageBoxData->title) : SetTitle(HAIKU_SDL_DefTitle);
159 (aMessageBoxData->message != NULL) ?
160 SetMessageText(aMessageBoxData->message) : SetMessageText(HAIKU_SDL_DefMessage);
161
162 SetType(ConvertMessageBoxType(static_cast<SDL_MessageBoxFlags>(aMessageBoxData->flags)));
163 }
164
165 void
ApplyAndParseColorScheme(const SDL_MessageBoxColorScheme * aColorScheme)166 ApplyAndParseColorScheme(const SDL_MessageBoxColorScheme *aColorScheme)
167 {
168 SetBackgroundColor(&aColorScheme->colors[SDL_MESSAGEBOX_COLOR_BACKGROUND]);
169 fTextColor = ConvertColorType(&aColorScheme->colors[SDL_MESSAGEBOX_COLOR_TEXT]);
170 SetButtonColors(&aColorScheme->colors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER],
171 &aColorScheme->colors[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND],
172 &aColorScheme->colors[SDL_MESSAGEBOX_COLOR_TEXT],
173 &aColorScheme->colors[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED]);
174 }
175
176 void
SetButtonColors(const SDL_MessageBoxColor * aBorderColor,const SDL_MessageBoxColor * aBackgroundColor,const SDL_MessageBoxColor * aTextColor,const SDL_MessageBoxColor * aSelectedColor)177 SetButtonColors(const SDL_MessageBoxColor *aBorderColor,
178 const SDL_MessageBoxColor *aBackgroundColor,
179 const SDL_MessageBoxColor *aTextColor,
180 const SDL_MessageBoxColor *aSelectedColor)
181 {
182 if (fCustomColorScheme)
183 {
184 int32 countButtons = CountButtons();
185 for (int i = 0; i < countButtons; ++i)
186 {
187 ButtonAt(i)->SetViewColor(ConvertColorType(aBorderColor));
188 ButtonAt(i)->SetLowColor(ConvertColorType(aBackgroundColor));
189
190 // This doesn't work. See this why:
191 // https://github.com/haiku/haiku/commit/de9c53f8f5008c7b3b0af75d944a628e17f6dffe
192 // Let it remain.
193 ButtonAt(i)->SetHighColor(ConvertColorType(aTextColor));
194 }
195 }
196 // TODO: Not Implemented.
197 // Is it even necessary?!
198 (void)aSelectedColor;
199 }
200
201 void
SetBackgroundColor(const SDL_MessageBoxColor * aColor)202 SetBackgroundColor(const SDL_MessageBoxColor *aColor)
203 {
204 rgb_color background = ConvertColorType(aColor);
205
206 GetLayout()->View()->SetViewColor(background);
207 // See file "haiku/src/kits/interface/Alert.cpp", the "TAlertView" is the internal name of the left panel.
208 FindView("TAlertView")->SetViewColor(background);
209 fMessageBoxTextView->SetViewColor(background);
210 }
211
212 bool
CheckLongLines(const char * aMessage)213 CheckLongLines(const char *aMessage)
214 {
215 int final = 0;
216
217 // This UTF-8 friendly.
218 BString message = aMessage;
219 int32 length = message.CountChars();
220
221 for (int i = 0, c = 0; i < length; ++i)
222 {
223 c++;
224 if (*(message.CharAt(i)) == '\n')
225 {
226 c = 0;
227 }
228 if (c > final)
229 {
230 final = c;
231 }
232 }
233
234 return (final > G_MAX_STRING_LENGTH_BYTES);
235 }
236
237 void
SetMessageText(const char * aMessage)238 SetMessageText(const char *aMessage)
239 {
240 fThereIsLongLine = CheckLongLines(aMessage);
241 if (fThereIsLongLine)
242 {
243 fMessageBoxTextView->SetWordWrap(true);
244 }
245
246 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
247 if (fCustomColorScheme)
248 {
249 textColor = fTextColor;
250 }
251
252 /*
253 if (fNoTitledWindow)
254 {
255 fMessageBoxTextView->SetFontAndColor(be_bold_font);
256 fMessageBoxTextView->Insert(fTitle);
257 fMessageBoxTextView->Insert("\n\n");
258 fMessageBoxTextView->SetFontAndColor(be_plain_font);
259 }
260 */
261
262 fMessageBoxTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
263 fMessageBoxTextView->Insert(aMessage);
264
265 // Be sure to call update width method.
266 UpdateTextViewWidth();
267 }
268
269 void
AddSdlButtons(const SDL_MessageBoxButtonData * aButtons,int aNumButtons)270 AddSdlButtons(const SDL_MessageBoxButtonData *aButtons, int aNumButtons)
271 {
272 for (int i = 0; i < aNumButtons; ++i)
273 {
274 fButtons.push_back(&aButtons[i]);
275 }
276
277 std::sort(fButtons.begin(), fButtons.end(), &HAIKU_SDL_MessageBox::SortButtonsPredicate);
278
279 size_t countButtons = fButtons.size();
280 for (size_t i = 0; i < countButtons; ++i)
281 {
282 switch (fButtons[i]->flags)
283 {
284 case SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT:
285 {
286 fCloseButton = static_cast<int>(i);
287 break;
288 }
289 case SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT:
290 {
291 fDefaultButton = static_cast<int>(i);
292 break;
293 }
294 default:
295 {
296 break;
297 }
298 }
299 AddButton(fButtons[i]->text);
300 }
301
302 SetDefaultButton(ButtonAt(fDefaultButton));
303 }
304
305 public:
306 explicit
HAIKU_SDL_MessageBox(const SDL_MessageBoxData * aMessageBoxData)307 HAIKU_SDL_MessageBox(const SDL_MessageBoxData *aMessageBoxData)
308 : BAlert(NULL, NULL, NULL, NULL, NULL, B_WIDTH_FROM_LABEL, B_WARNING_ALERT),
309 fComputedMessageBoxWidth(0.0f),
310 fCloseButton(G_CLOSE_BUTTON_ID), fDefaultButton(G_DEFAULT_BUTTON_ID),
311 fCustomColorScheme(false), fThereIsLongLine(false),
312 HAIKU_SDL_DefTitle("SDL2 MessageBox"),
313 HAIKU_SDL_DefMessage("Some information has been lost."),
314 HAIKU_SDL_DefButton("OK")
315 {
316 // MessageBox settings.
317 // We need a title to display it.
318 SetLook(B_TITLED_WINDOW_LOOK);
319 SetFlags(Flags() | B_CLOSE_ON_ESCAPE);
320
321 // MessageBox TextView settings.
322 fMessageBoxTextView = TextView();
323 fMessageBoxTextView->SetWordWrap(false);
324 fMessageBoxTextView->SetStylable(true);
325
326 ParseSdlMessageBoxData(aMessageBoxData);
327 }
328
329 int
GetCloseButtonId(void) const330 GetCloseButtonId(void) const
331 {
332 return fCloseButton;
333 }
334
335 virtual
~HAIKU_SDL_MessageBox(void)336 ~HAIKU_SDL_MessageBox(void)
337 {
338 fButtons.clear();
339 }
340
341 protected:
342 virtual void
FrameResized(float aNewWidth,float aNewHeight)343 FrameResized(float aNewWidth, float aNewHeight)
344 {
345 if (fComputedMessageBoxWidth > aNewWidth)
346 {
347 ResizeTo(fComputedMessageBoxWidth, aNewHeight);
348 }
349 else
350 {
351 BAlert::FrameResized(aNewWidth, aNewHeight);
352 }
353 }
354
355 virtual void
SetTitle(const char * aTitle)356 SetTitle(const char* aTitle)
357 {
358 fTitle = aTitle;
359 BAlert::SetTitle(aTitle);
360 }
361 };
362
363 #ifdef __cplusplus
364 extern "C" {
365 #endif
366
367 int
HAIKU_ShowMessageBox(const SDL_MessageBoxData * messageboxdata,int * buttonid)368 HAIKU_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
369 {
370 // Initialize button by closed or error value first.
371 *buttonid = G_CLOSE_BUTTON_ID;
372
373 // We need to check "be_app" pointer to "NULL". The "messageboxdata->window" pointer isn't appropriate here
374 // because it is possible to create a MessageBox from another thread. This fixes the following errors:
375 // "You need a valid BApplication object before interacting with the app_server."
376 // "2 BApplication objects were created. Only one is allowed."
377 BApplication *application = NULL;
378 if (be_app == NULL)
379 {
380 application = new(std::nothrow) BApplication(signature);
381 if (application == NULL)
382 {
383 return SDL_SetError("Cannot create the BApplication object. Lack of memory?");
384 }
385 }
386
387 HAIKU_SDL_MessageBox *SDL_MessageBox = new(std::nothrow) HAIKU_SDL_MessageBox(messageboxdata);
388 if (SDL_MessageBox == NULL)
389 {
390 return SDL_SetError("Cannot create the HAIKU_SDL_MessageBox (BAlert inheritor) object. Lack of memory?");
391 }
392 const int closeButton = SDL_MessageBox->GetCloseButtonId();
393 int pushedButton = SDL_MessageBox->Go();
394
395 // The close button is equivalent to pressing Escape.
396 if (closeButton != G_CLOSE_BUTTON_ID && pushedButton == G_CLOSE_BUTTON_ID)
397 {
398 pushedButton = closeButton;
399 }
400
401 // It's deleted by itself after the "Go()" method was executed.
402 /*
403 if (messageBox != NULL)
404 {
405 delete messageBox;
406 }
407 */
408 if (application != NULL)
409 {
410 delete application;
411 }
412
413 // Initialize button by real pushed value then.
414 *buttonid = pushedButton;
415
416 return 0;
417 }
418
419 #ifdef __cplusplus
420 }
421 #endif
422
423 #endif /* SDL_VIDEO_DRIVER_HAIKU */
424
425 /* vi: set ts=4 sw=4 expandtab: */
426