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

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

Wine resync

File size: 87.5 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 infoPtr->tabHeight = item_height + SELECTED_TAB_OFFSET +
1111 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1112 VERTICAL_ITEM_PADDING;
1113
1114 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1115 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1116 }
1117
1118 TRACE("client right=%d\n", clientRect.right);
1119
1120 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1121 {
1122 /* Set the leftmost position of the tab. */
1123 infoPtr->items[curItem].rect.left = curItemLeftPos;
1124
1125 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1126 {
1127 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1128 infoPtr->tabWidth +
1129 2 * HORIZONTAL_ITEM_PADDING;
1130 }
1131 else
1132 {
1133 int icon_width = 0;
1134 int num = 2;
1135
1136 /* Calculate how wide the tab is depending on the text it contains */
1137 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1138 lstrlenW(infoPtr->items[curItem].pszText), &size);
1139
1140 /* under Windows, there seems to be a minimum width of 2x the height
1141 * for button style tabs */
1142 if (lStyle & TCS_BUTTONS)
1143 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1144
1145 /* Add the icon width */
1146 if (infoPtr->himl)
1147 {
1148 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1149 num++;
1150 }
1151
1152 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1153 size.cx + icon_width +
1154 num * HORIZONTAL_ITEM_PADDING;
1155 TRACE("for <%s>, l,r=%d,%d, num=%d\n",
1156 debugstr_w(infoPtr->items[curItem].pszText),
1157 infoPtr->items[curItem].rect.left,
1158 infoPtr->items[curItem].rect.right,
1159 num);
1160 }
1161
1162 /*
1163 * Check if this is a multiline tab control and if so
1164 * check to see if we should wrap the tabs
1165 *
1166 * Because we are going to arange all these tabs evenly
1167 * really we are basically just counting rows at this point
1168 *
1169 */
1170
1171 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1172 (infoPtr->items[curItem].rect.right > clientRect.right))
1173 {
1174 infoPtr->items[curItem].rect.right -=
1175 infoPtr->items[curItem].rect.left;
1176
1177 infoPtr->items[curItem].rect.left = 0;
1178 curItemRowCount++;
1179 TRACE("wrapping <%s>, l,r=%d,%d\n",
1180 debugstr_w(infoPtr->items[curItem].pszText),
1181 infoPtr->items[curItem].rect.left,
1182 infoPtr->items[curItem].rect.right);
1183 }
1184
1185 infoPtr->items[curItem].rect.bottom = 0;
1186 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1187
1188 TRACE("TextSize: %li\n", size.cx);
1189 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1190 infoPtr->items[curItem].rect.top,
1191 infoPtr->items[curItem].rect.left,
1192 infoPtr->items[curItem].rect.bottom,
1193 infoPtr->items[curItem].rect.right);
1194
1195 /*
1196 * The leftmost position of the next item is the rightmost position
1197 * of this one.
1198 */
1199 if (lStyle & TCS_BUTTONS)
1200 {
1201 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1202 if (lStyle & TCS_FLATBUTTONS)
1203 curItemLeftPos += FLAT_BTN_SPACINGX;
1204 }
1205 else
1206 curItemLeftPos = infoPtr->items[curItem].rect.right;
1207 }
1208
1209 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1210 {
1211 /*
1212 * Check if we need a scrolling control.
1213 */
1214 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1215 clientRect.right);
1216
1217 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1218 if(!infoPtr->needsScrolling)
1219 infoPtr->leftmostVisible = 0;
1220
1221 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1222 }
1223
1224 /* Set the number of rows */
1225 infoPtr->uNumRows = curItemRowCount;
1226
1227 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1228 {
1229 INT widthDiff, remainder;
1230 INT tabPerRow,remTab;
1231 INT iRow,iItm;
1232 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1233
1234 /*
1235 * Ok windows tries to even out the rows. place the same
1236 * number of tabs in each row. So lets give that a shot
1237 */
1238
1239 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1240 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1241
1242 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1243 iItm<infoPtr->uNumItem;
1244 iItm++,iCount++)
1245 {
1246 /* normalize the current rect */
1247
1248 /* shift the item to the left side of the clientRect */
1249 infoPtr->items[iItm].rect.right -=
1250 infoPtr->items[iItm].rect.left;
1251 infoPtr->items[iItm].rect.left = 0;
1252
1253 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1254 infoPtr->items[iItm].rect.right,
1255 curItemLeftPos, clientRect.right,
1256 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1257
1258 /* if we have reached the maximum number of tabs on this row */
1259 /* move to the next row, reset our current item left position and */
1260 /* the count of items on this row */
1261
1262 /* ************ FIXME FIXME FIXME *************** */
1263 /* */
1264 /* FIXME: */
1265 /* if vertical, */
1266 /* if item n and n+1 are in the same row, */
1267 /* then the display has n+1 lower (toward the */
1268 /* bottom) than n. We do it just the */
1269 /* opposite!!! */
1270 /* */
1271 /* ************ FIXME FIXME FIXME *************** */
1272
1273 if (lStyle & TCS_VERTICAL) {
1274 /* Vert: Add the remaining tabs in the *last* remainder rows */
1275 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1276 iRow++;
1277 curItemLeftPos = 0;
1278 iCount = 0;
1279 }
1280 } else {
1281 /* Horz: Add the remaining tabs in the *first* remainder rows */
1282 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1283 iRow++;
1284 curItemLeftPos = 0;
1285 iCount = 0;
1286 }
1287 }
1288
1289 /* shift the item to the right to place it as the next item in this row */
1290 infoPtr->items[iItm].rect.left += curItemLeftPos;
1291 infoPtr->items[iItm].rect.right += curItemLeftPos;
1292 infoPtr->items[iItm].rect.top = iRow;
1293 if (lStyle & TCS_BUTTONS)
1294 {
1295 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1296 if (lStyle & TCS_FLATBUTTONS)
1297 curItemLeftPos += FLAT_BTN_SPACINGX;
1298 }
1299 else
1300 curItemLeftPos = infoPtr->items[iItm].rect.right;
1301
1302 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1303 debugstr_w(infoPtr->items[iItm].pszText),
1304 infoPtr->items[iItm].rect.left,
1305 infoPtr->items[iItm].rect.right,
1306 infoPtr->items[iItm].rect.top);
1307 }
1308
1309 /*
1310 * Justify the rows
1311 */
1312 {
1313 while(iIndexStart < infoPtr->uNumItem)
1314 {
1315 /*
1316 * find the indexs of the row
1317 */
1318 /* find the first item on the next row */
1319 for (iIndexEnd=iIndexStart;
1320 (iIndexEnd < infoPtr->uNumItem) &&
1321 (infoPtr->items[iIndexEnd].rect.top ==
1322 infoPtr->items[iIndexStart].rect.top) ;
1323 iIndexEnd++)
1324 /* intentionally blank */;
1325
1326 /*
1327 * we need to justify these tabs so they fill the whole given
1328 * client area
1329 *
1330 */
1331 /* find the amount of space remaining on this row */
1332 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1333 infoPtr->items[iIndexEnd - 1].rect.right;
1334
1335 /* iCount is the number of tab items on this row */
1336 iCount = iIndexEnd - iIndexStart;
1337
1338
1339 if (iCount > 1)
1340 {
1341 remainder = widthDiff % iCount;
1342 widthDiff = widthDiff / iCount;
1343 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1344 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1345 iIndex++,iCount++)
1346 {
1347 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1348 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1349
1350 TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1351 debugstr_w(infoPtr->items[iIndex].pszText),
1352 infoPtr->items[iIndex].rect.left,
1353 infoPtr->items[iIndex].rect.right);
1354
1355 }
1356 infoPtr->items[iIndex - 1].rect.right += remainder;
1357 }
1358 else /* we have only one item on this row, make it take up the entire row */
1359 {
1360 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1361 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1362
1363 TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1364 debugstr_w(infoPtr->items[iIndexStart].pszText),
1365 infoPtr->items[iIndexStart].rect.left,
1366 infoPtr->items[iIndexStart].rect.right);
1367
1368 }
1369
1370
1371 iIndexStart = iIndexEnd;
1372 }
1373 }
1374 }
1375
1376 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1377 if(lStyle & TCS_VERTICAL)
1378 {
1379 RECT rcOriginal;
1380 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1381 {
1382 rcItem = &(infoPtr->items[iIndex].rect);
1383
1384 rcOriginal = *rcItem;
1385
1386 /* this is rotating the items by 90 degrees around the center of the control */
1387 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1388 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1389 rcItem->left = rcOriginal.top;
1390 rcItem->right = rcOriginal.bottom;
1391 }
1392 }
1393
1394 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1395 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1396
1397 /* Cleanup */
1398 SelectObject (hdc, hOldFont);
1399 ReleaseDC (hwnd, hdc);
1400}
1401
1402/******************************************************************************
1403 * TAB_DrawItemInterior
1404 *
1405 * This method is used to draw the interior (text and icon) of a single tab
1406 * into the tab control.
1407 */
1408static void
1409TAB_DrawItemInterior
1410 (
1411 HWND hwnd,
1412 HDC hdc,
1413 INT iItem,
1414 RECT* drawRect
1415 )
1416{
1417 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1418 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1419
1420 RECT localRect;
1421
1422 HPEN htextPen;
1423 HPEN holdPen;
1424 INT oldBkMode;
1425
1426 if (drawRect == NULL)
1427 {
1428 BOOL isVisible;
1429 RECT itemRect;
1430 RECT selectedRect;
1431
1432 /*
1433 * Get the rectangle for the item.
1434 */
1435 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1436 if (!isVisible)
1437 return;
1438
1439 /*
1440 * Make sure drawRect points to something valid; simplifies code.
1441 */
1442 drawRect = &localRect;
1443
1444 /*
1445 * This logic copied from the part of TAB_DrawItem which draws
1446 * the tab background. It's important to keep it in sync. I
1447 * would have liked to avoid code duplication, but couldn't figure
1448 * out how without making spaghetti of TAB_DrawItem.
1449 */
1450 if (lStyle & TCS_BUTTONS)
1451 {
1452 *drawRect = itemRect;
1453 if (iItem == infoPtr->iSelected)
1454 {
1455 drawRect->right--;
1456 drawRect->bottom--;
1457 }
1458 }
1459 else
1460 {
1461 if (iItem == infoPtr->iSelected)
1462 *drawRect = selectedRect;
1463 else
1464 *drawRect = itemRect;
1465 drawRect->right--;
1466 drawRect->bottom--;
1467 }
1468 }
1469
1470 /*
1471 * Text pen
1472 */
1473 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1474 holdPen = SelectObject(hdc, htextPen);
1475
1476 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1477 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1478 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1479
1480 /*
1481 * Deflate the rectangle to acount for the padding
1482 */
1483 if(lStyle & TCS_VERTICAL)
1484 InflateRect(drawRect, -VERTICAL_ITEM_PADDING, -HORIZONTAL_ITEM_PADDING);
1485 else
1486 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1487
1488
1489 /*
1490 * if owner draw, tell the owner to draw
1491 */
1492 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1493 {
1494 DRAWITEMSTRUCT dis;
1495 UINT id;
1496
1497 /*
1498 * get the control id
1499 */
1500 id = GetWindowLongA( hwnd, GWL_ID );
1501
1502 /*
1503 * put together the DRAWITEMSTRUCT
1504 */
1505 dis.CtlType = ODT_TAB;
1506 dis.CtlID = id;
1507 dis.itemID = iItem;
1508 dis.itemAction = ODA_DRAWENTIRE;
1509 if ( iItem == infoPtr->iSelected )
1510 dis.itemState = ODS_SELECTED;
1511 else
1512 dis.itemState = 0;
1513 dis.hwndItem = hwnd; /* */
1514 dis.hDC = hdc;
1515 dis.rcItem = *drawRect; /* */
1516 dis.itemData = infoPtr->items[iItem].lParam;
1517
1518 /*
1519 * send the draw message
1520 */
1521 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1522 }
1523 else
1524 {
1525 INT cx;
1526 INT cy;
1527 UINT uHorizAlign;
1528 RECT rcTemp;
1529 RECT rcImage;
1530 LOGFONTA logfont;
1531 HFONT hFont = 0;
1532 HFONT hOldFont = 0; /* stop uninitialized warning */
1533
1534 INT nEscapement = 0; /* stop uninitialized warning */
1535 INT nOrientation = 0; /* stop uninitialized warning */
1536 INT iPointSize;
1537
1538 /* used to center the icon and text in the tab */
1539 RECT rcText;
1540 INT center_offset;
1541
1542 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1543 rcImage = *drawRect;
1544
1545 rcTemp = *drawRect;
1546
1547 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1548
1549 /*
1550 * Setup for text output
1551 */
1552 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1553 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1554 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1555
1556 /* get the rectangle that the text fits in */
1557 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1558 &rcText, DT_CALCRECT);
1559 rcText.right += 4;
1560 /*
1561 * If not owner draw, then do the drawing ourselves.
1562 *
1563 * Draw the icon.
1564 */
1565 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1566 {
1567 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1568
1569 if(lStyle & TCS_VERTICAL)
1570 center_offset = ((drawRect->bottom - drawRect->top) - (cy + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1571 else
1572 center_offset = ((drawRect->right - drawRect->left) - (cx + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1573
1574 TRACE("for <%s>, c_o=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1575 debugstr_w(infoPtr->items[iItem].pszText), center_offset,
1576 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1577 (rcText.right-rcText.left));
1578
1579 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1580 {
1581 rcImage.top = drawRect->top + center_offset;
1582 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1583 /* right side of the tab, but the image still uses the left as its x position */
1584 /* this keeps the image always drawn off of the same side of the tab */
1585 drawRect->top = rcImage.top + (cx + HORIZONTAL_ITEM_PADDING);
1586 }
1587 else if(lStyle & TCS_VERTICAL)
1588 {
1589 rcImage.top = drawRect->bottom - cy - center_offset;
1590 rcImage.left--;
1591 drawRect->bottom = rcImage.top - HORIZONTAL_ITEM_PADDING;
1592 }
1593 else /* normal style, whether TCS_BOTTOM or not */
1594 {
1595 rcImage.left = drawRect->left + center_offset + 3;
1596 drawRect->left = rcImage.left + cx + HORIZONTAL_ITEM_PADDING;
1597 rcImage.top -= (lStyle & TCS_BOTTOM) ? 2 : 1;
1598 }
1599
1600 TRACE("drawing image=%d, left=%d, top=%d\n",
1601 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1602 ImageList_Draw
1603 (
1604 infoPtr->himl,
1605 infoPtr->items[iItem].iImage,
1606 hdc,
1607 rcImage.left,
1608 rcImage.top,
1609 ILD_NORMAL
1610 );
1611 } else /* no image, so just shift the drawRect borders around */
1612 {
1613 if(lStyle & TCS_VERTICAL)
1614 {
1615 center_offset = 0;
1616 /*
1617 currently the rcText rect is flawed because the rotated font does not
1618 often match the horizontal font. So leave this as 0
1619 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1620 */
1621 if(lStyle & TCS_BOTTOM)
1622 drawRect->top+=center_offset;
1623 else
1624 drawRect->bottom-=center_offset;
1625 }
1626 else
1627 {
1628 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1629 drawRect->left+=center_offset;
1630 }
1631 }
1632
1633 /* Draw the text */
1634 if (lStyle & TCS_RIGHTJUSTIFY)
1635 uHorizAlign = DT_CENTER;
1636 else
1637 uHorizAlign = DT_LEFT;
1638
1639 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1640 {
1641 if(lStyle & TCS_BOTTOM)
1642 {
1643 nEscapement = -900;
1644 nOrientation = -900;
1645 }
1646 else
1647 {
1648 nEscapement = 900;
1649 nOrientation = 900;
1650 }
1651 }
1652
1653 /* to get a font with the escapement and orientation we are looking for, we need to */
1654 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1655 if(lStyle & TCS_VERTICAL)
1656 {
1657 if (!GetObjectA((infoPtr->hFont) ?
1658 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1659 sizeof(LOGFONTA),&logfont))
1660 {
1661 iPointSize = 9;
1662
1663 lstrcpyA(logfont.lfFaceName, "Arial");
1664 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1665 72);
1666 logfont.lfWeight = FW_NORMAL;
1667 logfont.lfItalic = 0;
1668 logfont.lfUnderline = 0;
1669 logfont.lfStrikeOut = 0;
1670 }
1671
1672 logfont.lfEscapement = nEscapement;
1673 logfont.lfOrientation = nOrientation;
1674 hFont = CreateFontIndirectA(&logfont);
1675 hOldFont = SelectObject(hdc, hFont);
1676 }
1677
1678 if (lStyle & TCS_VERTICAL)
1679 {
1680 ExtTextOutW(hdc,
1681 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1682 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1683 ETO_CLIPPED,
1684 drawRect,
1685 infoPtr->items[iItem].pszText,
1686 lstrlenW(infoPtr->items[iItem].pszText),
1687 0);
1688 }
1689 else
1690 {
1691 DrawTextW
1692 (
1693 hdc,
1694 infoPtr->items[iItem].pszText,
1695 lstrlenW(infoPtr->items[iItem].pszText),
1696 drawRect,
1697 uHorizAlign | DT_SINGLELINE
1698 );
1699 }
1700
1701 /* clean things up */
1702 *drawRect = rcTemp; /* restore drawRect */
1703
1704 if(lStyle & TCS_VERTICAL)
1705 {
1706 SelectObject(hdc, hOldFont); /* restore the original font */
1707 if (hFont)
1708 DeleteObject(hFont);
1709 }
1710 }
1711
1712 /*
1713 * Cleanup
1714 */
1715 SetBkMode(hdc, oldBkMode);
1716 SelectObject(hdc, holdPen);
1717 DeleteObject( htextPen );
1718}
1719
1720/******************************************************************************
1721 * TAB_DrawItem
1722 *
1723 * This method is used to draw a single tab into the tab control.
1724 */
1725static void TAB_DrawItem(
1726 HWND hwnd,
1727 HDC hdc,
1728 INT iItem)
1729{
1730 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1731 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1732 RECT itemRect;
1733 RECT selectedRect;
1734 BOOL isVisible;
1735 RECT r, fillRect, r1;
1736 INT clRight = 0;
1737 INT clBottom = 0;
1738 COLORREF bkgnd, corner;
1739
1740 /*
1741 * Get the rectangle for the item.
1742 */
1743 isVisible = TAB_InternalGetItemRect(hwnd,
1744 infoPtr,
1745 iItem,
1746 &itemRect,
1747 &selectedRect);
1748
1749 if (isVisible)
1750 {
1751 /* If you need to see what the control is doing,
1752 * then override these variables. They will change what
1753 * fill colors are used for filling the tabs, and the
1754 * corners when drawing the edge.
1755 */
1756 bkgnd = comctl32_color.clrBtnFace;
1757 corner = comctl32_color.clrBtnFace;
1758
1759 if (lStyle & TCS_BUTTONS)
1760 {
1761 HBRUSH hbr = CreateSolidBrush (bkgnd);
1762 BOOL deleteBrush = TRUE;
1763
1764 /* Get item rectangle */
1765 r = itemRect;
1766
1767 /* Separators between flat buttons */
1768 if (lStyle & TCS_FLATBUTTONS)
1769 {
1770 r1 = r;
1771 r1.right += (FLAT_BTN_SPACINGX -2);
1772 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1773 }
1774
1775 if (iItem == infoPtr->iSelected)
1776 {
1777 /* Background color */
1778 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1779 {
1780 DeleteObject(hbr);
1781 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1782
1783 SetTextColor(hdc, comctl32_color.clr3dFace);
1784 SetBkColor(hdc, comctl32_color.clr3dHilight);
1785
1786 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1787 * we better use 0x55aa bitmap brush to make scrollbar's background
1788 * look different from the window background.
1789 */
1790 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1791 hbr = COMCTL32_hPattern55AABrush;
1792
1793 deleteBrush = FALSE;
1794 }
1795
1796 /* Clear interior */
1797 FillRect(hdc, &r, hbr);
1798
1799 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1800 }
1801 else /* ! selected */
1802 {
1803 if (!(lStyle & TCS_FLATBUTTONS))
1804 {
1805 /* Clear interior */
1806 FillRect(hdc, &r, hbr);
1807
1808 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1809 }
1810 }
1811
1812 /* Cleanup */
1813 if (deleteBrush) DeleteObject(hbr);
1814 }
1815 else /* !TCS_BUTTONS */
1816 {
1817 /* We draw a rectangle of different sizes depending on the selection
1818 * state. */
1819 if (iItem == infoPtr->iSelected) {
1820 RECT rect;
1821 GetClientRect (hwnd, &rect);
1822 clRight = rect.right;
1823 clBottom = rect.bottom;
1824 r = selectedRect;
1825 }
1826 else
1827 r = itemRect;
1828
1829 /*
1830 * Erase the background. (Delay it but setup rectangle.)
1831 * This is necessary when drawing the selected item since it is larger
1832 * than the others, it might overlap with stuff already drawn by the
1833 * other tabs
1834 */
1835 fillRect = r;
1836
1837 if(lStyle & TCS_VERTICAL)
1838 {
1839 /* These are for adjusting the drawing of a Selected tab */
1840 /* The initial values are for the normal case of non-Selected */
1841 int ZZ = 1; /* Do not strech if selected */
1842 if (iItem == infoPtr->iSelected) {
1843 ZZ = 0;
1844
1845 /* if leftmost draw the line longer */
1846 if(selectedRect.top == 0)
1847 fillRect.top += 2;
1848 /* if rightmost draw the line longer */
1849 if(selectedRect.bottom == clBottom)
1850 fillRect.bottom -= 2;
1851 }
1852
1853 if (lStyle & TCS_BOTTOM)
1854 {
1855 /* Adjust both rectangles to match native */
1856 r.left += (1-ZZ);
1857
1858 TRACE("<left> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
1859 iItem,
1860 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1861 r.left,r.top,r.right,r.bottom);
1862
1863 /* Clear interior */
1864 SetBkColor(hdc, bkgnd);
1865 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1866
1867 /* Draw rectangular edge around tab */
1868 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1869
1870 /* Now erase the top corner and draw diagonal edge */
1871 SetBkColor(hdc, corner);
1872 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1873 r1.top = r.top;
1874 r1.right = r.right;
1875 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1876 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1877 r1.right--;
1878 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1879
1880 /* Now erase the bottom corner and draw diagonal edge */
1881 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1882 r1.bottom = r.bottom;
1883 r1.right = r.right;
1884 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1885 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1886 r1.right--;
1887 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1888
1889 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1890 r1 = r;
1891 r1.right = r1.left;
1892 r1.left--;
1893 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1894 }
1895
1896 }
1897 else
1898 {
1899 /* Adjust both rectangles to match native */
1900 fillRect.right += (1-ZZ);
1901
1902 TRACE("<left> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
1903 iItem,
1904 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1905 r.left,r.top,r.right,r.bottom);
1906
1907 /* Clear interior */
1908 SetBkColor(hdc, bkgnd);
1909 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1910
1911 /* Draw rectangular edge around tab */
1912 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
1913
1914 /* Now erase the top corner and draw diagonal edge */
1915 SetBkColor(hdc, corner);
1916 r1.left = r.left;
1917 r1.top = r.top;
1918 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1919 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1920 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1921 r1.left++;
1922 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
1923
1924 /* Now erase the bottom corner and draw diagonal edge */
1925 r1.left = r.left;
1926 r1.bottom = r.bottom;
1927 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1928 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1929 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1930 r1.left++;
1931 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
1932 }
1933 }
1934 else /* ! TCS_VERTICAL */
1935 {
1936 /* These are for adjusting the drawing of a Selected tab */
1937 /* The initial values are for the normal case of non-Selected */
1938 int ZZ = 1; /* Do not strech if selected */
1939 if (iItem == infoPtr->iSelected) {
1940 ZZ = 0;
1941
1942 /* if leftmost draw the line longer */
1943 if(selectedRect.left == 0)
1944 fillRect.left += 2;
1945 /* if rightmost draw the line longer */
1946 if(selectedRect.right == clRight)
1947 fillRect.right -= 2;
1948 }
1949
1950 if (lStyle & TCS_BOTTOM)
1951 {
1952
1953 /* Adjust both rectangles to match native */
1954 fillRect.top--;
1955 fillRect.bottom--;
1956 r.bottom--;
1957 r.top -= ZZ;
1958
1959 TRACE("<bottom> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
1960 iItem,
1961 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1962 r.left,r.top,r.right,r.bottom);
1963
1964 /* Clear interior */
1965 SetBkColor(hdc, bkgnd);
1966 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1967
1968 /* Draw rectangular edge around tab */
1969 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
1970
1971 /* Now erase the righthand corner and draw diagonal edge */
1972 SetBkColor(hdc, corner);
1973 r1.left = r.right - ROUND_CORNER_SIZE;
1974 r1.bottom = r.bottom;
1975 r1.right = r.right;
1976 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1977 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1978 r1.bottom--;
1979 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1980
1981 /* Now erase the lefthand corner and draw diagonal edge */
1982 r1.left = r.left;
1983 r1.bottom = r.bottom;
1984 r1.right = r1.left + ROUND_CORNER_SIZE;
1985 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1986 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1987 r1.bottom--;
1988 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1989
1990 if ((iItem == infoPtr->iSelected) && (selectedRect.left == 0)) {
1991 r1 = r;
1992 r1.bottom = r1.top;
1993 r1.top--;
1994 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
1995 }
1996
1997 }
1998 else
1999 {
2000
2001 /* Adjust both rectangles to match native */
2002 fillRect.bottom += (1-ZZ);
2003
2004 TRACE("<top> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2005 iItem,
2006 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2007 r.left,r.top,r.right,r.bottom);
2008
2009 /* Clear interior */
2010 SetBkColor(hdc, bkgnd);
2011 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2012
2013 /* Draw rectangular edge around tab */
2014 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2015
2016 /* Now erase the righthand corner and draw diagonal edge */
2017 SetBkColor(hdc, corner);
2018 r1.left = r.right - ROUND_CORNER_SIZE;
2019 r1.top = r.top;
2020 r1.right = r.right;
2021 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2022 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2023 r1.top++;
2024 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2025
2026 /* Now erase the lefthand corner and draw diagonal edge */
2027 r1.left = r.left;
2028 r1.top = r.top;
2029 r1.right = r1.left + ROUND_CORNER_SIZE;
2030 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2031 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2032 r1.top++;
2033 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2034
2035 }
2036 }
2037 }
2038
2039 TAB_DumpItemInternal(infoPtr, iItem);
2040
2041 /* This modifies r to be the text rectangle. */
2042 {
2043 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
2044 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2045 SelectObject(hdc,hOldFont);
2046 }
2047
2048 /* Draw the focus rectangle */
2049 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
2050 (GetFocus() == hwnd) &&
2051 (iItem == infoPtr->uFocus) )
2052 {
2053 r = itemRect;
2054 InflateRect(&r, -1, -1);
2055
2056 DrawFocusRect(hdc, &r);
2057 }
2058 }
2059}
2060
2061/******************************************************************************
2062 * TAB_DrawBorder
2063 *
2064 * This method is used to draw the raised border around the tab control
2065 * "content" area.
2066 */
2067static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2068{
2069 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2070 RECT rect;
2071 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2072
2073 GetClientRect (hwnd, &rect);
2074
2075 /*
2076 * Adjust for the style
2077 */
2078
2079 if (infoPtr->uNumItem)
2080 {
2081 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2082 {
2083 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2084 }
2085 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2086 {
2087 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2088 }
2089 else if(lStyle & TCS_VERTICAL)
2090 {
2091 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2092 }
2093 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2094 {
2095 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2096 }
2097 }
2098
2099 TRACE("border=(%d,%d)-(%d,%d)\n",
2100 rect.left, rect.top, rect.right, rect.bottom);
2101
2102 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2103}
2104
2105/******************************************************************************
2106 * TAB_Refresh
2107 *
2108 * This method repaints the tab control..
2109 */
2110static void TAB_Refresh (HWND hwnd, HDC hdc)
2111{
2112 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2113 HFONT hOldFont;
2114 INT i;
2115
2116 if (!infoPtr->DoRedraw)
2117 return;
2118
2119 hOldFont = SelectObject (hdc, infoPtr->hFont);
2120
2121 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2122 {
2123 for (i = 0; i < infoPtr->uNumItem; i++)
2124 TAB_DrawItem (hwnd, hdc, i);
2125 }
2126 else
2127 {
2128 /* Draw all the non selected item first */
2129 for (i = 0; i < infoPtr->uNumItem; i++)
2130 {
2131 if (i != infoPtr->iSelected)
2132 TAB_DrawItem (hwnd, hdc, i);
2133 }
2134
2135 /* Now, draw the border, draw it before the selected item
2136 * since the selected item overwrites part of the border. */
2137 TAB_DrawBorder (hwnd, hdc);
2138
2139 /* Then, draw the selected item */
2140 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2141
2142 /* If we haven't set the current focus yet, set it now.
2143 * Only happens when we first paint the tab controls */
2144 if (infoPtr->uFocus == -1)
2145 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2146 }
2147
2148 SelectObject (hdc, hOldFont);
2149}
2150
2151static DWORD
2152TAB_GetRowCount (HWND hwnd )
2153{
2154 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2155
2156 return infoPtr->uNumRows;
2157}
2158
2159static LRESULT
2160TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2161{
2162 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2163
2164 infoPtr->DoRedraw=(BOOL) wParam;
2165 return 0;
2166}
2167
2168static LRESULT TAB_EraseBackground(
2169 HWND hwnd,
2170 HDC givenDC)
2171{
2172 HDC hdc;
2173 RECT clientRect;
2174
2175 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2176
2177 hdc = givenDC ? givenDC : GetDC(hwnd);
2178
2179 GetClientRect(hwnd, &clientRect);
2180
2181 FillRect(hdc, &clientRect, brush);
2182
2183 if (givenDC==0)
2184 ReleaseDC(hwnd, hdc);
2185
2186 DeleteObject(brush);
2187
2188 return 0;
2189}
2190
2191/******************************************************************************
2192 * TAB_EnsureSelectionVisible
2193 *
2194 * This method will make sure that the current selection is completely
2195 * visible by scrolling until it is.
2196 */
2197static void TAB_EnsureSelectionVisible(
2198 HWND hwnd,
2199 TAB_INFO* infoPtr)
2200{
2201 INT iSelected = infoPtr->iSelected;
2202 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2203 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2204
2205 /* set the items row to the bottommost row or topmost row depending on
2206 * style */
2207 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2208 {
2209 INT newselected;
2210 INT iTargetRow;
2211
2212 if(lStyle & TCS_VERTICAL)
2213 newselected = infoPtr->items[iSelected].rect.left;
2214 else
2215 newselected = infoPtr->items[iSelected].rect.top;
2216
2217 /* the target row is always (number of rows - 1)
2218 as row 0 is furthest from the clientRect */
2219 iTargetRow = infoPtr->uNumRows - 1;
2220
2221 if (newselected != iTargetRow)
2222 {
2223 INT i;
2224 if(lStyle & TCS_VERTICAL)
2225 {
2226 for (i=0; i < infoPtr->uNumItem; i++)
2227 {
2228 /* move everything in the row of the selected item to the iTargetRow */
2229 if (infoPtr->items[i].rect.left == newselected )
2230 infoPtr->items[i].rect.left = iTargetRow;
2231 else
2232 {
2233 if (infoPtr->items[i].rect.left > newselected)
2234 infoPtr->items[i].rect.left-=1;
2235 }
2236 }
2237 }
2238 else
2239 {
2240 for (i=0; i < infoPtr->uNumItem; i++)
2241 {
2242 if (infoPtr->items[i].rect.top == newselected )
2243 infoPtr->items[i].rect.top = iTargetRow;
2244 else
2245 {
2246 if (infoPtr->items[i].rect.top > newselected)
2247 infoPtr->items[i].rect.top-=1;
2248 }
2249 }
2250 }
2251 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2252 }
2253 }
2254
2255 /*
2256 * Do the trivial cases first.
2257 */
2258 if ( (!infoPtr->needsScrolling) ||
2259 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2260 return;
2261
2262 if (infoPtr->leftmostVisible >= iSelected)
2263 {
2264 infoPtr->leftmostVisible = iSelected;
2265 }
2266 else
2267 {
2268 RECT r;
2269 INT width, i;
2270
2271 /* Calculate the part of the client area that is visible */
2272 GetClientRect(hwnd, &r);
2273 width = r.right;
2274
2275 GetClientRect(infoPtr->hwndUpDown, &r);
2276 width -= r.right;
2277
2278 if ((infoPtr->items[iSelected].rect.right -
2279 infoPtr->items[iSelected].rect.left) >= width )
2280 {
2281 /* Special case: width of selected item is greater than visible
2282 * part of control.
2283 */
2284 infoPtr->leftmostVisible = iSelected;
2285 }
2286 else
2287 {
2288 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2289 {
2290 if ((infoPtr->items[iSelected].rect.right -
2291 infoPtr->items[i].rect.left) < width)
2292 break;
2293 }
2294 infoPtr->leftmostVisible = i;
2295 }
2296 }
2297
2298 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2299 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2300
2301 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2302 MAKELONG(infoPtr->leftmostVisible, 0));
2303}
2304
2305/******************************************************************************
2306 * TAB_InvalidateTabArea
2307 *
2308 * This method will invalidate the portion of the control that contains the
2309 * tabs. It is called when the state of the control changes and needs
2310 * to be redisplayed
2311 */
2312static void TAB_InvalidateTabArea(
2313 HWND hwnd,
2314 TAB_INFO* infoPtr)
2315{
2316 RECT clientRect;
2317 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2318 INT lastRow = infoPtr->uNumRows - 1;
2319
2320 if (lastRow < 0) return;
2321
2322 GetClientRect(hwnd, &clientRect);
2323
2324 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2325 {
2326 clientRect.top = clientRect.bottom -
2327 infoPtr->tabHeight -
2328 lastRow * (infoPtr->tabHeight - 2) -
2329 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2330 }
2331 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2332 {
2333 clientRect.left = clientRect.right - infoPtr->tabHeight -
2334 lastRow * (infoPtr->tabHeight - 2) -
2335 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2336 }
2337 else if(lStyle & TCS_VERTICAL)
2338 {
2339 clientRect.right = clientRect.left + infoPtr->tabHeight +
2340 lastRow * (infoPtr->tabHeight - 2) -
2341 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2342
2343 }
2344 else
2345 {
2346 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2347 lastRow * (infoPtr->tabHeight - 2) +
2348 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2349 }
2350
2351 TRACE("invalidate (%d,%d)-(%d,%d)\n",
2352 clientRect.left,clientRect.top,
2353 clientRect.right,clientRect.bottom);
2354 InvalidateRect(hwnd, &clientRect, TRUE);
2355}
2356
2357static LRESULT
2358TAB_Paint (HWND hwnd, WPARAM wParam)
2359{
2360 HDC hdc;
2361 PAINTSTRUCT ps;
2362
2363 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2364
2365 TRACE("erase %d, rect=(%d,%d)-(%d,%d)\n",
2366 ps.fErase,
2367 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2368
2369 if (ps.fErase)
2370 TAB_EraseBackground (hwnd, hdc);
2371
2372 TAB_Refresh (hwnd, hdc);
2373
2374 if(!wParam)
2375 EndPaint (hwnd, &ps);
2376
2377 return 0;
2378}
2379
2380static LRESULT
2381TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2382{
2383 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2384 TCITEMA *pti;
2385 INT iItem;
2386 RECT rect;
2387
2388 GetClientRect (hwnd, &rect);
2389 TRACE("Rect: %p T %i, L %i, B %i, R %i\n", hwnd,
2390 rect.top, rect.left, rect.bottom, rect.right);
2391
2392 pti = (TCITEMA *)lParam;
2393 iItem = (INT)wParam;
2394
2395 if (iItem < 0) return -1;
2396 if (iItem > infoPtr->uNumItem)
2397 iItem = infoPtr->uNumItem;
2398
2399 TAB_DumpItemExternalA(pti, iItem);
2400
2401
2402 if (infoPtr->uNumItem == 0) {
2403 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2404 infoPtr->uNumItem++;
2405 infoPtr->iSelected = 0;
2406 }
2407 else {
2408 TAB_ITEM *oldItems = infoPtr->items;
2409
2410 infoPtr->uNumItem++;
2411 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2412
2413 /* pre insert copy */
2414 if (iItem > 0) {
2415 memcpy (&infoPtr->items[0], &oldItems[0],
2416 iItem * sizeof(TAB_ITEM));
2417 }
2418
2419 /* post insert copy */
2420 if (iItem < infoPtr->uNumItem - 1) {
2421 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2422 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2423
2424 }
2425
2426 if (iItem <= infoPtr->iSelected)
2427 infoPtr->iSelected++;
2428
2429 COMCTL32_Free (oldItems);
2430 }
2431
2432 infoPtr->items[iItem].mask = pti->mask;
2433 if (pti->mask & TCIF_TEXT)
2434 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2435
2436 if (pti->mask & TCIF_IMAGE)
2437 infoPtr->items[iItem].iImage = pti->iImage;
2438
2439 if (pti->mask & TCIF_PARAM)
2440 infoPtr->items[iItem].lParam = pti->lParam;
2441
2442 TAB_SetItemBounds(hwnd);
2443 if (infoPtr->uNumItem > 1)
2444 TAB_InvalidateTabArea(hwnd, infoPtr);
2445 else
2446 InvalidateRect(hwnd, NULL, TRUE);
2447
2448 TRACE("[%p]: added item %d %s\n",
2449 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2450
2451 return iItem;
2452}
2453
2454
2455static LRESULT
2456TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2457{
2458 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2459 TCITEMW *pti;
2460 INT iItem;
2461 RECT rect;
2462
2463 GetClientRect (hwnd, &rect);
2464 TRACE("Rect: %p T %i, L %i, B %i, R %i\n", hwnd,
2465 rect.top, rect.left, rect.bottom, rect.right);
2466
2467 pti = (TCITEMW *)lParam;
2468 iItem = (INT)wParam;
2469
2470 if (iItem < 0) return -1;
2471 if (iItem > infoPtr->uNumItem)
2472 iItem = infoPtr->uNumItem;
2473
2474 TAB_DumpItemExternalW(pti, iItem);
2475
2476 if (infoPtr->uNumItem == 0) {
2477 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2478 infoPtr->uNumItem++;
2479 infoPtr->iSelected = 0;
2480 }
2481 else {
2482 TAB_ITEM *oldItems = infoPtr->items;
2483
2484 infoPtr->uNumItem++;
2485 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2486
2487 /* pre insert copy */
2488 if (iItem > 0) {
2489 memcpy (&infoPtr->items[0], &oldItems[0],
2490 iItem * sizeof(TAB_ITEM));
2491 }
2492
2493 /* post insert copy */
2494 if (iItem < infoPtr->uNumItem - 1) {
2495 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2496 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2497
2498 }
2499
2500 if (iItem <= infoPtr->iSelected)
2501 infoPtr->iSelected++;
2502
2503 COMCTL32_Free (oldItems);
2504 }
2505
2506 infoPtr->items[iItem].mask = pti->mask;
2507 if (pti->mask & TCIF_TEXT)
2508 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2509
2510 if (pti->mask & TCIF_IMAGE)
2511 infoPtr->items[iItem].iImage = pti->iImage;
2512
2513 if (pti->mask & TCIF_PARAM)
2514 infoPtr->items[iItem].lParam = pti->lParam;
2515
2516 TAB_SetItemBounds(hwnd);
2517 if (infoPtr->uNumItem > 1)
2518 TAB_InvalidateTabArea(hwnd, infoPtr);
2519 else
2520 InvalidateRect(hwnd, NULL, TRUE);
2521
2522 TRACE("[%p]: added item %d %s\n",
2523 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2524
2525 return iItem;
2526}
2527
2528
2529static LRESULT
2530TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2531{
2532 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2533 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2534 LONG lResult = 0;
2535
2536 TRACE("\n");
2537 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2538 {
2539 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2540 infoPtr->tabWidth = (INT)LOWORD(lParam);
2541 infoPtr->tabHeight = (INT)HIWORD(lParam);
2542 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2543 HIWORD(lResult), LOWORD(lResult),
2544 infoPtr->tabHeight, infoPtr->tabWidth);
2545 }
2546 infoPtr->fSizeSet = TRUE;
2547
2548 return lResult;
2549}
2550
2551static LRESULT
2552TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2553{
2554 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2555 TCITEMA *tabItem;
2556 TAB_ITEM *wineItem;
2557 INT iItem;
2558
2559 iItem = (INT)wParam;
2560 tabItem = (LPTCITEMA)lParam;
2561
2562 TRACE("%d %p\n", iItem, tabItem);
2563 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2564
2565 TAB_DumpItemExternalA(tabItem, iItem);
2566
2567 wineItem = &infoPtr->items[iItem];
2568
2569 if (tabItem->mask & TCIF_IMAGE)
2570 wineItem->iImage = tabItem->iImage;
2571
2572 if (tabItem->mask & TCIF_PARAM)
2573 wineItem->lParam = tabItem->lParam;
2574
2575 if (tabItem->mask & TCIF_RTLREADING)
2576 FIXME("TCIF_RTLREADING\n");
2577
2578 if (tabItem->mask & TCIF_STATE)
2579 wineItem->dwState = tabItem->dwState;
2580
2581 if (tabItem->mask & TCIF_TEXT)
2582 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2583
2584 /* Update and repaint tabs */
2585 TAB_SetItemBounds(hwnd);
2586 TAB_InvalidateTabArea(hwnd,infoPtr);
2587
2588 return TRUE;
2589 }
2590
2591
2592static LRESULT
2593TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2594{
2595 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2596 TCITEMW *tabItem;
2597 TAB_ITEM *wineItem;
2598 INT iItem;
2599
2600 iItem = (INT)wParam;
2601 tabItem = (LPTCITEMW)lParam;
2602
2603 TRACE("%d %p\n", iItem, tabItem);
2604 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2605
2606 TAB_DumpItemExternalW(tabItem, iItem);
2607
2608 wineItem = &infoPtr->items[iItem];
2609
2610 if (tabItem->mask & TCIF_IMAGE)
2611 wineItem->iImage = tabItem->iImage;
2612
2613 if (tabItem->mask & TCIF_PARAM)
2614 wineItem->lParam = tabItem->lParam;
2615
2616 if (tabItem->mask & TCIF_RTLREADING)
2617 FIXME("TCIF_RTLREADING\n");
2618
2619 if (tabItem->mask & TCIF_STATE)
2620 wineItem->dwState = tabItem->dwState;
2621
2622 if (tabItem->mask & TCIF_TEXT)
2623 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2624
2625 /* Update and repaint tabs */
2626 TAB_SetItemBounds(hwnd);
2627 TAB_InvalidateTabArea(hwnd,infoPtr);
2628
2629 return TRUE;
2630}
2631
2632
2633static LRESULT
2634TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2635{
2636 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2637
2638 return infoPtr->uNumItem;
2639}
2640
2641
2642static LRESULT
2643TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2644{
2645 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2646 TCITEMA *tabItem;
2647 TAB_ITEM *wineItem;
2648 INT iItem;
2649
2650 iItem = (INT)wParam;
2651 tabItem = (LPTCITEMA)lParam;
2652 TRACE("\n");
2653 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2654 return FALSE;
2655
2656 wineItem = &infoPtr->items[iItem];
2657
2658 if (tabItem->mask & TCIF_IMAGE)
2659 tabItem->iImage = wineItem->iImage;
2660
2661 if (tabItem->mask & TCIF_PARAM)
2662 tabItem->lParam = wineItem->lParam;
2663
2664 if (tabItem->mask & TCIF_RTLREADING)
2665 FIXME("TCIF_RTLREADING\n");
2666
2667 if (tabItem->mask & TCIF_STATE)
2668 tabItem->dwState = wineItem->dwState;
2669
2670 if (tabItem->mask & TCIF_TEXT)
2671 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2672
2673 TAB_DumpItemExternalA(tabItem, iItem);
2674
2675 return TRUE;
2676}
2677
2678
2679static LRESULT
2680TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2681{
2682 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2683 TCITEMW *tabItem;
2684 TAB_ITEM *wineItem;
2685 INT iItem;
2686
2687 iItem = (INT)wParam;
2688 tabItem = (LPTCITEMW)lParam;
2689 TRACE("\n");
2690 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2691 return FALSE;
2692
2693 wineItem=& infoPtr->items[iItem];
2694
2695 if (tabItem->mask & TCIF_IMAGE)
2696 tabItem->iImage = wineItem->iImage;
2697
2698 if (tabItem->mask & TCIF_PARAM)
2699 tabItem->lParam = wineItem->lParam;
2700
2701 if (tabItem->mask & TCIF_RTLREADING)
2702 FIXME("TCIF_RTLREADING\n");
2703
2704 if (tabItem->mask & TCIF_STATE)
2705 tabItem->dwState = wineItem->dwState;
2706
2707 if (tabItem->mask & TCIF_TEXT)
2708 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2709
2710 TAB_DumpItemExternalW(tabItem, iItem);
2711
2712 return TRUE;
2713}
2714
2715
2716static LRESULT
2717TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2718{
2719 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2720 INT iItem = (INT) wParam;
2721 BOOL bResult = FALSE;
2722
2723 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2724 {
2725 TAB_ITEM *oldItems = infoPtr->items;
2726
2727 infoPtr->uNumItem--;
2728 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2729
2730 if (iItem > 0)
2731 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2732
2733 if (iItem < infoPtr->uNumItem)
2734 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2735 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2736
2737 COMCTL32_Free(oldItems);
2738
2739 /* Readjust the selected index */
2740 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2741 infoPtr->iSelected--;
2742
2743 if (iItem < infoPtr->iSelected)
2744 infoPtr->iSelected--;
2745
2746 if (infoPtr->uNumItem == 0)
2747 infoPtr->iSelected = -1;
2748
2749 /* Reposition and repaint tabs */
2750 TAB_SetItemBounds(hwnd);
2751 TAB_InvalidateTabArea(hwnd,infoPtr);
2752
2753 bResult = TRUE;
2754 }
2755
2756 return bResult;
2757}
2758
2759static LRESULT
2760TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2761{
2762 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2763
2764 COMCTL32_Free (infoPtr->items);
2765 infoPtr->uNumItem = 0;
2766 infoPtr->iSelected = -1;
2767 if (infoPtr->iHotTracked >= 0)
2768 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2769 infoPtr->iHotTracked = -1;
2770
2771 TAB_SetItemBounds(hwnd);
2772 TAB_InvalidateTabArea(hwnd,infoPtr);
2773 return TRUE;
2774}
2775
2776
2777static LRESULT
2778TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2779{
2780 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2781
2782 TRACE("\n");
2783 return (LRESULT)infoPtr->hFont;
2784}
2785
2786static LRESULT
2787TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2788
2789{
2790 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2791
2792 TRACE("%x %lx\n",wParam, lParam);
2793
2794 infoPtr->hFont = (HFONT)wParam;
2795
2796 TAB_SetItemBounds(hwnd);
2797
2798 TAB_InvalidateTabArea(hwnd, infoPtr);
2799
2800 return 0;
2801}
2802
2803
2804static LRESULT
2805TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2806{
2807 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2808
2809 TRACE("\n");
2810 return (LRESULT)infoPtr->himl;
2811}
2812
2813static LRESULT
2814TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2815{
2816 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2817 HIMAGELIST himlPrev;
2818
2819 TRACE("\n");
2820 himlPrev = infoPtr->himl;
2821 infoPtr->himl= (HIMAGELIST)lParam;
2822 return (LRESULT)himlPrev;
2823}
2824
2825static LRESULT
2826TAB_GetUnicodeFormat (HWND hwnd)
2827{
2828 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2829 return infoPtr->bUnicode;
2830}
2831
2832static LRESULT
2833TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2834{
2835 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2836 BOOL bTemp = infoPtr->bUnicode;
2837
2838 infoPtr->bUnicode = (BOOL)wParam;
2839
2840 return bTemp;
2841}
2842
2843static LRESULT
2844TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2845
2846{
2847/* I'm not really sure what the following code was meant to do.
2848 This is what it is doing:
2849 When WM_SIZE is sent with SIZE_RESTORED, the control
2850 gets positioned in the top left corner.
2851
2852 RECT parent_rect;
2853 HWND parent;
2854 UINT uPosFlags,cx,cy;
2855
2856 uPosFlags=0;
2857 if (!wParam) {
2858 parent = GetParent (hwnd);
2859 GetClientRect(parent, &parent_rect);
2860 cx=LOWORD (lParam);
2861 cy=HIWORD (lParam);
2862 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2863 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2864
2865 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2866 cx, cy, uPosFlags | SWP_NOZORDER);
2867 } else {
2868 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2869 } */
2870
2871 /* Recompute the size/position of the tabs. */
2872 TAB_SetItemBounds (hwnd);
2873
2874 /* Force a repaint of the control. */
2875 InvalidateRect(hwnd, NULL, TRUE);
2876
2877 return 0;
2878}
2879
2880
2881static LRESULT
2882TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2883{
2884 TAB_INFO *infoPtr;
2885 TEXTMETRICA fontMetrics;
2886 HDC hdc;
2887 HFONT hOldFont;
2888 DWORD dwStyle;
2889
2890 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2891
2892 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2893
2894 infoPtr->uNumItem = 0;
2895 infoPtr->uNumRows = 0;
2896 infoPtr->hFont = 0;
2897 infoPtr->items = 0;
2898 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2899 infoPtr->iSelected = -1;
2900 infoPtr->iHotTracked = -1;
2901 infoPtr->uFocus = -1;
2902 infoPtr->hwndToolTip = 0;
2903 infoPtr->DoRedraw = TRUE;
2904 infoPtr->needsScrolling = FALSE;
2905 infoPtr->hwndUpDown = 0;
2906 infoPtr->leftmostVisible = 0;
2907 infoPtr->fSizeSet = FALSE;
2908 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2909
2910 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2911
2912 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2913 if you don't specify it in CreateWindow. This is necessary in
2914 order for paint to work correctly. This follows windows behaviour. */
2915 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2916 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2917
2918 if (dwStyle & TCS_TOOLTIPS) {
2919 /* Create tooltip control */
2920 infoPtr->hwndToolTip =
2921 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2922 CW_USEDEFAULT, CW_USEDEFAULT,
2923 CW_USEDEFAULT, CW_USEDEFAULT,
2924 hwnd, 0, 0, 0);
2925
2926 /* Send NM_TOOLTIPSCREATED notification */
2927 if (infoPtr->hwndToolTip) {
2928 NMTOOLTIPSCREATED nmttc;
2929
2930 nmttc.hdr.hwndFrom = hwnd;
2931 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2932 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2933 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2934
2935 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2936 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2937 }
2938 }
2939
2940 /*
2941 * We need to get text information so we need a DC and we need to select
2942 * a font.
2943 */
2944 hdc = GetDC(hwnd);
2945 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2946
2947 /* Use the system font to determine the initial height of a tab. */
2948 GetTextMetricsA(hdc, &fontMetrics);
2949
2950 /*
2951 * Make sure there is enough space for the letters + growing the
2952 * selected item + extra space for the selected item.
2953 */
2954 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2955 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2956 VERTICAL_ITEM_PADDING;
2957
2958 /* Initialize the width of a tab. */
2959 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2960
2961 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2962
2963 SelectObject (hdc, hOldFont);
2964 ReleaseDC(hwnd, hdc);
2965
2966 return 0;
2967}
2968
2969static LRESULT
2970TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2971{
2972 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2973 INT iItem;
2974
2975 if (!infoPtr)
2976 return 0;
2977
2978 if (infoPtr->items) {
2979 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2980 if (infoPtr->items[iItem].pszText)
2981 COMCTL32_Free (infoPtr->items[iItem].pszText);
2982 }
2983 COMCTL32_Free (infoPtr->items);
2984 }
2985
2986 if (infoPtr->hwndToolTip)
2987 DestroyWindow (infoPtr->hwndToolTip);
2988
2989 if (infoPtr->hwndUpDown)
2990 DestroyWindow(infoPtr->hwndUpDown);
2991
2992 if (infoPtr->iHotTracked >= 0)
2993 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2994
2995 COMCTL32_Free (infoPtr);
2996 SetWindowLongA(hwnd, 0, 0);
2997 return 0;
2998}
2999
3000static LRESULT WINAPI
3001TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3002{
3003
3004 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3005 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3006 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3007
3008 switch (uMsg)
3009 {
3010 case TCM_GETIMAGELIST:
3011 return TAB_GetImageList (hwnd, wParam, lParam);
3012
3013 case TCM_SETIMAGELIST:
3014 return TAB_SetImageList (hwnd, wParam, lParam);
3015
3016 case TCM_GETITEMCOUNT:
3017 return TAB_GetItemCount (hwnd, wParam, lParam);
3018
3019 case TCM_GETITEMA:
3020 return TAB_GetItemA (hwnd, wParam, lParam);
3021
3022 case TCM_GETITEMW:
3023 return TAB_GetItemW (hwnd, wParam, lParam);
3024
3025 case TCM_SETITEMA:
3026 return TAB_SetItemA (hwnd, wParam, lParam);
3027
3028 case TCM_SETITEMW:
3029 return TAB_SetItemW (hwnd, wParam, lParam);
3030
3031 case TCM_DELETEITEM:
3032 return TAB_DeleteItem (hwnd, wParam, lParam);
3033
3034 case TCM_DELETEALLITEMS:
3035 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3036
3037 case TCM_GETITEMRECT:
3038 return TAB_GetItemRect (hwnd, wParam, lParam);
3039
3040 case TCM_GETCURSEL:
3041 return TAB_GetCurSel (hwnd);
3042
3043 case TCM_HITTEST:
3044 return TAB_HitTest (hwnd, wParam, lParam);
3045
3046 case TCM_SETCURSEL:
3047 return TAB_SetCurSel (hwnd, wParam);
3048
3049 case TCM_INSERTITEMA:
3050 return TAB_InsertItemA (hwnd, wParam, lParam);
3051
3052 case TCM_INSERTITEMW:
3053 return TAB_InsertItemW (hwnd, wParam, lParam);
3054
3055 case TCM_SETITEMEXTRA:
3056 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3057 return 0;
3058
3059 case TCM_ADJUSTRECT:
3060 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3061
3062 case TCM_SETITEMSIZE:
3063 return TAB_SetItemSize (hwnd, wParam, lParam);
3064
3065 case TCM_REMOVEIMAGE:
3066 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3067 return 0;
3068
3069 case TCM_SETPADDING:
3070 FIXME("Unimplemented msg TCM_SETPADDING\n");
3071 return 0;
3072
3073 case TCM_GETROWCOUNT:
3074 return TAB_GetRowCount(hwnd);
3075
3076 case TCM_GETUNICODEFORMAT:
3077 return TAB_GetUnicodeFormat (hwnd);
3078
3079 case TCM_SETUNICODEFORMAT:
3080 return TAB_SetUnicodeFormat (hwnd, wParam);
3081
3082 case TCM_HIGHLIGHTITEM:
3083 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
3084 return 0;
3085
3086 case TCM_GETTOOLTIPS:
3087 return TAB_GetToolTips (hwnd, wParam, lParam);
3088
3089 case TCM_SETTOOLTIPS:
3090 return TAB_SetToolTips (hwnd, wParam, lParam);
3091
3092 case TCM_GETCURFOCUS:
3093 return TAB_GetCurFocus (hwnd);
3094
3095 case TCM_SETCURFOCUS:
3096 return TAB_SetCurFocus (hwnd, wParam);
3097
3098 case TCM_SETMINTABWIDTH:
3099 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
3100 return 0;
3101
3102 case TCM_DESELECTALL:
3103 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3104 return 0;
3105
3106 case TCM_GETEXTENDEDSTYLE:
3107 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3108 return 0;
3109
3110 case TCM_SETEXTENDEDSTYLE:
3111 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3112 return 0;
3113
3114 case WM_GETFONT:
3115 return TAB_GetFont (hwnd, wParam, lParam);
3116
3117 case WM_SETFONT:
3118 return TAB_SetFont (hwnd, wParam, lParam);
3119
3120 case WM_CREATE:
3121 return TAB_Create (hwnd, wParam, lParam);
3122
3123 case WM_NCDESTROY:
3124 return TAB_Destroy (hwnd, wParam, lParam);
3125
3126 case WM_GETDLGCODE:
3127 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3128
3129 case WM_LBUTTONDOWN:
3130 return TAB_LButtonDown (hwnd, wParam, lParam);
3131
3132 case WM_LBUTTONUP:
3133 return TAB_LButtonUp (hwnd, wParam, lParam);
3134
3135 case WM_NOTIFY:
3136 return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
3137
3138 case WM_RBUTTONDOWN:
3139 return TAB_RButtonDown (hwnd, wParam, lParam);
3140
3141 case WM_MOUSEMOVE:
3142 return TAB_MouseMove (hwnd, wParam, lParam);
3143
3144 case WM_ERASEBKGND:
3145 return TAB_EraseBackground (hwnd, (HDC)wParam);
3146
3147 case WM_PAINT:
3148 return TAB_Paint (hwnd, wParam);
3149
3150 case WM_SIZE:
3151 return TAB_Size (hwnd, wParam, lParam);
3152
3153 case WM_SETREDRAW:
3154 return TAB_SetRedraw (hwnd, wParam);
3155
3156 case WM_HSCROLL:
3157 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3158
3159 case WM_STYLECHANGED:
3160 TAB_SetItemBounds (hwnd);
3161 InvalidateRect(hwnd, NULL, TRUE);
3162 return 0;
3163
3164 case WM_SYSCOLORCHANGE:
3165 COMCTL32_RefreshSysColors();
3166 return 0;
3167
3168 case WM_KILLFOCUS:
3169 case WM_SETFOCUS:
3170 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3171
3172 case WM_KEYUP:
3173 return TAB_KeyUp(hwnd, wParam);
3174 case WM_NCHITTEST:
3175 return TAB_NCHitTest(hwnd, lParam);
3176
3177 default:
3178 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3179 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3180 uMsg, wParam, lParam);
3181 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3182 }
3183
3184 return 0;
3185}
3186
3187
3188VOID
3189TAB_Register (void)
3190{
3191 WNDCLASSA wndClass;
3192
3193 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3194 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3195 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3196 wndClass.cbClsExtra = 0;
3197 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3198 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3199 wndClass.hbrBackground = (HBRUSH)NULL;
3200 wndClass.lpszClassName = WC_TABCONTROLA;
3201
3202 RegisterClassA (&wndClass);
3203}
3204
3205
3206VOID
3207TAB_Unregister (void)
3208{
3209 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);
3210}
Note: See TracBrowser for help on using the repository browser.