SDL_bmessagebox.cc (10674B)
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: */