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