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

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

* empty log message *

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