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

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

wine-990731 update

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