source: trunk/src/comctl32/tab.cpp@ 2875

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

C -> C++, WINE animate, treeview WM_VSCROLL fixed

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