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