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

Last change on this file since 1431 was 1431, checked in by sandervl, 26 years ago

RegisterClass even if atom exists

File size: 49.2 KB
Line 
1/* $Id: tab.c,v 1.15 1999-10-24 22:49:47 sandervl 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 //SvL: Bugfix: text is unicode, not ascii
736 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
737 lstrlenW(infoPtr->items[curItem].pszText), &size);
738
739 /*
740 * Add the icon width
741 */
742 if (infoPtr->himl)
743 {
744 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
745 num++;
746 }
747
748 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
749 size.cx + icon_width +
750 num*HORIZONTAL_ITEM_PADDING;
751 }
752
753// TRACE("TextSize: %i\n ", size.cx);
754// TRACE("Rect: T %i, L %i, B %i, R %i\n",
755// infoPtr->items[curItem].rect.top,
756// infoPtr->items[curItem].rect.left,
757// infoPtr->items[curItem].rect.bottom,
758// infoPtr->items[curItem].rect.right);
759
760 /*
761 * The leftmost position of the next item is the rightmost position
762 * of this one.
763 */
764 if (lStyle & TCS_BUTTONS)
765 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
766 else
767 curItemLeftPos = infoPtr->items[curItem].rect.right;
768 }
769
770 /*
771 * Check if we need a scrolling control.
772 */
773 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
774 clientRect.right);
775
776 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
777
778 /*
779 * Cleanup
780 */
781 SelectObject (hdc, hOldFont);
782 ReleaseDC (hwnd, hdc);
783}
784
785/******************************************************************************
786 * TAB_DrawItem
787 *
788 * This method is used to draw a single tab into the tab control.
789 */
790static void TAB_DrawItem(
791 HWND hwnd,
792 HDC hdc,
793 INT iItem)
794{
795 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
796 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
797 RECT itemRect;
798 RECT selectedRect;
799 BOOL isVisible;
800 RECT r;
801
802 /*
803 * Get the rectangle for the item.
804 */
805 isVisible = TAB_InternalGetItemRect(hwnd,
806 infoPtr,
807 iItem,
808 &itemRect,
809 &selectedRect);
810
811 if (isVisible)
812 {
813 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
814 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
815 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
816 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
817 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
818 HPEN holdPen;
819 INT oldBkMode;
820 INT cx,cy;
821
822 if (lStyle & TCS_BUTTONS)
823 {
824 /*
825 * Get item rectangle.
826 */
827 r = itemRect;
828
829 holdPen = SelectObject (hdc, hwPen);
830
831 if (iItem == infoPtr->iSelected)
832 {
833 /*
834 * Background color.
835 */
836 if (!(lStyle & TCS_OWNERDRAWFIXED))
837 {
838 DeleteObject(hbr);
839 hbr = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
840 }
841
842 /*
843 * Erase the background.
844 */
845 FillRect(hdc, &r, hbr);
846
847 /*
848 * Draw the tab now.
849 * The rectangles calculated exclude the right and bottom
850 * borders of the rectangle. To simply the following code, those
851 * borders are shaved-off beforehand.
852 */
853 r.right--;
854 r.bottom--;
855
856 /* highlight */
857 MoveToEx (hdc, r.left, r.bottom, NULL);
858 LineTo (hdc, r.right, r.bottom);
859 LineTo (hdc, r.right, r.top);
860
861 /* shadow */
862 SelectObject(hdc, hbPen);
863 LineTo (hdc, r.left, r.top);
864 LineTo (hdc, r.left, r.bottom);
865 }
866 else
867 {
868 /*
869 * Erase the background.
870 */
871 FillRect(hdc, &r, hbr);
872
873 /* highlight */
874 MoveToEx (hdc, r.left, r.bottom, NULL);
875 LineTo (hdc, r.left, r.top);
876 LineTo (hdc, r.right, r.top);
877
878 /* shadow */
879 SelectObject(hdc, hbPen);
880 LineTo (hdc, r.right, r.bottom);
881 LineTo (hdc, r.left, r.bottom);
882 }
883 }
884 else
885 {
886 /*
887 * Background color.
888 */
889 DeleteObject(hbr);
890 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
891
892 /*
893 * We draw a rectangle of different sizes depending on the selection
894 * state.
895 */
896 if (iItem == infoPtr->iSelected)
897 r = selectedRect;
898 else
899 r = itemRect;
900
901 /*
902 * Erase the background.
903 * This is necessary when drawing the selected item since it is larger
904 * than the others, it might overlap with stuff already drawn by the
905 * other tabs
906 */
907 FillRect(hdc, &r, hbr);
908
909 /*
910 * Draw the tab now.
911 * The rectangles calculated exclude the right and bottom
912 * borders of the rectangle. To simply the following code, those
913 * borders are shaved-off beforehand.
914 */
915 r.right--;
916 r.bottom--;
917
918 holdPen = SelectObject (hdc, hwPen);
919
920 if (lStyle & TCS_BOTTOM)
921 {
922 /* highlight */
923 MoveToEx (hdc, r.left, r.top, NULL);
924 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
925 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
926
927 /* shadow */
928 SelectObject(hdc, hbPen);
929 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
930 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
931 LineTo (hdc, r.right, r.top);
932 }
933 else
934 {
935 /* highlight */
936 MoveToEx (hdc, r.left, r.bottom, NULL);
937 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
938 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
939 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
940
941 /* shadow */
942 SelectObject(hdc, hbPen);
943 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
944 LineTo (hdc, r.right, r.bottom);
945 }
946 }
947
948 /*
949 * Text pen
950 */
951 SelectObject(hdc, hsdPen);
952
953 oldBkMode = SetBkMode(hdc, TRANSPARENT);
954 SetTextColor (hdc, COLOR_BTNTEXT);
955
956 /*
957 * Deflate the rectangle to acount for the padding
958 */
959 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
960
961 /*
962 * Draw the icon.
963 */
964 if (infoPtr->himl)
965 {
966 ImageList_Draw (infoPtr->himl, iItem, hdc,
967 r.left, r.top+1, ILD_NORMAL);
968 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
969 r.left+=(cx + HORIZONTAL_ITEM_PADDING);
970 }
971
972 /*
973 * Draw the text;
974 */
975 DrawTextW(hdc,
976 infoPtr->items[iItem].pszText,
977 lstrlenW(infoPtr->items[iItem].pszText),
978 &r,
979 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
980
981 /*
982 * Draw the focus rectangle
983 */
984 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
985 (GetFocus() == hwnd) &&
986 (iItem == infoPtr->uFocus) )
987 {
988 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
989
990 SelectObject(hdc, hfocusPen);
991
992 MoveToEx (hdc, r.left, r.top, NULL);
993 LineTo (hdc, r.right-1, r.top);
994 LineTo (hdc, r.right-1, r.bottom -1);
995 LineTo (hdc, r.left, r.bottom -1);
996 LineTo (hdc, r.left, r.top);
997 }
998
999 /*
1000 * Cleanup
1001 */
1002 SetBkMode(hdc, oldBkMode);
1003 SelectObject(hdc, holdPen);
1004 DeleteObject(hfocusPen);
1005 DeleteObject(hbr);
1006 }
1007}
1008
1009/******************************************************************************
1010 * TAB_DrawBorder
1011 *
1012 * This method is used to draw the raised border around the tab control
1013 * "content" area.
1014 */
1015static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1016{
1017 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1018 HPEN htmPen;
1019 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1020 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1021 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1022 RECT rect;
1023
1024 GetClientRect (hwnd, &rect);
1025
1026 /*
1027 * Adjust for the style
1028 */
1029 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1030 {
1031 rect.bottom -= infoPtr->tabHeight;
1032 }
1033 else
1034 {
1035 rect.top += infoPtr->tabHeight;
1036 }
1037
1038 /*
1039 * Shave-off the right and bottom margins (exluded in the
1040 * rect)
1041 */
1042 rect.right--;
1043 rect.bottom--;
1044
1045 /* highlight */
1046 htmPen = SelectObject (hdc, hwPen);
1047
1048 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1049 LineTo (hdc, rect.left, rect.top);
1050 LineTo (hdc, rect.right, rect.top);
1051
1052 /* Dark Shadow */
1053 SelectObject (hdc, hbPen);
1054 LineTo (hdc, rect.right, rect.bottom );
1055 LineTo (hdc, rect.left, rect.bottom);
1056
1057 /* shade */
1058 SelectObject (hdc, hShade );
1059 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1060 LineTo (hdc, rect.right-1, rect.bottom-1);
1061 LineTo (hdc, rect.left, rect.bottom-1);
1062
1063 SelectObject(hdc, htmPen);
1064}
1065
1066/******************************************************************************
1067 * TAB_Refresh
1068 *
1069 * This method repaints the tab control..
1070 */
1071static void TAB_Refresh (HWND hwnd, HDC hdc)
1072{
1073 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1074 HFONT hOldFont;
1075 INT i;
1076
1077 if (!infoPtr->DoRedraw)
1078 return;
1079
1080 hOldFont = SelectObject (hdc, infoPtr->hFont);
1081
1082 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1083 {
1084 for (i = 0; i < infoPtr->uNumItem; i++)
1085 {
1086 TAB_DrawItem (hwnd, hdc, i);
1087 }
1088 }
1089 else
1090 {
1091 /*
1092 * Draw all the non selected item first.
1093 */
1094 for (i = 0; i < infoPtr->uNumItem; i++)
1095 {
1096 if (i != infoPtr->iSelected)
1097 TAB_DrawItem (hwnd, hdc, i);
1098 }
1099
1100 /*
1101 * Now, draw the border, draw it before the selected item
1102 * since the selected item overwrites part of the border.
1103 */
1104 TAB_DrawBorder (hwnd, hdc);
1105
1106 /*
1107 * Then, draw the selected item
1108 */
1109 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1110 }
1111
1112 SelectObject (hdc, hOldFont);
1113}
1114
1115static LRESULT
1116TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1117{
1118 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1119
1120 infoPtr->DoRedraw=(BOOL) wParam;
1121 return 0;
1122}
1123
1124static LRESULT TAB_EraseBackground(
1125 HWND hwnd,
1126 HDC givenDC)
1127{
1128 HDC hdc;
1129 RECT clientRect;
1130
1131 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1132
1133 hdc = givenDC ? givenDC : GetDC(hwnd);
1134
1135 GetClientRect(hwnd, &clientRect);
1136
1137 FillRect(hdc, &clientRect, brush);
1138
1139 if (givenDC==0)
1140 ReleaseDC(hwnd, hdc);
1141
1142 DeleteObject(brush);
1143
1144 return 0;
1145}
1146
1147/******************************************************************************
1148 * TAB_EnsureSelectionVisible
1149 *
1150 * This method will make sure that the current selection is completely
1151 * visible by scrolling until it is.
1152 */
1153static void TAB_EnsureSelectionVisible(
1154 HWND hwnd,
1155 TAB_INFO* infoPtr)
1156{
1157 RECT selectedRect;
1158 RECT visibleRect;
1159 RECT scrollerRect;
1160 BOOL isVisible;
1161
1162 /*
1163 * Do the trivial cases first.
1164 */
1165 if ( (!infoPtr->needsScrolling) ||
1166 (infoPtr->hwndUpDown==0) )
1167 return;
1168
1169 if (infoPtr->leftmostVisible > infoPtr->iSelected)
1170 {
1171 infoPtr->leftmostVisible = infoPtr->iSelected;
1172 return;
1173 }
1174
1175 /*
1176 * Calculate the part of the client area that is visible.
1177 */
1178 GetClientRect(hwnd, &visibleRect);
1179 GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
1180 visibleRect.right -= scrollerRect.right;
1181
1182 /*
1183 * Get the rectangle for the item
1184 */
1185 isVisible = TAB_InternalGetItemRect(hwnd,
1186 infoPtr,
1187 infoPtr->iSelected,
1188 NULL,
1189 &selectedRect);
1190
1191 /*
1192 * If this function can't say it's completely invisible, maybe it
1193 * is partially visible. Let's check.
1194 */
1195 if (isVisible)
1196 {
1197 POINT pt1;
1198 POINT pt2;
1199
1200 pt1.x = selectedRect.left;
1201 pt1.y = selectedRect.top;
1202 pt2.x = selectedRect.right - 1;
1203 pt2.y = selectedRect.bottom - 1;
1204
1205 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1206 }
1207
1208 while ( (infoPtr->leftmostVisible < infoPtr->iSelected) &&
1209 !isVisible)
1210 {
1211 infoPtr->leftmostVisible++;
1212
1213 /*
1214 * Get the rectangle for the item
1215 */
1216 isVisible = TAB_InternalGetItemRect(hwnd,
1217 infoPtr,
1218 infoPtr->iSelected,
1219 NULL,
1220 &selectedRect);
1221
1222 /*
1223 * If this function can't say it's completely invisible, maybe it
1224 * is partially visible. Let's check.
1225 */
1226 if (isVisible)
1227 {
1228 POINT pt1;
1229 POINT pt2;
1230
1231 pt1.x = selectedRect.left;
1232 pt1.y = selectedRect.top;
1233 pt2.x = selectedRect.right - 1;
1234 pt2.y = selectedRect.bottom - 1;
1235
1236 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1237 }
1238 }
1239}
1240
1241/******************************************************************************
1242 * TAB_InvalidateTabArea
1243 *
1244 * This method will invalidate the portion of the control that contains the
1245 * tabs. It is called when the state of the control changes and needs
1246 * to be redisplayed
1247 */
1248static void TAB_InvalidateTabArea(
1249 HWND hwnd,
1250 TAB_INFO* infoPtr)
1251{
1252 RECT clientRect;
1253
1254 GetClientRect(hwnd, &clientRect);
1255
1256 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1257 {
1258 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
1259 }
1260 else
1261 {
1262 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1263 }
1264
1265 InvalidateRect(hwnd, &clientRect, TRUE);
1266}
1267
1268static LRESULT
1269TAB_Paint (HWND hwnd, WPARAM wParam)
1270{
1271 HDC hdc;
1272 PAINTSTRUCT ps;
1273
1274 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1275 TAB_Refresh (hwnd, hdc);
1276
1277 if(!wParam)
1278 EndPaint (hwnd, &ps);
1279
1280 return 0;
1281}
1282
1283static LRESULT TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1284{
1285 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1286 TCITEMA *pti;
1287 INT iItem, len;
1288 RECT rect;
1289
1290 GetClientRect (hwnd, &rect);
1291// TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1292// rect.top, rect.left, rect.bottom, rect.right);
1293
1294 pti = (TCITEMA *)lParam;
1295 iItem = (INT)wParam;
1296
1297 if (iItem < 0) return -1;
1298 if (iItem > infoPtr->uNumItem)
1299 iItem = infoPtr->uNumItem;
1300
1301 if (infoPtr->uNumItem == 0) {
1302 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1303 infoPtr->uNumItem++;
1304 infoPtr->iSelected = 0;
1305 }
1306 else {
1307 TAB_ITEM *oldItems = infoPtr->items;
1308
1309 infoPtr->uNumItem++;
1310 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1311
1312 /* pre insert copy */
1313 if (iItem > 0) {
1314 memcpy (&infoPtr->items[0], &oldItems[0],
1315 iItem * sizeof(TAB_ITEM));
1316 }
1317
1318 /* post insert copy */
1319 if (iItem < infoPtr->uNumItem - 1) {
1320 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1321 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1322
1323 }
1324
1325 if (iItem <= infoPtr->iSelected)
1326 infoPtr->iSelected++;
1327
1328 COMCTL32_Free (oldItems);
1329 }
1330
1331 infoPtr->items[iItem].mask = pti->mask;
1332 if (pti->mask & TCIF_TEXT) {
1333 len = lstrlenA (pti->pszText);
1334 infoPtr->items[iItem].pszText = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1335 lstrcpyAtoW (infoPtr->items[iItem].pszText, pti->pszText);
1336 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1337 }
1338
1339 if (pti->mask & TCIF_IMAGE)
1340 infoPtr->items[iItem].iImage = pti->iImage;
1341
1342 if (pti->mask & TCIF_PARAM)
1343 infoPtr->items[iItem].lParam = pti->lParam;
1344
1345 TAB_InvalidateTabArea(hwnd, infoPtr);
1346
1347// TRACE(tab, "[%04x]: added item %d '%s'\n",
1348// hwnd, iItem, infoPtr->items[iItem].pszText);
1349
1350 TAB_SetItemBounds(hwnd);
1351 return iItem;
1352}
1353
1354static LRESULT TAB_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1355{
1356 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1357 TCITEMW *pti;
1358 INT iItem, len;
1359 RECT rect;
1360
1361 GetClientRect (hwnd, &rect);
1362// TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1363// rect.top, rect.left, rect.bottom, rect.right);
1364
1365 pti = (TCITEMW*)lParam;
1366 iItem = (INT)wParam;
1367
1368 if (iItem < 0) return -1;
1369 if (iItem > infoPtr->uNumItem)
1370 iItem = infoPtr->uNumItem;
1371
1372 if (infoPtr->uNumItem == 0) {
1373 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1374 infoPtr->uNumItem++;
1375 }
1376 else {
1377 TAB_ITEM *oldItems = infoPtr->items;
1378
1379 infoPtr->uNumItem++;
1380 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1381
1382 /* pre insert copy */
1383 if (iItem > 0) {
1384 memcpy (&infoPtr->items[0], &oldItems[0],
1385 iItem * sizeof(TAB_ITEM));
1386 }
1387
1388 /* post insert copy */
1389 if (iItem < infoPtr->uNumItem - 1) {
1390 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1391 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1392
1393 }
1394
1395 COMCTL32_Free (oldItems);
1396 }
1397
1398 infoPtr->items[iItem].mask = pti->mask;
1399 if (pti->mask & TCIF_TEXT) {
1400 len = lstrlenW (pti->pszText);
1401 infoPtr->items[iItem].pszText = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1402 lstrcpyW (infoPtr->items[iItem].pszText, pti->pszText);
1403 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1404 }
1405
1406 if (pti->mask & TCIF_IMAGE)
1407 infoPtr->items[iItem].iImage = pti->iImage;
1408
1409 if (pti->mask & TCIF_PARAM)
1410 infoPtr->items[iItem].lParam = pti->lParam;
1411
1412 TAB_InvalidateTabArea(hwnd, infoPtr);
1413
1414// TRACE(tab, "[%04x]: added item %d '%s'\n",
1415// hwnd, iItem, infoPtr->items[iItem].pszText);
1416
1417 TAB_SetItemBounds(hwnd);
1418 return iItem;
1419}
1420
1421static LRESULT
1422TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1423{
1424 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1425 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1426 LONG lResult = 0;
1427
1428 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1429 {
1430 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1431 infoPtr->tabWidth = (INT)LOWORD(lParam);
1432 infoPtr->tabHeight = (INT)HIWORD(lParam);
1433 }
1434
1435 return lResult;
1436}
1437
1438static LRESULT
1439TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1440{
1441 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1442 TCITEMA *tabItem;
1443 TAB_ITEM *wineItem;
1444 INT iItem,len;
1445
1446 iItem=(INT) wParam;
1447 tabItem=(LPTCITEMA ) lParam;
1448// TRACE("%d %p\n",iItem, tabItem);
1449 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1450
1451 wineItem=& infoPtr->items[iItem];
1452
1453 if (tabItem->mask & TCIF_IMAGE)
1454 wineItem->iImage=tabItem->iImage;
1455
1456 if (tabItem->mask & TCIF_PARAM)
1457 wineItem->lParam=tabItem->lParam;
1458
1459// if (tabItem->mask & TCIF_RTLREADING)
1460// FIXME("TCIF_RTLREADING\n");
1461
1462 if (tabItem->mask & TCIF_STATE)
1463 wineItem->dwState=tabItem->dwState;
1464
1465 if (tabItem->mask & TCIF_TEXT) {
1466 len=lstrlenA (tabItem->pszText);
1467 if (len>wineItem->cchTextMax)
1468 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1469 lstrcpyA (wineItem->pszText, tabItem->pszText);
1470 }
1471
1472 /*
1473 * Update and repaint tabs.
1474 */
1475 TAB_SetItemBounds(hwnd);
1476 TAB_InvalidateTabArea(hwnd,infoPtr);
1477
1478 return TRUE;
1479}
1480
1481static LRESULT TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1482{
1483 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1484 TCITEMW *tabItem;
1485 TAB_ITEM *wineItem;
1486 INT iItem,len;
1487
1488 iItem=(INT) wParam;
1489 tabItem=(LPTCITEMW) lParam;
1490// TRACE (tab,"%d %p\n",iItem, tabItem);
1491 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1492
1493 wineItem=& infoPtr->items[iItem];
1494
1495 if (tabItem->mask & TCIF_IMAGE)
1496 wineItem->iImage=tabItem->iImage;
1497
1498 if (tabItem->mask & TCIF_PARAM)
1499 wineItem->lParam=tabItem->lParam;
1500
1501// if (tabItem->mask & TCIF_RTLREADING)
1502// FIXME (tab,"TCIF_RTLREADING\n");
1503
1504 if (tabItem->mask & TCIF_STATE)
1505 wineItem->dwState=tabItem->dwState;
1506
1507 if (tabItem->mask & TCIF_TEXT) {
1508 len = lstrlenW (tabItem->pszText);
1509 if (len>wineItem->cchTextMax)
1510 wineItem->pszText = COMCTL32_ReAlloc (wineItem->pszText, (len+1)*sizeof(WCHAR));
1511 lstrcpyW (wineItem->pszText, tabItem->pszText);
1512 }
1513
1514 return TRUE;
1515}
1516
1517static LRESULT
1518TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1519{
1520 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1521
1522 return infoPtr->uNumItem;
1523}
1524
1525
1526static LRESULT
1527TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1528{
1529 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1530 TCITEMA *tabItem;
1531 TAB_ITEM *wineItem;
1532 INT iItem;
1533
1534 iItem=(INT) wParam;
1535 tabItem=(LPTCITEMA) lParam;
1536// TRACE (tab,"\n");
1537 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1538
1539 wineItem=& infoPtr->items[iItem];
1540
1541 if (tabItem->mask & TCIF_IMAGE)
1542 tabItem->iImage=wineItem->iImage;
1543
1544 if (tabItem->mask & TCIF_PARAM)
1545 tabItem->lParam=wineItem->lParam;
1546
1547// if (tabItem->mask & TCIF_RTLREADING)
1548// FIXME (tab, "TCIF_RTLREADING\n");
1549
1550 if (tabItem->mask & TCIF_STATE)
1551 tabItem->dwState=wineItem->dwState;
1552
1553 if (tabItem->mask & TCIF_TEXT)
1554 lstrcpynWtoA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1555
1556 return TRUE;
1557}
1558
1559static LRESULT TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1560{
1561 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1562 TCITEMW *tabItem;
1563 TAB_ITEM *wineItem;
1564 INT iItem;
1565
1566 iItem=(INT) wParam;
1567 tabItem=(LPTCITEMW) lParam;
1568// TRACE (tab,"\n");
1569 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1570
1571 wineItem=& infoPtr->items[iItem];
1572
1573 if (tabItem->mask & TCIF_IMAGE)
1574 tabItem->iImage=wineItem->iImage;
1575
1576 if (tabItem->mask & TCIF_PARAM)
1577 tabItem->lParam=wineItem->lParam;
1578
1579// if (tabItem->mask & TCIF_RTLREADING)
1580// FIXME (tab, "TCIF_RTLREADING\n");
1581
1582 if (tabItem->mask & TCIF_STATE)
1583 tabItem->dwState=wineItem->dwState;
1584
1585 if (tabItem->mask & TCIF_TEXT)
1586 lstrcpynW (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1587
1588 return TRUE;
1589}
1590
1591static LRESULT
1592TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1593{
1594 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1595 INT iItem = (INT) wParam;
1596 BOOL bResult = FALSE;
1597
1598 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1599 {
1600 TAB_ITEM *oldItems = infoPtr->items;
1601
1602 infoPtr->uNumItem--;
1603 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1604
1605 if (iItem > 0)
1606 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1607
1608 if (iItem < infoPtr->uNumItem)
1609 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1610 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1611
1612 COMCTL32_Free (oldItems);
1613
1614 /*
1615 * Readjust the selected index.
1616 */
1617 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1618 infoPtr->iSelected--;
1619
1620 if (iItem < infoPtr->iSelected)
1621 infoPtr->iSelected--;
1622
1623 if (infoPtr->uNumItem == 0)
1624 infoPtr->iSelected = -1;
1625
1626 /*
1627 * Reposition and repaint tabs.
1628 */
1629 TAB_SetItemBounds(hwnd);
1630 TAB_InvalidateTabArea(hwnd,infoPtr);
1631
1632 bResult = TRUE;
1633 }
1634
1635 return bResult;
1636}
1637
1638static LRESULT
1639TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1640{
1641 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1642
1643 COMCTL32_Free (infoPtr->items);
1644 infoPtr->uNumItem = 0;
1645 infoPtr->iSelected = -1;
1646
1647 return TRUE;
1648}
1649
1650
1651static LRESULT
1652TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1653{
1654 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1655
1656// TRACE (tab,"\n");
1657 return (LRESULT)infoPtr->hFont;
1658}
1659
1660static LRESULT
1661TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1662
1663{
1664 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1665
1666// TRACE (tab,"%x %lx\n",wParam, lParam);
1667
1668 infoPtr->hFont = (HFONT)wParam;
1669
1670 TAB_SetItemBounds(hwnd);
1671
1672 TAB_InvalidateTabArea(hwnd, infoPtr);
1673
1674 return 0;
1675}
1676
1677
1678static LRESULT
1679TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1680{
1681 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1682
1683// TRACE (tab,"\n");
1684 return (LRESULT)infoPtr->himl;
1685}
1686
1687static LRESULT
1688TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1689{
1690 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1691 HIMAGELIST himlPrev;
1692
1693// TRACE (tab,"\n");
1694 himlPrev = infoPtr->himl;
1695 infoPtr->himl= (HIMAGELIST)lParam;
1696 return (LRESULT)himlPrev;
1697}
1698
1699
1700static LRESULT
1701TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1702
1703{
1704/* I'm not really sure what the following code was meant to do.
1705 This is what it is doing:
1706 When WM_SIZE is sent with SIZE_RESTORED, the control
1707 gets positioned in the top left corner.
1708
1709 RECT parent_rect;
1710 HWND parent;
1711 UINT uPosFlags,cx,cy;
1712
1713 uPosFlags=0;
1714 if (!wParam) {
1715 parent = GetParent (hwnd);
1716 GetClientRect(parent, &parent_rect);
1717 cx=LOWORD (lParam);
1718 cy=HIWORD (lParam);
1719 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1720 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1721
1722 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1723 cx, cy, uPosFlags | SWP_NOZORDER);
1724 } else {
1725// FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1726 } */
1727
1728 /*
1729 * Recompute the size/position of the tabs.
1730 */
1731 TAB_SetItemBounds (hwnd);
1732
1733 /*
1734 * Force a repaint of the control.
1735 */
1736 InvalidateRect(hwnd, NULL, TRUE);
1737
1738 return 0;
1739}
1740
1741
1742static LRESULT
1743TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1744{
1745 TAB_INFO *infoPtr;
1746 TEXTMETRICA fontMetrics;
1747 HDC hdc;
1748 HFONT hOldFont;
1749
1750 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1751
1752 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1753
1754 infoPtr->uNumItem = 0;
1755 infoPtr->hFont = 0;
1756 infoPtr->items = 0;
1757 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1758 infoPtr->iSelected = -1;
1759 infoPtr->uFocus = 0;
1760 infoPtr->hwndToolTip = 0;
1761 infoPtr->DoRedraw = TRUE;
1762 infoPtr->needsScrolling = FALSE;
1763 infoPtr->hwndUpDown = 0;
1764 infoPtr->leftmostVisible = 0;
1765
1766// TRACE(tab, "Created tab control, hwnd [%04x]\n", hwnd);
1767 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
1768 /* Create tooltip control */
1769 infoPtr->hwndToolTip =
1770 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1771 CW_USEDEFAULT, CW_USEDEFAULT,
1772 CW_USEDEFAULT, CW_USEDEFAULT,
1773 hwnd, 0, 0, 0);
1774
1775 /* Send NM_TOOLTIPSCREATED notification */
1776 if (infoPtr->hwndToolTip) {
1777 NMTOOLTIPSCREATED nmttc;
1778
1779 nmttc.hdr.hwndFrom = hwnd;
1780 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1781 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1782 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1783
1784 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1785 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1786 }
1787 }
1788
1789 /*
1790 * We need to get text information so we need a DC and we need to select
1791 * a font.
1792 */
1793 hdc = GetDC(hwnd);
1794 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1795
1796 /*
1797 * Use the system font to determine the initial height of a tab.
1798 */
1799 GetTextMetricsA(hdc, &fontMetrics);
1800
1801 /*
1802 * Make sure there is enough space for the letters + growing the
1803 * selected item + extra space for the selected item.
1804 */
1805 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1806 SELECTED_TAB_OFFSET;
1807
1808 /*
1809 * Initialize the width of a tab.
1810 */
1811 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1812
1813 SelectObject (hdc, hOldFont);
1814 ReleaseDC(hwnd, hdc);
1815
1816 return 0;
1817}
1818
1819static LRESULT
1820TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1821{
1822 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1823 INT iItem;
1824
1825 if (infoPtr->items) {
1826 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1827 if (infoPtr->items[iItem].pszText)
1828 COMCTL32_Free (infoPtr->items[iItem].pszText);
1829 }
1830 COMCTL32_Free (infoPtr->items);
1831 }
1832
1833 if (infoPtr->hwndToolTip)
1834 DestroyWindow (infoPtr->hwndToolTip);
1835
1836 if (infoPtr->hwndUpDown)
1837 DestroyWindow(infoPtr->hwndUpDown);
1838
1839 COMCTL32_Free (infoPtr);
1840 return 0;
1841}
1842
1843static LRESULT WINAPI
1844TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1845{
1846//if (uMsg == WM_CREATE) MessageBoxA(hwnd,"wm_create!",NULL,MB_OK);//CB:bug!!!
1847//CB: WM_CREATE never send!!!
1848 switch (uMsg)
1849 {
1850 case TCM_GETIMAGELIST:
1851 return TAB_GetImageList (hwnd, wParam, lParam);
1852
1853 case TCM_SETIMAGELIST:
1854 return TAB_SetImageList (hwnd, wParam, lParam);
1855
1856 case TCM_GETITEMCOUNT:
1857 return TAB_GetItemCount (hwnd, wParam, lParam);
1858
1859 case TCM_GETITEMA:
1860 return TAB_GetItemA (hwnd, wParam, lParam);
1861
1862 case TCM_GETITEMW:
1863 return TAB_GetItemW(hwnd,wParam,lParam);
1864
1865 case TCM_SETITEMA:
1866 return TAB_SetItemA (hwnd, wParam, lParam);
1867
1868 case TCM_SETITEMW:
1869 return TAB_SetItemW(hwnd,wParam,lParam);
1870
1871 case TCM_DELETEITEM:
1872 return TAB_DeleteItem (hwnd, wParam, lParam);
1873
1874 case TCM_DELETEALLITEMS:
1875 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1876
1877 case TCM_GETITEMRECT:
1878 return TAB_GetItemRect (hwnd, wParam, lParam);
1879
1880 case TCM_GETCURSEL:
1881 return TAB_GetCurSel (hwnd);
1882
1883 case TCM_HITTEST:
1884 return TAB_HitTest (hwnd, wParam, lParam);
1885
1886 case TCM_SETCURSEL:
1887 return TAB_SetCurSel (hwnd, wParam);
1888
1889 case TCM_INSERTITEMA:
1890 return TAB_InsertItemA(hwnd,wParam,lParam);
1891
1892 case TCM_INSERTITEMW:
1893 return TAB_InsertItemW(hwnd,wParam,lParam);
1894
1895 case TCM_SETITEMEXTRA:
1896// FIXME (tab, "Unimplemented msg TCM_SETITEMEXTRA\n");
1897 return 0;
1898
1899 case TCM_ADJUSTRECT:
1900 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1901
1902 case TCM_SETITEMSIZE:
1903 return TAB_SetItemSize (hwnd, wParam, lParam);
1904
1905 case TCM_REMOVEIMAGE:
1906// FIXME (tab, "Unimplemented msg TCM_REMOVEIMAGE\n");
1907 return 0;
1908
1909 case TCM_SETPADDING:
1910// FIXME (tab, "Unimplemented msg TCM_SETPADDING\n");
1911 return 0;
1912
1913 case TCM_GETROWCOUNT:
1914// FIXME (tab, "Unimplemented msg TCM_GETROWCOUNT\n");
1915 return 0;
1916
1917 case TCM_GETUNICODEFORMAT:
1918// FIXME (tab, "Unimplemented msg TCM_GETUNICODEFORMAT\n");
1919 return 0;
1920
1921 case TCM_SETUNICODEFORMAT:
1922// FIXME (tab, "Unimplemented msg TCM_SETUNICODEFORMAT\n");
1923 return 0;
1924
1925 case TCM_HIGHLIGHTITEM:
1926// FIXME (tab, "Unimplemented msg TCM_HIGHLIGHTITEM\n");
1927 return 0;
1928
1929 case TCM_GETTOOLTIPS:
1930 return TAB_GetToolTips (hwnd, wParam, lParam);
1931
1932 case TCM_SETTOOLTIPS:
1933 return TAB_SetToolTips (hwnd, wParam, lParam);
1934
1935 case TCM_GETCURFOCUS:
1936 return TAB_GetCurFocus (hwnd);
1937
1938 case TCM_SETCURFOCUS:
1939 return TAB_SetCurFocus (hwnd, wParam);
1940
1941 case TCM_SETMINTTABWIDTH:
1942// FIXME (tab, "Unimplemented msg TCM_SETMINTTABWIDTH\n");
1943 return 0;
1944
1945 case TCM_DESELECTALL:
1946// FIXME (tab, "Unimplemented msg TCM_DESELECTALL\n");
1947 return 0;
1948
1949 case TCM_GETEXTENDEDSTYLE:
1950// FIXME (tab, "Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1951 return 0;
1952
1953 case TCM_SETEXTENDEDSTYLE:
1954// FIXME (tab, "Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1955 return 0;
1956
1957 case WM_GETFONT:
1958 return TAB_GetFont (hwnd, wParam, lParam);
1959
1960 case WM_SETFONT:
1961 return TAB_SetFont (hwnd, wParam, lParam);
1962
1963 case WM_CREATE:
1964 return TAB_Create (hwnd, wParam, lParam);
1965
1966 case WM_NCDESTROY:
1967 return TAB_Destroy (hwnd, wParam, lParam);
1968
1969 case WM_GETDLGCODE:
1970 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1971
1972 case WM_LBUTTONDOWN:
1973 return TAB_LButtonDown (hwnd, wParam, lParam);
1974
1975 case WM_LBUTTONUP:
1976 return TAB_LButtonUp (hwnd, wParam, lParam);
1977
1978 case WM_RBUTTONDOWN:
1979 return TAB_RButtonDown (hwnd, wParam, lParam);
1980
1981 case WM_MOUSEMOVE:
1982 return TAB_MouseMove (hwnd, wParam, lParam);
1983
1984 case WM_ERASEBKGND:
1985 return TAB_EraseBackground (hwnd, (HDC)wParam);
1986
1987 case WM_PAINT:
1988 return TAB_Paint (hwnd, wParam);
1989
1990 case WM_SIZE:
1991 return TAB_Size (hwnd, wParam, lParam);
1992
1993 case WM_SETREDRAW:
1994 return TAB_SetRedraw (hwnd, wParam);
1995
1996 case WM_HSCROLL:
1997 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1998
1999 case WM_KILLFOCUS:
2000 case WM_SETFOCUS:
2001 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2002
2003 case WM_KEYUP:
2004 return TAB_KeyUp(hwnd, wParam);
2005
2006 default:
2007// if (uMsg >= WM_USER)
2008// ERR (tab, "unknown msg %04x wp=%08x lp=%08lx\n",
2009// uMsg, wParam, lParam);
2010 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2011 }
2012
2013 return 0;
2014}
2015
2016
2017VOID
2018TAB_Register (VOID)
2019{
2020 WNDCLASSA wndClass;
2021
2022//SvL: Don't check this now
2023// if (GlobalFindAtomA (WC_TABCONTROLA)) return;
2024
2025 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2026 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
2027 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2028 wndClass.cbClsExtra = 0;
2029 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2030 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2031 wndClass.hbrBackground = (HBRUSH)NULL;
2032 wndClass.lpszClassName = WC_TABCONTROLA;
2033
2034 RegisterClassA (&wndClass);
2035}
2036
2037
2038VOID
2039TAB_Unregister (VOID)
2040{
2041 if (GlobalFindAtomA (WC_TABCONTROLA))
2042 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);
2043}
2044
Note: See TracBrowser for help on using the repository browser.