Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenlistbox.c
Go to the documentation of this file.
00001 /* 00002 * Listbox controls 00003 * 00004 * Copyright 1996 Alexandre Julliard 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. 9, 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 * - GetListBoxInfo() 00031 * - LB_GETLISTBOXINFO 00032 * - LBS_NODATA 00033 */ 00034 00035 #include <user32.h> 00036 00037 #include <wine/debug.h> 00038 00039 WINE_DEFAULT_DEBUG_CHANNEL(listbox); 00040 00041 /* Items array granularity */ 00042 #define LB_ARRAY_GRANULARITY 16 00043 00044 /* Scrolling timeout in ms */ 00045 #define LB_SCROLL_TIMEOUT 50 00046 00047 /* Listbox system timer id */ 00048 #define LB_TIMER_ID 2 00049 00050 /* flag listbox changed while setredraw false - internal style */ 00051 #define LBS_DISPLAYCHANGED 0x80000000 00052 00053 /* Item structure */ 00054 typedef struct 00055 { 00056 LPWSTR str; /* Item text */ 00057 BOOL selected; /* Is item selected? */ 00058 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */ 00059 ULONG_PTR data; /* User data */ 00060 } LB_ITEMDATA; 00061 00062 /* Listbox structure */ 00063 typedef struct 00064 { 00065 HWND self; /* Our own window handle */ 00066 HWND owner; /* Owner window to send notifications to */ 00067 UINT style; /* Window style */ 00068 INT width; /* Window width */ 00069 INT height; /* Window height */ 00070 LB_ITEMDATA *items; /* Array of items */ 00071 INT nb_items; /* Number of items */ 00072 INT top_item; /* Top visible item */ 00073 INT selected_item; /* Selected item */ 00074 INT focus_item; /* Item that has the focus */ 00075 INT anchor_item; /* Anchor item for extended selection */ 00076 INT item_height; /* Default item height */ 00077 INT page_size; /* Items per listbox page */ 00078 INT column_width; /* Column width for multi-column listboxes */ 00079 INT horz_extent; /* Horizontal extent (0 if no hscroll) */ 00080 INT horz_pos; /* Horizontal position */ 00081 INT nb_tabs; /* Number of tabs in array */ 00082 INT *tabs; /* Array of tabs */ 00083 INT avg_char_width; /* Average width of characters */ 00084 BOOL caret_on; /* Is caret on? */ 00085 BOOL captured; /* Is mouse captured? */ 00086 BOOL in_focus; 00087 HFONT font; /* Current font */ 00088 LCID locale; /* Current locale for string comparisons */ 00089 LPHEADCOMBO lphc; /* ComboLBox */ 00090 LONG UIState; // REACTOS 00091 } LB_DESCR; 00092 00093 00094 #define IS_OWNERDRAW(descr) \ 00095 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) 00096 00097 #define HAS_STRINGS(descr) \ 00098 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS)) 00099 00100 00101 #define IS_MULTISELECT(descr) \ 00102 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \ 00103 !((descr)->style & LBS_NOSEL)) 00104 00105 #define SEND_NOTIFICATION(descr,code) \ 00106 (SendMessageW( (descr)->owner, WM_COMMAND, \ 00107 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) )) 00108 00109 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03) 00110 00111 /* Current timer status */ 00112 typedef enum 00113 { 00114 LB_TIMER_NONE, 00115 LB_TIMER_UP, 00116 LB_TIMER_LEFT, 00117 LB_TIMER_DOWN, 00118 LB_TIMER_RIGHT 00119 } TIMER_DIRECTION; 00120 00121 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE; 00122 00123 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect ); 00124 00125 /********************************************************************* 00126 * listbox class descriptor 00127 */ 00128 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0}; 00129 const struct builtin_class_descr LISTBOX_builtin_class = 00130 { 00131 listboxW, /* name */ 00132 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */ 00133 ListBoxWndProcA, /* procA */ 00134 ListBoxWndProcW, /* procW */ 00135 sizeof(LB_DESCR *), /* extra */ 00136 IDC_ARROW, /* cursor */ 00137 0 /* brush */ 00138 }; 00139 00140 00141 /********************************************************************* 00142 * combolbox class descriptor 00143 */ 00144 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0}; 00145 const struct builtin_class_descr COMBOLBOX_builtin_class = 00146 { 00147 combolboxW, /* name */ 00148 CS_DBLCLKS | CS_SAVEBITS, /* style */ 00149 ListBoxWndProcA, /* procA */ 00150 ListBoxWndProcW, /* procW */ 00151 sizeof(LB_DESCR *), /* extra */ 00152 IDC_ARROW, /* cursor */ 00153 0 /* brush */ 00154 }; 00155 00156 00157 /*********************************************************************** 00158 * LISTBOX_GetCurrentPageSize 00159 * 00160 * Return the current page size 00161 */ 00162 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr ) 00163 { 00164 INT i, height; 00165 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size; 00166 for (i = descr->top_item, height = 0; i < descr->nb_items; i++) 00167 { 00168 if ((height += descr->items[i].height) > descr->height) break; 00169 } 00170 if (i == descr->top_item) return 1; 00171 else return i - descr->top_item; 00172 } 00173 00174 00175 /*********************************************************************** 00176 * LISTBOX_GetMaxTopIndex 00177 * 00178 * Return the maximum possible index for the top of the listbox. 00179 */ 00180 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr ) 00181 { 00182 INT max, page; 00183 00184 if (descr->style & LBS_OWNERDRAWVARIABLE) 00185 { 00186 page = descr->height; 00187 for (max = descr->nb_items - 1; max >= 0; max--) 00188 if ((page -= descr->items[max].height) < 0) break; 00189 if (max < descr->nb_items - 1) max++; 00190 } 00191 else if (descr->style & LBS_MULTICOLUMN) 00192 { 00193 if ((page = descr->width / descr->column_width) < 1) page = 1; 00194 max = (descr->nb_items + descr->page_size - 1) / descr->page_size; 00195 max = (max - page) * descr->page_size; 00196 } 00197 else 00198 { 00199 max = descr->nb_items - descr->page_size; 00200 } 00201 if (max < 0) max = 0; 00202 return max; 00203 } 00204 00205 00206 /*********************************************************************** 00207 * LISTBOX_UpdateScroll 00208 * 00209 * Update the scrollbars. Should be called whenever the content 00210 * of the listbox changes. 00211 */ 00212 static void LISTBOX_UpdateScroll( LB_DESCR *descr ) 00213 { 00214 SCROLLINFO info; 00215 00216 /* Check the listbox scroll bar flags individually before we call 00217 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and 00218 no WS_VSCROLL, we end up with an uninitialized, visible horizontal 00219 scroll bar when we do not need one. 00220 if (!(descr->style & WS_VSCROLL)) return; 00221 */ 00222 00223 /* It is important that we check descr->style, and not wnd->dwStyle, 00224 for WS_VSCROLL, as the former is exactly the one passed in 00225 argument to CreateWindow. 00226 In Windows (and from now on in Wine :) a listbox created 00227 with such a style (no WS_SCROLL) does not update 00228 the scrollbar with listbox-related data, thus letting 00229 the programmer use it for his/her own purposes. */ 00230 00231 if (descr->style & LBS_NOREDRAW) return; 00232 info.cbSize = sizeof(info); 00233 00234 if (descr->style & LBS_MULTICOLUMN) 00235 { 00236 info.nMin = 0; 00237 info.nMax = (descr->nb_items - 1) / descr->page_size; 00238 info.nPos = descr->top_item / descr->page_size; 00239 info.nPage = descr->width / descr->column_width; 00240 if (info.nPage < 1) info.nPage = 1; 00241 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; 00242 if (descr->style & LBS_DISABLENOSCROLL) 00243 info.fMask |= SIF_DISABLENOSCROLL; 00244 if (descr->style & WS_HSCROLL) 00245 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); 00246 info.nMax = 0; 00247 info.fMask = SIF_RANGE; 00248 if (descr->style & WS_VSCROLL) 00249 SetScrollInfo( descr->self, SB_VERT, &info, TRUE ); 00250 } 00251 else 00252 { 00253 info.nMin = 0; 00254 info.nMax = descr->nb_items - 1; 00255 info.nPos = descr->top_item; 00256 info.nPage = LISTBOX_GetCurrentPageSize( descr ); 00257 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; 00258 if (descr->style & LBS_DISABLENOSCROLL) 00259 info.fMask |= SIF_DISABLENOSCROLL; 00260 if (descr->style & WS_VSCROLL) 00261 SetScrollInfo( descr->self, SB_VERT, &info, TRUE ); 00262 00263 if (descr->horz_extent) 00264 { 00265 info.nMin = 0; 00266 info.nMax = descr->horz_extent - 1; 00267 info.nPos = descr->horz_pos; 00268 info.nPage = descr->width; 00269 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; 00270 if (descr->style & LBS_DISABLENOSCROLL) 00271 info.fMask |= SIF_DISABLENOSCROLL; 00272 if (descr->style & WS_HSCROLL) 00273 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); 00274 } 00275 } 00276 } 00277 00278 00279 /*********************************************************************** 00280 * LISTBOX_SetTopItem 00281 * 00282 * Set the top item of the listbox, scrolling up or down if necessary. 00283 */ 00284 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll ) 00285 { 00286 INT max = LISTBOX_GetMaxTopIndex( descr ); 00287 00288 TRACE("setting top item %d, scroll %d\n", index, scroll); 00289 00290 if (index > max) index = max; 00291 if (index < 0) index = 0; 00292 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size; 00293 if (descr->top_item == index) return LB_OKAY; 00294 if (descr->style & LBS_MULTICOLUMN) 00295 { 00296 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width; 00297 if (scroll && (abs(diff) < descr->width)) 00298 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL, 00299 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 00300 00301 else 00302 scroll = FALSE; 00303 } 00304 else if (scroll) 00305 { 00306 INT diff; 00307 if (descr->style & LBS_OWNERDRAWVARIABLE) 00308 { 00309 INT i; 00310 diff = 0; 00311 if (index > descr->top_item) 00312 { 00313 for (i = index - 1; i >= descr->top_item; i--) 00314 diff -= descr->items[i].height; 00315 } 00316 else 00317 { 00318 for (i = index; i < descr->top_item; i++) 00319 diff += descr->items[i].height; 00320 } 00321 } 00322 else 00323 diff = (descr->top_item - index) * descr->item_height; 00324 00325 if (abs(diff) < descr->height) 00326 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL, 00327 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 00328 else 00329 scroll = FALSE; 00330 } 00331 if (!scroll) InvalidateRect( descr->self, NULL, TRUE ); 00332 descr->top_item = index; 00333 LISTBOX_UpdateScroll( descr ); 00334 return LB_OKAY; 00335 } 00336 00337 00338 /*********************************************************************** 00339 * LISTBOX_UpdatePage 00340 * 00341 * Update the page size. Should be called when the size of 00342 * the client area or the item height changes. 00343 */ 00344 static void LISTBOX_UpdatePage( LB_DESCR *descr ) 00345 { 00346 INT page_size; 00347 00348 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1) 00349 page_size = 1; 00350 if (page_size == descr->page_size) return; 00351 descr->page_size = page_size; 00352 if (descr->style & LBS_MULTICOLUMN) 00353 InvalidateRect( descr->self, NULL, TRUE ); 00354 LISTBOX_SetTopItem( descr, descr->top_item, FALSE ); 00355 } 00356 00357 00358 /*********************************************************************** 00359 * LISTBOX_UpdateSize 00360 * 00361 * Update the size of the listbox. Should be called when the size of 00362 * the client area changes. 00363 */ 00364 static void LISTBOX_UpdateSize( LB_DESCR *descr ) 00365 { 00366 RECT rect; 00367 00368 GetClientRect( descr->self, &rect ); 00369 descr->width = rect.right - rect.left; 00370 descr->height = rect.bottom - rect.top; 00371 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE)) 00372 { 00373 INT remaining; 00374 RECT rect; 00375 00376 GetWindowRect( descr->self, &rect ); 00377 if(descr->item_height != 0) 00378 remaining = descr->height % descr->item_height; 00379 else 00380 remaining = 0; 00381 if ((descr->height > descr->item_height) && remaining) 00382 { 00383 TRACE("[%p]: changing height %d -> %d\n", 00384 descr->self, descr->height, descr->height - remaining ); 00385 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left, 00386 rect.bottom - rect.top - remaining, 00387 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE ); 00388 return; 00389 } 00390 } 00391 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height ); 00392 LISTBOX_UpdatePage( descr ); 00393 LISTBOX_UpdateScroll( descr ); 00394 00395 /* Invalidate the focused item so it will be repainted correctly */ 00396 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1) 00397 { 00398 InvalidateRect( descr->self, &rect, FALSE ); 00399 } 00400 } 00401 00402 00403 /*********************************************************************** 00404 * LISTBOX_GetItemRect 00405 * 00406 * Get the rectangle enclosing an item, in listbox client coordinates. 00407 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error. 00408 */ 00409 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect ) 00410 { 00411 /* Index <= 0 is legal even on empty listboxes */ 00412 if (index && (index >= descr->nb_items)) 00413 { 00414 memset(rect, 0, sizeof(*rect)); 00415 SetLastError(ERROR_INVALID_INDEX); 00416 return LB_ERR; 00417 } 00418 SetRect( rect, 0, 0, descr->width, descr->height ); 00419 if (descr->style & LBS_MULTICOLUMN) 00420 { 00421 INT col = (index / descr->page_size) - 00422 (descr->top_item / descr->page_size); 00423 rect->left += col * descr->column_width; 00424 rect->right = rect->left + descr->column_width; 00425 rect->top += (index % descr->page_size) * descr->item_height; 00426 rect->bottom = rect->top + descr->item_height; 00427 } 00428 else if (descr->style & LBS_OWNERDRAWVARIABLE) 00429 { 00430 INT i; 00431 rect->right += descr->horz_pos; 00432 if ((index >= 0) && (index < descr->nb_items)) 00433 { 00434 if (index < descr->top_item) 00435 { 00436 for (i = descr->top_item-1; i >= index; i--) 00437 rect->top -= descr->items[i].height; 00438 } 00439 else 00440 { 00441 for (i = descr->top_item; i < index; i++) 00442 rect->top += descr->items[i].height; 00443 } 00444 rect->bottom = rect->top + descr->items[index].height; 00445 00446 } 00447 } 00448 else 00449 { 00450 rect->top += (index - descr->top_item) * descr->item_height; 00451 rect->bottom = rect->top + descr->item_height; 00452 rect->right += descr->horz_pos; 00453 } 00454 00455 TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect)); 00456 00457 return ((rect->left < descr->width) && (rect->right > 0) && 00458 (rect->top < descr->height) && (rect->bottom > 0)); 00459 } 00460 00461 00462 /*********************************************************************** 00463 * LISTBOX_GetItemFromPoint 00464 * 00465 * Return the item nearest from point (x,y) (in client coordinates). 00466 */ 00467 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y ) 00468 { 00469 INT index = descr->top_item; 00470 00471 if (!descr->nb_items) return -1; /* No items */ 00472 if (descr->style & LBS_OWNERDRAWVARIABLE) 00473 { 00474 INT pos = 0; 00475 if (y >= 0) 00476 { 00477 while (index < descr->nb_items) 00478 { 00479 if ((pos += descr->items[index].height) > y) break; 00480 index++; 00481 } 00482 } 00483 else 00484 { 00485 while (index > 0) 00486 { 00487 index--; 00488 if ((pos -= descr->items[index].height) <= y) break; 00489 } 00490 } 00491 } 00492 else if (descr->style & LBS_MULTICOLUMN) 00493 { 00494 if (y >= descr->item_height * descr->page_size) return -1; 00495 if (y >= 0) index += y / descr->item_height; 00496 if (x >= 0) index += (x / descr->column_width) * descr->page_size; 00497 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size; 00498 } 00499 else 00500 { 00501 index += (y / descr->item_height); 00502 } 00503 if (index < 0) return 0; 00504 if (index >= descr->nb_items) return -1; 00505 return index; 00506 } 00507 00508 00509 /*********************************************************************** 00510 * LISTBOX_PaintItem 00511 * 00512 * Paint an item. 00513 */ 00514 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, 00515 INT index, UINT action, BOOL ignoreFocus ) 00516 { 00517 LB_ITEMDATA *item = NULL; 00518 if (index < descr->nb_items) item = &descr->items[index]; 00519 00520 if (IS_OWNERDRAW(descr)) 00521 { 00522 DRAWITEMSTRUCT dis; 00523 RECT r; 00524 HRGN hrgn; 00525 00526 if (!item) 00527 { 00528 if (action == ODA_FOCUS) 00529 { // REACTOS 00530 if (!(descr->UIState & UISF_HIDEFOCUS)) 00531 DrawFocusRect( hdc, rect ); 00532 } // 00533 else 00534 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items); 00535 return; 00536 } 00537 00538 /* some programs mess with the clipping region when 00539 drawing the item, *and* restore the previous region 00540 after they are done, so a region has better to exist 00541 else everything ends clipped */ 00542 GetClientRect(descr->self, &r); 00543 hrgn = set_control_clipping( hdc, &r ); 00544 00545 dis.CtlType = ODT_LISTBOX; 00546 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID ); 00547 dis.hwndItem = descr->self; 00548 dis.itemAction = action; 00549 dis.hDC = hdc; 00550 dis.itemID = index; 00551 dis.itemState = 0; 00552 if (item->selected) dis.itemState |= ODS_SELECTED; 00553 if (!ignoreFocus && (descr->focus_item == index) && 00554 (descr->caret_on) && 00555 (descr->in_focus)) dis.itemState |= ODS_FOCUS; 00556 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED; 00557 dis.itemData = item->data; 00558 dis.rcItem = *rect; 00559 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n", 00560 descr->self, index, item ? debugstr_w(item->str) : "", action, 00561 dis.itemState, wine_dbgstr_rect(rect) ); 00562 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); 00563 SelectClipRgn( hdc, hrgn ); 00564 if (hrgn) DeleteObject( hrgn ); 00565 } 00566 else 00567 { 00568 COLORREF oldText = 0, oldBk = 0; 00569 00570 if (action == ODA_FOCUS) 00571 { 00572 if (!(descr->UIState & UISF_HIDEFOCUS)) // REACTOS 00573 DrawFocusRect( hdc, rect ); 00574 return; 00575 } 00576 if (item && item->selected) 00577 { 00578 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); 00579 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 00580 } 00581 00582 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n", 00583 descr->self, index, item ? debugstr_w(item->str) : "", action, 00584 wine_dbgstr_rect(rect) ); 00585 if (!item) 00586 ExtTextOutW( hdc, rect->left + 1, rect->top, 00587 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); 00588 else if (!(descr->style & LBS_USETABSTOPS)) 00589 ExtTextOutW( hdc, rect->left + 1, rect->top, 00590 ETO_OPAQUE | ETO_CLIPPED, rect, item->str, 00591 strlenW(item->str), NULL ); 00592 else 00593 { 00594 /* Output empty string to paint background in the full width. */ 00595 ExtTextOutW( hdc, rect->left + 1, rect->top, 00596 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); 00597 TabbedTextOutW( hdc, rect->left + 1 , rect->top, 00598 item->str, strlenW(item->str), 00599 descr->nb_tabs, descr->tabs, 0); 00600 } 00601 if (item && item->selected) 00602 { 00603 SetBkColor( hdc, oldBk ); 00604 SetTextColor( hdc, oldText ); 00605 } 00606 if (!ignoreFocus && (descr->focus_item == index) && 00607 (descr->caret_on) && 00608 (descr->in_focus) && 00609 !(descr->UIState & UISF_HIDEFOCUS)) DrawFocusRect( hdc, rect ); 00610 } 00611 } 00612 00613 00614 /*********************************************************************** 00615 * LISTBOX_SetRedraw 00616 * 00617 * Change the redraw flag. 00618 */ 00619 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on ) 00620 { 00621 if (on) 00622 { 00623 if (!(descr->style & LBS_NOREDRAW)) return; 00624 descr->style &= ~LBS_NOREDRAW; 00625 if (descr->style & LBS_DISPLAYCHANGED) 00626 { /* page was changed while setredraw false, refresh automatically */ 00627 InvalidateRect(descr->self, NULL, TRUE); 00628 if ((descr->top_item + descr->page_size) > descr->nb_items) 00629 { /* reset top of page if less than number of items/page */ 00630 descr->top_item = descr->nb_items - descr->page_size; 00631 if (descr->top_item < 0) descr->top_item = 0; 00632 } 00633 descr->style &= ~LBS_DISPLAYCHANGED; 00634 } 00635 LISTBOX_UpdateScroll( descr ); 00636 } 00637 else descr->style |= LBS_NOREDRAW; 00638 } 00639 00640 00641 /*********************************************************************** 00642 * LISTBOX_RepaintItem 00643 * 00644 * Repaint a single item synchronously. 00645 */ 00646 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action ) 00647 { 00648 HDC hdc; 00649 RECT rect; 00650 HFONT oldFont = 0; 00651 HBRUSH hbrush, oldBrush = 0; 00652 00653 /* Do not repaint the item if the item is not visible */ 00654 if (!IsWindowVisible(descr->self)) return; 00655 if (descr->style & LBS_NOREDRAW) 00656 { 00657 descr->style |= LBS_DISPLAYCHANGED; 00658 return; 00659 } 00660 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return; 00661 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return; 00662 if (descr->font) oldFont = SelectObject( hdc, descr->font ); 00663 hbrush = GetControlColor( descr->owner, descr->self, hdc, WM_CTLCOLORLISTBOX); 00664 if (hbrush) oldBrush = SelectObject( hdc, hbrush ); 00665 if (!IsWindowEnabled(descr->self)) 00666 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); 00667 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); 00668 LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE ); 00669 if (oldFont) SelectObject( hdc, oldFont ); 00670 if (oldBrush) SelectObject( hdc, oldBrush ); 00671 ReleaseDC( descr->self, hdc ); 00672 } 00673 00674 00675 /*********************************************************************** 00676 * LISTBOX_DrawFocusRect 00677 */ 00678 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on ) 00679 { 00680 HDC hdc; 00681 RECT rect; 00682 HFONT oldFont = 0; 00683 00684 /* Do not repaint the item if the item is not visible */ 00685 if (!IsWindowVisible(descr->self)) return; 00686 00687 if (descr->focus_item == -1) return; 00688 if (!descr->caret_on || !descr->in_focus) return; 00689 00690 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return; 00691 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return; 00692 if (descr->font) oldFont = SelectObject( hdc, descr->font ); 00693 if (!IsWindowEnabled(descr->self)) 00694 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); 00695 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); 00696 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE ); 00697 if (oldFont) SelectObject( hdc, oldFont ); 00698 ReleaseDC( descr->self, hdc ); 00699 } 00700 00701 00702 /*********************************************************************** 00703 * LISTBOX_InitStorage 00704 */ 00705 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items ) 00706 { 00707 LB_ITEMDATA *item; 00708 00709 nb_items += LB_ARRAY_GRANULARITY - 1; 00710 nb_items -= (nb_items % LB_ARRAY_GRANULARITY); 00711 if (descr->items) { 00712 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); 00713 item = HeapReAlloc( GetProcessHeap(), 0, descr->items, 00714 nb_items * sizeof(LB_ITEMDATA)); 00715 } 00716 else { 00717 item = HeapAlloc( GetProcessHeap(), 0, 00718 nb_items * sizeof(LB_ITEMDATA)); 00719 } 00720 00721 if (!item) 00722 { 00723 SEND_NOTIFICATION( descr, LBN_ERRSPACE ); 00724 return LB_ERRSPACE; 00725 } 00726 descr->items = item; 00727 return LB_OKAY; 00728 } 00729 00730 00731 /*********************************************************************** 00732 * LISTBOX_SetTabStops 00733 */ 00734 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs ) 00735 { 00736 INT i; 00737 00738 if (!(descr->style & LBS_USETABSTOPS)) 00739 { 00740 SetLastError(ERROR_LB_WITHOUT_TABSTOPS); 00741 return FALSE; 00742 } 00743 00744 HeapFree( GetProcessHeap(), 0, descr->tabs ); 00745 if (!(descr->nb_tabs = count)) 00746 { 00747 descr->tabs = NULL; 00748 return TRUE; 00749 } 00750 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0, 00751 descr->nb_tabs * sizeof(INT) ))) 00752 return FALSE; 00753 memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) ); 00754 00755 /* convert into "dialog units"*/ 00756 for (i = 0; i < descr->nb_tabs; i++) 00757 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4); 00758 00759 return TRUE; 00760 } 00761 00762 00763 /*********************************************************************** 00764 * LISTBOX_GetText 00765 */ 00766 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode ) 00767 { 00768 DWORD len; 00769 00770 if ((index < 0) || (index >= descr->nb_items)) 00771 { 00772 SetLastError(ERROR_INVALID_INDEX); 00773 return LB_ERR; 00774 } 00775 if (HAS_STRINGS(descr)) 00776 { 00777 if (!buffer) 00778 { 00779 len = strlenW(descr->items[index].str); 00780 if( unicode ) 00781 return len; 00782 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len, 00783 NULL, 0, NULL, NULL ); 00784 } 00785 00786 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str)); 00787 00788 _SEH2_TRY /* hide a Delphi bug that passes a read-only buffer */ 00789 { 00790 if(unicode) 00791 { 00792 strcpyW( buffer, descr->items[index].str ); 00793 len = strlenW(buffer); 00794 } 00795 else 00796 { 00797 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, 00798 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1; 00799 } 00800 } 00801 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 00802 { 00803 WARN( "got an invalid buffer (Delphi bug?)\n" ); 00804 SetLastError( ERROR_INVALID_PARAMETER ); 00805 len = LB_ERR; 00806 } 00807 _SEH2_END 00808 } else { 00809 if (buffer) 00810 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data); 00811 len = sizeof(DWORD); 00812 } 00813 return len; 00814 } 00815 00816 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 ) 00817 { 00818 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 ); 00819 if (ret == CSTR_LESS_THAN) 00820 return -1; 00821 if (ret == CSTR_EQUAL) 00822 return 0; 00823 if (ret == CSTR_GREATER_THAN) 00824 return 1; 00825 return -1; 00826 } 00827 00828 /*********************************************************************** 00829 * LISTBOX_FindStringPos 00830 * 00831 * Find the nearest string located before a given string in sort order. 00832 * If 'exact' is TRUE, return an error if we don't get an exact match. 00833 */ 00834 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact ) 00835 { 00836 INT index, min, max, res = -1; 00837 00838 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */ 00839 min = 0; 00840 max = descr->nb_items; 00841 while (min != max) 00842 { 00843 index = (min + max) / 2; 00844 if (HAS_STRINGS(descr)) 00845 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str); 00846 else 00847 { 00848 COMPAREITEMSTRUCT cis; 00849 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 00850 00851 cis.CtlType = ODT_LISTBOX; 00852 cis.CtlID = id; 00853 cis.hwndItem = descr->self; 00854 /* note that some application (MetaStock) expects the second item 00855 * to be in the listbox */ 00856 cis.itemID1 = -1; 00857 cis.itemData1 = (ULONG_PTR)str; 00858 cis.itemID2 = index; 00859 cis.itemData2 = descr->items[index].data; 00860 cis.dwLocaleId = descr->locale; 00861 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis ); 00862 } 00863 if (!res) return index; 00864 if (res < 0) max = index; 00865 else min = index + 1; 00866 } 00867 return exact ? -1 : max; 00868 } 00869 00870 00871 /*********************************************************************** 00872 * LISTBOX_FindFileStrPos 00873 * 00874 * Find the nearest string located before a given string in directory 00875 * sort order (i.e. first files, then directories, then drives). 00876 */ 00877 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str ) 00878 { 00879 INT min, max, res = -1; 00880 00881 if (!HAS_STRINGS(descr)) 00882 return LISTBOX_FindStringPos( descr, str, FALSE ); 00883 min = 0; 00884 max = descr->nb_items; 00885 while (min != max) 00886 { 00887 INT index = (min + max) / 2; 00888 LPCWSTR p = descr->items[index].str; 00889 if (*p == '[') /* drive or directory */ 00890 { 00891 if (*str != '[') res = -1; 00892 else if (p[1] == '-') /* drive */ 00893 { 00894 if (str[1] == '-') res = str[2] - p[2]; 00895 else res = -1; 00896 } 00897 else /* directory */ 00898 { 00899 if (str[1] == '-') res = 1; 00900 else res = LISTBOX_lstrcmpiW( descr->locale, str, p ); 00901 } 00902 } 00903 else /* filename */ 00904 { 00905 if (*str == '[') res = 1; 00906 else res = LISTBOX_lstrcmpiW( descr->locale, str, p ); 00907 } 00908 if (!res) return index; 00909 if (res < 0) max = index; 00910 else min = index + 1; 00911 } 00912 return max; 00913 } 00914 00915 00916 /*********************************************************************** 00917 * LISTBOX_FindString 00918 * 00919 * Find the item beginning with a given string. 00920 */ 00921 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact ) 00922 { 00923 INT i; 00924 LB_ITEMDATA *item; 00925 00926 if (start >= descr->nb_items) start = -1; 00927 item = descr->items + start + 1; 00928 if (HAS_STRINGS(descr)) 00929 { 00930 if (!str || ! str[0] ) return LB_ERR; 00931 if (exact) 00932 { 00933 for (i = start + 1; i < descr->nb_items; i++, item++) 00934 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i; 00935 for (i = 0, item = descr->items; i <= start; i++, item++) 00936 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i; 00937 } 00938 else 00939 { 00940 /* Special case for drives and directories: ignore prefix */ 00941 #define CHECK_DRIVE(item) \ 00942 if ((item)->str[0] == '[') \ 00943 { \ 00944 if (!strncmpiW( str, (item)->str+1, len )) return i; \ 00945 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \ 00946 return i; \ 00947 } 00948 00949 INT len = strlenW(str); 00950 for (i = start + 1; i < descr->nb_items; i++, item++) 00951 { 00952 if (!strncmpiW( str, item->str, len )) return i; 00953 CHECK_DRIVE(item); 00954 } 00955 for (i = 0, item = descr->items; i <= start; i++, item++) 00956 { 00957 if (!strncmpiW( str, item->str, len )) return i; 00958 CHECK_DRIVE(item); 00959 } 00960 #undef CHECK_DRIVE 00961 } 00962 } 00963 else 00964 { 00965 if (exact && (descr->style & LBS_SORT)) 00966 /* If sorted, use a WM_COMPAREITEM binary search */ 00967 return LISTBOX_FindStringPos( descr, str, TRUE ); 00968 00969 /* Otherwise use a linear search */ 00970 for (i = start + 1; i < descr->nb_items; i++, item++) 00971 if (item->data == (ULONG_PTR)str) return i; 00972 for (i = 0, item = descr->items; i <= start; i++, item++) 00973 if (item->data == (ULONG_PTR)str) return i; 00974 } 00975 return LB_ERR; 00976 } 00977 00978 00979 /*********************************************************************** 00980 * LISTBOX_GetSelCount 00981 */ 00982 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr ) 00983 { 00984 INT i, count; 00985 const LB_ITEMDATA *item = descr->items; 00986 00987 if (!(descr->style & LBS_MULTIPLESEL) || 00988 (descr->style & LBS_NOSEL)) 00989 return LB_ERR; 00990 for (i = count = 0; i < descr->nb_items; i++, item++) 00991 if (item->selected) count++; 00992 return count; 00993 } 00994 00995 00996 /*********************************************************************** 00997 * LISTBOX_GetSelItems 00998 */ 00999 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array ) 01000 { 01001 INT i, count; 01002 const LB_ITEMDATA *item = descr->items; 01003 01004 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; 01005 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++) 01006 if (item->selected) array[count++] = i; 01007 return count; 01008 } 01009 01010 01011 /*********************************************************************** 01012 * LISTBOX_Paint 01013 */ 01014 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc ) 01015 { 01016 INT i, col_pos = descr->page_size - 1; 01017 RECT rect; 01018 RECT focusRect = {-1, -1, -1, -1}; 01019 HFONT oldFont = 0; 01020 HBRUSH hbrush, oldBrush = 0; 01021 01022 if (descr->style & LBS_NOREDRAW) return 0; 01023 01024 SetRect( &rect, 0, 0, descr->width, descr->height ); 01025 if (descr->style & LBS_MULTICOLUMN) 01026 rect.right = rect.left + descr->column_width; 01027 else if (descr->horz_pos) 01028 { 01029 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); 01030 rect.right += descr->horz_pos; 01031 } 01032 01033 if (descr->font) oldFont = SelectObject( hdc, descr->font ); 01034 hbrush = GetControlColor( descr->owner, descr->self, hdc, WM_CTLCOLORLISTBOX); 01035 if (hbrush) oldBrush = SelectObject( hdc, hbrush ); 01036 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); 01037 01038 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on && 01039 (descr->in_focus)) 01040 { 01041 /* Special case for empty listbox: paint focus rect */ 01042 rect.bottom = rect.top + descr->item_height; 01043 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 01044 &rect, NULL, 0, NULL ); 01045 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE ); 01046 rect.top = rect.bottom; 01047 } 01048 01049 /* Paint all the item, regarding the selection 01050 Focus state will be painted after */ 01051 01052 for (i = descr->top_item; i < descr->nb_items; i++) 01053 { 01054 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) 01055 rect.bottom = rect.top + descr->item_height; 01056 else 01057 rect.bottom = rect.top + descr->items[i].height; 01058 01059 if (i == descr->focus_item) 01060 { 01061 /* keep the focus rect, to paint the focus item after */ 01062 focusRect.left = rect.left; 01063 focusRect.right = rect.right; 01064 focusRect.top = rect.top; 01065 focusRect.bottom = rect.bottom; 01066 } 01067 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE ); 01068 rect.top = rect.bottom; 01069 01070 if ((descr->style & LBS_MULTICOLUMN) && !col_pos) 01071 { 01072 if (!IS_OWNERDRAW(descr)) 01073 { 01074 /* Clear the bottom of the column */ 01075 if (rect.top < descr->height) 01076 { 01077 rect.bottom = descr->height; 01078 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 01079 &rect, NULL, 0, NULL ); 01080 } 01081 } 01082 01083 /* Go to the next column */ 01084 rect.left += descr->column_width; 01085 rect.right += descr->column_width; 01086 rect.top = 0; 01087 col_pos = descr->page_size - 1; 01088 } 01089 else 01090 { 01091 col_pos--; 01092 if (rect.top >= descr->height) break; 01093 } 01094 } 01095 01096 /* Paint the focus item now */ 01097 if (focusRect.top != focusRect.bottom && 01098 descr->caret_on && descr->in_focus) 01099 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE ); 01100 01101 if (!IS_OWNERDRAW(descr)) 01102 { 01103 /* Clear the remainder of the client area */ 01104 if (rect.top < descr->height) 01105 { 01106 rect.bottom = descr->height; 01107 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 01108 &rect, NULL, 0, NULL ); 01109 } 01110 if (rect.right < descr->width) 01111 { 01112 rect.left = rect.right; 01113 rect.right = descr->width; 01114 rect.top = 0; 01115 rect.bottom = descr->height; 01116 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 01117 &rect, NULL, 0, NULL ); 01118 } 01119 } 01120 if (oldFont) SelectObject( hdc, oldFont ); 01121 if (oldBrush) SelectObject( hdc, oldBrush ); 01122 return 0; 01123 } 01124 01125 01126 /*********************************************************************** 01127 * LISTBOX_InvalidateItems 01128 * 01129 * Invalidate all items from a given item. If the specified item is not 01130 * visible, nothing happens. 01131 */ 01132 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index ) 01133 { 01134 RECT rect; 01135 01136 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1) 01137 { 01138 if (descr->style & LBS_NOREDRAW) 01139 { 01140 descr->style |= LBS_DISPLAYCHANGED; 01141 return; 01142 } 01143 rect.bottom = descr->height; 01144 InvalidateRect( descr->self, &rect, TRUE ); 01145 if (descr->style & LBS_MULTICOLUMN) 01146 { 01147 /* Repaint the other columns */ 01148 rect.left = rect.right; 01149 rect.right = descr->width; 01150 rect.top = 0; 01151 InvalidateRect( descr->self, &rect, TRUE ); 01152 } 01153 } 01154 } 01155 01156 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index ) 01157 { 01158 RECT rect; 01159 01160 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1) 01161 InvalidateRect( descr->self, &rect, TRUE ); 01162 } 01163 01164 /*********************************************************************** 01165 * LISTBOX_GetItemHeight 01166 */ 01167 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index ) 01168 { 01169 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0) 01170 { 01171 if ((index < 0) || (index >= descr->nb_items)) 01172 { 01173 SetLastError(ERROR_INVALID_INDEX); 01174 return LB_ERR; 01175 } 01176 return descr->items[index].height; 01177 } 01178 else return descr->item_height; 01179 } 01180 01181 01182 /*********************************************************************** 01183 * LISTBOX_SetItemHeight 01184 */ 01185 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint ) 01186 { 01187 if (height > MAXBYTE) 01188 return -1; 01189 01190 if (!height) height = 1; 01191 01192 if (descr->style & LBS_OWNERDRAWVARIABLE) 01193 { 01194 if ((index < 0) || (index >= descr->nb_items)) 01195 { 01196 SetLastError(ERROR_INVALID_INDEX); 01197 return LB_ERR; 01198 } 01199 TRACE("[%p]: item %d height = %d\n", descr->self, index, height ); 01200 descr->items[index].height = height; 01201 LISTBOX_UpdateScroll( descr ); 01202 if (repaint) 01203 LISTBOX_InvalidateItems( descr, index ); 01204 } 01205 else if (height != descr->item_height) 01206 { 01207 TRACE("[%p]: new height = %d\n", descr->self, height ); 01208 descr->item_height = height; 01209 LISTBOX_UpdatePage( descr ); 01210 LISTBOX_UpdateScroll( descr ); 01211 if (repaint) 01212 InvalidateRect( descr->self, 0, TRUE ); 01213 } 01214 return LB_OKAY; 01215 } 01216 01217 01218 /*********************************************************************** 01219 * LISTBOX_SetHorizontalPos 01220 */ 01221 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos ) 01222 { 01223 INT diff; 01224 01225 if (pos > descr->horz_extent - descr->width) 01226 pos = descr->horz_extent - descr->width; 01227 if (pos < 0) pos = 0; 01228 if (!(diff = descr->horz_pos - pos)) return; 01229 TRACE("[%p]: new horz pos = %d\n", descr->self, pos ); 01230 descr->horz_pos = pos; 01231 LISTBOX_UpdateScroll( descr ); 01232 if (abs(diff) < descr->width) 01233 { 01234 RECT rect; 01235 /* Invalidate the focused item so it will be repainted correctly */ 01236 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1) 01237 InvalidateRect( descr->self, &rect, TRUE ); 01238 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL, 01239 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 01240 } 01241 else 01242 InvalidateRect( descr->self, NULL, TRUE ); 01243 } 01244 01245 01246 /*********************************************************************** 01247 * LISTBOX_SetHorizontalExtent 01248 */ 01249 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent ) 01250 { 01251 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN)) 01252 return LB_OKAY; 01253 if (extent <= 0) extent = 1; 01254 if (extent == descr->horz_extent) return LB_OKAY; 01255 TRACE("[%p]: new horz extent = %d\n", descr->self, extent ); 01256 descr->horz_extent = extent; 01257 if (descr->horz_pos > extent - descr->width) 01258 LISTBOX_SetHorizontalPos( descr, extent - descr->width ); 01259 else 01260 LISTBOX_UpdateScroll( descr ); 01261 return LB_OKAY; 01262 } 01263 01264 01265 /*********************************************************************** 01266 * LISTBOX_SetColumnWidth 01267 */ 01268 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width) 01269 { 01270 if (width == descr->column_width) return LB_OKAY; 01271 TRACE("[%p]: new column width = %d\n", descr->self, width ); 01272 descr->column_width = width; 01273 LISTBOX_UpdatePage( descr ); 01274 return LB_OKAY; 01275 } 01276 01277 01278 /*********************************************************************** 01279 * LISTBOX_SetFont 01280 * 01281 * Returns the item height. 01282 */ 01283 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font ) 01284 { 01285 HDC hdc; 01286 HFONT oldFont = 0; 01287 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 01288 SIZE sz; 01289 01290 descr->font = font; 01291 01292 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) 01293 { 01294 ERR("unable to get DC.\n" ); 01295 return 16; 01296 } 01297 if (font) oldFont = SelectObject( hdc, font ); 01298 GetTextExtentPointA( hdc, alphabet, 52, &sz); 01299 if (oldFont) SelectObject( hdc, oldFont ); 01300 ReleaseDC( descr->self, hdc ); 01301 01302 descr->avg_char_width = (sz.cx / 26 + 1) / 2; 01303 if (!IS_OWNERDRAW(descr)) 01304 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE ); 01305 return sz.cy; 01306 } 01307 01308 01309 /*********************************************************************** 01310 * LISTBOX_MakeItemVisible 01311 * 01312 * Make sure that a given item is partially or fully visible. 01313 */ 01314 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully ) 01315 { 01316 INT top; 01317 01318 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully); 01319 01320 if (index <= descr->top_item) top = index; 01321 else if (descr->style & LBS_MULTICOLUMN) 01322 { 01323 INT cols = descr->width; 01324 if (!fully) cols += descr->column_width - 1; 01325 if (cols >= descr->column_width) cols /= descr->column_width; 01326 else cols = 1; 01327 if (index < descr->top_item + (descr->page_size * cols)) return; 01328 top = index - descr->page_size * (cols - 1); 01329 } 01330 else if (descr->style & LBS_OWNERDRAWVARIABLE) 01331 { 01332 INT height = fully ? descr->items[index].height : 1; 01333 for (top = index; top > descr->top_item; top--) 01334 if ((height += descr->items[top-1].height) > descr->height) break; 01335 } 01336 else 01337 { 01338 if (index < descr->top_item + descr->page_size) return; 01339 if (!fully && (index == descr->top_item + descr->page_size) && 01340 (descr->height > (descr->page_size * descr->item_height))) return; 01341 top = index - descr->page_size + 1; 01342 } 01343 LISTBOX_SetTopItem( descr, top, TRUE ); 01344 } 01345 01346 /*********************************************************************** 01347 * LISTBOX_SetCaretIndex 01348 * 01349 * NOTES 01350 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned. 01351 * 01352 */ 01353 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible ) 01354 { 01355 INT oldfocus = descr->focus_item; 01356 01357 TRACE("old focus %d, index %d\n", oldfocus, index); 01358 01359 if (descr->style & LBS_NOSEL) return LB_ERR; 01360 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; 01361 if (index == oldfocus) return LB_OKAY; 01362 01363 LISTBOX_DrawFocusRect( descr, FALSE ); 01364 descr->focus_item = index; 01365 01366 LISTBOX_MakeItemVisible( descr, index, fully_visible ); 01367 LISTBOX_DrawFocusRect( descr, TRUE ); 01368 01369 return LB_OKAY; 01370 } 01371 01372 01373 /*********************************************************************** 01374 * LISTBOX_SelectItemRange 01375 * 01376 * Select a range of items. Should only be used on a MULTIPLESEL listbox. 01377 */ 01378 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first, 01379 INT last, BOOL on ) 01380 { 01381 INT i; 01382 01383 /* A few sanity checks */ 01384 01385 if (descr->style & LBS_NOSEL) return LB_ERR; 01386 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; 01387 01388 if (!descr->nb_items) return LB_OKAY; 01389 01390 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1; 01391 if (first < 0) first = 0; 01392 if (last < first) return LB_OKAY; 01393 01394 if (on) /* Turn selection on */ 01395 { 01396 for (i = first; i <= last; i++) 01397 { 01398 if (descr->items[i].selected) continue; 01399 descr->items[i].selected = TRUE; 01400 LISTBOX_InvalidateItemRect(descr, i); 01401 } 01402 } 01403 else /* Turn selection off */ 01404 { 01405 for (i = first; i <= last; i++) 01406 { 01407 if (!descr->items[i].selected) continue; 01408 descr->items[i].selected = FALSE; 01409 LISTBOX_InvalidateItemRect(descr, i); 01410 } 01411 } 01412 return LB_OKAY; 01413 } 01414 01415 /*********************************************************************** 01416 * LISTBOX_SetSelection 01417 */ 01418 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index, 01419 BOOL on, BOOL send_notify ) 01420 { 01421 TRACE( "cur_sel=%d index=%d notify=%s\n", 01422 descr->selected_item, index, send_notify ? "YES" : "NO" ); 01423 01424 if (descr->style & LBS_NOSEL) 01425 { 01426 descr->selected_item = index; 01427 return LB_ERR; 01428 } 01429 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR; 01430 if (descr->style & LBS_MULTIPLESEL) 01431 { 01432 if (index == -1) /* Select all items */ 01433 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on ); 01434 else /* Only one item */ 01435 return LISTBOX_SelectItemRange( descr, index, index, on ); 01436 } 01437 else 01438 { 01439 INT oldsel = descr->selected_item; 01440 if (index == oldsel) return LB_OKAY; 01441 if (oldsel != -1) descr->items[oldsel].selected = FALSE; 01442 if (index != -1) descr->items[index].selected = TRUE; 01443 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); 01444 descr->selected_item = index; 01445 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT ); 01446 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr, 01447 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL ); 01448 else 01449 if( descr->lphc ) /* set selection change flag for parent combo */ 01450 descr->lphc->wState |= CBF_SELCHANGE; 01451 } 01452 return LB_OKAY; 01453 } 01454 01455 01456 /*********************************************************************** 01457 * LISTBOX_MoveCaret 01458 * 01459 * Change the caret position and extend the selection to the new caret. 01460 */ 01461 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible ) 01462 { 01463 TRACE("old focus %d, index %d\n", descr->focus_item, index); 01464 01465 if ((index < 0) || (index >= descr->nb_items)) 01466 return; 01467 01468 /* Important, repaint needs to be done in this order if 01469 you want to mimic Windows behavior: 01470 1. Remove the focus and paint the item 01471 2. Remove the selection and paint the item(s) 01472 3. Set the selection and repaint the item(s) 01473 4. Set the focus to 'index' and repaint the item */ 01474 01475 /* 1. remove the focus and repaint the item */ 01476 LISTBOX_DrawFocusRect( descr, FALSE ); 01477 01478 /* 2. then turn off the previous selection */ 01479 /* 3. repaint the new selected item */ 01480 if (descr->style & LBS_EXTENDEDSEL) 01481 { 01482 if (descr->anchor_item != -1) 01483 { 01484 INT first = min( index, descr->anchor_item ); 01485 INT last = max( index, descr->anchor_item ); 01486 if (first > 0) 01487 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE ); 01488 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE ); 01489 LISTBOX_SelectItemRange( descr, first, last, TRUE ); 01490 } 01491 } 01492 else if (!(descr->style & LBS_MULTIPLESEL)) 01493 { 01494 /* Set selection to new caret item */ 01495 LISTBOX_SetSelection( descr, index, TRUE, FALSE ); 01496 } 01497 01498 /* 4. repaint the new item with the focus */ 01499 descr->focus_item = index; 01500 LISTBOX_MakeItemVisible( descr, index, fully_visible ); 01501 LISTBOX_DrawFocusRect( descr, TRUE ); 01502 } 01503 01504 01505 /*********************************************************************** 01506 * LISTBOX_InsertItem 01507 */ 01508 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index, 01509 LPWSTR str, ULONG_PTR data ) 01510 { 01511 LB_ITEMDATA *item; 01512 INT max_items; 01513 INT oldfocus = descr->focus_item; 01514 01515 if (index == -1) index = descr->nb_items; 01516 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR; 01517 if (!descr->items) max_items = 0; 01518 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); 01519 if (descr->nb_items == max_items) 01520 { 01521 /* We need to grow the array */ 01522 max_items += LB_ARRAY_GRANULARITY; 01523 if (descr->items) 01524 item = HeapReAlloc( GetProcessHeap(), 0, descr->items, 01525 max_items * sizeof(LB_ITEMDATA) ); 01526 else 01527 item = HeapAlloc( GetProcessHeap(), 0, 01528 max_items * sizeof(LB_ITEMDATA) ); 01529 if (!item) 01530 { 01531 SEND_NOTIFICATION( descr, LBN_ERRSPACE ); 01532 return LB_ERRSPACE; 01533 } 01534 descr->items = item; 01535 } 01536 01537 /* Insert the item structure */ 01538 01539 item = &descr->items[index]; 01540 if (index < descr->nb_items) 01541 RtlMoveMemory( item + 1, item, 01542 (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); 01543 item->str = str; 01544 item->data = data; 01545 item->height = 0; 01546 item->selected = FALSE; 01547 descr->nb_items++; 01548 01549 /* Get item height */ 01550 01551 if (descr->style & LBS_OWNERDRAWVARIABLE) 01552 { 01553 MEASUREITEMSTRUCT mis; 01554 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 01555 01556 mis.CtlType = ODT_LISTBOX; 01557 mis.CtlID = id; 01558 mis.itemID = index; 01559 mis.itemData = descr->items[index].data; 01560 mis.itemHeight = descr->item_height; 01561 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); 01562 item->height = mis.itemHeight ? mis.itemHeight : 1; 01563 TRACE("[%p]: measure item %d (%s) = %d\n", 01564 descr->self, index, str ? debugstr_w(str) : "", item->height ); 01565 } 01566 01567 /* Repaint the items */ 01568 01569 LISTBOX_UpdateScroll( descr ); 01570 LISTBOX_InvalidateItems( descr, index ); 01571 01572 /* Move selection and focused item */ 01573 /* If listbox was empty, set focus to the first item */ 01574 if (descr->nb_items == 1) 01575 LISTBOX_SetCaretIndex( descr, 0, FALSE ); 01576 /* single select don't change selection index in win31 */ 01577 else if ((ISWIN31) && !(IS_MULTISELECT(descr))) 01578 { 01579 descr->selected_item++; 01580 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE ); 01581 } 01582 else 01583 { 01584 if (index <= descr->selected_item) 01585 { 01586 descr->selected_item++; 01587 descr->focus_item = oldfocus; /* focus not changed */ 01588 } 01589 } 01590 return LB_OKAY; 01591 } 01592 01593 01594 /*********************************************************************** 01595 * LISTBOX_InsertString 01596 */ 01597 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str ) 01598 { 01599 LPWSTR new_str = NULL; 01600 ULONG_PTR data = 0; 01601 LRESULT ret; 01602 01603 if (HAS_STRINGS(descr)) 01604 { 01605 static const WCHAR empty_stringW[] = { 0 }; 01606 if (!str) str = empty_stringW; 01607 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) ))) 01608 { 01609 SEND_NOTIFICATION( descr, LBN_ERRSPACE ); 01610 return LB_ERRSPACE; 01611 } 01612 strcpyW(new_str, str); 01613 } 01614 else data = (ULONG_PTR)str; 01615 01616 if (index == -1) index = descr->nb_items; 01617 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0) 01618 { 01619 HeapFree( GetProcessHeap(), 0, new_str ); 01620 return ret; 01621 } 01622 01623 TRACE("[%p]: added item %d %s\n", 01624 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" ); 01625 return index; 01626 } 01627 01628 01629 /*********************************************************************** 01630 * LISTBOX_DeleteItem 01631 * 01632 * Delete the content of an item. 'index' must be a valid index. 01633 */ 01634 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index ) 01635 { 01636 /* save the item data before it gets freed by LB_RESETCONTENT */ 01637 ULONG_PTR item_data = descr->items[index].data; 01638 LPWSTR item_str = descr->items[index].str; 01639 01640 if (!descr->nb_items) 01641 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 ); 01642 01643 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items, 01644 * while Win95 sends it for all items with user data. 01645 * It's probably better to send it too often than not 01646 * often enough, so this is what we do here. 01647 */ 01648 if (IS_OWNERDRAW(descr) || item_data) 01649 { 01650 DELETEITEMSTRUCT dis; 01651 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 01652 01653 dis.CtlType = ODT_LISTBOX; 01654 dis.CtlID = id; 01655 dis.itemID = index; 01656 dis.hwndItem = descr->self; 01657 dis.itemData = item_data; 01658 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis ); 01659 } 01660 if (HAS_STRINGS(descr)) 01661 HeapFree( GetProcessHeap(), 0, item_str ); 01662 } 01663 01664 01665 /*********************************************************************** 01666 * LISTBOX_RemoveItem 01667 * 01668 * Remove an item from the listbox and delete its content. 01669 */ 01670 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index ) 01671 { 01672 LB_ITEMDATA *item; 01673 INT max_items; 01674 01675 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; 01676 01677 /* We need to invalidate the original rect instead of the updated one. */ 01678 LISTBOX_InvalidateItems( descr, index ); 01679 01680 descr->nb_items--; 01681 LISTBOX_DeleteItem( descr, index ); 01682 01683 if (!descr->nb_items) return LB_OKAY; 01684 01685 /* Remove the item */ 01686 01687 item = &descr->items[index]; 01688 if (index < descr->nb_items) 01689 RtlMoveMemory( item, item + 1, 01690 (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); 01691 if (descr->anchor_item == descr->nb_items) descr->anchor_item--; 01692 01693 /* Shrink the item array if possible */ 01694 01695 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA); 01696 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) 01697 { 01698 max_items -= LB_ARRAY_GRANULARITY; 01699 item = HeapReAlloc( GetProcessHeap(), 0, descr->items, 01700 max_items * sizeof(LB_ITEMDATA) ); 01701 if (item) descr->items = item; 01702 } 01703 /* Repaint the items */ 01704 01705 LISTBOX_UpdateScroll( descr ); 01706 /* if we removed the scrollbar, reset the top of the list 01707 (correct for owner-drawn ???) */ 01708 if (descr->nb_items == descr->page_size) 01709 LISTBOX_SetTopItem( descr, 0, TRUE ); 01710 01711 /* Move selection and focused item */ 01712 if (!IS_MULTISELECT(descr)) 01713 { 01714 if (index == descr->selected_item) 01715 descr->selected_item = -1; 01716 else if (index < descr->selected_item) 01717 { 01718 descr->selected_item--; 01719 if (ISWIN31) /* win 31 do not change the selected item number */ 01720 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE); 01721 } 01722 } 01723 01724 if (descr->focus_item >= descr->nb_items) 01725 { 01726 descr->focus_item = descr->nb_items - 1; 01727 if (descr->focus_item < 0) descr->focus_item = 0; 01728 } 01729 return LB_OKAY; 01730 } 01731 01732 01733 /*********************************************************************** 01734 * LISTBOX_ResetContent 01735 */ 01736 static void LISTBOX_ResetContent( LB_DESCR *descr ) 01737 { 01738 INT i; 01739 01740 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i); 01741 HeapFree( GetProcessHeap(), 0, descr->items ); 01742 descr->nb_items = 0; 01743 descr->top_item = 0; 01744 descr->selected_item = -1; 01745 descr->focus_item = 0; 01746 descr->anchor_item = -1; 01747 descr->items = NULL; 01748 } 01749 01750 01751 /*********************************************************************** 01752 * LISTBOX_SetCount 01753 */ 01754 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count ) 01755 { 01756 LRESULT ret; 01757 01758 if (HAS_STRINGS(descr)) 01759 { 01760 SetLastError(ERROR_SETCOUNT_ON_BAD_LB); 01761 return LB_ERR; 01762 } 01763 01764 /* FIXME: this is far from optimal... */ 01765 if (count > descr->nb_items) 01766 { 01767 while (count > descr->nb_items) 01768 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0) 01769 return ret; 01770 } 01771 else if (count < descr->nb_items) 01772 { 01773 while (count < descr->nb_items) 01774 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0) 01775 return ret; 01776 } 01777 return LB_OKAY; 01778 } 01779 01780 01781 /*********************************************************************** 01782 * LISTBOX_Directory 01783 */ 01784 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib, 01785 LPCWSTR filespec, BOOL long_names ) 01786 { 01787 HANDLE handle; 01788 LRESULT ret = LB_OKAY; 01789 WIN32_FIND_DATAW entry; 01790 int pos; 01791 LRESULT maxinsert = LB_ERR; 01792 01793 /* don't scan directory if we just want drives exclusively */ 01794 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) { 01795 /* scan directory */ 01796 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE) 01797 { 01798 int le = GetLastError(); 01799 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR; 01800 } 01801 else 01802 { 01803 do 01804 { 01805 WCHAR buffer[270]; 01806 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 01807 { 01808 static const WCHAR bracketW[] = { ']',0 }; 01809 static const WCHAR dotW[] = { '.',0 }; 01810 if (!(attrib & DDL_DIRECTORY) || 01811 !strcmpW( entry.cFileName, dotW )) continue; 01812 buffer[0] = '['; 01813 if (!long_names && entry.cAlternateFileName[0]) 01814 strcpyW( buffer + 1, entry.cAlternateFileName ); 01815 else 01816 strcpyW( buffer + 1, entry.cFileName ); 01817 strcatW(buffer, bracketW); 01818 } 01819 else /* not a directory */ 01820 { 01821 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \ 01822 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE) 01823 01824 if ((attrib & DDL_EXCLUSIVE) && 01825 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS))) 01826 continue; 01827 #undef ATTRIBS 01828 if (!long_names && entry.cAlternateFileName[0]) 01829 strcpyW( buffer, entry.cAlternateFileName ); 01830 else 01831 strcpyW( buffer, entry.cFileName ); 01832 } 01833 if (!long_names) CharLowerW( buffer ); 01834 pos = LISTBOX_FindFileStrPos( descr, buffer ); 01835 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0) 01836 break; 01837 if (ret <= maxinsert) maxinsert++; else maxinsert = ret; 01838 } while (FindNextFileW( handle, &entry )); 01839 FindClose( handle ); 01840 } 01841 } 01842 if (ret >= 0) 01843 { 01844 ret = maxinsert; 01845 01846 /* scan drives */ 01847 if (attrib & DDL_DRIVES) 01848 { 01849 WCHAR buffer[] = {'[','-','a','-',']',0}; 01850 WCHAR root[] = {'A',':','\\',0}; 01851 int drive; 01852 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++) 01853 { 01854 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue; 01855 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0) 01856 break; 01857 } 01858 } 01859 } 01860 return ret; 01861 } 01862 01863 01864 /*********************************************************************** 01865 * LISTBOX_HandleVScroll 01866 */ 01867 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos ) 01868 { 01869 SCROLLINFO info; 01870 01871 if (descr->style & LBS_MULTICOLUMN) return 0; 01872 switch(scrollReq) 01873 { 01874 case SB_LINEUP: 01875 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE ); 01876 break; 01877 case SB_LINEDOWN: 01878 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE ); 01879 break; 01880 case SB_PAGEUP: 01881 LISTBOX_SetTopItem( descr, descr->top_item - 01882 LISTBOX_GetCurrentPageSize( descr ), TRUE ); 01883 break; 01884 case SB_PAGEDOWN: 01885 LISTBOX_SetTopItem( descr, descr->top_item + 01886 LISTBOX_GetCurrentPageSize( descr ), TRUE ); 01887 break; 01888 case SB_THUMBPOSITION: 01889 LISTBOX_SetTopItem( descr, pos, TRUE ); 01890 break; 01891 case SB_THUMBTRACK: 01892 info.cbSize = sizeof(info); 01893 info.fMask = SIF_TRACKPOS; 01894 GetScrollInfo( descr->self, SB_VERT, &info ); 01895 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE ); 01896 break; 01897 case SB_TOP: 01898 LISTBOX_SetTopItem( descr, 0, TRUE ); 01899 break; 01900 case SB_BOTTOM: 01901 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE ); 01902 break; 01903 } 01904 return 0; 01905 } 01906 01907 01908 /*********************************************************************** 01909 * LISTBOX_HandleHScroll 01910 */ 01911 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos ) 01912 { 01913 SCROLLINFO info; 01914 INT page; 01915 01916 if (descr->style & LBS_MULTICOLUMN) 01917 { 01918 switch(scrollReq) 01919 { 01920 case SB_LINELEFT: 01921 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size, 01922 TRUE ); 01923 break; 01924 case SB_LINERIGHT: 01925 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size, 01926 TRUE ); 01927 break; 01928 case SB_PAGELEFT: 01929 page = descr->width / descr->column_width; 01930 if (page < 1) page = 1; 01931 LISTBOX_SetTopItem( descr, 01932 descr->top_item - page * descr->page_size, TRUE ); 01933 break; 01934 case SB_PAGERIGHT: 01935 page = descr->width / descr->column_width; 01936 if (page < 1) page = 1; 01937 LISTBOX_SetTopItem( descr, 01938 descr->top_item + page * descr->page_size, TRUE ); 01939 break; 01940 case SB_THUMBPOSITION: 01941 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE ); 01942 break; 01943 case SB_THUMBTRACK: 01944 info.cbSize = sizeof(info); 01945 info.fMask = SIF_TRACKPOS; 01946 GetScrollInfo( descr->self, SB_VERT, &info ); 01947 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size, 01948 TRUE ); 01949 break; 01950 case SB_LEFT: 01951 LISTBOX_SetTopItem( descr, 0, TRUE ); 01952 break; 01953 case SB_RIGHT: 01954 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE ); 01955 break; 01956 } 01957 } 01958 else if (descr->horz_extent) 01959 { 01960 switch(scrollReq) 01961 { 01962 case SB_LINELEFT: 01963 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 ); 01964 break; 01965 case SB_LINERIGHT: 01966 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 ); 01967 break; 01968 case SB_PAGELEFT: 01969 LISTBOX_SetHorizontalPos( descr, 01970 descr->horz_pos - descr->width ); 01971 break; 01972 case SB_PAGERIGHT: 01973 LISTBOX_SetHorizontalPos( descr, 01974 descr->horz_pos + descr->width ); 01975 break; 01976 case SB_THUMBPOSITION: 01977 LISTBOX_SetHorizontalPos( descr, pos ); 01978 break; 01979 case SB_THUMBTRACK: 01980 info.cbSize = sizeof(info); 01981 info.fMask = SIF_TRACKPOS; 01982 GetScrollInfo( descr->self, SB_HORZ, &info ); 01983 LISTBOX_SetHorizontalPos( descr, info.nTrackPos ); 01984 break; 01985 case SB_LEFT: 01986 LISTBOX_SetHorizontalPos( descr, 0 ); 01987 break; 01988 case SB_RIGHT: 01989 LISTBOX_SetHorizontalPos( descr, 01990 descr->horz_extent - descr->width ); 01991 break; 01992 } 01993 } 01994 return 0; 01995 } 01996 01997 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta ) 01998 { 01999 short gcWheelDelta = 0; 02000 UINT pulScrollLines = 3; 02001 02002 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); 02003 02004 gcWheelDelta -= delta; 02005 02006 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines) 02007 { 02008 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines); 02009 cLineScroll *= (gcWheelDelta / WHEEL_DELTA); 02010 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE ); 02011 } 02012 return 0; 02013 } 02014 02015 /*********************************************************************** 02016 * LISTBOX_HandleLButtonDown 02017 */ 02018 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y ) 02019 { 02020 INT index = LISTBOX_GetItemFromPoint( descr, x, y ); 02021 02022 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n", 02023 descr->self, x, y, index, descr->focus_item); 02024 02025 if (!descr->caret_on && (descr->in_focus)) return 0; 02026 02027 if (!descr->in_focus) 02028 { 02029 if( !descr->lphc ) SetFocus( descr->self ); 02030 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self ); 02031 } 02032 02033 if (index == -1) return 0; 02034 02035 if (!descr->lphc) 02036 { 02037 if (descr->style & LBS_NOTIFY ) 02038 SendMessageW( descr->owner, WM_LBTRACKPOINT, index, 02039 MAKELPARAM( x, y ) ); 02040 } 02041 02042 descr->captured = TRUE; 02043 SetCapture( descr->self ); 02044 02045 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) 02046 { 02047 /* we should perhaps make sure that all items are deselected 02048 FIXME: needed for !LBS_EXTENDEDSEL, too ? 02049 if (!(keys & (MK_SHIFT|MK_CONTROL))) 02050 LISTBOX_SetSelection( descr, -1, FALSE, FALSE); 02051 */ 02052 02053 if (!(keys & MK_SHIFT)) descr->anchor_item = index; 02054 if (keys & MK_CONTROL) 02055 { 02056 LISTBOX_SetCaretIndex( descr, index, FALSE ); 02057 LISTBOX_SetSelection( descr, index, 02058 !descr->items[index].selected, 02059 (descr->style & LBS_NOTIFY) != 0); 02060 } 02061 else 02062 { 02063 LISTBOX_MoveCaret( descr, index, FALSE ); 02064 02065 if (descr->style & LBS_EXTENDEDSEL) 02066 { 02067 LISTBOX_SetSelection( descr, index, 02068 descr->items[index].selected, 02069 (descr->style & LBS_NOTIFY) != 0 ); 02070 } 02071 else 02072 { 02073 LISTBOX_SetSelection( descr, index, 02074 !descr->items[index].selected, 02075 (descr->style & LBS_NOTIFY) != 0 ); 02076 } 02077 } 02078 } 02079 else 02080 { 02081 descr->anchor_item = index; 02082 LISTBOX_MoveCaret( descr, index, FALSE ); 02083 LISTBOX_SetSelection( descr, index, 02084 TRUE, (descr->style & LBS_NOTIFY) != 0 ); 02085 } 02086 02087 if (!descr->lphc) 02088 { // See rev 40864 use Ptr for 64 bit. 02089 if (GetWindowLongPtrW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT) 02090 { 02091 POINT pt; 02092 02093 pt.x = x; 02094 pt.y = y; 02095 02096 if (DragDetect( descr->self, pt )) 02097 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 ); 02098 } 02099 } 02100 return 0; 02101 } 02102 02103 02104 /************************************************************************* 02105 * LISTBOX_HandleLButtonDownCombo [Internal] 02106 * 02107 * Process LButtonDown message for the ComboListBox 02108 * 02109 * PARAMS 02110 * pWnd [I] The windows internal structure 02111 * pDescr [I] The ListBox internal structure 02112 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info) 02113 * x [I] X Mouse Coordinate 02114 * y [I] Y Mouse Coordinate 02115 * 02116 * RETURNS 02117 * 0 since we are processing the WM_LBUTTONDOWN Message 02118 * 02119 * NOTES 02120 * This function is only to be used when a ListBox is a ComboListBox 02121 */ 02122 02123 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y) 02124 { 02125 RECT clientRect, screenRect; 02126 POINT mousePos; 02127 02128 mousePos.x = x; 02129 mousePos.y = y; 02130 02131 GetClientRect(descr->self, &clientRect); 02132 02133 if(PtInRect(&clientRect, mousePos)) 02134 { 02135 /* MousePos is in client, resume normal processing */ 02136 if (msg == WM_LBUTTONDOWN) 02137 { 02138 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1; 02139 return LISTBOX_HandleLButtonDown( descr, keys, x, y); 02140 } 02141 else if (descr->style & LBS_NOTIFY) 02142 SEND_NOTIFICATION( descr, LBN_DBLCLK ); 02143 } 02144 else 02145 { 02146 POINT screenMousePos; 02147 HWND hWndOldCapture; 02148 02149 /* Check the Non-Client Area */ 02150 screenMousePos = mousePos; 02151 hWndOldCapture = GetCapture(); 02152 ReleaseCapture(); 02153 GetWindowRect(descr->self, &screenRect); 02154 ClientToScreen(descr->self, &screenMousePos); 02155 02156 if(!PtInRect(&screenRect, screenMousePos)) 02157 { 02158 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE ); 02159 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE ); 02160 COMBO_FlipListbox( descr->lphc, FALSE, FALSE ); 02161 } 02162 else 02163 { 02164 /* Check to see the NC is a scrollbar */ 02165 INT nHitTestType=0; 02166 LONG style = GetWindowLongPtrW( descr->self, GWL_STYLE ); 02167 /* Check Vertical scroll bar */ 02168 if (style & WS_VSCROLL) 02169 { 02170 clientRect.right += GetSystemMetrics(SM_CXVSCROLL); 02171 if (PtInRect( &clientRect, mousePos )) 02172 nHitTestType = HTVSCROLL; 02173 } 02174 /* Check horizontal scroll bar */ 02175 if (style & WS_HSCROLL) 02176 { 02177 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL); 02178 if (PtInRect( &clientRect, mousePos )) 02179 nHitTestType = HTHSCROLL; 02180 } 02181 /* Windows sends this message when a scrollbar is clicked 02182 */ 02183 02184 if(nHitTestType != 0) 02185 { 02186 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType, 02187 MAKELONG(screenMousePos.x, screenMousePos.y)); 02188 } 02189 /* Resume the Capture after scrolling is complete 02190 */ 02191 if(hWndOldCapture != 0) 02192 SetCapture(hWndOldCapture); 02193 } 02194 } 02195 return 0; 02196 } 02197 02198 /*********************************************************************** 02199 * LISTBOX_HandleLButtonUp 02200 */ 02201 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr ) 02202 { 02203 if (LISTBOX_Timer != LB_TIMER_NONE) 02204 KillSystemTimer( descr->self, LB_TIMER_ID ); 02205 LISTBOX_Timer = LB_TIMER_NONE; 02206 if (descr->captured) 02207 { 02208 descr->captured = FALSE; 02209 if (GetCapture() == descr->self) ReleaseCapture(); 02210 if ((descr->style & LBS_NOTIFY) && descr->nb_items) 02211 SEND_NOTIFICATION( descr, LBN_SELCHANGE ); 02212 } 02213 return 0; 02214 } 02215 02216 02217 /*********************************************************************** 02218 * LISTBOX_HandleTimer 02219 * 02220 * Handle scrolling upon a timer event. 02221 * Return TRUE if scrolling should continue. 02222 */ 02223 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir ) 02224 { 02225 switch(dir) 02226 { 02227 case LB_TIMER_UP: 02228 if (descr->top_item) index = descr->top_item - 1; 02229 else index = 0; 02230 break; 02231 case LB_TIMER_LEFT: 02232 if (descr->top_item) index -= descr->page_size; 02233 break; 02234 case LB_TIMER_DOWN: 02235 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr ); 02236 if (index == descr->focus_item) index++; 02237 if (index >= descr->nb_items) index = descr->nb_items - 1; 02238 break; 02239 case LB_TIMER_RIGHT: 02240 if (index + descr->page_size < descr->nb_items) 02241 index += descr->page_size; 02242 break; 02243 case LB_TIMER_NONE: 02244 break; 02245 } 02246 if (index == descr->focus_item) return FALSE; 02247 LISTBOX_MoveCaret( descr, index, FALSE ); 02248 return TRUE; 02249 } 02250 02251 02252 /*********************************************************************** 02253 * LISTBOX_HandleSystemTimer 02254 * 02255 * WM_SYSTIMER handler. 02256 */ 02257 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr ) 02258 { 02259 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer )) 02260 { 02261 KillSystemTimer( descr->self, LB_TIMER_ID ); 02262 LISTBOX_Timer = LB_TIMER_NONE; 02263 } 02264 return 0; 02265 } 02266 02267 02268 /*********************************************************************** 02269 * LISTBOX_HandleMouseMove 02270 * 02271 * WM_MOUSEMOVE handler. 02272 */ 02273 static void LISTBOX_HandleMouseMove( LB_DESCR *descr, 02274 INT x, INT y ) 02275 { 02276 INT index; 02277 TIMER_DIRECTION dir = LB_TIMER_NONE; 02278 02279 if (!descr->captured) return; 02280 02281 if (descr->style & LBS_MULTICOLUMN) 02282 { 02283 if (y < 0) y = 0; 02284 else if (y >= descr->item_height * descr->page_size) 02285 y = descr->item_height * descr->page_size - 1; 02286 02287 if (x < 0) 02288 { 02289 dir = LB_TIMER_LEFT; 02290 x = 0; 02291 } 02292 else if (x >= descr->width) 02293 { 02294 dir = LB_TIMER_RIGHT; 02295 x = descr->width - 1; 02296 } 02297 } 02298 else 02299 { 02300 if (y < 0) dir = LB_TIMER_UP; /* above */ 02301 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */ 02302 } 02303 02304 index = LISTBOX_GetItemFromPoint( descr, x, y ); 02305 if (index == -1) index = descr->focus_item; 02306 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE; 02307 02308 /* Start/stop the system timer */ 02309 02310 if (dir != LB_TIMER_NONE) 02311 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL); 02312 else if (LISTBOX_Timer != LB_TIMER_NONE) 02313 KillSystemTimer( descr->self, LB_TIMER_ID ); 02314 LISTBOX_Timer = dir; 02315 } 02316 02317 02318 /*********************************************************************** 02319 * LISTBOX_HandleKeyDown 02320 */ 02321 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key ) 02322 { 02323 INT caret = -1; 02324 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */ 02325 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item)) 02326 bForceSelection = FALSE; /* only for single select list */ 02327 02328 if (descr->style & LBS_WANTKEYBOARDINPUT) 02329 { 02330 caret = SendMessageW( descr->owner, WM_VKEYTOITEM, 02331 MAKEWPARAM(LOWORD(key), descr->focus_item), 02332 (LPARAM)descr->self ); 02333 if (caret == -2) return 0; 02334 } 02335 if (caret == -1) switch(key) 02336 { 02337 case VK_LEFT: 02338 if (descr->style & LBS_MULTICOLUMN) 02339 { 02340 bForceSelection = FALSE; 02341 if (descr->focus_item >= descr->page_size) 02342 caret = descr->focus_item - descr->page_size; 02343 break; 02344 } 02345 /* fall through */ 02346 case VK_UP: 02347 caret = descr->focus_item - 1; 02348 if (caret < 0) caret = 0; 02349 break; 02350 case VK_RIGHT: 02351 if (descr->style & LBS_MULTICOLUMN) 02352 { 02353 bForceSelection = FALSE; 02354 if (descr->focus_item + descr->page_size < descr->nb_items) 02355 caret = descr->focus_item + descr->page_size; 02356 break; 02357 } 02358 /* fall through */ 02359 case VK_DOWN: 02360 caret = descr->focus_item + 1; 02361 if (caret >= descr->nb_items) caret = descr->nb_items - 1; 02362 break; 02363 02364 case VK_PRIOR: 02365 if (descr->style & LBS_MULTICOLUMN) 02366 { 02367 INT page = descr->width / descr->column_width; 02368 if (page < 1) page = 1; 02369 caret = descr->focus_item - (page * descr->page_size) + 1; 02370 } 02371 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1; 02372 if (caret < 0) caret = 0; 02373 break; 02374 case VK_NEXT: 02375 if (descr->style & LBS_MULTICOLUMN) 02376 { 02377 INT page = descr->width / descr->column_width; 02378 if (page < 1) page = 1; 02379 caret = descr->focus_item + (page * descr->page_size) - 1; 02380 } 02381 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1; 02382 if (caret >= descr->nb_items) caret = descr->nb_items - 1; 02383 break; 02384 case VK_HOME: 02385 caret = 0; 02386 break; 02387 case VK_END: 02388 caret = descr->nb_items - 1; 02389 break; 02390 case VK_SPACE: 02391 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item; 02392 else if (descr->style & LBS_MULTIPLESEL) 02393 { 02394 LISTBOX_SetSelection( descr, descr->focus_item, 02395 !descr->items[descr->focus_item].selected, 02396 (descr->style & LBS_NOTIFY) != 0 ); 02397 } 02398 break; 02399 default: 02400 bForceSelection = FALSE; 02401 } 02402 if (bForceSelection) /* focused item is used instead of key */ 02403 caret = descr->focus_item; 02404 if (caret >= 0) 02405 { 02406 if (((descr->style & LBS_EXTENDEDSEL) && 02407 !(GetKeyState( VK_SHIFT ) & 0x8000)) || 02408 !IS_MULTISELECT(descr)) 02409 descr->anchor_item = caret; 02410 LISTBOX_MoveCaret( descr, caret, TRUE ); 02411 02412 if (descr->style & LBS_MULTIPLESEL) 02413 descr->selected_item = caret; 02414 else 02415 LISTBOX_SetSelection( descr, caret, TRUE, FALSE); 02416 if (descr->style & LBS_NOTIFY) 02417 { 02418 if (descr->lphc && IsWindowVisible( descr->self )) 02419 { 02420 /* make sure that combo parent doesn't hide us */ 02421 descr->lphc->wState |= CBF_NOROLLUP; 02422 } 02423 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE ); 02424 } 02425 } 02426 return 0; 02427 } 02428 02429 02430 /*********************************************************************** 02431 * LISTBOX_HandleChar 02432 */ 02433 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW ) 02434 { 02435 INT caret = -1; 02436 WCHAR str[2]; 02437 02438 str[0] = charW; 02439 str[1] = '\0'; 02440 02441 if (descr->style & LBS_WANTKEYBOARDINPUT) 02442 { 02443 caret = SendMessageW( descr->owner, WM_CHARTOITEM, 02444 MAKEWPARAM(charW, descr->focus_item), 02445 (LPARAM)descr->self ); 02446 if (caret == -2) return 0; 02447 } 02448 if (caret == -1) 02449 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE); 02450 if (caret != -1) 02451 { 02452 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1) 02453 LISTBOX_SetSelection( descr, caret, TRUE, FALSE); 02454 LISTBOX_MoveCaret( descr, caret, TRUE ); 02455 if ((descr->style & LBS_NOTIFY) && descr->nb_items) 02456 SEND_NOTIFICATION( descr, LBN_SELCHANGE ); 02457 } 02458 return 0; 02459 } 02460 02461 /* ReactOS Retrieve the UI state for the control */ 02462 static BOOL LISTBOX_update_uistate(LB_DESCR *descr) 02463 { 02464 LONG prev_flags; 02465 02466 prev_flags = descr->UIState; 02467 descr->UIState = DefWindowProcW(descr->self, WM_QUERYUISTATE, 0, 0); 02468 return prev_flags != descr->UIState; 02469 } 02470 02471 02472 /*********************************************************************** 02473 * LISTBOX_Create 02474 */ 02475 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc ) 02476 { 02477 LB_DESCR *descr; 02478 MEASUREITEMSTRUCT mis; 02479 RECT rect; 02480 02481 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) ))) 02482 return FALSE; 02483 02484 GetClientRect( hwnd, &rect ); 02485 descr->self = hwnd; 02486 descr->owner = GetParent( descr->self ); 02487 descr->style = GetWindowLongPtrW( descr->self, GWL_STYLE ); 02488 descr->width = rect.right - rect.left; 02489 descr->height = rect.bottom - rect.top; 02490 descr->items = NULL; 02491 descr->nb_items = 0; 02492 descr->top_item = 0; 02493 descr->selected_item = -1; 02494 descr->focus_item = 0; 02495 descr->anchor_item = -1; 02496 descr->item_height = 1; 02497 descr->page_size = 1; 02498 descr->column_width = 150; 02499 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0; 02500 descr->horz_pos = 0; 02501 descr->nb_tabs = 0; 02502 descr->tabs = NULL; 02503 descr->caret_on = lphc ? FALSE : TRUE; 02504 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE; 02505 descr->in_focus = FALSE; 02506 descr->captured = FALSE; 02507 descr->font = 0; 02508 descr->locale = GetUserDefaultLCID(); 02509 descr->lphc = lphc; 02510 02511 if( lphc ) 02512 { 02513 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self ); 02514 descr->owner = lphc->self; 02515 } 02516 02517 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr ); 02518 02519 LISTBOX_update_uistate(descr); // ReactOS 02520 02521 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY; 02522 */ 02523 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL; 02524 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE; 02525 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT; 02526 descr->item_height = LISTBOX_SetFont( descr, 0 ); 02527 02528 if (descr->style & LBS_OWNERDRAWFIXED) 02529 { 02530 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN)) 02531 { 02532 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */ 02533 descr->item_height = lphc->fixedOwnerDrawHeight; 02534 } 02535 else 02536 { 02537 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 02538 mis.CtlType = ODT_LISTBOX; 02539 mis.CtlID = id; 02540 mis.itemID = -1; 02541 mis.itemWidth = 0; 02542 mis.itemData = 0; 02543 mis.itemHeight = descr->item_height; 02544 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); 02545 descr->item_height = mis.itemHeight ? mis.itemHeight : 1; 02546 } 02547 } 02548 02549 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height); 02550 return TRUE; 02551 } 02552 02553 02554 /*********************************************************************** 02555 * LISTBOX_Destroy 02556 */ 02557 static BOOL LISTBOX_Destroy( LB_DESCR *descr ) 02558 { 02559 LISTBOX_ResetContent( descr ); 02560 SetWindowLongPtrW( descr->self, 0, 0 ); 02561 HeapFree( GetProcessHeap(), 0, descr ); 02562 return TRUE; 02563 } 02564 02565 02566 /*********************************************************************** 02567 * ListBoxWndProc_common 02568 */ 02569 LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg, 02570 WPARAM wParam, LPARAM lParam, BOOL unicode ) 02571 { 02572 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 ); 02573 LPHEADCOMBO lphc = 0; 02574 LRESULT ret; 02575 #ifdef __REACTOS__ 02576 PWND pWnd; 02577 02578 pWnd = ValidateHwnd(hwnd); 02579 if (pWnd) 02580 { 02581 if (!pWnd->fnid) 02582 { 02583 NtUserSetWindowFNID(hwnd, FNID_LISTBOX); // Could be FNID_COMBOLBOX by class. 02584 } 02585 else 02586 { 02587 if (pWnd->fnid != FNID_LISTBOX) 02588 { 02589 ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd->fnid); 02590 return 0; 02591 } 02592 } 02593 } 02594 #endif 02595 02596 if (!descr) 02597 { 02598 if (!IsWindow(hwnd)) return 0; 02599 02600 if (msg == WM_CREATE) 02601 { 02602 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam; 02603 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams; 02604 if (!LISTBOX_Create( hwnd, lphc )) return -1; 02605 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) ); 02606 return 0; 02607 } 02608 /* Ignore all other messages before we get a WM_CREATE */ 02609 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) : 02610 DefWindowProcA( hwnd, msg, wParam, lParam ); 02611 } 02612 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc; 02613 02614 TRACE("[%p]: msg %s wp %08lx lp %08lx\n", 02615 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam ); 02616 02617 switch(msg) 02618 { 02619 case LB_RESETCONTENT: 02620 LISTBOX_ResetContent( descr ); 02621 LISTBOX_UpdateScroll( descr ); 02622 InvalidateRect( descr->self, NULL, TRUE ); 02623 return 0; 02624 02625 case LB_ADDSTRING: 02626 case LB_ADDSTRING_LOWER: 02627 case LB_ADDSTRING_UPPER: 02628 { 02629 INT ret; 02630 LPWSTR textW; 02631 if(unicode || !HAS_STRINGS(descr)) 02632 textW = (LPWSTR)lParam; 02633 else 02634 { 02635 LPSTR textA = (LPSTR)lParam; 02636 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02637 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02638 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02639 else 02640 return LB_ERRSPACE; 02641 } 02642 /* in the unicode the version, the string is really overwritten 02643 during the converting case */ 02644 if (msg == LB_ADDSTRING_LOWER) 02645 strlwrW(textW); 02646 else if (msg == LB_ADDSTRING_UPPER) 02647 struprW(textW); 02648 wParam = LISTBOX_FindStringPos( descr, textW, FALSE ); 02649 ret = LISTBOX_InsertString( descr, wParam, textW ); 02650 if (!unicode && HAS_STRINGS(descr)) 02651 HeapFree(GetProcessHeap(), 0, textW); 02652 return ret; 02653 } 02654 02655 case LB_INSERTSTRING: 02656 case LB_INSERTSTRING_UPPER: 02657 case LB_INSERTSTRING_LOWER: 02658 { 02659 INT ret; 02660 LPWSTR textW; 02661 if(unicode || !HAS_STRINGS(descr)) 02662 textW = (LPWSTR)lParam; 02663 else 02664 { 02665 LPSTR textA = (LPSTR)lParam; 02666 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02667 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02668 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02669 else 02670 return LB_ERRSPACE; 02671 } 02672 /* in the unicode the version, the string is really overwritten 02673 during the converting case */ 02674 if (msg == LB_INSERTSTRING_LOWER) 02675 strlwrW(textW); 02676 else if (msg == LB_INSERTSTRING_UPPER) 02677 struprW(textW); 02678 ret = LISTBOX_InsertString( descr, wParam, textW ); 02679 if(!unicode && HAS_STRINGS(descr)) 02680 HeapFree(GetProcessHeap(), 0, textW); 02681 return ret; 02682 } 02683 02684 case LB_ADDFILE: 02685 { 02686 INT ret; 02687 LPWSTR textW; 02688 if(unicode || !HAS_STRINGS(descr)) 02689 textW = (LPWSTR)lParam; 02690 else 02691 { 02692 LPSTR textA = (LPSTR)lParam; 02693 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02694 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02695 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02696 else 02697 return LB_ERRSPACE; 02698 } 02699 wParam = LISTBOX_FindFileStrPos( descr, textW ); 02700 ret = LISTBOX_InsertString( descr, wParam, textW ); 02701 if(!unicode && HAS_STRINGS(descr)) 02702 HeapFree(GetProcessHeap(), 0, textW); 02703 return ret; 02704 } 02705 02706 case LB_DELETESTRING: 02707 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR) 02708 return descr->nb_items; 02709 else 02710 { 02711 SetLastError(ERROR_INVALID_INDEX); 02712 return LB_ERR; 02713 } 02714 02715 case LB_GETITEMDATA: 02716 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) 02717 { 02718 SetLastError(ERROR_INVALID_INDEX); 02719 return LB_ERR; 02720 } 02721 return descr->items[wParam].data; 02722 02723 case LB_SETITEMDATA: 02724 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) 02725 { 02726 SetLastError(ERROR_INVALID_INDEX); 02727 return LB_ERR; 02728 } 02729 descr->items[wParam].data = lParam; 02730 /* undocumented: returns TRUE, not LB_OKAY (0) */ 02731 return TRUE; 02732 02733 case LB_GETCOUNT: 02734 return descr->nb_items; 02735 02736 case LB_GETTEXT: 02737 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode ); 02738 02739 case LB_GETTEXTLEN: 02740 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0) 02741 { 02742 SetLastError(ERROR_INVALID_INDEX); 02743 return LB_ERR; 02744 } 02745 if (!HAS_STRINGS(descr)) return sizeof(DWORD); 02746 if (unicode) return strlenW( descr->items[wParam].str ); 02747 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str, 02748 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL ); 02749 02750 case LB_GETCURSEL: 02751 if (descr->nb_items == 0) 02752 return LB_ERR; 02753 if (!IS_MULTISELECT(descr)) 02754 return descr->selected_item; 02755 if (descr->selected_item != -1) 02756 return descr->selected_item; 02757 return descr->focus_item; 02758 /* otherwise, if the user tries to move the selection with the */ 02759 /* arrow keys, we will give the application something to choke on */ 02760 case LB_GETTOPINDEX: 02761 return descr->top_item; 02762 02763 case LB_GETITEMHEIGHT: 02764 return LISTBOX_GetItemHeight( descr, wParam ); 02765 02766 case LB_SETITEMHEIGHT: 02767 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE ); 02768 02769 case LB_ITEMFROMPOINT: 02770 { 02771 POINT pt; 02772 RECT rect; 02773 int index; 02774 BOOL hit = TRUE; 02775 02776 /* The hiword of the return value is not a client area 02777 hittest as suggested by MSDN, but rather a hittest on 02778 the returned listbox item. */ 02779 02780 if(descr->nb_items == 0) 02781 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */ 02782 02783 pt.x = (short)LOWORD(lParam); 02784 pt.y = (short)HIWORD(lParam); 02785 02786 SetRect(&rect, 0, 0, descr->width, descr->height); 02787 02788 if(!PtInRect(&rect, pt)) 02789 { 02790 pt.x = min(pt.x, rect.right - 1); 02791 pt.x = max(pt.x, 0); 02792 pt.y = min(pt.y, rect.bottom - 1); 02793 pt.y = max(pt.y, 0); 02794 hit = FALSE; 02795 } 02796 02797 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y); 02798 02799 if(index == -1) 02800 { 02801 index = descr->nb_items - 1; 02802 hit = FALSE; 02803 } 02804 return MAKELONG(index, hit ? 0 : 1); 02805 } 02806 02807 case LB_SETCARETINDEX: 02808 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR; 02809 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR) 02810 return LB_ERR; 02811 else if (ISWIN31) 02812 return wParam; 02813 else 02814 return LB_OKAY; 02815 02816 case LB_GETCARETINDEX: 02817 return descr->focus_item; 02818 02819 case LB_SETTOPINDEX: 02820 return LISTBOX_SetTopItem( descr, wParam, TRUE ); 02821 02822 case LB_SETCOLUMNWIDTH: 02823 return LISTBOX_SetColumnWidth( descr, wParam ); 02824 02825 case LB_GETITEMRECT: 02826 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam ); 02827 02828 case LB_FINDSTRING: 02829 { 02830 INT ret; 02831 LPWSTR textW; 02832 if(unicode || !HAS_STRINGS(descr)) 02833 textW = (LPWSTR)lParam; 02834 else 02835 { 02836 LPSTR textA = (LPSTR)lParam; 02837 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02838 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02839 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02840 } 02841 ret = LISTBOX_FindString( descr, wParam, textW, FALSE ); 02842 if(!unicode && HAS_STRINGS(descr)) 02843 HeapFree(GetProcessHeap(), 0, textW); 02844 return ret; 02845 } 02846 02847 case LB_FINDSTRINGEXACT: 02848 { 02849 INT ret; 02850 LPWSTR textW; 02851 if(unicode || !HAS_STRINGS(descr)) 02852 textW = (LPWSTR)lParam; 02853 else 02854 { 02855 LPSTR textA = (LPSTR)lParam; 02856 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02857 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02858 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02859 } 02860 ret = LISTBOX_FindString( descr, wParam, textW, TRUE ); 02861 if(!unicode && HAS_STRINGS(descr)) 02862 HeapFree(GetProcessHeap(), 0, textW); 02863 return ret; 02864 } 02865 02866 case LB_SELECTSTRING: 02867 { 02868 INT index; 02869 LPWSTR textW; 02870 02871 if(HAS_STRINGS(descr)) 02872 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) : 02873 debugstr_a((LPSTR)lParam)); 02874 if(unicode || !HAS_STRINGS(descr)) 02875 textW = (LPWSTR)lParam; 02876 else 02877 { 02878 LPSTR textA = (LPSTR)lParam; 02879 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02880 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02881 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02882 } 02883 index = LISTBOX_FindString( descr, wParam, textW, FALSE ); 02884 if(!unicode && HAS_STRINGS(descr)) 02885 HeapFree(GetProcessHeap(), 0, textW); 02886 if (index != LB_ERR) 02887 { 02888 LISTBOX_MoveCaret( descr, index, TRUE ); 02889 LISTBOX_SetSelection( descr, index, TRUE, FALSE ); 02890 } 02891 return index; 02892 } 02893 02894 case LB_GETSEL: 02895 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) 02896 return LB_ERR; 02897 return descr->items[wParam].selected; 02898 02899 case LB_SETSEL: 02900 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE ); 02901 02902 case LB_SETCURSEL: 02903 if (IS_MULTISELECT(descr)) return LB_ERR; 02904 LISTBOX_SetCaretIndex( descr, wParam, FALSE ); 02905 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE ); 02906 if (ret != LB_ERR) ret = descr->selected_item; 02907 return ret; 02908 02909 case LB_GETSELCOUNT: 02910 return LISTBOX_GetSelCount( descr ); 02911 02912 case LB_GETSELITEMS: 02913 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam ); 02914 02915 case LB_SELITEMRANGE: 02916 if (LOWORD(lParam) <= HIWORD(lParam)) 02917 return LISTBOX_SelectItemRange( descr, LOWORD(lParam), 02918 HIWORD(lParam), wParam ); 02919 else 02920 return LISTBOX_SelectItemRange( descr, HIWORD(lParam), 02921 LOWORD(lParam), wParam ); 02922 02923 case LB_SELITEMRANGEEX: 02924 if ((INT)lParam >= (INT)wParam) 02925 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE ); 02926 else 02927 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE); 02928 02929 case LB_GETHORIZONTALEXTENT: 02930 return descr->horz_extent; 02931 02932 case LB_SETHORIZONTALEXTENT: 02933 return LISTBOX_SetHorizontalExtent( descr, wParam ); 02934 02935 case LB_GETANCHORINDEX: 02936 return descr->anchor_item; 02937 02938 case LB_SETANCHORINDEX: 02939 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items)) 02940 { 02941 SetLastError(ERROR_INVALID_INDEX); 02942 return LB_ERR; 02943 } 02944 descr->anchor_item = (INT)wParam; 02945 return LB_OKAY; 02946 02947 case LB_DIR: 02948 { 02949 INT ret; 02950 LPWSTR textW; 02951 if(unicode) 02952 textW = (LPWSTR)lParam; 02953 else 02954 { 02955 LPSTR textA = (LPSTR)lParam; 02956 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 02957 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 02958 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 02959 } 02960 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR ); 02961 if(!unicode) 02962 HeapFree(GetProcessHeap(), 0, textW); 02963 return ret; 02964 } 02965 02966 case LB_GETLOCALE: 02967 return descr->locale; 02968 02969 case LB_SETLOCALE: 02970 { 02971 LCID ret; 02972 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED)) 02973 return LB_ERR; 02974 ret = descr->locale; 02975 descr->locale = (LCID)wParam; 02976 return ret; 02977 } 02978 02979 case LB_INITSTORAGE: 02980 return LISTBOX_InitStorage( descr, wParam ); 02981 02982 case LB_SETCOUNT: 02983 return LISTBOX_SetCount( descr, (INT)wParam ); 02984 02985 case LB_SETTABSTOPS: 02986 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam ); 02987 02988 case LB_CARETON: 02989 if (descr->caret_on) 02990 return LB_OKAY; 02991 descr->caret_on = TRUE; 02992 if ((descr->focus_item != -1) && (descr->in_focus)) 02993 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); 02994 return LB_OKAY; 02995 02996 case LB_CARETOFF: 02997 if (!descr->caret_on) 02998 return LB_OKAY; 02999 descr->caret_on = FALSE; 03000 if ((descr->focus_item != -1) && (descr->in_focus)) 03001 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); 03002 return LB_OKAY; 03003 03004 case LB_GETLISTBOXINFO: 03005 FIXME("LB_GETLISTBOXINFO: stub!\n"); 03006 return 0; 03007 03008 case WM_DESTROY: 03009 return LISTBOX_Destroy( descr ); 03010 03011 case WM_ENABLE: 03012 InvalidateRect( descr->self, NULL, TRUE ); 03013 return 0; 03014 03015 case WM_SETREDRAW: 03016 LISTBOX_SetRedraw( descr, wParam != 0 ); 03017 return 0; 03018 03019 case WM_GETDLGCODE: 03020 return DLGC_WANTARROWS | DLGC_WANTCHARS; 03021 03022 case WM_PRINTCLIENT: 03023 case WM_PAINT: 03024 { 03025 PAINTSTRUCT ps; 03026 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps ); 03027 ret = LISTBOX_Paint( descr, hdc ); 03028 if( !wParam ) EndPaint( descr->self, &ps ); 03029 } 03030 return ret; 03031 case WM_SIZE: 03032 LISTBOX_UpdateSize( descr ); 03033 return 0; 03034 case WM_GETFONT: 03035 return (LRESULT)descr->font; 03036 case WM_SETFONT: 03037 LISTBOX_SetFont( descr, (HFONT)wParam ); 03038 if (lParam) InvalidateRect( descr->self, 0, TRUE ); 03039 return 0; 03040 case WM_SETFOCUS: 03041 descr->in_focus = TRUE; 03042 descr->caret_on = TRUE; 03043 if (descr->focus_item != -1) 03044 LISTBOX_DrawFocusRect( descr, TRUE ); 03045 SEND_NOTIFICATION( descr, LBN_SETFOCUS ); 03046 return 0; 03047 case WM_KILLFOCUS: 03048 descr->in_focus = FALSE; 03049 if ((descr->focus_item != -1) && descr->caret_on) 03050 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); 03051 SEND_NOTIFICATION( descr, LBN_KILLFOCUS ); 03052 return 0; 03053 case WM_HSCROLL: 03054 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) ); 03055 case WM_VSCROLL: 03056 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) ); 03057 case WM_MOUSEWHEEL: 03058 if (wParam & (MK_SHIFT | MK_CONTROL)) 03059 return DefWindowProcW( descr->self, msg, wParam, lParam ); 03060 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) ); 03061 case WM_LBUTTONDOWN: 03062 if (lphc) 03063 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam, 03064 (INT16)LOWORD(lParam), 03065 (INT16)HIWORD(lParam) ); 03066 return LISTBOX_HandleLButtonDown( descr, wParam, 03067 (INT16)LOWORD(lParam), 03068 (INT16)HIWORD(lParam) ); 03069 case WM_LBUTTONDBLCLK: 03070 if (lphc) 03071 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam, 03072 (INT16)LOWORD(lParam), 03073 (INT16)HIWORD(lParam) ); 03074 if (descr->style & LBS_NOTIFY) 03075 SEND_NOTIFICATION( descr, LBN_DBLCLK ); 03076 return 0; 03077 case WM_MOUSEMOVE: 03078 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) ) 03079 { 03080 BOOL captured = descr->captured; 03081 POINT mousePos; 03082 RECT clientRect; 03083 03084 mousePos.x = (INT16)LOWORD(lParam); 03085 mousePos.y = (INT16)HIWORD(lParam); 03086 03087 /* 03088 * If we are in a dropdown combobox, we simulate that 03089 * the mouse is captured to show the tracking of the item. 03090 */ 03091 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos )) 03092 descr->captured = TRUE; 03093 03094 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y); 03095 03096 descr->captured = captured; 03097 } 03098 else if (GetCapture() == descr->self) 03099 { 03100 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam), 03101 (INT16)HIWORD(lParam) ); 03102 } 03103 return 0; 03104 case WM_LBUTTONUP: 03105 if (lphc) 03106 { 03107 POINT mousePos; 03108 RECT clientRect; 03109 03110 /* 03111 * If the mouse button "up" is not in the listbox, 03112 * we make sure there is no selection by re-selecting the 03113 * item that was selected when the listbox was made visible. 03114 */ 03115 mousePos.x = (INT16)LOWORD(lParam); 03116 mousePos.y = (INT16)HIWORD(lParam); 03117 03118 GetClientRect(descr->self, &clientRect); 03119 03120 /* 03121 * When the user clicks outside the combobox and the focus 03122 * is lost, the owning combobox will send a fake buttonup with 03123 * 0xFFFFFFF as the mouse location, we must also revert the 03124 * selection to the original selection. 03125 */ 03126 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) ) 03127 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE ); 03128 } 03129 return LISTBOX_HandleLButtonUp( descr ); 03130 case WM_KEYDOWN: 03131 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE ) 03132 { 03133 /* for some reason Windows makes it possible to 03134 * show/hide ComboLBox by sending it WM_KEYDOWNs */ 03135 03136 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) || 03137 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED) 03138 && (wParam == VK_DOWN || wParam == VK_UP)) ) 03139 { 03140 COMBO_FlipListbox( lphc, FALSE, FALSE ); 03141 return 0; 03142 } 03143 } 03144 return LISTBOX_HandleKeyDown( descr, wParam ); 03145 case WM_CHAR: 03146 { 03147 WCHAR charW; 03148 if(unicode) 03149 charW = (WCHAR)wParam; 03150 else 03151 { 03152 CHAR charA = (CHAR)wParam; 03153 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1); 03154 } 03155 return LISTBOX_HandleChar( descr, charW ); 03156 } 03157 case WM_SYSTIMER: 03158 return LISTBOX_HandleSystemTimer( descr ); 03159 case WM_ERASEBKGND: 03160 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED)) 03161 { 03162 RECT rect; 03163 HBRUSH hbrush = GetControlColor( descr->owner, descr->self, (HDC)wParam, WM_CTLCOLORLISTBOX); 03164 TRACE("hbrush = %p\n", hbrush); 03165 if(!hbrush) 03166 hbrush = GetSysColorBrush(COLOR_WINDOW); 03167 if(hbrush) 03168 { 03169 GetClientRect(descr->self, &rect); 03170 FillRect((HDC)wParam, &rect, hbrush); 03171 } 03172 } 03173 return 1; 03174 case WM_DROPFILES: 03175 if( lphc ) return 0; 03176 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) : 03177 SendMessageA( descr->owner, msg, wParam, lParam ); 03178 03179 case WM_NCDESTROY: 03180 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE ) 03181 lphc->hWndLBox = 0; 03182 #ifdef __REACTOS__ 03183 NtUserSetWindowFNID(hwnd, FNID_DESTROY); 03184 #endif 03185 break; 03186 03187 case WM_NCACTIVATE: 03188 if (lphc) return 0; 03189 break; 03190 // ReactOS 03191 case WM_UPDATEUISTATE: 03192 if (unicode) 03193 DefWindowProcW(descr->self, msg, wParam, lParam); 03194 else 03195 DefWindowProcA(descr->self, msg, wParam, lParam); 03196 03197 if (LISTBOX_update_uistate(descr)) 03198 { 03199 /* redraw text */ 03200 if (descr->focus_item != -1) 03201 LISTBOX_DrawFocusRect( descr, descr->in_focus ); 03202 } 03203 break; 03204 // 03205 default: 03206 if ((msg >= WM_USER) && (msg < 0xc000)) 03207 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n", 03208 hwnd, msg, wParam, lParam ); 03209 } 03210 03211 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) : 03212 DefWindowProcA( hwnd, msg, wParam, lParam ); 03213 } 03214 03215 /*********************************************************************** 03216 * ListBoxWndProcA 03217 */ 03218 LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) 03219 { 03220 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE ); 03221 } 03222 03223 /*********************************************************************** 03224 * ListBoxWndProcW 03225 */ 03226 LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) 03227 { 03228 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE ); 03229 } Generated on Sun May 27 2012 04:38:41 for ReactOS by
1.7.6.1
|