Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » `al_show_native_message_box` buttons argument ignored

This thread is locked; no one can reply to it. rss feed Print
 1   2 
`al_show_native_message_box` buttons argument ignored
Karadoc ~~
Member #2,749
September 2002
avatar

This code:
al_show_native_message_box(0, "Native Message", "Allegro test", "If this works, there will be three buttons.", "A|B|C", ALLEGRO_MESSAGEBOX_WARN);

Produces the attached screenshot.

{"name":"606650","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/a\/aa0fb20013ef645f5ad17249012d1b5c.png","w":319,"h":174,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/a\/aa0fb20013ef645f5ad17249012d1b5c"}606650

Based on my understanding of the manual I'd expect it to show three buttons, labeled 'A', 'B', and 'C'.

I've tried a few different messagebox flags, and the problem is the same.

I'm using allegro v5.1.2[1], and mingw v4.7.0. (It isn't the latest version of Allegro, but I didn't see anything about this in the changelog.)

-----------

Arthur Kalliokoski
Second in Command
February 2005
avatar

I think this was discussed a few weeks ago but I can't find it now. The problem is that nobody has bothered to write a full blown modeless dialog box function for Windows.

“Throughout history, poverty is the normal condition of man. Advances which permit this norm to be exceeded — here and there, now and then — are the work of an extremely small minority, frequently despised, often condemned, and almost always opposed by all right-thinking people. Whenever this tiny minority is kept from creating, or (as sometimes happens) is driven out of a society, the people then slip back into abject poverty. This is known as "bad luck.”

― Robert A. Heinlein

Elias
Member #358
May 2000

And it seems to be not easy at all, compared to OSX/GTK. But yes, if anyone has the code for it and can make a patch it would be added of course.

The current implementation is this btw: https://github.com/elias-pschernig/allegro5/blob/5.1/addons/native_dialog/win_dialog.c#L238

So basically just a call to MessageBoxW.

--
"Either help out or stop whining" - Evert

Karadoc ~~
Member #2,749
September 2002
avatar

Ok. I might look into adding that functionality later today. (Or rather, I will look into it, but I might give up before doing anything useful...)

Is there some kind of todo list for Allegro which tracks unfinished features like this? I didn't notice anything in the manual, or any comments in the code, to indicate that this feature is incomplete on Windows.

[edit]
Damn... this Windows dialog stuff looks like a bit of a nightmare. From what I can tell, we'd need to do something like this, but with automatically generated parameters and style etc, and without using C++ features. I haven't been able to find any neat shortcuts.

Surely there must be some libraries or something which already do this stuff. It's a pretty basic and useful task, but the winapi stuff is just too low level. :(

-----------

SiegeLord
Member #7,827
October 2006
avatar

From what I can tell, we'd need to do something like this [blogs.msdn.com], but with automatically generated parameters and style etc, and without using C++ features.

That looks horribly obtuse, but not too terrible. It's fine to use C++ (I mean the OSX dialogs are implemented in ObjC) but I don't think C++ is necessary here.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Karadoc ~~
Member #2,749
September 2002
avatar

I've been writing some code to implement the custom buttons functionality in Allegro. My code isn't quite finished yet, but I've just tried to compile it as part of Allegro and I've run into some problems...

Apparently Allegro is set up to only allow ISO C90 code, and so I'm getting error messages like this:

win_dialog.c:303:4: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
win_dialog.c:308:4: error: 'for' loop initial declarations are only allowed in C99 mode

Is there some good reason for enforcing C90 rather than C99? In my view, declaring variable where they are needed, and initialing variables when they are declared are both good style, and I'd rather not have to change it.

In case someone is interested, here is the code I've got so far:

#SelectExpand
1int _al_show_native_message_box(ALLEGRO_DISPLAY *display, 2 ALLEGRO_NATIVE_DIALOG *fd) 3{ 4 if (fd->mb_buttons && !(fd->flags & (ALLEGRO_MESSAGEBOX_OK_CANCEL | ALLEGRO_MESSAGEBOX_YES_NO))) 5 { 6 // (all of the code from the current implementation) 7 // ... 8 } 9 10 // else there are custom buttons - so things are more complex 11 12 // (new code) 13 size_t text_size = al_ustr_size_utf16(fd->mb_heading); 14 size_t title_size = al_ustr_size_utf16(fd->title); 15 16 // count the number of custom buttons 17 size_t num_buttons = 1; 18 for (ALLEGRO_USTR* i = fd->mb_buttons; *i != '\0'; i++) 19 { 20 if (*i == '|') 21 num_buttons++; 22 } 23 24 // record the starting position and length of each button name 25 size_t button_caption_length[num_buttons]; // note: this is string length, not memory size 26 ALLEGRO_USTR* button_caption_start[num_buttons]; 27 button_caption_start[0] = fd->mb_buttons; 28 num_buttons = 0; 29 for (ALLEGRO_USTR* i = fd->mb_buttons; true; i++) 30 { 31 if (*i == '|' || *i == '\0') 32 { 33 button_caption_length[num_buttons] = i - button_caption_start[num_buttons]; 34 num_buttons++; 35 if (*i == '\0') 36 break; 37 else 38 button_caption_start[num_buttons] = i+1; 39 } 40 } 41 ASSERT(num_buttons == sizeof(button_caption_length) / sizeof(*button_caption_length)); 42 43 // Note: the sizes of the buttons, and text box, and the dialog itself are currently mostly just arbitrary numbers. 44 // Ideally these sizes should be calculated to best suit the content of the dialog. 45 const short button_width = 50; 46 const short button_height = 14; 47 short dlg_width = 200 > num_buttons*(button_width+20) ? 200 : num_buttons*(button_width+20); 48 short dlg_height = 80; 49 50 // 51 size_t dlg_buffer_size = sizeof(DLGTEMPLATE); 52 dlg_buffer_size += 2*sizeof(WORD); // menu and class (unused) 53 dlg_buffer_size += title_size; // dialog title 54 55 // text (heading and text combined) 56 // align to dword 57 { 58 int excess = dlg_buffer_size % sizeof(DWORD); 59 if (excess) 60 dlg_buffer_size += sizeof(DWORD) - excess; 61 } 62 // item data 63 dlg_buffer_size += sizeof(DLGITEMTEMPLATE); 64 dlg_buffer_size += sizeof(DWORD); // class 65 dlg_buffer_size += text_size; // the text itself 66 dlg_buffer_size += sizeof(WORD); // creation data 67 68 // similarly for each of the buttons. 69 for (size_t i = 0; i < num_buttons; i++) 70 { 71 // align to dword 72 { 73 int excess = dlg_buffer_size % sizeof(DWORD); 74 if (excess) 75 dlg_buffer_size += sizeof(DWORD) - excess; 76 } 77 78 // add the data size of this button 79 dlg_buffer_size += sizeof(DLGITEMTEMPLATE); 80 dlg_buffer_size += sizeof(DWORD); // class 81 dlg_buffer_size += button_caption_length[i]*sizeof(WCHAR); // caption 82 dlg_buffer_size += sizeof(WORD); // creation data 83 } 84 85 DLGTEMPLATE* dlg_template = al_malloc(dlg_buffer_size); 86 if (!dlg_template) { 87 // Not enough memory. :( 88 return 0; 89 } 90 dlg_template->x = 0; 91 dlg_template->y = 0; 92 dlg_template->cx = dlg_width; 93 dlg_template->cy = dlg_height; 94 dlg_template->cdit = 1 + num_buttons; // components = text + number of buttons (todo + icons for warning / error etc) 95 dlg_template->style = DS_CENTER | DS_MODALFRAME | WS_CAPTION | WS_POPUP | WS_BORDER; 96 97 98 size_t buffer_offset = sizeof(DLGTEMPLATE); 99 (WORD*)(dlg_template+buffer_offset) = 0; // menu (none) 100 buffer_offset += sizeof(WORD); 101 (WORD*)(dlg_template+buffer_offset) = 0; // class (none) 102 buffer_offset += sizeof(WORD); 103 al_ustr_encode_utf16(fd->title, (uint16_t*)(dlg_template+buffer_offset), title_size); // title 104 buffer_offset += title_size; 105 106 // dialog header complete. Now for the dialog items. 107 // First, the text. 108 buffer_offset += (sizeof(DWORD) - buffer_offset % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD 109 { 110 DLGITEMTEMPLATE* item = (dlg_template+buffer_offset); 111 item->helpID = 0; 112 item->dwExtendedStyle = 0; 113 item->style = WS_CHILD | WS_VISIBLE; 114 item->x = 7; 115 item->y = 7; 116 item->cx = dlg_width - 14; 117 item->cy = dlg_height-7-button_height-7; 118 item->id = -1; 119 } 120 buffer_offset += sizeof(DLGITEMTEMPLATE); 121 122 (WORD*)(dlg_template+buffer_offset) = 0xFFFF; // pre-defined type to follow 123 (WORD*)(dlg_template+buffer_offset+sizeof(WORD)) = 0x0082; // 'static' 124 buffer_offset += 2*sizeof(WORD); 125 126 al_ustr_encode_utf16(fd->title, (uint16_t*)(dlg_template+buffer_offset), text_size); // text body 127 buffer_offset += text_size; 128 (WORD*)(dlg_template+buffer_offset) = 0; // creation data (whatever that means - we don't need it) 129 buffer_offset += sizeof(WORD); 130 131 // Next, each of the buttons. 132 for (size_t i = 0; i < num_buttons; i++) 133 { 134 buffer_offset += (sizeof(DWORD) - buffer_offset % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD 135 136 DLGITEMTEMPLATE* item = (dlg_template+buffer_offset); 137 item->helpID = 0; 138 item->dwExtendedStyle = 0; 139 item->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | (i == 0 ? WS_GROUP : 0); 140 item->x = dlg_width - (num_buttons - i) * button_width - (num_buttons + 1 - i) * 20; 141 item->y = dlg_height - button_height - 7; 142 item->cx = button_width; 143 item->cy = button_height; 144 item->id = 1000 + i; 145 146 buffer_offset += sizeof(DLGITEMTEMPLATE); 147 148 (WORD*)(dlg_template+buffer_offset) = 0xFFFF; // pre-defined type to follow 149 (WORD*)(dlg_template+buffer_offset+sizeof(WORD)) = 0x0080; // 'button' 150 buffer_offset += 2*sizeof(WORD); 151 152 al_ustr_encode_utf16(button_caption_start, (uint16_t*)(dlg_template+buffer_offset), button_caption_length[i]*sizeof(WCHAR)); // caption 153 buffer_offset += button_caption_length[i]*sizeof(WCHAR); 154 (WORD*)(dlg_template+buffer_offset) = 0; 155 buffer_offset += sizeof(WORD); 156 } 157 int result = DialogBoxIndirect(0, dlg_template, al_get_win_window_handle(display), 0); 158 al_free(dlg_template); 159 return result; 160}

As I said though, it isn't quite finished.

-----------

Matthew Leverton
Supreme Loser
January 1999
avatar

Is there some good reason for enforcing C90 rather than C99?

Visual C++.

Thomas Fjellstrom
Member #476
June 2000
avatar

Visual C++.

he asked if there were any good reasons.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

torhu
Member #2,727
September 2002
avatar

he asked if there were any good reasons.

To annoy the Freetards ;D

Arthur Kalliokoski
Second in Command
February 2005
avatar

Sticking to a 22 year old standard instead of a 13 year old standard is good business practice!

Let me see, what else was new in 1990?

Tim Berners Lee proposes a "hypertext" system, a precursor to the World Wide Wait, er, Web!

Windows 3.0 was brand spanking new!

The Sound Blaster Pro was new!

IBM introduces XGA to extend the VGA standard.

The first 32 bit Amiga is introduced! Yves hasn't been born yet. Or BAF (!)

I'll stop now.

“Throughout history, poverty is the normal condition of man. Advances which permit this norm to be exceeded — here and there, now and then — are the work of an extremely small minority, frequently despised, often condemned, and almost always opposed by all right-thinking people. Whenever this tiny minority is kept from creating, or (as sometimes happens) is driven out of a society, the people then slip back into abject poverty. This is known as "bad luck.”

― Robert A. Heinlein

Elias
Member #358
May 2000

I wouldn't mind dropping Visual C++ support. Not for using Allegro, but for compiling it. We'd of course adjust the DLL creation process so the produced DLLs can be used with VC before doing that.

But it would mean, Allegro itself could be written in proper C11. To compile it you would need to use clang or gcc or the Intel compiler or any other C11 compiler. Most VS users wouldn't compile the library themselves anyway, so nothing would change for them. They get the headers and libraries and can use Allegro.

--
"Either help out or stop whining" - Evert

torhu
Member #2,727
September 2002
avatar

Elias said:

But it would mean, Allegro itself could be written in proper C11. To compile it you would need to use clang or gcc or the Intel compiler or any other C11 compiler. Most VS users wouldn't compile the library themselves anyway, so nothing would change for them.

Being unable to build and debug Allegro in Visual Studio could mean losing most Windows contributors. And you wouldn't have debug information for Allegro that the MSVC debugger can read. Etc.

Trent Gamblin
Member #261
April 2000
avatar

I don't see what the big problem is with declaring variables at the top of functions, including loop iterators. Really not worth losing MSVC support over.

Thomas Fjellstrom
Member #476
June 2000
avatar

torhu said:

Being unable to build and debug Allegro in Visual Studio could mean losing most Windows contributors.

You mean almost none? If not none? We don't really have a windows dev.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Trent Gamblin
Member #261
April 2000
avatar

I maintain the d3d port and work on opengl too :P. The only thing I haven't fixed is the topic of this very thread. I use MinGW myself but I may be using VC++ more for Windows 8 stuff. If I have the money to buy a Windows 8 license I intend to "port" Allegro to it (meaning have it feel like a real Windows 8 app with metro and all that, not just a compatibility app, ready for the Windows store etc.)

torhu
Member #2,727
September 2002
avatar

You mean almost none? If not none? We don't really have a windows dev?

Remember END_OF_MAIN()? That would probably still be there if someone that use MSVC as their main development tool hadn't realized that it's not needed. It would have stayed because "the stupid MSVC crap can't do things in a sane way". And nobody would know that the real reason was just that the Allegro dev team lacked familiarity with the MSVC tool chain.

And this was done by an outside contributor who probably wouldn't bother if he couldn't build and debug Allegro in his beloved MSVC. And I should know, as it was me :P

I don't see what the big problem is with declaring variables at the top of functions, including loop iterators. Really not worth losing MSVC support over.

Me neither. I see how it's annoying having to get used to a slightly different (and inferior) style of programming if you've never had to do it like this before. And even worse when it's evil Microsoft's fault. But it's still pretty much the same C that we all know and love.

Karadoc ~~
Member #2,749
September 2002
avatar

Well, I'd vote in favour of dropping Visual C++ support for building Allegro; but since I'm not a significant contributor to Allegro, I don't think my vote should count for much.

The archaic rules about variable declarations are pretty annoying[1]; but more generally, I just don't think Allegro should allow itself to be held back by just because one compiler is no longer being updated.

There are a bunch of new C features which some Allegro developers may want to use at some point. And although it may not be easy to come up with a priori reasons for using any given feature, I think that the question of whether or not developers can use any given feature should be "is this standard C" rather than "is this supported by Microsoft?" For me, this kind of VC++ stuff is bad for morale. Instead of thinking about making high quality code, I just end up getting frustrated at Microsoft and it makes me want to avoid working in such an environment.

References

  1. I like initialize variables on the same line that they are declared, and to have them go out of scope when their value is obsolete - that way there's no chance of accidentally using uninitialized junk.

-----------

Thomas Fjellstrom
Member #476
June 2000
avatar

torhu said:

And this was done by an outside contributor who probably wouldn't bother if he couldn't build and debug Allegro in his beloved MSVC. And I should know, as it was me

You are welcome to become the full time windows maintainer :)

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Peter Wang
Member #23
April 2000

We're not dropping MSVC support over this trivial issue.

torhu
Member #2,727
September 2002
avatar

You are welcome to become the full time windows maintainer :)

Thanks, but just the thought of making any commitment to an unpaid project makes me feel a bit stressed right now ;)

