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

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

source code cleanup, remove warnings

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