SDL  2.0
SDL_bmessagebox.cc
Go to the documentation of this file.
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:
91  {
92  return B_WARNING_ALERT;
93  }
95  {
96  return B_STOP_ALERT;
97  }
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 != 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
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],
172  &aColorScheme->colors[SDL_MESSAGEBOX_COLOR_TEXT],
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  {
285  {
286  fCloseButton = static_cast<int>(i);
287  break;
288  }
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: */
SDL_MESSAGEBOX_COLOR_BUTTON_BORDER
@ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER
Definition: SDL_messagebox.h:77
c
const GLubyte * c
Definition: SDL_opengl_glext.h:11096
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
Definition: SDL_messagebox.h:52
SDL_MessageBoxColorScheme::colors
SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]
Definition: SDL_messagebox.h:88
scale
GLenum GLenum GLenum GLenum GLenum scale
Definition: SDL_opengl_glext.h:9378
SDL_MessageBoxData::colorScheme
const SDL_MessageBoxColorScheme * colorScheme
Definition: SDL_messagebox.h:104
SDL_MessageBoxColor::b
Uint8 b
Definition: SDL_messagebox.h:70
NULL
#define NULL
Definition: begin_code.h:167
message
GLuint GLsizei const GLchar * message
Definition: SDL_opengl_glext.h:2486
textColor
static SDL_Color textColor
Definition: testime.c:46
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:98
SDL_MESSAGEBOX_COLOR_TEXT
@ SDL_MESSAGEBOX_COLOR_TEXT
Definition: SDL_messagebox.h:76
SDL_MESSAGEBOX_COLOR_BACKGROUND
@ SDL_MESSAGEBOX_COLOR_BACKGROUND
Definition: SDL_messagebox.h:75
SDL_messagebox.h
SDL_MESSAGEBOX_WARNING
@ SDL_MESSAGEBOX_WARNING
Definition: SDL_messagebox.h:40
SDL_MessageBoxData::message
const char * message
Definition: SDL_messagebox.h:99
length
GLuint GLsizei GLsizei * length
Definition: SDL_opengl_glext.h:672
SDL_MessageBoxFlags
SDL_MessageBoxFlags
SDL_MessageBox flags. If supported will display warning icon, etc.
Definition: SDL_messagebox.h:38
SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND
@ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND
Definition: SDL_messagebox.h:78
color
GLuint color
Definition: SDL_opengl_glext.h:1151
f
GLfloat f
Definition: SDL_opengl_glext.h:1873
SDL_MessageBoxData::flags
Uint32 flags
Definition: SDL_messagebox.h:96
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:95
SDL_MessageBoxColor
RGB value used in a message box color scheme.
Definition: SDL_messagebox.h:69
text
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_MESSAGEBOX_ERROR
@ SDL_MESSAGEBOX_ERROR
Definition: SDL_messagebox.h:39
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:59
SDL_MessageBoxButtonData::buttonid
int buttonid
Definition: SDL_messagebox.h:61
background
SDL_Texture * background
Definition: testgamecontroller.c:67
SDL_MESSAGEBOX_INFORMATION
@ SDL_MESSAGEBOX_INFORMATION
Definition: SDL_messagebox.h:41
SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED
@ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED
Definition: SDL_messagebox.h:79
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:102
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
Definition: SDL_messagebox.h:51
SDL_MessageBoxColorScheme
A set of colors to use for message box dialogs.
Definition: SDL_messagebox.h:87
SDL_MessageBoxColor::g
Uint8 g
Definition: SDL_messagebox.h:70
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
void
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
flags
GLbitfield flags
Definition: SDL_opengl_glext.h:1483
SDL_MessageBoxColor::r
Uint8 r
Definition: SDL_messagebox.h:70
signature
const char * signature
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:101
i
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50