source: trunk/src/comctl32/tab.c@ 6502

Last change on this file since 6502 was 5630, checked in by sandervl, 24 years ago

wine resync

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