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

Last change on this file since 164 was 164, checked in by achimha, 26 years ago

updated comctl32 with the latest WINE bugfixes. Listview is much better now and tab control doesn't have the wrong color anymore. Many small improvements

File size: 43.9 KB
Line 
1/* $Id: tab.c,v 1.5 1999-06-23 19:45:01 achimha Exp $ */
2/*
3 * Tab control
4 *
5 * Copyright 1998 Anders Carlsson
6 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
7 * Copyright 1999 Francis Beaudet
8 * Copyright 1999 Achim Hasenmueller
9 *
10 * TODO:
11 * Image list support
12 * Multiline support
13 * Unicode support
14 */
15
16#include <string.h>
17
18#include "winbase.h"
19#include "commctrl.h"
20#include "tab.h"
21
22
23/******************************************************************************
24 * Positioning constants
25 */
26#define SELECTED_TAB_OFFSET 2
27#define HORIZONTAL_ITEM_PADDING 5
28#define VERTICAL_ITEM_PADDING 3
29#define ROUND_CORNER_SIZE 2
30#define FOCUS_RECT_HOFFSET 2
31#define FOCUS_RECT_VOFFSET 1
32#define DISPLAY_AREA_PADDINGX 5
33#define DISPLAY_AREA_PADDINGY 5
34#define CONTROL_BORDER_SIZEX 2
35#define CONTROL_BORDER_SIZEY 2
36#define BUTTON_SPACINGX 10
37#define DEFAULT_TAB_WIDTH 96
38
39#define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
40
41/******************************************************************************
42 * Prototypes
43 */
44static void TAB_Refresh (HWND hwnd, HDC hdc);
45static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
46static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
47
48static BOOL
49TAB_SendSimpleNotify (HWND hwnd, UINT code)
50{
51 NMHDR nmhdr;
52
53 nmhdr.hwndFrom = hwnd;
54 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
55 nmhdr.code = code;
56
57 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
58 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
59}
60
61
62static VOID
63TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
64 WPARAM wParam, LPARAM lParam)
65{
66 MSG msg;
67
68 msg.hwnd = hwndMsg;
69 msg.message = uMsg;
70 msg.wParam = wParam;
71 msg.lParam = lParam;
72 msg.time = GetMessageTime ();
73 msg.pt.x = LOWORD(GetMessagePos ());
74 msg.pt.y = HIWORD(GetMessagePos ());
75
76 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
77}
78
79
80
81static LRESULT
82TAB_GetCurSel (HWND hwnd)
83{
84 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
85
86 return infoPtr->iSelected;
87}
88
89static LRESULT
90TAB_GetCurFocus (HWND hwnd)
91{
92 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
93
94 return infoPtr->uFocus;
95}
96
97static LRESULT
98TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
99{
100 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
101
102 if (infoPtr == NULL) return 0;
103 return infoPtr->hwndToolTip;
104}
105
106
107static LRESULT
108TAB_SetCurSel (HWND hwnd,WPARAM wParam)
109{
110 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
111 INT iItem=(INT) wParam;
112 INT prevItem;
113
114 prevItem=-1;
115 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
116 prevItem=infoPtr->iSelected;
117 infoPtr->iSelected=iItem;
118 }
119 return prevItem;
120}
121
122static LRESULT
123TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
124{
125 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
126 INT iItem=(INT) wParam;
127
128 if ((iItem < 0) || (iItem > infoPtr->uNumItem)) return 0;
129
130 infoPtr->uFocus=iItem;
131 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
132// FIXME (tab,"Should set input focus\n");
133 } else {
134 if (infoPtr->iSelected != iItem) {
135 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
136 infoPtr->iSelected = iItem;
137 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
138
139 TAB_EnsureSelectionVisible(hwnd, infoPtr);
140 TAB_InvalidateTabArea(hwnd, infoPtr);
141 }
142 }
143 }
144 return 0;
145}
146
147static LRESULT
148TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
149{
150 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
151
152 if (infoPtr == NULL) return 0;
153 infoPtr->hwndToolTip = (HWND)wParam;
154 return 0;
155}
156
157/******************************************************************************
158 * TAB_InternalGetItemRect
159 *
160 * This method will calculate the rectangle representing a given tab item in
161 * client coordinates. This method takes scrolling into account.
162 *
163 * This method returns TRUE if the item is visible in the window and FALSE
164 * if it is completely outside the client area.
165 */
166static BOOL TAB_InternalGetItemRect(
167 HWND hwnd,
168 TAB_INFO* infoPtr,
169 INT itemIndex,
170 RECT* itemRect,
171 RECT* selectedRect)
172{
173 RECT tmpItemRect;
174
175 /*
176 * Perform a sanity check and a trivial visibility check.
177 */
178 if ( (infoPtr->uNumItem <=0) ||
179 (itemIndex >= infoPtr->uNumItem) ||
180 (itemIndex < infoPtr->leftmostVisible) )
181 return FALSE;
182
183 /*
184 * Avoid special cases in this procedure by assigning the "out"
185 * parameters if the caller didn't supply them
186 */
187 if (itemRect==NULL)
188 itemRect = &tmpItemRect;
189
190 /*
191 * Retrieve the unmodified item rect.
192 */
193 *itemRect = infoPtr->items[itemIndex].rect;
194
195 /*
196 * "scroll" it to make sure the item at the very left of the
197 * tab control is the leftmost visible tab.
198 */
199 OffsetRect(itemRect,
200 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
201 0);
202
203 /*
204 * Move the rectangle so the first item is slightly offset from
205 * the left of the tab control.
206 */
207 OffsetRect(itemRect,
208 SELECTED_TAB_OFFSET,
209 0);
210
211
212 /*
213 * Now, calculate the position of the item as if it were selected.
214 */
215 if (selectedRect!=NULL)
216 {
217 CopyRect(selectedRect, itemRect);
218
219 /*
220 * The rectangle of a selected item is a bit wider.
221 */
222 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
223
224 /*
225 * If it also a bit higher.
226 */
227 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
228 {
229 selectedRect->top -=2; /* the border is thicker on the bottom */
230 selectedRect->bottom +=SELECTED_TAB_OFFSET;
231 }
232 else
233 {
234 selectedRect->top -=SELECTED_TAB_OFFSET;
235 selectedRect->bottom+=1;
236 }
237 }
238
239 return TRUE;
240}
241
242static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
243{
244 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
245 (LPRECT)lParam, (LPRECT)NULL);
246}
247
248/******************************************************************************
249 * TAB_KeyUp
250 *
251 * This method is called to handle keyboard input
252 */
253static LRESULT TAB_KeyUp(
254 HWND hwnd,
255 WPARAM keyCode)
256{
257 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
258 int newItem = -1;
259
260 switch (keyCode)
261 {
262 case VK_LEFT:
263 newItem = infoPtr->uFocus-1;
264 break;
265 case VK_RIGHT:
266 newItem = infoPtr->uFocus+1;
267 break;
268 }
269
270 /*
271 * If we changed to a valid item, change the selection
272 */
273 if ( (newItem >= 0) &&
274 (newItem < infoPtr->uNumItem) &&
275 (infoPtr->uFocus != newItem) )
276 {
277 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
278 {
279 infoPtr->iSelected = newItem;
280 infoPtr->uFocus = newItem;
281 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
282
283 TAB_EnsureSelectionVisible(hwnd, infoPtr);
284 TAB_InvalidateTabArea(hwnd, infoPtr);
285 }
286 }
287
288 return 0;
289}
290
291/******************************************************************************
292 * TAB_FocusChanging
293 *
294 * This method is called whenever the focus goes in or out of this control
295 * it is used to update the visual state of the control.
296 */
297static LRESULT TAB_FocusChanging(
298 HWND hwnd,
299 UINT uMsg,
300 WPARAM wParam,
301 LPARAM lParam)
302{
303 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
304 RECT selectedRect;
305 BOOL isVisible;
306
307 /*
308 * Get the rectangle for the item.
309 */
310 isVisible = TAB_InternalGetItemRect(hwnd,
311 infoPtr,
312 infoPtr->uFocus,
313 NULL,
314 &selectedRect);
315
316 /*
317 * If the rectangle is not completely invisible, invalidate that
318 * portion of the window.
319 */
320 if (isVisible)
321 {
322 InvalidateRect(hwnd, &selectedRect, TRUE);
323 }
324
325 /*
326 * Don't otherwise disturb normal behavior.
327 */
328 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
329}
330
331static HWND TAB_InternalHitTest (
332 HWND hwnd,
333 TAB_INFO* infoPtr,
334 POINT pt,
335 UINT* flags)
336
337{
338 RECT rect;
339 int iCount;
340
341 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
342 {
343 TAB_InternalGetItemRect(hwnd,
344 infoPtr,
345 iCount,
346 &rect,
347 NULL);
348
349 if (PtInRect (&rect, pt))
350 {
351 *flags = TCHT_ONITEM;
352 return iCount;
353 }
354 }
355
356 *flags=TCHT_NOWHERE;
357 return -1;
358}
359
360static LRESULT
361TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
362{
363 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
364 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
365
366 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
367}
368
369
370static LRESULT
371TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
372{
373 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
374
375 if (infoPtr->hwndToolTip)
376 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
377 WM_LBUTTONDOWN, wParam, lParam);
378
379 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
380 SetFocus (hwnd);
381 }
382 return 0;
383}
384
385static LRESULT
386TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
387{
388 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
389 POINT pt;
390 INT newItem,dummy;
391
392 if (infoPtr->hwndToolTip)
393 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
394 WM_LBUTTONDOWN, wParam, lParam);
395
396 pt.x = (INT)LOWORD(lParam);
397 pt.y = (INT)HIWORD(lParam);
398
399 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,(unsigned int*)&dummy);
400
401// TRACE(tab, "On Tab, item %d\n", newItem);
402
403 if ( (newItem!=-1) &&
404 (infoPtr->iSelected != newItem) )
405 {
406 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
407 {
408 infoPtr->iSelected = newItem;
409 infoPtr->uFocus = newItem;
410 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
411
412 TAB_EnsureSelectionVisible(hwnd, infoPtr);
413
414 TAB_InvalidateTabArea(hwnd, infoPtr);
415 }
416 }
417 TAB_SendSimpleNotify(hwnd, NM_CLICK);
418
419 return 0;
420}
421
422static LRESULT
423TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
424{
425 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
426 return 0;
427}
428
429static LRESULT
430TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
431{
432 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
433
434 if (infoPtr->hwndToolTip)
435 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
436 WM_LBUTTONDOWN, wParam, lParam);
437 return 0;
438}
439
440/******************************************************************************
441 * TAB_AdjustRect
442 *
443 * Calculates the tab control's display area given the windows rectangle or
444 * the window rectangle given the requested display rectangle.
445 */
446static LRESULT TAB_AdjustRect(
447 HWND hwnd,
448 WPARAM fLarger,
449 LPRECT prc)
450{
451 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
452
453 if (fLarger)
454 {
455 /*
456 * Go from display rectangle
457 */
458
459 /*
460 * Add the height of the tabs.
461 */
462 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
463 prc->bottom += infoPtr->tabHeight;
464 else
465 prc->top -= infoPtr->tabHeight;
466
467 /*
468 * Inflate the rectangle for the padding
469 */
470 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
471
472 /*
473 * Inflate for the border
474 */
475 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
476 }
477 else
478 {
479 /*
480 * Go from window rectangle.
481 */
482
483 /*
484 * Deflate the rectangle for the border
485 */
486 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
487
488 /*
489 * Deflate the rectangle for the padding
490 */
491 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
492
493 /*
494 * Remove the height of the tabs.
495 */
496 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
497 prc->bottom -= infoPtr->tabHeight;
498 else
499 prc->top += infoPtr->tabHeight;
500
501 }
502
503 return 0;
504}
505
506/******************************************************************************
507 * TAB_OnHScroll
508 *
509 * This method will handle the notification from the scroll control and
510 * perform the scrolling operation on the tab control.
511 */
512static LRESULT TAB_OnHScroll(
513 HWND hwnd,
514 int nScrollCode,
515 int nPos,
516 HWND hwndScroll)
517{
518 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
519
520 if (nScrollCode == SB_LINELEFT)
521 {
522 if (infoPtr->leftmostVisible>0)
523 {
524 infoPtr->leftmostVisible--;
525
526 TAB_InvalidateTabArea(hwnd, infoPtr);
527 }
528 }
529 else if (nScrollCode == SB_LINERIGHT)
530 {
531 if (infoPtr->leftmostVisible< (infoPtr->uNumItem-1))
532 {
533 infoPtr->leftmostVisible++;
534
535 TAB_InvalidateTabArea(hwnd, infoPtr);
536 }
537 }
538
539 return 0;
540}
541
542/******************************************************************************
543 * TAB_SetupScroling
544 *
545 * This method will check the current scrolling state and make sure the
546 * scrolling control is displayed (or not).
547 */
548static void TAB_SetupScrolling(
549 HWND hwnd,
550 TAB_INFO* infoPtr,
551 const RECT* clientRect)
552{
553 if (infoPtr->needsScrolling)
554 {
555 RECT controlPos;
556
557 /*
558 * Calculate the position of the scroll control.
559 */
560 controlPos.right = clientRect->right;
561 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
562
563 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
564 {
565 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
566 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
567 }
568 else
569 {
570 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
571 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
572 }
573
574 /*
575 * If we don't have a scroll control yet, we want to create one.
576 * If we have one, we want to make sure it's positioned right.
577 */
578 if (infoPtr->hwndUpDown==0)
579 {
580 /*
581 * I use a scrollbar since it seems to be more stable than the Updown
582 * control.
583 */
584 infoPtr->hwndUpDown = CreateWindowA("ScrollBar",
585 "",
586 WS_VISIBLE | WS_CHILD | WS_OVERLAPPED | SBS_HORZ,
587 controlPos.left, controlPos.top,
588 controlPos.right - controlPos.left,
589 controlPos.bottom - controlPos.top,
590 hwnd,
591 (HMENU)NULL,
592 (HINSTANCE)NULL,
593 NULL);
594 }
595 else
596 {
597 SetWindowPos(infoPtr->hwndUpDown,
598 (HWND)NULL,
599 controlPos.left, controlPos.top,
600 controlPos.right - controlPos.left,
601 controlPos.bottom - controlPos.top,
602 SWP_SHOWWINDOW | SWP_NOZORDER);
603 }
604 }
605 else
606 {
607 /*
608 * If we once had a scroll control... hide it.
609 */
610 if (infoPtr->hwndUpDown!=0)
611 {
612 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
613 }
614 }
615}
616
617/******************************************************************************
618 * TAB_SetItemBounds
619 *
620 * This method will calculate the position rectangles of all the items in the
621 * control. The rectangle calculated starts at 0 for the first item in the
622 * list and ignores scrolling and selection.
623 * It also uses the current font to determine the height of the tab row and
624 * it checks if all the tabs fit in the client area of the window. If they
625 * dont, a scrolling control is added.
626 */
627static void TAB_SetItemBounds (HWND hwnd)
628{
629 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
630 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
631 TEXTMETRICA fontMetrics;
632 INT curItem;
633 INT curItemLeftPos;
634 HFONT hFont, hOldFont;
635 HDC hdc;
636 RECT clientRect;
637 SIZE size;
638
639 /*
640 * We need to get text information so we need a DC and we need to select
641 * a font.
642 */
643 hdc = GetDC(hwnd);
644
645 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
646 hOldFont = SelectObject (hdc, hFont);
647
648 /*
649 * We will base the rectangle calculations on the client rectangle
650 * of the control.
651 */
652 GetClientRect(hwnd, &clientRect);
653
654 /*
655 * The leftmost item will be "0" aligned
656 */
657 curItemLeftPos = 0;
658
659 if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)))
660 {
661 /*
662 * Use the current font to determine the height of a tab.
663 */
664 GetTextMetricsA(hdc, &fontMetrics);
665
666 /*
667 * Make sure there is enough space for the letters + growing the
668 * selected item + extra space for the selected item.
669 */
670 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
671 SELECTED_TAB_OFFSET;
672 }
673
674 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
675 {
676 /*
677 * Calculate the vertical position of the tab
678 */
679 if (lStyle & TCS_BOTTOM)
680 {
681 infoPtr->items[curItem].rect.bottom = clientRect.bottom -
682 SELECTED_TAB_OFFSET;
683 infoPtr->items[curItem].rect.top = clientRect.bottom -
684 infoPtr->tabHeight;
685 }
686 else
687 {
688 infoPtr->items[curItem].rect.top = clientRect.top +
689 SELECTED_TAB_OFFSET;
690 infoPtr->items[curItem].rect.bottom = clientRect.top +
691 infoPtr->tabHeight;
692 }
693
694 /*
695 * Set the leftmost position of the tab.
696 */
697 infoPtr->items[curItem].rect.left = curItemLeftPos;
698
699 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
700 {
701 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
702 infoPtr->tabWidth +
703 2*HORIZONTAL_ITEM_PADDING;
704 }
705 else
706 {
707 /*
708 * Calculate how wide the tab is depending on the text it contains
709 */
710 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
711 lstrlenA(infoPtr->items[curItem].pszText), &size);
712
713 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
714 size.cx + 2*HORIZONTAL_ITEM_PADDING;
715 }
716
717// TRACE(tab, "TextSize: %i\n ", size.cx);
718// TRACE(tab, "Rect: T %i, L %i, B %i, R %i\n",
719// infoPtr->items[curItem].rect.top,
720// infoPtr->items[curItem].rect.left,
721// infoPtr->items[curItem].rect.bottom,
722// infoPtr->items[curItem].rect.right);
723
724 /*
725 * The leftmost position of the next item is the rightmost position
726 * of this one.
727 */
728 if (lStyle & TCS_BUTTONS)
729 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
730 else
731 curItemLeftPos = infoPtr->items[curItem].rect.right;
732 }
733
734 /*
735 * Check if we need a scrolling control.
736 */
737 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
738 clientRect.right);
739
740 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
741
742 /*
743 * Cleanup
744 */
745 SelectObject (hdc, hOldFont);
746 ReleaseDC (hwnd, hdc);
747}
748
749/******************************************************************************
750 * TAB_DrawItem
751 *
752 * This method is used to draw a single tab into the tab control.
753 */
754static void TAB_DrawItem(
755 HWND hwnd,
756 HDC hdc,
757 INT iItem)
758{
759 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
760 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
761 RECT itemRect;
762 RECT selectedRect;
763 BOOL isVisible;
764 RECT r;
765
766 /*
767 * Get the rectangle for the item.
768 */
769 isVisible = TAB_InternalGetItemRect(hwnd,
770 infoPtr,
771 iItem,
772 &itemRect,
773 &selectedRect);
774
775 if (isVisible)
776 {
777 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
778 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
779 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
780 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
781 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
782 HPEN holdPen;
783 INT oldBkMode;
784 INT cx,cy;
785
786 if (lStyle & TCS_BUTTONS)
787 {
788 /*
789 * Get item rectangle.
790 */
791 r = itemRect;
792
793 holdPen = SelectObject (hdc, hwPen);
794
795 if (iItem == infoPtr->iSelected)
796 {
797 /*
798 * Background color.
799 */
800 if (!(lStyle & TCS_OWNERDRAWFIXED))
801 hbr = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
802
803 /*
804 * Erase the background.
805 */
806 FillRect(hdc, &r, hbr);
807
808 /*
809 * Draw the tab now.
810 * The rectangles calculated exclude the right and bottom
811 * borders of the rectangle. To simply the following code, those
812 * borders are shaved-off beforehand.
813 */
814 r.right--;
815 r.bottom--;
816
817 /* highlight */
818 MoveToEx (hdc, r.left, r.bottom, NULL);
819 LineTo (hdc, r.right, r.bottom);
820 LineTo (hdc, r.right, r.top);
821
822 /* shadow */
823 SelectObject(hdc, hbPen);
824 LineTo (hdc, r.left, r.top);
825 LineTo (hdc, r.left, r.bottom);
826 }
827 else
828 {
829 /*
830 * Erase the background.
831 */
832 FillRect(hdc, &r, hbr);
833
834 /* highlight */
835 MoveToEx (hdc, r.left, r.bottom, NULL);
836 LineTo (hdc, r.left, r.top);
837 LineTo (hdc, r.right, r.top);
838
839 /* shadow */
840 SelectObject(hdc, hbPen);
841 LineTo (hdc, r.right, r.bottom);
842 LineTo (hdc, r.left, r.bottom);
843 }
844 }
845 else
846 {
847 /*
848 * Background color.
849 */
850 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
851
852 /*
853 * We draw a rectangle of different sizes depending on the selection
854 * state.
855 */
856 if (iItem == infoPtr->iSelected)
857 r = selectedRect;
858 else
859 r = itemRect;
860
861 /*
862 * Erase the background.
863 * This is necessary when drawing the selected item since it is larger
864 * than the others, it might overlap with stuff already drawn by the
865 * other tabs
866 */
867 FillRect(hdc, &r, hbr);
868
869 /*
870 * Draw the tab now.
871 * The rectangles calculated exclude the right and bottom
872 * borders of the rectangle. To simply the following code, those
873 * borders are shaved-off beforehand.
874 */
875 r.right--;
876 r.bottom--;
877
878 holdPen = SelectObject (hdc, hwPen);
879
880 if (lStyle & TCS_BOTTOM)
881 {
882 /* highlight */
883 MoveToEx (hdc, r.left, r.top, NULL);
884 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
885 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
886
887 /* shadow */
888 SelectObject(hdc, hbPen);
889 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
890 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
891 LineTo (hdc, r.right, r.top);
892 }
893 else
894 {
895 /* highlight */
896 MoveToEx (hdc, r.left, r.bottom, NULL);
897 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
898 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
899 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
900
901 /* shadow */
902 SelectObject(hdc, hbPen);
903 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
904 LineTo (hdc, r.right, r.bottom);
905 }
906 }
907
908 /*
909 * Text pen
910 */
911 SelectObject(hdc, hsdPen);
912
913 oldBkMode = SetBkMode(hdc, TRANSPARENT);
914 SetTextColor (hdc, COLOR_BTNTEXT);
915
916 /*
917 * Deflate the rectangle to acount for the padding
918 */
919 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
920
921 /*
922 * Draw the icon.
923 */
924 if (infoPtr->himl)
925 {
926 ImageList_Draw (infoPtr->himl, iItem, hdc,
927 r.left, r.top+1, ILD_NORMAL);
928 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
929 r.left+=cx;
930 }
931
932 /*
933 * Draw the text;
934 */
935 DrawTextA(hdc,
936 infoPtr->items[iItem].pszText,
937 lstrlenA(infoPtr->items[iItem].pszText),
938 &r,
939 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
940
941 /*
942 * Draw the focus rectangle
943 */
944 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
945 (GetFocus() == hwnd) &&
946 (iItem == infoPtr->uFocus) )
947 {
948 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
949
950 SelectObject(hdc, hfocusPen);
951
952 MoveToEx (hdc, r.left, r.top, NULL);
953 LineTo (hdc, r.right-1, r.top);
954 LineTo (hdc, r.right-1, r.bottom -1);
955 LineTo (hdc, r.left, r.bottom -1);
956 LineTo (hdc, r.left, r.top);
957 }
958
959 /*
960 * Cleanup
961 */
962 SetBkMode(hdc, oldBkMode);
963 SelectObject(hdc, holdPen);
964 }
965}
966
967/******************************************************************************
968 * TAB_DrawBorder
969 *
970 * This method is used to draw the raised border around the tab control
971 * "content" area.
972 */
973static void TAB_DrawBorder (HWND hwnd, HDC hdc)
974{
975 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
976 HPEN htmPen;
977 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
978 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
979 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
980 RECT rect;
981
982 GetClientRect (hwnd, &rect);
983
984 /*
985 * Adjust for the style
986 */
987 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
988 {
989 rect.bottom -= infoPtr->tabHeight;
990 }
991 else
992 {
993 rect.top += infoPtr->tabHeight;
994 }
995
996 /*
997 * Shave-off the right and bottom margins (exluded in the
998 * rect)
999 */
1000 rect.right--;
1001 rect.bottom--;
1002
1003 /* highlight */
1004 htmPen = SelectObject (hdc, hwPen);
1005
1006 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1007 LineTo (hdc, rect.left, rect.top);
1008 LineTo (hdc, rect.right, rect.top);
1009
1010 /* Dark Shadow */
1011 SelectObject (hdc, hbPen);
1012 LineTo (hdc, rect.right, rect.bottom );
1013 LineTo (hdc, rect.left, rect.bottom);
1014
1015 /* shade */
1016 SelectObject (hdc, hShade );
1017 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1018 LineTo (hdc, rect.right-1, rect.bottom-1);
1019 LineTo (hdc, rect.left, rect.bottom-1);
1020
1021 SelectObject(hdc, htmPen);
1022}
1023
1024/******************************************************************************
1025 * TAB_Refresh
1026 *
1027 * This method repaints the tab control..
1028 */
1029static void TAB_Refresh (HWND hwnd, HDC hdc)
1030{
1031 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1032 HFONT hOldFont;
1033 INT i;
1034
1035 if (!infoPtr->DoRedraw)
1036 return;
1037
1038 hOldFont = SelectObject (hdc, infoPtr->hFont);
1039
1040 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1041 {
1042 for (i = 0; i < infoPtr->uNumItem; i++)
1043 {
1044 TAB_DrawItem (hwnd, hdc, i);
1045 }
1046 }
1047 else
1048 {
1049 /*
1050 * Draw all the non selected item first.
1051 */
1052 for (i = 0; i < infoPtr->uNumItem; i++)
1053 {
1054 if (i != infoPtr->iSelected)
1055 TAB_DrawItem (hwnd, hdc, i);
1056 }
1057
1058 /*
1059 * Now, draw the border, draw it before the selected item
1060 * since the selected item overwrites part of the border.
1061 */
1062 TAB_DrawBorder (hwnd, hdc);
1063
1064 /*
1065 * Then, draw the selected item
1066 */
1067 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1068 }
1069
1070 SelectObject (hdc, hOldFont);
1071}
1072
1073static LRESULT
1074TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1075{
1076 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1077
1078 infoPtr->DoRedraw=(BOOL) wParam;
1079 return 0;
1080}
1081
1082static LRESULT TAB_EraseBackground(
1083 HWND hwnd,
1084 HDC givenDC)
1085{
1086 HDC hdc;
1087 RECT clientRect;
1088
1089 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1090
1091 hdc = givenDC ? givenDC : GetDC(hwnd);
1092
1093 GetClientRect(hwnd, &clientRect);
1094
1095 FillRect(hdc, &clientRect, brush);
1096
1097 if (givenDC==0)
1098 ReleaseDC(hwnd, hdc);
1099
1100 return 0;
1101}
1102
1103/******************************************************************************
1104 * TAB_EnsureSelectionVisible
1105 *
1106 * This method will make sure that the current selection is completely
1107 * visible by scrolling until it is.
1108 */
1109static void TAB_EnsureSelectionVisible(
1110 HWND hwnd,
1111 TAB_INFO* infoPtr)
1112{
1113 RECT selectedRect;
1114 RECT visibleRect;
1115 RECT scrollerRect;
1116 BOOL isVisible;
1117
1118 /*
1119 * Do the trivial cases first.
1120 */
1121 if ( (!infoPtr->needsScrolling) ||
1122 (infoPtr->hwndUpDown==0) )
1123 return;
1124
1125 if (infoPtr->leftmostVisible > infoPtr->iSelected)
1126 {
1127 infoPtr->leftmostVisible = infoPtr->iSelected;
1128 return;
1129 }
1130
1131 /*
1132 * Calculate the part of the client area that is visible.
1133 */
1134 GetClientRect(hwnd, &visibleRect);
1135 GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
1136 visibleRect.right -= scrollerRect.right;
1137
1138 /*
1139 * Get the rectangle for the item
1140 */
1141 isVisible = TAB_InternalGetItemRect(hwnd,
1142 infoPtr,
1143 infoPtr->iSelected,
1144 NULL,
1145 &selectedRect);
1146
1147 /*
1148 * If this function can't say it's completely invisible, maybe it
1149 * is partially visible. Let's check.
1150 */
1151 if (isVisible)
1152 {
1153 POINT pt1;
1154 POINT pt2;
1155
1156 pt1.x = selectedRect.left;
1157 pt1.y = selectedRect.top;
1158 pt2.x = selectedRect.right - 1;
1159 pt2.y = selectedRect.bottom - 1;
1160
1161 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1162 }
1163
1164 while ( (infoPtr->leftmostVisible < infoPtr->iSelected) &&
1165 !isVisible)
1166 {
1167 infoPtr->leftmostVisible++;
1168
1169 /*
1170 * Get the rectangle for the item
1171 */
1172 isVisible = TAB_InternalGetItemRect(hwnd,
1173 infoPtr,
1174 infoPtr->iSelected,
1175 NULL,
1176 &selectedRect);
1177
1178 /*
1179 * If this function can't say it's completely invisible, maybe it
1180 * is partially visible. Let's check.
1181 */
1182 if (isVisible)
1183 {
1184 POINT pt1;
1185 POINT pt2;
1186
1187 pt1.x = selectedRect.left;
1188 pt1.y = selectedRect.top;
1189 pt2.x = selectedRect.right - 1;
1190 pt2.y = selectedRect.bottom - 1;
1191
1192 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1193 }
1194 }
1195}
1196
1197/******************************************************************************
1198 * TAB_InvalidateTabArea
1199 *
1200 * This method will invalidate the portion of the control that contains the
1201 * tabs. It is called when the state of the control changes and needs
1202 * to be redisplayed
1203 */
1204static void TAB_InvalidateTabArea(
1205 HWND hwnd,
1206 TAB_INFO* infoPtr)
1207{
1208 RECT clientRect;
1209
1210 GetClientRect(hwnd, &clientRect);
1211
1212 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1213 {
1214 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
1215 }
1216 else
1217 {
1218 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1219 }
1220
1221 InvalidateRect(hwnd, &clientRect, TRUE);
1222}
1223
1224static LRESULT
1225TAB_Paint (HWND hwnd, WPARAM wParam)
1226{
1227 HDC hdc;
1228 PAINTSTRUCT ps;
1229
1230 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1231 TAB_Refresh (hwnd, hdc);
1232
1233 if(!wParam)
1234 EndPaint (hwnd, &ps);
1235
1236 return 0;
1237}
1238
1239static LRESULT
1240TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1241{
1242 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1243 TCITEMA *pti;
1244 INT iItem, len;
1245 RECT rect;
1246
1247 GetClientRect (hwnd, &rect);
1248// TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1249// rect.top, rect.left, rect.bottom, rect.right);
1250
1251 pti = (TCITEMA *)lParam;
1252 iItem = (INT)wParam;
1253
1254 if (iItem < 0) return -1;
1255 if (iItem > infoPtr->uNumItem)
1256 iItem = infoPtr->uNumItem;
1257
1258 if (infoPtr->uNumItem == 0) {
1259 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1260 infoPtr->uNumItem++;
1261 }
1262 else {
1263 TAB_ITEM *oldItems = infoPtr->items;
1264
1265 infoPtr->uNumItem++;
1266 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1267
1268 /* pre insert copy */
1269 if (iItem > 0) {
1270 memcpy (&infoPtr->items[0], &oldItems[0],
1271 iItem * sizeof(TAB_ITEM));
1272 }
1273
1274 /* post insert copy */
1275 if (iItem < infoPtr->uNumItem - 1) {
1276 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1277 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1278
1279 }
1280
1281 COMCTL32_Free (oldItems);
1282 }
1283
1284 infoPtr->items[iItem].mask = pti->mask;
1285 if (pti->mask & TCIF_TEXT) {
1286 len = lstrlenA (pti->pszText);
1287 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1288 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1289 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1290 }
1291
1292 if (pti->mask & TCIF_IMAGE)
1293 infoPtr->items[iItem].iImage = pti->iImage;
1294
1295 if (pti->mask & TCIF_PARAM)
1296 infoPtr->items[iItem].lParam = pti->lParam;
1297
1298 TAB_InvalidateTabArea(hwnd, infoPtr);
1299
1300// TRACE(tab, "[%04x]: added item %d '%s'\n",
1301// hwnd, iItem, infoPtr->items[iItem].pszText);
1302
1303 TAB_SetItemBounds(hwnd);
1304 return iItem;
1305}
1306
1307static LRESULT
1308TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1309{
1310 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1311 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1312 LONG lResult = 0;
1313
1314 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1315 {
1316 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1317 infoPtr->tabWidth = (INT)LOWORD(lParam);
1318 infoPtr->tabHeight = (INT)HIWORD(lParam);
1319 }
1320
1321 return lResult;
1322}
1323
1324static LRESULT
1325TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1326{
1327 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1328 TCITEMA *tabItem;
1329 TAB_ITEM *wineItem;
1330 INT iItem,len;
1331
1332 iItem=(INT) wParam;
1333 tabItem=(LPTCITEMA ) lParam;
1334// TRACE (tab,"%d %p\n",iItem, tabItem);
1335 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1336
1337 wineItem=& infoPtr->items[iItem];
1338
1339 if (tabItem->mask & TCIF_IMAGE)
1340 wineItem->iImage=tabItem->iImage;
1341
1342 if (tabItem->mask & TCIF_PARAM)
1343 wineItem->lParam=tabItem->lParam;
1344
1345// if (tabItem->mask & TCIF_RTLREADING)
1346// FIXME (tab,"TCIF_RTLREADING\n");
1347
1348 if (tabItem->mask & TCIF_STATE)
1349 wineItem->dwState=tabItem->dwState;
1350
1351 if (tabItem->mask & TCIF_TEXT) {
1352 len=lstrlenA (tabItem->pszText);
1353 if (len>wineItem->cchTextMax)
1354 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1355 lstrcpynA (wineItem->pszText, tabItem->pszText, len);
1356 }
1357
1358 return TRUE;
1359}
1360
1361static LRESULT
1362TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1363{
1364 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1365
1366 return infoPtr->uNumItem;
1367}
1368
1369
1370static LRESULT
1371TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1372{
1373 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1374 TCITEMA *tabItem;
1375 TAB_ITEM *wineItem;
1376 INT iItem;
1377
1378 iItem=(INT) wParam;
1379 tabItem=(LPTCITEMA) lParam;
1380// TRACE (tab,"\n");
1381 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1382
1383 wineItem=& infoPtr->items[iItem];
1384
1385 if (tabItem->mask & TCIF_IMAGE)
1386 tabItem->iImage=wineItem->iImage;
1387
1388 if (tabItem->mask & TCIF_PARAM)
1389 tabItem->lParam=wineItem->lParam;
1390
1391// if (tabItem->mask & TCIF_RTLREADING)
1392// FIXME (tab, "TCIF_RTLREADING\n");
1393
1394 if (tabItem->mask & TCIF_STATE)
1395 tabItem->dwState=wineItem->dwState;
1396
1397 if (tabItem->mask & TCIF_TEXT)
1398 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1399
1400 return TRUE;
1401}
1402
1403static LRESULT
1404TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1405{
1406 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1407 INT iItem = (INT) wParam;
1408 BOOL bResult = FALSE;
1409
1410 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1411 {
1412 TAB_ITEM *oldItems = infoPtr->items;
1413
1414 infoPtr->uNumItem--;
1415 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1416
1417 if (iItem > 0)
1418 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1419
1420 if (iItem < infoPtr->uNumItem)
1421 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1422 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1423
1424 COMCTL32_Free (oldItems);
1425
1426 /*
1427 * Readjust the selected index.
1428 */
1429 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1430 infoPtr->iSelected--;
1431
1432 if (iItem < infoPtr->iSelected)
1433 infoPtr->iSelected--;
1434
1435 /*
1436 * Reposition and repaint tabs.
1437 */
1438 TAB_SetItemBounds(hwnd);
1439 TAB_InvalidateTabArea(hwnd,infoPtr);
1440
1441 bResult = TRUE;
1442 }
1443
1444 return bResult;
1445}
1446
1447static LRESULT
1448TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1449{
1450 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1451
1452 COMCTL32_Free (infoPtr->items);
1453 infoPtr->uNumItem=0;
1454
1455 return TRUE;
1456}
1457
1458
1459static LRESULT
1460TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1461{
1462 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1463
1464// TRACE (tab,"\n");
1465 return (LRESULT)infoPtr->hFont;
1466}
1467
1468static LRESULT
1469TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1470
1471{
1472 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1473
1474// TRACE (tab,"%x %lx\n",wParam, lParam);
1475
1476 infoPtr->hFont = (HFONT)wParam;
1477
1478 TAB_SetItemBounds(hwnd);
1479
1480 TAB_InvalidateTabArea(hwnd, infoPtr);
1481
1482 return 0;
1483}
1484
1485
1486static LRESULT
1487TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1488{
1489 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1490
1491// TRACE (tab,"\n");
1492 return (LRESULT)infoPtr->himl;
1493}
1494
1495static LRESULT
1496TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1497{
1498 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1499 HIMAGELIST himlPrev;
1500
1501// TRACE (tab,"\n");
1502 himlPrev = infoPtr->himl;
1503 infoPtr->himl= (HIMAGELIST)lParam;
1504 return (LRESULT)himlPrev;
1505}
1506
1507
1508static LRESULT
1509TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1510
1511{
1512/* I'm not really sure what the following code was meant to do.
1513 This is what it is doing:
1514 When WM_SIZE is sent with SIZE_RESTORED, the control
1515 gets positioned in the top left corner.
1516
1517 RECT parent_rect;
1518 HWND parent;
1519 UINT uPosFlags,cx,cy;
1520
1521 uPosFlags=0;
1522 if (!wParam) {
1523 parent = GetParent (hwnd);
1524 GetClientRect(parent, &parent_rect);
1525 cx=LOWORD (lParam);
1526 cy=HIWORD (lParam);
1527 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1528 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1529
1530 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1531 cx, cy, uPosFlags | SWP_NOZORDER);
1532 } else {
1533// FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1534 } */
1535
1536 /*
1537 * Recompute the size/position of the tabs.
1538 */
1539 TAB_SetItemBounds (hwnd);
1540
1541 /*
1542 * Force a repaint of the control.
1543 */
1544 InvalidateRect(hwnd, NULL, TRUE);
1545
1546 return 0;
1547}
1548
1549
1550static LRESULT
1551TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1552{
1553 TAB_INFO *infoPtr;
1554 TEXTMETRICA fontMetrics;
1555 HDC hdc;
1556 HFONT hOldFont;
1557
1558 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1559
1560 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1561
1562 infoPtr->uNumItem = 0;
1563 infoPtr->hFont = 0;
1564 infoPtr->items = 0;
1565 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1566 infoPtr->iSelected = 0;
1567 infoPtr->uFocus = 0;
1568 infoPtr->hwndToolTip = 0;
1569 infoPtr->DoRedraw = TRUE;
1570 infoPtr->needsScrolling = FALSE;
1571 infoPtr->hwndUpDown = 0;
1572 infoPtr->leftmostVisible = 0;
1573
1574// TRACE(tab, "Created tab control, hwnd [%04x]\n", hwnd);
1575 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
1576 /* Create tooltip control */
1577 infoPtr->hwndToolTip =
1578 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1579 CW_USEDEFAULT, CW_USEDEFAULT,
1580 CW_USEDEFAULT, CW_USEDEFAULT,
1581 hwnd, 0, 0, 0);
1582
1583 /* Send NM_TOOLTIPSCREATED notification */
1584 if (infoPtr->hwndToolTip) {
1585 NMTOOLTIPSCREATED nmttc;
1586
1587 nmttc.hdr.hwndFrom = hwnd;
1588 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1589 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1590 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1591
1592 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1593 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1594 }
1595 }
1596
1597 /*
1598 * We need to get text information so we need a DC and we need to select
1599 * a font.
1600 */
1601 hdc = GetDC(hwnd);
1602 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1603
1604 /*
1605 * Use the system font to determine the initial height of a tab.
1606 */
1607 GetTextMetricsA(hdc, &fontMetrics);
1608
1609 /*
1610 * Make sure there is enough space for the letters + growing the
1611 * selected item + extra space for the selected item.
1612 */
1613 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1614 SELECTED_TAB_OFFSET;
1615
1616 /*
1617 * Initialize the width of a tab.
1618 */
1619 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1620
1621 SelectObject (hdc, hOldFont);
1622 ReleaseDC(hwnd, hdc);
1623
1624 return 0;
1625}
1626
1627static LRESULT
1628TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1629{
1630 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1631 INT iItem;
1632
1633 if (infoPtr->items) {
1634 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1635 if (infoPtr->items[iItem].pszText)
1636 COMCTL32_Free (infoPtr->items[iItem].pszText);
1637 }
1638 COMCTL32_Free (infoPtr->items);
1639 }
1640
1641 if (infoPtr->hwndToolTip)
1642 DestroyWindow (infoPtr->hwndToolTip);
1643
1644 if (infoPtr->hwndUpDown)
1645 DestroyWindow(infoPtr->hwndUpDown);
1646
1647 COMCTL32_Free (infoPtr);
1648 return 0;
1649}
1650
1651LRESULT WINAPI
1652TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1653{
1654 switch (uMsg)
1655 {
1656 case TCM_GETIMAGELIST:
1657 return TAB_GetImageList (hwnd, wParam, lParam);
1658
1659 case TCM_SETIMAGELIST:
1660 return TAB_SetImageList (hwnd, wParam, lParam);
1661
1662 case TCM_GETITEMCOUNT:
1663 return TAB_GetItemCount (hwnd, wParam, lParam);
1664
1665 case TCM_GETITEMA:
1666 return TAB_GetItemA (hwnd, wParam, lParam);
1667
1668 case TCM_GETITEMW:
1669// FIXME (tab, "Unimplemented msg TCM_GETITEMW\n");
1670 return 0;
1671
1672 case TCM_SETITEMA:
1673 return TAB_SetItemA (hwnd, wParam, lParam);
1674
1675 case TCM_SETITEMW:
1676// FIXME (tab, "Unimplemented msg TCM_SETITEMW\n");
1677 return 0;
1678
1679 case TCM_DELETEITEM:
1680 return TAB_DeleteItem (hwnd, wParam, lParam);
1681
1682 case TCM_DELETEALLITEMS:
1683 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1684
1685 case TCM_GETITEMRECT:
1686 return TAB_GetItemRect (hwnd, wParam, lParam);
1687
1688 case TCM_GETCURSEL:
1689 return TAB_GetCurSel (hwnd);
1690
1691 case TCM_HITTEST:
1692 return TAB_HitTest (hwnd, wParam, lParam);
1693
1694 case TCM_SETCURSEL:
1695 return TAB_SetCurSel (hwnd, wParam);
1696
1697 case TCM_INSERTITEMA:
1698 return TAB_InsertItem (hwnd, wParam, lParam);
1699
1700 case TCM_INSERTITEMW:
1701// FIXME (tab, "Unimplemented msg TCM_INSERTITEM32W\n");
1702 return 0;
1703
1704 case TCM_SETITEMEXTRA:
1705// FIXME (tab, "Unimplemented msg TCM_SETITEMEXTRA\n");
1706 return 0;
1707
1708 case TCM_ADJUSTRECT:
1709 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1710
1711 case TCM_SETITEMSIZE:
1712 return TAB_SetItemSize (hwnd, wParam, lParam);
1713
1714 case TCM_REMOVEIMAGE:
1715// FIXME (tab, "Unimplemented msg TCM_REMOVEIMAGE\n");
1716 return 0;
1717
1718 case TCM_SETPADDING:
1719// FIXME (tab, "Unimplemented msg TCM_SETPADDING\n");
1720 return 0;
1721
1722 case TCM_GETROWCOUNT:
1723// FIXME (tab, "Unimplemented msg TCM_GETROWCOUNT\n");
1724 return 0;
1725
1726 case TCM_GETUNICODEFORMAT:
1727// FIXME (tab, "Unimplemented msg TCM_GETUNICODEFORMAT\n");
1728 return 0;
1729
1730 case TCM_SETUNICODEFORMAT:
1731// FIXME (tab, "Unimplemented msg TCM_SETUNICODEFORMAT\n");
1732 return 0;
1733
1734 case TCM_HIGHLIGHTITEM:
1735// FIXME (tab, "Unimplemented msg TCM_HIGHLIGHTITEM\n");
1736 return 0;
1737
1738 case TCM_GETTOOLTIPS:
1739 return TAB_GetToolTips (hwnd, wParam, lParam);
1740
1741 case TCM_SETTOOLTIPS:
1742 return TAB_SetToolTips (hwnd, wParam, lParam);
1743
1744 case TCM_GETCURFOCUS:
1745 return TAB_GetCurFocus (hwnd);
1746
1747 case TCM_SETCURFOCUS:
1748 return TAB_SetCurFocus (hwnd, wParam);
1749
1750 case TCM_SETMINTTABWIDTH:
1751// FIXME (tab, "Unimplemented msg TCM_SETMINTTABWIDTH\n");
1752 return 0;
1753
1754 case TCM_DESELECTALL:
1755// FIXME (tab, "Unimplemented msg TCM_DESELECTALL\n");
1756 return 0;
1757
1758 case TCM_GETEXTENDEDSTYLE:
1759// FIXME (tab, "Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1760 return 0;
1761
1762 case TCM_SETEXTENDEDSTYLE:
1763// FIXME (tab, "Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1764 return 0;
1765
1766 case WM_GETFONT:
1767 return TAB_GetFont (hwnd, wParam, lParam);
1768
1769 case WM_SETFONT:
1770 return TAB_SetFont (hwnd, wParam, lParam);
1771
1772 case WM_CREATE:
1773 return TAB_Create (hwnd, wParam, lParam);
1774
1775 case WM_NCDESTROY:
1776 return TAB_Destroy (hwnd, wParam, lParam);
1777
1778 case WM_GETDLGCODE:
1779 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1780
1781 case WM_LBUTTONDOWN:
1782 return TAB_LButtonDown (hwnd, wParam, lParam);
1783
1784 case WM_LBUTTONUP:
1785 return TAB_LButtonUp (hwnd, wParam, lParam);
1786
1787 case WM_RBUTTONDOWN:
1788 return TAB_RButtonDown (hwnd, wParam, lParam);
1789
1790 case WM_MOUSEMOVE:
1791 return TAB_MouseMove (hwnd, wParam, lParam);
1792
1793 case WM_ERASEBKGND:
1794 return TAB_EraseBackground (hwnd, (HDC)wParam);
1795
1796 case WM_PAINT:
1797 return TAB_Paint (hwnd, wParam);
1798
1799 case WM_SIZE:
1800 return TAB_Size (hwnd, wParam, lParam);
1801
1802 case WM_SETREDRAW:
1803 return TAB_SetRedraw (hwnd, wParam);
1804
1805 case WM_HSCROLL:
1806 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1807
1808 case WM_KILLFOCUS:
1809 case WM_SETFOCUS:
1810 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
1811
1812 case WM_KEYUP:
1813 return TAB_KeyUp(hwnd, wParam);
1814
1815 default:
1816// if (uMsg >= WM_USER)
1817// ERR (tab, "unknown msg %04x wp=%08x lp=%08lx\n",
1818// uMsg, wParam, lParam);
1819 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1820 }
1821
1822 return 0;
1823}
1824
1825
1826VOID
1827TAB_Register (VOID)
1828{
1829 WNDCLASSA wndClass;
1830
1831 if (GlobalFindAtomA (WC_TABCONTROLA)) return;
1832
1833 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1834 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1835 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
1836 wndClass.cbClsExtra = 0;
1837 wndClass.cbWndExtra = sizeof(TAB_INFO *);
1838 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1839 wndClass.hbrBackground = (HBRUSH)NULL;
1840 wndClass.lpszClassName = WC_TABCONTROLA;
1841
1842 RegisterClassA (&wndClass);
1843}
1844
1845
1846VOID
1847TAB_Unregister (VOID)
1848{
1849 if (GlobalFindAtomA (WC_TABCONTROLA))
1850 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);
1851}
1852
Note: See TracBrowser for help on using the repository browser.