Karadoc, most of your reasons are valid but pretty insignificant in practice.

Thomas Fjellstrom
Member #476
June 2000
avatar

torhu said:

Thanks, but just the thought of making any commitment to an unpaid project makes me feel a bit stressed right now

That busy eh?

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Karadoc ~~
Member #2,749
September 2002
avatar

Ok. Moving on from that VC++ stuff, and back to implementing this custom buttons functionality...

In the code I posted, one of the big and obvious holes is that the way I've handled the buttons names is broken. I'm going to fix that now; but I'm not familiar with how to handle unicode strings, so I just want to do a quick sanity check here before I write the code. (Somewhat embarrassingly, I didn't even realise that unicode character had variable length until a few minutes ago. I thought UTF-16 just meant all characters were 16 bit numbers or something like that.)

As I understand it, we start with a ALLEGRO_USTR called mb_buttons which contains a list of button names separated by '|' characters. To use this in the Windows API, we'll need to encode the individual button names into the correct places in the dialog template buffer, as UTF-16 strings.

I've just browsed through some of the Allegro API to see how this can be done. I now know how to iterate through the characters in mb_buttons to find the starting position and the size of each button name. But unfortunately I can't see a way to encode one of these substrings directly into the dialog buffer.

What I'd like is to use a function like al_ustr_encode_utf16_substr to encode the button names directly onto the buffer. Unfortunately, that function doesn't exist. So it seems to me that I have to use al_ustr_dup_substr to create separate `ALLEGRO_USTR`s for each button name and then use al_ustr_encode_utf16 to encode each of them into their place in the dialog buffer. Does that sound about right? It sucks a bit, because its a bunch of allocating/copying/freeing just to move data from one API to the other.

