ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

combo.c
Go to the documentation of this file.
00001 /*
00002  * Combo controls
00003  *
00004  * Copyright 1997 Alex Korobka
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  *
00020  * NOTES
00021  *
00022  * This code was audited for completeness against the documented features
00023  * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
00024  * 
00025  * Unless otherwise noted, we believe this code to be complete, as per
00026  * the specification mentioned above.
00027  * If you discover missing features, or bugs, please note them below.
00028  * 
00029  * TODO:
00030  *   - ComboBox_[GS]etMinVisible()
00031  *   - CB_GETMINVISIBLE, CB_SETMINVISIBLE
00032  *   - CB_SETTOPINDEX
00033  */
00034 
00035 #include <user32.h>
00036 
00037 #include <wine/debug.h>
00038 WINE_DEFAULT_DEBUG_CHANNEL(combo);
00039 
00040 /*
00041  * Additional combo box definitions
00042  */
00043 
00044 #define CB_NOTIFY( lphc, code ) \
00045     (SendMessageW((lphc)->owner, WM_COMMAND, \
00046                   MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
00047 
00048 #define CB_DISABLED( lphc )   (!IsWindowEnabled((lphc)->self))
00049 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
00050 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
00051 #define CB_HWND( lphc )       ((lphc)->self)
00052 // ReactOS already define in include/controls.h We have it here as a sync note.
00053 //#define CB_GETTYPE( lphc )    ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
00054 
00055 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
00056 
00057 /*
00058  * Drawing globals
00059  */
00060 static HBITMAP  hComboBmp = 0;
00061 static UINT CBitHeight, CBitWidth;
00062 
00063 /*
00064  * Look and feel dependent "constants"
00065  */
00066 
00067 #define COMBO_YBORDERGAP         5
00068 #define COMBO_XBORDERSIZE()      2
00069 #define COMBO_YBORDERSIZE()      2
00070 #define COMBO_EDITBUTTONSPACE()  0
00071 #define EDIT_CONTROL_PADDING()   1
00072 
00073 /*********************************************************************
00074  * combo class descriptor
00075  */
00076 static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
00077 const struct builtin_class_descr COMBO_builtin_class =
00078 {
00079     comboboxW,            /* name */
00080     CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style  */
00081     ComboWndProcA,        /* procA */
00082     ComboWndProcW,        /* procW */
00083     sizeof(HEADCOMBO *),  /* extra */
00084     IDC_ARROW,            /* cursor */
00085     0                     /* brush */
00086 };
00087 
00088 
00089 /***********************************************************************
00090  *           COMBO_Init
00091  *
00092  * Load combo button bitmap.
00093  */
00094 static BOOL COMBO_Init(void)
00095 {
00096   HDC       hDC;
00097 
00098   if( hComboBmp ) return TRUE;
00099   if( (hDC = CreateCompatibleDC(0)) )
00100   {
00101     BOOL    bRet = FALSE;
00102     if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
00103     {
00104       BITMAP      bm;
00105       HBITMAP     hPrevB;
00106       RECT        r;
00107 
00108       GetObjectW( hComboBmp, sizeof(bm), &bm );
00109       CBitHeight = bm.bmHeight;
00110       CBitWidth  = bm.bmWidth;
00111 
00112       TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
00113 
00114       hPrevB = SelectObject( hDC, hComboBmp);
00115       SetRect( &r, 0, 0, CBitWidth, CBitHeight );
00116       InvertRect( hDC, &r );
00117       SelectObject( hDC, hPrevB );
00118       bRet = TRUE;
00119     }
00120     DeleteDC( hDC );
00121     return bRet;
00122   }
00123   return FALSE;
00124 }
00125 
00126 
00127 /* Retrieve the UI state for the control */
00128 static BOOL COMBO_update_uistate(LPHEADCOMBO lphc)
00129 {
00130     LONG prev_flags;
00131 
00132     prev_flags = lphc->UIState;
00133     lphc->UIState = DefWindowProcW(lphc->self, WM_QUERYUISTATE, 0, 0);
00134     return prev_flags != lphc->UIState;
00135 }
00136 
00137 /***********************************************************************
00138  *           COMBO_NCCreate
00139  */
00140 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
00141 {
00142     LPHEADCOMBO lphc;
00143 
00144     if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
00145     {
00146         lphc->self = hwnd;
00147         SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
00148 
00149         COMBO_update_uistate(lphc);
00150 
00151        /* some braindead apps do try to use scrollbar/border flags */
00152 
00153     lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
00154         SetWindowLongPtrW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
00155 
00156     /*
00157      * We also have to remove the client edge style to make sure
00158      * we don't end-up with a non client area.
00159      */
00160         SetWindowLongPtrW( hwnd, GWL_EXSTYLE,
00161                         GetWindowLongPtrW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
00162 
00163     if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
00164               lphc->dwStyle |= CBS_HASSTRINGS;
00165     if( !(GetWindowLongPtrW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
00166           lphc->wState |= CBF_NOTIFY;
00167 
00168         TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
00169         return TRUE;
00170     }
00171     return FALSE;
00172 }
00173 
00174 /***********************************************************************
00175  *           COMBO_NCDestroy
00176  */
00177 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
00178 {
00179 
00180    if( lphc )
00181    {
00182        TRACE("[%p]: freeing storage\n", lphc->self);
00183 
00184        if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
00185        DestroyWindow( lphc->hWndLBox );
00186 
00187        SetWindowLongPtrW( lphc->self, 0, 0 );
00188        HeapFree( GetProcessHeap(), 0, lphc );
00189    }
00190    return 0;
00191 }
00192 
00193 /***********************************************************************
00194  *           CBGetTextAreaHeight
00195  *
00196  * This method will calculate the height of the text area of the
00197  * combobox.
00198  * The height of the text area is set in two ways.
00199  * It can be set explicitly through a combobox message or through a
00200  * WM_MEASUREITEM callback.
00201  * If this is not the case, the height is set to font height + 4px
00202  * This height was determined through experimentation.
00203  * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
00204  */
00205 static INT CBGetTextAreaHeight(
00206   HWND        hwnd,
00207   LPHEADCOMBO lphc)
00208 {
00209   INT iTextItemHeight;
00210 
00211   if( lphc->editHeight ) /* explicitly set height */
00212   {
00213     iTextItemHeight = lphc->editHeight;
00214   }
00215   else
00216   {
00217     TEXTMETRICW tm;
00218     HDC         hDC       = GetDC(hwnd);
00219     HFONT       hPrevFont = 0;
00220     INT         baseUnitY;
00221 
00222     if (lphc->hFont)
00223       hPrevFont = SelectObject( hDC, lphc->hFont );
00224 
00225     GetTextMetricsW(hDC, &tm);
00226 
00227     baseUnitY = tm.tmHeight;
00228 
00229     if( hPrevFont )
00230       SelectObject( hDC, hPrevFont );
00231 
00232     ReleaseDC(hwnd, hDC);
00233 
00234     iTextItemHeight = baseUnitY + 4;
00235   }
00236 
00237   /*
00238    * Check the ownerdraw case if we haven't asked the parent the size
00239    * of the item yet.
00240    */
00241   if ( CB_OWNERDRAWN(lphc) &&
00242        (lphc->wState & CBF_MEASUREITEM) )
00243   {
00244     MEASUREITEMSTRUCT measureItem;
00245     RECT              clientRect;
00246     INT               originalItemHeight = iTextItemHeight;
00247     UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
00248 
00249     /*
00250      * We use the client rect for the width of the item.
00251      */
00252     GetClientRect(hwnd, &clientRect);
00253 
00254     lphc->wState &= ~CBF_MEASUREITEM;
00255 
00256     /*
00257      * Send a first one to measure the size of the text area
00258      */
00259     measureItem.CtlType    = ODT_COMBOBOX;
00260     measureItem.CtlID      = id;
00261     measureItem.itemID     = -1;
00262     measureItem.itemWidth  = clientRect.right;
00263     measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
00264     measureItem.itemData   = 0;
00265     SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
00266     iTextItemHeight = 6 + measureItem.itemHeight;
00267 
00268     /*
00269      * Send a second one in the case of a fixed ownerdraw list to calculate the
00270      * size of the list items. (we basically do this on behalf of the listbox)
00271      */
00272     if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
00273     {
00274       measureItem.CtlType    = ODT_COMBOBOX;
00275       measureItem.CtlID      = id;
00276       measureItem.itemID     = 0;
00277       measureItem.itemWidth  = clientRect.right;
00278       measureItem.itemHeight = originalItemHeight;
00279       measureItem.itemData   = 0;
00280       SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
00281       lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
00282     }
00283 
00284     /*
00285      * Keep the size for the next time
00286      */
00287     lphc->editHeight = iTextItemHeight;
00288   }
00289 
00290   return iTextItemHeight;
00291 }
00292 
00293 /***********************************************************************
00294  *           CBForceDummyResize
00295  *
00296  * The dummy resize is used for listboxes that have a popup to trigger
00297  * a re-arranging of the contents of the combobox and the recalculation
00298  * of the size of the "real" control window.
00299  */
00300 static void CBForceDummyResize(
00301   LPHEADCOMBO lphc)
00302 {
00303   RECT windowRect;
00304   int newComboHeight;
00305 
00306   newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
00307 
00308   GetWindowRect(lphc->self, &windowRect);
00309 
00310   /*
00311    * We have to be careful, resizing a combobox also has the meaning that the
00312    * dropped rect will be resized. In this case, we want to trigger a resize
00313    * to recalculate layout but we don't want to change the dropped rectangle
00314    * So, we pass the height of text area of control as the height.
00315    * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
00316    * message.
00317    */
00318   SetWindowPos( lphc->self,
00319         NULL,
00320         0, 0,
00321         windowRect.right  - windowRect.left,
00322         newComboHeight,
00323         SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
00324 }
00325 
00326 /***********************************************************************
00327  *           CBCalcPlacement
00328  *
00329  * Set up component coordinates given valid lphc->RectCombo.
00330  */
00331 static void CBCalcPlacement(
00332   HWND        hwnd,
00333   LPHEADCOMBO lphc,
00334   LPRECT      lprEdit,
00335   LPRECT      lprButton,
00336   LPRECT      lprLB)
00337 {
00338   /*
00339    * Again, start with the client rectangle.
00340    */
00341   GetClientRect(hwnd, lprEdit);
00342 
00343   /*
00344    * Remove the borders
00345    */
00346   InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
00347 
00348   /*
00349    * Chop off the bottom part to fit with the height of the text area.
00350    */
00351   lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
00352 
00353   /*
00354    * The button starts the same vertical position as the text area.
00355    */
00356   CopyRect(lprButton, lprEdit);
00357 
00358   /*
00359    * If the combobox is "simple" there is no button.
00360    */
00361   if( CB_GETTYPE(lphc) == CBS_SIMPLE )
00362     lprButton->left = lprButton->right = lprButton->bottom = 0;
00363   else
00364   {
00365     /*
00366      * Let's assume the combobox button is the same width as the
00367      * scrollbar button.
00368      * size the button horizontally and cut-off the text area.
00369      */
00370     lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
00371     lprEdit->right  = lprButton->left;
00372   }
00373 
00374   /*
00375    * In the case of a dropdown, there is an additional spacing between the
00376    * text area and the button.
00377    */
00378   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
00379   {
00380     lprEdit->right -= COMBO_EDITBUTTONSPACE();
00381   }
00382 
00383   /*
00384    * If we have an edit control, we space it away from the borders slightly.
00385    */
00386   if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
00387   {
00388     InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
00389   }
00390 
00391   /*
00392    * Adjust the size of the listbox popup.
00393    */
00394   if( CB_GETTYPE(lphc) == CBS_SIMPLE )
00395   {
00396     /*
00397      * Use the client rectangle to initialize the listbox rectangle
00398      */
00399     GetClientRect(hwnd, lprLB);
00400 
00401     /*
00402      * Then, chop-off the top part.
00403      */
00404     lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
00405   }
00406   else
00407   {
00408     /*
00409      * Make sure the dropped width is as large as the combobox itself.
00410      */
00411     if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
00412     {
00413       lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
00414 
00415       /*
00416        * In the case of a dropdown, the popup listbox is offset to the right.
00417        * so, we want to make sure it's flush with the right side of the
00418        * combobox
00419        */
00420       if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
00421     lprLB->right -= COMBO_EDITBUTTONSPACE();
00422     }
00423     else
00424        lprLB->right = lprLB->left + lphc->droppedWidth;
00425   }
00426 
00427   /* don't allow negative window width */
00428   if (lprEdit->right < lprEdit->left)
00429     lprEdit->right = lprEdit->left;
00430 
00431   TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
00432 
00433   TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
00434 
00435   TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
00436 }
00437 
00438 /***********************************************************************
00439  *           CBGetDroppedControlRect
00440  */
00441 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
00442 {
00443     /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
00444      of the combo box and the lower right corner of the listbox */
00445 
00446     GetWindowRect(lphc->self, lpRect);
00447 
00448     lpRect->right =  lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
00449     lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
00450 
00451 }
00452 
00453 /***********************************************************************
00454  *           COMBO_WindowPosChanging
00455  */
00456 static LRESULT COMBO_WindowPosChanging(
00457   HWND        hwnd,
00458   LPHEADCOMBO lphc,
00459   WINDOWPOS*  posChanging)
00460 {
00461   /*
00462    * We need to override the WM_WINDOWPOSCHANGING method to handle all
00463    * the non-simple comboboxes. The problem is that those controls are
00464    * always the same height. We have to make sure they are not resized
00465    * to another value.
00466    */
00467   if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
00468        ((posChanging->flags & SWP_NOSIZE) == 0) )
00469   {
00470     int newComboHeight;
00471 
00472     newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
00473                       2*COMBO_YBORDERSIZE();
00474 
00475     /*
00476      * Resizing a combobox has another side effect, it resizes the dropped
00477      * rectangle as well. However, it does it only if the new height for the
00478      * combobox is more than the height it should have. In other words,
00479      * if the application resizing the combobox only had the intention to resize
00480      * the actual control, for example, to do the layout of a dialog that is
00481      * resized, the height of the dropdown is not changed.
00482      */
00483     if (posChanging->cy > newComboHeight)
00484     {
00485     TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
00486           posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
00487           lphc->droppedRect.top);
00488       lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
00489 
00490     }
00491     posChanging->cy = newComboHeight;
00492   }
00493 
00494   return 0;
00495 }
00496 
00497 /***********************************************************************
00498  *           COMBO_Create
00499  */
00500 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
00501                              BOOL unicode )
00502 {
00503   static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
00504   static const WCHAR editName[] = {'E','d','i','t',0};
00505 
00506   if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
00507   if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
00508 
00509   lphc->owner = hwndParent;
00510 
00511   /*
00512    * The item height and dropped width are not set when the control
00513    * is created.
00514    */
00515   lphc->droppedWidth = lphc->editHeight = 0;
00516 
00517   /*
00518    * The first time we go through, we want to measure the ownerdraw item
00519    */
00520   lphc->wState |= CBF_MEASUREITEM;
00521 
00522   /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
00523 
00524   if( lphc->owner || !(style & WS_VISIBLE) )
00525   {
00526       UINT lbeStyle   = 0;
00527       UINT lbeExStyle = 0;
00528 
00529       /*
00530        * Initialize the dropped rect to the size of the client area of the
00531        * control and then, force all the areas of the combobox to be
00532        * recalculated.
00533        */
00534       GetClientRect( hwnd, &lphc->droppedRect );
00535       CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
00536 
00537       /*
00538        * Adjust the position of the popup listbox if it's necessary
00539        */
00540       if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
00541       {
00542     lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE();
00543 
00544     /*
00545      * If it's a dropdown, the listbox is offset
00546      */
00547     if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
00548       lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
00549 
00550         if (lphc->droppedRect.bottom < lphc->droppedRect.top)
00551             lphc->droppedRect.bottom = lphc->droppedRect.top;
00552         if (lphc->droppedRect.right < lphc->droppedRect.left)
00553             lphc->droppedRect.right = lphc->droppedRect.left;
00554         MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
00555       }
00556 
00557       /* create listbox popup */
00558 
00559       lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
00560                  (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
00561 
00562       if( lphc->dwStyle & CBS_SORT )
00563     lbeStyle |= LBS_SORT;
00564       if( lphc->dwStyle & CBS_HASSTRINGS )
00565     lbeStyle |= LBS_HASSTRINGS;
00566       if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
00567     lbeStyle |= LBS_NOINTEGRALHEIGHT;
00568       if( lphc->dwStyle & CBS_DISABLENOSCROLL )
00569     lbeStyle |= LBS_DISABLENOSCROLL;
00570 
00571       if( CB_GETTYPE(lphc) == CBS_SIMPLE )  /* child listbox */
00572       {
00573     lbeStyle |= WS_VISIBLE;
00574 
00575     /*
00576      * In win 95 look n feel, the listbox in the simple combobox has
00577      * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
00578      */
00579     lbeStyle   &= ~WS_BORDER;
00580     lbeExStyle |= WS_EX_CLIENTEDGE;
00581       }
00582       else
00583       {
00584         lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
00585       }
00586 
00587       if (unicode)
00588           lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
00589                                            lphc->droppedRect.left,
00590                                            lphc->droppedRect.top,
00591                                            lphc->droppedRect.right - lphc->droppedRect.left,
00592                                            lphc->droppedRect.bottom - lphc->droppedRect.top,
00593                                            hwnd, (HMENU)ID_CB_LISTBOX,
00594                                            (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
00595       else
00596           lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
00597                                            lphc->droppedRect.left,
00598                                            lphc->droppedRect.top,
00599                                            lphc->droppedRect.right - lphc->droppedRect.left,
00600                                            lphc->droppedRect.bottom - lphc->droppedRect.top,
00601                                            hwnd, (HMENU)ID_CB_LISTBOX,
00602                                            (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
00603 
00604       if( lphc->hWndLBox )
00605       {
00606       BOOL  bEdit = TRUE;
00607       lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
00608 
00609       if( lphc->wState & CBF_EDIT )
00610       {
00611           if( lphc->dwStyle & CBS_OEMCONVERT )
00612           lbeStyle |= ES_OEMCONVERT;
00613           if( lphc->dwStyle & CBS_AUTOHSCROLL )
00614           lbeStyle |= ES_AUTOHSCROLL;
00615           if( lphc->dwStyle & CBS_LOWERCASE )
00616           lbeStyle |= ES_LOWERCASE;
00617           else if( lphc->dwStyle & CBS_UPPERCASE )
00618           lbeStyle |= ES_UPPERCASE;
00619 
00620               if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
00621 
00622               if (unicode)
00623                   lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
00624                                                    lphc->textRect.left, lphc->textRect.top,
00625                                                    lphc->textRect.right - lphc->textRect.left,
00626                                                    lphc->textRect.bottom - lphc->textRect.top,
00627                                                    hwnd, (HMENU)ID_CB_EDIT,
00628                                                    (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
00629               else
00630                   lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
00631                                                    lphc->textRect.left, lphc->textRect.top,
00632                                                    lphc->textRect.right - lphc->textRect.left,
00633                                                    lphc->textRect.bottom - lphc->textRect.top,
00634                                                    hwnd, (HMENU)ID_CB_EDIT,
00635                                                    (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
00636 
00637           if( !lphc->hWndEdit )
00638         bEdit = FALSE;
00639       }
00640 
00641           if( bEdit )
00642       {
00643         if( CB_GETTYPE(lphc) != CBS_SIMPLE )
00644         {
00645               /* Now do the trick with parent */
00646           SetParent(lphc->hWndLBox, HWND_DESKTOP);
00647               /*
00648                * If the combo is a dropdown, we must resize the control
00649            * to fit only the text area and button. To do this,
00650            * we send a dummy resize and the WM_WINDOWPOSCHANGING message
00651            * will take care of setting the height for us.
00652                */
00653           CBForceDummyResize(lphc);
00654         }
00655 
00656         TRACE("init done\n");
00657         return 0;
00658       }
00659       ERR("edit control failure.\n");
00660       } else ERR("listbox failure.\n");
00661   } else ERR("no owner for visible combo.\n");
00662 
00663   /* CreateWindow() will send WM_NCDESTROY to cleanup */
00664 
00665   return -1;
00666 }
00667 
00668 /***********************************************************************
00669  *           CBPaintButton
00670  *
00671  * Paint combo button (normal, pressed, and disabled states).
00672  */
00673 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
00674 {
00675     UINT buttonState = DFCS_SCROLLCOMBOBOX;
00676 
00677     if( lphc->wState & CBF_NOREDRAW )
00678       return;
00679 
00680 
00681     if (lphc->wState & CBF_BUTTONDOWN)
00682     buttonState |= DFCS_PUSHED;
00683 
00684     if (CB_DISABLED(lphc))
00685     buttonState |= DFCS_INACTIVE;
00686 
00687     DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
00688 }
00689 
00690 /***********************************************************************
00691  *           CBPaintText
00692  *
00693  * Paint CBS_DROPDOWNLIST text field / update edit control contents.
00694  */
00695 static void CBPaintText(
00696   LPHEADCOMBO lphc,
00697   HDC         hdc,
00698   RECT        rectEdit)
00699 {
00700    INT  id, size = 0;
00701    LPWSTR pText = NULL;
00702 
00703    if( lphc->wState & CBF_NOREDRAW ) return;
00704 
00705    TRACE("\n");
00706 
00707    /* follow Windows combobox that sends a bunch of text
00708     * inquiries to its listbox while processing WM_PAINT. */
00709 
00710    if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
00711    {
00712         size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
00713     if (size == LB_ERR)
00714       FIXME("LB_ERR probably not handled yet\n");
00715         if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
00716     {
00717             /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
00718         size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
00719         pText[size] = '\0'; /* just in case */
00720     } else return;
00721    }
00722    else
00723        if( !CB_OWNERDRAWN(lphc) )
00724        return;
00725 
00726    if( lphc->wState & CBF_EDIT )
00727    {
00728         static const WCHAR empty_stringW[] = { 0 };
00729     if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
00730     if( lphc->wState & CBF_FOCUSED )
00731         SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
00732    }
00733    else /* paint text field ourselves */
00734    {
00735      UINT   itemState = ODS_COMBOBOXEDIT;
00736      HFONT  hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
00737 
00738      /*
00739       * Give ourselves some space.
00740       */
00741      InflateRect( &rectEdit, -1, -1 );
00742 
00743      if( CB_OWNERDRAWN(lphc) )
00744      {
00745        DRAWITEMSTRUCT dis;
00746        HRGN           clipRegion;
00747        UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
00748 
00749        /* setup state for DRAWITEM message. Owner will highlight */
00750        if ( (lphc->wState & CBF_FOCUSED) &&
00751         !(lphc->wState & CBF_DROPPED) )
00752        itemState |= ODS_SELECTED | ODS_FOCUS;
00753 
00754        if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
00755 
00756        dis.CtlType  = ODT_COMBOBOX;
00757        dis.CtlID    = ctlid;
00758        dis.hwndItem = lphc->self;
00759        dis.itemAction   = ODA_DRAWENTIRE;
00760        dis.itemID   = id;
00761        dis.itemState    = itemState;
00762        dis.hDC      = hdc;
00763        dis.rcItem   = rectEdit;
00764        dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0 );
00765 
00766        /*
00767     * Clip the DC and have the parent draw the item.
00768     */
00769        clipRegion = set_control_clipping( hdc, &rectEdit );
00770 
00771        SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
00772 
00773        SelectClipRgn( hdc, clipRegion);
00774        if (clipRegion) DeleteObject( clipRegion );
00775      }
00776      else
00777      {
00778        static const WCHAR empty_stringW[] = { 0 };
00779 
00780        if ( (lphc->wState & CBF_FOCUSED) &&
00781         !(lphc->wState & CBF_DROPPED) ) {
00782 
00783        /* highlight */
00784        FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
00785        SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
00786        SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
00787        }
00788 
00789        ExtTextOutW( hdc,
00790             rectEdit.left + 1,
00791             rectEdit.top + 1,
00792             ETO_OPAQUE | ETO_CLIPPED,
00793             &rectEdit,
00794             pText ? pText : empty_stringW , size, NULL );
00795 
00796        if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED) &&
00797           !(lphc->UIState & UISF_HIDEFOCUS))
00798      DrawFocusRect( hdc, &rectEdit );
00799      }
00800 
00801      if( hPrevFont )
00802        SelectObject(hdc, hPrevFont );
00803    }
00804    if (pText)
00805     HeapFree( GetProcessHeap(), 0, pText );
00806 }
00807 
00808 /***********************************************************************
00809  *           CBPaintBorder
00810  */
00811 static void CBPaintBorder(
00812   HWND            hwnd,
00813   const HEADCOMBO *lphc,
00814   HDC             hdc)
00815 {
00816   RECT clientRect;
00817 
00818   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
00819   {
00820     GetClientRect(hwnd, &clientRect);
00821   }
00822   else
00823   {
00824     CopyRect(&clientRect, &lphc->textRect);
00825 
00826     InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
00827     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
00828   }
00829 
00830   DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
00831 }
00832 
00833 /***********************************************************************
00834  *           COMBO_PrepareColors
00835  *
00836  * This method will sent the appropriate WM_CTLCOLOR message to
00837  * prepare and setup the colors for the combo's DC.
00838  *
00839  * It also returns the brush to use for the background.
00840  */
00841 static HBRUSH COMBO_PrepareColors(
00842   LPHEADCOMBO lphc,
00843   HDC         hDC)
00844 {
00845   HBRUSH  hBkgBrush;
00846 
00847   /*
00848    * Get the background brush for this control.
00849    */
00850   if (CB_DISABLED(lphc))
00851   {
00852      hBkgBrush = GetControlColor(lphc->owner, lphc->self, hDC, WM_CTLCOLORSTATIC);
00853     /*
00854      * We have to change the text color since WM_CTLCOLORSTATIC will
00855      * set it to the "enabled" color. This is the same behavior as the
00856      * edit control
00857      */
00858     SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
00859   }
00860   else
00861   {
00862       /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
00863      hBkgBrush = GetControlColor(lphc->owner, lphc->self, hDC, WM_CTLCOLOREDIT);
00864   }
00865 
00866   /*
00867    * Catch errors.
00868    */
00869   if( !hBkgBrush )
00870     hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
00871 
00872   return hBkgBrush;
00873 }
00874 
00875 
00876 /***********************************************************************
00877  *           COMBO_Paint
00878  */
00879 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
00880 {
00881   PAINTSTRUCT ps;
00882   HDC   hDC;
00883 
00884   hDC = (hParamDC) ? hParamDC
00885            : BeginPaint( lphc->self, &ps);
00886 
00887   TRACE("hdc=%p\n", hDC);
00888 
00889   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
00890   {
00891       HBRUSH    hPrevBrush, hBkgBrush;
00892 
00893       /*
00894        * Retrieve the background brush and select it in the
00895        * DC.
00896        */
00897       hBkgBrush = COMBO_PrepareColors(lphc, hDC);
00898 
00899       hPrevBrush = SelectObject( hDC, hBkgBrush );
00900       if (!(lphc->wState & CBF_EDIT))
00901         FillRect(hDC, &lphc->textRect, hBkgBrush);
00902 
00903       /*
00904        * In non 3.1 look, there is a sunken border on the combobox
00905        */
00906       CBPaintBorder(lphc->self, lphc, hDC);
00907 
00908       if( !IsRectEmpty(&lphc->buttonRect) )
00909       {
00910     CBPaintButton(lphc, hDC, lphc->buttonRect);
00911       }
00912 
00913       /* paint the edit control padding area */
00914       if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
00915       {
00916           RECT rPadEdit = lphc->textRect;
00917 
00918           InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
00919 
00920           FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
00921       }
00922 
00923       if( !(lphc->wState & CBF_EDIT) )
00924     CBPaintText( lphc, hDC, lphc->textRect);
00925 
00926       if( hPrevBrush )
00927     SelectObject( hDC, hPrevBrush );
00928   }
00929 
00930   if( !hParamDC )
00931     EndPaint(lphc->self, &ps);
00932 
00933   return 0;
00934 }
00935 
00936 /***********************************************************************
00937  *           CBUpdateLBox
00938  *
00939  * Select listbox entry according to the contents of the edit control.
00940  */
00941 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
00942 {
00943    INT  length, idx;
00944    LPWSTR pText = NULL;
00945 
00946    idx = LB_ERR;
00947    length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
00948 
00949    if( length > 0 )
00950        pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
00951 
00952    TRACE("\t edit text length %i\n", length );
00953 
00954    if( pText )
00955    {
00956        GetWindowTextW( lphc->hWndEdit, pText, length + 1);
00957        idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
00958                  (WPARAM)(-1), (LPARAM)pText );
00959        HeapFree( GetProcessHeap(), 0, pText );
00960    }
00961 
00962    SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
00963 
00964    /* probably superfluous but Windows sends this too */
00965    SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
00966    SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
00967 
00968    return idx;
00969 }
00970 
00971 /***********************************************************************
00972  *           CBUpdateEdit
00973  *
00974  * Copy a listbox entry to the edit control.
00975  */
00976 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
00977 {
00978    INT  length;
00979    LPWSTR pText = NULL;
00980    static const WCHAR empty_stringW[] = { 0 };
00981 
00982    TRACE("\t %i\n", index );
00983 
00984    if( index >= 0 ) /* got an entry */
00985    {
00986        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
00987        if( length != LB_ERR)
00988        {
00989        if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
00990        {
00991         SendMessageW(lphc->hWndLBox, LB_GETTEXT,
00992                 (WPARAM)index, (LPARAM)pText );
00993        }
00994        }
00995    }
00996 
00997    if( CB_HASSTRINGS(lphc) )
00998    {
00999       lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
01000       SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
01001       lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
01002    }
01003 
01004    if( lphc->wState & CBF_FOCUSED )
01005       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
01006 
01007    HeapFree( GetProcessHeap(), 0, pText );
01008 }
01009 
01010 /***********************************************************************
01011  *           CBDropDown
01012  *
01013  * Show listbox popup.
01014  */
01015 static void CBDropDown( LPHEADCOMBO lphc )
01016 {
01017     HMONITOR monitor;
01018     MONITORINFO mon_info;
01019    RECT rect,r;
01020    int nItems = 0;
01021    int nDroppedHeight;
01022 
01023    TRACE("[%p]: drop down\n", lphc->self);
01024 
01025    CB_NOTIFY( lphc, CBN_DROPDOWN );
01026 
01027    /* set selection */
01028 
01029    lphc->wState |= CBF_DROPPED;
01030    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
01031    {
01032        lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
01033 
01034        /* Update edit only if item is in the list */
01035        if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
01036      CBUpdateEdit( lphc, lphc->droppedIndex );
01037    }
01038    else
01039    {
01040        lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
01041 
01042        SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
01043                      (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
01044        SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
01045    }
01046 
01047    /* now set popup position */
01048    GetWindowRect( lphc->self, &rect );
01049 
01050    /*
01051     * If it's a dropdown, the listbox is offset
01052     */
01053    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
01054      rect.left += COMBO_EDITBUTTONSPACE();
01055 
01056   /* if the dropped height is greater than the total height of the dropped
01057      items list, then force the drop down list height to be the total height
01058      of the items in the dropped list */
01059 
01060   /* And Remove any extra space (Best Fit) */
01061    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
01062   /* if listbox length has been set directly by its handle */
01063    GetWindowRect(lphc->hWndLBox, &r);
01064    if (nDroppedHeight < r.bottom - r.top)
01065        nDroppedHeight = r.bottom - r.top;
01066    nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
01067 
01068    if (nItems > 0)
01069    {
01070       int nHeight;
01071       int nIHeight;
01072 
01073       nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
01074 
01075       nHeight = nIHeight*nItems;
01076 
01077       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
01078          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
01079 
01080       if (nDroppedHeight < nHeight)
01081       {
01082             if (nItems < 5)
01083                 nDroppedHeight = (nItems+1)*nIHeight;
01084             else if (nDroppedHeight < 6*nIHeight)
01085                 nDroppedHeight = 6*nIHeight;
01086       }
01087    }
01088 
01089    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
01090    monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
01091    mon_info.cbSize = sizeof(mon_info);
01092    GetMonitorInfoW( monitor, &mon_info );
01093 
01094    if( (rect.bottom + nDroppedHeight) >= mon_info.rcWork.bottom )
01095       rect.bottom = rect.top - nDroppedHeight;
01096 
01097    SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, rect.left, rect.bottom,
01098          lphc->droppedRect.right - lphc->droppedRect.left,
01099          nDroppedHeight,
01100          SWP_NOACTIVATE | SWP_SHOWWINDOW);
01101 
01102 
01103    if( !(lphc->wState & CBF_NOREDRAW) )
01104      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
01105                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
01106 
01107    EnableWindow( lphc->hWndLBox, TRUE );
01108    if (GetCapture() != lphc->self)
01109       SetCapture(lphc->hWndLBox);
01110 }
01111 
01112 /***********************************************************************
01113  *           CBRollUp
01114  *
01115  * Hide listbox popup.
01116  */
01117 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
01118 {
01119    HWND hWnd = lphc->self;
01120 
01121    TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
01122      lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
01123 
01124    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
01125 
01126    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
01127    {
01128 
01129        if( lphc->wState & CBF_DROPPED )
01130        {
01131        RECT rect;
01132 
01133        lphc->wState &= ~CBF_DROPPED;
01134        ShowWindow( lphc->hWndLBox, SW_HIDE );
01135 
01136            if(GetCapture() == lphc->hWndLBox)
01137            {
01138                ReleaseCapture();
01139            }
01140 
01141        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
01142        {
01143            rect = lphc->buttonRect;
01144        }
01145        else
01146            {
01147            if( bButton )
01148            {
01149          UnionRect( &rect,
01150                 &lphc->buttonRect,
01151                 &lphc->textRect);
01152            }
01153            else
01154          rect = lphc->textRect;
01155 
01156            bButton = TRUE;
01157        }
01158 
01159        if( bButton && !(lphc->wState & CBF_NOREDRAW) )
01160            RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
01161                    RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
01162        CB_NOTIFY( lphc, CBN_CLOSEUP );
01163        }
01164    }
01165 }
01166 
01167 /***********************************************************************
01168  *           COMBO_FlipListbox
01169  *
01170  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
01171  */
01172 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
01173 {
01174    if( lphc->wState & CBF_DROPPED )
01175    {
01176        CBRollUp( lphc, ok, bRedrawButton );
01177        return FALSE;
01178    }
01179 
01180    CBDropDown( lphc );
01181    return TRUE;
01182 }
01183 
01184 /***********************************************************************
01185  *           CBRepaintButton
01186  */
01187 static void CBRepaintButton( LPHEADCOMBO lphc )
01188    {
01189   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
01190   UpdateWindow(lphc->self);
01191 }
01192 
01193 /***********************************************************************
01194  *           COMBO_SetFocus
01195  */
01196 static void COMBO_SetFocus( LPHEADCOMBO lphc )
01197 {
01198    if( !(lphc->wState & CBF_FOCUSED) )
01199    {
01200        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
01201            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
01202 
01203        /* This is wrong. Message sequences seem to indicate that this
01204           is set *after* the notify. */
01205        /* lphc->wState |= CBF_FOCUSED;  */
01206 
01207        if( !(lphc->wState & CBF_EDIT) )
01208      InvalidateRect(lphc->self, &lphc->textRect, TRUE);
01209 
01210        CB_NOTIFY( lphc, CBN_SETFOCUS );
01211        lphc->wState |= CBF_FOCUSED;
01212    }
01213 }
01214 
01215 /***********************************************************************
01216  *           COMBO_KillFocus
01217  */
01218 static void COMBO_KillFocus( LPHEADCOMBO lphc )
01219 {
01220    HWND hWnd = lphc->self;
01221 
01222    if( lphc->wState & CBF_FOCUSED )
01223    {
01224        CBRollUp( lphc, FALSE, TRUE );
01225        if( IsWindow( hWnd ) )
01226        {
01227            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
01228                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
01229 
01230        lphc->wState &= ~CBF_FOCUSED;
01231 
01232            /* redraw text */
01233        if( !(lphc->wState & CBF_EDIT) )
01234          InvalidateRect(lphc->self, &lphc->textRect, TRUE);
01235 
01236            CB_NOTIFY( lphc, CBN_KILLFOCUS );
01237        }
01238    }
01239 }
01240 
01241 /***********************************************************************
01242  *           COMBO_Command
01243  */
01244 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
01245 {
01246    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
01247    {
01248        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
01249 
01250        switch( HIWORD(wParam) >> 8 )
01251        {
01252        case (EN_SETFOCUS >> 8):
01253 
01254                TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
01255 
01256         COMBO_SetFocus( lphc );
01257             break;
01258 
01259        case (EN_KILLFOCUS >> 8):
01260 
01261                TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
01262 
01263         /* NOTE: it seems that Windows' edit control sends an
01264          * undocumented message WM_USER + 0x1B instead of this
01265          * notification (only when it happens to be a part of
01266          * the combo). ?? - AK.
01267          */
01268 
01269         COMBO_KillFocus( lphc );
01270         break;
01271 
01272 
01273        case (EN_CHANGE >> 8):
01274            /*
01275             * In some circumstances (when the selection of the combobox
01276         * is changed for example) we don't want the EN_CHANGE notification
01277         * to be forwarded to the parent of the combobox. This code
01278         * checks a flag that is set in these occasions and ignores the
01279         * notification.
01280             */
01281         if (lphc->wState & CBF_NOLBSELECT)
01282         {
01283           lphc->wState &= ~CBF_NOLBSELECT;
01284         }
01285         else
01286         {
01287           CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
01288         }
01289 
01290             if (!(lphc->wState & CBF_NOEDITNOTIFY))
01291           CB_NOTIFY( lphc, CBN_EDITCHANGE );
01292         break;
01293 
01294        case (EN_UPDATE >> 8):
01295             if (!(lphc->wState & CBF_NOEDITNOTIFY))
01296           CB_NOTIFY( lphc, CBN_EDITUPDATE );
01297         break;
01298 
01299        case (EN_ERRSPACE >> 8):
01300         CB_NOTIFY( lphc, CBN_ERRSPACE );
01301        }
01302    }
01303    else if( lphc->hWndLBox == hWnd )
01304    {
01305        switch( (short)HIWORD(wParam) )
01306        {
01307        case LBN_ERRSPACE:
01308         CB_NOTIFY( lphc, CBN_ERRSPACE );
01309         break;
01310 
01311        case LBN_DBLCLK:
01312         CB_NOTIFY( lphc, CBN_DBLCLK );
01313         break;
01314 
01315        case LBN_SELCHANGE:
01316        case LBN_SELCANCEL:
01317 
01318                 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
01319 
01320                 /* do not roll up if selection is being tracked
01321                  * by arrow keys in the dropdown listbox */
01322                 if (!(lphc->wState & CBF_NOROLLUP))
01323                 {
01324                     CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
01325                 }
01326                 else lphc->wState &= ~CBF_NOROLLUP;
01327 
01328         CB_NOTIFY( lphc, CBN_SELCHANGE );
01329 
01330         if( HIWORD(wParam) == LBN_SELCHANGE)
01331         {
01332            if( lphc->wState & CBF_EDIT )
01333            {
01334                INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
01335                lphc->wState |= CBF_NOLBSELECT;
01336                CBUpdateEdit( lphc, index );
01337                /* select text in edit, as Windows does */
01338                SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
01339            }
01340            else
01341                    {
01342                InvalidateRect(lphc->self, &lphc->textRect, TRUE);
01343                        UpdateWindow(lphc->self);
01344                    }
01345         }
01346                 break;
01347 
01348        case LBN_SETFOCUS:
01349        case LBN_KILLFOCUS:
01350         /* nothing to do here since ComboLBox always resets the focus to its
01351          * combo/edit counterpart */
01352          break;
01353        }
01354    }
01355    return 0;
01356 }
01357 
01358 /***********************************************************************
01359  *           COMBO_ItemOp
01360  *
01361  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
01362  */
01363 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
01364 {
01365    HWND hWnd = lphc->self;
01366    UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
01367 
01368    TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
01369 
01370    switch( msg )
01371    {
01372    case WM_DELETEITEM:
01373        {
01374            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
01375            lpIS->CtlType  = ODT_COMBOBOX;
01376            lpIS->CtlID    = id;
01377            lpIS->hwndItem = hWnd;
01378            break;
01379        }
01380    case WM_DRAWITEM:
01381        {
01382            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
01383            lpIS->CtlType  = ODT_COMBOBOX;
01384            lpIS->CtlID    = id;
01385            lpIS->hwndItem = hWnd;
01386            break;
01387        }
01388    case WM_COMPAREITEM:
01389        {
01390            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
01391            lpIS->CtlType  = ODT_COMBOBOX;
01392            lpIS->CtlID    = id;
01393            lpIS->hwndItem = hWnd;
01394            break;
01395        }
01396    case WM_MEASUREITEM:
01397        {
01398            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
01399            lpIS->CtlType  = ODT_COMBOBOX;
01400            lpIS->CtlID    = id;
01401            break;
01402        }
01403    }
01404    return SendMessageW(lphc->owner, msg, id, lParam);
01405 }
01406 
01407 
01408 /***********************************************************************
01409  *           COMBO_GetTextW
01410  */
01411 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
01412 {
01413     INT length;
01414 
01415     if( lphc->wState & CBF_EDIT )
01416         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
01417 
01418     /* get it from the listbox */
01419 
01420     if (!count || !buf) return 0;
01421     if( lphc->hWndLBox )
01422     {
01423         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
01424         if (idx == LB_ERR) goto error;
01425         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
01426         if (length == LB_ERR) goto error;
01427 
01428         /* 'length' is without the terminating character */
01429         if (length >= count)
01430         {
01431             LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
01432             if (!lpBuffer) goto error;
01433             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
01434 
01435             /* truncate if buffer is too short */
01436             if (length != LB_ERR)
01437             {
01438                 lstrcpynW( buf, lpBuffer, count );
01439                 length = count;
01440             }
01441             HeapFree( GetProcessHeap(), 0, lpBuffer );
01442         }
01443         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
01444 
01445         if (length == LB_ERR) return 0;
01446         return length;
01447     }
01448 
01449  error:  /* error - truncate string, return zero */
01450     buf[0] = 0;
01451     return 0;
01452 }
01453 
01454 
01455 /***********************************************************************
01456  *           COMBO_GetTextA
01457  *
01458  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
01459  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
01460  */
01461 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
01462 {
01463     INT length;
01464 
01465     if( lphc->wState & CBF_EDIT )
01466         return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
01467 
01468     /* get it from the listbox */
01469 
01470     if (!count || !buf) return 0;
01471     if( lphc->hWndLBox )
01472     {
01473         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
01474         if (idx == LB_ERR) goto error;
01475         length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
01476         if (length == LB_ERR) goto error;
01477 
01478         /* 'length' is without the terminating character */
01479         if (length >= count)
01480         {
01481             LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
01482             if (!lpBuffer) goto error;
01483             length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
01484 
01485             /* truncate if buffer is too short */
01486             if (length != LB_ERR)
01487             {
01488                 lstrcpynA( buf, lpBuffer, count );
01489                 length = count;
01490             }
01491             HeapFree( GetProcessHeap(), 0, lpBuffer );
01492         }
01493         else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
01494 
01495         if (length == LB_ERR) return 0;
01496         return length;
01497     }
01498 
01499  error:  /* error - truncate string, return zero */
01500     buf[0] = 0;
01501     return 0;
01502 }
01503 
01504 
01505 /***********************************************************************
01506  *           CBResetPos
01507  *
01508  * This function sets window positions according to the updated
01509  * component placement struct.
01510  */
01511 static void CBResetPos(
01512   LPHEADCOMBO lphc,
01513   const RECT  *rectEdit,
01514   const RECT  *rectLB,
01515   BOOL        bRedraw)
01516 {
01517    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
01518 
01519    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
01520     * sizing messages */
01521 
01522    if( lphc->wState & CBF_EDIT )
01523      SetWindowPos( lphc->hWndEdit, 0,
01524            rectEdit->left, rectEdit->top,
01525            rectEdit->right - rectEdit->left,
01526            rectEdit->bottom - rectEdit->top,
01527                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
01528 
01529    SetWindowPos( lphc->hWndLBox, 0,
01530          rectLB->left, rectLB->top,
01531                  rectLB->right - rectLB->left,
01532          rectLB->bottom - rectLB->top,
01533            SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
01534 
01535    if( bDrop )
01536    {
01537        if( lphc->wState & CBF_DROPPED )
01538        {
01539            lphc->wState &= ~CBF_DROPPED;
01540            ShowWindow( lphc->hWndLBox, SW_HIDE );
01541        }
01542 
01543        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
01544            RedrawWindow( lphc->self, NULL, 0,
01545                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
01546    }
01547 }
01548 
01549 
01550 /***********************************************************************
01551  *           COMBO_Size
01552  */
01553 static void COMBO_Size( LPHEADCOMBO lphc, BOOL bRedraw )
01554   {
01555   CBCalcPlacement(lphc->self,
01556           lphc,
01557           &lphc->textRect,
01558           &lphc->buttonRect,
01559           &lphc->droppedRect);
01560 
01561   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, bRedraw );
01562 }
01563 
01564 
01565 /***********************************************************************
01566  *           COMBO_Font
01567  */
01568 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
01569 {
01570   /*
01571    * Set the font
01572    */
01573   lphc->hFont = hFont;
01574 
01575   /*
01576    * Propagate to owned windows.
01577    */
01578   if( lphc->wState & CBF_EDIT )
01579       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
01580   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
01581 
01582   /*
01583    * Redo the layout of the control.
01584    */
01585   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
01586   {
01587     CBCalcPlacement(lphc->self,
01588             lphc,
01589             &lphc->textRect,
01590             &lphc->buttonRect,
01591             &lphc->droppedRect);
01592 
01593     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
01594   }
01595   else
01596   {
01597     CBForceDummyResize(lphc);
01598   }
01599 }
01600 
01601 
01602 /***********************************************************************
01603  *           COMBO_SetItemHeight
01604  */
01605 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
01606 {
01607    LRESULT  lRet = CB_ERR;
01608 
01609    if( index == -1 ) /* set text field height */
01610    {
01611        if( height < 32768 )
01612        {
01613            lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
01614 
01615      /*
01616       * Redo the layout of the control.
01617       */
01618      if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
01619      {
01620        CBCalcPlacement(lphc->self,
01621                lphc,
01622                &lphc->textRect,
01623                &lphc->buttonRect,
01624                &lphc->droppedRect);
01625 
01626        CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
01627      }
01628      else
01629      {
01630        CBForceDummyResize(lphc);
01631      }
01632 
01633        lRet = height;
01634        }
01635    }
01636    else if ( CB_OWNERDRAWN(lphc) )  /* set listbox item height */
01637     lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
01638                   (WPARAM)index, (LPARAM)height );
01639    return lRet;
01640 }
01641 
01642 /***********************************************************************
01643  *           COMBO_SelectString
01644  */
01645 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
01646 {
01647    INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
01648                          SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
01649    if( index >= 0 )
01650    {
01651      if( lphc->wState & CBF_EDIT )
01652        CBUpdateEdit( lphc, index );
01653      else
01654      {
01655        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
01656      }
01657    }
01658    return (LRESULT)index;
01659 }
01660 
01661 /***********************************************************************
01662  *           COMBO_LButtonDown
01663  */
01664 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
01665 {
01666    POINT     pt;
01667    BOOL      bButton;
01668    HWND      hWnd = lphc->self;
01669 
01670    pt.x = (short)LOWORD(lParam);
01671    pt.y = (short)HIWORD(lParam);
01672    bButton = PtInRect(&lphc->buttonRect, pt);
01673 
01674    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
01675        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
01676    {
01677        lphc->wState |= CBF_BUTTONDOWN;
01678        if( lphc->wState & CBF_DROPPED )
01679        {
01680        /* got a click to cancel selection */
01681 
01682            lphc->wState &= ~CBF_BUTTONDOWN;
01683            CBRollUp( lphc, TRUE, FALSE );
01684        if( !IsWindow( hWnd ) ) return;
01685 
01686            if( lphc->wState & CBF_CAPTURE )
01687            {
01688                lphc->wState &= ~CBF_CAPTURE;
01689                ReleaseCapture();
01690            }
01691        }
01692        else
01693        {
01694        /* drop down the listbox and start tracking */
01695 
01696            lphc->wState |= CBF_CAPTURE;
01697            SetCapture( hWnd );
01698            CBDropDown( lphc );
01699        }
01700        if( bButton ) CBRepaintButton( lphc );
01701    }
01702 }
01703 
01704 /***********************************************************************
01705  *           COMBO_LButtonUp
01706  *
01707  * Release capture and stop tracking if needed.
01708  */
01709 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
01710 {
01711    if( lphc->wState & CBF_CAPTURE )
01712    {
01713        lphc->wState &= ~CBF_CAPTURE;
01714        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
01715        {
01716        INT index = CBUpdateLBox( lphc, TRUE );
01717        /* Update edit only if item is in the list */
01718        if(index >= 0)
01719        {
01720            lphc->wState |= CBF_NOLBSELECT;
01721            CBUpdateEdit( lphc, index );
01722            lphc->wState &= ~CBF_NOLBSELECT;
01723        }
01724        }
01725        ReleaseCapture();
01726        SetCapture(lphc->hWndLBox);
01727    }
01728 
01729    if( lphc->wState & CBF_BUTTONDOWN )
01730    {
01731        lphc->wState &= ~CBF_BUTTONDOWN;
01732        CBRepaintButton( lphc );
01733    }
01734 }
01735 
01736 /***********************************************************************
01737  *           COMBO_MouseMove
01738  *
01739  * Two things to do - track combo button and release capture when
01740  * pointer goes into the listbox.
01741  */
01742 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
01743 {
01744    POINT  pt;
01745    RECT   lbRect;
01746 
01747    pt.x = (short)LOWORD(lParam);
01748    pt.y = (short)HIWORD(lParam);
01749 
01750    if( lphc->wState & CBF_BUTTONDOWN )
01751    {
01752      BOOL bButton;
01753 
01754      bButton = PtInRect(&lphc->buttonRect, pt);
01755 
01756      if( !bButton )
01757      {
01758        lphc->wState &= ~CBF_BUTTONDOWN;
01759        CBRepaintButton( lphc );
01760      }
01761    }
01762 
01763    GetClientRect( lphc->hWndLBox, &lbRect );
01764    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
01765    if( PtInRect(&lbRect, pt) )
01766    {
01767        lphc->wState &= ~CBF_CAPTURE;
01768        ReleaseCapture();
01769        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
01770 
01771        /* hand over pointer tracking */
01772        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
01773    }
01774 }
01775 
01776 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
01777 {
01778     if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
01779         return FALSE;
01780 
01781     pcbi->rcItem = lphc->textRect;
01782     pcbi->rcButton = lphc->buttonRect;
01783     pcbi->stateButton = 0;
01784     if (lphc->wState & CBF_BUTTONDOWN)
01785         pcbi->stateButton |= STATE_SYSTEM_PRESSED;
01786     if (IsRectEmpty(&lphc->buttonRect))
01787         pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
01788     pcbi->hwndCombo = lphc->self;
01789     pcbi->hwndItem = lphc->hWndEdit;
01790     pcbi->hwndList = lphc->hWndLBox;
01791     return TRUE;
01792 }
01793 
01794 static char *strdupA(LPCSTR str)
01795 {
01796     char *ret;
01797     DWORD len;
01798 
01799     if(!str) return NULL;
01800 
01801     len = strlen(str);
01802     ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
01803     if (ret != NULL)
01804         memcpy(ret, str, len + 1);
01805     return ret;
01806 }
01807 
01808 /***********************************************************************
01809  *           ComboWndProc_common
01810  */
01811 LRESULT WINAPI ComboWndProc_common( HWND hwnd, UINT message,
01812                                   WPARAM wParam, LPARAM lParam, BOOL unicode )
01813 {
01814       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
01815 #ifdef __REACTOS__
01816       PWND pWnd;
01817 
01818       pWnd = ValidateHwnd(hwnd);
01819       if (pWnd)
01820       {
01821          if (!pWnd->fnid)
01822          {
01823             NtUserSetWindowFNID(hwnd, FNID_COMBOBOX);
01824          }
01825          else
01826          {
01827             if (pWnd->fnid != FNID_COMBOBOX)
01828             {
01829                ERR("Wrong window class for ComboBox! fnId 0x%x\n",pWnd->fnid);
01830                return 0;
01831             }
01832          }
01833       }    
01834 #endif    
01835 
01836       TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
01837             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
01838 
01839       if( lphc || message == WM_NCCREATE )
01840       switch(message)
01841       {
01842 
01843     /* System messages */
01844 
01845         case WM_NCCREATE:
01846     {
01847         LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
01848                        ((LPCREATESTRUCTA)lParam)->style;
01849                 return COMBO_NCCreate(hwnd, style);
01850     }
01851         case WM_NCDESTROY:
01852         COMBO_NCDestroy(lphc);
01853 #ifdef __REACTOS__
01854                 NtUserSetWindowFNID(hwnd, FNID_DESTROY);
01855 #endif
01856         break;/* -> DefWindowProc */
01857 
01858         case WM_CREATE:
01859     {
01860         HWND hwndParent;
01861         LONG style;
01862         if(unicode)
01863         {
01864             hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
01865             style = ((LPCREATESTRUCTW)lParam)->style;
01866         }
01867         else
01868         {
01869             hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
01870             style = ((LPCREATESTRUCTA)lParam)->style;
01871         }
01872                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
01873     }
01874 
01875         case WM_PRINTCLIENT:
01876         /* Fallthrough */
01877         case WM_PAINT:
01878         /* wParam may contain a valid HDC! */
01879         return  COMBO_Paint(lphc, (HDC)wParam);
01880 
01881     case WM_ERASEBKGND:
01882                 /* do all painting in WM_PAINT like Windows does */
01883                 return 1;
01884 
01885     case WM_GETDLGCODE:
01886     {
01887         LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
01888         if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
01889         {
01890            int vk = (int)((LPMSG)lParam)->wParam;
01891 
01892            if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
01893                result |= DLGC_WANTMESSAGE;
01894         }
01895         return  result;
01896     }
01897     case WM_WINDOWPOSCHANGING:
01898             return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
01899     case WM_WINDOWPOSCHANGED:
01900         /* SetWindowPos can be called on a Combobox to resize its Listbox.
01901          * In that case, the Combobox itself will not be resized, so we won't
01902          * get a WM_SIZE. Since we still want to update the Listbox, we have to
01903          * do it here.
01904          */
01905         /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
01906          * Z-order based painting.
01907          */
01908         /* fall through */
01909     case WM_SIZE:
01910             if( lphc->hWndLBox &&
01911           !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
01912         return  TRUE;
01913     case WM_SETFONT:
01914         COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
01915         return  TRUE;
01916     case WM_GETFONT:
01917         return  (LRESULT)lphc->hFont;
01918     case WM_SETFOCUS:
01919                if( lphc->wState & CBF_EDIT ) {
01920                    SetFocus( lphc->hWndEdit );
01921                    /* The first time focus is received, select all the text */
01922                    if( !(lphc->wState & CBF_BEENFOCUSED) ) {
01923                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
01924                        lphc->wState |= CBF_BEENFOCUSED;
01925                    }
01926                }
01927                 else
01928                     COMBO_SetFocus( lphc );
01929         return  TRUE;
01930     case WM_KILLFOCUS:
01931             {
01932                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
01933         if( !hwndFocus ||
01934             (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
01935             COMBO_KillFocus( lphc );
01936         return  TRUE;
01937             }
01938     case WM_COMMAND:
01939         return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
01940     case WM_GETTEXT:
01941             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
01942                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
01943     case WM_SETTEXT:
01944     case WM_GETTEXTLENGTH:
01945     case WM_CLEAR:
01946                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
01947                 {
01948                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
01949                     if (j == -1) return 0;
01950                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
01951                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
01952                 }
01953         else if( lphc->wState & CBF_EDIT )
01954         {
01955             LRESULT ret;
01956             lphc->wState |= CBF_NOEDITNOTIFY;
01957             ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
01958                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
01959             lphc->wState &= ~CBF_NOEDITNOTIFY;
01960             return ret;
01961         }
01962         else return CB_ERR;
01963     case WM_CUT:
01964         case WM_PASTE:
01965     case WM_COPY:
01966         if( lphc->wState & CBF_EDIT )
01967         {
01968             return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
01969                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
01970         }
01971         else return  CB_ERR;
01972 
01973     case WM_DRAWITEM:
01974     case WM_DELETEITEM:
01975     case WM_COMPAREITEM:
01976     case WM_MEASUREITEM:
01977         return COMBO_ItemOp(lphc, message, lParam);
01978     case WM_ENABLE:
01979         if( lphc->wState & CBF_EDIT )
01980             EnableWindow( lphc->hWndEdit, (BOOL)wParam );
01981         EnableWindow( lphc->hWndLBox, (BOOL)wParam );
01982 
01983         /* Force the control to repaint when the enabled state changes. */
01984         InvalidateRect(lphc->self, NULL, TRUE);
01985         return  TRUE;
01986     case WM_SETREDRAW:
01987         if( wParam )
01988             lphc->wState &= ~CBF_NOREDRAW;
01989         else
01990             lphc->wState |= CBF_NOREDRAW;
01991 
01992         if( lphc->wState & CBF_EDIT )
01993             SendMessageW(lphc->hWndEdit, message, wParam, lParam);
01994         SendMessageW(lphc->hWndLBox, message, wParam, lParam);
01995         return  0;
01996     case WM_SYSKEYDOWN:
01997         if( KF_ALTDOWN & HIWORD(lParam) ) // ReactOS (wine) KEYDATA_ALT
01998             if( wParam == VK_UP || wParam == VK_DOWN )
01999             COMBO_FlipListbox( lphc, FALSE, FALSE );
02000                 return  0;
02001 
02002     case WM_KEYDOWN:
02003         if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
02004              (lphc->wState & CBF_DROPPED))
02005         {
02006            CBRollUp( lphc, wParam == VK_RETURN, FALSE );
02007            return TRUE;
02008         }
02009                else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
02010                {
02011                   COMBO_FlipListbox( lphc, FALSE, FALSE );
02012                   return TRUE;
02013                }
02014                /* fall through */
02015     case WM_CHAR:
02016     case WM_IME_CHAR:
02017         {
02018         HWND hwndTarget;
02019 
02020         if( lphc->wState & CBF_EDIT )
02021             hwndTarget = lphc->hWndEdit;
02022         else
02023             hwndTarget = lphc->hWndLBox;
02024 
02025         return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
02026                  SendMessageA(hwndTarget, message, wParam, lParam);
02027     }
02028     case WM_LBUTTONDOWN:
02029         if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
02030         if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
02031         return  TRUE;
02032     case WM_LBUTTONUP:
02033         COMBO_LButtonUp( lphc );
02034         return  TRUE;
02035     case WM_MOUSEMOVE:
02036         if( lphc->wState & CBF_CAPTURE )
02037             COMBO_MouseMove( lphc, wParam, lParam );
02038         return  TRUE;
02039 
02040         case WM_MOUSEWHEEL:
02041                 if (wParam & (MK_SHIFT | MK_CONTROL))
02042                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
02043                      DefWindowProcA(hwnd, message, wParam, lParam);
02044 
02045                 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
02046                 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
02047                 return TRUE;
02048 
02049     /* Combo messages */
02050 
02051     case CB_ADDSTRING:
02052         if( unicode )
02053                 {
02054                     if( lphc->dwStyle & CBS_LOWERCASE )
02055                         CharLowerW((LPWSTR)lParam);
02056                     else if( lphc->dwStyle & CBS_UPPERCASE )
02057                         CharUpperW((LPWSTR)lParam);
02058                     return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
02059                 }
02060                 else /* unlike the unicode version, the ansi version does not overwrite
02061                         the string if converting case */
02062                 {
02063                     char *string = NULL;
02064                     LRESULT ret;
02065                     if( lphc->dwStyle & CBS_LOWERCASE )
02066                     {
02067                         string = strdupA((LPSTR)lParam);
02068                         CharLowerA(string);
02069                     }
02070 
02071                     else if( lphc->dwStyle & CBS_UPPERCASE )
02072                     {
02073                         string = strdupA((LPSTR)lParam);
02074                         CharUpperA(string);
02075                     }
02076 
02077                     ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
02078                     HeapFree(GetProcessHeap(), 0, string);
02079                     return ret;
02080                 }
02081     case CB_INSERTSTRING:
02082         if( unicode )
02083                 {
02084                     if( lphc->dwStyle & CBS_LOWERCASE )
02085                         CharLowerW((LPWSTR)lParam);
02086                     else if( lphc->dwStyle & CBS_UPPERCASE )
02087                         CharUpperW((LPWSTR)lParam);
02088                     return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
02089                 }
02090                 else
02091                 {
02092                     if( lphc->dwStyle & CBS_LOWERCASE )
02093                         CharLowerA((LPSTR)lParam);
02094                     else if( lphc->dwStyle & CBS_UPPERCASE )
02095                         CharUpperA((LPSTR)lParam);
02096 
02097                     return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
02098                 }
02099     case CB_DELETESTRING:
02100         return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
02101                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
02102     case CB_SELECTSTRING:
02103         return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
02104     case CB_FINDSTRING:
02105         return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
02106                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
02107     case CB_FINDSTRINGEXACT:
02108         return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
02109                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
02110     case CB_SETITEMHEIGHT:
02111         return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
02112     case CB_GETITEMHEIGHT:
02113         if( (INT)wParam >= 0 )  /* listbox item */
02114                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
02115                 return  CBGetTextAreaHeight(hwnd, lphc);
02116     case CB_RESETCONTENT:
02117         SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
02118                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
02119         {
02120             static const WCHAR empty_stringW[] = { 0 };
02121                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
02122         }
02123                 else
02124                     InvalidateRect(lphc->self, NULL, TRUE);
02125         return  TRUE;
02126     case CB_INITSTORAGE:
02127         return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
02128     case CB_GETHORIZONTALEXTENT:
02129         return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
02130     case CB_SETHORIZONTALEXTENT:
02131         return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
02132     case CB_GETTOPINDEX:
02133         return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
02134     case CB_GETLOCALE:
02135         return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
02136     case CB_SETLOCALE:
02137         return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
02138         case CB_SETDROPPEDWIDTH:
02139                 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
02140                     (INT)wParam >= 32768 )
02141                     return CB_ERR;
02142                 /* new value must be higher than combobox width */
02143                 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
02144                     lphc->droppedWidth = wParam;
02145                 else if(wParam)
02146                     lphc->droppedWidth = 0;
02147 
02148                 /* recalculate the combobox area */
02149                 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
02150 
02151                 /* fall through */
02152     case CB_GETDROPPEDWIDTH:
02153         if( lphc->droppedWidth )
02154                     return  lphc->droppedWidth;
02155         return  lphc->droppedRect.right - lphc->droppedRect.left;
02156     case CB_GETDROPPEDCONTROLRECT:
02157         if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
02158         return  CB_OKAY;
02159     case CB_GETDROPPEDSTATE:
02160         return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
02161     case CB_DIR:
02162         return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
02163                  SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
02164 
02165     case CB_SHOWDROPDOWN:
02166         if( CB_GETTYPE(lphc) != CBS_SIMPLE )
02167         {
02168             if( wParam )
02169             {
02170             if( !(lphc->wState & CBF_DROPPED) )
02171                 CBDropDown( lphc );
02172             }
02173             else
02174             if( lphc->wState & CBF_DROPPED )
02175                     CBRollUp( lphc, FALSE, TRUE );
02176         }
02177         return  TRUE;
02178     case CB_GETCOUNT:
02179         return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
02180     case CB_GETCURSEL:
02181         return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
02182     case CB_SETCURSEL:
02183         lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
02184             if( lParam >= 0 )
02185                 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
02186 
02187         /* no LBN_SELCHANGE in this case, update manually */
02188         if( lphc->wState & CBF_EDIT )
02189             CBUpdateEdit( lphc, (INT)wParam );
02190         else
02191             InvalidateRect(lphc->self, &lphc->textRect, TRUE);
02192         lphc->wState &= ~CBF_SELCHANGE;
02193             return  lParam;
02194     case CB_GETLBTEXT:
02195         return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
02196                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
02197     case CB_GETLBTEXTLEN:
02198                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
02199                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
02200     case CB_GETITEMDATA:
02201         return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
02202     case CB_SETITEMDATA:
02203         return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
02204     case CB_GETEDITSEL:
02205         /* Edit checks passed parameters itself */
02206         if( lphc->wState & CBF_EDIT )
02207             return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
02208         return  CB_ERR;
02209     case CB_SETEDITSEL:
02210         if( lphc->wState & CBF_EDIT )
02211                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
02212               (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
02213         return  CB_ERR;
02214     case CB_SETEXTENDEDUI:
02215                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
02216                     return  CB_ERR;
02217         if( wParam )
02218             lphc->wState |= CBF_EUI;
02219         else lphc->wState &= ~CBF_EUI;
02220         return  CB_OKAY;
02221     case CB_GETEXTENDEDUI:
02222         return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
02223     case CB_GETCOMBOBOXINFO:
02224         return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
02225     case CB_LIMITTEXT:
02226         if( lphc->wState & CBF_EDIT )
02227             return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
02228                 return  TRUE;
02229 
02230     case WM_UPDATEUISTATE:
02231         if (unicode)
02232             DefWindowProcW(lphc->self, message, wParam, lParam);
02233         else
02234             DefWindowProcA(lphc->self, message, wParam, lParam);
02235 
02236         if (COMBO_update_uistate(lphc))
02237         {
02238            /* redraw text */
02239            if( !(lphc->wState & CBF_EDIT) )
02240                 NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
02241         }
02242         break;
02243 
02244     default:
02245         if (message >= WM_USER)
02246             WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
02247             message - WM_USER, wParam, lParam );
02248         break;
02249       }
02250       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
02251                        DefWindowProcA(hwnd, message, wParam, lParam);
02252 }
02253 
02254 /***********************************************************************
02255  *           ComboWndProcA
02256  *
02257  * This is just a wrapper for the real ComboWndProc which locks/unlocks
02258  * window structs.
02259  */
02260 LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
02261 {
02262     if (!IsWindow(hwnd)) return 0;
02263     return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
02264 }
02265 
02266 /***********************************************************************
02267  *           ComboWndProcW
02268  */
02269 LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
02270 {
02271     if (!IsWindow(hwnd)) return 0;
02272     return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
02273 }
02274 
02275 /*************************************************************************
02276  *           GetComboBoxInfo   (USER32.@)
02277  */
02278 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
02279                 PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
02280 {
02281     TRACE("(%p, %p)\n", hwndCombo, pcbi);
02282 #ifndef __REACTOS__
02283     return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
02284 #else
02285     return NtUserGetComboBoxInfo(hwndCombo, pcbi);
02286 #endif
02287 }

Generated on Sat May 26 2012 04:37:28 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.