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

Last change on this file since 10098 was 10098, checked in by sandervl, 22 years ago

Wine resync

File size: 88.4 KB
Line 
1/*
2 * Tab control
3 *
4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * TODO:
23 * Image list support
24 * Unicode support (under construction)
25 *
26 * FIXME:
27 * UpDown control not displayed until after a tab is clicked on
28 */
29
30#include <string.h>
31
32#include "winbase.h"
33#include "commctrl.h"
34#include "comctl32.h"
35#include "wine/debug.h"
36#include <math.h>
37
38WINE_DEFAULT_DEBUG_CHANNEL(tab);
39
40typedef struct
41{
42 UINT mask;
43 DWORD dwState;
44 LPWSTR pszText;
45 INT iImage;
46 LPARAM lParam;
47 RECT rect; /* bounding rectangle of the item relative to the
48 * leftmost item (the leftmost item, 0, would have a
49 * "left" member of 0 in this rectangle)
50 *
51 * additionally the top member hold the row number
52 * and bottom is unused and should be 0 */
53} TAB_ITEM;
54
55typedef struct
56{
57 UINT uNumItem; /* number of tab items */
58 UINT uNumRows; /* number of tab rows */
59 INT tabHeight; /* height of the tab row */
60 INT tabWidth; /* width of tabs */
61 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
62 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
63 HFONT hFont; /* handle to the current font */
64 HCURSOR hcurArrow; /* handle to the current cursor */
65 HIMAGELIST himl; /* handle to a image list (may be 0) */
66 HWND hwndToolTip; /* handle to tab's tooltip */
67 INT leftmostVisible; /* Used for scrolling, this member contains
68 * the index of the first visible item */
69 INT iSelected; /* the currently selected item */
70 INT iHotTracked; /* the highlighted item under the mouse */
71 INT uFocus; /* item which has the focus */
72 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
73 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
74 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
75 * the size of the control */
76 BOOL fSizeSet; /* was the size of the tabs explicitly set? */
77 BOOL bUnicode; /* Unicode control? */
78 HWND hwndUpDown; /* Updown control used for scrolling */
79} TAB_INFO;
80
81/******************************************************************************
82 * Positioning constants
83 */
84#define SELECTED_TAB_OFFSET 2
85#define ROUND_CORNER_SIZE 2
86#define DISPLAY_AREA_PADDINGX 2
87#define DISPLAY_AREA_PADDINGY 2
88#define CONTROL_BORDER_SIZEX 2
89#define CONTROL_BORDER_SIZEY 2
90#define BUTTON_SPACINGX 4
91#define BUTTON_SPACINGY 4
92#define FLAT_BTN_SPACINGX 8
93#define DEFAULT_TAB_WIDTH 96
94
95#define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
96
97/******************************************************************************
98 * Hot-tracking timer constants
99 */
100#define TAB_HOTTRACK_TIMER 1
101#define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
102
103/******************************************************************************
104 * Prototypes
105 */
106static void TAB_Refresh (HWND hwnd, HDC hdc);
107static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
108static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
109static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
110static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
111
112static BOOL
113TAB_SendSimpleNotify (HWND hwnd, UINT code)
114{
115 NMHDR nmhdr;
116
117 nmhdr.hwndFrom = hwnd;
118 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
119 nmhdr.code = code;
120
121 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
122 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
123}
124
125static VOID
126TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
127 WPARAM wParam, LPARAM lParam)
128{
129 MSG msg;
130
131 msg.hwnd = hwndMsg;
132 msg.message = uMsg;
133 msg.wParam = wParam;
134 msg.lParam = lParam;
135 msg.time = GetMessageTime ();
136 msg.pt.x = LOWORD(GetMessagePos ());
137 msg.pt.y = HIWORD(GetMessagePos ());
138
139 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
140}
141
142static void
143TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
144{
145 if (TRACE_ON(tab)) {
146 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
147 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
148 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
149 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
150 }
151}
152
153
154static void
155TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
156{
157 if (TRACE_ON(tab)) {
158 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
159 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
160 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
161 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
162 }
163}
164
165static void
166TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
167{
168 if (TRACE_ON(tab)) {
169 TAB_ITEM *ti;
170
171 ti = &infoPtr->items[iItem];
172 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
173 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
174 ti->iImage);
175 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
176 iItem, ti->lParam, ti->rect.left, ti->rect.top);
177 }
178}
179
180static LRESULT
181TAB_GetCurSel (HWND hwnd)
182{
183 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
184
185 return infoPtr->iSelected;
186}
187
188static LRESULT
189TAB_GetCurFocus (HWND hwnd)
190{
191 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
192
193 return infoPtr->uFocus;
194}
195
196static LRESULT
197TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
198{
199 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
200
201 if (infoPtr == NULL) return 0;
202 return (LRESULT)infoPtr->hwndToolTip;
203}
204
205static LRESULT
206TAB_SetCurSel (HWND hwnd,WPARAM wParam)
207{
208 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
209 INT iItem = (INT)wParam;
210 INT prevItem;
211
212 prevItem = -1;
213 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
214 prevItem=infoPtr->iSelected;
215 infoPtr->iSelected=iItem;
216 TAB_EnsureSelectionVisible(hwnd, infoPtr);
217 TAB_InvalidateTabArea(hwnd, infoPtr);
218 }
219 return prevItem;
220}
221
222static LRESULT
223TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
224{
225 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
226 INT iItem=(INT) wParam;
227
228 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
229
230 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
231 FIXME("Should set input focus\n");
232 } else {
233 int oldFocus = infoPtr->uFocus;
234 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
235 infoPtr->uFocus = iItem;
236 if (oldFocus != -1) {
237 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
238 infoPtr->iSelected = iItem;
239 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
240 }
241 else
242 infoPtr->iSelected = iItem;
243 TAB_EnsureSelectionVisible(hwnd, infoPtr);
244 TAB_InvalidateTabArea(hwnd, infoPtr);
245 }
246 }
247 }
248 return 0;
249}
250
251static LRESULT
252TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
253{
254 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
255
256 if (infoPtr == NULL) return 0;
257 infoPtr->hwndToolTip = (HWND)wParam;
258 return 0;
259}
260
261static LRESULT
262TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
263{
264 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
265
266 if (infoPtr == NULL) return 0;
267 infoPtr->uHItemPadding=LOWORD(lParam);
268 infoPtr->uVItemPadding=HIWORD(lParam);
269 return 0;
270}
271
272/******************************************************************************
273 * TAB_InternalGetItemRect
274 *
275 * This method will calculate the rectangle representing a given tab item in
276 * client coordinates. This method takes scrolling into account.
277 *
278 * This method returns TRUE if the item is visible in the window and FALSE
279 * if it is completely outside the client area.
280 */
281static BOOL TAB_InternalGetItemRect(
282 HWND hwnd,
283 TAB_INFO* infoPtr,
284 INT itemIndex,
285 RECT* itemRect,
286 RECT* selectedRect)
287{
288 RECT tmpItemRect,clientRect;
289 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
290
291 /* Perform a sanity check and a trivial visibility check. */
292 if ( (infoPtr->uNumItem <= 0) ||
293 (itemIndex >= infoPtr->uNumItem) ||
294 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
295 return FALSE;
296
297 /*
298 * Avoid special cases in this procedure by assigning the "out"
299 * parameters if the caller didn't supply them
300 */
301 if (itemRect == NULL)
302 itemRect = &tmpItemRect;
303
304 /* Retrieve the unmodified item rect. */
305 *itemRect = infoPtr->items[itemIndex].rect;
306
307 /* calculate the times bottom and top based on the row */
308 GetClientRect(hwnd, &clientRect);
309
310 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
311 {
312 itemRect->bottom = clientRect.bottom -
313 SELECTED_TAB_OFFSET -
314 itemRect->top * (infoPtr->tabHeight - 2) -
315 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
316
317 itemRect->top = clientRect.bottom -
318 infoPtr->tabHeight -
319 itemRect->top * (infoPtr->tabHeight - 2) -
320 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
321 }
322 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
323 {
324 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
325 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
326 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
327 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
328 }
329 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
330 {
331 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
332 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
333 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
334 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
335 }
336 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
337 {
338 itemRect->bottom = clientRect.top +
339 infoPtr->tabHeight +
340 itemRect->top * (infoPtr->tabHeight - 2) +
341 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
342 itemRect->top = clientRect.top +
343 SELECTED_TAB_OFFSET +
344 itemRect->top * (infoPtr->tabHeight - 2) +
345 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
346 }
347
348 /*
349 * "scroll" it to make sure the item at the very left of the
350 * tab control is the leftmost visible tab.
351 */
352 if(lStyle & TCS_VERTICAL)
353 {
354 OffsetRect(itemRect,
355 0,
356 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
357
358 /*
359 * Move the rectangle so the first item is slightly offset from
360 * the bottom of the tab control.
361 */
362 OffsetRect(itemRect,
363 0,
364 -SELECTED_TAB_OFFSET);
365
366 } else
367 {
368 OffsetRect(itemRect,
369 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
370 0);
371
372 /*
373 * Move the rectangle so the first item is slightly offset from
374 * the left of the tab control.
375 */
376 OffsetRect(itemRect,
377 SELECTED_TAB_OFFSET,
378 0);
379 }
380 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
381 itemIndex, infoPtr->tabHeight,
382 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
383
384 /* Now, calculate the position of the item as if it were selected. */
385 if (selectedRect!=NULL)
386 {
387 CopyRect(selectedRect, itemRect);
388
389 /* The rectangle of a selected item is a bit wider. */
390 if(lStyle & TCS_VERTICAL)
391 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
392 else
393 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
394
395 /* If it also a bit higher. */
396 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
397 {
398 selectedRect->top -= 2; /* the border is thicker on the bottom */
399 selectedRect->bottom += SELECTED_TAB_OFFSET;
400 }
401 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
402 {
403 selectedRect->left -= 2; /* the border is thicker on the right */
404 selectedRect->right += SELECTED_TAB_OFFSET;
405 }
406 else if(lStyle & TCS_VERTICAL)
407 {
408 selectedRect->left -= SELECTED_TAB_OFFSET;
409 selectedRect->right += 1;
410 }
411 else
412 {
413 selectedRect->top -= SELECTED_TAB_OFFSET;
414 selectedRect->bottom += 1;
415 }
416 }
417
418 return TRUE;
419}
420
421static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
422{
423 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
424 (LPRECT)lParam, (LPRECT)NULL);
425}
426
427/******************************************************************************
428 * TAB_KeyUp
429 *
430 * This method is called to handle keyboard input
431 */
432static LRESULT TAB_KeyUp(
433 HWND hwnd,
434 WPARAM keyCode)
435{
436 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
437 int newItem = -1;
438
439 switch (keyCode)
440 {
441 case VK_LEFT:
442 newItem = infoPtr->uFocus - 1;
443 break;
444 case VK_RIGHT:
445 newItem = infoPtr->uFocus + 1;
446 break;
447 }
448
449 /*
450 * If we changed to a valid item, change the selection
451 */
452 if ((newItem >= 0) &&
453 (newItem < infoPtr->uNumItem) &&
454 (infoPtr->uFocus != newItem))
455 {
456 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
457 {
458 infoPtr->iSelected = newItem;
459 infoPtr->uFocus = newItem;
460 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
461
462 TAB_EnsureSelectionVisible(hwnd, infoPtr);
463 TAB_InvalidateTabArea(hwnd, infoPtr);
464 }
465 }
466
467 return 0;
468}
469
470/******************************************************************************
471 * TAB_FocusChanging
472 *
473 * This method is called whenever the focus goes in or out of this control
474 * it is used to update the visual state of the control.
475 */
476static LRESULT TAB_FocusChanging(
477 HWND hwnd,
478 UINT uMsg,
479 WPARAM wParam,
480 LPARAM lParam)
481{
482 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
483 RECT selectedRect;
484 BOOL isVisible;
485
486 /*
487 * Get the rectangle for the item.
488 */
489 isVisible = TAB_InternalGetItemRect(hwnd,
490 infoPtr,
491 infoPtr->uFocus,
492 NULL,
493 &selectedRect);
494
495 /*
496 * If the rectangle is not completely invisible, invalidate that
497 * portion of the window.
498 */
499 if (isVisible)
500 {
501 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
502 selectedRect.left,selectedRect.top,
503 selectedRect.right,selectedRect.bottom);
504 InvalidateRect(hwnd, &selectedRect, TRUE);
505 }
506
507 /*
508 * Don't otherwise disturb normal behavior.
509 */
510 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
511}
512
513static INT TAB_InternalHitTest (
514 HWND hwnd,
515 TAB_INFO* infoPtr,
516 POINT pt,
517 UINT* flags)
518
519{
520 RECT rect;
521 INT iCount;
522
523 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
524 {
525 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
526
527 if (PtInRect(&rect, pt))
528 {
529 *flags = TCHT_ONITEM;
530 return iCount;
531 }
532 }
533
534 *flags = TCHT_NOWHERE;
535 return -1;
536}
537
538static LRESULT
539TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
540{
541 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
542 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
543
544 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
545}
546
547/******************************************************************************
548 * TAB_NCHitTest
549 *
550 * Napster v2b5 has a tab control for its main navigation which has a client
551 * area that covers the whole area of the dialog pages.
552 * That's why it receives all msgs for that area and the underlying dialog ctrls
553 * are dead.
554 * So I decided that we should handle WM_NCHITTEST here and return
555 * HTTRANSPARENT if we don't hit the tab control buttons.
556 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
557 * doesn't do it that way. Maybe depends on tab control styles ?
558 */
559static LRESULT
560TAB_NCHitTest (HWND hwnd, LPARAM lParam)
561{
562 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
563 POINT pt;
564 UINT dummyflag;
565
566 pt.x = LOWORD(lParam);
567 pt.y = HIWORD(lParam);
568 ScreenToClient(hwnd, &pt);
569
570 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
571 return HTTRANSPARENT;
572 else
573 return HTCLIENT;
574}
575
576static LRESULT
577TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
578{
579 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
580 POINT pt;
581 INT newItem, dummy;
582
583 if (infoPtr->hwndToolTip)
584 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
585 WM_LBUTTONDOWN, wParam, lParam);
586
587 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
588 SetFocus (hwnd);
589 }
590
591 if (infoPtr->hwndToolTip)
592 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
593 WM_LBUTTONDOWN, wParam, lParam);
594
595 pt.x = (INT)LOWORD(lParam);
596 pt.y = (INT)HIWORD(lParam);
597
598 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
599
600 TRACE("On Tab, item %d\n", newItem);
601
602 if ((newItem != -1) && (infoPtr->iSelected != newItem))
603 {
604 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
605 {
606 infoPtr->iSelected = newItem;
607 infoPtr->uFocus = newItem;
608 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
609
610 TAB_EnsureSelectionVisible(hwnd, infoPtr);
611
612 TAB_InvalidateTabArea(hwnd, infoPtr);
613 }
614 }
615 return 0;
616}
617
618static LRESULT
619TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
620{
621 TAB_SendSimpleNotify(hwnd, NM_CLICK);
622
623 return 0;
624}
625
626static LRESULT
627TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
628{
629 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
630 return 0;
631}
632
633/******************************************************************************
634 * TAB_DrawLoneItemInterior
635 *
636 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
637 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
638 * up the device context and font. This routine does the same setup but
639 * only calls TAB_DrawItemInterior for the single specified item.
640 */
641static void
642TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
643{
644 HDC hdc = GetDC(hwnd);
645 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
646 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
647 SelectObject(hdc, hOldFont);
648 ReleaseDC(hwnd, hdc);
649}
650
651/******************************************************************************
652 * TAB_HotTrackTimerProc
653 *
654 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
655 * timer is setup so we can check if the mouse is moved out of our window.
656 * (We don't get an event when the mouse leaves, the mouse-move events just
657 * stop being delivered to our window and just start being delivered to
658 * another window.) This function is called when the timer triggers so
659 * we can check if the mouse has left our window. If so, we un-highlight
660 * the hot-tracked tab.
661 */
662static VOID CALLBACK
663TAB_HotTrackTimerProc
664 (
665 HWND hwnd, /* handle of window for timer messages */
666 UINT uMsg, /* WM_TIMER message */
667 UINT idEvent, /* timer identifier */
668 DWORD dwTime /* current system time */
669 )
670{
671 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
672
673 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
674 {
675 POINT pt;
676
677 /*
678 ** If we can't get the cursor position, or if the cursor is outside our
679 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
680 ** "outside" even if it is within our bounding rect if another window
681 ** overlaps. Note also that the case where the cursor stayed within our
682 ** window but has moved off the hot-tracked tab will be handled by the
683 ** WM_MOUSEMOVE event.
684 */
685 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
686 {
687 /* Redraw iHotTracked to look normal */
688 INT iRedraw = infoPtr->iHotTracked;
689 infoPtr->iHotTracked = -1;
690 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
691
692 /* Kill this timer */
693 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
694 }
695 }
696}
697
698/******************************************************************************
699 * TAB_RecalcHotTrack
700 *
701 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
702 * should be highlighted. This function determines which tab in a tab control,
703 * if any, is under the mouse and records that information. The caller may
704 * supply output parameters to receive the item number of the tab item which
705 * was highlighted but isn't any longer and of the tab item which is now
706 * highlighted but wasn't previously. The caller can use this information to
707 * selectively redraw those tab items.
708 *
709 * If the caller has a mouse position, it can supply it through the pos
710 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
711 * supplies NULL and this function determines the current mouse position
712 * itself.
713 */
714static void
715TAB_RecalcHotTrack
716 (
717 HWND hwnd,
718 const LPARAM* pos,
719 int* out_redrawLeave,
720 int* out_redrawEnter
721 )
722{
723 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
724
725 int item = -1;
726
727
728 if (out_redrawLeave != NULL)
729 *out_redrawLeave = -1;
730 if (out_redrawEnter != NULL)
731 *out_redrawEnter = -1;
732
733 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
734 {
735 POINT pt;
736 UINT flags;
737
738 if (pos == NULL)
739 {
740 GetCursorPos(&pt);
741 ScreenToClient(hwnd, &pt);
742 }
743 else
744 {
745 pt.x = LOWORD(*pos);
746 pt.y = HIWORD(*pos);
747 }
748
749 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
750 }
751
752 if (item != infoPtr->iHotTracked)
753 {
754 if (infoPtr->iHotTracked >= 0)
755 {
756 /* Mark currently hot-tracked to be redrawn to look normal */
757 if (out_redrawLeave != NULL)
758 *out_redrawLeave = infoPtr->iHotTracked;
759
760 if (item < 0)
761 {
762 /* Kill timer which forces recheck of mouse pos */
763 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
764 }
765 }
766 else
767 {
768 /* Start timer so we recheck mouse pos */
769 UINT timerID = SetTimer
770 (
771 hwnd,
772 TAB_HOTTRACK_TIMER,
773 TAB_HOTTRACK_TIMER_INTERVAL,
774 TAB_HotTrackTimerProc
775 );
776
777 if (timerID == 0)
778 return; /* Hot tracking not available */
779 }
780
781 infoPtr->iHotTracked = item;
782
783 if (item >= 0)
784 {
785 /* Mark new hot-tracked to be redrawn to look highlighted */
786 if (out_redrawEnter != NULL)
787 *out_redrawEnter = item;
788 }
789 }
790}
791
792/******************************************************************************
793 * TAB_MouseMove
794 *
795 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
796 */
797static LRESULT
798TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
799{
800 int redrawLeave;
801 int redrawEnter;
802
803 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
804
805 if (infoPtr->hwndToolTip)
806 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
807 WM_LBUTTONDOWN, wParam, lParam);
808
809 /* Determine which tab to highlight. Redraw tabs which change highlight
810 ** status. */
811 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
812
813 if (redrawLeave != -1)
814 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
815 if (redrawEnter != -1)
816 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
817
818 return 0;
819}
820
821/******************************************************************************
822 * TAB_AdjustRect
823 *
824 * Calculates the tab control's display area given the window rectangle or
825 * the window rectangle given the requested display rectangle.
826 */
827static LRESULT TAB_AdjustRect(
828 HWND hwnd,
829 WPARAM fLarger,
830 LPRECT prc)
831{
832 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
833 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
834
835 if(lStyle & TCS_VERTICAL)
836 {
837 if (fLarger) /* Go from display rectangle */
838 {
839 /* Add the height of the tabs. */
840 if (lStyle & TCS_BOTTOM)
841 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
842 else
843 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
844
845 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
846 /* Inflate the rectangle for the padding */
847 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
848
849 /* Inflate for the border */
850 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
851 }
852 else /* Go from window rectangle. */
853 {
854 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
855 /* Deflate the rectangle for the border */
856 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
857
858 /* Deflate the rectangle for the padding */
859 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
860
861 /* Remove the height of the tabs. */
862 if (lStyle & TCS_BOTTOM)
863 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
864 else
865 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
866 }
867 }
868 else {
869 if (fLarger) /* Go from display rectangle */
870 {
871 /* Add the height of the tabs. */
872 if (lStyle & TCS_BOTTOM)
873 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
874 else
875 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
876
877 /* Inflate the rectangle for the padding */
878 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
879
880 /* Inflate for the border */
881 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
882 }
883 else /* Go from window rectangle. */
884 {
885 /* Deflate the rectangle for the border */
886 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
887
888 /* Deflate the rectangle for the padding */
889 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
890
891 /* Remove the height of the tabs. */
892 if (lStyle & TCS_BOTTOM)
893 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
894 else
895 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
896 }
897 }
898
899 return 0;
900}
901
902/******************************************************************************
903 * TAB_OnHScroll
904 *
905 * This method will handle the notification from the scroll control and
906 * perform the scrolling operation on the tab control.
907 */
908static LRESULT TAB_OnHScroll(
909 HWND hwnd,
910 int nScrollCode,
911 int nPos,
912 HWND hwndScroll)
913{
914 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
915
916 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
917 {
918 if(nPos < infoPtr->leftmostVisible)
919 infoPtr->leftmostVisible--;
920 else
921 infoPtr->leftmostVisible++;
922
923 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
924 TAB_InvalidateTabArea(hwnd, infoPtr);
925 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
926 MAKELONG(infoPtr->leftmostVisible, 0));
927 }
928
929 return 0;
930}
931
932/******************************************************************************
933 * TAB_SetupScrolling
934 *
935 * This method will check the current scrolling state and make sure the
936 * scrolling control is displayed (or not).
937 */
938static void TAB_SetupScrolling(
939 HWND hwnd,
940 TAB_INFO* infoPtr,
941 const RECT* clientRect)
942{
943 INT maxRange = 0;
944 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
945
946 if (infoPtr->needsScrolling)
947 {
948 RECT controlPos;
949 INT vsize, tabwidth;
950
951 /*
952 * Calculate the position of the scroll control.
953 */
954 if(lStyle & TCS_VERTICAL)
955 {
956 controlPos.right = clientRect->right;
957 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
958
959 if (lStyle & TCS_BOTTOM)
960 {
961 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
962 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
963 }
964 else
965 {
966 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
967 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
968 }
969 }
970 else
971 {
972 controlPos.right = clientRect->right;
973 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
974
975 if (lStyle & TCS_BOTTOM)
976 {
977 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
978 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
979 }
980 else
981 {
982 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
983 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
984 }
985 }
986
987 /*
988 * If we don't have a scroll control yet, we want to create one.
989 * If we have one, we want to make sure it's positioned properly.
990 */
991 if (infoPtr->hwndUpDown==0)
992 {
993 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
994 "",
995 WS_VISIBLE | WS_CHILD | UDS_HORZ,
996 controlPos.left, controlPos.top,
997 controlPos.right - controlPos.left,
998 controlPos.bottom - controlPos.top,
999 hwnd,
1000 NULL,
1001 NULL,
1002 NULL);
1003 }
1004 else
1005 {
1006 SetWindowPos(infoPtr->hwndUpDown,
1007 NULL,
1008 controlPos.left, controlPos.top,
1009 controlPos.right - controlPos.left,
1010 controlPos.bottom - controlPos.top,
1011 SWP_SHOWWINDOW | SWP_NOZORDER);
1012 }
1013
1014 /* Now calculate upper limit of the updown control range.
1015 * We do this by calculating how many tabs will be offscreen when the
1016 * last tab is visible.
1017 */
1018 if(infoPtr->uNumItem)
1019 {
1020 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1021 maxRange = infoPtr->uNumItem;
1022 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1023
1024 for(; maxRange > 0; maxRange--)
1025 {
1026 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1027 break;
1028 }
1029
1030 if(maxRange == infoPtr->uNumItem)
1031 maxRange--;
1032 }
1033 }
1034 else
1035 {
1036 /* If we once had a scroll control... hide it */
1037 if (infoPtr->hwndUpDown!=0)
1038 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1039 }
1040 if (infoPtr->hwndUpDown)
1041 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1042}
1043
1044/******************************************************************************
1045 * TAB_SetItemBounds
1046 *
1047 * This method will calculate the position rectangles of all the items in the
1048 * control. The rectangle calculated starts at 0 for the first item in the
1049 * list and ignores scrolling and selection.
1050 * It also uses the current font to determine the height of the tab row and
1051 * it checks if all the tabs fit in the client area of the window. If they
1052 * dont, a scrolling control is added.
1053 */
1054static void TAB_SetItemBounds (HWND hwnd)
1055{
1056 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1057 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1058 TEXTMETRICA fontMetrics;
1059 INT curItem;
1060 INT curItemLeftPos;
1061 INT curItemRowCount;
1062 HFONT hFont, hOldFont;
1063 HDC hdc;
1064 RECT clientRect;
1065 SIZE size;
1066 INT iTemp;
1067 RECT* rcItem;
1068 INT iIndex;
1069
1070 /*
1071 * We need to get text information so we need a DC and we need to select
1072 * a font.
1073 */
1074 hdc = GetDC(hwnd);
1075
1076 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1077 hOldFont = SelectObject (hdc, hFont);
1078
1079 /*
1080 * We will base the rectangle calculations on the client rectangle
1081 * of the control.
1082 */
1083 GetClientRect(hwnd, &clientRect);
1084
1085 /* if TCS_VERTICAL then swap the height and width so this code places the
1086 tabs along the top of the rectangle and we can just rotate them after
1087 rather than duplicate all of the below code */
1088 if(lStyle & TCS_VERTICAL)
1089 {
1090 iTemp = clientRect.bottom;
1091 clientRect.bottom = clientRect.right;
1092 clientRect.right = iTemp;
1093 }
1094
1095 /* The leftmost item will be "0" aligned */
1096 curItemLeftPos = 0;
1097 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1098
1099 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
1100 {
1101 int item_height;
1102 int icon_height = 0;
1103
1104 /* Use the current font to determine the height of a tab. */
1105 GetTextMetricsA(hdc, &fontMetrics);
1106
1107 /* Get the icon height */
1108 if (infoPtr->himl)
1109 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1110
1111 /* Take the highest between font or icon */
1112 if (fontMetrics.tmHeight > icon_height)
1113 item_height = fontMetrics.tmHeight + 2;
1114 else
1115 item_height = icon_height;
1116
1117 /*
1118 * Make sure there is enough space for the letters + icon + growing the
1119 * selected item + extra space for the selected item.
1120 */
1121 infoPtr->tabHeight = item_height + SELECTED_TAB_OFFSET +
1122 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1123 infoPtr->uVItemPadding;
1124
1125 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1126 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1127 }
1128
1129 TRACE("client right=%ld\n", clientRect.right);
1130
1131 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1132 {
1133 /* Set the leftmost position of the tab. */
1134 infoPtr->items[curItem].rect.left = curItemLeftPos;
1135
1136 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1137 {
1138 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1139 infoPtr->tabWidth +
1140 2 * infoPtr->uHItemPadding;
1141 }
1142 else
1143 {
1144 int icon_width = 0;
1145 int num = 2;
1146
1147 /* Calculate how wide the tab is depending on the text it contains */
1148 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1149 lstrlenW(infoPtr->items[curItem].pszText), &size);
1150
1151 /* under Windows, there seems to be a minimum width of 2x the height
1152 * for button style tabs */
1153 if (lStyle & TCS_BUTTONS)
1154 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1155
1156 /* Add the icon width */
1157 if (infoPtr->himl)
1158 {
1159 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1160 num++;
1161 }
1162
1163 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1164 size.cx + icon_width +
1165 num * infoPtr->uHItemPadding;
1166 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1167 debugstr_w(infoPtr->items[curItem].pszText),
1168 infoPtr->items[curItem].rect.left,
1169 infoPtr->items[curItem].rect.right,
1170 num);
1171 }
1172
1173 /*
1174 * Check if this is a multiline tab control and if so
1175 * check to see if we should wrap the tabs
1176 *
1177 * Because we are going to arange all these tabs evenly
1178 * really we are basically just counting rows at this point
1179 *
1180 */
1181
1182 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1183 (infoPtr->items[curItem].rect.right > clientRect.right))
1184 {
1185 infoPtr->items[curItem].rect.right -=
1186 infoPtr->items[curItem].rect.left;
1187
1188 infoPtr->items[curItem].rect.left = 0;
1189 curItemRowCount++;
1190 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1191 debugstr_w(infoPtr->items[curItem].pszText),
1192 infoPtr->items[curItem].rect.left,
1193 infoPtr->items[curItem].rect.right);
1194 }
1195
1196 infoPtr->items[curItem].rect.bottom = 0;
1197 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1198
1199 TRACE("TextSize: %li\n", size.cx);
1200 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1201 infoPtr->items[curItem].rect.top,
1202 infoPtr->items[curItem].rect.left,
1203 infoPtr->items[curItem].rect.bottom,
1204 infoPtr->items[curItem].rect.right);
1205
1206 /*
1207 * The leftmost position of the next item is the rightmost position
1208 * of this one.
1209 */
1210 if (lStyle & TCS_BUTTONS)
1211 {
1212 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1213 if (lStyle & TCS_FLATBUTTONS)
1214 curItemLeftPos += FLAT_BTN_SPACINGX;
1215 }
1216 else
1217 curItemLeftPos = infoPtr->items[curItem].rect.right;
1218 }
1219
1220 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1221 {
1222 /*
1223 * Check if we need a scrolling control.
1224 */
1225 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1226 clientRect.right);
1227
1228 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1229 if(!infoPtr->needsScrolling)
1230 infoPtr->leftmostVisible = 0;
1231
1232 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1233 }
1234
1235 /* Set the number of rows */
1236 infoPtr->uNumRows = curItemRowCount;
1237
1238 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1239 {
1240 INT widthDiff, remainder;
1241 INT tabPerRow,remTab;
1242 INT iRow,iItm;
1243 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1244
1245 /*
1246 * Ok windows tries to even out the rows. place the same
1247 * number of tabs in each row. So lets give that a shot
1248 */
1249
1250 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1251 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1252
1253 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1254 iItm<infoPtr->uNumItem;
1255 iItm++,iCount++)
1256 {
1257 /* normalize the current rect */
1258
1259 /* shift the item to the left side of the clientRect */
1260 infoPtr->items[iItm].rect.right -=
1261 infoPtr->items[iItm].rect.left;
1262 infoPtr->items[iItm].rect.left = 0;
1263
1264 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1265 infoPtr->items[iItm].rect.right,
1266 curItemLeftPos, clientRect.right,
1267 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1268
1269 /* if we have reached the maximum number of tabs on this row */
1270 /* move to the next row, reset our current item left position and */
1271 /* the count of items on this row */
1272
1273 /* ************ FIXME FIXME FIXME *************** */
1274 /* */
1275 /* FIXME: */
1276 /* if vertical, */
1277 /* if item n and n+1 are in the same row, */
1278 /* then the display has n+1 lower (toward the */
1279 /* bottom) than n. We do it just the */
1280 /* opposite!!! */
1281 /* */
1282 /* ************ FIXME FIXME FIXME *************** */
1283
1284 if (lStyle & TCS_VERTICAL) {
1285 /* Vert: Add the remaining tabs in the *last* remainder rows */
1286 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1287 iRow++;
1288 curItemLeftPos = 0;
1289 iCount = 0;
1290 }
1291 } else {
1292 /* Horz: Add the remaining tabs in the *first* remainder rows */
1293 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1294 iRow++;
1295 curItemLeftPos = 0;
1296 iCount = 0;
1297 }
1298 }
1299
1300 /* shift the item to the right to place it as the next item in this row */
1301 infoPtr->items[iItm].rect.left += curItemLeftPos;
1302 infoPtr->items[iItm].rect.right += curItemLeftPos;
1303 infoPtr->items[iItm].rect.top = iRow;
1304 if (lStyle & TCS_BUTTONS)
1305 {
1306 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1307 if (lStyle & TCS_FLATBUTTONS)
1308 curItemLeftPos += FLAT_BTN_SPACINGX;
1309 }
1310 else
1311 curItemLeftPos = infoPtr->items[iItm].rect.right;
1312
1313 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1314 debugstr_w(infoPtr->items[iItm].pszText),
1315 infoPtr->items[iItm].rect.left,
1316 infoPtr->items[iItm].rect.right,
1317 infoPtr->items[iItm].rect.top);
1318 }
1319
1320 /*
1321 * Justify the rows
1322 */
1323 {
1324 while(iIndexStart < infoPtr->uNumItem)
1325 {
1326 /*
1327 * find the indexs of the row
1328 */
1329 /* find the first item on the next row */
1330 for (iIndexEnd=iIndexStart;
1331 (iIndexEnd < infoPtr->uNumItem) &&
1332 (infoPtr->items[iIndexEnd].rect.top ==
1333 infoPtr->items[iIndexStart].rect.top) ;
1334 iIndexEnd++)
1335 /* intentionally blank */;
1336
1337 /*
1338 * we need to justify these tabs so they fill the whole given
1339 * client area
1340 *
1341 */
1342 /* find the amount of space remaining on this row */
1343 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1344 infoPtr->items[iIndexEnd - 1].rect.right;
1345
1346 /* iCount is the number of tab items on this row */
1347 iCount = iIndexEnd - iIndexStart;
1348
1349
1350 if (iCount > 1)
1351 {
1352 remainder = widthDiff % iCount;
1353 widthDiff = widthDiff / iCount;
1354 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1355 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1356 iIndex++,iCount++)
1357 {
1358 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1359 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1360
1361 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1362 debugstr_w(infoPtr->items[iIndex].pszText),
1363 infoPtr->items[iIndex].rect.left,
1364 infoPtr->items[iIndex].rect.right);
1365
1366 }
1367 infoPtr->items[iIndex - 1].rect.right += remainder;
1368 }
1369 else /* we have only one item on this row, make it take up the entire row */
1370 {
1371 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1372 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1373
1374 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1375 debugstr_w(infoPtr->items[iIndexStart].pszText),
1376 infoPtr->items[iIndexStart].rect.left,
1377 infoPtr->items[iIndexStart].rect.right);
1378
1379 }
1380
1381
1382 iIndexStart = iIndexEnd;
1383 }
1384 }
1385 }
1386
1387 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1388 if(lStyle & TCS_VERTICAL)
1389 {
1390 RECT rcOriginal;
1391 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1392 {
1393 rcItem = &(infoPtr->items[iIndex].rect);
1394
1395 rcOriginal = *rcItem;
1396
1397 /* this is rotating the items by 90 degrees around the center of the control */
1398 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1399 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1400 rcItem->left = rcOriginal.top;
1401 rcItem->right = rcOriginal.bottom;
1402 }
1403 }
1404
1405 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1406 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1407
1408 /* Cleanup */
1409 SelectObject (hdc, hOldFont);
1410 ReleaseDC (hwnd, hdc);
1411}
1412
1413/******************************************************************************
1414 * TAB_DrawItemInterior
1415 *
1416 * This method is used to draw the interior (text and icon) of a single tab
1417 * into the tab control.
1418 */
1419static void
1420TAB_DrawItemInterior
1421 (
1422 HWND hwnd,
1423 HDC hdc,
1424 INT iItem,
1425 RECT* drawRect
1426 )
1427{
1428 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1429 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1430
1431 RECT localRect;
1432
1433 HPEN htextPen;
1434 HPEN holdPen;
1435 INT oldBkMode;
1436
1437 if (drawRect == NULL)
1438 {
1439 BOOL isVisible;
1440 RECT itemRect;
1441 RECT selectedRect;
1442
1443 /*
1444 * Get the rectangle for the item.
1445 */
1446 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1447 if (!isVisible)
1448 return;
1449
1450 /*
1451 * Make sure drawRect points to something valid; simplifies code.
1452 */
1453 drawRect = &localRect;
1454
1455 /*
1456 * This logic copied from the part of TAB_DrawItem which draws
1457 * the tab background. It's important to keep it in sync. I
1458 * would have liked to avoid code duplication, but couldn't figure
1459 * out how without making spaghetti of TAB_DrawItem.
1460 */
1461 if (lStyle & TCS_BUTTONS)
1462 {
1463 *drawRect = itemRect;
1464 if (iItem == infoPtr->iSelected)
1465 {
1466 drawRect->right--;
1467 drawRect->bottom--;
1468 }
1469 }
1470 else
1471 {
1472 if (iItem == infoPtr->iSelected)
1473 *drawRect = selectedRect;
1474 else
1475 *drawRect = itemRect;
1476 drawRect->right--;
1477 drawRect->bottom--;
1478 }
1479 }
1480
1481 /*
1482 * Text pen
1483 */
1484 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1485 holdPen = SelectObject(hdc, htextPen);
1486
1487 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1488 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1489 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1490
1491
1492 /*
1493 * if owner draw, tell the owner to draw
1494 */
1495 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1496 {
1497 DRAWITEMSTRUCT dis;
1498 UINT id;
1499
1500 /*
1501 * get the control id
1502 */
1503 id = GetWindowLongA( hwnd, GWL_ID );
1504
1505 /*
1506 * put together the DRAWITEMSTRUCT
1507 */
1508 dis.CtlType = ODT_TAB;
1509 dis.CtlID = id;
1510 dis.itemID = iItem;
1511 dis.itemAction = ODA_DRAWENTIRE;
1512 dis.itemState = 0;
1513 if ( iItem == infoPtr->iSelected )
1514 dis.itemState |= ODS_SELECTED;
1515 if (infoPtr->uFocus == iItem)
1516 dis.itemState |= ODS_FOCUS;
1517 dis.hwndItem = hwnd; /* */
1518 dis.hDC = hdc;
1519 CopyRect(&dis.rcItem,drawRect);
1520 dis.itemData = infoPtr->items[iItem].lParam;
1521
1522 /*
1523 * send the draw message
1524 */
1525 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1526 }
1527 else
1528 {
1529 INT cx;
1530 INT cy;
1531 UINT uHorizAlign;
1532 RECT rcTemp;
1533 RECT rcImage;
1534 LOGFONTA logfont;
1535 HFONT hFont = 0;
1536 HFONT hOldFont = 0; /* stop uninitialized warning */
1537
1538 INT nEscapement = 0; /* stop uninitialized warning */
1539 INT nOrientation = 0; /* stop uninitialized warning */
1540 INT iPointSize;
1541
1542 /* used to center the icon and text in the tab */
1543 RECT rcText;
1544 INT center_offset;
1545
1546 /*
1547 * Deflate the rectangle to acount for the padding
1548 */
1549 if(lStyle & TCS_VERTICAL)
1550 InflateRect(drawRect, -infoPtr->uVItemPadding, -infoPtr->uHItemPadding);
1551 else
1552 InflateRect(drawRect, -infoPtr->uHItemPadding, -infoPtr->uVItemPadding);
1553
1554 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1555 rcImage = *drawRect;
1556
1557 rcTemp = *drawRect;
1558
1559 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1560
1561 /*
1562 * Setup for text output
1563 */
1564 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1565 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1566 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1567
1568 /* get the rectangle that the text fits in */
1569 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1570 &rcText, DT_CALCRECT);
1571 rcText.right += 4;
1572 /*
1573 * If not owner draw, then do the drawing ourselves.
1574 *
1575 * Draw the icon.
1576 */
1577 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1578 {
1579 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1580
1581 if(lStyle & TCS_VERTICAL)
1582 center_offset = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1583 else
1584 center_offset = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1585
1586 TRACE("for <%s>, c_o=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1587 debugstr_w(infoPtr->items[iItem].pszText), center_offset,
1588 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1589 (rcText.right-rcText.left));
1590
1591 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1592 {
1593 rcImage.top = drawRect->top + center_offset;
1594 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1595 /* right side of the tab, but the image still uses the left as its x position */
1596 /* this keeps the image always drawn off of the same side of the tab */
1597 drawRect->top = rcImage.top + (cx + infoPtr->uHItemPadding);
1598 }
1599 else if(lStyle & TCS_VERTICAL)
1600 {
1601 rcImage.top = drawRect->bottom - cy - center_offset;
1602 rcImage.left--;
1603 drawRect->bottom = rcImage.top - infoPtr->uHItemPadding;
1604 }
1605 else /* normal style, whether TCS_BOTTOM or not */
1606 {
1607 rcImage.left = drawRect->left + center_offset + 3;
1608 drawRect->left = rcImage.left + cx + infoPtr->uHItemPadding;
1609 rcImage.top -= (lStyle & TCS_BOTTOM) ? 2 : 1;
1610 }
1611
1612 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1613 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1614 ImageList_Draw
1615 (
1616 infoPtr->himl,
1617 infoPtr->items[iItem].iImage,
1618 hdc,
1619 rcImage.left,
1620 rcImage.top,
1621 ILD_NORMAL
1622 );
1623 } else /* no image, so just shift the drawRect borders around */
1624 {
1625 if(lStyle & TCS_VERTICAL)
1626 {
1627 center_offset = 0;
1628 /*
1629 currently the rcText rect is flawed because the rotated font does not
1630 often match the horizontal font. So leave this as 0
1631 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1632 */
1633 if(lStyle & TCS_BOTTOM)
1634 drawRect->top+=center_offset;
1635 else
1636 drawRect->bottom-=center_offset;
1637 }
1638 else
1639 {
1640 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1641 drawRect->left+=center_offset;
1642 }
1643 }
1644
1645 /* Draw the text */
1646 if (lStyle & TCS_RIGHTJUSTIFY)
1647 uHorizAlign = DT_CENTER;
1648 else
1649 uHorizAlign = DT_LEFT;
1650
1651 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1652 {
1653 if(lStyle & TCS_BOTTOM)
1654 {
1655 nEscapement = -900;
1656 nOrientation = -900;
1657 }
1658 else
1659 {
1660 nEscapement = 900;
1661 nOrientation = 900;
1662 }
1663 }
1664
1665 /* to get a font with the escapement and orientation we are looking for, we need to */
1666 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1667 if(lStyle & TCS_VERTICAL)
1668 {
1669 if (!GetObjectA((infoPtr->hFont) ?
1670 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1671 sizeof(LOGFONTA),&logfont))
1672 {
1673 iPointSize = 9;
1674
1675 lstrcpyA(logfont.lfFaceName, "Arial");
1676 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1677 72);
1678 logfont.lfWeight = FW_NORMAL;
1679 logfont.lfItalic = 0;
1680 logfont.lfUnderline = 0;
1681 logfont.lfStrikeOut = 0;
1682 }
1683
1684 logfont.lfEscapement = nEscapement;
1685 logfont.lfOrientation = nOrientation;
1686 hFont = CreateFontIndirectA(&logfont);
1687 hOldFont = SelectObject(hdc, hFont);
1688 }
1689
1690 if (lStyle & TCS_VERTICAL)
1691 {
1692 ExtTextOutW(hdc,
1693 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1694 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1695 ETO_CLIPPED,
1696 drawRect,
1697 infoPtr->items[iItem].pszText,
1698 lstrlenW(infoPtr->items[iItem].pszText),
1699 0);
1700 }
1701 else
1702 {
1703 DrawTextW
1704 (
1705 hdc,
1706 infoPtr->items[iItem].pszText,
1707 lstrlenW(infoPtr->items[iItem].pszText),
1708 drawRect,
1709 uHorizAlign | DT_SINGLELINE
1710 );
1711 }
1712
1713 /* clean things up */
1714 *drawRect = rcTemp; /* restore drawRect */
1715
1716 if(lStyle & TCS_VERTICAL)
1717 {
1718 SelectObject(hdc, hOldFont); /* restore the original font */
1719 if (hFont)
1720 DeleteObject(hFont);
1721 }
1722 }
1723
1724 /*
1725 * Cleanup
1726 */
1727 SetBkMode(hdc, oldBkMode);
1728 SelectObject(hdc, holdPen);
1729 DeleteObject( htextPen );
1730}
1731
1732/******************************************************************************
1733 * TAB_DrawItem
1734 *
1735 * This method is used to draw a single tab into the tab control.
1736 */
1737static void TAB_DrawItem(
1738 HWND hwnd,
1739 HDC hdc,
1740 INT iItem)
1741{
1742 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1743 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1744 RECT itemRect;
1745 RECT selectedRect;
1746 BOOL isVisible;
1747 RECT r, fillRect, r1;
1748 INT clRight = 0;
1749 INT clBottom = 0;
1750 COLORREF bkgnd, corner;
1751
1752 /*
1753 * Get the rectangle for the item.
1754 */
1755 isVisible = TAB_InternalGetItemRect(hwnd,
1756 infoPtr,
1757 iItem,
1758 &itemRect,
1759 &selectedRect);
1760
1761 if (isVisible)
1762 {
1763 /* If you need to see what the control is doing,
1764 * then override these variables. They will change what
1765 * fill colors are used for filling the tabs, and the
1766 * corners when drawing the edge.
1767 */
1768 bkgnd = comctl32_color.clrBtnFace;
1769 corner = comctl32_color.clrBtnFace;
1770
1771 if (lStyle & TCS_BUTTONS)
1772 {
1773 HBRUSH hbr = CreateSolidBrush (bkgnd);
1774 BOOL deleteBrush = TRUE;
1775
1776 /* Get item rectangle */
1777 r = itemRect;
1778
1779 /* Separators between flat buttons */
1780 if (lStyle & TCS_FLATBUTTONS)
1781 {
1782 r1 = r;
1783 r1.right += (FLAT_BTN_SPACINGX -2);
1784 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1785 }
1786
1787 if (iItem == infoPtr->iSelected)
1788 {
1789 /* Background color */
1790 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1791 {
1792 DeleteObject(hbr);
1793 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1794
1795 SetTextColor(hdc, comctl32_color.clr3dFace);
1796 SetBkColor(hdc, comctl32_color.clr3dHilight);
1797
1798 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1799 * we better use 0x55aa bitmap brush to make scrollbar's background
1800 * look different from the window background.
1801 */
1802 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1803 hbr = COMCTL32_hPattern55AABrush;
1804
1805 deleteBrush = FALSE;
1806 }
1807
1808 /* Clear interior */
1809 FillRect(hdc, &r, hbr);
1810
1811 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1812 }
1813 else /* ! selected */
1814 {
1815 if (!(lStyle & TCS_FLATBUTTONS))
1816 {
1817 /* Clear interior */
1818 FillRect(hdc, &r, hbr);
1819
1820 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1821 }
1822 }
1823
1824 /* Cleanup */
1825 if (deleteBrush) DeleteObject(hbr);
1826 }
1827 else /* !TCS_BUTTONS */
1828 {
1829 /* We draw a rectangle of different sizes depending on the selection
1830 * state. */
1831 if (iItem == infoPtr->iSelected) {
1832 RECT rect;
1833 GetClientRect (hwnd, &rect);
1834 clRight = rect.right;
1835 clBottom = rect.bottom;
1836 r = selectedRect;
1837 }
1838 else
1839 r = itemRect;
1840
1841 /*
1842 * Erase the background. (Delay it but setup rectangle.)
1843 * This is necessary when drawing the selected item since it is larger
1844 * than the others, it might overlap with stuff already drawn by the
1845 * other tabs
1846 */
1847 fillRect = r;
1848
1849 if(lStyle & TCS_VERTICAL)
1850 {
1851 /* These are for adjusting the drawing of a Selected tab */
1852 /* The initial values are for the normal case of non-Selected */
1853 int ZZ = 1; /* Do not strech if selected */
1854 if (iItem == infoPtr->iSelected) {
1855 ZZ = 0;
1856
1857 /* if leftmost draw the line longer */
1858 if(selectedRect.top == 0)
1859 fillRect.top += 2;
1860 /* if rightmost draw the line longer */
1861 if(selectedRect.bottom == clBottom)
1862 fillRect.bottom -= 2;
1863 }
1864
1865 if (lStyle & TCS_BOTTOM)
1866 {
1867 /* Adjust both rectangles to match native */
1868 r.left += (1-ZZ);
1869
1870 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1871 iItem,
1872 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1873 r.left,r.top,r.right,r.bottom);
1874
1875 /* Clear interior */
1876 SetBkColor(hdc, bkgnd);
1877 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1878
1879 /* Draw rectangular edge around tab */
1880 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1881
1882 /* Now erase the top corner and draw diagonal edge */
1883 SetBkColor(hdc, corner);
1884 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1885 r1.top = r.top;
1886 r1.right = r.right;
1887 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1888 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1889 r1.right--;
1890 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1891
1892 /* Now erase the bottom corner and draw diagonal edge */
1893 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1894 r1.bottom = r.bottom;
1895 r1.right = r.right;
1896 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1897 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1898 r1.right--;
1899 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1900
1901 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1902 r1 = r;
1903 r1.right = r1.left;
1904 r1.left--;
1905 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1906 }
1907
1908 }
1909 else
1910 {
1911 /* Adjust both rectangles to match native */
1912 fillRect.right += (1-ZZ);
1913
1914 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1915 iItem,
1916 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1917 r.left,r.top,r.right,r.bottom);
1918
1919 /* Clear interior */
1920 SetBkColor(hdc, bkgnd);
1921 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1922
1923 /* Draw rectangular edge around tab */
1924 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
1925
1926 /* Now erase the top corner and draw diagonal edge */
1927 SetBkColor(hdc, corner);
1928 r1.left = r.left;
1929 r1.top = r.top;
1930 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1931 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1932 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1933 r1.left++;
1934 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
1935
1936 /* Now erase the bottom corner and draw diagonal edge */
1937 r1.left = r.left;
1938 r1.bottom = r.bottom;
1939 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1940 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1941 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1942 r1.left++;
1943 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
1944 }
1945 }
1946 else /* ! TCS_VERTICAL */
1947 {
1948 /* These are for adjusting the drawing of a Selected tab */
1949 /* The initial values are for the normal case of non-Selected */
1950 int ZZ = 1; /* Do not strech if selected */
1951 if (iItem == infoPtr->iSelected) {
1952 ZZ = 0;
1953
1954 /* if leftmost draw the line longer */
1955 if(selectedRect.left == 0)
1956 fillRect.left += 2;
1957 /* if rightmost draw the line longer */
1958 if(selectedRect.right == clRight)
1959 fillRect.right -= 2;
1960 }
1961
1962 if (lStyle & TCS_BOTTOM)
1963 {
1964
1965 /* Adjust both rectangles to match native */
1966 fillRect.top--;
1967 fillRect.bottom--;
1968 r.bottom--;
1969 r.top -= ZZ;
1970
1971 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1972 iItem,
1973 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1974 r.left,r.top,r.right,r.bottom);
1975
1976 /* Clear interior */
1977 SetBkColor(hdc, bkgnd);
1978 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1979
1980 /* Draw rectangular edge around tab */
1981 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
1982
1983 /* Now erase the righthand corner and draw diagonal edge */
1984 SetBkColor(hdc, corner);
1985 r1.left = r.right - ROUND_CORNER_SIZE;
1986 r1.bottom = r.bottom;
1987 r1.right = r.right;
1988 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1989 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1990 r1.bottom--;
1991 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1992
1993 /* Now erase the lefthand corner and draw diagonal edge */
1994 r1.left = r.left;
1995 r1.bottom = r.bottom;
1996 r1.right = r1.left + ROUND_CORNER_SIZE;
1997 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1998 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1999 r1.bottom--;
2000 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2001
2002 if ((iItem == infoPtr->iSelected) && (selectedRect.left == 0)) {
2003 r1 = r;
2004 r1.bottom = r1.top;
2005 r1.top--;
2006 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2007 }
2008
2009 }
2010 else
2011 {
2012
2013 /* Adjust both rectangles to match native */
2014 fillRect.bottom += (1-ZZ);
2015
2016 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2017 iItem,
2018 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2019 r.left,r.top,r.right,r.bottom);
2020
2021 /* Clear interior */
2022 SetBkColor(hdc, bkgnd);
2023 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2024
2025 /* Draw rectangular edge around tab */
2026 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2027
2028 /* Now erase the righthand corner and draw diagonal edge */
2029 SetBkColor(hdc, corner);
2030 r1.left = r.right - ROUND_CORNER_SIZE;
2031 r1.top = r.top;
2032 r1.right = r.right;
2033 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2034 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2035 r1.top++;
2036 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2037
2038 /* Now erase the lefthand corner and draw diagonal edge */
2039 r1.left = r.left;
2040 r1.top = r.top;
2041 r1.right = r1.left + ROUND_CORNER_SIZE;
2042 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2043 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2044 r1.top++;
2045 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2046
2047 }
2048 }
2049 }
2050
2051 TAB_DumpItemInternal(infoPtr, iItem);
2052
2053 /* This modifies r to be the text rectangle. */
2054 {
2055 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
2056 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2057 SelectObject(hdc,hOldFont);
2058 }
2059
2060 /* Draw the focus rectangle */
2061 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
2062 (GetFocus() == hwnd) &&
2063 (iItem == infoPtr->uFocus) )
2064 {
2065 r = itemRect;
2066 InflateRect(&r, -1, -1);
2067
2068 DrawFocusRect(hdc, &r);
2069 }
2070 }
2071}
2072
2073/******************************************************************************
2074 * TAB_DrawBorder
2075 *
2076 * This method is used to draw the raised border around the tab control
2077 * "content" area.
2078 */
2079static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2080{
2081 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2082 RECT rect;
2083 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2084
2085 GetClientRect (hwnd, &rect);
2086
2087 /*
2088 * Adjust for the style
2089 */
2090
2091 if (infoPtr->uNumItem)
2092 {
2093 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2094 {
2095 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2096 }
2097 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2098 {
2099 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2100 }
2101 else if(lStyle & TCS_VERTICAL)
2102 {
2103 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2104 }
2105 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2106 {
2107 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2108 }
2109 }
2110
2111 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2112 rect.left, rect.top, rect.right, rect.bottom);
2113
2114 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2115}
2116
2117/******************************************************************************
2118 * TAB_Refresh
2119 *
2120 * This method repaints the tab control..
2121 */
2122static void TAB_Refresh (HWND hwnd, HDC hdc)
2123{
2124 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2125 HFONT hOldFont;
2126 INT i;
2127
2128 if (!infoPtr->DoRedraw)
2129 return;
2130
2131 hOldFont = SelectObject (hdc, infoPtr->hFont);
2132
2133 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2134 {
2135 for (i = 0; i < infoPtr->uNumItem; i++)
2136 TAB_DrawItem (hwnd, hdc, i);
2137 }
2138 else
2139 {
2140 /* Draw all the non selected item first */
2141 for (i = 0; i < infoPtr->uNumItem; i++)
2142 {
2143 if (i != infoPtr->iSelected)
2144 TAB_DrawItem (hwnd, hdc, i);
2145 }
2146
2147 /* Now, draw the border, draw it before the selected item
2148 * since the selected item overwrites part of the border. */
2149 TAB_DrawBorder (hwnd, hdc);
2150
2151 /* Then, draw the selected item */
2152 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2153
2154 /* If we haven't set the current focus yet, set it now.
2155 * Only happens when we first paint the tab controls */
2156 if (infoPtr->uFocus == -1)
2157 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2158 }
2159
2160 SelectObject (hdc, hOldFont);
2161}
2162
2163static DWORD
2164TAB_GetRowCount (HWND hwnd )
2165{
2166 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2167
2168 return infoPtr->uNumRows;
2169}
2170
2171static LRESULT
2172TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2173{
2174 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2175
2176 infoPtr->DoRedraw=(BOOL) wParam;
2177 return 0;
2178}
2179
2180static LRESULT TAB_EraseBackground(
2181 HWND hwnd,
2182 HDC givenDC)
2183{
2184 HDC hdc;
2185 RECT clientRect;
2186
2187 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2188
2189 hdc = givenDC ? givenDC : GetDC(hwnd);
2190
2191 GetClientRect(hwnd, &clientRect);
2192
2193 FillRect(hdc, &clientRect, brush);
2194
2195 if (givenDC==0)
2196 ReleaseDC(hwnd, hdc);
2197
2198 DeleteObject(brush);
2199
2200 return 0;
2201}
2202
2203/******************************************************************************
2204 * TAB_EnsureSelectionVisible
2205 *
2206 * This method will make sure that the current selection is completely
2207 * visible by scrolling until it is.
2208 */
2209static void TAB_EnsureSelectionVisible(
2210 HWND hwnd,
2211 TAB_INFO* infoPtr)
2212{
2213 INT iSelected = infoPtr->iSelected;
2214 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2215 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2216
2217 /* set the items row to the bottommost row or topmost row depending on
2218 * style */
2219 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2220 {
2221 INT newselected;
2222 INT iTargetRow;
2223
2224 if(lStyle & TCS_VERTICAL)
2225 newselected = infoPtr->items[iSelected].rect.left;
2226 else
2227 newselected = infoPtr->items[iSelected].rect.top;
2228
2229 /* the target row is always (number of rows - 1)
2230 as row 0 is furthest from the clientRect */
2231 iTargetRow = infoPtr->uNumRows - 1;
2232
2233 if (newselected != iTargetRow)
2234 {
2235 INT i;
2236 if(lStyle & TCS_VERTICAL)
2237 {
2238 for (i=0; i < infoPtr->uNumItem; i++)
2239 {
2240 /* move everything in the row of the selected item to the iTargetRow */
2241 if (infoPtr->items[i].rect.left == newselected )
2242 infoPtr->items[i].rect.left = iTargetRow;
2243 else
2244 {
2245 if (infoPtr->items[i].rect.left > newselected)
2246 infoPtr->items[i].rect.left-=1;
2247 }
2248 }
2249 }
2250 else
2251 {
2252 for (i=0; i < infoPtr->uNumItem; i++)
2253 {
2254 if (infoPtr->items[i].rect.top == newselected )
2255 infoPtr->items[i].rect.top = iTargetRow;
2256 else
2257 {
2258 if (infoPtr->items[i].rect.top > newselected)
2259 infoPtr->items[i].rect.top-=1;
2260 }
2261 }
2262 }
2263 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2264 }
2265 }
2266
2267 /*
2268 * Do the trivial cases first.
2269 */
2270 if ( (!infoPtr->needsScrolling) ||
2271 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2272 return;
2273
2274 if (infoPtr->leftmostVisible >= iSelected)
2275 {
2276 infoPtr->leftmostVisible = iSelected;
2277 }
2278 else
2279 {
2280 RECT r;
2281 INT width, i;
2282
2283 /* Calculate the part of the client area that is visible */
2284 GetClientRect(hwnd, &r);
2285 width = r.right;
2286
2287 GetClientRect(infoPtr->hwndUpDown, &r);
2288 width -= r.right;
2289
2290 if ((infoPtr->items[iSelected].rect.right -
2291 infoPtr->items[iSelected].rect.left) >= width )
2292 {
2293 /* Special case: width of selected item is greater than visible
2294 * part of control.
2295 */
2296 infoPtr->leftmostVisible = iSelected;
2297 }
2298 else
2299 {
2300 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2301 {
2302 if ((infoPtr->items[iSelected].rect.right -
2303 infoPtr->items[i].rect.left) < width)
2304 break;
2305 }
2306 infoPtr->leftmostVisible = i;
2307 }
2308 }
2309
2310 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2311 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2312
2313 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2314 MAKELONG(infoPtr->leftmostVisible, 0));
2315}
2316
2317/******************************************************************************
2318 * TAB_InvalidateTabArea
2319 *
2320 * This method will invalidate the portion of the control that contains the
2321 * tabs. It is called when the state of the control changes and needs
2322 * to be redisplayed
2323 */
2324static void TAB_InvalidateTabArea(
2325 HWND hwnd,
2326 TAB_INFO* infoPtr)
2327{
2328 RECT clientRect, r;
2329 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2330 INT lastRow = infoPtr->uNumRows - 1;
2331
2332 if (lastRow < 0) return;
2333
2334 GetClientRect(hwnd, &clientRect);
2335
2336 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2337 {
2338 clientRect.top = clientRect.bottom -
2339 infoPtr->tabHeight -
2340 lastRow * (infoPtr->tabHeight - 2) -
2341 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2342 }
2343 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2344 {
2345 clientRect.left = clientRect.right - infoPtr->tabHeight -
2346 lastRow * (infoPtr->tabHeight - 2) -
2347 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2348 }
2349 else if(lStyle & TCS_VERTICAL)
2350 {
2351 clientRect.right = clientRect.left + infoPtr->tabHeight +
2352 lastRow * (infoPtr->tabHeight - 2) -
2353 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2354
2355 }
2356 else
2357 {
2358 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2359 lastRow * (infoPtr->tabHeight - 2) +
2360 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2361 }
2362
2363 /* Punch out the updown control */
2364 if (infoPtr->needsScrolling && (clientRect.right > 0)) {
2365 GetClientRect(infoPtr->hwndUpDown, &r);
2366 clientRect.right = clientRect.right - (r.right - r.left);
2367 }
2368
2369 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2370 clientRect.left,clientRect.top,
2371 clientRect.right,clientRect.bottom);
2372
2373 InvalidateRect(hwnd, &clientRect, TRUE);
2374}
2375
2376static LRESULT
2377TAB_Paint (HWND hwnd, WPARAM wParam)
2378{
2379 HDC hdc;
2380 PAINTSTRUCT ps;
2381
2382 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2383
2384 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2385 ps.fErase,
2386 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2387
2388 if (ps.fErase)
2389 TAB_EraseBackground (hwnd, hdc);
2390
2391 TAB_Refresh (hwnd, hdc);
2392
2393 if(!wParam)
2394 EndPaint (hwnd, &ps);
2395
2396 return 0;
2397}
2398
2399static LRESULT
2400TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2401{
2402 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2403 TCITEMA *pti;
2404 INT iItem;
2405 RECT rect;
2406
2407 GetClientRect (hwnd, &rect);
2408 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2409 rect.top, rect.left, rect.bottom, rect.right);
2410
2411 pti = (TCITEMA *)lParam;
2412 iItem = (INT)wParam;
2413
2414 if (iItem < 0) return -1;
2415 if (iItem > infoPtr->uNumItem)
2416 iItem = infoPtr->uNumItem;
2417
2418 TAB_DumpItemExternalA(pti, iItem);
2419
2420
2421 if (infoPtr->uNumItem == 0) {
2422 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2423 infoPtr->uNumItem++;
2424 infoPtr->iSelected = 0;
2425 }
2426 else {
2427 TAB_ITEM *oldItems = infoPtr->items;
2428
2429 infoPtr->uNumItem++;
2430 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2431
2432 /* pre insert copy */
2433 if (iItem > 0) {
2434 memcpy (&infoPtr->items[0], &oldItems[0],
2435 iItem * sizeof(TAB_ITEM));
2436 }
2437
2438 /* post insert copy */
2439 if (iItem < infoPtr->uNumItem - 1) {
2440 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2441 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2442
2443 }
2444
2445 if (iItem <= infoPtr->iSelected)
2446 infoPtr->iSelected++;
2447
2448 COMCTL32_Free (oldItems);
2449 }
2450
2451 infoPtr->items[iItem].mask = pti->mask;
2452 if (pti->mask & TCIF_TEXT)
2453 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2454
2455 if (pti->mask & TCIF_IMAGE)
2456 infoPtr->items[iItem].iImage = pti->iImage;
2457
2458 if (pti->mask & TCIF_PARAM)
2459 infoPtr->items[iItem].lParam = pti->lParam;
2460
2461 TAB_SetItemBounds(hwnd);
2462 if (infoPtr->uNumItem > 1)
2463 TAB_InvalidateTabArea(hwnd, infoPtr);
2464 else
2465 InvalidateRect(hwnd, NULL, TRUE);
2466
2467 TRACE("[%p]: added item %d %s\n",
2468 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2469
2470 return iItem;
2471}
2472
2473
2474static LRESULT
2475TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2476{
2477 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2478 TCITEMW *pti;
2479 INT iItem;
2480 RECT rect;
2481
2482 GetClientRect (hwnd, &rect);
2483 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2484 rect.top, rect.left, rect.bottom, rect.right);
2485
2486 pti = (TCITEMW *)lParam;
2487 iItem = (INT)wParam;
2488
2489 if (iItem < 0) return -1;
2490 if (iItem > infoPtr->uNumItem)
2491 iItem = infoPtr->uNumItem;
2492
2493 TAB_DumpItemExternalW(pti, iItem);
2494
2495 if (infoPtr->uNumItem == 0) {
2496 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2497 infoPtr->uNumItem++;
2498 infoPtr->iSelected = 0;
2499 }
2500 else {
2501 TAB_ITEM *oldItems = infoPtr->items;
2502
2503 infoPtr->uNumItem++;
2504 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2505
2506 /* pre insert copy */
2507 if (iItem > 0) {
2508 memcpy (&infoPtr->items[0], &oldItems[0],
2509 iItem * sizeof(TAB_ITEM));
2510 }
2511
2512 /* post insert copy */
2513 if (iItem < infoPtr->uNumItem - 1) {
2514 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2515 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2516
2517 }
2518
2519 if (iItem <= infoPtr->iSelected)
2520 infoPtr->iSelected++;
2521
2522 COMCTL32_Free (oldItems);
2523 }
2524
2525 infoPtr->items[iItem].mask = pti->mask;
2526 if (pti->mask & TCIF_TEXT)
2527 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2528
2529 if (pti->mask & TCIF_IMAGE)
2530 infoPtr->items[iItem].iImage = pti->iImage;
2531
2532 if (pti->mask & TCIF_PARAM)
2533 infoPtr->items[iItem].lParam = pti->lParam;
2534
2535 TAB_SetItemBounds(hwnd);
2536 if (infoPtr->uNumItem > 1)
2537 TAB_InvalidateTabArea(hwnd, infoPtr);
2538 else
2539 InvalidateRect(hwnd, NULL, TRUE);
2540
2541 TRACE("[%p]: added item %d %s\n",
2542 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2543
2544 return iItem;
2545}
2546
2547
2548static LRESULT
2549TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2550{
2551 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2552 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2553 LONG lResult = 0;
2554
2555 TRACE("\n");
2556 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2557 {
2558 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2559 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use default. */
2560 if (LOWORD(lParam)) infoPtr->tabWidth = (INT)LOWORD(lParam);
2561 if (HIWORD(lParam)) infoPtr->tabHeight = (INT)HIWORD(lParam);
2562 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2563 HIWORD(lResult), LOWORD(lResult),
2564 infoPtr->tabHeight, infoPtr->tabWidth);
2565 }
2566 infoPtr->fSizeSet = TRUE;
2567
2568 return lResult;
2569}
2570
2571static LRESULT
2572TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2573{
2574 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2575 TCITEMA *tabItem;
2576 TAB_ITEM *wineItem;
2577 INT iItem;
2578
2579 iItem = (INT)wParam;
2580 tabItem = (LPTCITEMA)lParam;
2581
2582 TRACE("%d %p\n", iItem, tabItem);
2583 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2584
2585 TAB_DumpItemExternalA(tabItem, iItem);
2586
2587 wineItem = &infoPtr->items[iItem];
2588
2589 if (tabItem->mask & TCIF_IMAGE)
2590 wineItem->iImage = tabItem->iImage;
2591
2592 if (tabItem->mask & TCIF_PARAM)
2593 wineItem->lParam = tabItem->lParam;
2594
2595 if (tabItem->mask & TCIF_RTLREADING)
2596 FIXME("TCIF_RTLREADING\n");
2597
2598 if (tabItem->mask & TCIF_STATE)
2599 wineItem->dwState = tabItem->dwState;
2600
2601 if (tabItem->mask & TCIF_TEXT)
2602 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2603
2604 /* Update and repaint tabs */
2605 TAB_SetItemBounds(hwnd);
2606 TAB_InvalidateTabArea(hwnd,infoPtr);
2607
2608 return TRUE;
2609 }
2610
2611
2612static LRESULT
2613TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2614{
2615 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2616 TCITEMW *tabItem;
2617 TAB_ITEM *wineItem;
2618 INT iItem;
2619
2620 iItem = (INT)wParam;
2621 tabItem = (LPTCITEMW)lParam;
2622
2623 TRACE("%d %p\n", iItem, tabItem);
2624 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2625
2626 TAB_DumpItemExternalW(tabItem, iItem);
2627
2628 wineItem = &infoPtr->items[iItem];
2629
2630 if (tabItem->mask & TCIF_IMAGE)
2631 wineItem->iImage = tabItem->iImage;
2632
2633 if (tabItem->mask & TCIF_PARAM)
2634 wineItem->lParam = tabItem->lParam;
2635
2636 if (tabItem->mask & TCIF_RTLREADING)
2637 FIXME("TCIF_RTLREADING\n");
2638
2639 if (tabItem->mask & TCIF_STATE)
2640 wineItem->dwState = tabItem->dwState;
2641
2642 if (tabItem->mask & TCIF_TEXT)
2643 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2644
2645 /* Update and repaint tabs */
2646 TAB_SetItemBounds(hwnd);
2647 TAB_InvalidateTabArea(hwnd,infoPtr);
2648
2649 return TRUE;
2650}
2651
2652
2653static LRESULT
2654TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2655{
2656 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2657
2658 return infoPtr->uNumItem;
2659}
2660
2661
2662static LRESULT
2663TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2664{
2665 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2666 TCITEMA *tabItem;
2667 TAB_ITEM *wineItem;
2668 INT iItem;
2669
2670 iItem = (INT)wParam;
2671 tabItem = (LPTCITEMA)lParam;
2672 TRACE("\n");
2673 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2674 return FALSE;
2675
2676 wineItem = &infoPtr->items[iItem];
2677
2678 if (tabItem->mask & TCIF_IMAGE)
2679 tabItem->iImage = wineItem->iImage;
2680
2681 if (tabItem->mask & TCIF_PARAM)
2682 tabItem->lParam = wineItem->lParam;
2683
2684 if (tabItem->mask & TCIF_RTLREADING)
2685 FIXME("TCIF_RTLREADING\n");
2686
2687 if (tabItem->mask & TCIF_STATE)
2688 tabItem->dwState = wineItem->dwState;
2689
2690 if (tabItem->mask & TCIF_TEXT)
2691 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2692
2693 TAB_DumpItemExternalA(tabItem, iItem);
2694
2695 return TRUE;
2696}
2697
2698
2699static LRESULT
2700TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2701{
2702 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2703 TCITEMW *tabItem;
2704 TAB_ITEM *wineItem;
2705 INT iItem;
2706
2707 iItem = (INT)wParam;
2708 tabItem = (LPTCITEMW)lParam;
2709 TRACE("\n");
2710 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2711 return FALSE;
2712
2713 wineItem=& infoPtr->items[iItem];
2714
2715 if (tabItem->mask & TCIF_IMAGE)
2716 tabItem->iImage = wineItem->iImage;
2717
2718 if (tabItem->mask & TCIF_PARAM)
2719 tabItem->lParam = wineItem->lParam;
2720
2721 if (tabItem->mask & TCIF_RTLREADING)
2722 FIXME("TCIF_RTLREADING\n");
2723
2724 if (tabItem->mask & TCIF_STATE)
2725 tabItem->dwState = wineItem->dwState;
2726
2727 if (tabItem->mask & TCIF_TEXT)
2728 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2729
2730 TAB_DumpItemExternalW(tabItem, iItem);
2731
2732 return TRUE;
2733}
2734
2735
2736static LRESULT
2737TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2738{
2739 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2740 INT iItem = (INT) wParam;
2741 BOOL bResult = FALSE;
2742
2743 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2744 {
2745 TAB_ITEM *oldItems = infoPtr->items;
2746
2747 infoPtr->uNumItem--;
2748 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2749
2750 if (iItem > 0)
2751 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2752
2753 if (iItem < infoPtr->uNumItem)
2754 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2755 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2756
2757 COMCTL32_Free(oldItems);
2758
2759 /* Readjust the selected index */
2760 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2761 infoPtr->iSelected--;
2762
2763 if (iItem < infoPtr->iSelected)
2764 infoPtr->iSelected--;
2765
2766 if (infoPtr->uNumItem == 0)
2767 infoPtr->iSelected = -1;
2768
2769 /* Reposition and repaint tabs */
2770 TAB_SetItemBounds(hwnd);
2771 TAB_InvalidateTabArea(hwnd,infoPtr);
2772
2773 bResult = TRUE;
2774 }
2775
2776 return bResult;
2777}
2778
2779static LRESULT
2780TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2781{
2782 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2783
2784 COMCTL32_Free (infoPtr->items);
2785 infoPtr->uNumItem = 0;
2786 infoPtr->iSelected = -1;
2787 if (infoPtr->iHotTracked >= 0)
2788 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2789 infoPtr->iHotTracked = -1;
2790
2791 TAB_SetItemBounds(hwnd);
2792 TAB_InvalidateTabArea(hwnd,infoPtr);
2793 return TRUE;
2794}
2795
2796
2797static LRESULT
2798TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2799{
2800 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2801
2802 TRACE("\n");
2803 return (LRESULT)infoPtr->hFont;
2804}
2805
2806static LRESULT
2807TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2808
2809{
2810 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2811
2812 TRACE("%x %lx\n",wParam, lParam);
2813
2814 infoPtr->hFont = (HFONT)wParam;
2815
2816 TAB_SetItemBounds(hwnd);
2817
2818 TAB_InvalidateTabArea(hwnd, infoPtr);
2819
2820 return 0;
2821}
2822
2823
2824static LRESULT
2825TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2826{
2827 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2828
2829 TRACE("\n");
2830 return (LRESULT)infoPtr->himl;
2831}
2832
2833static LRESULT
2834TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2835{
2836 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2837 HIMAGELIST himlPrev;
2838
2839 TRACE("\n");
2840 himlPrev = infoPtr->himl;
2841 infoPtr->himl= (HIMAGELIST)lParam;
2842 return (LRESULT)himlPrev;
2843}
2844
2845static LRESULT
2846TAB_GetUnicodeFormat (HWND hwnd)
2847{
2848 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2849 return infoPtr->bUnicode;
2850}
2851
2852static LRESULT
2853TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2854{
2855 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2856 BOOL bTemp = infoPtr->bUnicode;
2857
2858 infoPtr->bUnicode = (BOOL)wParam;
2859
2860 return bTemp;
2861}
2862
2863static LRESULT
2864TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2865
2866{
2867/* I'm not really sure what the following code was meant to do.
2868 This is what it is doing:
2869 When WM_SIZE is sent with SIZE_RESTORED, the control
2870 gets positioned in the top left corner.
2871
2872 RECT parent_rect;
2873 HWND parent;
2874 UINT uPosFlags,cx,cy;
2875
2876 uPosFlags=0;
2877 if (!wParam) {
2878 parent = GetParent (hwnd);
2879 GetClientRect(parent, &parent_rect);
2880 cx=LOWORD (lParam);
2881 cy=HIWORD (lParam);
2882 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2883 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2884
2885 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2886 cx, cy, uPosFlags | SWP_NOZORDER);
2887 } else {
2888 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2889 } */
2890
2891 /* Recompute the size/position of the tabs. */
2892 TAB_SetItemBounds (hwnd);
2893
2894 /* Force a repaint of the control. */
2895 InvalidateRect(hwnd, NULL, TRUE);
2896
2897 return 0;
2898}
2899
2900
2901static LRESULT
2902TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2903{
2904 TAB_INFO *infoPtr;
2905 TEXTMETRICA fontMetrics;
2906 HDC hdc;
2907 HFONT hOldFont;
2908 DWORD dwStyle;
2909
2910 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2911
2912 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2913
2914 infoPtr->uNumItem = 0;
2915 infoPtr->uNumRows = 0;
2916 infoPtr->uHItemPadding = 6;
2917 infoPtr->uVItemPadding = 3;
2918 infoPtr->hFont = 0;
2919 infoPtr->items = 0;
2920 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2921 infoPtr->iSelected = -1;
2922 infoPtr->iHotTracked = -1;
2923 infoPtr->uFocus = -1;
2924 infoPtr->hwndToolTip = 0;
2925 infoPtr->DoRedraw = TRUE;
2926 infoPtr->needsScrolling = FALSE;
2927 infoPtr->hwndUpDown = 0;
2928 infoPtr->leftmostVisible = 0;
2929 infoPtr->fSizeSet = FALSE;
2930 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2931
2932 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2933
2934 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2935 if you don't specify it in CreateWindow. This is necessary in
2936 order for paint to work correctly. This follows windows behaviour. */
2937 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2938 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2939
2940 if (dwStyle & TCS_TOOLTIPS) {
2941 /* Create tooltip control */
2942 infoPtr->hwndToolTip =
2943 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2944 CW_USEDEFAULT, CW_USEDEFAULT,
2945 CW_USEDEFAULT, CW_USEDEFAULT,
2946 hwnd, 0, 0, 0);
2947
2948 /* Send NM_TOOLTIPSCREATED notification */
2949 if (infoPtr->hwndToolTip) {
2950 NMTOOLTIPSCREATED nmttc;
2951
2952 nmttc.hdr.hwndFrom = hwnd;
2953 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2954 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2955 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2956
2957 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2958 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2959 }
2960 }
2961
2962 /*
2963 * We need to get text information so we need a DC and we need to select
2964 * a font.
2965 */
2966 hdc = GetDC(hwnd);
2967 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2968
2969 /* Use the system font to determine the initial height of a tab. */
2970 GetTextMetricsA(hdc, &fontMetrics);
2971
2972 /*
2973 * Make sure there is enough space for the letters + growing the
2974 * selected item + extra space for the selected item.
2975 */
2976 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2977 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2978 infoPtr->uVItemPadding;
2979
2980 /* Initialize the width of a tab. */
2981 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2982
2983 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2984
2985 SelectObject (hdc, hOldFont);
2986 ReleaseDC(hwnd, hdc);
2987
2988 return 0;
2989}
2990
2991static LRESULT
2992TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2993{
2994 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2995 INT iItem;
2996
2997 if (!infoPtr)
2998 return 0;
2999
3000 if (infoPtr->items) {
3001 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3002 if (infoPtr->items[iItem].pszText)
3003 COMCTL32_Free (infoPtr->items[iItem].pszText);
3004 }
3005 COMCTL32_Free (infoPtr->items);
3006 }
3007
3008 if (infoPtr->hwndToolTip)
3009 DestroyWindow (infoPtr->hwndToolTip);
3010
3011 if (infoPtr->hwndUpDown)
3012 DestroyWindow(infoPtr->hwndUpDown);
3013
3014 if (infoPtr->iHotTracked >= 0)
3015 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3016
3017 COMCTL32_Free (infoPtr);
3018 SetWindowLongA(hwnd, 0, 0);
3019 return 0;
3020}
3021
3022static LRESULT WINAPI
3023TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3024{
3025
3026 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3027 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3028 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3029
3030 switch (uMsg)
3031 {
3032 case TCM_GETIMAGELIST:
3033 return TAB_GetImageList (hwnd, wParam, lParam);
3034
3035 case TCM_SETIMAGELIST:
3036 return TAB_SetImageList (hwnd, wParam, lParam);
3037
3038 case TCM_GETITEMCOUNT:
3039 return TAB_GetItemCount (hwnd, wParam, lParam);
3040
3041 case TCM_GETITEMA:
3042 return TAB_GetItemA (hwnd, wParam, lParam);
3043
3044 case TCM_GETITEMW:
3045 return TAB_GetItemW (hwnd, wParam, lParam);
3046
3047 case TCM_SETITEMA:
3048 return TAB_SetItemA (hwnd, wParam, lParam);
3049
3050 case TCM_SETITEMW:
3051 return TAB_SetItemW (hwnd, wParam, lParam);
3052
3053 case TCM_DELETEITEM:
3054 return TAB_DeleteItem (hwnd, wParam, lParam);
3055
3056 case TCM_DELETEALLITEMS:
3057 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3058
3059 case TCM_GETITEMRECT:
3060 return TAB_GetItemRect (hwnd, wParam, lParam);
3061
3062 case TCM_GETCURSEL:
3063 return TAB_GetCurSel (hwnd);
3064
3065 case TCM_HITTEST:
3066 return TAB_HitTest (hwnd, wParam, lParam);
3067
3068 case TCM_SETCURSEL:
3069 return TAB_SetCurSel (hwnd, wParam);
3070
3071 case TCM_INSERTITEMA:
3072 return TAB_InsertItemA (hwnd, wParam, lParam);
3073
3074 case TCM_INSERTITEMW:
3075 return TAB_InsertItemW (hwnd, wParam, lParam);
3076
3077 case TCM_SETITEMEXTRA:
3078 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3079 return 0;
3080
3081 case TCM_ADJUSTRECT:
3082 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3083
3084 case TCM_SETITEMSIZE:
3085 return TAB_SetItemSize (hwnd, wParam, lParam);
3086
3087 case TCM_REMOVEIMAGE:
3088 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3089 return 0;
3090
3091 case TCM_SETPADDING:
3092 return TAB_SetPadding (hwnd, wParam, lParam);
3093
3094 case TCM_GETROWCOUNT:
3095 return TAB_GetRowCount(hwnd);
3096
3097 case TCM_GETUNICODEFORMAT:
3098 return TAB_GetUnicodeFormat (hwnd);
3099
3100 case TCM_SETUNICODEFORMAT:
3101 return TAB_SetUnicodeFormat (hwnd, wParam);
3102
3103 case TCM_HIGHLIGHTITEM:
3104 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
3105 return 0;
3106
3107 case TCM_GETTOOLTIPS:
3108 return TAB_GetToolTips (hwnd, wParam, lParam);
3109
3110 case TCM_SETTOOLTIPS:
3111 return TAB_SetToolTips (hwnd, wParam, lParam);
3112
3113 case TCM_GETCURFOCUS:
3114 return TAB_GetCurFocus (hwnd);
3115
3116 case TCM_SETCURFOCUS:
3117 return TAB_SetCurFocus (hwnd, wParam);
3118
3119 case TCM_SETMINTABWIDTH:
3120 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
3121 return 0;
3122
3123 case TCM_DESELECTALL:
3124 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3125 return 0;
3126
3127 case TCM_GETEXTENDEDSTYLE:
3128 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3129 return 0;
3130
3131 case TCM_SETEXTENDEDSTYLE:
3132 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3133 return 0;
3134
3135 case WM_GETFONT:
3136 return TAB_GetFont (hwnd, wParam, lParam);
3137
3138 case WM_SETFONT:
3139 return TAB_SetFont (hwnd, wParam, lParam);
3140
3141 case WM_CREATE:
3142 return TAB_Create (hwnd, wParam, lParam);
3143
3144 case WM_NCDESTROY:
3145 return TAB_Destroy (hwnd, wParam, lParam);
3146
3147 case WM_GETDLGCODE:
3148 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3149
3150 case WM_LBUTTONDOWN:
3151 return TAB_LButtonDown (hwnd, wParam, lParam);
3152
3153 case WM_LBUTTONUP:
3154 return TAB_LButtonUp (hwnd, wParam, lParam);
3155
3156 case WM_NOTIFY:
3157 return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
3158
3159 case WM_RBUTTONDOWN:
3160 return TAB_RButtonDown (hwnd, wParam, lParam);
3161
3162 case WM_MOUSEMOVE:
3163 return TAB_MouseMove (hwnd, wParam, lParam);
3164
3165 case WM_ERASEBKGND:
3166 return TAB_EraseBackground (hwnd, (HDC)wParam);
3167
3168 case WM_PAINT:
3169 return TAB_Paint (hwnd, wParam);
3170
3171 case WM_SIZE:
3172 return TAB_Size (hwnd, wParam, lParam);
3173
3174 case WM_SETREDRAW:
3175 return TAB_SetRedraw (hwnd, wParam);
3176
3177 case WM_HSCROLL:
3178 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3179
3180 case WM_STYLECHANGED:
3181 TAB_SetItemBounds (hwnd);
3182 InvalidateRect(hwnd, NULL, TRUE);
3183 return 0;
3184
3185 case WM_SYSCOLORCHANGE:
3186 COMCTL32_RefreshSysColors();
3187 return 0;
3188
3189 case WM_KILLFOCUS:
3190 case WM_SETFOCUS:
3191 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3192
3193 case WM_KEYUP:
3194 return TAB_KeyUp(hwnd, wParam);
3195 case WM_NCHITTEST:
3196 return TAB_NCHitTest(hwnd, lParam);
3197
3198 default:
3199 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3200 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3201 uMsg, wParam, lParam);
3202 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3203 }
3204
3205 return 0;
3206}
3207
3208
3209VOID
3210TAB_Register (void)
3211{
3212 WNDCLASSA wndClass;
3213
3214 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3215 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3216 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3217 wndClass.cbClsExtra = 0;
3218 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3219 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3220 wndClass.hbrBackground = NULL;
3221 wndClass.lpszClassName = WC_TABCONTROLA;
3222
3223 RegisterClassA (&wndClass);
3224}
3225
3226
3227VOID
3228TAB_Unregister (void)
3229{
3230 UnregisterClassA (WC_TABCONTROLA, NULL);
3231}
Note: See TracBrowser for help on using the repository browser.