-----------

Peter Wang
Member #23
April 2000

al_ref_ustr

Karadoc ~~
Member #2,749
September 2002
avatar

Right. al_ref_ustr is the thing to use; and I've worked around the variable declaration problem by putting a bunch of braces all over the place.

I've now finished what I was trying to implement; but I'm pretty disappointed with how it turned out. It doesn't really look 'native' at all. It looks like quite bad. :(

{"name":"606671","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/a\/ba6f742214ab05bcfd8921e98b5d9082.png","w":466,"h":188,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/a\/ba6f742214ab05bcfd8921e98b5d9082"}606671

I assumed that if I didn't specify a font, Windows would automatically pick whatever the normal font is – but unfortunately it actually just uses a trashy looking system font.

Also, my current implementation doesn't make any attempt to make the box big enough to fit the text. The height of the box is the same regardless of the text.

So.. I guess the main shortcomings of my implementation are as follows:

  • The height of the box is not set dynamically, and so long messages may appear underneath the buttons or be truncated.

  • The size of the buttons is not set dynamically, and so long button names might not fit.

  • The text is missing the white background that standard dialog boxes have.

  • Flags such as ALLEGRO_MESSAGEBOX_WARN and ALLEGRO_MESSAGEBOX_ERROR are ignored. (These flags should have a corresponding sound-effect, and an icon inside the dialog box.)

... So the result is pretty ugly, but at least it has the correct buttons!

I'm not intending to work on this again in the near future, but if someone else wants to have a go at it, or if you think this is good enough to put into Allegro, then here's the code:

#SelectExpand
1// DialogProc callback for customized windows dialog boxes 2INT_PTR CALLBACK _al_winAPI_DlgProc(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam) 3{ 4 (void)lParam; // unused 5 if (wm == WM_INITDIALOG) 6 return TRUE; 7 if (wm == WM_COMMAND) 8 { 9 int control_id = LOWORD(wParam); 10 11 if (control_id > 1000) 12 { 13 EndDialog(hwnd, control_id-1000); 14 return TRUE; 15 } 16 } 17 return FALSE; 18} 19 20int _al_show_native_message_box(ALLEGRO_DISPLAY *display, 21 ALLEGRO_NATIVE_DIALOG *fd) 22{ 23 /* heading + text are combined together */ 24 25 if (al_ustr_size(fd->mb_heading)) 26 al_ustr_append_cstr(fd->mb_heading, "\n\n"); 27 28 al_ustr_append(fd->mb_heading, fd->mb_text); 29 30 // If there are no custom button names, then we can use a standard Windows message box. 31 if (!fd->mb_buttons || (fd->flags & (ALLEGRO_MESSAGEBOX_OK_CANCEL | ALLEGRO_MESSAGEBOX_YES_NO))) 32 { 33 UINT type = 0; 34 int result; 35 36 uint16_t *wide_text, *wide_title; 37 size_t text_len, title_len; 38 39 /* Note: the message box code cannot assume that Allegro is installed. */ 40 41 if (fd->flags & ALLEGRO_MESSAGEBOX_QUESTION) 42 type |= MB_ICONQUESTION; 43 else if (fd->flags & ALLEGRO_MESSAGEBOX_WARN) 44 type |= MB_ICONWARNING; 45 else if (fd->flags & ALLEGRO_MESSAGEBOX_ERROR) 46 type |= MB_ICONERROR; 47 else 48 type |= MB_ICONINFORMATION; 49 50 if (fd->flags & ALLEGRO_MESSAGEBOX_YES_NO) 51 type |= MB_YESNO; 52 else if (fd->flags & ALLEGRO_MESSAGEBOX_OK_CANCEL) 53 type |= MB_OKCANCEL; 54 55 text_len = al_ustr_size_utf16(fd->mb_heading); 56 title_len = al_ustr_size_utf16(fd->title); 57 58 wide_text = al_malloc(text_len + 1); 59 if (!wide_text) 60 return 0; 61 62 wide_title = al_malloc(title_len + 1); 63 if (!wide_title) { 64 al_free(wide_text); 65 return 0; 66 } 67 68 al_ustr_encode_utf16(fd->mb_heading, wide_text, text_len); 69 al_ustr_encode_utf16(fd->title, wide_title, title_len); 70 71 result = MessageBoxW(al_get_win_window_handle(display), 72 (LPCWSTR) wide_text, (LPCWSTR) wide_title, type); 73 74 al_free(wide_text); 75 al_free(wide_title); 76 77 if (result == IDYES || result == IDOK) 78 return 1; 79 else 80 return 0; 81 } 82 // else there are custom buttons - so things are more complex 83 else 84 { 85 size_t text_size = al_ustr_size_utf16(fd->mb_heading); 86 size_t title_size = al_ustr_size_utf16(fd->title); 87 88 size_t num_buttons; 89 int* button_caption_start; 90 int* button_caption_end; 91 92 // count the number of custom buttons 93 { 94 int offset = 0; 95 num_buttons = 0; 96 do 97 { 98 offset = al_ustr_find_chr(fd->mb_buttons, offset, '|'); 99 al_ustr_next(fd->mb_buttons, &offset); 100 num_buttons++; 101 } while (offset > 0); 102 } 103 ASSERT(num_buttons > 0); 104 105 // record the start and end of each button name 106 button_caption_start = al_malloc(sizeof(*button_caption_start)*num_buttons); 107 button_caption_end = al_malloc(sizeof(*button_caption_end)*num_buttons); 108 if (!button_caption_start || !button_caption_end) 109 { 110 // failed to allocate memory for at least one of the arrays 111 al_free(button_caption_start); 112 al_free(button_caption_end); 113 return 0; 114 } 115 116 // record the start and end of each button name 117 { 118 int offset = 0; 119 num_buttons = 0; 120 do 121 { 122 button_caption_start[num_buttons] = offset; 123 offset = al_ustr_find_chr(fd->mb_buttons, offset, '|'); 124 button_caption_end[num_buttons] = offset; 125 al_ustr_next(fd->mb_buttons, &offset); 126 num_buttons++; 127 } while (offset > 0); 128 button_caption_end[num_buttons-1] = al_ustr_size(fd->mb_buttons); 129 } 130 131 { 132 // Note: the sizes of the buttons, and text box, and the dialog itself are currently mostly just arbitrary numbers. 133 // Ideally these sizes should be calculated to best suit the content of the dialog. 134 const short button_width = 50; 135 const short button_height = 14; 136 short dlg_width = 200 > 20+num_buttons*(button_width+20) ? 200 : 20+num_buttons*(button_width+20); 137 short dlg_height = 80; 138 139 // 140 int result; 141 size_t i; // generic iterator 142 DLGTEMPLATE* dlg_template; 143 BYTE* buffer; 144 size_t dlg_buffer_size = sizeof(DLGTEMPLATE); 145 dlg_buffer_size += 2*sizeof(WORD); // menu and class (unused) 146 dlg_buffer_size += title_size; // dialog title 147 148 // text (heading and text combined) 149 dlg_buffer_size += (sizeof(DWORD) - dlg_buffer_size % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD 150 151 // item data 152 dlg_buffer_size += sizeof(DLGITEMTEMPLATE); 153 dlg_buffer_size += sizeof(DWORD); // class 154 dlg_buffer_size += text_size; // the text itself 155 dlg_buffer_size += sizeof(WORD); // creation data 156 157 // similarly for each of the buttons. 158 for (i = 0; i < num_buttons; i++) 159 { 160 dlg_buffer_size += (sizeof(DWORD) - dlg_buffer_size % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD 161 162 // add the data size of this button 163 dlg_buffer_size += sizeof(DLGITEMTEMPLATE); 164 dlg_buffer_size += sizeof(DWORD); // class 165 // caption 166 { 167 ALLEGRO_USTR_INFO str_info; 168 const ALLEGRO_USTR* sub_string = al_ref_ustr(&str_info, fd->mb_buttons, button_caption_start[i], button_caption_end[i]); 169 size_t caption_size = al_ustr_size_utf16(sub_string); 170 dlg_buffer_size += caption_size; 171 } 172 dlg_buffer_size += sizeof(WORD); // creation data 173 } 174 175 dlg_template = al_malloc(dlg_buffer_size); 176 if (!dlg_template) 177 { 178 // Not enough memory. :( 179 al_free(button_caption_start); 180 al_free(button_caption_end); 181 return 0; 182 } 183 dlg_template->x = 0; 184 dlg_template->y = 0; 185 dlg_template->cx = dlg_width; 186 dlg_template->cy = dlg_height; 187 dlg_template->cdit = 1 + num_buttons; // components = text + number of buttons (todo + icons for warning / error etc) 188 dlg_template->style = DS_CENTER | DS_MODALFRAME | WS_CAPTION | WS_POPUP | WS_BORDER; 189 dlg_template->dwExtendedStyle = 0; 190 191 buffer = (BYTE*)dlg_template; 192 buffer += sizeof(DLGTEMPLATE); 193 *(WORD*)buffer = 0; // menu (none) 194 buffer += sizeof(WORD); 195 *(WORD*)buffer = 0; // class (none) 196 buffer += sizeof(WORD); 197 al_ustr_encode_utf16(fd->title, (uint16_t*)buffer, title_size); // title 198 buffer += title_size; 199 200 // dialog header complete. Now for the dialog items. 201 // First, the text. 202 buffer += (sizeof(DWORD) - (buffer - (BYTE*)dlg_template) % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD 203 { 204 DLGITEMTEMPLATE* item = (DLGITEMTEMPLATE*)(buffer); 205 item->dwExtendedStyle = 0; 206 item->style = WS_CHILD | WS_VISIBLE; 207 item->x = 7; 208 item->y = 7; 209 item->cx = dlg_width - 14; 210 item->cy = dlg_height-7-button_height-7; 211 item->id = -1; 212 } 213 buffer += sizeof(DLGITEMTEMPLATE); 214 215 *(WORD*)buffer = 0xFFFF; // pre-defined type to follow 216 *(WORD*)(buffer+sizeof(WORD)) = 0x0082; // 'static' 217 buffer += 2*sizeof(WORD); 218 219 al_ustr_encode_utf16(fd->mb_heading, (uint16_t*)buffer, text_size); // text body 220 buffer += text_size; 221 *(WORD*)buffer = 0; // creation data (whatever that means - we don't need it) 222 buffer += sizeof(WORD); 223 224 // Next, each of the buttons. 225 for (i = 0; i < num_buttons; i++) 226 { 227 buffer += (sizeof(DWORD) - (buffer - (BYTE*)dlg_template) % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD 228 { 229 DLGITEMTEMPLATE* item = (DLGITEMTEMPLATE*)buffer; 230 item->dwExtendedStyle = 0; 231 item->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | (i == 0 ? WS_GROUP : 0); 232 item->x = dlg_width - (num_buttons - i) * (button_width+20); 233 item->y = dlg_height - button_height - 7; 234 item->cx = button_width; 235 item->cy = button_height; 236 item->id = 1000 + i + 1; 237 // Note: item id will determine dialog return value. See _al_winAPI_DlgProc. 238 } 239 buffer += sizeof(DLGITEMTEMPLATE); 240 241 *(WORD*)buffer = 0xFFFF; // pre-defined type to follow 242 *(WORD*)(buffer+sizeof(WORD)) = 0x0080; // 'button' 243 buffer += 2*sizeof(WORD); 244 245 // caption 246 { 247 ALLEGRO_USTR_INFO str_info; 248 const ALLEGRO_USTR* sub_string = al_ref_ustr(&str_info, fd->mb_buttons, button_caption_start[i], button_caption_end[i]); 249 size_t caption_size = al_ustr_size_utf16(sub_string); 250 251 al_ustr_encode_utf16(sub_string, (uint16_t*)buffer, caption_size); 252 buffer += caption_size; 253 } 254 *(WORD*)buffer = 0; 255 buffer += sizeof(WORD); 256 } 257 ASSERT(buffer - (BYTE*)dlg_template == (int)dlg_buffer_size); 258 result = DialogBoxIndirect(0, dlg_template, al_get_win_window_handle(display), _al_winAPI_DlgProc); 259 al_free(dlg_template); 260 al_free(button_caption_start); 261 al_free(button_caption_end); 262 return result == -1 ? 0 : result; 263 } 264 } 265}

Unless someone wants to make a better version, I think that this version should be put into Allegro so that at least then the function will do what the manual says it should do. The previous Allegro functionality for the cases without custom buttons is unchanged, so this ugliness wouldn't be a regression.

[edit]
In case anyone is interested in fixing the font problem, I think this is a good place to start. If no one else wants to look at it, I may tackle it again at some point in the future; but for the time being I'm just going to leave it alone. -- I only wanted to use these dialog boxes for debugging anyway...

-----------

weapon_S
Member #7,859
October 2006
avatar

/* Note: the message box code cannot assume that Allegro is installed. */

That's a PITA, I guess.
Sound effect:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms680356(v=vs.85).aspx
I think maybe it would be most structured to first have a function which determines whether a standard dialog can be used, and then calls either a "normal" message box or "constructs" a dialog.

 1   2 


Go to: