source: trunk/src/comctl32/tab.c

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

Wine resync

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