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

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

merged latest WINE 990923 changes

File size: 49.1 KB
Line 
1/* $Id: tab.c,v 1.13 1999-09-26 11:01:11 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 990823 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 infoPtr->iSelected = 0;
1304 }
1305 else {
1306 TAB_ITEM *oldItems = infoPtr->items;
1307
1308 infoPtr->uNumItem++;
1309 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1310
1311 /* pre insert copy */
1312 if (iItem > 0) {
1313 memcpy (&infoPtr->items[0], &oldItems[0],
1314 iItem * sizeof(TAB_ITEM));
1315 }
1316
1317 /* post insert copy */
1318 if (iItem < infoPtr->uNumItem - 1) {
1319 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1320 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1321
1322 }
1323
1324 if (iItem <= infoPtr->iSelected)
1325 infoPtr->iSelected++;
1326
1327 COMCTL32_Free (oldItems);
1328 }
1329
1330 infoPtr->items[iItem].mask = pti->mask;
1331 if (pti->mask & TCIF_TEXT) {
1332 len = lstrlenA (pti->pszText);
1333 infoPtr->items[iItem].pszText = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1334 lstrcpyAtoW (infoPtr->items[iItem].pszText, pti->pszText);
1335 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1336 }
1337
1338 if (pti->mask & TCIF_IMAGE)
1339 infoPtr->items[iItem].iImage = pti->iImage;
1340
1341 if (pti->mask & TCIF_PARAM)
1342 infoPtr->items[iItem].lParam = pti->lParam;
1343
1344 TAB_InvalidateTabArea(hwnd, infoPtr);
1345
1346// TRACE(tab, "[%04x]: added item %d '%s'\n",
1347// hwnd, iItem, infoPtr->items[iItem].pszText);
1348
1349 TAB_SetItemBounds(hwnd);
1350 return iItem;
1351}
1352
1353static LRESULT TAB_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1354{
1355 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1356 TCITEMW *pti;
1357 INT iItem, len;
1358 RECT rect;
1359
1360 GetClientRect (hwnd, &rect);
1361// TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1362// rect.top, rect.left, rect.bottom, rect.right);
1363
1364 pti = (TCITEMW*)lParam;
1365 iItem = (INT)wParam;
1366
1367 if (iItem < 0) return -1;
1368 if (iItem > infoPtr->uNumItem)
1369 iItem = infoPtr->uNumItem;
1370
1371 if (infoPtr->uNumItem == 0) {
1372 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1373 infoPtr->uNumItem++;
1374 }
1375 else {
1376 TAB_ITEM *oldItems = infoPtr->items;
1377
1378 infoPtr->uNumItem++;
1379 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1380
1381 /* pre insert copy */
1382 if (iItem > 0) {
1383 memcpy (&infoPtr->items[0], &oldItems[0],
1384 iItem * sizeof(TAB_ITEM));
1385 }
1386
1387 /* post insert copy */
1388 if (iItem < infoPtr->uNumItem - 1) {
1389 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1390 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1391
1392 }
1393
1394 COMCTL32_Free (oldItems);
1395 }
1396
1397 infoPtr->items[iItem].mask = pti->mask;
1398 if (pti->mask & TCIF_TEXT) {
1399 len = lstrlenW (pti->pszText);
1400 infoPtr->items[iItem].pszText = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1401 lstrcpyW (infoPtr->items[iItem].pszText, pti->pszText);
1402 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1403 }
1404
1405 if (pti->mask & TCIF_IMAGE)
1406 infoPtr->items[iItem].iImage = pti->iImage;
1407
1408 if (pti->mask & TCIF_PARAM)
1409 infoPtr->items[iItem].lParam = pti->lParam;
1410
1411 TAB_InvalidateTabArea(hwnd, infoPtr);
1412
1413// TRACE(tab, "[%04x]: added item %d '%s'\n",
1414// hwnd, iItem, infoPtr->items[iItem].pszText);
1415
1416 TAB_SetItemBounds(hwnd);
1417 return iItem;
1418}
1419
1420static LRESULT
1421TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1422{
1423 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1424 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1425 LONG lResult = 0;
1426
1427 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1428 {
1429 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1430 infoPtr->tabWidth = (INT)LOWORD(lParam);
1431 infoPtr->tabHeight = (INT)HIWORD(lParam);
1432 }
1433
1434 return lResult;
1435}
1436
1437static LRESULT
1438TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1439{
1440 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1441 TCITEMA *tabItem;
1442 TAB_ITEM *wineItem;
1443 INT iItem,len;
1444
1445 iItem=(INT) wParam;
1446 tabItem=(LPTCITEMA ) lParam;
1447// TRACE("%d %p\n",iItem, tabItem);
1448 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1449
1450 wineItem=& infoPtr->items[iItem];
1451
1452 if (tabItem->mask & TCIF_IMAGE)
1453 wineItem->iImage=tabItem->iImage;
1454
1455 if (tabItem->mask & TCIF_PARAM)
1456 wineItem->lParam=tabItem->lParam;
1457
1458// if (tabItem->mask & TCIF_RTLREADING)
1459// FIXME("TCIF_RTLREADING\n");
1460
1461 if (tabItem->mask & TCIF_STATE)
1462 wineItem->dwState=tabItem->dwState;
1463
1464 if (tabItem->mask & TCIF_TEXT) {
1465 len=lstrlenA (tabItem->pszText);
1466 if (len>wineItem->cchTextMax)
1467 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1468 lstrcpyA (wineItem->pszText, tabItem->pszText);
1469 }
1470
1471 /*
1472 * Update and repaint tabs.
1473 */
1474 TAB_SetItemBounds(hwnd);
1475 TAB_InvalidateTabArea(hwnd,infoPtr);
1476
1477 return TRUE;
1478}
1479
1480static LRESULT TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1481{
1482 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1483 TCITEMW *tabItem;
1484 TAB_ITEM *wineItem;
1485 INT iItem,len;
1486
1487 iItem=(INT) wParam;
1488 tabItem=(LPTCITEMW) lParam;
1489// TRACE (tab,"%d %p\n",iItem, tabItem);
1490 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1491
1492 wineItem=& infoPtr->items[iItem];
1493
1494 if (tabItem->mask & TCIF_IMAGE)
1495 wineItem->iImage=tabItem->iImage;
1496
1497 if (tabItem->mask & TCIF_PARAM)
1498 wineItem->lParam=tabItem->lParam;
1499
1500// if (tabItem->mask & TCIF_RTLREADING)
1501// FIXME (tab,"TCIF_RTLREADING\n");
1502
1503 if (tabItem->mask & TCIF_STATE)
1504 wineItem->dwState=tabItem->dwState;
1505
1506 if (tabItem->mask & TCIF_TEXT) {
1507 len = lstrlenW (tabItem->pszText);
1508 if (len>wineItem->cchTextMax)
1509 wineItem->pszText = COMCTL32_ReAlloc (wineItem->pszText, (len+1)*sizeof(WCHAR));
1510 lstrcpyW (wineItem->pszText, tabItem->pszText);
1511 }
1512
1513 return TRUE;
1514}
1515
1516static LRESULT
1517TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1518{
1519 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1520
1521 return infoPtr->uNumItem;
1522}
1523
1524
1525static LRESULT
1526TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1527{
1528 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1529 TCITEMA *tabItem;
1530 TAB_ITEM *wineItem;
1531 INT iItem;
1532
1533 iItem=(INT) wParam;
1534 tabItem=(LPTCITEMA) lParam;
1535// TRACE (tab,"\n");
1536 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1537
1538 wineItem=& infoPtr->items[iItem];
1539
1540 if (tabItem->mask & TCIF_IMAGE)
1541 tabItem->iImage=wineItem->iImage;
1542
1543 if (tabItem->mask & TCIF_PARAM)
1544 tabItem->lParam=wineItem->lParam;
1545
1546// if (tabItem->mask & TCIF_RTLREADING)
1547// FIXME (tab, "TCIF_RTLREADING\n");
1548
1549 if (tabItem->mask & TCIF_STATE)
1550 tabItem->dwState=wineItem->dwState;
1551
1552 if (tabItem->mask & TCIF_TEXT)
1553 lstrcpynWtoA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1554
1555 return TRUE;
1556}
1557
1558static LRESULT TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1559{
1560 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1561 TCITEMW *tabItem;
1562 TAB_ITEM *wineItem;
1563 INT iItem;
1564
1565 iItem=(INT) wParam;
1566 tabItem=(LPTCITEMW) lParam;
1567// TRACE (tab,"\n");
1568 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1569
1570 wineItem=& infoPtr->items[iItem];
1571
1572 if (tabItem->mask & TCIF_IMAGE)
1573 tabItem->iImage=wineItem->iImage;
1574
1575 if (tabItem->mask & TCIF_PARAM)
1576 tabItem->lParam=wineItem->lParam;
1577
1578// if (tabItem->mask & TCIF_RTLREADING)
1579// FIXME (tab, "TCIF_RTLREADING\n");
1580
1581 if (tabItem->mask & TCIF_STATE)
1582 tabItem->dwState=wineItem->dwState;
1583
1584 if (tabItem->mask & TCIF_TEXT)
1585 lstrcpynW (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1586
1587 return TRUE;
1588}
1589
1590static LRESULT
1591TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1592{
1593 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1594 INT iItem = (INT) wParam;
1595 BOOL bResult = FALSE;
1596
1597 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1598 {
1599 TAB_ITEM *oldItems = infoPtr->items;
1600
1601 infoPtr->uNumItem--;
1602 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1603
1604 if (iItem > 0)
1605 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1606
1607 if (iItem < infoPtr->uNumItem)
1608 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1609 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1610
1611 COMCTL32_Free (oldItems);
1612
1613 /*
1614 * Readjust the selected index.
1615 */
1616 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1617 infoPtr->iSelected--;
1618
1619 if (iItem < infoPtr->iSelected)
1620 infoPtr->iSelected--;
1621
1622 if (infoPtr->uNumItem == 0)
1623 infoPtr->iSelected = -1;
1624
1625 /*
1626 * Reposition and repaint tabs.
1627 */
1628 TAB_SetItemBounds(hwnd);
1629 TAB_InvalidateTabArea(hwnd,infoPtr);
1630
1631 bResult = TRUE;
1632 }
1633
1634 return bResult;
1635}
1636
1637static LRESULT
1638TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1639{
1640 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1641
1642 COMCTL32_Free (infoPtr->items);
1643 infoPtr->uNumItem = 0;
1644 infoPtr->iSelected = -1;
1645
1646 return TRUE;
1647}
1648
1649
1650static LRESULT
1651TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1652{
1653 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1654
1655// TRACE (tab,"\n");
1656 return (LRESULT)infoPtr->hFont;
1657}
1658
1659static LRESULT
1660TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1661
1662{
1663 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1664
1665// TRACE (tab,"%x %lx\n",wParam, lParam);
1666
1667 infoPtr->hFont = (HFONT)wParam;
1668
1669 TAB_SetItemBounds(hwnd);
1670
1671 TAB_InvalidateTabArea(hwnd, infoPtr);
1672
1673 return 0;
1674}
1675
1676
1677static LRESULT
1678TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1679{
1680 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1681
1682// TRACE (tab,"\n");
1683 return (LRESULT)infoPtr->himl;
1684}
1685
1686static LRESULT
1687TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1688{
1689 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1690 HIMAGELIST himlPrev;
1691
1692// TRACE (tab,"\n");
1693 himlPrev = infoPtr->himl;
1694 infoPtr->himl= (HIMAGELIST)lParam;
1695 return (LRESULT)himlPrev;
1696}
1697
1698
1699static LRESULT
1700TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1701
1702{
1703/* I'm not really sure what the following code was meant to do.
1704 This is what it is doing:
1705 When WM_SIZE is sent with SIZE_RESTORED, the control
1706 gets positioned in the top left corner.
1707
1708 RECT parent_rect;
1709 HWND parent;
1710 UINT uPosFlags,cx,cy;
1711
1712 uPosFlags=0;
1713 if (!wParam) {
1714 parent = GetParent (hwnd);
1715 GetClientRect(parent, &parent_rect);
1716 cx=LOWORD (lParam);
1717 cy=HIWORD (lParam);
1718 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1719 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1720
1721 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1722 cx, cy, uPosFlags | SWP_NOZORDER);
1723 } else {
1724// FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1725 } */
1726
1727 /*
1728 * Recompute the size/position of the tabs.
1729 */
1730 TAB_SetItemBounds (hwnd);
1731
1732 /*
1733 * Force a repaint of the control.
1734 */
1735 InvalidateRect(hwnd, NULL, TRUE);
1736
1737 return 0;
1738}
1739
1740
1741static LRESULT
1742TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1743{
1744 TAB_INFO *infoPtr;
1745 TEXTMETRICA fontMetrics;
1746 HDC hdc;
1747 HFONT hOldFont;
1748
1749 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1750
1751 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1752
1753 infoPtr->uNumItem = 0;
1754 infoPtr->hFont = 0;
1755 infoPtr->items = 0;
1756 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1757 infoPtr->iSelected = -1;
1758 infoPtr->uFocus = 0;
1759 infoPtr->hwndToolTip = 0;
1760 infoPtr->DoRedraw = TRUE;
1761 infoPtr->needsScrolling = FALSE;
1762 infoPtr->hwndUpDown = 0;
1763 infoPtr->leftmostVisible = 0;
1764
1765// TRACE(tab, "Created tab control, hwnd [%04x]\n", hwnd);
1766 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
1767 /* Create tooltip control */
1768 infoPtr->hwndToolTip =
1769 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1770 CW_USEDEFAULT, CW_USEDEFAULT,
1771 CW_USEDEFAULT, CW_USEDEFAULT,
1772 hwnd, 0, 0, 0);
1773
1774 /* Send NM_TOOLTIPSCREATED notification */
1775 if (infoPtr->hwndToolTip) {
1776 NMTOOLTIPSCREATED nmttc;
1777
1778 nmttc.hdr.hwndFrom = hwnd;
1779 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1780 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1781 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1782
1783 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1784 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1785 }
1786 }
1787
1788 /*
1789 * We need to get text information so we need a DC and we need to select
1790 * a font.
1791 */
1792 hdc = GetDC(hwnd);
1793 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1794
1795 /*
1796 * Use the system font to determine the initial height of a tab.
1797 */
1798 GetTextMetricsA(hdc, &fontMetrics);
1799
1800 /*
1801 * Make sure there is enough space for the letters + growing the
1802 * selected item + extra space for the selected item.
1803 */
1804 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1805 SELECTED_TAB_OFFSET;
1806
1807 /*
1808 * Initialize the width of a tab.
1809 */
1810 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1811
1812 SelectObject (hdc, hOldFont);
1813 ReleaseDC(hwnd, hdc);
1814
1815 return 0;
1816}
1817
1818static LRESULT
1819TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1820{
1821 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1822 INT iItem;
1823
1824 if (infoPtr->items) {
1825 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1826 if (infoPtr->items[iItem].pszText)
1827 COMCTL32_Free (infoPtr->items[iItem].pszText);
1828 }
1829 COMCTL32_Free (infoPtr->items);
1830 }
1831
1832 if (infoPtr->hwndToolTip)
1833 DestroyWindow (infoPtr->hwndToolTip);
1834
1835 if (infoPtr->hwndUpDown)
1836 DestroyWindow(infoPtr->hwndUpDown);
1837
1838 COMCTL32_Free (infoPtr);
1839 return 0;
1840}
1841
1842static LRESULT WINAPI
1843TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1844{
1845//if (uMsg == WM_CREATE) MessageBoxA(hwnd,"wm_create!",NULL,MB_OK);//CB:bug!!!
1846//CB: WM_CREATE never send!!!
1847 switch (uMsg)
1848 {
1849 case TCM_GETIMAGELIST:
1850 return TAB_GetImageList (hwnd, wParam, lParam);
1851
1852 case TCM_SETIMAGELIST:
1853 return TAB_SetImageList (hwnd, wParam, lParam);
1854
1855 case TCM_GETITEMCOUNT:
1856 return TAB_GetItemCount (hwnd, wParam, lParam);
1857
1858 case TCM_GETITEMA:
1859 return TAB_GetItemA (hwnd, wParam, lParam);
1860
1861 case TCM_GETITEMW:
1862 return TAB_GetItemW(hwnd,wParam,lParam);
1863
1864 case TCM_SETITEMA:
1865 return TAB_SetItemA (hwnd, wParam, lParam);
1866
1867 case TCM_SETITEMW:
1868 return TAB_SetItemW(hwnd,wParam,lParam);
1869
1870 case TCM_DELETEITEM:
1871 return TAB_DeleteItem (hwnd, wParam, lParam);
1872
1873 case TCM_DELETEALLITEMS:
1874 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1875
1876 case TCM_GETITEMRECT:
1877 return TAB_GetItemRect (hwnd, wParam, lParam);
1878
1879 case TCM_GETCURSEL:
1880 return TAB_GetCurSel (hwnd);
1881
1882 case TCM_HITTEST:
1883 return TAB_HitTest (hwnd, wParam, lParam);
1884
1885 case TCM_SETCURSEL:
1886 return TAB_SetCurSel (hwnd, wParam);
1887
1888 case TCM_INSERTITEMA:
1889 return TAB_InsertItemA(hwnd,wParam,lParam);
1890
1891 case TCM_INSERTITEMW:
1892 return TAB_InsertItemW(hwnd,wParam,lParam);
1893
1894 case TCM_SETITEMEXTRA:
1895// FIXME (tab, "Unimplemented msg TCM_SETITEMEXTRA\n");
1896 return 0;
1897
1898 case TCM_ADJUSTRECT:
1899 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1900
1901 case TCM_SETITEMSIZE:
1902 return TAB_SetItemSize (hwnd, wParam, lParam);
1903
1904 case TCM_REMOVEIMAGE:
1905// FIXME (tab, "Unimplemented msg TCM_REMOVEIMAGE\n");
1906 return 0;
1907
1908 case TCM_SETPADDING:
1909// FIXME (tab, "Unimplemented msg TCM_SETPADDING\n");
1910 return 0;
1911
1912 case TCM_GETROWCOUNT:
1913// FIXME (tab, "Unimplemented msg TCM_GETROWCOUNT\n");
1914 return 0;
1915
1916 case TCM_GETUNICODEFORMAT:
1917// FIXME (tab, "Unimplemented msg TCM_GETUNICODEFORMAT\n");
1918 return 0;
1919
1920 case TCM_SETUNICODEFORMAT:
1921// FIXME (tab, "Unimplemented msg TCM_SETUNICODEFORMAT\n");
1922 return 0;
1923
1924 case TCM_HIGHLIGHTITEM:
1925// FIXME (tab, "Unimplemented msg TCM_HIGHLIGHTITEM\n");
1926 return 0;
1927
1928 case TCM_GETTOOLTIPS:
1929 return TAB_GetToolTips (hwnd, wParam, lParam);
1930
1931 case TCM_SETTOOLTIPS:
1932 return TAB_SetToolTips (hwnd, wParam, lParam);
1933
1934 case TCM_GETCURFOCUS:
1935 return TAB_GetCurFocus (hwnd);
1936
1937 case TCM_SETCURFOCUS:
1938 return TAB_SetCurFocus (hwnd, wParam);
1939
1940 case TCM_SETMINTTABWIDTH:
1941// FIXME (tab, "Unimplemented msg TCM_SETMINTTABWIDTH\n");
1942 return 0;
1943
1944 case TCM_DESELECTALL:
1945// FIXME (tab, "Unimplemented msg TCM_DESELECTALL\n");
1946 return 0;
1947
1948 case TCM_GETEXTENDEDSTYLE:
1949// FIXME (tab, "Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1950 return 0;
1951
1952 case TCM_SETEXTENDEDSTYLE:
1953// FIXME (tab, "Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1954 return 0;
1955
1956 case WM_GETFONT:
1957 return TAB_GetFont (hwnd, wParam, lParam);
1958
1959 case WM_SETFONT:
1960 return TAB_SetFont (hwnd, wParam, lParam);
1961
1962 case WM_CREATE:
1963 return TAB_Create (hwnd, wParam, lParam);
1964
1965 case WM_NCDESTROY:
1966 return TAB_Destroy (hwnd, wParam, lParam);
1967
1968 case WM_GETDLGCODE:
1969 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1970
1971 case WM_LBUTTONDOWN:
1972 return TAB_LButtonDown (hwnd, wParam, lParam);
1973
1974 case WM_LBUTTONUP:
1975 return TAB_LButtonUp (hwnd, wParam, lParam);
1976
1977 case WM_RBUTTONDOWN:
1978 return TAB_RButtonDown (hwnd, wParam, lParam);
1979
1980 case WM_MOUSEMOVE:
1981 return TAB_MouseMove (hwnd, wParam, lParam);
1982
1983 case WM_ERASEBKGND:
1984 return TAB_EraseBackground (hwnd, (HDC)wParam);
1985
1986 case WM_PAINT:
1987 return TAB_Paint (hwnd, wParam);
1988
1989 case WM_SIZE:
1990 return TAB_Size (hwnd, wParam, lParam);
1991
1992 case WM_SETREDRAW:
1993 return TAB_SetRedraw (hwnd, wParam);
1994
1995 case WM_HSCROLL:
1996 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1997
1998 case WM_KILLFOCUS:
1999 case WM_SETFOCUS:
2000 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2001
2002 case WM_KEYUP:
2003 return TAB_KeyUp(hwnd, wParam);
2004
2005 default:
2006// if (uMsg >= WM_USER)
2007// ERR (tab, "unknown msg %04x wp=%08x lp=%08lx\n",
2008// uMsg, wParam, lParam);
2009 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2010 }
2011
2012 return 0;
2013}
2014
2015
2016VOID
2017TAB_Register (VOID)
2018{
2019 WNDCLASSA wndClass;
2020
2021 if (GlobalFindAtomA (WC_TABCONTROLA)) return;
2022
2023 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2024 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
2025 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2026 wndClass.cbClsExtra = 0;
2027 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2028 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2029 wndClass.hbrBackground = (HBRUSH)NULL;
2030 wndClass.lpszClassName = WC_TABCONTROLA;
2031
2032 RegisterClassA (&wndClass);
2033}
2034
2035
2036VOID
2037TAB_Unregister (VOID)
2038{
2039 if (GlobalFindAtomA (WC_TABCONTROLA))
2040 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);
2041}
2042
Note: See TracBrowser for help on using the repository browser.