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

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

* empty log message *

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