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

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

merged with Corel WINE 20000513, added new DPA_* functions

File size: 63.0 KB
Line 
1/* $Id: tab.cpp,v 1.4 2000-05-22 17:25:11 cbratschi Exp $ */
2/*
3 * Tab control
4 *
5 * Copyright 1998 Anders Carlsson
6 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
7 * Copyright 1999 Francis Beaudet
8 * Copyright 1999 Achim Hasenmueller
9 * Copyright 1999 Christoph Bratschi
10 *
11 * TODO:
12 * Image list support
13 * Multiline support
14 */
15
16/* inconsistent: report! */
17/*
18 - Corel WINE 20000513 level
19 - (WINE 991212 level)
20*/
21
22#include <string.h>
23
24#include "winbase.h"
25#include "commctrl.h"
26#include "ccbase.h"
27#include "tab.h"
28#include "comctl32.h"
29
30
31/******************************************************************************
32 * Positioning constants
33 */
34#define SELECTED_TAB_OFFSET 2
35#define HORIZONTAL_ITEM_PADDING 5
36#define VERTICAL_ITEM_PADDING 3
37#define ROUND_CORNER_SIZE 2
38#define FOCUS_RECT_HOFFSET 2
39#define FOCUS_RECT_VOFFSET 1
40#define DISPLAY_AREA_PADDINGX 5
41#define DISPLAY_AREA_PADDINGY 5
42#define CONTROL_BORDER_SIZEX 2
43#define CONTROL_BORDER_SIZEY 2
44#define BUTTON_SPACINGX 10
45#define DEFAULT_TAB_WIDTH 96
46
47#define TAB_GetInfoPtr(hwnd) ((TAB_INFO*)getInfoPtr(hwnd))
48
49/******************************************************************************
50 * Hot-tracking timer constants
51 */
52#define TAB_HOTTRACK_TIMER 1
53#define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
54
55/******************************************************************************
56 * Prototypes
57 */
58static void TAB_Refresh (HWND hwnd, HDC hdc);
59static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
60static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
61static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
62static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
63
64static VOID
65TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
66 WPARAM wParam, LPARAM lParam)
67{
68 MSG msg;
69
70 msg.hwnd = hwndMsg;
71 msg.message = uMsg;
72 msg.wParam = wParam;
73 msg.lParam = lParam;
74 msg.time = GetMessageTime ();
75 msg.pt.x = LOWORD(GetMessagePos ());
76 msg.pt.y = HIWORD(GetMessagePos ());
77
78 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
79}
80
81static LRESULT
82TAB_GetCurSel (HWND hwnd)
83{
84 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
85
86 return infoPtr->iSelected;
87}
88
89static LRESULT
90TAB_GetCurFocus (HWND hwnd)
91{
92 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
93
94 return infoPtr->uFocus;
95}
96
97static LRESULT
98TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
99{
100 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
101
102 if (infoPtr == NULL) return 0;
103 return infoPtr->hwndToolTip;
104}
105
106
107static LRESULT
108TAB_SetCurSel (HWND hwnd,WPARAM wParam)
109{
110 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
111 INT iItem=(INT) wParam;
112 INT prevItem;
113
114 prevItem=-1;
115 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
116 prevItem=infoPtr->iSelected;
117 infoPtr->iSelected=iItem;
118 TAB_EnsureSelectionVisible(hwnd, infoPtr);
119 TAB_InvalidateTabArea(hwnd, infoPtr);
120 }
121 return prevItem;
122}
123
124static LRESULT
125TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
126{
127 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
128 INT iItem=(INT) wParam;
129
130 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
131
132 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
133 {
134 //FIXME (tab,"Should set input focus\n");
135 } else
136 {
137 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1)
138 {
139 infoPtr->uFocus = iItem;
140 if (sendNotify(hwnd,TCN_SELCHANGING) != TRUE)
141 {
142 infoPtr->iSelected = iItem;
143 sendNotify(hwnd,TCN_SELCHANGE);
144
145 TAB_EnsureSelectionVisible(hwnd, infoPtr);
146 TAB_InvalidateTabArea(hwnd, infoPtr);
147 }
148 }
149 }
150 return 0;
151}
152
153static LRESULT
154TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
155{
156 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
157
158 if (infoPtr == NULL) return 0;
159 infoPtr->hwndToolTip = (HWND)wParam;
160 return 0;
161}
162
163/******************************************************************************
164 * TAB_InternalGetItemRect
165 *
166 * This method will calculate the rectangle representing a given tab item in
167 * client coordinates. This method takes scrolling into account.
168 *
169 * This method returns TRUE if the item is visible in the window and FALSE
170 * if it is completely outside the client area.
171 */
172static BOOL TAB_InternalGetItemRect(
173 HWND hwnd,
174 TAB_INFO* infoPtr,
175 INT itemIndex,
176 RECT* itemRect,
177 RECT* selectedRect)
178{
179 RECT tmpItemRect,clientRect;
180 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
181
182 /*
183 * Perform a sanity check and a trivial visibility check.
184 */
185 if ( (infoPtr->uNumItem <= 0) ||
186 (itemIndex >= infoPtr->uNumItem) ||
187 (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) )
188 return FALSE;
189
190 /*
191 * Avoid special cases in this procedure by assigning the "out"
192 * parameters if the caller didn't supply them
193 */
194 if (itemRect==NULL)
195 itemRect = &tmpItemRect;
196
197 /*
198 * Retrieve the unmodified item rect.
199 */
200 *itemRect = infoPtr->items[itemIndex].rect;
201
202 /*
203 * calculate the times bottom and top based on the row
204 */
205 GetClientRect(hwnd, &clientRect);
206
207 if (lStyle & TCS_BOTTOM)
208 {
209 itemRect->bottom = clientRect.bottom -
210 SELECTED_TAB_OFFSET -
211 itemRect->top * (infoPtr->tabHeight - 2);
212
213 itemRect->top = clientRect.bottom -
214 infoPtr->tabHeight -
215 itemRect->top * ( infoPtr->tabHeight - 2);
216 }
217 else
218 {
219 itemRect->bottom = clientRect.top +
220 infoPtr->tabHeight +
221 itemRect->top * (infoPtr->tabHeight - 2);
222 itemRect->top = clientRect.top +
223 SELECTED_TAB_OFFSET+
224 itemRect->top * (infoPtr->tabHeight - 2);
225 }
226
227 /*
228 * "scroll" it to make sure the item at the very left of the
229 * tab control is the leftmost visible tab.
230 */
231 OffsetRect(itemRect,
232 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
233 0);
234
235 /*
236 * Move the rectangle so the first item is slightly offset from
237 * the left of the tab control.
238 */
239 OffsetRect(itemRect,
240 SELECTED_TAB_OFFSET,
241 0);
242
243
244 /*
245 * Now, calculate the position of the item as if it were selected.
246 */
247 if (selectedRect!=NULL)
248 {
249 CopyRect(selectedRect, itemRect);
250
251 /*
252 * The rectangle of a selected item is a bit wider.
253 */
254 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
255
256 /*
257 * If it also a bit higher.
258 */
259 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
260 {
261 selectedRect->top -=2; /* the border is thicker on the bottom */
262 selectedRect->bottom +=SELECTED_TAB_OFFSET;
263 }
264 else
265 {
266 selectedRect->top -=SELECTED_TAB_OFFSET;
267 selectedRect->bottom+=1;
268 }
269 }
270
271 return TRUE;
272}
273
274static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
275{
276 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
277 (LPRECT)lParam, (LPRECT)NULL);
278}
279
280/******************************************************************************
281 * TAB_KeyUp
282 *
283 * This method is called to handle keyboard input
284 */
285static LRESULT TAB_KeyUp(
286 HWND hwnd,
287 WPARAM keyCode)
288{
289 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
290 int newItem = -1;
291
292 switch (keyCode)
293 {
294 case VK_LEFT:
295 newItem = infoPtr->uFocus-1;
296 break;
297 case VK_RIGHT:
298 newItem = infoPtr->uFocus+1;
299 break;
300 }
301
302 /*
303 * If we changed to a valid item, change the selection
304 */
305 if ( (newItem >= 0) &&
306 (newItem < infoPtr->uNumItem) &&
307 (infoPtr->uFocus != newItem) )
308 {
309 if (!sendNotify(hwnd,TCN_SELCHANGING))
310 {
311 infoPtr->iSelected = newItem;
312 infoPtr->uFocus = newItem;
313 sendNotify(hwnd,TCN_SELCHANGE);
314
315 TAB_EnsureSelectionVisible(hwnd, infoPtr);
316 TAB_InvalidateTabArea(hwnd, infoPtr);
317 }
318 }
319
320 return 0;
321}
322
323/******************************************************************************
324 * TAB_FocusChanging
325 *
326 * This method is called whenever the focus goes in or out of this control
327 * it is used to update the visual state of the control.
328 */
329static LRESULT TAB_FocusChanging(
330 HWND hwnd,
331 UINT uMsg,
332 WPARAM wParam,
333 LPARAM lParam)
334{
335 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
336 RECT selectedRect;
337 BOOL isVisible;
338
339 /*
340 * Get the rectangle for the item.
341 */
342 isVisible = TAB_InternalGetItemRect(hwnd,
343 infoPtr,
344 infoPtr->uFocus,
345 NULL,
346 &selectedRect);
347
348 /*
349 * If the rectangle is not completely invisible, invalidate that
350 * portion of the window.
351 */
352 if (isVisible)
353 {
354 InvalidateRect(hwnd, &selectedRect, TRUE);
355 }
356
357 /*
358 * Don't otherwise disturb normal behavior.
359 */
360 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
361}
362
363static HWND TAB_InternalHitTest (
364 HWND hwnd,
365 TAB_INFO* infoPtr,
366 POINT pt,
367 UINT* flags)
368
369{
370 RECT rect;
371 int iCount;
372
373 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
374 {
375 TAB_InternalGetItemRect(hwnd,
376 infoPtr,
377 iCount,
378 &rect,
379 NULL);
380
381 if (PtInRect (&rect, pt))
382 {
383 *flags = TCHT_ONITEM;
384 return iCount;
385 }
386 }
387
388 *flags=TCHT_NOWHERE;
389 return -1;
390}
391
392static LRESULT
393TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
394{
395 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
396 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
397
398 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
399}
400
401
402static LRESULT
403TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
404{
405 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
406 POINT pt;
407 INT newItem,dummy;
408
409 if (infoPtr->hwndToolTip)
410 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
411 WM_LBUTTONDOWN, wParam, lParam);
412
413 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
414 SetFocus (hwnd);
415 }
416
417 if (infoPtr->hwndToolTip)
418 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
419 WM_LBUTTONDOWN, wParam, lParam);
420
421 pt.x = (INT)LOWORD(lParam);
422 pt.y = (INT)HIWORD(lParam);
423
424 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, (unsigned int*)&dummy);
425
426// TRACE("On Tab, item %d\n", newItem);
427
428 if ( (newItem!=-1) &&
429 (infoPtr->iSelected != newItem) )
430 {
431 if (sendNotify(hwnd,TCN_SELCHANGING) != TRUE)
432 {
433 infoPtr->iSelected = newItem;
434 infoPtr->uFocus = newItem;
435 sendNotify(hwnd, TCN_SELCHANGE);
436
437 TAB_EnsureSelectionVisible(hwnd, infoPtr);
438
439 TAB_InvalidateTabArea(hwnd, infoPtr);
440 }
441 }
442 return 0;
443}
444
445static LRESULT
446TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
447{
448 sendNotify(hwnd,NM_CLICK);
449
450 return 0;
451}
452
453static LRESULT
454TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
455{
456 sendNotify(hwnd,NM_RCLICK);
457
458 return 0;
459}
460
461/******************************************************************************
462 * TAB_DrawLoneItemInterior
463 *
464 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
465 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
466 * up the device context and font. This routine does the same setup but
467 * only calls TAB_DrawItemInterior for the single specified item.
468 */
469static void
470TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
471{
472 HDC hdc = GetDC(hwnd);
473 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
474 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
475 SelectObject(hdc, hOldFont);
476 ReleaseDC(hwnd, hdc);
477}
478
479/******************************************************************************
480 * TAB_HotTrackTimerProc
481 *
482 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
483 * timer is setup so we can check if the mouse is moved out of our window.
484 * (We don't get an event when the mouse leaves, the mouse-move events just
485 * stop being delivered to our window and just start being delivered to
486 * another window.) This function is called when the timer triggers so
487 * we can check if the mouse has left our window. If so, we un-highlight
488 * the hot-tracked tab.
489 */
490static VOID CALLBACK
491TAB_HotTrackTimerProc
492 (
493 HWND hwnd, // handle of window for timer messages
494 UINT uMsg, // WM_TIMER message
495 UINT idEvent, // timer identifier
496 DWORD dwTime // current system time
497 )
498{
499 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
500
501 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
502 {
503 POINT pt;
504
505 /*
506 ** If we can't get the cursor position, or if the cursor is outside our
507 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
508 ** "outside" even if it is within our bounding rect if another window
509 ** overlaps. Note also that the case where the cursor stayed within our
510 ** window but has moved off the hot-tracked tab will be handled by the
511 ** WM_MOUSEMOVE event.
512 */
513 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
514 {
515 // Redraw iHotTracked to look normal
516 INT iRedraw = infoPtr->iHotTracked;
517 infoPtr->iHotTracked = -1;
518 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
519
520 // Kill this timer
521 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
522 }
523 }
524}
525
526/******************************************************************************
527 * TAB_RecalcHotTrack
528 *
529 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
530 * should be highlighted. This function determines which tab in a tab control,
531 * if any, is under the mouse and records that information. The caller may
532 * supply output parameters to receive the item number of the tab item which
533 * was highlighted but isn't any longer and of the tab item which is now
534 * highlighted but wasn't previously. The caller can use this information to
535 * selectively redraw those tab items.
536 *
537 * If the caller has a mouse position, it can supply it through the pos
538 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
539 * supplies NULL and this function determines the current mouse position
540 * itself.
541 */
542static void
543TAB_RecalcHotTrack
544 (
545 HWND hwnd,
546 const LPARAM* pos,
547 int* out_redrawLeave,
548 int* out_redrawEnter
549 )
550{
551 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
552
553 int item = -1;
554
555
556 if (out_redrawLeave != NULL)
557 *out_redrawLeave = -1;
558 if (out_redrawEnter != NULL)
559 *out_redrawEnter = -1;
560
561 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
562 {
563 POINT pt;
564 UINT flags;
565
566 if (pos == NULL)
567 {
568 GetCursorPos(&pt);
569 ScreenToClient(hwnd, &pt);
570 }
571 else
572 {
573 pt.x = LOWORD(*pos);
574 pt.y = HIWORD(*pos);
575 }
576
577 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
578 }
579
580 if (item != infoPtr->iHotTracked)
581 {
582 if (infoPtr->iHotTracked >= 0)
583 {
584 // Mark currently hot-tracked to be redrawn to look normal
585 if (out_redrawLeave != NULL)
586 *out_redrawLeave = infoPtr->iHotTracked;
587
588 if (item < 0)
589 {
590 // Kill timer which forces recheck of mouse pos
591 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
592 }
593 }
594 else
595 {
596 // Start timer so we recheck mouse pos
597 UINT timerID = SetTimer
598 (
599 hwnd,
600 TAB_HOTTRACK_TIMER,
601 TAB_HOTTRACK_TIMER_INTERVAL,
602 TAB_HotTrackTimerProc
603 );
604
605 if (timerID == 0)
606 return; /* Hot tracking not available */
607 }
608
609 infoPtr->iHotTracked = item;
610
611 if (item >= 0)
612 {
613 // Mark new hot-tracked to be redrawn to look highlighted
614 if (out_redrawEnter != NULL)
615 *out_redrawEnter = item;
616 }
617 }
618}
619
620/******************************************************************************
621 * TAB_MouseMove
622 *
623 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
624 */
625static LRESULT
626TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
627{
628 int redrawLeave;
629 int redrawEnter;
630
631 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
632
633 if (infoPtr->hwndToolTip)
634 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
635 WM_LBUTTONDOWN, wParam, lParam);
636
637 /* Determine which tab to highlight. Redraw tabs which change highlight
638 ** status. */
639 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
640
641 if (redrawLeave != -1)
642 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
643 if (redrawEnter != -1)
644 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
645
646 return 0;
647}
648
649/******************************************************************************
650 * TAB_AdjustRect
651 *
652 * Calculates the tab control's display area given the windows rectangle or
653 * the window rectangle given the requested display rectangle.
654 */
655static LRESULT TAB_AdjustRect(
656 HWND hwnd,
657 WPARAM fLarger,
658 LPRECT prc)
659{
660 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
661
662 if (fLarger)
663 {
664 /*
665 * Go from display rectangle
666 */
667
668 /*
669 * Add the height of the tabs.
670 */
671 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
672 prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
673 else
674 prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
675
676 /*
677 * Inflate the rectangle for the padding
678 */
679 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
680
681 /*
682 * Inflate for the border
683 */
684 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
685 }
686 else
687 {
688 /*
689 * Go from window rectangle.
690 */
691
692 /*
693 * Deflate the rectangle for the border
694 */
695 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
696
697 /*
698 * Deflate the rectangle for the padding
699 */
700 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
701
702 /*
703 * Remove the height of the tabs.
704 */
705 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
706 prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
707 else
708 prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
709
710 }
711
712 return 0;
713}
714
715/******************************************************************************
716 * TAB_OnHScroll
717 *
718 * This method will handle the notification from the scroll control and
719 * perform the scrolling operation on the tab control.
720 */
721static LRESULT TAB_OnHScroll(
722 HWND hwnd,
723 int nScrollCode,
724 int nPos,
725 HWND hwndScroll)
726{
727 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
728
729 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
730 {
731 if(nPos < infoPtr->leftmostVisible)
732 infoPtr->leftmostVisible--;
733 else
734 infoPtr->leftmostVisible++;
735
736 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
737 TAB_InvalidateTabArea(hwnd, infoPtr);
738 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
739 MAKELONG(infoPtr->leftmostVisible, 0));
740 }
741
742 return 0;
743}
744
745/******************************************************************************
746 * TAB_SetupScrolling
747 *
748 * This method will check the current scrolling state and make sure the
749 * scrolling control is displayed (or not).
750 */
751static void TAB_SetupScrolling(
752 HWND hwnd,
753 TAB_INFO* infoPtr,
754 const RECT* clientRect)
755{
756 INT maxRange = 0;
757
758 if (infoPtr->needsScrolling)
759 {
760 RECT controlPos;
761 INT vsize, tabwidth;
762
763 /*
764 * Calculate the position of the scroll control.
765 */
766 controlPos.right = clientRect->right;
767 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
768
769 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
770 {
771 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
772 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
773 }
774 else
775 {
776 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
777 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
778 }
779
780 /*
781 * If we don't have a scroll control yet, we want to create one.
782 * If we have one, we want to make sure it's positioned right.
783 */
784 if (infoPtr->hwndUpDown==0)
785 {
786 /*
787 * I use a scrollbar since it seems to be more stable than the Updown
788 * control.
789 */
790 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
791 "",
792 WS_VISIBLE | WS_CHILD | UDS_HORZ,
793 controlPos.left, controlPos.top,
794 controlPos.right - controlPos.left,
795 controlPos.bottom - controlPos.top,
796 hwnd,
797 (HMENU)NULL,
798 (HINSTANCE)NULL,
799 NULL);
800 }
801 else
802 {
803 SetWindowPos(infoPtr->hwndUpDown,
804 (HWND)NULL,
805 controlPos.left, controlPos.top,
806 controlPos.right - controlPos.left,
807 controlPos.bottom - controlPos.top,
808 SWP_SHOWWINDOW | SWP_NOZORDER);
809 }
810
811 /* Now calculate upper limit of the updown control range.
812 * We do this by calculating how many tabs will be offscreen when the
813 * last tab is visible.
814 */
815 if(infoPtr->uNumItem)
816 {
817 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
818 maxRange = infoPtr->uNumItem;
819 tabwidth = infoPtr->items[maxRange-1].rect.right;
820
821 for(; maxRange > 0; maxRange--)
822 {
823 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
824 break;
825 }
826
827 if(maxRange == infoPtr->uNumItem)
828 maxRange--;
829 }
830 }
831 else
832 {
833 /*
834 * If we once had a scroll control... hide it.
835 */
836 if (infoPtr->hwndUpDown!=0)
837 {
838 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
839 }
840 }
841
842 if (infoPtr->hwndUpDown)
843 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
844}
845
846/******************************************************************************
847 * TAB_SetItemBounds
848 *
849 * This method will calculate the position rectangles of all the items in the
850 * control. The rectangle calculated starts at 0 for the first item in the
851 * list and ignores scrolling and selection.
852 * It also uses the current font to determine the height of the tab row and
853 * it checks if all the tabs fit in the client area of the window. If they
854 * dont, a scrolling control is added.
855 */
856static void TAB_SetItemBounds (HWND hwnd)
857{
858 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
859 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
860 TEXTMETRICA fontMetrics;
861 INT curItem;
862 INT curItemLeftPos;
863 INT curItemRowCount;
864 HFONT hFont, hOldFont;
865 HDC hdc;
866 RECT clientRect;
867 SIZE size;
868
869 /*
870 * We need to get text information so we need a DC and we need to select
871 * a font.
872 */
873 hdc = GetDC(hwnd);
874
875 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
876 hOldFont = SelectObject (hdc, hFont);
877
878 /*
879 * We will base the rectangle calculations on the client rectangle
880 * of the control.
881 */
882 GetClientRect(hwnd, &clientRect);
883
884 /*
885 * The leftmost item will be "0" aligned
886 */
887 curItemLeftPos = 0;
888 curItemRowCount = 0;
889
890 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
891 {
892 int item_height;
893 int icon_height = 0;
894
895 /*
896 * Use the current font to determine the height of a tab.
897 */
898 GetTextMetricsA(hdc, &fontMetrics);
899
900 /*
901 * Get the icon height
902 */
903 if (infoPtr->himl)
904 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
905
906 /*
907 * Take the highest between font or icon
908 */
909 if (fontMetrics.tmHeight > icon_height)
910 item_height = fontMetrics.tmHeight;
911 else
912 item_height = icon_height;
913
914 /*
915 * Make sure there is enough space for the letters + icon + growing the
916 * selected item + extra space for the selected item.
917 */
918 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
919 SELECTED_TAB_OFFSET;
920 }
921
922 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
923 {
924 /*
925 * Set the leftmost position of the tab.
926 */
927 infoPtr->items[curItem].rect.left = curItemLeftPos;
928
929 if ((lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
930 {
931 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
932 infoPtr->tabWidth +
933 2*HORIZONTAL_ITEM_PADDING;
934 }
935 else
936 {
937 int icon_width = 0;
938 int num = 2;
939
940 /*
941 * Calculate how wide the tab is depending on the text it contains
942 */
943 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
944 lstrlenW(infoPtr->items[curItem].pszText), &size);
945
946 /*
947 * Add the icon width
948 */
949 if (infoPtr->himl)
950 {
951 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
952 num++;
953 }
954
955 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
956 size.cx + icon_width +
957 num*HORIZONTAL_ITEM_PADDING;
958 }
959
960 /*
961 * Check if this is a multiline tab control and if so
962 * check to see if we should wrap the tabs
963 *
964 * Because we are going to arange all these tabs evenly
965 * really we are basically just counting rows at this point
966 *
967 */
968
969 if ((lStyle & TCS_MULTILINE)&&
970 (infoPtr->items[curItem].rect.right > clientRect.right))
971 {
972 infoPtr->items[curItem].rect.right -=
973 infoPtr->items[curItem].rect.left;
974 infoPtr->items[curItem].rect.left = 0;
975 curItemRowCount ++;
976 }
977
978 infoPtr->items[curItem].rect.bottom = 0;
979 infoPtr->items[curItem].rect.top = curItemRowCount;
980
981// TRACE("TextSize: %i\n ", size.cx);
982// TRACE("Rect: T %i, L %i, B %i, R %i\n",
983// infoPtr->items[curItem].rect.top,
984// infoPtr->items[curItem].rect.left,
985// infoPtr->items[curItem].rect.bottom,
986// infoPtr->items[curItem].rect.right);
987
988 /*
989 * The leftmost position of the next item is the rightmost position
990 * of this one.
991 */
992 if (lStyle & TCS_BUTTONS)
993 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
994 else
995 curItemLeftPos = infoPtr->items[curItem].rect.right;
996 }
997
998 if (!(lStyle & TCS_MULTILINE))
999 {
1000 /*
1001 * Check if we need a scrolling control.
1002 */
1003 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
1004 clientRect.right);
1005
1006 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1007 if(!infoPtr->needsScrolling)
1008 infoPtr->leftmostVisible = 0;
1009
1010 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1011 }
1012
1013 /*
1014 * Set the number of rows
1015 */
1016
1017 infoPtr->uNumRows = curItemRowCount;
1018
1019 if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0))
1020 {
1021 INT widthDiff,remainder;
1022 INT tabPerRow,remTab;
1023 INT iRow,iItm;
1024 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1025
1026 /*
1027 * Ok Microsoft trys to even out the rows. place the same
1028 * number of tabs in each row. So lets give that a shot
1029 *
1030 */
1031
1032 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows + 1);
1033 remTab = infoPtr->uNumItem % (infoPtr->uNumRows + 1);
1034
1035 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1036 iItm<infoPtr->uNumItem;
1037 iItm++,iCount++)
1038 {
1039 if (iCount >= ((iRow<remTab)?tabPerRow+1:tabPerRow))
1040 {
1041 iRow++;
1042 curItemLeftPos = 0;
1043 iCount = 0;
1044 }
1045 /*
1046 * normalize the current rect
1047 */
1048 infoPtr->items[iItm].rect.right -=
1049 infoPtr->items[iItm].rect.left;
1050 infoPtr->items[iItm].rect.left = 0;
1051
1052 infoPtr->items[iItm].rect.top = iRow;
1053 infoPtr->items[iItm].rect.left +=curItemLeftPos;
1054 infoPtr->items[iItm].rect.right +=curItemLeftPos;
1055 if (lStyle & TCS_BUTTONS)
1056 curItemLeftPos = infoPtr->items[iItm].rect.right +
1057 BUTTON_SPACINGX;
1058 else
1059 curItemLeftPos = infoPtr->items[iItm].rect.right;
1060 }
1061
1062 /*
1063 * Justify the rows
1064 *
1065 */
1066 {
1067 while(iIndexStart < infoPtr->uNumItem)
1068 {
1069 /*
1070 * find the indexs of the row
1071 */
1072 for (iIndexEnd=iIndexStart;
1073 (iIndexEnd < infoPtr->uNumItem) &&
1074 (infoPtr->items[iIndexEnd].rect.top ==
1075 infoPtr->items[iIndexStart].rect.top) ;
1076 iIndexEnd++)
1077 /* intentionaly blank */;
1078
1079 /*
1080 * we need to justify these tabs so they fill the whole given
1081 * client area
1082 *
1083 */
1084 widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) -
1085 infoPtr->items[iIndexEnd-1].rect.right;
1086
1087 iCount = iIndexEnd-iIndexStart;
1088
1089 if (iCount)
1090 {
1091 INT iIndex;
1092 remainder = widthDiff % iCount;
1093 widthDiff = widthDiff / iCount;
1094 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1095 iIndex++,iCount++)
1096 {
1097 infoPtr->items[iIndex].rect.left +=iCount*widthDiff;
1098 infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff;
1099 }
1100 infoPtr->items[iIndex-1].rect.right += remainder;
1101 }
1102
1103 iIndexStart=iIndexEnd;
1104 }
1105 }
1106 }
1107
1108 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1109 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1110
1111 /*
1112 * Cleanup
1113 */
1114 SelectObject (hdc, hOldFont);
1115 ReleaseDC (hwnd, hdc);
1116}
1117
1118/******************************************************************************
1119 * TAB_DrawItemInterior
1120 *
1121 * This method is used to draw the interior (text and icon) of a single tab
1122 * into the tab control.
1123 */
1124static void
1125TAB_DrawItemInterior
1126 (
1127 HWND hwnd,
1128 HDC hdc,
1129 INT iItem,
1130 RECT* drawRect
1131 )
1132{
1133 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1134 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1135
1136 RECT localRect;
1137
1138 HPEN htextPen = GetSysColorPen (COLOR_BTNTEXT);
1139 HPEN holdPen;
1140 INT oldBkMode;
1141
1142 if (drawRect == NULL)
1143 {
1144 BOOL isVisible;
1145 RECT itemRect;
1146 RECT selectedRect;
1147
1148 /*
1149 * Get the rectangle for the item.
1150 */
1151 isVisible = TAB_InternalGetItemRect
1152 (
1153 hwnd,
1154 infoPtr,
1155 iItem,
1156 &itemRect,
1157 &selectedRect
1158 );
1159 if (!isVisible)
1160 return;
1161
1162 /*
1163 * Make sure drawRect points to something valid; simplifies code.
1164 */
1165 drawRect = &localRect;
1166
1167 /*
1168 * This logic copied from the part of TAB_DrawItem which draws
1169 * the tab background. It's important to keep it in sync. I
1170 * would have liked to avoid code duplication, but couldn't figure
1171 * out how without making spaghetti of TAB_DrawItem.
1172 */
1173 if (lStyle & TCS_BUTTONS)
1174 {
1175 *drawRect = itemRect;
1176 if (iItem == infoPtr->iSelected)
1177 {
1178 drawRect->right--;
1179 drawRect->bottom--;
1180 }
1181 }
1182 else
1183 {
1184 if (iItem == infoPtr->iSelected)
1185 *drawRect = selectedRect;
1186 else
1187 *drawRect = itemRect;
1188 drawRect->right--;
1189 drawRect->bottom--;
1190 }
1191 }
1192
1193 /*
1194 * Text pen
1195 */
1196 holdPen = SelectObject(hdc, htextPen);
1197
1198 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1199 SetTextColor
1200 (
1201 hdc,
1202 GetSysColor
1203 (
1204 (iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT
1205 )
1206 );
1207
1208 /*
1209 * Deflate the rectangle to acount for the padding
1210 */
1211 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1212
1213 /*
1214 * if owner draw, tell the owner to draw
1215 */
1216 if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
1217 {
1218 DRAWITEMSTRUCT dis;
1219 UINT id;
1220
1221 /*
1222 * get the control id
1223 */
1224 id = GetWindowLongA(hwnd,GWL_ID);
1225
1226 /*
1227 * put together the DRAWITEMSTRUCT
1228 */
1229 dis.CtlType = ODT_TAB;
1230 dis.CtlID = id;
1231 dis.itemID = iItem;
1232 dis.itemAction = ODA_DRAWENTIRE;
1233 if ( iItem == infoPtr->iSelected )
1234 dis.itemState = ODS_SELECTED;
1235 else
1236 dis.itemState = 0;
1237 dis.hwndItem = hwnd; /* */
1238 dis.hDC = hdc;
1239 dis.rcItem = *drawRect; /* */
1240 dis.itemData = infoPtr->items[iItem].lParam;
1241
1242 /*
1243 * send the draw message
1244 */
1245 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1246 }
1247 else
1248 {
1249 UINT uHorizAlign;
1250
1251 /*
1252 * If not owner draw, then do the drawing ourselves.
1253 *
1254 * Draw the icon.
1255 */
1256 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1257 {
1258 INT cx;
1259 INT cy;
1260
1261 ImageList_Draw
1262 (
1263 infoPtr->himl,
1264 infoPtr->items[iItem].iImage,
1265 hdc,
1266 drawRect->left,
1267 drawRect->top + 1,
1268 ILD_NORMAL
1269 );
1270 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1271 drawRect->left += (cx + HORIZONTAL_ITEM_PADDING);
1272 }
1273
1274 /*
1275 * Draw the text;
1276 */
1277 if (lStyle & TCS_RIGHTJUSTIFY)
1278 uHorizAlign = DT_CENTER;
1279 else
1280 uHorizAlign = DT_LEFT;
1281
1282 DrawTextW
1283 (
1284 hdc,
1285 infoPtr->items[iItem].pszText,
1286 lstrlenW(infoPtr->items[iItem].pszText),
1287 drawRect,
1288 uHorizAlign | DT_SINGLELINE | DT_VCENTER
1289 );
1290 }
1291
1292 /*
1293 * Cleanup
1294 */
1295 SetBkMode(hdc, oldBkMode);
1296 SelectObject(hdc, holdPen);
1297}
1298
1299/******************************************************************************
1300 * TAB_DrawItem
1301 *
1302 * This method is used to draw a single tab into the tab control.
1303 */
1304static void TAB_DrawItem(
1305 HWND hwnd,
1306 HDC hdc,
1307 INT iItem)
1308{
1309 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1310 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1311 RECT itemRect;
1312 RECT selectedRect;
1313 BOOL isVisible;
1314 RECT r;
1315
1316 /*
1317 * Get the rectangle for the item.
1318 */
1319 isVisible = TAB_InternalGetItemRect(hwnd,
1320 infoPtr,
1321 iItem,
1322 &itemRect,
1323 &selectedRect);
1324
1325 if (isVisible)
1326 {
1327 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
1328 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1329 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
1330 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
1331 HPEN holdPen;
1332 INT oldBkMode;
1333 BOOL deleteBrush = TRUE;
1334
1335 if (lStyle & TCS_BUTTONS)
1336 {
1337 /*
1338 * Get item rectangle.
1339 */
1340 r = itemRect;
1341
1342 holdPen = SelectObject (hdc, hwPen);
1343
1344 if (iItem == infoPtr->iSelected)
1345 {
1346 /*
1347 * Background color.
1348 */
1349 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1350 {
1351 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
1352 DeleteObject(hbr);
1353 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1354 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
1355 SetBkColor(hdc, bk);
1356
1357 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1358 * we better use 0x55aa bitmap brush to make scrollbar's background
1359 * look different from the window background.
1360 */
1361 if (bk == GetSysColor(COLOR_WINDOW))
1362 hbr = GetPattern55AABrush();
1363
1364 deleteBrush = FALSE;
1365 }
1366
1367 /*
1368 * Erase the background.
1369 */
1370 FillRect(hdc, &r, hbr);
1371
1372 /*
1373 * Draw the tab now.
1374 * The rectangles calculated exclude the right and bottom
1375 * borders of the rectangle. To simply the following code, those
1376 * borders are shaved-off beforehand.
1377 */
1378 r.right--;
1379 r.bottom--;
1380
1381 /* highlight */
1382 MoveToEx (hdc, r.left, r.bottom, NULL);
1383 LineTo (hdc, r.right, r.bottom);
1384 LineTo (hdc, r.right, r.top);
1385
1386 /* shadow */
1387 SelectObject(hdc, hbPen);
1388 LineTo (hdc, r.left, r.top);
1389 LineTo (hdc, r.left, r.bottom);
1390 }
1391 else
1392 {
1393 /*
1394 * Erase the background.
1395 */
1396 FillRect(hdc, &r, hbr);
1397
1398 /* highlight */
1399 MoveToEx (hdc, r.left, r.bottom, NULL);
1400 LineTo (hdc, r.left, r.top);
1401 LineTo (hdc, r.right, r.top);
1402
1403 /* shadow */
1404 SelectObject(hdc, hbPen);
1405 LineTo (hdc, r.right, r.bottom);
1406 LineTo (hdc, r.left, r.bottom);
1407 }
1408 }
1409 else
1410 {
1411 /*
1412 * Background color.
1413 */
1414 DeleteObject(hbr);
1415 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1416
1417 /*
1418 * We draw a rectangle of different sizes depending on the selection
1419 * state.
1420 */
1421 if (iItem == infoPtr->iSelected)
1422 r = selectedRect;
1423 else
1424 r = itemRect;
1425
1426 /*
1427 * Erase the background.
1428 * This is necessary when drawing the selected item since it is larger
1429 * than the others, it might overlap with stuff already drawn by the
1430 * other tabs
1431 */
1432 FillRect(hdc, &r, hbr);
1433
1434 /*
1435 * Draw the tab now.
1436 * The rectangles calculated exclude the right and bottom
1437 * borders of the rectangle. To simply the following code, those
1438 * borders are shaved-off beforehand.
1439 */
1440 r.right--;
1441 r.bottom--;
1442
1443 holdPen = SelectObject (hdc, hwPen);
1444
1445 if (lStyle & TCS_BOTTOM)
1446 {
1447 /* highlight */
1448 MoveToEx (hdc, r.left, r.top, NULL);
1449 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1450 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1451
1452 /* shadow */
1453 SelectObject(hdc, hbPen);
1454 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1455 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1456 LineTo (hdc, r.right, r.top);
1457 }
1458 else
1459 {
1460 /* highlight */
1461 MoveToEx (hdc, r.left, r.bottom, NULL);
1462 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1463 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1464 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1465
1466 /* shadow */
1467 SelectObject(hdc, hbPen);
1468 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1469 LineTo (hdc, r.right, r.bottom);
1470 }
1471 }
1472
1473 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1474
1475 /* This modifies r to be the text rectangle. */
1476 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
1477
1478 /*
1479 * Draw the focus rectangle
1480 */
1481 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1482 (GetFocus() == hwnd) &&
1483 (iItem == infoPtr->uFocus) )
1484 {
1485 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1486
1487 SelectObject(hdc, hfocusPen);
1488
1489 MoveToEx (hdc, r.left, r.top, NULL);
1490 LineTo (hdc, r.right-1, r.top);
1491 LineTo (hdc, r.right-1, r.bottom -1);
1492 LineTo (hdc, r.left, r.bottom -1);
1493 LineTo (hdc, r.left, r.top);
1494 }
1495
1496 /*
1497 * Cleanup
1498 */
1499 SetBkMode(hdc, oldBkMode);
1500 SelectObject(hdc, holdPen);
1501 DeleteObject(hfocusPen);
1502 if (deleteBrush) DeleteObject(hbr);
1503 }
1504}
1505
1506/******************************************************************************
1507 * TAB_DrawBorder
1508 *
1509 * This method is used to draw the raised border around the tab control
1510 * "content" area.
1511 */
1512static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1513{
1514 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1515 HPEN htmPen;
1516 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1517 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1518 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1519 RECT rect;
1520
1521 GetClientRect (hwnd, &rect);
1522
1523 /*
1524 * Adjust for the style
1525 */
1526 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1527 {
1528 rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1529 }
1530 else
1531 {
1532 rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1533 }
1534
1535 /*
1536 * Shave-off the right and bottom margins (exluded in the
1537 * rect)
1538 */
1539 rect.right--;
1540 rect.bottom--;
1541
1542 /* highlight */
1543 htmPen = SelectObject (hdc, hwPen);
1544
1545 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1546 LineTo (hdc, rect.left, rect.top);
1547 LineTo (hdc, rect.right, rect.top);
1548
1549 /* Dark Shadow */
1550 SelectObject (hdc, hbPen);
1551 LineTo (hdc, rect.right, rect.bottom );
1552 LineTo (hdc, rect.left, rect.bottom);
1553
1554 /* shade */
1555 SelectObject (hdc, hShade );
1556 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1557 LineTo (hdc, rect.right-1, rect.bottom-1);
1558 LineTo (hdc, rect.left, rect.bottom-1);
1559
1560 SelectObject(hdc, htmPen);
1561}
1562
1563/******************************************************************************
1564 * TAB_Refresh
1565 *
1566 * This method repaints the tab control..
1567 */
1568static void TAB_Refresh (HWND hwnd, HDC hdc)
1569{
1570 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1571 HFONT hOldFont;
1572 INT i;
1573
1574 if (!infoPtr->DoRedraw)
1575 return;
1576
1577 hOldFont = SelectObject (hdc, infoPtr->hFont);
1578
1579 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1580 {
1581 for (i = 0; i < infoPtr->uNumItem; i++)
1582 {
1583 TAB_DrawItem (hwnd, hdc, i);
1584 }
1585 }
1586 else
1587 {
1588 /*
1589 * Draw all the non selected item first.
1590 */
1591 for (i = 0; i < infoPtr->uNumItem; i++)
1592 {
1593 if (i != infoPtr->iSelected)
1594 TAB_DrawItem (hwnd, hdc, i);
1595 }
1596
1597 /*
1598 * Now, draw the border, draw it before the selected item
1599 * since the selected item overwrites part of the border.
1600 */
1601 TAB_DrawBorder (hwnd, hdc);
1602
1603 /*
1604 * Then, draw the selected item
1605 */
1606 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1607
1608 /*
1609 * If we haven't set the current focus yet, set it now.
1610 * Only happens when we first paint the tab controls.
1611 */
1612 if (infoPtr->uFocus == -1)
1613 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1614 }
1615
1616 SelectObject (hdc, hOldFont);
1617}
1618
1619static LRESULT
1620TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1621{
1622 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1623
1624 infoPtr->DoRedraw=(BOOL) wParam;
1625 return 0;
1626}
1627
1628static LRESULT TAB_EraseBackground(
1629 HWND hwnd,
1630 HDC givenDC)
1631{
1632 HDC hdc;
1633 RECT clientRect;
1634
1635 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1636
1637 hdc = givenDC ? givenDC : GetDC(hwnd);
1638
1639 GetClientRect(hwnd, &clientRect);
1640
1641 FillRect(hdc, &clientRect, brush);
1642
1643 if (givenDC==0)
1644 ReleaseDC(hwnd, hdc);
1645
1646 DeleteObject(brush);
1647
1648 return 0;
1649}
1650
1651/******************************************************************************
1652 * TAB_EnsureSelectionVisible
1653 *
1654 * This method will make sure that the current selection is completely
1655 * visible by scrolling until it is.
1656 */
1657static void TAB_EnsureSelectionVisible(
1658 HWND hwnd,
1659 TAB_INFO* infoPtr)
1660{
1661 INT iSelected = infoPtr->iSelected;
1662
1663 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
1664
1665 /*
1666 * set the items row to the bottommost row or topmost row depending on
1667 * style
1668 */
1669
1670 if (infoPtr->uNumRows > 0)
1671 {
1672 INT newselected=infoPtr->items[iSelected].rect.top;
1673 INT iTargetRow;
1674 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1675
1676 if (lStyle & TCS_BOTTOM)
1677 iTargetRow = 0;
1678 else
1679 iTargetRow = infoPtr->uNumRows;
1680
1681 if (newselected != iTargetRow)
1682 {
1683 INT i;
1684 for (i=0; i < infoPtr->uNumItem; i++)
1685 if (infoPtr->items[i].rect.top == newselected )
1686 infoPtr->items[i].rect.top = iTargetRow;
1687 else if (lStyle&TCS_BOTTOM)
1688 {
1689 if (infoPtr->items[i].rect.top < newselected)
1690 infoPtr->items[i].rect.top+=1;
1691 }
1692 else
1693 {
1694 if (infoPtr->items[i].rect.top > newselected)
1695 infoPtr->items[i].rect.top-=1;
1696 }
1697
1698 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1699 }
1700 }
1701
1702 /*
1703 * Do the trivial cases first.
1704 */
1705 if ( (!infoPtr->needsScrolling) ||
1706 (infoPtr->hwndUpDown==0) )
1707 return;
1708
1709 if (infoPtr->leftmostVisible >= iSelected)
1710 {
1711 infoPtr->leftmostVisible = iSelected;
1712 }
1713 else
1714 {
1715 RECT r;
1716 INT width, i;
1717 /*
1718 * Calculate the part of the client area that is visible.
1719 */
1720 GetClientRect(hwnd, &r);
1721 width = r.right;
1722
1723 GetClientRect(infoPtr->hwndUpDown, &r);
1724 width -= r.right;
1725
1726 if ((infoPtr->items[iSelected].rect.right -
1727 infoPtr->items[iSelected].rect.left) >= width )
1728 {
1729 /* Special case: width of selected item is greater than visible
1730 * part of control.
1731 */
1732 infoPtr->leftmostVisible = iSelected;
1733 }
1734 else
1735 {
1736 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1737 {
1738 if ((infoPtr->items[iSelected].rect.right -
1739 infoPtr->items[i].rect.left) < width)
1740 break;
1741 }
1742 infoPtr->leftmostVisible = i;
1743 }
1744 }
1745
1746 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
1747 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1748
1749 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1750 MAKELONG(infoPtr->leftmostVisible, 0));
1751}
1752
1753/******************************************************************************
1754 * TAB_InvalidateTabArea
1755 *
1756 * This method will invalidate the portion of the control that contains the
1757 * tabs. It is called when the state of the control changes and needs
1758 * to be redisplayed
1759 */
1760static void TAB_InvalidateTabArea(
1761 HWND hwnd,
1762 TAB_INFO* infoPtr)
1763{
1764 RECT clientRect;
1765
1766 GetClientRect(hwnd, &clientRect);
1767
1768 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1769 {
1770 clientRect.top = clientRect.bottom - (infoPtr->tabHeight *
1771 (infoPtr->uNumRows + 1) + 3);
1772 }
1773 else
1774 {
1775 clientRect.bottom = clientRect.top + (infoPtr->tabHeight *
1776 (infoPtr->uNumRows + 1) + 1);
1777 }
1778
1779 InvalidateRect(hwnd, &clientRect, TRUE);
1780}
1781
1782static LRESULT
1783TAB_Paint (HWND hwnd, WPARAM wParam)
1784{
1785 HDC hdc;
1786 PAINTSTRUCT ps;
1787
1788 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1789 TAB_Refresh (hwnd, hdc);
1790
1791 if(!wParam)
1792 EndPaint (hwnd, &ps);
1793
1794 return 0;
1795}
1796
1797static LRESULT TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1798{
1799 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1800 TCITEMA *pti;
1801 INT iItem, len;
1802 RECT rect;
1803
1804 GetClientRect (hwnd, &rect);
1805// TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1806// rect.top, rect.left, rect.bottom, rect.right);
1807
1808 pti = (TCITEMA *)lParam;
1809 iItem = (INT)wParam;
1810
1811 if (iItem < 0) return -1;
1812 if (iItem > infoPtr->uNumItem)
1813 iItem = infoPtr->uNumItem;
1814
1815 if (infoPtr->uNumItem == 0) {
1816 infoPtr->items = (TAB_ITEM*)COMCTL32_Alloc (sizeof (TAB_ITEM));
1817 infoPtr->uNumItem++;
1818 infoPtr->iSelected = 0;
1819 }
1820 else {
1821 TAB_ITEM *oldItems = infoPtr->items;
1822
1823 infoPtr->uNumItem++;
1824 infoPtr->items = (TAB_ITEM*)COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1825
1826 /* pre insert copy */
1827 if (iItem > 0) {
1828 memcpy (&infoPtr->items[0], &oldItems[0],
1829 iItem * sizeof(TAB_ITEM));
1830 }
1831
1832 /* post insert copy */
1833 if (iItem < infoPtr->uNumItem - 1) {
1834 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1835 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1836
1837 }
1838
1839 if (iItem <= infoPtr->iSelected)
1840 infoPtr->iSelected++;
1841
1842 COMCTL32_Free (oldItems);
1843 }
1844
1845 infoPtr->items[iItem].mask = pti->mask;
1846 if (pti->mask & TCIF_TEXT) {
1847 len = lstrlenA (pti->pszText);
1848 infoPtr->items[iItem].pszText = (WCHAR*)COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1849 lstrcpyAtoW (infoPtr->items[iItem].pszText, pti->pszText);
1850 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1851 }
1852
1853 if (pti->mask & TCIF_IMAGE)
1854 infoPtr->items[iItem].iImage = pti->iImage;
1855
1856 if (pti->mask & TCIF_PARAM)
1857 infoPtr->items[iItem].lParam = pti->lParam;
1858
1859 TAB_SetItemBounds(hwnd);
1860 TAB_InvalidateTabArea(hwnd, infoPtr);
1861
1862// TRACE(tab, "[%04x]: added item %d '%s'\n",
1863// hwnd, iItem, infoPtr->items[iItem].pszText);
1864
1865 return iItem;
1866}
1867
1868static LRESULT TAB_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1869{
1870 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1871 TCITEMW *pti;
1872 INT iItem, len;
1873 RECT rect;
1874
1875 GetClientRect (hwnd, &rect);
1876// TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1877// rect.top, rect.left, rect.bottom, rect.right);
1878
1879 pti = (TCITEMW*)lParam;
1880 iItem = (INT)wParam;
1881
1882 if (iItem < 0) return -1;
1883 if (iItem > infoPtr->uNumItem)
1884 iItem = infoPtr->uNumItem;
1885
1886 if (infoPtr->uNumItem == 0) {
1887 infoPtr->items = (TAB_ITEM*)COMCTL32_Alloc (sizeof (TAB_ITEM));
1888 infoPtr->uNumItem++;
1889 }
1890 else {
1891 TAB_ITEM *oldItems = infoPtr->items;
1892
1893 infoPtr->uNumItem++;
1894 infoPtr->items = (TAB_ITEM*)COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1895
1896 /* pre insert copy */
1897 if (iItem > 0) {
1898 memcpy (&infoPtr->items[0], &oldItems[0],
1899 iItem * sizeof(TAB_ITEM));
1900 }
1901
1902 /* post insert copy */
1903 if (iItem < infoPtr->uNumItem - 1) {
1904 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1905 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1906
1907 }
1908
1909 COMCTL32_Free (oldItems);
1910 }
1911
1912 infoPtr->items[iItem].mask = pti->mask;
1913 if (pti->mask & TCIF_TEXT) {
1914 len = lstrlenW (pti->pszText);
1915 infoPtr->items[iItem].pszText = (WCHAR*)COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1916 lstrcpyW (infoPtr->items[iItem].pszText, pti->pszText);
1917 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1918 }
1919
1920 if (pti->mask & TCIF_IMAGE)
1921 infoPtr->items[iItem].iImage = pti->iImage;
1922
1923 if (pti->mask & TCIF_PARAM)
1924 infoPtr->items[iItem].lParam = pti->lParam;
1925
1926 TAB_SetItemBounds(hwnd);
1927 TAB_InvalidateTabArea(hwnd, infoPtr);
1928
1929// TRACE(tab, "[%04x]: added item %d '%s'\n",
1930// hwnd, iItem, infoPtr->items[iItem].pszText);
1931
1932 return iItem;
1933}
1934
1935static LRESULT
1936TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1937{
1938 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1939 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1940 LONG lResult = 0;
1941
1942 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1943 {
1944 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1945 infoPtr->tabWidth = (INT)LOWORD(lParam);
1946 infoPtr->tabHeight = (INT)HIWORD(lParam);
1947 }
1948
1949 infoPtr->fSizeSet = TRUE;
1950
1951 return lResult;
1952}
1953
1954static LRESULT
1955TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1956{
1957 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1958 TCITEMA *tabItem;
1959 TAB_ITEM *wineItem;
1960 INT iItem,len;
1961
1962 iItem=(INT) wParam;
1963 tabItem=(LPTCITEMA ) lParam;
1964// TRACE("%d %p\n",iItem, tabItem);
1965 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1966
1967 wineItem=& infoPtr->items[iItem];
1968
1969 if (tabItem->mask & TCIF_IMAGE)
1970 wineItem->iImage=tabItem->iImage;
1971
1972 if (tabItem->mask & TCIF_PARAM)
1973 wineItem->lParam=tabItem->lParam;
1974
1975// if (tabItem->mask & TCIF_RTLREADING)
1976// FIXME("TCIF_RTLREADING\n");
1977
1978 if (tabItem->mask & TCIF_STATE)
1979 wineItem->dwState=tabItem->dwState;
1980
1981 if (tabItem->mask & TCIF_TEXT) {
1982 len=lstrlenA (tabItem->pszText);
1983 if (len > wineItem->cchTextMax)
1984 wineItem->pszText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,(len+1)*sizeof(WCHAR));
1985 lstrcpyAtoW(wineItem->pszText,tabItem->pszText);
1986 }
1987
1988 /*
1989 * Update and repaint tabs.
1990 */
1991 TAB_SetItemBounds(hwnd);
1992 TAB_InvalidateTabArea(hwnd,infoPtr);
1993
1994 return TRUE;
1995}
1996
1997static LRESULT TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1998{
1999 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2000 TCITEMW *tabItem;
2001 TAB_ITEM *wineItem;
2002 INT iItem,len;
2003
2004 iItem=(INT) wParam;
2005 tabItem=(LPTCITEMW) lParam;
2006// TRACE (tab,"%d %p\n",iItem, tabItem);
2007 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
2008
2009 wineItem=& infoPtr->items[iItem];
2010
2011 if (tabItem->mask & TCIF_IMAGE)
2012 wineItem->iImage=tabItem->iImage;
2013
2014 if (tabItem->mask & TCIF_PARAM)
2015 wineItem->lParam=tabItem->lParam;
2016
2017// if (tabItem->mask & TCIF_RTLREADING)
2018// FIXME (tab,"TCIF_RTLREADING\n");
2019
2020 if (tabItem->mask & TCIF_STATE)
2021 wineItem->dwState=tabItem->dwState;
2022
2023 if (tabItem->mask & TCIF_TEXT) {
2024 len = lstrlenW (tabItem->pszText);
2025 if (len>wineItem->cchTextMax)
2026 wineItem->pszText = (WCHAR*)COMCTL32_ReAlloc (wineItem->pszText, (len+1)*sizeof(WCHAR));
2027 lstrcpyW (wineItem->pszText, tabItem->pszText);
2028 }
2029
2030 return TRUE;
2031}
2032
2033static LRESULT
2034TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2035{
2036 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2037
2038 return infoPtr->uNumItem;
2039}
2040
2041
2042static LRESULT
2043TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2044{
2045 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2046 TCITEMA *tabItem;
2047 TAB_ITEM *wineItem;
2048 INT iItem;
2049
2050 iItem=(INT) wParam;
2051 tabItem=(LPTCITEMA) lParam;
2052// TRACE (tab,"\n");
2053 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2054
2055 wineItem=& infoPtr->items[iItem];
2056
2057 if (tabItem->mask & TCIF_IMAGE)
2058 tabItem->iImage=wineItem->iImage;
2059
2060 if (tabItem->mask & TCIF_PARAM)
2061 tabItem->lParam=wineItem->lParam;
2062
2063// if (tabItem->mask & TCIF_RTLREADING)
2064// FIXME (tab, "TCIF_RTLREADING\n");
2065
2066 if (tabItem->mask & TCIF_STATE)
2067 tabItem->dwState=wineItem->dwState;
2068
2069 if (tabItem->mask & TCIF_TEXT)
2070 lstrcpynWtoA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
2071
2072 return TRUE;
2073}
2074
2075static LRESULT TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2076{
2077 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2078 TCITEMW *tabItem;
2079 TAB_ITEM *wineItem;
2080 INT iItem;
2081
2082 iItem=(INT) wParam;
2083 tabItem=(LPTCITEMW) lParam;
2084// TRACE (tab,"\n");
2085 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
2086
2087 wineItem=& infoPtr->items[iItem];
2088
2089 if (tabItem->mask & TCIF_IMAGE)
2090 tabItem->iImage=wineItem->iImage;
2091
2092 if (tabItem->mask & TCIF_PARAM)
2093 tabItem->lParam=wineItem->lParam;
2094
2095// if (tabItem->mask & TCIF_RTLREADING)
2096// FIXME (tab, "TCIF_RTLREADING\n");
2097
2098 if (tabItem->mask & TCIF_STATE)
2099 tabItem->dwState=wineItem->dwState;
2100
2101 if (tabItem->mask & TCIF_TEXT)
2102 lstrcpynW (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
2103
2104 return TRUE;
2105}
2106
2107static LRESULT
2108TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2109{
2110 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2111 INT iItem = (INT) wParam;
2112 BOOL bResult = FALSE;
2113
2114 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2115 {
2116 TAB_ITEM *oldItems = infoPtr->items;
2117
2118 infoPtr->uNumItem--;
2119 infoPtr->items = (TAB_ITEM*)COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2120
2121 if (iItem > 0)
2122 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2123
2124 if (iItem < infoPtr->uNumItem)
2125 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2126 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2127
2128 COMCTL32_Free (oldItems);
2129
2130 /*
2131 * Readjust the selected index.
2132 */
2133 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2134 infoPtr->iSelected--;
2135
2136 if (iItem < infoPtr->iSelected)
2137 infoPtr->iSelected--;
2138
2139 if (infoPtr->uNumItem == 0)
2140 infoPtr->iSelected = -1;
2141
2142 /*
2143 * Reposition and repaint tabs.
2144 */
2145 TAB_SetItemBounds(hwnd);
2146 TAB_InvalidateTabArea(hwnd,infoPtr);
2147
2148 bResult = TRUE;
2149 }
2150
2151 return bResult;
2152}
2153
2154static LRESULT
2155TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2156{
2157 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2158
2159 COMCTL32_Free (infoPtr->items);
2160 infoPtr->uNumItem = 0;
2161 infoPtr->iSelected = -1;
2162 if (infoPtr->iHotTracked >= 0)
2163 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2164 infoPtr->iHotTracked = -1;
2165
2166 TAB_SetItemBounds(hwnd);
2167 TAB_InvalidateTabArea(hwnd,infoPtr);
2168
2169 return TRUE;
2170}
2171
2172
2173static LRESULT
2174TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2175{
2176 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2177
2178// TRACE (tab,"\n");
2179 return (LRESULT)infoPtr->hFont;
2180}
2181
2182static LRESULT
2183TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2184
2185{
2186 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2187
2188// TRACE (tab,"%x %lx\n",wParam, lParam);
2189
2190 infoPtr->hFont = (HFONT)wParam;
2191
2192 TAB_SetItemBounds(hwnd);
2193
2194 TAB_InvalidateTabArea(hwnd, infoPtr);
2195
2196 return 0;
2197}
2198
2199
2200static LRESULT
2201TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2202{
2203 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2204
2205// TRACE (tab,"\n");
2206 return (LRESULT)infoPtr->himl;
2207}
2208
2209static LRESULT
2210TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2211{
2212 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2213 HIMAGELIST himlPrev;
2214
2215// TRACE (tab,"\n");
2216 himlPrev = infoPtr->himl;
2217 infoPtr->himl= (HIMAGELIST)lParam;
2218 return (LRESULT)himlPrev;
2219}
2220
2221
2222static LRESULT
2223TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2224
2225{
2226/* I'm not really sure what the following code was meant to do.
2227 This is what it is doing:
2228 When WM_SIZE is sent with SIZE_RESTORED, the control
2229 gets positioned in the top left corner.
2230
2231 RECT parent_rect;
2232 HWND parent;
2233 UINT uPosFlags,cx,cy;
2234
2235 uPosFlags=0;
2236 if (!wParam) {
2237 parent = GetParent (hwnd);
2238 GetClientRect(parent, &parent_rect);
2239 cx=LOWORD (lParam);
2240 cy=HIWORD (lParam);
2241 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2242 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2243
2244 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2245 cx, cy, uPosFlags | SWP_NOZORDER);
2246 } else {
2247// FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2248 } */
2249
2250 /*
2251 * Recompute the size/position of the tabs.
2252 */
2253 TAB_SetItemBounds (hwnd);
2254
2255 /*
2256 * Force a repaint of the control.
2257 */
2258 InvalidateRect(hwnd, NULL, TRUE);
2259
2260 return 0;
2261}
2262
2263
2264static LRESULT
2265TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2266{
2267 TAB_INFO *infoPtr;
2268 TEXTMETRICA fontMetrics;
2269 HDC hdc;
2270 HFONT hOldFont;
2271 DWORD dwStyle;
2272
2273 infoPtr = (TAB_INFO*)initControl(hwnd,sizeof(TAB_INFO));
2274
2275 infoPtr->uNumItem = 0;
2276 infoPtr->uNumRows = 0;
2277 infoPtr->hFont = 0;
2278 infoPtr->items = 0;
2279 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2280 infoPtr->iSelected = -1;
2281 infoPtr->iHotTracked = -1;
2282 infoPtr->uFocus = -1;
2283 infoPtr->hwndToolTip = 0;
2284 infoPtr->DoRedraw = TRUE;
2285 infoPtr->needsScrolling = FALSE;
2286 infoPtr->hwndUpDown = 0;
2287 infoPtr->leftmostVisible = 0;
2288 infoPtr->fSizeSet = FALSE;
2289
2290 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2291 if you don't specify in CreateWindow. This is necesary in
2292 order for paint to work correctly. This follows windows behaviour. */
2293 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2294 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2295
2296
2297 /* Create tooltip control */
2298 if (dwStyle & TCS_TOOLTIPS)
2299 infoPtr->hwndToolTip = createToolTip(hwnd,0,FALSE);
2300
2301 /*
2302 * We need to get text information so we need a DC and we need to select
2303 * a font.
2304 */
2305 hdc = GetDC(hwnd);
2306 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2307
2308 /*
2309 * Use the system font to determine the initial height of a tab.
2310 */
2311 GetTextMetricsA(hdc, &fontMetrics);
2312
2313 /*
2314 * Make sure there is enough space for the letters + growing the
2315 * selected item + extra space for the selected item.
2316 */
2317 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
2318 SELECTED_TAB_OFFSET;
2319
2320 /*
2321 * Initialize the width of a tab.
2322 */
2323 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2324
2325 SelectObject (hdc, hOldFont);
2326 ReleaseDC(hwnd, hdc);
2327
2328 return 0;
2329}
2330
2331static LRESULT
2332TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2333{
2334 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2335 INT iItem;
2336
2337 if (infoPtr->items) {
2338 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2339 if (infoPtr->items[iItem].pszText)
2340 COMCTL32_Free (infoPtr->items[iItem].pszText);
2341 }
2342 COMCTL32_Free (infoPtr->items);
2343 }
2344
2345 if (infoPtr->iHotTracked >= 0)
2346 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2347
2348 doneControl(hwnd);
2349
2350 return 0;
2351}
2352
2353static LRESULT TAB_StyleChanged(HWND hwnd,WPARAM wParam,LPARAM lParam)
2354{
2355 TAB_SetItemBounds (hwnd);
2356 InvalidateRect(hwnd, NULL, TRUE);
2357
2358 return 0;
2359}
2360
2361static LRESULT WINAPI
2362TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2363{
2364 switch (uMsg)
2365 {
2366 case TCM_GETIMAGELIST:
2367 return TAB_GetImageList (hwnd, wParam, lParam);
2368
2369 case TCM_SETIMAGELIST:
2370 return TAB_SetImageList (hwnd, wParam, lParam);
2371
2372 case TCM_GETITEMCOUNT:
2373 return TAB_GetItemCount (hwnd, wParam, lParam);
2374
2375 case TCM_GETITEMA:
2376 return TAB_GetItemA (hwnd, wParam, lParam);
2377
2378 case TCM_GETITEMW:
2379 return TAB_GetItemW(hwnd,wParam,lParam);
2380
2381 case TCM_SETITEMA:
2382 return TAB_SetItemA (hwnd, wParam, lParam);
2383
2384 case TCM_SETITEMW:
2385 return TAB_SetItemW(hwnd,wParam,lParam);
2386
2387 case TCM_DELETEITEM:
2388 return TAB_DeleteItem (hwnd, wParam, lParam);
2389
2390 case TCM_DELETEALLITEMS:
2391 return TAB_DeleteAllItems (hwnd, wParam, lParam);
2392
2393 case TCM_GETITEMRECT:
2394 return TAB_GetItemRect (hwnd, wParam, lParam);
2395
2396 case TCM_GETCURSEL:
2397 return TAB_GetCurSel (hwnd);
2398
2399 case TCM_HITTEST:
2400 return TAB_HitTest (hwnd, wParam, lParam);
2401
2402 case TCM_SETCURSEL:
2403 return TAB_SetCurSel (hwnd, wParam);
2404
2405 case TCM_INSERTITEMA:
2406 return TAB_InsertItemA(hwnd,wParam,lParam);
2407
2408 case TCM_INSERTITEMW:
2409 return TAB_InsertItemW(hwnd,wParam,lParam);
2410
2411 case TCM_SETITEMEXTRA:
2412// FIXME (tab, "Unimplemented msg TCM_SETITEMEXTRA\n");
2413 return 0;
2414
2415 case TCM_ADJUSTRECT:
2416 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2417
2418 case TCM_SETITEMSIZE:
2419 return TAB_SetItemSize (hwnd, wParam, lParam);
2420
2421 case TCM_REMOVEIMAGE:
2422// FIXME (tab, "Unimplemented msg TCM_REMOVEIMAGE\n");
2423 return 0;
2424
2425 case TCM_SETPADDING:
2426// FIXME (tab, "Unimplemented msg TCM_SETPADDING\n");
2427 return 0;
2428
2429 case TCM_GETROWCOUNT:
2430// FIXME (tab, "Unimplemented msg TCM_GETROWCOUNT\n");
2431 return 0;
2432
2433 case TCM_HIGHLIGHTITEM:
2434// FIXME (tab, "Unimplemented msg TCM_HIGHLIGHTITEM\n");
2435 return 0;
2436
2437 case TCM_GETTOOLTIPS:
2438 return TAB_GetToolTips (hwnd, wParam, lParam);
2439
2440 case TCM_SETTOOLTIPS:
2441 return TAB_SetToolTips (hwnd, wParam, lParam);
2442
2443 case TCM_GETCURFOCUS:
2444 return TAB_GetCurFocus (hwnd);
2445
2446 case TCM_SETCURFOCUS:
2447 return TAB_SetCurFocus (hwnd, wParam);
2448
2449 case TCM_SETMINTTABWIDTH:
2450// FIXME (tab, "Unimplemented msg TCM_SETMINTTABWIDTH\n");
2451 return 0;
2452
2453 case TCM_DESELECTALL:
2454// FIXME (tab, "Unimplemented msg TCM_DESELECTALL\n");
2455 return 0;
2456
2457 case TCM_GETEXTENDEDSTYLE:
2458// FIXME (tab, "Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2459 return 0;
2460
2461 case TCM_SETEXTENDEDSTYLE:
2462// FIXME (tab, "Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2463 return 0;
2464
2465 case WM_GETFONT:
2466 return TAB_GetFont (hwnd, wParam, lParam);
2467
2468 case WM_SETFONT:
2469 return TAB_SetFont (hwnd, wParam, lParam);
2470
2471 case WM_CREATE:
2472 return TAB_Create (hwnd, wParam, lParam);
2473
2474 case WM_NCDESTROY:
2475 return TAB_Destroy (hwnd, wParam, lParam);
2476
2477 case WM_GETDLGCODE:
2478 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2479
2480 case WM_LBUTTONDOWN:
2481 return TAB_LButtonDown (hwnd, wParam, lParam);
2482
2483 case WM_LBUTTONUP:
2484 return TAB_LButtonUp (hwnd, wParam, lParam);
2485
2486 case WM_RBUTTONDOWN:
2487 return TAB_RButtonDown (hwnd, wParam, lParam);
2488
2489 case WM_MOUSEMOVE:
2490 return TAB_MouseMove (hwnd, wParam, lParam);
2491
2492 case WM_ERASEBKGND:
2493 return TAB_EraseBackground (hwnd, (HDC)wParam);
2494
2495 case WM_PAINT:
2496 return TAB_Paint (hwnd, wParam);
2497
2498 case WM_SIZE:
2499 return TAB_Size (hwnd, wParam, lParam);
2500
2501 case WM_SETREDRAW:
2502 return TAB_SetRedraw (hwnd, wParam);
2503
2504 case WM_HSCROLL:
2505 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2506
2507 case WM_STYLECHANGED:
2508 return TAB_StyleChanged(hwnd,wParam,lParam);
2509
2510 case WM_KILLFOCUS:
2511 case WM_SETFOCUS:
2512 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2513
2514 case WM_KEYUP:
2515 return TAB_KeyUp(hwnd, wParam);
2516
2517 default:
2518// if (uMsg >= WM_USER)
2519// ERR (tab, "unknown msg %04x wp=%08x lp=%08lx\n",
2520// uMsg, wParam, lParam);
2521 return defComCtl32ProcA (hwnd, uMsg, wParam, lParam);
2522 }
2523
2524 return 0;
2525}
2526
2527
2528VOID TAB_Register(VOID)
2529{
2530 WNDCLASSA wndClass;
2531
2532 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2533 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2534 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2535 wndClass.cbClsExtra = 0;
2536 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2537 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2538 wndClass.hbrBackground = (HBRUSH)NULL;
2539 wndClass.lpszClassName = WC_TABCONTROLA;
2540
2541 RegisterClassA (&wndClass);
2542}
2543
2544
2545VOID TAB_Unregister(VOID)
2546{
2547 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);
2548}
2549
Note: See TracBrowser for help on using the repository browser.