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

Last change on this file since 8266 was 7815, checked in by sandervl, 24 years ago

Wine updates

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