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

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

merged with latest WINE changes

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