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

Last change on this file since 9469 was 9469, checked in by sandervl, 23 years ago

Put back old tab height calculation code

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