src/video/haiku/SDL_bmessagebox.cc
author EXL <exlmotodev@gmail.com>
Tue, 12 Nov 2019 17:24:37 -0500
changeset 13238 007002587d5d
parent 13230 57ed423da32a
child 13422 fd6a12de91c7
permissions -rw-r--r--
haiku: Rename BE_* entities to HAIKU_*
In favor Bugzilla #2349.
Update copyright years to 2019.

Partially fixes Bugzilla #4442.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 != 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],
   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: */