1 /*
2 * Tab control
3 *
4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 * Copyright 2003 Vitaliy Margolen
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 * NOTES
24 *
25 * This code was audited for completeness against the documented features
26 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
27 *
28 * Unless otherwise noted, we believe this code to be complete, as per
29 * the specification mentioned above.
30 * If you discover missing features, or bugs, please note them below.
31 *
32 * TODO:
33 *
34 * Styles:
35 * TCS_MULTISELECT
36 * TCS_RIGHT
37 * TCS_RIGHTJUSTIFY
38 * TCS_SCROLLOPPOSITE
39 * TCS_SINGLELINE
40 * TCIF_RTLREADING
41 *
42 * Extended Styles:
43 * TCS_EX_FLATSEPARATORS
44 * TCS_EX_REGISTERDROP
45 *
46 * States:
47 * TCIS_BUTTONPRESSED
48 *
49 * Notifications:
50 * NM_RELEASEDCAPTURE
51 * TCN_FOCUSCHANGE
52 * TCN_GETOBJECT
53 * TCN_KEYDOWN
54 *
55 * Messages:
56 * TCM_REMOVEIMAGE
57 * TCM_DESELECTALL
58 * TCM_GETEXTENDEDSTYLE
59 * TCM_SETEXTENDEDSTYLE
60 *
61 * Macros:
62 * TabCtrl_AdjustRect
63 *
64 */
65
66 #include <stdarg.h>
67 #include <string.h>
68
69 #include "windef.h"
70 #include "winbase.h"
71 #include "wingdi.h"
72 #include "winuser.h"
73 #include "winnls.h"
74 #include "commctrl.h"
75 #include "comctl32.h"
76 #include "uxtheme.h"
77 #include "tmschema.h"
78 #include "wine/debug.h"
79 #include <math.h>
80
81 WINE_DEFAULT_DEBUG_CHANNEL(tab);
82
83 typedef struct
84 {
85 DWORD dwState;
86 LPWSTR pszText;
87 INT iImage;
88 RECT rect; /* bounding rectangle of the item relative to the
89 * leftmost item (the leftmost item, 0, would have a
90 * "left" member of 0 in this rectangle)
91 *
92 * additionally the top member holds the row number
93 * and bottom is unused and should be 0 */
94 BYTE extra[1]; /* Space for caller supplied info, variable size */
95 } TAB_ITEM;
96
97 /* The size of a tab item depends on how much extra data is requested */
98 #define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo]))
99
100 typedef struct
101 {
102 HWND hwnd; /* Tab control window */
103 HWND hwndNotify; /* notification window (parent) */
104 UINT uNumItem; /* number of tab items */
105 UINT uNumRows; /* number of tab rows */
106 INT tabHeight; /* height of the tab row */
107 INT tabWidth; /* width of tabs */
108 INT tabMinWidth; /* minimum width of items */
109 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
110 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
111 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
112 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
113 HFONT hFont; /* handle to the current font */
114 HCURSOR hcurArrow; /* handle to the current cursor */
115 HIMAGELIST himl; /* handle to an image list (may be 0) */
116 HWND hwndToolTip; /* handle to tab's tooltip */
117 INT leftmostVisible; /* Used for scrolling, this member contains
118 * the index of the first visible item */
119 INT iSelected; /* the currently selected item */
120 INT iHotTracked; /* the highlighted item under the mouse */
121 INT uFocus; /* item which has the focus */
122 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
123 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
124 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
125 * the size of the control */
126 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
127 BOOL bUnicode; /* Unicode control? */
128 HWND hwndUpDown; /* Updown control used for scrolling */
129 INT cbInfo; /* Number of bytes of caller supplied info per tab */
130 } TAB_INFO;
131
132 /******************************************************************************
133 * Positioning constants
134 */
135 #define SELECTED_TAB_OFFSET 2
136 #define ROUND_CORNER_SIZE 2
137 #define DISPLAY_AREA_PADDINGX 2
138 #define DISPLAY_AREA_PADDINGY 2
139 #define CONTROL_BORDER_SIZEX 2
140 #define CONTROL_BORDER_SIZEY 2
141 #define BUTTON_SPACINGX 3
142 #define BUTTON_SPACINGY 3
143 #define FLAT_BTN_SPACINGX 8
144 #define DEFAULT_MIN_TAB_WIDTH 54
145 #define DEFAULT_TAB_WIDTH_FIXED 96
146 #define DEFAULT_PADDING_X 6
147 #define EXTRA_ICON_PADDING 3
148
149 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
150 /* Since items are variable sized, cannot directly access them */
151 #define TAB_GetItem(info,i) \
152 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
153
154 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
155
156 /******************************************************************************
157 * Hot-tracking timer constants
158 */
159 #define TAB_HOTTRACK_TIMER 1
160 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
161
162 static const WCHAR themeClass[] = { 'T','a','b',0 };
163
164 /******************************************************************************
165 * Prototypes
166 */
167 static void TAB_InvalidateTabArea(const TAB_INFO *);
168 static void TAB_EnsureSelectionVisible(TAB_INFO *);
169 static void TAB_DrawItemInterior(const TAB_INFO *, HDC, INT, RECT*);
170
171 static BOOL
172 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
173 {
174 NMHDR nmhdr;
175
176 nmhdr.hwndFrom = infoPtr->hwnd;
177 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
178 nmhdr.code = code;
179
180 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
181 nmhdr.idFrom, (LPARAM) &nmhdr);
182 }
183
184 static void
185 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
186 WPARAM wParam, LPARAM lParam)
187 {
188 MSG msg;
189
190 msg.hwnd = hwndMsg;
191 msg.message = uMsg;
192 msg.wParam = wParam;
193 msg.lParam = lParam;
194 msg.time = GetMessageTime ();
195 msg.pt.x = (short)LOWORD(GetMessagePos ());
196 msg.pt.y = (short)HIWORD(GetMessagePos ());
197
198 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
199 }
200
201 static void
202 TAB_DumpItemExternalT(const TCITEMW *pti, UINT iItem, BOOL isW)
203 {
204 if (TRACE_ON(tab)) {
205 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
206 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
207 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
208 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
209 }
210 }
211
212 static void
213 TAB_DumpItemInternal(const TAB_INFO *infoPtr, UINT iItem)
214 {
215 if (TRACE_ON(tab)) {
216 TAB_ITEM *ti;
217
218 ti = TAB_GetItem(infoPtr, iItem);
219 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
220 iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage);
221 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
222 iItem, ti->rect.left, ti->rect.top);
223 }
224 }
225
226 /* RETURNS
227 * the index of the selected tab, or -1 if no tab is selected. */
228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
229 {
230 return infoPtr->iSelected;
231 }
232
233 /* RETURNS
234 * the index of the tab item that has the focus. */
235 static inline LRESULT
236 TAB_GetCurFocus (const TAB_INFO *infoPtr)
237 {
238 return infoPtr->uFocus;
239 }
240
241 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
242 {
243 if (infoPtr == NULL) return 0;
244 return (LRESULT)infoPtr->hwndToolTip;
245 }
246
247 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
248 {
249 INT prevItem = infoPtr->iSelected;
250
251 if (iItem < 0)
252 infoPtr->iSelected=-1;
253 else if (iItem >= infoPtr->uNumItem)
254 return -1;
255 else {
256 if (infoPtr->iSelected != iItem) {
257 infoPtr->iSelected=iItem;
258 infoPtr->uFocus=iItem;
259 TAB_EnsureSelectionVisible(infoPtr);
260 TAB_InvalidateTabArea(infoPtr);
261 }
262 }
263 return prevItem;
264 }
265
266 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
267 {
268 if (iItem < 0)
269 infoPtr->uFocus = -1;
270 else if (iItem < infoPtr->uNumItem) {
271 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
272 FIXME("Should set input focus\n");
273 } else {
274 int oldFocus = infoPtr->uFocus;
275 if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
276 infoPtr->uFocus = iItem;
277 if (oldFocus != -1) {
278 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
279 infoPtr->iSelected = iItem;
280 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
281 }
282 else
283 infoPtr->iSelected = iItem;
284 TAB_EnsureSelectionVisible(infoPtr);
285 TAB_InvalidateTabArea(infoPtr);
286 }
287 }
288 }
289 }
290 return 0;
291 }
292
293 static inline LRESULT
294 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
295 {
296 if (infoPtr)
297 infoPtr->hwndToolTip = hwndToolTip;
298 return 0;
299 }
300
301 static inline LRESULT
302 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
303 {
304 if (infoPtr)
305 {
306 infoPtr->uHItemPadding_s=LOWORD(lParam);
307 infoPtr->uVItemPadding_s=HIWORD(lParam);
308 }
309 return 0;
310 }
311
312 /******************************************************************************
313 * TAB_InternalGetItemRect
314 *
315 * This method will calculate the rectangle representing a given tab item in
316 * client coordinates. This method takes scrolling into account.
317 *
318 * This method returns TRUE if the item is visible in the window and FALSE
319 * if it is completely outside the client area.
320 */
321 static BOOL TAB_InternalGetItemRect(
322 const TAB_INFO* infoPtr,
323 INT itemIndex,
324 RECT* itemRect,
325 RECT* selectedRect)
326 {
327 RECT tmpItemRect,clientRect;
328 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
329
330 /* Perform a sanity check and a trivial visibility check. */
331 if ( (infoPtr->uNumItem <= 0) ||
332 (itemIndex >= infoPtr->uNumItem) ||
333 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
334 {
335 TRACE("Not Visible\n");
336 /* need to initialize these to empty rects */
337 if (itemRect)
338 {
339 memset(itemRect,0,sizeof(RECT));
340 itemRect->bottom = infoPtr->tabHeight;
341 }
342 if (selectedRect)
343 memset(selectedRect,0,sizeof(RECT));
344 return FALSE;
345 }
346
347 /*
348 * Avoid special cases in this procedure by assigning the "out"
349 * parameters if the caller didn't supply them
350 */
351 if (itemRect == NULL)
352 itemRect = &tmpItemRect;
353
354 /* Retrieve the unmodified item rect. */
355 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
356
357 /* calculate the times bottom and top based on the row */
358 GetClientRect(infoPtr->hwnd, &clientRect);
359
360 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
361 {
362 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
363 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
364 itemRect->left = itemRect->right - infoPtr->tabHeight;
365 }
366 else if (lStyle & TCS_VERTICAL)
367 {
368 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
369 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
370 itemRect->right = itemRect->left + infoPtr->tabHeight;
371 }
372 else if (lStyle & TCS_BOTTOM)
373 {
374 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
375 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
376 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
377 }
378 else /* not TCS_BOTTOM and not TCS_VERTICAL */
379 {
380 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
381 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
382 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
383 }
384
385 /*
386 * "scroll" it to make sure the item at the very left of the
387 * tab control is the leftmost visible tab.
388 */
389 if(lStyle & TCS_VERTICAL)
390 {
391 OffsetRect(itemRect,
392 0,
393 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
394
395 /*
396 * Move the rectangle so the first item is slightly offset from
397 * the bottom of the tab control.
398 */
399 OffsetRect(itemRect,
400 0,
401 SELECTED_TAB_OFFSET);
402
403 } else
404 {
405 OffsetRect(itemRect,
406 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
407 0);
408
409 /*
410 * Move the rectangle so the first item is slightly offset from
411 * the left of the tab control.
412 */
413 OffsetRect(itemRect,
414 SELECTED_TAB_OFFSET,
415 0);
416 }
417 TRACE("item %d tab h=%d, rect=(%s)\n",
418 itemIndex, infoPtr->tabHeight, wine_dbgstr_rect(itemRect));
419
420 /* Now, calculate the position of the item as if it were selected. */
421 if (selectedRect!=NULL)
422 {
423 CopyRect(selectedRect, itemRect);
424
425 /* The rectangle of a selected item is a bit wider. */
426 if(lStyle & TCS_VERTICAL)
427 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
428 else
429 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
430
431 /* If it also a bit higher. */
432 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
433 {
434 selectedRect->left -= 2; /* the border is thicker on the right */
435 selectedRect->right += SELECTED_TAB_OFFSET;
436 }
437 else if (lStyle & TCS_VERTICAL)
438 {
439 selectedRect->left -= SELECTED_TAB_OFFSET;
440 selectedRect->right += 1;
441 }
442 else if (lStyle & TCS_BOTTOM)
443 {
444 selectedRect->bottom += SELECTED_TAB_OFFSET;
445 }
446 else /* not TCS_BOTTOM and not TCS_VERTICAL */
447 {
448 selectedRect->top -= SELECTED_TAB_OFFSET;
449 selectedRect->bottom -= 1;
450 }
451 }
452
453 /* Check for visibility */
454 if (lStyle & TCS_VERTICAL)
455 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
456 else
457 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
458 }
459
460 static inline BOOL
461 TAB_GetItemRect(const TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
462 {
463 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
464 }
465
466 /******************************************************************************
467 * TAB_KeyUp
468 *
469 * This method is called to handle keyboard input
470 */
471 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
472 {
473 int newItem = -1;
474
475 switch (keyCode)
476 {
477 case VK_LEFT:
478 newItem = infoPtr->uFocus - 1;
479 break;
480 case VK_RIGHT:
481 newItem = infoPtr->uFocus + 1;
482 break;
483 }
484
485 /*
486 * If we changed to a valid item, change the selection
487 */
488 if (newItem >= 0 &&
489 newItem < infoPtr->uNumItem &&
490 infoPtr->uFocus != newItem)
491 {
492 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
493 {
494 infoPtr->iSelected = newItem;
495 infoPtr->uFocus = newItem;
496 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
497
498 TAB_EnsureSelectionVisible(infoPtr);
499 TAB_InvalidateTabArea(infoPtr);
500 }
501 }
502
503 return 0;
504 }
505
506 /******************************************************************************
507 * TAB_FocusChanging
508 *
509 * This method is called whenever the focus goes in or out of this control
510 * it is used to update the visual state of the control.
511 */
512 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
513 {
514 RECT selectedRect;
515 BOOL isVisible;
516
517 /*
518 * Get the rectangle for the item.
519 */
520 isVisible = TAB_InternalGetItemRect(infoPtr,
521 infoPtr->uFocus,
522 NULL,
523 &selectedRect);
524
525 /*
526 * If the rectangle is not completely invisible, invalidate that
527 * portion of the window.
528 */
529 if (isVisible)
530 {
531 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect));
532 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
533 }
534 }
535
536 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags)
537 {
538 RECT rect;
539 INT iCount;
540
541 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
542 {
543 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
544
545 if (PtInRect(&rect, pt))
546 {
547 *flags = TCHT_ONITEM;
548 return iCount;
549 }
550 }
551
552 *flags = TCHT_NOWHERE;
553 return -1;
554 }
555
556 static inline LRESULT
557 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
558 {
559 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
560 }
561
562 /******************************************************************************
563 * TAB_NCHitTest
564 *
565 * Napster v2b5 has a tab control for its main navigation which has a client
566 * area that covers the whole area of the dialog pages.
567 * That's why it receives all msgs for that area and the underlying dialog ctrls
568 * are dead.
569 * So I decided that we should handle WM_NCHITTEST here and return
570 * HTTRANSPARENT if we don't hit the tab control buttons.
571 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
572 * doesn't do it that way. Maybe depends on tab control styles ?
573 */
574 static inline LRESULT
575 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam)
576 {
577 POINT pt;
578 UINT dummyflag;
579
580 pt.x = (short)LOWORD(lParam);
581 pt.y = (short)HIWORD(lParam);
582 ScreenToClient(infoPtr->hwnd, &pt);
583
584 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
585 return HTTRANSPARENT;
586 else
587 return HTCLIENT;
588 }
589
590 static LRESULT
591 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
592 {
593 POINT pt;
594 INT newItem;
595 UINT dummy;
596
597 if (infoPtr->hwndToolTip)
598 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
599 WM_LBUTTONDOWN, wParam, lParam);
600
601 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
602 SetFocus (infoPtr->hwnd);
603 }
604
605 if (infoPtr->hwndToolTip)
606 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
607 WM_LBUTTONDOWN, wParam, lParam);
608
609 pt.x = (short)LOWORD(lParam);
610 pt.y = (short)HIWORD(lParam);
611
612 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
613
614 TRACE("On Tab, item %d\n", newItem);
615
616 if (newItem != -1 && infoPtr->iSelected != newItem)
617 {
618 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
619 {
620 infoPtr->iSelected = newItem;
621 infoPtr->uFocus = newItem;
622 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
623
624 TAB_EnsureSelectionVisible(infoPtr);
625
626 TAB_InvalidateTabArea(infoPtr);
627 }
628 }
629 return 0;
630 }
631
632 static inline LRESULT
633 TAB_LButtonUp (const TAB_INFO *infoPtr)
634 {
635 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
636
637 return 0;
638 }
639
640 static inline LRESULT
641 TAB_RButtonDown (const TAB_INFO *infoPtr)
642 {
643 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
644 return 0;
645 }
646
647 /******************************************************************************
648 * TAB_DrawLoneItemInterior
649 *
650 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
651 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
652 * up the device context and font. This routine does the same setup but
653 * only calls TAB_DrawItemInterior for the single specified item.
654 */
655 static void
656 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem)
657 {
658 HDC hdc = GetDC(infoPtr->hwnd);
659 RECT r, rC;
660
661 /* Clip UpDown control to not draw over it */
662 if (infoPtr->needsScrolling)
663 {
664 GetWindowRect(infoPtr->hwnd, &rC);
665 GetWindowRect(infoPtr->hwndUpDown, &r);
666 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
667 }
668 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
669 ReleaseDC(infoPtr->hwnd, hdc);
670 }
671
672 /* update a tab after hottracking - invalidate it or just redraw the interior,
673 * based on whether theming is used or not */
674 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex)
675 {
676 if (tabIndex == -1) return;
677
678 if (GetWindowTheme (infoPtr->hwnd))
679 {
680 RECT rect;
681 TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
682 InvalidateRect (infoPtr->hwnd, &rect, FALSE);
683 }
684 else
685 TAB_DrawLoneItemInterior(infoPtr, tabIndex);
686 }
687
688 /******************************************************************************
689 * TAB_HotTrackTimerProc
690 *
691 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
692 * timer is setup so we can check if the mouse is moved out of our window.
693 * (We don't get an event when the mouse leaves, the mouse-move events just
694 * stop being delivered to our window and just start being delivered to
695 * another window.) This function is called when the timer triggers so
696 * we can check if the mouse has left our window. If so, we un-highlight
697 * the hot-tracked tab.
698 */
699 static void CALLBACK
700 TAB_HotTrackTimerProc
701 (
702 HWND hwnd, /* handle of window for timer messages */
703 UINT uMsg, /* WM_TIMER message */
704 UINT_PTR idEvent, /* timer identifier */
705 DWORD dwTime /* current system time */
706 )
707 {
708 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
709
710 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
711 {
712 POINT pt;
713
714 /*
715 ** If we can't get the cursor position, or if the cursor is outside our
716 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
717 ** "outside" even if it is within our bounding rect if another window
718 ** overlaps. Note also that the case where the cursor stayed within our
719 ** window but has moved off the hot-tracked tab will be handled by the
720 ** WM_MOUSEMOVE event.
721 */
722 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
723 {
724 /* Redraw iHotTracked to look normal */
725 INT iRedraw = infoPtr->iHotTracked;
726 infoPtr->iHotTracked = -1;
727 hottrack_refresh (infoPtr, iRedraw);
728
729 /* Kill this timer */
730 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
731 }
732 }
733 }
734
735 /******************************************************************************
736 * TAB_RecalcHotTrack
737 *
738 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
739 * should be highlighted. This function determines which tab in a tab control,
740 * if any, is under the mouse and records that information. The caller may
741 * supply output parameters to receive the item number of the tab item which
742 * was highlighted but isn't any longer and of the tab item which is now
743 * highlighted but wasn't previously. The caller can use this information to
744 * selectively redraw those tab items.
745 *
746 * If the caller has a mouse position, it can supply it through the pos
747 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
748 * supplies NULL and this function determines the current mouse position
749 * itself.
750 */
751 static void
752 TAB_RecalcHotTrack
753 (
754 TAB_INFO* infoPtr,
755 const LPARAM* pos,
756 int* out_redrawLeave,
757 int* out_redrawEnter
758 )
759 {
760 int item = -1;
761
762
763 if (out_redrawLeave != NULL)
764 *out_redrawLeave = -1;
765 if (out_redrawEnter != NULL)
766 *out_redrawEnter = -1;
767
768 if ((GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
769 || GetWindowTheme (infoPtr->hwnd))
770 {
771 POINT pt;
772 UINT flags;
773
774 if (pos == NULL)
775 {
776 GetCursorPos(&pt);
777 ScreenToClient(infoPtr->hwnd, &pt);
778 }
779 else
780 {
781 pt.x = (short)LOWORD(*pos);
782 pt.y = (short)HIWORD(*pos);
783 }
784
785 item = TAB_InternalHitTest(infoPtr, pt, &flags);
786 }
787
788 if (item != infoPtr->iHotTracked)
789 {
790 if (infoPtr->iHotTracked >= 0)
791 {
792 /* Mark currently hot-tracked to be redrawn to look normal */
793 if (out_redrawLeave != NULL)
794 *out_redrawLeave = infoPtr->iHotTracked;
795
796 if (item < 0)
797 {
798 /* Kill timer which forces recheck of mouse pos */
799 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
800 }
801 }
802 else
803 {
804 /* Start timer so we recheck mouse pos */
805 UINT timerID = SetTimer
806 (
807 infoPtr->hwnd,
808 TAB_HOTTRACK_TIMER,
809 TAB_HOTTRACK_TIMER_INTERVAL,
810 TAB_HotTrackTimerProc
811 );
812
813 if (timerID == 0)
814 return; /* Hot tracking not available */
815 }
816
817 infoPtr->iHotTracked = item;
818
819 if (item >= 0)
820 {
821 /* Mark new hot-tracked to be redrawn to look highlighted */
822 if (out_redrawEnter != NULL)
823 *out_redrawEnter = item;
824 }
825 }
826 }
827
828 /******************************************************************************
829 * TAB_MouseMove
830 *
831 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
832 */
833 static LRESULT
834 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
835 {
836 int redrawLeave;
837 int redrawEnter;
838
839 if (infoPtr->hwndToolTip)
840 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
841 WM_LBUTTONDOWN, wParam, lParam);
842
843 /* Determine which tab to highlight. Redraw tabs which change highlight
844 ** status. */
845 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
846
847 hottrack_refresh (infoPtr, redrawLeave);
848 hottrack_refresh (infoPtr, redrawEnter);
849
850 return 0;
851 }
852
853 /******************************************************************************
854 * TAB_AdjustRect
855 *
856 * Calculates the tab control's display area given the window rectangle or
857 * the window rectangle given the requested display rectangle.
858 */
859 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc)
860 {
861 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
862 LONG *iRightBottom, *iLeftTop;
863
864 TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr->hwnd, fLarger,
865 wine_dbgstr_rect(prc));
866
867 if(lStyle & TCS_VERTICAL)
868 {
869 iRightBottom = &(prc->right);
870 iLeftTop = &(prc->left);
871 }
872 else
873 {
874 iRightBottom = &(prc->bottom);
875 iLeftTop = &(prc->top);
876 }
877
878 if (fLarger) /* Go from display rectangle */
879 {
880 /* Add the height of the tabs. */
881 if (lStyle & TCS_BOTTOM)
882 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
883 else
884 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
885 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
886
887 /* Inflate the rectangle for the padding */
888 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
889
890 /* Inflate for the border */
891 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
892 }
893 else /* Go from window rectangle. */
894 {
895 /* Deflate the rectangle for the border */
896 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
897
898 /* Deflate the rectangle for the padding */
899 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
900
901 /* Remove the height of the tabs. */
902 if (lStyle & TCS_BOTTOM)
903 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
904 else
905 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
906 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
907 }
908
909 return 0;
910 }
911
912 /******************************************************************************
913 * TAB_OnHScroll
914 *
915 * This method will handle the notification from the scroll control and
916 * perform the scrolling operation on the tab control.
917 */
918 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos, HWND hwndScroll)
919 {
920 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
921 {
922 if(nPos < infoPtr->leftmostVisible)
923 infoPtr->leftmostVisible--;
924 else
925 infoPtr->leftmostVisible++;
926
927 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
928 TAB_InvalidateTabArea(infoPtr);
929 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
930 MAKELONG(infoPtr->leftmostVisible, 0));
931 }
932
933 return 0;
934 }
935
936 /******************************************************************************
937 * TAB_SetupScrolling
938 *
939 * This method will check the current scrolling state and make sure the
940 * scrolling control is displayed (or not).
941 */
942 static void TAB_SetupScrolling(
943 HWND hwnd,
944 TAB_INFO* infoPtr,
945 const RECT* clientRect)
946 {
947 static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
948 static const WCHAR emptyW[] = { 0 };
949 INT maxRange = 0;
950 DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE);
951
952 if (infoPtr->needsScrolling)
953 {
954 RECT controlPos;
955 INT vsize, tabwidth;
956
957 /*
958 * Calculate the position of the scroll control.
959 */
960 if(lStyle & TCS_VERTICAL)
961 {
962 controlPos.right = clientRect->right;
963 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
964
965 if (lStyle & TCS_BOTTOM)
966 {
967 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
968 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
969 }
970 else
971 {
972 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
973 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
974 }
975 }
976 else
977 {
978 controlPos.right = clientRect->right;
979 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
980
981 if (lStyle & TCS_BOTTOM)
982 {
983 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
984 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
985 }
986 else
987 {
988 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
989 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
990 }
991 }
992
993 /*
994 * If we don't have a scroll control yet, we want to create one.
995 * If we have one, we want to make sure it's positioned properly.
996 */
997 if (infoPtr->hwndUpDown==0)
998 {
999 infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW,
1000 WS_VISIBLE | WS_CHILD | UDS_HORZ,
1001 controlPos.left, controlPos.top,
1002 controlPos.right - controlPos.left,
1003 controlPos.bottom - controlPos.top,
1004 hwnd, NULL, NULL, NULL);
1005 }
1006 else
1007 {
1008 SetWindowPos(infoPtr->hwndUpDown,
1009 NULL,
1010 controlPos.left, controlPos.top,
1011 controlPos.right - controlPos.left,
1012 controlPos.bottom - controlPos.top,
1013 SWP_SHOWWINDOW | SWP_NOZORDER);
1014 }
1015
1016 /* Now calculate upper limit of the updown control range.
1017 * We do this by calculating how many tabs will be offscreen when the
1018 * last tab is visible.
1019 */
1020 if(infoPtr->uNumItem)
1021 {
1022 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1023 maxRange = infoPtr->uNumItem;
1024 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1025
1026 for(; maxRange > 0; maxRange--)
1027 {
1028 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1029 break;
1030 }
1031
1032 if(maxRange == infoPtr->uNumItem)
1033 maxRange--;
1034 }
1035 }
1036 else
1037 {
1038 /* If we once had a scroll control... hide it */
1039 if (infoPtr->hwndUpDown!=0)
1040 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1041 }
1042 if (infoPtr->hwndUpDown)
1043 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1044 }
1045
1046 /******************************************************************************
1047 * TAB_SetItemBounds
1048 *
1049 * This method will calculate the position rectangles of all the items in the
1050 * control. The rectangle calculated starts at 0 for the first item in the
1051 * list and ignores scrolling and selection.
1052 * It also uses the current font to determine the height of the tab row and
1053 * it checks if all the tabs fit in the client area of the window. If they
1054 * don't, a scrolling control is added.
1055 */
1056 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1057 {
1058 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1059 TEXTMETRICW fontMetrics;
1060 UINT curItem;
1061 INT curItemLeftPos;
1062 INT curItemRowCount;
1063 HFONT hFont, hOldFont;
1064 HDC hdc;
1065 RECT clientRect;
1066 INT iTemp;
1067 RECT* rcItem;
1068 INT iIndex;
1069 INT icon_width = 0;
1070
1071 /*
1072 * We need to get text information so we need a DC and we need to select
1073 * a font.
1074 */
1075 hdc = GetDC(infoPtr->hwnd);
1076
1077 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1078 hOldFont = SelectObject (hdc, hFont);
1079
1080 /*
1081 * We will base the rectangle calculations on the client rectangle
1082 * of the control.
1083 */
1084 GetClientRect(infoPtr->hwnd, &clientRect);
1085
1086 /* if TCS_VERTICAL then swap the height and width so this code places the
1087 tabs along the top of the rectangle and we can just rotate them after
1088 rather than duplicate all of the below code */
1089 if(lStyle & TCS_VERTICAL)
1090 {
1091 iTemp = clientRect.bottom;
1092 clientRect.bottom = clientRect.right;
1093 clientRect.right = iTemp;
1094 }
1095
1096 /* Now use hPadding and vPadding */
1097 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1098 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1099
1100 /* The leftmost item will be "" aligned */
1101 curItemLeftPos = 0;
1102 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1103
1104 if (!(infoPtr->fHeightSet))
1105 {
1106 int item_height;
1107 int icon_height = 0;
1108
1109 /* Use the current font to determine the height of a tab. */
1110 GetTextMetricsW(hdc, &fontMetrics);
1111
1112 /* Get the icon height */
1113 if (infoPtr->himl)
1114 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1115
1116 /* Take the highest between font or icon */
1117 if (fontMetrics.tmHeight > icon_height)
1118 item_height = fontMetrics.tmHeight + 2;
1119 else
1120 item_height = icon_height;
1121
1122 /*
1123 * Make sure there is enough space for the letters + icon + growing the
1124 * selected item + extra space for the selected item.
1125 */
1126 infoPtr->tabHeight = item_height +
1127 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1128 infoPtr->uVItemPadding;
1129
1130 TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1131 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1132 }
1133
1134 TRACE("client right=%d\n", clientRect.right);
1135
1136 /* Get the icon width */
1137 if (infoPtr->himl)
1138 {
1139 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1140
1141 if (lStyle & TCS_FIXEDWIDTH)
1142 icon_width += 4;
1143 else
1144 /* Add padding if icon is present */
1145 icon_width += infoPtr->uHItemPadding;
1146 }
1147
1148 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1149 {
1150 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1151
1152 /* Set the leftmost position of the tab. */
1153 curr->rect.left = curItemLeftPos;
1154
1155 if (lStyle & TCS_FIXEDWIDTH)
1156 {
1157 curr->rect.right = curr->rect.left +
1158 max(infoPtr->tabWidth, icon_width);
1159 }
1160 else if (!curr->pszText)
1161 {
1162 /* If no text use minimum tab width including padding. */
1163 if (infoPtr->tabMinWidth < 0)
1164 curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr);
1165 else
1166 {
1167 curr->rect.right = curr->rect.left + infoPtr->tabMinWidth;
1168
1169 /* Add extra padding if icon is present */
1170 if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH
1171 && infoPtr->uHItemPadding > 1)
1172 curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1);
1173 }
1174 }
1175 else
1176 {
1177 int tabwidth;
1178 SIZE size;
1179 /* Calculate how wide the tab is depending on the text it contains */
1180 GetTextExtentPoint32W(hdc, curr->pszText,
1181 lstrlenW(curr->pszText), &size);
1182
1183 tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding;
1184
1185 if (infoPtr->tabMinWidth < 0)
1186 tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr));
1187 else
1188 tabwidth = max(tabwidth, infoPtr->tabMinWidth);
1189
1190 curr->rect.right = curr->rect.left + tabwidth;
1191 TRACE("for <%s>, l,r=%d,%d\n",
1192 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right);
1193 }
1194
1195 /*
1196 * Check if this is a multiline tab control and if so
1197 * check to see if we should wrap the tabs
1198 *
1199 * Wrap all these tabs. We will arrange them evenly later.
1200 *
1201 */
1202
1203 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1204 (curr->rect.right >
1205 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1206 {
1207 curr->rect.right -= curr->rect.left;
1208
1209 curr->rect.left = 0;
1210 curItemRowCount++;
1211 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText),
1212 curr->rect.left, curr->rect.right);
1213 }
1214
1215 curr->rect.bottom = 0;
1216 curr->rect.top = curItemRowCount - 1;
1217
1218 TRACE("Rect: %s\n", wine_dbgstr_rect(&curr->rect));
1219
1220 /*
1221 * The leftmost position of the next item is the rightmost position
1222 * of this one.
1223 */
1224 if (lStyle & TCS_BUTTONS)
1225 {
1226 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1227 if (lStyle & TCS_FLATBUTTONS)
1228 curItemLeftPos += FLAT_BTN_SPACINGX;
1229 }
1230 else
1231 curItemLeftPos = curr->rect.right;
1232 }
1233
1234 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1235 {
1236 /*
1237 * Check if we need a scrolling control.
1238 */
1239 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1240 clientRect.right);
1241
1242 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1243 if(!infoPtr->needsScrolling)
1244 infoPtr->leftmostVisible = 0;
1245 }
1246 else
1247 {
1248 /*
1249 * No scrolling in Multiline or Vertical styles.
1250 */
1251 infoPtr->needsScrolling = FALSE;
1252 infoPtr->leftmostVisible = 0;
1253 }
1254 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1255
1256 /* Set the number of rows */
1257 infoPtr->uNumRows = curItemRowCount;
1258
1259 /* Arrange all tabs evenly if style says so */
1260 if (!(lStyle & TCS_RAGGEDRIGHT) &&
1261 ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1262 (infoPtr->uNumItem > 0) &&
1263 (infoPtr->uNumRows > 1))
1264 {
1265 INT tabPerRow,remTab,iRow;
1266 UINT iItm;
1267 INT iCount=0;
1268
1269 /*
1270 * Ok windows tries to even out the rows. place the same
1271 * number of tabs in each row. So lets give that a shot
1272 */
1273
1274 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1275 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1276
1277 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1278 iItm<infoPtr->uNumItem;
1279 iItm++,iCount++)
1280 {
1281 /* normalize the current rect */
1282 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1283
1284 /* shift the item to the left side of the clientRect */
1285 curr->rect.right -= curr->rect.left;
1286 curr->rect.left = 0;
1287
1288 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1289 curr->rect.right, curItemLeftPos, clientRect.right,
1290 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1291
1292 /* if we have reached the maximum number of tabs on this row */
1293 /* move to the next row, reset our current item left position and */
1294 /* the count of items on this row */
1295
1296 if (lStyle & TCS_VERTICAL) {
1297 /* Vert: Add the remaining tabs in the *last* remainder rows */
1298 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1299 iRow++;
1300 curItemLeftPos = 0;
1301 iCount = 0;
1302 }
1303 } else {
1304 /* Horz: Add the remaining tabs in the *first* remainder rows */
1305 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1306 iRow++;
1307 curItemLeftPos = 0;
1308 iCount = 0;
1309 }
1310 }
1311
1312 /* shift the item to the right to place it as the next item in this row */
1313 curr->rect.left += curItemLeftPos;
1314 curr->rect.right += curItemLeftPos;
1315 curr->rect.top = iRow;
1316 if (lStyle & TCS_BUTTONS)
1317 {
1318 curItemLeftPos = curr->rect.right + 1;
1319 if (lStyle & TCS_FLATBUTTONS)
1320 curItemLeftPos += FLAT_BTN_SPACINGX;
1321 }
1322 else
1323 curItemLeftPos = curr->rect.right;
1324
1325 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1326 debugstr_w(curr->pszText), curr->rect.left,
1327 curr->rect.right, curr->rect.top);
1328 }
1329
1330 /*
1331 * Justify the rows
1332 */
1333 {
1334 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1335 INT remainder;
1336 INT iCount=0;
1337
1338 while(iIndexStart < infoPtr->uNumItem)
1339 {
1340 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1341
1342 /*
1343 * find the index of the row
1344 */
1345 /* find the first item on the next row */
1346 for (iIndexEnd=iIndexStart;
1347 (iIndexEnd < infoPtr->uNumItem) &&
1348 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1349 start->rect.top) ;
1350 iIndexEnd++)
1351 /* intentionally blank */;
1352
1353 /*
1354 * we need to justify these tabs so they fill the whole given
1355 * client area
1356 *
1357 */
1358 /* find the amount of space remaining on this row */
1359 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1360 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1361
1362 /* iCount is the number of tab items on this row */
1363 iCount = iIndexEnd - iIndexStart;
1364
1365 if (iCount > 1)
1366 {
1367 remainder = widthDiff % iCount;
1368 widthDiff = widthDiff / iCount;
1369 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1370 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1371 {
1372 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1373
1374 item->rect.left += iCount * widthDiff;
1375 item->rect.right += (iCount + 1) * widthDiff;
1376
1377 TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1378 debugstr_w(item->pszText),
1379 item->rect.left, item->rect.right);
1380
1381 }
1382 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1383 }
1384 else /* we have only one item on this row, make it take up the entire row */
1385 {
1386 start->rect.left = clientRect.left;
1387 start->rect.right = clientRect.right - 4;
1388
1389 TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1390 debugstr_w(start->pszText),
1391 start->rect.left, start->rect.right);
1392
1393 }
1394
1395
1396 iIndexStart = iIndexEnd;
1397 }
1398 }
1399 }
1400
1401 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1402 if(lStyle & TCS_VERTICAL)
1403 {
1404 RECT rcOriginal;
1405 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1406 {
1407 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1408
1409 rcOriginal = *rcItem;
1410
1411 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1412 rcItem->top = (rcOriginal.left - clientRect.left);
1413 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1414 rcItem->left = rcOriginal.top;
1415 rcItem->right = rcOriginal.bottom;
1416 }
1417 }
1418
1419 TAB_EnsureSelectionVisible(infoPtr);
1420 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1421
1422 /* Cleanup */
1423 SelectObject (hdc, hOldFont);
1424 ReleaseDC (infoPtr->hwnd, hdc);
1425 }
1426
1427
1428 static void
1429 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
1430 {
1431 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1432 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1433 BOOL deleteBrush = TRUE;
1434 RECT rTemp = *drawRect;
1435
1436 InflateRect(&rTemp, -2, -2);
1437 if (lStyle & TCS_BUTTONS)
1438 {
1439 if (iItem == infoPtr->iSelected)
1440 {
1441 /* Background color */
1442 if (!(lStyle & TCS_OWNERDRAWFIXED))
1443 {
1444 DeleteObject(hbr);
1445 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1446
1447 SetTextColor(hdc, comctl32_color.clr3dFace);
1448 SetBkColor(hdc, comctl32_color.clr3dHilight);
1449
1450 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1451 * we better use 0x55aa bitmap brush to make scrollbar's background
1452 * look different from the window background.
1453 */
1454 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1455 hbr = COMCTL32_hPattern55AABrush;
1456
1457 deleteBrush = FALSE;
1458 }
1459 FillRect(hdc, &rTemp, hbr);
1460 }
1461 else /* ! selected */
1462 {
1463 if (lStyle & TCS_FLATBUTTONS)
1464 {
1465 FillRect(hdc, drawRect, hbr);
1466 if (iItem == infoPtr->iHotTracked)
1467 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1468 }
1469 else
1470 FillRect(hdc, &rTemp, hbr);
1471 }
1472
1473 }
1474 else /* !TCS_BUTTONS */
1475 {
1476 if (!GetWindowTheme (infoPtr->hwnd))
1477 FillRect(hdc, &rTemp, hbr);
1478 }
1479
1480 /* Cleanup */
1481 if (deleteBrush) DeleteObject(hbr);
1482 }
1483
1484 /******************************************************************************
1485 * TAB_DrawItemInterior
1486 *
1487 * This method is used to draw the interior (text and icon) of a single tab
1488 * into the tab control.
1489 */
1490 static void
1491 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
1492 {
1493 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1494
1495 RECT localRect;
1496
1497 HPEN htextPen;
1498 HPEN holdPen;
1499 INT oldBkMode;
1500 HFONT hOldFont;
1501
1502 /* if (drawRect == NULL) */
1503 {
1504 BOOL isVisible;
1505 RECT itemRect;
1506 RECT selectedRect;
1507
1508 /*
1509 * Get the rectangle for the item.
1510 */
1511 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1512 if (!isVisible)
1513 return;
1514
1515 /*
1516 * Make sure drawRect points to something valid; simplifies code.
1517 */
1518 drawRect = &localRect;
1519
1520 /*
1521 * This logic copied from the part of TAB_DrawItem which draws
1522 * the tab background. It's important to keep it in sync. I
1523 * would have liked to avoid code duplication, but couldn't figure
1524 * out how without making spaghetti of TAB_DrawItem.
1525 */
1526 if (iItem == infoPtr->iSelected)
1527 *drawRect = selectedRect;
1528 else
1529 *drawRect = itemRect;
1530
1531 if (lStyle & TCS_BUTTONS)
1532 {
1533 if (iItem == infoPtr->iSelected)
1534 {
1535 drawRect->left += 4;
1536 drawRect->top += 4;
1537 drawRect->right -= 4;
1538 drawRect->bottom -= 1;
1539 }
1540 else
1541 {
1542 drawRect->left += 2;
1543 drawRect->top += 2;
1544 drawRect->right -= 2;
1545 drawRect->bottom -= 2;
1546 }
1547 }
1548 else
1549 {
1550 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1551 {
1552 if (iItem != infoPtr->iSelected)
1553 {
1554 drawRect->left += 2;
1555 drawRect->top += 2;
1556 drawRect->bottom -= 2;
1557 }
1558 }
1559 else if (lStyle & TCS_VERTICAL)
1560 {
1561 if (iItem == infoPtr->iSelected)
1562 {
1563 drawRect->right += 1;
1564 }
1565 else
1566 {
1567 drawRect->top += 2;
1568 drawRect->right -= 2;
1569 drawRect->bottom -= 2;
1570 }
1571 }
1572 else if (lStyle & TCS_BOTTOM)
1573 {
1574 if (iItem == infoPtr->iSelected)
1575 {
1576 drawRect->top -= 2;
1577 }
1578 else
1579 {
1580 InflateRect(drawRect, -2, -2);
1581 drawRect->bottom += 2;
1582 }
1583 }
1584 else
1585 {
1586 if (iItem == infoPtr->iSelected)
1587 {
1588 drawRect->bottom += 3;
1589 }
1590 else
1591 {
1592 drawRect->bottom -= 2;
1593 InflateRect(drawRect, -2, 0);
1594 }
1595 }
1596 }
1597 }
1598 TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect));
1599
1600 /* Clear interior */
1601 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1602
1603 /* Draw the focus rectangle */
1604 if (!(lStyle & TCS_FOCUSNEVER) &&
1605 (GetFocus() == infoPtr->hwnd) &&
1606 (iItem == infoPtr->uFocus) )
1607 {
1608 RECT rFocus = *drawRect;
1609 InflateRect(&rFocus, -3, -3);
1610 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1611 rFocus.top -= 3;
1612 if (lStyle & TCS_BUTTONS)
1613 {
1614 rFocus.left -= 3;
1615 rFocus.top -= 3;
1616 }
1617
1618 DrawFocusRect(hdc, &rFocus);
1619 }
1620
1621 /*
1622 * Text pen
1623 */
1624 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1625 holdPen = SelectObject(hdc, htextPen);
1626 hOldFont = SelectObject(hdc, infoPtr->hFont);
1627
1628 /*
1629 * Setup for text output
1630 */
1631 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1632 if (!GetWindowTheme (infoPtr->hwnd) || (lStyle & TCS_BUTTONS))
1633 SetTextColor(hdc, (((lStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked)
1634 && !(lStyle & TCS_FLATBUTTONS))
1635 | (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1636 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1637
1638 /*
1639 * if owner draw, tell the owner to draw
1640 */
1641 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1642 {
1643 DRAWITEMSTRUCT dis;
1644 UINT id;
1645
1646 drawRect->top += 2;
1647 drawRect->right -= 1;
1648 if ( iItem == infoPtr->iSelected )
1649 {
1650 drawRect->right -= 1;
1651 drawRect->left += 1;
1652 }
1653
1654 /*
1655 * get the control id
1656 */
1657 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1658
1659 /*
1660 * put together the DRAWITEMSTRUCT
1661 */
1662 dis.CtlType = ODT_TAB;
1663 dis.CtlID = id;
1664 dis.itemID = iItem;
1665 dis.itemAction = ODA_DRAWENTIRE;
1666 dis.itemState = 0;
1667 if ( iItem == infoPtr->iSelected )
1668 dis.itemState |= ODS_SELECTED;
1669 if (infoPtr->uFocus == iItem)
1670 dis.itemState |= ODS_FOCUS;
1671 dis.hwndItem = infoPtr->hwnd;
1672 dis.hDC = hdc;
1673 CopyRect(&dis.rcItem,drawRect);
1674 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1675
1676 /*
1677 * send the draw message
1678 */
1679 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1680 }
1681 else
1682 {
1683 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1684 RECT rcTemp;
1685 RECT rcImage;
1686
1687 /* used to center the icon and text in the tab */
1688 RECT rcText;
1689 INT center_offset_h, center_offset_v;
1690
1691 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1692 rcImage = *drawRect;
1693
1694 rcTemp = *drawRect;
1695
1696 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1697
1698 /* get the rectangle that the text fits in */
1699 if (item->pszText)
1700 {
1701 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1702 }
1703 /*
1704 * If not owner draw, then do the drawing ourselves.
1705 *
1706 * Draw the icon.
1707 */
1708 if (infoPtr->himl && item->iImage != -1)
1709 {
1710 INT cx;
1711 INT cy;
1712
1713 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1714
1715 if(lStyle & TCS_VERTICAL)
1716 {
1717 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1718 center_offset_v = ((drawRect->right - drawRect->left) - cx) / 2;
1719 }
1720 else
1721 {
1722 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1723 center_offset_v = ((drawRect->bottom - drawRect->top) - cy) / 2;
1724 }
1725
1726 /* if an item is selected, the icon is shifted up instead of down */
1727 if (iItem == infoPtr->iSelected)
1728 center_offset_v -= infoPtr->uVItemPadding / 2;
1729 else
1730 center_offset_v += infoPtr->uVItemPadding / 2;
1731
1732 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1733 center_offset_h = infoPtr->uHItemPadding;
1734
1735 if (center_offset_h < 2)
1736 center_offset_h = 2;
1737
1738 if (center_offset_v < 0)
1739 center_offset_v = 0;
1740
1741 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1742 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1743 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
1744
1745 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1746 {
1747 rcImage.top = drawRect->top + center_offset_h;
1748 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1749 /* right side of the tab, but the image still uses the left as its x position */
1750 /* this keeps the image always drawn off of the same side of the tab */
1751 rcImage.left = drawRect->right - cx - center_offset_v;
1752 drawRect->top += cy + infoPtr->uHItemPadding;
1753 }
1754 else if(lStyle & TCS_VERTICAL)
1755 {
1756 rcImage.top = drawRect->bottom - cy - center_offset_h;
1757 rcImage.left = drawRect->left + center_offset_v;
1758 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1759 }
1760 else /* normal style, whether TCS_BOTTOM or not */
1761 {
1762 rcImage.left = drawRect->left + center_offset_h;
1763 rcImage.top = drawRect->top + center_offset_v;
1764 drawRect->left += cx + infoPtr->uHItemPadding;
1765 }
1766
1767 TRACE("drawing image=%d, left=%d, top=%d\n",
1768 item->iImage, rcImage.left, rcImage.top-1);
1769 ImageList_Draw
1770 (
1771 infoPtr->himl,
1772 item->iImage,
1773 hdc,
1774 rcImage.left,
1775 rcImage.top,
1776 ILD_NORMAL
1777 );
1778 }
1779
1780 /* Now position text */
1781 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1782 center_offset_h = infoPtr->uHItemPadding;
1783 else
1784 if(lStyle & TCS_VERTICAL)
1785 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1786 else
1787 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1788
1789 if(lStyle & TCS_VERTICAL)
1790 {
1791 if(lStyle & TCS_BOTTOM)
1792 drawRect->top+=center_offset_h;
1793 else
1794 drawRect->bottom-=center_offset_h;
1795
1796 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1797 }
1798 else
1799 {
1800 drawRect->left += center_offset_h;
1801 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1802 }
1803
1804 /* if an item is selected, the text is shifted up instead of down */
1805 if (iItem == infoPtr->iSelected)
1806 center_offset_v -= infoPtr->uVItemPadding / 2;
1807 else
1808 center_offset_v += infoPtr->uVItemPadding / 2;
1809
1810 if (center_offset_v < 0)
1811 center_offset_v = 0;
1812
1813 if(lStyle & TCS_VERTICAL)
1814 drawRect->left += center_offset_v;
1815 else
1816 drawRect->top += center_offset_v;
1817
1818 /* Draw the text */
1819 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1820 {
1821 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1822 LOGFONTW logfont;
1823 HFONT hFont = 0;
1824 INT nEscapement = 900;
1825 INT nOrientation = 900;
1826
1827 if(lStyle & TCS_BOTTOM)
1828 {
1829 nEscapement = -900;
1830 nOrientation = -900;
1831 }
1832
1833 /* to get a font with the escapement and orientation we are looking for, we need to */
1834 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1835 if (!GetObjectW((infoPtr->hFont) ?
1836 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1837 sizeof(LOGFONTW),&logfont))
1838 {
1839 INT iPointSize = 9;
1840
1841 lstrcpyW(logfont.lfFaceName, ArialW);
1842 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1843 72);
1844 logfont.lfWeight = FW_NORMAL;
1845 logfont.lfItalic = 0;
1846 logfont.lfUnderline = 0;
1847 logfont.lfStrikeOut = 0;
1848 }
1849
1850 logfont.lfEscapement = nEscapement;
1851 logfont.lfOrientation = nOrientation;
1852 hFont = CreateFontIndirectW(&logfont);
1853 SelectObject(hdc, hFont);
1854
1855 if (item->pszText)
1856 {
1857 ExtTextOutW(hdc,
1858 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1859 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1860 ETO_CLIPPED,
1861 drawRect,
1862 item->pszText,
1863 lstrlenW(item->pszText),
1864 0);
1865 }
1866
1867 DeleteObject(hFont);
1868 }
1869 else
1870 {
1871 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1872 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1873 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
1874 if (item->pszText)
1875 {
1876 DrawTextW
1877 (
1878 hdc,
1879 item->pszText,
1880 lstrlenW(item->pszText),
1881 drawRect,
1882 DT_LEFT | DT_SINGLELINE
1883 );
1884 }
1885 }
1886
1887 *drawRect = rcTemp; /* restore drawRect */
1888 }
1889
1890 /*
1891 * Cleanup
1892 */
1893 SelectObject(hdc, hOldFont);
1894 SetBkMode(hdc, oldBkMode);
1895 SelectObject(hdc, holdPen);
1896 DeleteObject( htextPen );
1897 }
1898
1899 /******************************************************************************
1900 * TAB_DrawItem
1901 *
1902 * This method is used to draw a single tab into the tab control.
1903 */
1904 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC hdc, INT iItem)
1905 {
1906 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1907 RECT itemRect;
1908 RECT selectedRect;
1909 BOOL isVisible;
1910 RECT r, fillRect, r1;
1911 INT clRight = 0;
1912 INT clBottom = 0;
1913 COLORREF bkgnd, corner;
1914 HTHEME theme;
1915
1916 /*
1917 * Get the rectangle for the item.
1918 */
1919 isVisible = TAB_InternalGetItemRect(infoPtr,
1920 iItem,
1921 &itemRect,
1922 &selectedRect);
1923
1924 if (isVisible)
1925 {
1926 RECT rUD, rC;
1927
1928 /* Clip UpDown control to not draw over it */
1929 if (infoPtr->needsScrolling)
1930 {
1931 GetWindowRect(infoPtr->hwnd, &rC);
1932 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1933 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1934 }
1935
1936 /* If you need to see what the control is doing,
1937 * then override these variables. They will change what
1938 * fill colors are used for filling the tabs, and the
1939 * corners when drawing the edge.
1940 */
1941 bkgnd = comctl32_color.clrBtnFace;
1942 corner = comctl32_color.clrBtnFace;
1943
1944 if (lStyle & TCS_BUTTONS)
1945 {
1946 /* Get item rectangle */
1947 r = itemRect;
1948
1949 /* Separators between flat buttons */
1950 if (lStyle & TCS_FLATBUTTONS)
1951 {
1952 r1 = r;
1953 r1.right += (FLAT_BTN_SPACINGX -2);
1954 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1955 }
1956
1957 if (iItem == infoPtr->iSelected)
1958 {
1959 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1960
1961 OffsetRect(&r, 1, 1);
1962 }
1963 else /* ! selected */
1964 {
1965 if (!(lStyle & TCS_FLATBUTTONS))
1966 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1967 }
1968 }
1969 else /* !TCS_BUTTONS */
1970 {
1971 /* We draw a rectangle of different sizes depending on the selection
1972 * state. */
1973 if (iItem == infoPtr->iSelected) {
1974 RECT rect;
1975 GetClientRect (infoPtr->hwnd, &rect);
1976 clRight = rect.right;
1977 clBottom = rect.bottom;
1978 r = selectedRect;
1979 }
1980 else
1981 r = itemRect;
1982
1983 /*
1984 * Erase the background. (Delay it but setup rectangle.)
1985 * This is necessary when drawing the selected item since it is larger
1986 * than the others, it might overlap with stuff already drawn by the
1987 * other tabs
1988 */
1989 fillRect = r;
1990
1991 /* Draw themed tabs - but only if they are at the top.
1992 * Windows draws even side or bottom tabs themed, with wacky results.
1993 * However, since in Wine apps may get themed that did not opt in via
1994 * a manifest avoid theming when we know the result will be wrong */
1995 if ((theme = GetWindowTheme (infoPtr->hwnd))
1996 && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
1997 {
1998 static const int partIds[8] = {
1999 /* Normal item */
2000 TABP_TABITEM,
2001 TABP_TABITEMLEFTEDGE,
2002 TABP_TABITEMRIGHTEDGE,
2003 TABP_TABITEMBOTHEDGE,
2004 /* Selected tab */
2005 TABP_TOPTABITEM,
2006 TABP_TOPTABITEMLEFTEDGE,
2007 TABP_TOPTABITEMRIGHTEDGE,
2008 TABP_TOPTABITEMBOTHEDGE,
2009 };
2010 int partIndex = 0;
2011 int stateId = TIS_NORMAL;
2012
2013 /* selected and unselected tabs have different parts */
2014 if (iItem == infoPtr->iSelected)
2015 partIndex += 4;
2016 /* The part also differs on the position of a tab on a line.
2017 * "Visually" determining the position works well enough. */
2018 if(selectedRect.left == 0)
2019 partIndex += 1;
2020 if(selectedRect.right == clRight)
2021 partIndex += 2;
2022
2023 if (iItem == infoPtr->iSelected)
2024 stateId = TIS_SELECTED;
2025 else if (iItem == infoPtr->iHotTracked)
2026 stateId = TIS_HOT;
2027 else if (iItem == infoPtr->uFocus)
2028 stateId = TIS_FOCUSED;
2029
2030 /* Adjust rectangle for bottommost row */
2031 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2032 r.bottom += 3;
2033
2034 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2035 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2036 }
2037 else if(lStyle & TCS_VERTICAL)
2038 {
2039 /* These are for adjusting the drawing of a Selected tab */
2040 /* The initial values are for the normal case of non-Selected */
2041 int ZZ = 1; /* Do not stretch if selected */
2042 if (iItem == infoPtr->iSelected) {
2043 ZZ = 0;
2044
2045 /* if leftmost draw the line longer */
2046 if(selectedRect.top == 0)
2047 fillRect.top += CONTROL_BORDER_SIZEY;
2048 /* if rightmost draw the line longer */
2049 if(selectedRect.bottom == clBottom)
2050 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2051 }
2052
2053 if (lStyle & TCS_BOTTOM)
2054 {
2055 /* Adjust both rectangles to match native */
2056 r.left += (1-ZZ);
2057
2058 TRACE("<right> item=%d, fill=(%s), edge=(%s)\n",
2059 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2060
2061 /* Clear interior */
2062 SetBkColor(hdc, bkgnd);
2063 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2064
2065 /* Draw rectangular edge around tab */
2066 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2067
2068 /* Now erase the top corner and draw diagonal edge */
2069 SetBkColor(hdc, corner);
2070 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2071 r1.top = r.top;
2072 r1.right = r.right;
2073 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2074 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2075 r1.right--;
2076 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2077
2078 /* Now erase the bottom corner and draw diagonal edge */
2079 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2080 r1.bottom = r.bottom;
2081 r1.right = r.right;
2082 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2083 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2084 r1.right--;
2085 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2086
2087 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2088 r1 = r;
2089 r1.right = r1.left;
2090 r1.left--;
2091 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2092 }
2093
2094 }
2095 else
2096 {
2097 TRACE("<left> item=%d, fill=(%s), edge=(%s)\n",
2098 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2099
2100 /* Clear interior */
2101 SetBkColor(hdc, bkgnd);
2102 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2103
2104 /* Draw rectangular edge around tab */
2105 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2106
2107 /* Now erase the top corner and draw diagonal edge */
2108 SetBkColor(hdc, corner);
2109 r1.left = r.left;
2110 r1.top = r.top;
2111 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2112 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2113 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2114 r1.left++;
2115 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2116
2117 /* Now erase the bottom corner and draw diagonal edge */
2118 r1.left = r.left;
2119 r1.bottom = r.bottom;
2120 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2121 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2122 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2123 r1.left++;
2124 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2125 }
2126 }
2127 else /* ! TCS_VERTICAL */
2128 {
2129 /* These are for adjusting the drawing of a Selected tab */
2130 /* The initial values are for the normal case of non-Selected */
2131 if (iItem == infoPtr->iSelected) {
2132 /* if leftmost draw the line longer */
2133 if(selectedRect.left == 0)
2134 fillRect.left += CONTROL_BORDER_SIZEX;
2135 /* if rightmost draw the line longer */
2136 if(selectedRect.right == clRight)
2137 fillRect.right -= CONTROL_BORDER_SIZEX;
2138 }
2139
2140 if (lStyle & TCS_BOTTOM)
2141 {
2142 /* Adjust both rectangles for topmost row */
2143 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2144 {
2145 fillRect.top -= 2;
2146 r.top -= 1;
2147 }
2148
2149 TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n",
2150 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2151
2152 /* Clear interior */
2153 SetBkColor(hdc, bkgnd);
2154 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2155
2156 /* Draw rectangular edge around tab */
2157 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2158
2159 /* Now erase the righthand corner and draw diagonal edge */
2160 SetBkColor(hdc, corner);
2161 r1.left = r.right - ROUND_CORNER_SIZE;
2162 r1.bottom = r.bottom;
2163 r1.right = r.right;
2164 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2165 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2166 r1.bottom--;
2167 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2168
2169 /* Now erase the lefthand corner and draw diagonal edge */
2170 r1.left = r.left;
2171 r1.bottom = r.bottom;
2172 r1.right = r1.left + ROUND_CORNER_SIZE;
2173 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2174 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2175 r1.bottom--;
2176 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2177
2178 if (iItem == infoPtr->iSelected)
2179 {
2180 r.top += 2;
2181 r.left += 1;
2182 if (selectedRect.left == 0)
2183 {
2184 r1 = r;
2185 r1.bottom = r1.top;
2186 r1.top--;
2187 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2188 }
2189 }
2190
2191 }
2192 else
2193 {
2194 /* Adjust both rectangles for bottommost row */
2195 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2196 {
2197 fillRect.bottom += 3;
2198 r.bottom += 2;
2199 }
2200
2201 TRACE("<top> item=%d, fill=(%s), edge=(%s)\n",
2202 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2203
2204 /* Clear interior */
2205 SetBkColor(hdc, bkgnd);
2206 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2207
2208 /* Draw rectangular edge around tab */
2209 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2210
2211 /* Now erase the righthand corner and draw diagonal edge */
2212 SetBkColor(hdc, corner);
2213 r1.left = r.right - ROUND_CORNER_SIZE;
2214 r1.top = r.top;
2215 r1.right = r.right;
2216 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2217 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2218 r1.top++;
2219 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2220
2221 /* Now erase the lefthand corner and draw diagonal edge */
2222 r1.left = r.left;
2223 r1.top = r.top;
2224 r1.right = r1.left + ROUND_CORNER_SIZE;
2225 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2226 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2227 r1.top++;
2228 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2229 }
2230 }
2231 }
2232
2233 TAB_DumpItemInternal(infoPtr, iItem);
2234
2235 /* This modifies r to be the text rectangle. */
2236 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2237 }
2238 }
2239
2240 /******************************************************************************
2241 * TAB_DrawBorder
2242 *
2243 * This method is used to draw the raised border around the tab control
2244 * "content" area.
2245 */
2246 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc)
2247 {
2248 RECT rect;
2249 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2250 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2251
2252 GetClientRect (infoPtr->hwnd, &rect);
2253
2254 /*
2255 * Adjust for the style
2256 */
2257
2258 if (infoPtr->uNumItem)
2259 {
2260 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2261 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2262 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2263 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2264 else if(lStyle & TCS_VERTICAL)
2265 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2266 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2267 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2268 }
2269
2270 TRACE("border=(%s)\n", wine_dbgstr_rect(&rect));
2271
2272 if (theme)
2273 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2274 else
2275 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2276 }
2277
2278 /******************************************************************************
2279 * TAB_Refresh
2280 *
2281 * This method repaints the tab control..
2282 */
2283 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2284 {
2285 HFONT hOldFont;
2286 INT i;
2287
2288 if (!infoPtr->DoRedraw)
2289 return;
2290
2291 hOldFont = SelectObject (hdc, infoPtr->hFont);
2292
2293 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2294 {
2295 for (i = 0; i < infoPtr->uNumItem; i++)
2296 TAB_DrawItem (infoPtr, hdc, i);
2297 }
2298 else
2299 {
2300 /* Draw all the non selected item first */
2301 for (i = 0; i < infoPtr->uNumItem; i++)
2302 {
2303 if (i != infoPtr->iSelected)
2304 TAB_DrawItem (infoPtr, hdc, i);
2305 }
2306
2307 /* Now, draw the border, draw it before the selected item
2308 * since the selected item overwrites part of the border. */
2309 TAB_DrawBorder (infoPtr, hdc);
2310
2311 /* Then, draw the selected item */
2312 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2313 }
2314
2315 SelectObject (hdc, hOldFont);
2316 }
2317
2318 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2319 {
2320 return infoPtr->uNumRows;
2321 }
2322
2323 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2324 {
2325 infoPtr->DoRedraw = doRedraw;
2326 return 0;
2327 }
2328
2329 /******************************************************************************
2330 * TAB_EnsureSelectionVisible
2331 *
2332 * This method will make sure that the current selection is completely
2333 * visible by scrolling until it is.
2334 */
2335 static void TAB_EnsureSelectionVisible(
2336 TAB_INFO* infoPtr)
2337 {
2338 INT iSelected = infoPtr->iSelected;
2339 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2340 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2341
2342 /* set the items row to the bottommost row or topmost row depending on
2343 * style */
2344 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2345 {
2346 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2347 INT newselected;
2348 INT iTargetRow;
2349
2350 if(lStyle & TCS_VERTICAL)
2351 newselected = selected->rect.left;
2352 else
2353 newselected = selected->rect.top;
2354
2355 /* the target row is always (number of rows - 1)
2356 as row 0 is furthest from the clientRect */
2357 iTargetRow = infoPtr->uNumRows - 1;
2358
2359 if (newselected != iTargetRow)
2360 {
2361 UINT i;
2362 if(lStyle & TCS_VERTICAL)
2363 {
2364 for (i=0; i < infoPtr->uNumItem; i++)
2365 {
2366 /* move everything in the row of the selected item to the iTargetRow */
2367 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2368
2369 if (item->rect.left == newselected )
2370 item->rect.left = iTargetRow;
2371 else
2372 {
2373 if (item->rect.left > newselected)
2374 item->rect.left-=1;
2375 }
2376 }
2377 }
2378 else
2379 {
2380 for (i=0; i < infoPtr->uNumItem; i++)
2381 {
2382 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2383
2384 if (item->rect.top == newselected )
2385 item->rect.top = iTargetRow;
2386 else
2387 {
2388 if (item->rect.top > newselected)
2389 item->rect.top-=1;
2390 }
2391 }
2392 }
2393 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2394 }
2395 }
2396
2397 /*
2398 * Do the trivial cases first.
2399 */
2400 if ( (!infoPtr->needsScrolling) ||
2401 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2402 return;
2403
2404 if (infoPtr->leftmostVisible >= iSelected)
2405 {
2406 infoPtr->leftmostVisible = iSelected;
2407 }
2408 else
2409 {
2410 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2411 RECT r;
2412 INT width;
2413 UINT i;
2414
2415 /* Calculate the part of the client area that is visible */
2416 GetClientRect(infoPtr->hwnd, &r);
2417 width = r.right;
2418
2419 GetClientRect(infoPtr->hwndUpDown, &r);
2420 width -= r.right;
2421
2422 if ((selected->rect.right -
2423 selected->rect.left) >= width )
2424 {
2425 /* Special case: width of selected item is greater than visible
2426 * part of control.
2427 */
2428 infoPtr->leftmostVisible = iSelected;
2429 }
2430 else
2431 {
2432 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2433 {
2434 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2435 break;
2436 }
2437 infoPtr->leftmostVisible = i;
2438 }
2439 }
2440
2441 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2442 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2443
2444 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2445 MAKELONG(infoPtr->leftmostVisible, 0));
2446 }
2447
2448 /******************************************************************************
2449 * TAB_InvalidateTabArea
2450 *
2451 * This method will invalidate the portion of the control that contains the
2452 * tabs. It is called when the state of the control changes and needs
2453 * to be redisplayed
2454 */
2455 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr)
2456 {
2457 RECT clientRect, rInvalidate, rAdjClient;
2458 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2459 INT lastRow = infoPtr->uNumRows - 1;
2460 RECT rect;
2461
2462 if (lastRow < 0) return;
2463
2464 GetClientRect(infoPtr->hwnd, &clientRect);
2465 rInvalidate = clientRect;
2466 rAdjClient = clientRect;
2467
2468 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2469
2470 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2471 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2472 {
2473 rInvalidate.left = rAdjClient.right;
2474 if (infoPtr->uNumRows == 1)
2475 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2476 }
2477 else if(lStyle & TCS_VERTICAL)
2478 {
2479 rInvalidate.right = rAdjClient.left;
2480 if (infoPtr->uNumRows == 1)
2481 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2482 }
2483 else if (lStyle & TCS_BOTTOM)
2484 {
2485 rInvalidate.top = rAdjClient.bottom;
2486 if (infoPtr->uNumRows == 1)
2487 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2488 }
2489 else
2490 {
2491 rInvalidate.bottom = rAdjClient.top;
2492 if (infoPtr->uNumRows == 1)
2493 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2494 }
2495
2496 /* Punch out the updown control */
2497 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2498 RECT r;
2499 GetClientRect(infoPtr->hwndUpDown, &r);
2500 if (rInvalidate.right > clientRect.right - r.left)
2501 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2502 else
2503 rInvalidate.right = clientRect.right - r.left;
2504 }
2505
2506 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate));
2507
2508 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2509 }
2510
2511 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2512 {
2513 HDC hdc;
2514 PAINTSTRUCT ps;
2515
2516 if (hdcPaint)
2517 hdc = hdcPaint;
2518 else
2519 {
2520 hdc = BeginPaint (infoPtr->hwnd, &ps);
2521 TRACE("erase %d, rect=(%s)\n", ps.fErase, wine_dbgstr_rect(&ps.rcPaint));
2522 }
2523
2524 TAB_Refresh (infoPtr, hdc);
2525
2526 if (!hdcPaint)
2527 EndPaint (infoPtr->hwnd, &ps);
2528
2529 return 0;
2530 }
2531
2532 static LRESULT
2533 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2534 {
2535 TAB_ITEM *item;
2536 TCITEMW *pti;
2537 INT iItem;
2538 RECT rect;
2539
2540 GetClientRect (infoPtr->hwnd, &rect);
2541 TRACE("Rect: %p %s\n", infoPtr->hwnd, wine_dbgstr_rect(&rect));
2542
2543 pti = (TCITEMW *)lParam;
2544 iItem = (INT)wParam;
2545
2546 if (iItem < 0) return -1;
2547 if (iItem > infoPtr->uNumItem)
2548 iItem = infoPtr->uNumItem;
2549
2550 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2551
2552
2553 if (infoPtr->uNumItem == 0) {
2554 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2555 infoPtr->uNumItem++;
2556 infoPtr->iSelected = 0;
2557 }
2558 else {
2559 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2560
2561 infoPtr->uNumItem++;
2562 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2563
2564 /* pre insert copy */
2565 if (iItem > 0) {
2566 memcpy (infoPtr->items, oldItems,
2567 iItem * TAB_ITEM_SIZE(infoPtr));
2568 }
2569
2570 /* post insert copy */
2571 if (iItem < infoPtr->uNumItem - 1) {
2572 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2573 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2574 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2575
2576 }
2577
2578 if (iItem <= infoPtr->iSelected)
2579 infoPtr->iSelected++;
2580
2581 Free (oldItems);
2582 }
2583
2584 item = TAB_GetItem(infoPtr, iItem);
2585
2586 item->pszText = NULL;
2587
2588 if (pti->mask & TCIF_TEXT)
2589 {
2590 if (bUnicode)
2591 Str_SetPtrW (&item->pszText, pti->pszText);
2592 else
2593 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2594 }
2595
2596 if (pti->mask & TCIF_IMAGE)
2597 item->iImage = pti->iImage;
2598 else
2599 item->iImage = -1;
2600
2601 if (pti->mask & TCIF_PARAM)
2602 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2603 else
2604 memset(item->extra, 0, infoPtr->cbInfo);
2605
2606 TAB_SetItemBounds(infoPtr);
2607 if (infoPtr->uNumItem > 1)
2608 TAB_InvalidateTabArea(infoPtr);
2609 else
2610 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2611
2612 TRACE("[%p]: added item %d %s\n",
2613 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2614
2615 /* If we haven't set the current focus yet, set it now. */
2616 if (infoPtr->uFocus == -1)
2617 TAB_SetCurFocus(infoPtr, iItem);
2618
2619 return iItem;
2620 }
2621
2622 static LRESULT
2623 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2624 {
2625 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2626 LONG lResult = 0;
2627 BOOL bNeedPaint = FALSE;
2628
2629 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2630
2631 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2632 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2633 {
2634 infoPtr->tabWidth = (INT)LOWORD(lParam);
2635 bNeedPaint = TRUE;
2636 }
2637
2638 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2639 {
2640 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2641 infoPtr->tabHeight = (INT)HIWORD(lParam);
2642
2643 bNeedPaint = TRUE;
2644 }
2645 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2646 HIWORD(lResult), LOWORD(lResult),
2647 infoPtr->tabHeight, infoPtr->tabWidth);
2648
2649 if (bNeedPaint)
2650 {
2651 TAB_SetItemBounds(infoPtr);
2652 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2653 }
2654
2655 return lResult;
2656 }
2657
2658 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2659 {
2660 INT oldcx = 0;
2661
2662 TRACE("(%p,%d)\n", infoPtr, cx);
2663
2664 oldcx = infoPtr->tabMinWidth;
2665 infoPtr->tabMinWidth = cx;
2666 TAB_SetItemBounds(infoPtr);
2667 return oldcx;
2668 }
2669
2670 static inline LRESULT
2671 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2672 {
2673 LPDWORD lpState;
2674
2675 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2676
2677 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2678 return FALSE;
2679
2680 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2681
2682 if (fHighlight)
2683 *lpState |= TCIS_HIGHLIGHTED;
2684 else
2685 *lpState &= ~TCIS_HIGHLIGHTED;
2686
2687 return TRUE;
2688 }
2689
2690 static LRESULT
2691 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2692 {
2693 TAB_ITEM *wineItem;
2694
2695 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2696
2697 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2698 return FALSE;
2699
2700 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2701
2702 wineItem = TAB_GetItem(infoPtr, iItem);
2703
2704 if (tabItem->mask & TCIF_IMAGE)
2705 wineItem->iImage = tabItem->iImage;
2706
2707 if (tabItem->mask & TCIF_PARAM)
2708 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2709
2710 if (tabItem->mask & TCIF_RTLREADING)
2711 FIXME("TCIF_RTLREADING\n");
2712
2713 if (tabItem->mask & TCIF_STATE)
2714 wineItem->dwState = tabItem->dwState;
2715
2716 if (tabItem->mask & TCIF_TEXT)
2717 {
2718 Free(wineItem->pszText);
2719 wineItem->pszText = NULL;
2720 if (bUnicode)
2721 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2722 else
2723 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2724 }
2725
2726 /* Update and repaint tabs */
2727 TAB_SetItemBounds(infoPtr);
2728 TAB_InvalidateTabArea(infoPtr);
2729
2730 return TRUE;
2731 }
2732
2733 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2734 {
2735 return infoPtr->uNumItem;
2736 }
2737
2738
2739 static LRESULT
2740 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2741 {
2742 TAB_ITEM *wineItem;
2743
2744 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2745
2746 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2747 return FALSE;
2748
2749 wineItem = TAB_GetItem(infoPtr, iItem);
2750
2751 if (tabItem->mask & TCIF_IMAGE)
2752 tabItem->iImage = wineItem->iImage;
2753
2754 if (tabItem->mask & TCIF_PARAM)
2755 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2756
2757 if (tabItem->mask & TCIF_RTLREADING)
2758 FIXME("TCIF_RTLREADING\n");
2759
2760 if (tabItem->mask & TCIF_STATE)
2761 tabItem->dwState = wineItem->dwState;
2762
2763 if (tabItem->mask & TCIF_TEXT)
2764 {
2765 if (bUnicode)
2766 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2767 else
2768 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2769 }
2770
2771 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2772
2773 return TRUE;
2774 }
2775
2776
2777 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2778 {
2779 BOOL bResult = FALSE;
2780
2781 TRACE("(%p, %d)\n", infoPtr, iItem);
2782
2783 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2784 {
2785 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2786 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2787
2788 TAB_InvalidateTabArea(infoPtr);
2789 Free(item->pszText);
2790 infoPtr->uNumItem--;
2791
2792 if (!infoPtr->uNumItem)
2793 {
2794 infoPtr->items = NULL;
2795 if (infoPtr->iHotTracked >= 0)
2796 {
2797 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2798 infoPtr->iHotTracked = -1;
2799 }
2800 }
2801 else
2802 {
2803 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2804
2805 if (iItem > 0)
2806 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2807
2808 if (iItem < infoPtr->uNumItem)
2809 memcpy(TAB_GetItem(infoPtr, iItem),
2810 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2811 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2812
2813 if (iItem <= infoPtr->iHotTracked)
2814 {
2815 /* When tabs move left/up, the hot track item may change */
2816 FIXME("Recalc hot track\n");
2817 }
2818 }
2819 Free(oldItems);
2820
2821 /* Readjust the selected index */
2822 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2823 infoPtr->iSelected--;
2824
2825 if (iItem < infoPtr->iSelected)
2826 infoPtr->iSelected--;
2827
2828 if (infoPtr->uNumItem == 0)
2829 infoPtr->iSelected = -1;
2830
2831 /* Reposition and repaint tabs */
2832 TAB_SetItemBounds(infoPtr);
2833
2834 bResult = TRUE;
2835 }
2836
2837 return bResult;
2838 }
2839
2840 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2841 {
2842 TRACE("(%p)\n", infoPtr);
2843 while (infoPtr->uNumItem)
2844 TAB_DeleteItem (infoPtr, 0);
2845 return TRUE;
2846 }
2847
2848
2849 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2850 {
2851 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2852 return (LRESULT)infoPtr->hFont;
2853 }
2854
2855 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2856 {
2857 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2858
2859 infoPtr->hFont = hNewFont;
2860
2861 TAB_SetItemBounds(infoPtr);
2862
2863 TAB_InvalidateTabArea(infoPtr);
2864
2865 return 0;
2866 }
2867
2868
2869 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2870 {
2871 TRACE("\n");
2872 return (LRESULT)infoPtr->himl;
2873 }
2874
2875 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2876 {
2877 HIMAGELIST himlPrev = infoPtr->himl;
2878 TRACE("\n");
2879 infoPtr->himl = himlNew;
2880 TAB_SetItemBounds(infoPtr);
2881 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2882 return (LRESULT)himlPrev;
2883 }
2884
2885 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2886 {
2887 return infoPtr->bUnicode;
2888 }
2889
2890 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2891 {
2892 BOOL bTemp = infoPtr->bUnicode;
2893
2894 infoPtr->bUnicode = bUnicode;
2895
2896 return bTemp;
2897 }
2898
2899 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2900 {
2901 /* I'm not really sure what the following code was meant to do.
2902 This is what it is doing:
2903 When WM_SIZE is sent with SIZE_RESTORED, the control
2904 gets positioned in the top left corner.
2905
2906 RECT parent_rect;
2907 HWND parent;
2908 UINT uPosFlags,cx,cy;
2909
2910 uPosFlags=0;
2911 if (!wParam) {
2912 parent = GetParent (hwnd);
2913 GetClientRect(parent, &parent_rect);
2914 cx=LOWORD (lParam);
2915 cy=HIWORD (lParam);
2916 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2917 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2918
2919 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2920 cx, cy, uPosFlags | SWP_NOZORDER);
2921 } else {
2922 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2923 } */
2924
2925 /* Recompute the size/position of the tabs. */
2926 TAB_SetItemBounds (infoPtr);
2927
2928 /* Force a repaint of the control. */
2929 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2930
2931 return 0;
2932 }
2933
2934
2935 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2936 {
2937 TAB_INFO *infoPtr;
2938 TEXTMETRICW fontMetrics;
2939 HDC hdc;
2940 HFONT hOldFont;
2941 DWORD dwStyle;
2942
2943 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2944
2945 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
2946
2947 infoPtr->hwnd = hwnd;
2948 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2949 infoPtr->uNumItem = 0;
2950 infoPtr->uNumRows = 0;
2951 infoPtr->uHItemPadding = 6;
2952 infoPtr->uVItemPadding = 3;
2953 infoPtr->uHItemPadding_s = 6;
2954 infoPtr->uVItemPadding_s = 3;
2955 infoPtr->hFont = 0;
2956 infoPtr->items = 0;
2957 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2958 infoPtr->iSelected = -1;
2959 infoPtr->iHotTracked = -1;
2960 infoPtr->uFocus = -1;
2961 infoPtr->hwndToolTip = 0;
2962 infoPtr->DoRedraw = TRUE;
2963 infoPtr->needsScrolling = FALSE;
2964 infoPtr->hwndUpDown = 0;
2965 infoPtr->leftmostVisible = 0;
2966 infoPtr->fHeightSet = FALSE;
2967 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2968 infoPtr->cbInfo = sizeof(LPARAM);
2969
2970 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2971
2972 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2973 if you don't specify it in CreateWindow. This is necessary in
2974 order for paint to work correctly. This follows windows behaviour. */
2975 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
2976 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2977
2978 if (dwStyle & TCS_TOOLTIPS) {
2979 /* Create tooltip control */
2980 infoPtr->hwndToolTip =
2981 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
2982 CW_USEDEFAULT, CW_USEDEFAULT,
2983 CW_USEDEFAULT, CW_USEDEFAULT,
2984 hwnd, 0, 0, 0);
2985
2986 /* Send NM_TOOLTIPSCREATED notification */
2987 if (infoPtr->hwndToolTip) {
2988 NMTOOLTIPSCREATED nmttc;
2989
2990 nmttc.hdr.hwndFrom = hwnd;
2991 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
2992 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2993 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2994
2995 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
2996 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
2997 }
2998 }
2999
3000 OpenThemeData (infoPtr->hwnd, themeClass);
3001
3002 /*
3003 * We need to get text information so we need a DC and we need to select
3004 * a font.
3005 */
3006 hdc = GetDC(hwnd);
3007 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3008
3009 /* Use the system font to determine the initial height of a tab. */
3010 GetTextMetricsW(hdc, &fontMetrics);
3011
3012 /*
3013 * Make sure there is enough space for the letters + growing the
3014 * selected item + extra space for the selected item.
3015 */
3016 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3017 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3018 infoPtr->uVItemPadding;
3019
3020 /* Initialize the width of a tab. */
3021 if (dwStyle & TCS_FIXEDWIDTH)
3022 infoPtr->tabWidth = DEFAULT_TAB_WIDTH_FIXED;
3023
3024 infoPtr->tabMinWidth = -1;
3025
3026 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3027
3028 SelectObject (hdc, hOldFont);
3029 ReleaseDC(hwnd, hdc);
3030
3031 return 0;
3032 }
3033
3034 static LRESULT
3035 TAB_Destroy (TAB_INFO *infoPtr)
3036 {
3037 UINT iItem;
3038
3039 if (!infoPtr)
3040 return 0;
3041
3042 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3043
3044 if (infoPtr->items) {
3045 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3046 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3047 }
3048 Free (infoPtr->items);
3049 }
3050
3051 if (infoPtr->hwndToolTip)
3052 DestroyWindow (infoPtr->hwndToolTip);
3053
3054 if (infoPtr->hwndUpDown)
3055 DestroyWindow(infoPtr->hwndUpDown);
3056
3057 if (infoPtr->iHotTracked >= 0)
3058 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3059
3060 CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3061
3062 Free (infoPtr);
3063 return 0;
3064 }
3065
3066 /* update theme after a WM_THEMECHANGED message */
3067 static LRESULT theme_changed(const TAB_INFO *infoPtr)
3068 {
3069 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3070 CloseThemeData (theme);
3071 OpenThemeData (infoPtr->hwnd, themeClass);
3072 return 0;
3073 }
3074
3075 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3076 {
3077 if (!wParam)
3078 return 0;
3079 return WVR_ALIGNTOP;
3080 }
3081
3082 static inline LRESULT
3083 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3084 {
3085 if (!infoPtr || cbInfo <= 0)
3086 return FALSE;
3087
3088 if (infoPtr->uNumItem)
3089 {
3090 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3091 return FALSE;
3092 }
3093
3094 infoPtr->cbInfo = cbInfo;
3095 return TRUE;
3096 }
3097
3098 static LRESULT WINAPI
3099 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3100 {
3101 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3102
3103 TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3104 if (!infoPtr && (uMsg != WM_CREATE))
3105 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3106
3107 switch (uMsg)
3108 {
3109 case TCM_GETIMAGELIST:
3110 return TAB_GetImageList (infoPtr);
3111
3112 case TCM_SETIMAGELIST:
3113 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3114
3115 case TCM_GETITEMCOUNT:
3116 return TAB_GetItemCount (infoPtr);
3117
3118 case TCM_GETITEMA:
3119 case TCM_GETITEMW:
3120 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3121
3122 case TCM_SETITEMA:
3123 case TCM_SETITEMW:
3124 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3125
3126 case TCM_DELETEITEM:
3127 return TAB_DeleteItem (infoPtr, (INT)wParam);
3128
3129 case TCM_DELETEALLITEMS:
3130 return TAB_DeleteAllItems (infoPtr);
3131
3132 case TCM_GETITEMRECT:
3133 return TAB_GetItemRect (infoPtr, wParam, lParam);
3134
3135 case TCM_GETCURSEL:
3136 return TAB_GetCurSel (infoPtr);
3137
3138 case TCM_HITTEST:
3139 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3140
3141 case TCM_SETCURSEL:
3142 return TAB_SetCurSel (infoPtr, (INT)wParam);
3143
3144 case TCM_INSERTITEMA:
3145 case TCM_INSERTITEMW:
3146 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3147
3148 case TCM_SETITEMEXTRA:
3149 return TAB_SetItemExtra (infoPtr, (int)wParam);
3150
3151 case TCM_ADJUSTRECT:
3152 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3153
3154 case TCM_SETITEMSIZE:
3155 return TAB_SetItemSize (infoPtr, lParam);
3156
3157 case TCM_REMOVEIMAGE:
3158 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3159 return 0;
3160
3161 case TCM_SETPADDING:
3162 return TAB_SetPadding (infoPtr, lParam);
3163
3164 case TCM_GETROWCOUNT:
3165 return TAB_GetRowCount(infoPtr);
3166
3167 case TCM_GETUNICODEFORMAT:
3168 return TAB_GetUnicodeFormat (infoPtr);
3169
3170 case TCM_SETUNICODEFORMAT:
3171 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3172
3173 case TCM_HIGHLIGHTITEM:
3174 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3175
3176 case TCM_GETTOOLTIPS:
3177 return TAB_GetToolTips (infoPtr);
3178
3179 case TCM_SETTOOLTIPS:
3180 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3181
3182 case TCM_GETCURFOCUS:
3183 return TAB_GetCurFocus (infoPtr);
3184
3185 case TCM_SETCURFOCUS:
3186 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3187
3188 case TCM_SETMINTABWIDTH:
3189 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3190
3191 case TCM_DESELECTALL:
3192 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3193 return 0;
3194
3195 case TCM_GETEXTENDEDSTYLE:
3196 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3197 return 0;
3198
3199 case TCM_SETEXTENDEDSTYLE:
3200 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3201 return 0;
3202
3203 case WM_GETFONT:
3204 return TAB_GetFont (infoPtr);
3205
3206 case WM_SETFONT:
3207 return TAB_SetFont (infoPtr, (HFONT)wParam);
3208
3209 case WM_CREATE:
3210 return TAB_Create (hwnd, wParam, lParam);
3211
3212 case WM_NCDESTROY:
3213 return TAB_Destroy (infoPtr);
3214
3215 case WM_GETDLGCODE:
3216 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3217
3218 case WM_LBUTTONDOWN:
3219 return TAB_LButtonDown (infoPtr, wParam, lParam);
3220
3221 case WM_LBUTTONUP:
3222 return TAB_LButtonUp (infoPtr);
3223
3224 case WM_NOTIFY:
3225 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3226
3227 case WM_RBUTTONDOWN:
3228 return TAB_RButtonDown (infoPtr);
3229
3230 case WM_MOUSEMOVE:
3231 return TAB_MouseMove (infoPtr, wParam, lParam);
3232
3233 case WM_PRINTCLIENT:
3234 case WM_PAINT:
3235 return TAB_Paint (infoPtr, (HDC)wParam);
3236
3237 case WM_SIZE:
3238 return TAB_Size (infoPtr);
3239
3240 case WM_SETREDRAW:
3241 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3242
3243 case WM_HSCROLL:
3244 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3245
3246 case WM_STYLECHANGED:
3247 TAB_SetItemBounds (infoPtr);
3248 InvalidateRect(hwnd, NULL, TRUE);
3249 return 0;
3250
3251 case WM_SYSCOLORCHANGE:
3252 COMCTL32_RefreshSysColors();
3253 return 0;
3254
3255 case WM_THEMECHANGED:
3256 return theme_changed (infoPtr);
3257
3258 case WM_KILLFOCUS:
3259 case WM_SETFOCUS:
3260 TAB_FocusChanging(infoPtr);
3261 break; /* Don't disturb normal focus behavior */
3262
3263 case WM_KEYUP:
3264 return TAB_KeyUp(infoPtr, wParam);
3265 case WM_NCHITTEST:
3266 return TAB_NCHitTest(infoPtr, lParam);
3267
3268 case WM_NCCALCSIZE:
3269 return TAB_NCCalcSize(hwnd, wParam, lParam);
3270
3271 default:
3272 if (uMsg >= WM_USER && uMsg < WM_APP && !COMCTL32_IsReflectedMessage(uMsg))
3273 WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
3274 uMsg, wParam, lParam);
3275 break;
3276 }
3277 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3278 }
3279
3280
3281 void
3282 TAB_Register (void)
3283 {
3284 WNDCLASSW wndClass;
3285
3286 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3287 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3288 wndClass.lpfnWndProc = TAB_WindowProc;
3289 wndClass.cbClsExtra = 0;
3290 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3291 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3292 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3293 wndClass.lpszClassName = WC_TABCONTROLW;
3294
3295 RegisterClassW (&wndClass);
3296 }
3297
3298
3299 void
3300 TAB_Unregister (void)
3301 {
3302 UnregisterClassW (WC_TABCONTROLW, NULL);
3303 }
3304
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.