source: trunk/src/comctl32/tab.cpp@ 3154

Last change on this file since 3154 was 3154, checked in by cbratschi, 25 years ago

Corel 20000317 merge, ccbase finished, bug fixes

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