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