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

Last change on this file since 603 was 603, checked in by cbratschi, 26 years ago

trackbar: performance updates

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