Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / haiku / SDL_bmessagebox.cc
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
78         SortButtonsPredicate(const SDL_MessageBoxButtonData *aButtonLeft,
79                                          const SDL_MessageBoxButtonData *aButtonRight)
80         {
81                 return aButtonLeft->buttonid < aButtonRight->buttonid;
82         }
83
84         alert_type
85         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
106         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
113         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
126         UpdateTextViewWidth(void)
127         {
128                 fComputedMessageBoxWidth = fMessageBoxTextView->PreferredSize().Width() + GetLeftPanelWidth();
129         }
130
131         void
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[0]) ?
158                         SetTitle(aMessageBoxData->title) : SetTitle(HAIKU_SDL_DefTitle);
159                 (aMessageBoxData->message[0]) ?
160                         SetMessageText(aMessageBoxData->message) : SetMessageText(HAIKU_SDL_DefMessage);
161
162                 SetType(ConvertMessageBoxType(static_cast<SDL_MessageBoxFlags>(aMessageBoxData->flags)));
163         }
164
165         void
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
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
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
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
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
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
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
330         GetCloseButtonId(void) const
331         {
332                 return fCloseButton;
333         }
334
335         virtual
336         ~HAIKU_SDL_MessageBox(void)
337         {
338                 fButtons.clear();
339         }
340
341 protected:
342         virtual void
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
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
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: */