Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygencombo.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
1.7.6.1
|