core/vgui/impl/win32/vgui_win32_dialog_impl.cxx
Go to the documentation of this file.
00001 // This is core/vgui/impl/win32/vgui_win32_dialog_impl.cxx
00002 #include "vgui_win32_dialog_impl.h"
00003 
00004 #include <vcl_algorithm.h> // for vcl_find
00005 #include <vcl_cstdio.h> // for vcl_sprintf
00006 #include <vcl_cstddef.h> // for vcl_size_t
00007 #include <vcl_cstring.h> // for vcl_strlen()
00008 #include <vcl_iostream.h>
00009 #include <vgui/internals/vgui_simple_field.h>
00010 #include <vgui/internals/vgui_file_field.h>
00011 #include <vgui/internals/vgui_button_field.h>
00012 #include <vgui/impl/win32/vgui_win32_adaptor.h>
00013 
00014 extern LRESULT CALLBACK globalDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
00015 
00016 vgui_win32_dialog_impl::vgui_win32_dialog_impl(const char* name, HWND hWnd)
00017   : vgui_dialog_impl(name), ok_clicked(false), is_modal(true),
00018   hWndParent(hWnd), hWnd(NULL)
00019 {
00020   inline_tableaus.clear();
00021   fb_ids.clear();
00022   cc_ids.clear();
00023   callback_controls.clear();
00024 }
00025 
00026 struct vgui_win32_dialog_pushbutton;
00027 
00028 vgui_win32_dialog_impl::~vgui_win32_dialog_impl()
00029 {
00030   for ( vcl_vector<inline_tab_data>::iterator
00031         it = inline_tableaus.begin(); it != inline_tableaus.end(); ++it )
00032     delete it->adaptor;
00033 
00034   for (vcl_vector<element>::iterator iter = elements.begin();
00035        iter != elements.end(); ++iter) {
00036     if ( iter->type == button_elem )
00037       delete (vgui_win32_dialog_pushbutton *)iter->widget;
00038   }
00039 }
00040 
00041 // Structure to contain data for a choice field.
00042 struct vgui_win32_dialog_choice
00043 {
00044   vcl_vector<vcl_string> names;
00045   int index;
00046 };
00047 
00048 
00049 // Make a choice widget
00050 void* vgui_win32_dialog_impl::choice_field_widget(const char* /*txt*/,
00051                                                   const vcl_vector<vcl_string>& labels, int& val)
00052 {
00053   vgui_win32_dialog_choice *ch = new vgui_win32_dialog_choice;
00054   ch->names = labels;
00055   ch->index = val;
00056 
00057   return (void*)ch;
00058 }
00059 
00060 // Structure to contain data for a inline tableau.
00061 struct vgui_win32_dialog_inline_tab
00062 {
00063   vgui_tableau_sptr tab;
00064   unsigned height;
00065   unsigned width;
00066 };
00067 
00068 // Make a tableau widget.
00069 void* vgui_win32_dialog_impl::inline_tableau_widget(const vgui_tableau_sptr tab,
00070                                                     unsigned width, unsigned height)
00071 {
00072   vgui_win32_dialog_inline_tab* tab_data = new vgui_win32_dialog_inline_tab;
00073   tab_data->tab = tab;
00074   tab_data->height = height;
00075   tab_data->width =  width;
00076   return (void*)tab_data;
00077 }
00078 
00079 
00080 struct vgui_win32_dialog_pushbutton
00081 {
00082   // TODO: the destructor is not executed in Visual Studio debugger.
00083   ~vgui_win32_dialog_pushbutton()
00084   { if ( hBitmap!=NULL ) DeleteObject(hBitmap); }
00085 
00086   unsigned short ctrl_id;
00087   vcl_string label;
00088   HANDLE     hBitmap;
00089   unsigned   width, height;
00090 };
00091 
00092 void* vgui_win32_dialog_impl::pushbutton_field_widget(const char *label, const void *icon)
00093 {
00094   vgui_win32_dialog_pushbutton *pb = new vgui_win32_dialog_pushbutton;
00095   // Set default values
00096   pb->label = label ? label : ""; // cannot assign a NULL char* to a C++ string
00097   pb->hBitmap = NULL;
00098   pb->width   = 0;
00099   pb->height  = 0;
00100 
00101   if ( icon != NULL ) {
00102     // Load image/icon from disk file and convert to a Windows handle.
00103     pb->hBitmap = LoadImage(NULL, (char*)icon, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
00104     if ( pb->hBitmap != NULL ) {
00105       BITMAP bm;
00106       if ( GetObject(pb->hBitmap, sizeof(bm), &bm) ) {
00107         pb->width = bm.bmWidth;
00108         pb->height = bm.bmHeight;
00109       }
00110     }
00111   }
00112 
00113   return (void *)pb;
00114 }
00115 
00116 // Find out the size of the dialog box needed
00117 void vgui_win32_dialog_impl::FindDialogSize(int &width, int &height,
00118                                             int &max_length, int &fbsr_count,
00119                                             int cxChar, int cyChar, int width_sep, int height_sep,
00120                                             int button_length, int edit_length, int browser_length)
00121 {
00122   // Minimum dialog size
00123   const int cyCaption = GetSystemMetrics(SM_CYCAPTION);
00124   int field_length, field_height;
00125 
00126   height = 3*height_sep+cyCaption; // top, bottom margin plus an OK/Cancel button;
00127   max_length = width_sep;
00128   fbsr_count = 0;
00129 
00130   // Count the length of OK and Cancel button first.
00131   if (ok_button_text_.size() > 0)
00132     max_length += button_length+width_sep;
00133   if (cancel_button_text_.size() > 0)
00134     max_length += button_length+width_sep;
00135 
00136   field_length = field_height = 0;
00137   for (vcl_vector<element>::iterator e_iter = elements.begin();
00138        e_iter != elements.end(); ++e_iter) {
00139     element l = *e_iter;
00140     vgui_dialog_field *field = l.field;
00141 
00142     if ( !use_line_break ) { // when no line break is present, each control
00143       field_length = 0;      // takes up one line
00144       field_height = 0;
00145     }
00146 
00147     if ( l.type == int_elem || l.type == long_elem ||
00148          l.type == float_elem || l.type == double_elem ||
00149          l.type == string_elem || l.type == choice_elem ||
00150          l.type == color_csr || l.type == inline_color_csr ) {
00151       // a label and a edit box.
00152       int w = vcl_strlen(field->label.c_str()) + edit_length;
00153       int h = height_sep;
00154       if ( l.type == color_csr || l.type == inline_color_csr )
00155         h += height_sep; // plus a row for choose color button.
00156 
00157       field_length += w;
00158       if ( field_height < h ) field_height = h;
00159 
00160       if ( !use_line_break ) {
00161         if (max_length<field_length)
00162           max_length = field_length;
00163         height += field_height;
00164       }
00165     }
00166     else if (l.type == bool_elem )
00167     {
00168       // a label
00169       int w = vcl_strlen(field->label.c_str());
00170       int h = height_sep;
00171 
00172       field_length += w;
00173       if ( field_height < h ) field_height = h;
00174 
00175       if ( !use_line_break ) {
00176         if (max_length<field_length)
00177           max_length = field_length;
00178         height += field_height;
00179       }
00180     }
00181     else if ( l.type == text_msg )
00182     {
00183       // one or several lines of text
00184       char *text, *next_text;
00185       int w, w1, h;
00186 
00187       text = (char *)field->label.c_str();
00188       w = 0; h = 0;
00189       while ( text ) {
00190         next_text = vcl_strchr(text, '\n');
00191         if ( next_text )
00192           w1 = next_text-text;
00193         else
00194           w1 = vcl_strlen(text);
00195         text = next_text ? next_text+1 : NULL;
00196 
00197         if ( w < w1 ) w = w1;
00198         h += height_sep;
00199       }
00200 
00201       field_length += w;
00202       if ( field_height < h ) field_height = h;
00203 
00204       if ( !use_line_break ) {
00205         if (max_length<field_length)
00206           max_length = field_length;
00207         height += field_height;
00208       }
00209     }
00210     else if (l.type == inline_tabl)
00211     {
00212       // a tableau
00213       vgui_win32_dialog_inline_tab* tab_data = (vgui_win32_dialog_inline_tab*)l.widget;
00214       int w = tab_data->width/cxChar + 2*width_sep; // plus width_sep on each side
00215       int h = tab_data->height;
00216 
00217       field_length += w;
00218       if ( field_height < h ) field_height = h;
00219 
00220       if ( !use_line_break ) {
00221         if (max_length < field_length)
00222           max_length = field_length;
00223         height += field_height;
00224       }
00225     }
00226     else if (l.type == file_bsr || l.type == inline_file_bsr || l.type == dir_bsr)
00227     {
00228       // a label, an edit box, and a "Browse..." button
00229       int w = vcl_strlen(field->label.c_str()) + browser_length;
00230       int h = 2*height_sep;
00231 
00232       field_length += w;
00233       if ( field_height < h ) field_height = h;
00234 
00235       if ( !use_line_break ) {
00236         if (max_length<field_length)
00237           max_length = field_length;
00238         height += field_height;
00239       }
00240       fbsr_count++;
00241     }
00242     else if (l.type == button_elem )
00243     {
00244       // a button with an image and/or a label
00245       vgui_win32_dialog_pushbutton *pb = (vgui_win32_dialog_pushbutton*)l.widget;
00246       // the button width is the maximum of label length, image width
00247       // and default button width
00248       int w = (pb->width+cxChar-1)/cxChar;
00249       int slen = vcl_strlen(field->label.c_str());
00250       if ( w < slen )
00251         w = slen;
00252 
00253       int h = pb->height;
00254       h += height_sep * (field->label.empty() ? 0 : 1);
00255 
00256       field_length += w;
00257       if ( field_height < h ) field_height = h;
00258 
00259       if ( !use_line_break ) {
00260         if (max_length<field_length)
00261           max_length = field_length;
00262         height += field_height;
00263       }
00264     }
00265     else if ( l.type == line_br )
00266     {
00267       if (max_length<field_length)
00268         max_length = field_length;
00269       height += field_height;
00270 
00271       field_length = 0;
00272       field_height = 0;
00273     }
00274     else // Remaining types
00275     {
00276       // add a row.
00277       height += height_sep;
00278     }
00279   }
00280 
00281   // Width of the dialog box is approx 8 times the number of characters:
00282   width = (max_length+width_sep) * cxChar;
00283   // restore the max actual field length
00284   max_length -= fbsr_count ? browser_length : edit_length;
00285 }
00286 
00287 bool vgui_win32_dialog_impl::ask()
00288 {
00289   // Retrieve the average width and height of system font in pixels,
00290   // and convert to dialog box base unit.
00291   // It's observed that 2 pixel equals to 1 dialog box base unit.
00292   const int cxChar = LOWORD(GetDialogBaseUnits());
00293   const int cyChar = HIWORD(GetDialogBaseUnits());
00294 
00295   const int height_sep = 6*cyChar/4; // vertical distance between two adjacent controls.
00296   const int width_sep = 2; // horizontal distance between two adjacent controls.
00297   const int edit_length = 20;
00298   const int browser_length = 40;
00299   const int button_length = 12;
00300   const int button_height = 6*cyChar/4;
00301   const int edit_height = 5*cyChar/4;
00302 
00303   int width, height, max_length, fbsr_count;
00304   short x, y, cx, cy;
00305   RECT rect;
00306 
00307   // Find if line_break is used
00308   for (vcl_vector<element>::iterator e_iter = elements.begin();
00309        e_iter != elements.end(); ++e_iter) {
00310     if ( e_iter->type == line_br ) {
00311       use_line_break = true;
00312       break;
00313     }
00314   }
00315 
00316   // Find out the size of the dialog box needed
00317   FindDialogSize(width, height, max_length, fbsr_count, cxChar, cyChar,
00318                  width_sep, height_sep, button_length, edit_length, browser_length);
00319 
00320 #ifdef _WIN64
00321   HINSTANCE hInstance = (HINSTANCE)GetWindowLong(hWndParent, GWLP_HINSTANCE);
00322 #else
00323   HINSTANCE hInstance = (HINSTANCE)GetWindowLong(hWndParent, GWL_HINSTANCE);
00324 #endif //_WIN64
00325   // Get the position and size of the parent window to position the
00326   // popup dialog box.
00327   GetWindowRect(hWndParent, &rect);
00328   hWnd = CreateWindow(TEXT("vgui_win32_dialog"), name.c_str(),
00329                       WS_CAPTION | WS_POPUP | WS_SYSMENU | DS_MODALFRAME,
00330                       rect.left+10, rect.top+10, width, height,
00331                       hWndParent, NULL, hInstance, NULL);
00332   if ( hWnd == NULL )
00333     MessageBox(NULL, TEXT("Fail to create dialog box window!"),
00334                NULL, MB_ICONERROR);
00335 
00336   // Attach basic buttons "OK" and "Cancel" at the bottom of the dialog
00337   cx = button_length*cxChar;
00338   cy = button_height;
00339   x = width - 2*(cx + width_sep*cxChar);
00340   y = height - 3*height_sep;
00341 
00342   // Ok button
00343   if (ok_button_text_.size() > 0) {
00344     CreateWindow(TEXT("BUTTON"), (char*)ok_button_text_.c_str(),
00345                  WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, x, y, cx, cy,
00346                  hWnd, (HMENU)IDOK, hInstance, NULL);
00347   }
00348 
00349   // Cancel button
00350   if (cancel_button_text_.size() > 0) {
00351     x += cx+width_sep*cxChar;
00352     CreateWindow(TEXT("BUTTON"), (char*)cancel_button_text_.c_str(),
00353                  WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, x, y, cx, cy,
00354                  hWnd, (HMENU)IDCANCEL, hInstance, NULL);
00355   }
00356 
00357   // Add elements from top to bottom
00358   x = cxChar; y = cyChar>>1;
00359   unsigned short ctrl_count = 0;
00360   HWND hCtrlWnd;
00361   short dy = 0;
00362   for (vcl_vector<element>::iterator e_iter = elements.begin();
00363        e_iter != elements.end(); ++e_iter) {
00364     element l = *e_iter;
00365     vgui_dialog_field *field = l.field;
00366 
00367     if (l.type == int_elem ||
00368         l.type == long_elem ||
00369         l.type == float_elem ||
00370         l.type == double_elem ||
00371         l.type == string_elem)
00372     {
00373       // Set static text first
00374       cx = field->label.size()*cxChar;
00375       cy = edit_height;
00376       CreateWindow(TEXT("STATIC"), (char*)field->label.c_str(),
00377                    WS_CHILD | WS_VISIBLE | SS_LEFT, x, y, cx, cy,
00378                    hWnd, (HMENU)IDC_STATIC, hInstance, NULL);
00379 
00380       // Now set the line editor next
00381       int savex = x;
00382       x = (max_length)*cxChar;
00383       cx = edit_length*cxChar;
00384       WORD wDlgCtrlId = DLG_ID_START + ctrl_count++;
00385       CreateWindow(TEXT("EDIT"), (char*)field->current_value().c_str(),
00386                    WS_CHILD | WS_VISIBLE | ES_LEFT|ES_AUTOHSCROLL, x, y, cx, cy,
00387                    hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00388 
00389       if ( use_line_break ) {
00390         x += (field->label.size()+edit_length)*cxChar+2*width_sep; // keep the pentip on the line
00391         if ( dy < height_sep ) dy = height_sep;
00392       }
00393       else {
00394         x = savex;      // move the pentip to the beginning of the next line
00395         y += height_sep;
00396       }
00397     }
00398 
00399     else if (l.type == bool_elem)
00400     {
00401       cx = (field->label.size()+3)*cxChar;
00402       cy = button_height;
00403       WORD wDlgCtrlId = DLG_ID_START + ctrl_count++;
00404       CreateWindow(TEXT("BUTTON"), (char*)field->label.c_str(),
00405                    WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, x, y, cx, cy,
00406                    hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00407       // set check status
00408       vgui_bool_field *bfield = static_cast<vgui_bool_field*>(field);
00409       CheckDlgButton(hWnd, wDlgCtrlId, bfield->var ? BST_CHECKED : BST_UNCHECKED);
00410 
00411 
00412       if ( use_line_break ) {
00413         x += cx+width_sep; // keep the pentip on the line
00414         if ( dy < height_sep ) dy = height_sep;
00415       }
00416       else {
00417         x = cxChar;      // move the pentip to the beginning of the next line
00418         y += height_sep;
00419       }
00420     }
00421 
00422     else if (l.type == choice_elem)
00423     {
00424       //vgui_int_field *field = static_cast<vgui_int_field*>(l.field);
00425       cx = field->label.size()*cxChar;
00426       cy = edit_height;
00427       // Set static text first
00428       CreateWindow(TEXT("STATIC"), (char*)field->label.c_str(),
00429                    WS_CHILD | WS_VISIBLE | SS_LEFT, x, y, cx, cy,
00430                    hWnd, (HMENU)IDC_STATIC, hInstance, NULL);
00431 
00432       // Then the combobox
00433       vgui_win32_dialog_choice *ch = (vgui_win32_dialog_choice*)l.widget;
00434 
00435       int savex = x;
00436       x = (max_length)*cxChar;
00437       cx = edit_length*cxChar;
00438       cy = ch->names.size() * cyChar;
00439       WORD wDlgCtrlId = DLG_ID_START + ctrl_count++;
00440       hCtrlWnd = CreateWindow(TEXT("COMBOBOX"), "",
00441                               WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST,
00442                               x, y, cx, cy, hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00443 
00444       // Add optional strings to the list
00445       for (vcl_vector<vcl_string>::iterator s_iter =  ch->names.begin();
00446            s_iter != ch->names.end(); ++s_iter)
00447         SendMessage(hCtrlWnd, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)s_iter->c_str());
00448 
00449       // And set the selection.
00450       SendMessage(hCtrlWnd, CB_SETCURSEL, (WPARAM)ch->index, 0);
00451 
00452       if ( use_line_break ) {
00453         x += (field->label.size()+edit_length)*cxChar+2*width_sep; // keep the pentip on the line
00454         if ( dy < height_sep ) dy = height_sep;
00455       }
00456       else {
00457         x = savex;      // move the pentip to the beginning of the next line
00458         y += height_sep;
00459       }
00460     }
00461 
00462     else if (l.type == text_msg)
00463     {
00464       // one or several lines of text
00465       char *text, *next_text;
00466       int field_length;
00467 
00468       text = (char *)field->label.c_str();
00469       cx = 0; cy = 0;
00470       while ( text ) {
00471         next_text = vcl_strchr(text, '\n');
00472         if ( next_text )
00473           field_length = next_text-text;
00474         else
00475           field_length = vcl_strlen(text);
00476         if (cx<field_length)
00477           cx = field_length;
00478         cy++;
00479         text = next_text ? next_text+1 : NULL;
00480       }
00481       cx *= cxChar;
00482       cy *= edit_height;
00483       CreateWindow(TEXT("STATIC"), (char*)field->label.c_str(),
00484                    WS_CHILD | WS_VISIBLE | SS_LEFT, x, y, cx, cy,
00485                    hWnd, (HMENU)IDC_STATIC, hInstance, NULL);
00486 
00487       if ( use_line_break ) {
00488         x += cx+width_sep; // keep the pentip on the line
00489         if ( dy < cy ) dy = cy;
00490       }
00491       else {
00492         x = cxChar;      // move the pentip to the beginning of the next line
00493         y += cy;
00494       }
00495     }
00496 
00497     else if (l.type == file_bsr || l.type == inline_file_bsr ||
00498              l.type == color_csr || l.type == inline_color_csr )
00499     {
00500       cx = field->label.size()*cxChar;
00501       cy = edit_height;
00502       // Set static text first
00503       CreateWindow(TEXT("STATIC"), (char*)field->label.c_str(),
00504                    WS_CHILD | WS_VISIBLE | SS_LEFT, x, y, cx, cy,
00505                    hWnd, (HMENU)IDC_STATIC, hInstance, NULL);
00506 
00507       // Next set the line editor
00508       int savex = x;
00509       x = (max_length)*cxChar;
00510       if ( l.type == file_bsr || l.type == inline_file_bsr )
00511         cx = browser_length*cxChar;
00512       else
00513         cx = edit_length*cxChar;
00514       WORD wDlgCtrlId = DLG_ID_START + ctrl_count++;
00515       CreateWindow(TEXT("EDIT"), (char*)field->current_value().c_str(),
00516                    WS_CHILD | WS_VISIBLE | ES_LEFT|ES_AUTOHSCROLL, x, y, cx, cy,
00517                    hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00518 
00519       // Third, add a browse button
00520       int savey = y;
00521       y += height_sep;
00522       cx = button_length*cxChar;
00523       cy = button_height;
00524       wDlgCtrlId = DLG_ID_START + ctrl_count++;
00525       if ( l.type == file_bsr || l.type == inline_file_bsr ) {
00526         CreateWindow(TEXT("BUTTON"), "Browse...",
00527                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, x, y, cx, cy,
00528                      hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00529         fb_ids.push_back(wDlgCtrlId);
00530       }
00531       else {
00532         CreateWindow(TEXT("BUTTON"), "Colour...",
00533                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, x, y, cx, cy,
00534                      hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00535         cc_ids.push_back(wDlgCtrlId);
00536       }
00537 
00538       if ( use_line_break ) {
00539         x += (field->label.size()+edit_length)*cxChar+2*width_sep; // keep the pentip on the line
00540         y = savey;
00541         if ( dy < 2*height_sep ) dy = 2*height_sep;
00542       }
00543       else {
00544         x = savex;      // move the pentip to the beginning of the next line
00545         y += height_sep;
00546       }
00547     }
00548 
00549     else if (l.type == inline_tabl)
00550     {
00551       vgui_win32_dialog_inline_tab* tab_data = (vgui_win32_dialog_inline_tab *)l.widget;
00552 
00553       //int savecy = cy;
00554       cx = short(tab_data->width); // in dialog box base unit
00555       cy = short(tab_data->height);
00556       WORD wDlgCtrlId = DLG_ID_START + ctrl_count++;
00557       hCtrlWnd = CreateWindow(TEXT("vgui_win32_inline_tab"), "",
00558                               WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, x, y, cx, cy,
00559                               hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00560 
00561       // Create a vgui_win32_adaptor as the child of the control.
00562       vgui_win32_adaptor* adptr = new vgui_win32_adaptor(hCtrlWnd);
00563       adptr->set_tableau(tab_data->tab);
00564       adptr->set_width(tab_data->width);
00565       adptr->set_height(tab_data->height);
00566 
00567       // Save the info of this tab.
00568       inline_tab_data the_tab;
00569       the_tab.childId = wDlgCtrlId;
00570       the_tab.hWnd    = hCtrlWnd;
00571       the_tab.adaptor = adptr;
00572       inline_tableaus.push_back(the_tab);
00573 
00574       if ( use_line_break ) {
00575         x += cx+width_sep; // keep the pentip on the line
00576         if ( dy < cy ) dy = cy;
00577       }
00578       else {
00579         x = cxChar;      // move the pentip to the beginning of the next line
00580         y += cy;
00581       }
00582     }
00583 
00584     else if (l.type == button_elem) {
00585       // a button with an image and/or a label
00586       vgui_win32_dialog_pushbutton *pb = (vgui_win32_dialog_pushbutton*)l.widget;
00587       // the button width is the maximum of label length, image width,
00588       // and default button width
00589       cx = field->label.size()*cxChar;
00590       if ( cx < (short)(pb->width) ) cx = pb->width;
00591       //if ( cx < button_length ) cx = button_length;
00592       cy = pb->height;
00593       cy += field->label.empty() ? 0 : button_height;
00594       WORD wDlgCtrlId = DLG_ID_START + ctrl_count++;
00595       hCtrlWnd = CreateWindow(TEXT("BUTTON"), (char*)field->label.c_str(),
00596                               WS_CHILD | WS_VISIBLE | (pb->hBitmap ? BS_OWNERDRAW : BS_PUSHBUTTON),
00597                               x, y, cx, cy, hWnd, (HMENU)wDlgCtrlId, hInstance, NULL);
00598       pb->ctrl_id = wDlgCtrlId;
00599 
00600       callback_control_data ccd;
00601       vgui_button_field *l = (vgui_button_field *)field;
00602       ccd.child_id = wDlgCtrlId;
00603       ccd.cmnd     = l->cmnd;
00604       callback_controls.push_back(ccd);
00605 
00606       if ( use_line_break ) {
00607         x += cx+width_sep; // keep the pentip on the line
00608         if ( dy < cy ) dy = cy;
00609       }
00610       else {
00611         x = cxChar;      // move the pentip to the beginning of the next line
00612         y += cy;
00613       }
00614     }
00615 
00616     else if (l.type == line_br) { // move the pentip to
00617        x = cxChar;                // the beginning
00618        y += dy;                   // of the next line
00619        dy = 0;
00620     }
00621   }
00622 
00623   // Show the dialog box.
00624   ShowWindow(hWnd, SW_SHOW);
00625   UpdateWindow(hWnd);
00626 
00627   // Show inline tableaus
00628   for ( vcl_size_t i = 0; i < inline_tableaus.size(); i++ )
00629     inline_tableaus[i].adaptor->post_redraw();
00630 
00631   // Enter the message loop.
00632   run();
00633 
00634   return ok_clicked;
00635 }
00636 
00637 void vgui_win32_dialog_impl::run()
00638 {
00639   MSG msg;
00640 
00641   while ( GetMessage(&msg, NULL, 0, 0) ) {
00642     TranslateMessage(&msg);
00643     DispatchMessage(&msg);
00644   }
00645 }
00646 
00647 // Handle messages that are related to vgui_dialog_impl.
00648 LRESULT vgui_win32_dialog_impl::DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
00649 {
00650   switch (message) {
00651     case WM_CREATE:
00652       // Disable keyboard/mouse message of parent window for modal dialog
00653       if ( is_modal )
00654         EnableWindow(GetParent(hDlg), FALSE);
00655       return 1;
00656 
00657     case WM_DESTROY:
00658       if ( is_modal )
00659         EnableWindow(GetParent(hDlg), TRUE);
00660       SetActiveWindow(GetParent(hDlg));
00661       PostQuitMessage(0);
00662       return 1;
00663 
00664     case WM_DRAWITEM: {
00665       LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
00666       // Obtain info of control item
00667       HDC hDC = lpDIS->hDC;
00668       RECT rectItem = lpDIS->rcItem;
00669       UINT ctrlID = lpDIS->CtlID;
00670       BOOL isDisabled = lpDIS->itemState & ODS_DISABLED; // get button state
00671       BOOL isFocused  = lpDIS->itemState & ODS_FOCUS;
00672       BOOL isPressed  =    lpDIS->itemState & ODS_SELECTED;
00673       //BOOL bDrawFocusRect = !(lpDIS->itemState & ODS_NOFOCUSRECT);
00674 
00675       // Get button's info (label, bitmap)
00676       vcl_vector<element>::iterator e_iter;
00677       vgui_win32_dialog_pushbutton *pb;
00678       for ( e_iter = elements.begin(); e_iter != elements.end(); ++e_iter) {
00679          if ( e_iter->type == button_elem ) {
00680            pb = (vgui_win32_dialog_pushbutton*)e_iter->widget;
00681            if ( ctrlID == pb->ctrl_id )
00682              break;
00683          }
00684       }
00685 
00686       if ( e_iter == elements.end() )
00687         break; // exit if the owner-draw button is not found
00688 
00689       SetBkMode(hDC, TRANSPARENT); // TODO: experiment
00690 
00691       // Paint button background
00692 #if 0
00693       if (Themed) {
00694         DWORD state = (bIsPressed)?PBS_PRESSED:PBS_NORMAL;
00695 
00696         if (state == PBS_NORMAL)    {
00697           if (bIsFocused)
00698             state = PBS_DEFAULTED;
00699           if (bMouseOverButton)
00700             state = PBS_HOT;
00701         }
00702         zDrawThemeBackground(hTheme, hDC, BP_PUSHBUTTON, state, &lpDIS->rcItem, NULL);
00703       }
00704       else {
00705 #endif
00706         if ( isFocused ) {
00707           // Simulate button pressed down
00708           HBRUSH hbr = CreateSolidBrush(RGB(0,0,0));
00709           FrameRect(hDC, &rectItem, hbr);
00710           InflateRect(&rectItem, -1, -1);
00711           DeleteObject(hbr);
00712         }
00713 
00714         //COLORREF crColor = GetSysColor(COLOR_BTNFACE);
00715         //HBRUSH hbr = CreateSolidBrush(crColor);
00716         //FillRect(hDC, &rectItem, hbr);
00717         //DeleteObject(hbr);
00718 
00719         // Draw pressed button
00720         if ( isPressed ) {
00721           // Simulate button released
00722           HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
00723           FrameRect(hDC, &rectItem, hbr);
00724           DeleteObject(hbr);
00725         }
00726         else {
00727           DrawFrameControl(hDC, &rectItem, DFC_BUTTON, DFCS_BUTTONPUSH);
00728         }
00729 #if 0
00730       }
00731 #endif
00732 
00733       // Draw image on the button (if any)
00734       if ( pb->hBitmap != NULL ) {
00735         RECT rcImage;
00736 
00737         // Center image horizontally
00738         CopyRect(&rcImage, &rectItem);
00739         UINT image_width = rcImage.right - rcImage.left;
00740         rcImage.left = (image_width - pb->width)/2;
00741 
00742         DrawState(hDC, NULL, NULL, (LPARAM)(HBITMAP)pb->hBitmap, 0,
00743                   rcImage.left, rcImage.top,
00744                   rcImage.right - rcImage.left,
00745                   rcImage.bottom - rcImage.top,
00746                   (isDisabled ? DSS_DISABLED : DSS_NORMAL) | DST_BITMAP);
00747         //DrawImageOnButton(hDC, &rectItem, (HBITMAP)pb->hBitmap, pb->width, pb->height, isDisabled);
00748       }
00749 
00750       // Write the button label (if any)
00751       if ( !pb->label.empty() ) {
00752         RECT rcLabel;
00753 
00754         CopyRect(&rcLabel, &lpDIS->rcItem);
00755 
00756         // If button is pressed then "press" the label also
00757         //if ( isPressed /*&& !Themed*/) OffsetRect(&rcLabel, 1, 1);
00758 
00759         // Center text
00760         //RECT centerRect = rcLabel;
00761         // Determine the width and height of rcLabel but does not draw the text.
00762         //DrawText(hDC, pb->label.c_str(), -1, &rcLabel, DT_WORDBREAK | DT_CENTER | DT_CALCRECT);
00763         rcLabel.top += pb->height;
00764         //LONG captionRectWidth = rcLabel.right - rcLabel.left;
00765         //LONG captionRectHeight = rcLabel.bottom - rcLabel.top;
00766         //LONG centerRectWidth = centerRect.right - centerRect.left;
00767         //LONG centerRectHeight = centerRect.bottom - centerRect.top;
00768         //OffsetRect(&rcLabel, (centerRectWidth - captionRectWidth)/2, (centerRectHeight - captionRectHeight)/2);
00769 #if 0
00770         if (Themed) {
00771           // convert title to UNICODE obviously you don't need to do this if you are a UNICODE app.
00772           int nTextLen = vcl_strlen(sTitle);
00773           int mlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)sTitle, nTextLen + 1, NULL, 0);
00774           WCHAR* output = new WCHAR[mlen];
00775           if (output)
00776           {
00777             MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)sTitle, nTextLen + 1, output, mlen);
00778             zDrawThemeText(hTheme, hDC, BP_PUSHBUTTON, PBS_NORMAL,
00779                            output, wcslen(output),
00780                            DT_CENTER | DT_VCENTER | DT_SINGLELINE,
00781                            0, &rcLabel);
00782             delete output;
00783           }
00784         }
00785         else {
00786 #endif // 0
00787           //SetBkMode(hDC, TRANSPARENT);
00788           if ( isDisabled ) {
00789             OffsetRect(&rcLabel, 1, 1);
00790             SetTextColor(hDC, ::GetSysColor(COLOR_3DHILIGHT));
00791             DrawText(hDC, pb->label.c_str(), -1, &rcLabel, DT_WORDBREAK | DT_CENTER);
00792             OffsetRect(&rcLabel, -1, -1);
00793             SetTextColor(hDC, ::GetSysColor(COLOR_3DSHADOW));
00794             DrawText(hDC, pb->label.c_str(), -1, &rcLabel, DT_WORDBREAK | DT_CENTER);
00795           }
00796           else {
00797             SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT));
00798             SetBkColor(hDC, GetSysColor(COLOR_BTNFACE));
00799             DrawText(hDC, pb->label.c_str(), -1, &rcLabel, DT_WORDBREAK | DT_CENTER);
00800           }
00801 
00802 #if 0
00803         }
00804 #endif
00805       }
00806 
00807       // Draw the focus rect
00808       //if ( bIsFocused && bDrawFocusRect) {
00809       //  RECT focusRect = itemRect;
00810       //  InflateRect(&focusRect, -3, -3);
00811       //  DrawFocusRect(hDC, &focusRect);
00812       //}
00813 
00814       break;
00815     }
00816 
00817     case WM_COMMAND: {
00818       WORD wCtrlId = LOWORD(wParam);
00819       if ( wCtrlId == IDOK ) {
00820         OnOK();
00821         DestroyWindow(hDlg);
00822         return 0;
00823       }
00824       if ( wCtrlId == IDCANCEL ) {
00825         OnCancel();
00826         DestroyWindow(hDlg);
00827         return 0;
00828       }
00829       if ( IsFileBrowserButton(wCtrlId) ) {
00830         OnBrowse(hDlg, wCtrlId);
00831         return 0;
00832       }
00833       if ( IsColorChooserButton(wCtrlId) ) {
00834         char strColor[16];
00835         GetWindowText(GetDlgItem(hDlg, wCtrlId-1), strColor, 16);
00836         OnColor(hDlg, wCtrlId, strColor);
00837         return 0;
00838       }
00839       if ( IsCallbackControl(wCtrlId) ) {
00840         dialog_dispatcher(wCtrlId);
00841         return 0;
00842       }
00843       break;
00844     }
00845   }
00846 
00847   return 0;
00848 }
00849 
00850 // Update values of all elements when user click OK button.
00851 void vgui_win32_dialog_impl::OnOK()
00852 {
00853   HWND hWndCtrl;
00854   char buf[MAX_PATH];
00855   int control_count, ctrl_id;
00856 
00857   ok_clicked = true;
00858 
00859   // Update values of all elements.
00860   control_count = 0;
00861   for (vcl_vector<element>::iterator e_iter = elements.begin();
00862        e_iter != elements.end(); ++e_iter) {
00863     element l = *e_iter;
00864     vgui_dialog_field *field = l.field;
00865     ctrl_id = DLG_ID_START+control_count;
00866     hWndCtrl = GetDlgItem(hWnd, ctrl_id);
00867 
00868     if ( l.type == int_elem ||
00869          l.type == long_elem ||
00870          l.type == float_elem ||
00871          l.type == double_elem ||
00872          l.type == string_elem ) { // a line/multiline edit
00873       GetWindowText(hWndCtrl, buf, MAX_PATH);
00874       field->update_value(buf);
00875       control_count++;
00876     }
00877 
00878     else if ( l.type == bool_elem ) { // a check box
00879       vgui_bool_field *bfield = static_cast<vgui_bool_field*>(field);
00880       bfield->var = IsDlgButtonChecked(hWnd, ctrl_id) ? true : false;
00881       control_count++;
00882     }
00883 
00884     else if ( l.type == choice_elem ) { // a combo box
00885       vgui_int_field *ifield = (vgui_int_field *)field;
00886       ifield->var = SendMessage(hWndCtrl, CB_GETCURSEL, 0, 0);
00887       control_count++;
00888     }
00889 
00890     else if ( l.type == file_bsr ||
00891               l.type == color_csr ||
00892               l.type == inline_file_bsr ||
00893               l.type == inline_color_csr ) { // a line edit and a button
00894       GetWindowText(hWndCtrl, buf, MAX_PATH);
00895       field->update_value(buf);
00896       control_count += 2;
00897     }
00898 
00899     else if ( l.type == inline_tabl ) { // a tab
00900       control_count++;
00901     }
00902   }
00903 }
00904 
00905 void vgui_win32_dialog_impl::OnCancel()
00906 {
00907   ok_clicked = false;
00908 }
00909 
00910 // Open a file open dialog box.
00911 BOOL vgui_win32_dialog_impl::OnBrowse(HWND hDlg, WORD wCtrlId)
00912 {
00913   static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH];
00914   static TCHAR szFilter[] = TEXT("All Files (*.*)\0*.*\0")
00915                             TEXT("All Files (*.*)\0*.*\0\0");
00916   static OPENFILENAME ofn;
00917 
00918   ofn.lStructSize       = sizeof(OPENFILENAME);
00919   ofn.hwndOwner         = hDlg;
00920   ofn.hInstance         = NULL;
00921   ofn.lpstrFilter       = szFilter;
00922   ofn.lpstrCustomFilter = NULL;
00923   ofn.nMaxCustFilter    = 0;
00924   ofn.nFilterIndex      = 0;
00925   ofn.lpstrFile         = szFileName;
00926   ofn.nMaxFile          = MAX_PATH;
00927   ofn.lpstrFileTitle    = szTitleName;
00928   ofn.nMaxFileTitle     = MAX_PATH;
00929   ofn.lpstrInitialDir   = NULL;
00930   ofn.lpstrTitle        = NULL;
00931   ofn.Flags             = OFN_CREATEPROMPT;
00932   ofn.nFileOffset       = 0;
00933   ofn.nFileExtension    = 0;
00934   ofn.lpstrDefExt       = NULL;
00935   ofn.lCustData         = 0;
00936   ofn.lpfnHook          = NULL;
00937   ofn.lpTemplateName    = NULL;
00938 
00939   if ( GetOpenFileName(&ofn) )
00940     SetWindowText(GetDlgItem(hDlg, wCtrlId-1), szFileName);
00941 
00942   return TRUE;
00943 }
00944 
00945 BOOL vgui_win32_dialog_impl::OnColor(HWND hDlg, WORD wCtrlId, LPTSTR lpColor)
00946 {
00947   char buffer[16];
00948   vcl_string strColor;
00949 
00950   static CHOOSECOLOR cc;
00951   static COLORREF    crCustColors[16];
00952 
00953   cc.lStructSize    = sizeof(CHOOSECOLOR);
00954   cc.hwndOwner      = hDlg;
00955   cc.hInstance      = NULL;
00956   cc.rgbResult      = ColorStringToRGB(lpColor);
00957   cc.lpCustColors   = crCustColors;
00958   cc.Flags          = CC_RGBINIT | CC_FULLOPEN;
00959   cc.lCustData      = 0;
00960   cc.lpfnHook       = NULL;
00961   cc.lpTemplateName = NULL;
00962 
00963   if ( ChooseColor(&cc) ) {
00964     vcl_sprintf(buffer, "%3d", GetRValue(cc.rgbResult));
00965     strColor += buffer;
00966     vcl_sprintf(buffer, " %3d", GetGValue(cc.rgbResult));
00967     strColor += buffer;
00968     vcl_sprintf(buffer, " %3d", GetBValue(cc.rgbResult));
00969     strColor += buffer;
00970     SetWindowText(GetDlgItem(hDlg, wCtrlId-1), strColor.c_str());
00971   }
00972 
00973   return TRUE;
00974 }
00975 
00976 // Convert a color that is defined in string format "lpColor" to equivalent
00977 // COLORREF format
00978 COLORREF vgui_win32_dialog_impl::ColorStringToRGB(LPTSTR lpColor)
00979 {
00980   int r, g, b;
00981 
00982   if ( vcl_strcmp(lpColor, "red") == 0 )
00983     return RGB(255, 0, 0);
00984   if ( vcl_strcmp(lpColor, "green") == 0 )
00985     return RGB(0, 255, 0);
00986   if ( vcl_strcmp(lpColor, "blue") == 0 )
00987     return RGB(0, 0, 255);
00988   if ( vcl_strcmp(lpColor, "yellow") == 0 )
00989     return RGB(255, 255, 0);
00990   if ( vcl_strcmp(lpColor, "pink") == 0 )
00991     return RGB(255, 0, 255);
00992   if ( vcl_strcmp(lpColor, "cyan") == 0 )
00993     return RGB(0, 255, 255);
00994   if ( vcl_strcmp(lpColor, "black") == 0 )
00995     return RGB(0, 0, 0);
00996   if ( vcl_strcmp(lpColor, "white") == 0 )
00997     return RGB(255, 255, 255);
00998 
00999   int ret = vcl_sscanf(lpColor, TEXT("%d%d%d"), &r, &g, &b);
01000   if ( ret == 3 )
01001     return COLORREF(RGB(r, g, b));
01002   else // in case of senseless string.
01003     return COLORREF(RGB(0, 0, 0));
01004 }
01005 
01006 inline bool vgui_win32_dialog_impl::IsFileBrowserButton(unsigned short ctrl_id)
01007 {
01008   vcl_vector<unsigned short>::iterator result;
01009 
01010   result = vcl_find(fb_ids.begin(), fb_ids.end(), ctrl_id);
01011   return result == fb_ids.end() ? false : true;
01012 }
01013 
01014 
01015 inline bool vgui_win32_dialog_impl::IsColorChooserButton(unsigned short ctrl_id)
01016 {
01017   vcl_vector<unsigned short>::iterator result;
01018 
01019   result = vcl_find(cc_ids.begin(), cc_ids.end(), ctrl_id);
01020   return result == cc_ids.end() ? false : true;
01021 }
01022 
01023 inline bool vgui_win32_dialog_impl::IsCallbackControl(unsigned short ctrl_id)
01024 {
01025   for ( vcl_size_t i = 0; i < callback_controls.size(); i++ )
01026     if ( ctrl_id == callback_controls[i].child_id )
01027       return true;
01028 
01029   return false;
01030 }
01031 
01032 vgui_win32_adaptor* vgui_win32_dialog_impl::find_adaptor(unsigned short ctrl_id)
01033 {
01034   for ( vcl_size_t i = 0; i < inline_tableaus.size(); i++ )
01035     if ( ctrl_id == inline_tableaus[i].childId )
01036       return inline_tableaus[i].adaptor;
01037 
01038   return NULL;
01039 }
01040 
01041 void vgui_win32_dialog_impl::dialog_dispatcher(int ctrl_id)
01042 {
01043   for ( vcl_size_t i = 0; i < callback_controls.size(); i++ ) {
01044     if ( ctrl_id == callback_controls[i].child_id ) {
01045       callback_controls[i].cmnd->execute();
01046       break;
01047     }
01048   }
01049 }
01050 
01051 #if 0
01052 void PrepareImageRect(BOOL bHasTitle, RECT* rpItem, RECT* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, RECT* rpImage)
01053 {
01054   CopyRect(rpImage, rpItem);
01055 
01056   if (bHasTitle == FALSE) {
01057     // Center image horizontally
01058     LONG rpImageWidth = rpImage->right - rpImage->left;
01059     rpImage->left += ((rpImageWidth - (long)dwWidth)/2);
01060   }
01061   else {
01062     // Image must be placed just inside the focus rect
01063     LONG rpTitleWidth = rpTitle->right - rpTitle->left;
01064     rpTitle->right = rpTitleWidth - dwWidth - 30;
01065     rpTitle->left = 30;
01066 
01067     rpImage->left = rpItem->right - dwWidth - 30;
01068     // Center image vertically
01069     LONG rpImageHeight = rpImage->bottom - rpImage->top;
01070     rpImage->top += ((rpImageHeight - (long)dwHeight)/2);
01071   }
01072 
01073   // If button is pressed then press image also
01074   if (bIsPressed && !Themed)
01075     OffsetRect(rpImage, 1, 1);
01076 }
01077 #endif // 0
01078 
01079 void vgui_win32_dialog_impl::DrawImageOnButton(HDC hDC, RECT* lprcItem,
01080                                                HBITMAP hBitmap, unsigned w, unsigned h, BOOL isDisabled)
01081 {
01082   RECT rcImage;
01083 
01084   // Center image horizontally
01085   CopyRect(&rcImage, lprcItem);
01086   UINT image_width = rcImage.right - rcImage.left;
01087   rcImage.left = (image_width - w)/2;
01088 
01089   DrawState(hDC, NULL, NULL, (LPARAM)hBitmap, 0,
01090             rcImage.left, rcImage.top,
01091             rcImage.right - rcImage.left,
01092             rcImage.bottom - rcImage.top,
01093             (isDisabled ? DSS_DISABLED : DSS_NORMAL) | DST_BITMAP);
01094 
01095   return;
01096 }
01097