Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "tools/debug.hxx"
21 : #include "tools/rc.h"
22 :
23 : #include "vcl/svapp.hxx"
24 : #include "vcl/help.hxx"
25 : #include "vcl/event.hxx"
26 : #include "vcl/menu.hxx"
27 : #include "vcl/button.hxx"
28 : #include "vcl/tabpage.hxx"
29 : #include "vcl/tabctrl.hxx"
30 : #include "vcl/controllayout.hxx"
31 : #include "vcl/layout.hxx"
32 : #include "vcl/lstbox.hxx"
33 : #include "vcl/settings.hxx"
34 :
35 : #include "controldata.hxx"
36 : #include "svdata.hxx"
37 : #include "window.h"
38 :
39 : #include <boost/unordered_map.hpp>
40 : #include <vector>
41 :
42 1508 : struct ImplTabItem
43 : {
44 : sal_uInt16 mnId;
45 : TabPage* mpTabPage;
46 : OUString maText;
47 : OUString maFormatText;
48 : OUString maHelpText;
49 : OString maHelpId;
50 : OString maTabName;
51 : Rectangle maRect;
52 : sal_uInt16 mnLine;
53 : bool mbFullVisible;
54 : bool mbEnabled;
55 : Image maTabImage;
56 :
57 260 : ImplTabItem()
58 : : mnId( 0 ), mpTabPage( NULL ),
59 260 : mnLine( 0 ), mbFullVisible( false ), mbEnabled( true )
60 260 : {}
61 : };
62 :
63 112 : struct ImplTabCtrlData
64 : {
65 : boost::unordered_map< int, int > maLayoutPageIdToLine;
66 : boost::unordered_map< int, int > maLayoutLineToPageId;
67 : std::vector< Rectangle > maTabRectangles;
68 : Point maItemsOffset; // offset of the tabitems
69 : std::vector< ImplTabItem > maItemList;
70 : ListBox* mpListBox;
71 : };
72 :
73 : #define TAB_OFFSET 3
74 : #define TAB_TABOFFSET_X 3
75 : #define TAB_TABOFFSET_Y 3
76 : #define TAB_EXTRASPACE_X 6
77 : #define TAB_BORDER_LEFT 1
78 : #define TAB_BORDER_TOP 1
79 : #define TAB_BORDER_RIGHT 2
80 : #define TAB_BORDER_BOTTOM 2
81 :
82 : // for the Tab positions
83 : #define TAB_PAGERECT 0xFFFF
84 :
85 56 : void TabControl::ImplInit( vcl::Window* pParent, WinBits nStyle )
86 : {
87 56 : mbLayoutDirty = true;
88 :
89 56 : if ( !(nStyle & WB_NOTABSTOP) )
90 56 : nStyle |= WB_TABSTOP;
91 56 : if ( !(nStyle & WB_NOGROUP) )
92 56 : nStyle |= WB_GROUP;
93 56 : if ( !(nStyle & WB_NODIALOGCONTROL) )
94 56 : nStyle |= WB_DIALOGCONTROL;
95 :
96 56 : Control::ImplInit( pParent, nStyle, NULL );
97 :
98 56 : mnLastWidth = 0;
99 56 : mnLastHeight = 0;
100 56 : mnBtnSize = 0;
101 56 : mnMaxPageWidth = 0;
102 56 : mnActPageId = 0;
103 56 : mnCurPageId = 0;
104 56 : mbFormat = true;
105 56 : mbRestoreHelpId = false;
106 56 : mbRestoreUnqId = false;
107 56 : mbSmallInvalidate = false;
108 56 : mpTabCtrlData = new ImplTabCtrlData;
109 56 : mpTabCtrlData->mpListBox = NULL;
110 :
111 56 : ImplInitSettings( true, true, true );
112 :
113 56 : if( (nStyle & WB_DROPDOWN) )
114 : {
115 0 : mpTabCtrlData->mpListBox = new ListBox( this, WB_DROPDOWN );
116 0 : mpTabCtrlData->mpListBox->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
117 0 : mpTabCtrlData->mpListBox->SetSelectHdl( LINK( this, TabControl, ImplListBoxSelectHdl ) );
118 0 : mpTabCtrlData->mpListBox->Show();
119 : }
120 :
121 : // if the tabcontrol is drawn (ie filled) by a native widget, make sure all contols will have transparent background
122 : // otherwise they will paint with a wrong background
123 56 : if( IsNativeControlSupported(CTRL_TAB_PANE, PART_ENTIRE_CONTROL) )
124 0 : EnableChildTransparentMode( true );
125 :
126 56 : if (pParent && pParent->IsDialog())
127 0 : pParent->AddChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
128 56 : }
129 :
130 58 : const vcl::Font& TabControl::GetCanonicalFont( const StyleSettings& _rStyle ) const
131 : {
132 58 : return _rStyle.GetAppFont();
133 : }
134 :
135 58 : const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
136 : {
137 58 : return _rStyle.GetButtonTextColor();
138 : }
139 :
140 58 : void TabControl::ImplInitSettings( bool bFont,
141 : bool bForeground, bool bBackground )
142 : {
143 58 : Control::ImplInitSettings( bFont, bForeground );
144 :
145 58 : if ( bBackground )
146 : {
147 58 : vcl::Window* pParent = GetParent();
148 116 : if ( !IsControlBackground() &&
149 58 : (pParent->IsChildTransparentModeEnabled()
150 58 : || IsNativeControlSupported(CTRL_TAB_PANE, PART_ENTIRE_CONTROL)
151 58 : || IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL) ) )
152 :
153 : {
154 : // set transparent mode for NWF tabcontrols to have
155 : // the background always cleared properly
156 0 : EnableChildTransparentMode( true );
157 0 : SetParentClipMode( PARENTCLIPMODE_NOCLIP );
158 0 : SetPaintTransparent( true );
159 0 : SetBackground();
160 0 : ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
161 : }
162 : else
163 : {
164 58 : EnableChildTransparentMode( false );
165 58 : SetParentClipMode( 0 );
166 58 : SetPaintTransparent( false );
167 :
168 58 : if ( IsControlBackground() )
169 0 : SetBackground( GetControlBackground() );
170 : else
171 58 : SetBackground( pParent->GetBackground() );
172 : }
173 : }
174 58 : }
175 :
176 1112 : void TabControl::ImplFreeLayoutData()
177 : {
178 1112 : if( HasLayoutData() )
179 : {
180 0 : ImplClearLayoutData();
181 0 : mpTabCtrlData->maLayoutPageIdToLine.clear();
182 0 : mpTabCtrlData->maLayoutLineToPageId.clear();
183 : }
184 1112 : }
185 :
186 56 : TabControl::TabControl( vcl::Window* pParent, WinBits nStyle ) :
187 56 : Control( WINDOW_TABCONTROL )
188 : {
189 56 : ImplInit( pParent, nStyle );
190 : OSL_TRACE("*** TABCONTROL no notabs? %s", ( GetStyle() & WB_NOBORDER ) ? "true" : "false" );
191 56 : }
192 :
193 112 : TabControl::~TabControl()
194 : {
195 56 : Window *pParent = GetParent();
196 56 : if (pParent && pParent->IsDialog())
197 0 : GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
198 :
199 56 : ImplFreeLayoutData();
200 :
201 : // delete TabCtrl data
202 56 : if ( mpTabCtrlData )
203 : {
204 56 : if( mpTabCtrlData->mpListBox )
205 0 : delete mpTabCtrlData->mpListBox;
206 56 : delete mpTabCtrlData;
207 : }
208 56 : }
209 :
210 3536 : ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
211 : {
212 21840 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
213 14560 : it != mpTabCtrlData->maItemList.end(); ++it )
214 : {
215 7176 : if( it->mnId == nId )
216 3432 : return &(*it);
217 : }
218 :
219 104 : return NULL;
220 : }
221 :
222 3404 : Size TabControl::ImplGetItemSize( ImplTabItem* pItem, long nMaxWidth )
223 : {
224 3404 : pItem->maFormatText = pItem->maText;
225 3404 : Size aSize( GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
226 3404 : Size aImageSize( 0, 0 );
227 3404 : if( !!pItem->maTabImage )
228 : {
229 0 : aImageSize = pItem->maTabImage.GetSizePixel();
230 0 : if( !pItem->maFormatText.isEmpty() )
231 0 : aImageSize.Width() += GetTextHeight()/4;
232 : }
233 3404 : aSize.Width() += aImageSize.Width();
234 3404 : if( aImageSize.Height() > aSize.Height() )
235 0 : aSize.Height() = aImageSize.Height();
236 :
237 3404 : aSize.Width() += TAB_TABOFFSET_X*2;
238 3404 : aSize.Height() += TAB_TABOFFSET_Y*2;
239 :
240 3404 : Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
241 3404 : Rectangle aBoundingRgn, aContentRgn;
242 3404 : const ImplControlValue aControlValue;
243 6808 : if(GetNativeControlRegion( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, aCtrlRegion,
244 : CTRL_STATE_ENABLED, aControlValue, OUString(),
245 6808 : aBoundingRgn, aContentRgn ) )
246 : {
247 0 : return aContentRgn.GetSize();
248 : }
249 :
250 : // For languages with short names (e.g. Chinese), because the space is
251 : // normally only one pixel per char
252 3404 : if ( pItem->maFormatText.getLength() < TAB_EXTRASPACE_X )
253 660 : aSize.Width() += TAB_EXTRASPACE_X-pItem->maFormatText.getLength();
254 :
255 : // shorten Text if needed
256 3404 : if ( aSize.Width()+4 >= nMaxWidth )
257 : {
258 104 : OUString aAppendStr("...");
259 104 : pItem->maFormatText += aAppendStr;
260 624 : do
261 : {
262 624 : pItem->maFormatText = pItem->maFormatText.replaceAt( pItem->maFormatText.getLength()-aAppendStr.getLength()-1, 1, "" );
263 624 : aSize.Width() = GetCtrlTextWidth( pItem->maFormatText );
264 624 : aSize.Width() += aImageSize.Width();
265 624 : aSize.Width() += TAB_TABOFFSET_X*2;
266 : }
267 624 : while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.getLength() > aAppendStr.getLength()) );
268 104 : if ( aSize.Width()+4 >= nMaxWidth )
269 : {
270 104 : pItem->maFormatText = ".";
271 104 : aSize.Width() = 1;
272 104 : }
273 : }
274 :
275 3404 : if( pItem->maFormatText.isEmpty() )
276 : {
277 0 : if( aSize.Height() < aImageSize.Height()+4 ) //leave space for focus rect
278 0 : aSize.Height() = aImageSize.Height()+4;
279 : }
280 :
281 3404 : return aSize;
282 : }
283 :
284 : // Feel free to move this to some more general place for reuse
285 : // http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
286 : // Mostly based on Alexey Frunze's nifty example at
287 : // http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
288 : namespace MinimumRaggednessWrap
289 : {
290 382 : std::deque<size_t> GetEndOfLineIndexes(const std::vector<sal_Int32>& rWidthsOf, sal_Int32 nLineWidth)
291 : {
292 382 : ++nLineWidth;
293 :
294 382 : size_t nWidthsCount = rWidthsOf.size();
295 382 : std::vector<sal_Int32> aCosts(nWidthsCount * nWidthsCount);
296 :
297 : // cost function c(i, j) that computes the cost of a line consisting of
298 : // the words Word[i] to Word[j]
299 2084 : for (size_t i = 0; i < nWidthsCount; ++i)
300 : {
301 10004 : for (size_t j = 0; j < nWidthsCount; ++j)
302 : {
303 8302 : if (j >= i)
304 : {
305 5002 : sal_Int32 c = nLineWidth - (j - i);
306 16604 : for (size_t k = i; k <= j; ++k)
307 11602 : c -= rWidthsOf[k];
308 5002 : c = (c >= 0) ? c * c : SAL_MAX_INT32;
309 5002 : aCosts[j * nWidthsCount + i] = c;
310 : }
311 : else
312 : {
313 3300 : aCosts[j * nWidthsCount + i] = SAL_MAX_INT32;
314 : }
315 : }
316 : }
317 :
318 764 : std::vector<sal_Int32> aFunction(nWidthsCount);
319 764 : std::vector<sal_Int32> aWrapPoints(nWidthsCount);
320 :
321 : // f(j) in aFunction[], collect wrap points in aWrapPoints[]
322 2084 : for (size_t j = 0; j < nWidthsCount; ++j)
323 : {
324 1702 : aFunction[j] = aCosts[j * nWidthsCount];
325 1702 : if (aFunction[j] == SAL_MAX_INT32)
326 : {
327 52 : for (size_t k = 0; k < j; ++k)
328 : {
329 : sal_Int32 s;
330 0 : if (aFunction[k] == SAL_MAX_INT32 || aCosts[j * nWidthsCount + k + 1] == SAL_MAX_INT32)
331 0 : s = SAL_MAX_INT32;
332 : else
333 0 : s = aFunction[k] + aCosts[j * nWidthsCount + k + 1];
334 0 : if (aFunction[j] > s)
335 : {
336 0 : aFunction[j] = s;
337 0 : aWrapPoints[j] = k + 1;
338 : }
339 : }
340 : }
341 : }
342 :
343 382 : std::deque<size_t> aSolution;
344 :
345 : // no solution
346 382 : if (aFunction[nWidthsCount - 1] == SAL_MAX_INT32)
347 52 : return aSolution;
348 :
349 : // optimal solution
350 330 : size_t j = nWidthsCount - 1;
351 : while (true)
352 : {
353 330 : aSolution.push_front(j);
354 330 : if (!aWrapPoints[j])
355 330 : break;
356 0 : j = aWrapPoints[j] - 1;
357 : }
358 :
359 712 : return aSolution;
360 : }
361 : };
362 :
363 1034 : Rectangle TabControl::ImplGetTabRect( sal_uInt16 nItemPos, long nWidth, long nHeight )
364 : {
365 1034 : Size aWinSize = Control::GetOutputSizePixel();
366 1034 : if ( nWidth < 0 )
367 465 : nWidth = aWinSize.Width();
368 1034 : if ( nHeight < 0 )
369 465 : nHeight = aWinSize.Height();
370 :
371 1034 : if ( mpTabCtrlData->maItemList.empty() )
372 : {
373 0 : long nW = nWidth-TAB_OFFSET*2;
374 0 : long nH = nHeight-TAB_OFFSET*2;
375 0 : return (nW > 0 && nH > 0)
376 : ? Rectangle( Point( TAB_OFFSET, TAB_OFFSET ), Size( nW, nH ) )
377 0 : : Rectangle();
378 : }
379 :
380 1034 : if ( nItemPos == TAB_PAGERECT )
381 : {
382 : sal_uInt16 nLastPos;
383 517 : if ( mnCurPageId )
384 517 : nLastPos = GetPagePos( mnCurPageId );
385 : else
386 0 : nLastPos = 0;
387 :
388 517 : Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
389 517 : long nW = nWidth-TAB_OFFSET*2;
390 517 : long nH = nHeight-aRect.Bottom()-TAB_OFFSET*2;
391 379 : aRect = (nW > 0 && nH > 0)
392 168 : ? Rectangle( Point( TAB_OFFSET, aRect.Bottom()+TAB_OFFSET ), Size( nW, nH ) )
393 853 : : Rectangle();
394 517 : return aRect;
395 : }
396 :
397 517 : nWidth -= 1;
398 :
399 517 : if ( (nWidth <= 0) || (nHeight <= 0) )
400 86 : return Rectangle();
401 :
402 431 : if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
403 : {
404 382 : vcl::Font aFont( GetFont() );
405 382 : aFont.SetTransparent( true );
406 382 : SetFont( aFont );
407 :
408 382 : Size aSize;
409 382 : const long nOffsetX = 2 + GetItemsOffset().X();
410 382 : const long nOffsetY = 2 + GetItemsOffset().Y();
411 382 : long nX = nOffsetX;
412 382 : long nY = nOffsetY;
413 382 : long nMaxWidth = nWidth;
414 382 : sal_uInt16 nPos = 0;
415 :
416 : //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
417 : //of ugly bare tabs on lines of their own
418 :
419 : //collect widths
420 764 : std::vector<sal_Int32> aWidths;
421 6252 : for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
422 4168 : it != mpTabCtrlData->maItemList.end(); ++it )
423 : {
424 1702 : aWidths.push_back(ImplGetItemSize( &(*it), nMaxWidth ).Width());
425 : }
426 :
427 : //aBreakIndexes will contain the indexes of the last tab on each row
428 764 : std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));
429 :
430 382 : if ( (mnMaxPageWidth > 0) && (mnMaxPageWidth < nMaxWidth) )
431 0 : nMaxWidth = mnMaxPageWidth;
432 382 : nMaxWidth -= GetItemsOffset().X();
433 :
434 382 : sal_uInt16 nLines = 0;
435 382 : sal_uInt16 nCurLine = 0;
436 : long nLineWidthAry[100];
437 : sal_uInt16 nLinePosAry[101];
438 :
439 382 : nLineWidthAry[0] = 0;
440 382 : nLinePosAry[0] = 0;
441 382 : size_t nIndex = 0;
442 6252 : for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
443 4168 : it != mpTabCtrlData->maItemList.end(); ++it, ++nIndex )
444 : {
445 1702 : aSize = ImplGetItemSize( &(*it), nMaxWidth );
446 :
447 1702 : bool bNewLine = false;
448 1702 : if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
449 : {
450 0 : aBreakIndexes.pop_front();
451 0 : bNewLine = true;
452 : }
453 :
454 1702 : if ( bNewLine && (nWidth > 2+nOffsetX) )
455 : {
456 0 : if ( nLines == 99 )
457 0 : break;
458 :
459 0 : nX = nOffsetX;
460 0 : nY += aSize.Height();
461 0 : nLines++;
462 0 : nLineWidthAry[nLines] = 0;
463 0 : nLinePosAry[nLines] = nPos;
464 : }
465 :
466 1702 : Rectangle aNewRect( Point( nX, nY ), aSize );
467 1702 : if ( mbSmallInvalidate && (it->maRect != aNewRect) )
468 0 : mbSmallInvalidate = false;
469 1702 : it->maRect = aNewRect;
470 1702 : it->mnLine = nLines;
471 1702 : it->mbFullVisible = true;
472 :
473 1702 : nLineWidthAry[nLines] += aSize.Width();
474 1702 : nX += aSize.Width();
475 :
476 1702 : if ( it->mnId == mnCurPageId )
477 382 : nCurLine = nLines;
478 :
479 1702 : nPos++;
480 : }
481 :
482 382 : if ( nLines && !mpTabCtrlData->maItemList.empty() )
483 : {
484 0 : long nDX = 0;
485 0 : long nModDX = 0;
486 0 : long nIDX = 0;
487 : sal_uInt16 i;
488 : sal_uInt16 n;
489 : long nLineHeightAry[100];
490 0 : long nIH = mpTabCtrlData->maItemList[0].maRect.Bottom()-2;
491 :
492 0 : i = 0;
493 0 : while ( i < nLines+1 )
494 : {
495 0 : if ( i <= nCurLine )
496 0 : nLineHeightAry[i] = nIH*(nLines-(nCurLine-i)) + GetItemsOffset().Y();
497 : else
498 0 : nLineHeightAry[i] = nIH*(i-nCurLine-1) + GetItemsOffset().Y();
499 0 : i++;
500 : }
501 :
502 0 : i = 0;
503 0 : n = 0;
504 0 : nLinePosAry[nLines+1] = (sal_uInt16)mpTabCtrlData->maItemList.size();
505 0 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
506 0 : it != mpTabCtrlData->maItemList.end(); ++it )
507 : {
508 0 : if ( i == nLinePosAry[n] )
509 : {
510 0 : if ( n == nLines+1 )
511 0 : break;
512 :
513 0 : nIDX = 0;
514 0 : if( nLinePosAry[n+1]-i > 0 )
515 : {
516 0 : nDX = (nWidth-nOffsetX-nLineWidthAry[n]) / (nLinePosAry[n+1]-i);
517 0 : nModDX = (nWidth-nOffsetX-nLineWidthAry[n]) % (nLinePosAry[n+1]-i);
518 : }
519 : else
520 : {
521 : // FIXME: this is a bad case of tabctrl way too small
522 0 : nDX = 0;
523 0 : nModDX = 0;
524 : }
525 0 : n++;
526 : }
527 :
528 0 : it->maRect.Left() += nIDX;
529 0 : it->maRect.Right() += nIDX+nDX;
530 0 : it->maRect.Top() = nLineHeightAry[n-1];
531 0 : it->maRect.Bottom() = nLineHeightAry[n-1]+nIH;
532 0 : nIDX += nDX;
533 :
534 0 : if ( nModDX )
535 : {
536 0 : nIDX++;
537 0 : it->maRect.Right()++;
538 0 : nModDX--;
539 : }
540 :
541 0 : i++;
542 : }
543 : }
544 : else
545 : {//only one line
546 382 : if(ImplGetSVData()->maNWFData.mbCenteredTabs)
547 : {
548 0 : int nRightSpace=nMaxWidth;//space left on the right by the tabs
549 0 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
550 0 : it != mpTabCtrlData->maItemList.end(); ++it )
551 : {
552 0 : nRightSpace-=it->maRect.Right()-it->maRect.Left();
553 : }
554 0 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
555 0 : it != mpTabCtrlData->maItemList.end(); ++it )
556 : {
557 0 : it->maRect.Left()+=(int) (nRightSpace/2);
558 0 : it->maRect.Right()+=(int) (nRightSpace/2);
559 : }
560 : }
561 : }
562 :
563 382 : mnLastWidth = nWidth;
564 382 : mnLastHeight = nHeight;
565 764 : mbFormat = false;
566 : }
567 :
568 431 : return size_t(nItemPos) < mpTabCtrlData->maItemList.size() ? mpTabCtrlData->maItemList[nItemPos].maRect : Rectangle();
569 : }
570 :
571 52 : void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
572 : {
573 52 : ImplFreeLayoutData();
574 :
575 52 : ImplTabItem* pOldItem = ImplGetItem( nOldId );
576 52 : ImplTabItem* pItem = ImplGetItem( nId );
577 52 : TabPage* pOldPage = (pOldItem) ? pOldItem->mpTabPage : NULL;
578 52 : TabPage* pPage = (pItem) ? pItem->mpTabPage : NULL;
579 52 : vcl::Window* pCtrlParent = GetParent();
580 :
581 52 : if ( IsReallyVisible() && IsUpdateMode() )
582 : {
583 0 : sal_uInt16 nPos = GetPagePos( nId );
584 0 : Rectangle aRect = ImplGetTabRect( nPos );
585 :
586 0 : if ( !pOldItem || !pItem || (pOldItem->mnLine != pItem->mnLine) )
587 : {
588 0 : aRect.Left() = 0;
589 0 : aRect.Top() = 0;
590 0 : aRect.Right() = Control::GetOutputSizePixel().Width();
591 : }
592 : else
593 : {
594 0 : aRect.Left() -= 3;
595 0 : aRect.Top() -= 2;
596 0 : aRect.Right() += 3;
597 0 : Invalidate( aRect );
598 0 : nPos = GetPagePos( nOldId );
599 0 : aRect = ImplGetTabRect( nPos );
600 0 : aRect.Left() -= 3;
601 0 : aRect.Top() -= 2;
602 0 : aRect.Right() += 3;
603 : }
604 0 : Invalidate( aRect );
605 : }
606 :
607 52 : if ( pOldPage == pPage )
608 52 : return;
609 :
610 52 : Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
611 :
612 52 : if ( pOldPage )
613 : {
614 0 : if ( mbRestoreHelpId )
615 0 : pCtrlParent->SetHelpId( OString() );
616 0 : if ( mbRestoreUnqId )
617 0 : pCtrlParent->SetUniqueId( OString() );
618 0 : pOldPage->DeactivatePage();
619 : }
620 :
621 52 : if ( pPage )
622 : {
623 52 : if ( ( GetStyle() & WB_NOBORDER ) )
624 : {
625 0 : Rectangle aRectNoTab( (const Point&)Point( 0, 0 ), GetSizePixel() );
626 0 : pPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
627 : }
628 : else
629 52 : pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
630 :
631 : // activate page here so the conbtrols can be switched
632 : // also set the help id of the parent window to that of the tab page
633 52 : if ( GetHelpId().isEmpty() )
634 : {
635 52 : mbRestoreHelpId = true;
636 52 : pCtrlParent->SetHelpId( pPage->GetHelpId() );
637 : }
638 52 : if ( pCtrlParent->GetUniqueId().isEmpty() )
639 : {
640 52 : mbRestoreUnqId = true;
641 52 : pCtrlParent->SetUniqueId( pPage->GetUniqueId() );
642 : }
643 :
644 52 : pPage->ActivatePage();
645 52 : pPage->Show();
646 :
647 52 : if ( pOldPage && pOldPage->HasChildPathFocus() )
648 : {
649 0 : sal_uInt16 n = 0;
650 0 : vcl::Window* pFirstChild = pPage->ImplGetDlgWindow( n, DLGWINDOW_FIRST );
651 0 : if ( pFirstChild )
652 0 : pFirstChild->ImplControlFocus( GETFOCUS_INIT );
653 : else
654 0 : GrabFocus();
655 : }
656 : }
657 :
658 52 : if ( pOldPage )
659 0 : pOldPage->Hide();
660 :
661 : // Invalidate the same region that will be send to NWF
662 : // to always allow for bitmap caching
663 : // see Window::DrawNativeControl()
664 52 : if( IsNativeControlSupported( CTRL_TAB_PANE, PART_ENTIRE_CONTROL ) )
665 : {
666 0 : aRect.Left() -= TAB_OFFSET;
667 0 : aRect.Top() -= TAB_OFFSET;
668 0 : aRect.Right() += TAB_OFFSET;
669 0 : aRect.Bottom() += TAB_OFFSET;
670 : }
671 :
672 52 : Invalidate( aRect );
673 : }
674 :
675 364 : bool TabControl::ImplPosCurTabPage()
676 : {
677 : // resize/position current TabPage
678 364 : ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
679 364 : if ( pItem && pItem->mpTabPage )
680 : {
681 364 : if ( ( GetStyle() & WB_NOBORDER ) )
682 : {
683 0 : Rectangle aRectNoTab( (const Point&)Point( 0, 0 ), GetSizePixel() );
684 0 : pItem->mpTabPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
685 0 : return true;
686 : }
687 364 : Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
688 364 : pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
689 364 : return true;
690 : }
691 :
692 0 : return false;
693 : }
694 :
695 0 : void TabControl::ImplActivateTabPage( bool bNext )
696 : {
697 0 : sal_uInt16 nCurPos = GetPagePos( GetCurPageId() );
698 :
699 0 : if ( bNext )
700 0 : nCurPos = (nCurPos + 1) % GetPageCount();
701 : else
702 : {
703 0 : if ( !nCurPos )
704 0 : nCurPos = GetPageCount()-1;
705 : else
706 0 : nCurPos--;
707 : }
708 :
709 0 : SelectTabPage( GetPageId( nCurPos ) );
710 0 : }
711 :
712 0 : void TabControl::ImplShowFocus()
713 : {
714 0 : if ( !GetPageCount() || mpTabCtrlData->mpListBox )
715 0 : return;
716 :
717 0 : sal_uInt16 nCurPos = GetPagePos( mnCurPageId );
718 0 : Rectangle aRect = ImplGetTabRect( nCurPos );
719 0 : const ImplTabItem& rItem = mpTabCtrlData->maItemList[ nCurPos ];
720 0 : Size aTabSize = aRect.GetSize();
721 0 : Size aImageSize( 0, 0 );
722 0 : long nTextHeight = GetTextHeight();
723 0 : long nTextWidth = GetCtrlTextWidth( rItem.maFormatText );
724 : sal_uInt16 nOff;
725 :
726 0 : if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_MONO) )
727 0 : nOff = 1;
728 : else
729 0 : nOff = 0;
730 :
731 0 : if( !! rItem.maTabImage )
732 : {
733 0 : aImageSize = rItem.maTabImage.GetSizePixel();
734 0 : if( !rItem.maFormatText.isEmpty() )
735 0 : aImageSize.Width() += GetTextHeight()/4;
736 : }
737 :
738 0 : if( !rItem.maFormatText.isEmpty() )
739 : {
740 : // show focus around text
741 0 : aRect.Left() = aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1;
742 0 : aRect.Top() = aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1;
743 0 : aRect.Right() = aRect.Left()+nTextWidth+2;
744 0 : aRect.Bottom() = aRect.Top()+nTextHeight+2;
745 : }
746 : else
747 : {
748 : // show focus around image
749 0 : long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
750 0 : long nYPos = aRect.Top();
751 0 : if( aImageSize.Height() < aRect.GetHeight() )
752 0 : nYPos += (aRect.GetHeight() - aImageSize.Height())/2;
753 :
754 0 : aRect.Left() = nXPos - 2;
755 0 : aRect.Top() = nYPos - 2;
756 0 : aRect.Right() = aRect.Left() + aImageSize.Width() + 4;
757 0 : aRect.Bottom() = aRect.Top() + aImageSize.Height() + 4;
758 : }
759 0 : ShowFocus( aRect );
760 : }
761 :
762 125 : void TabControl::ImplDrawItem( ImplTabItem* pItem, const Rectangle& rCurRect, bool bLayout, bool bFirstInGroup, bool bLastInGroup, bool /* bIsCurrentItem */ )
763 : {
764 125 : if ( pItem->maRect.IsEmpty() )
765 125 : return;
766 :
767 125 : if( bLayout )
768 : {
769 0 : if( !HasLayoutData() )
770 : {
771 0 : mpControlData->mpLayoutData = new vcl::ControlLayoutData();
772 0 : mpTabCtrlData->maLayoutLineToPageId.clear();
773 0 : mpTabCtrlData->maLayoutPageIdToLine.clear();
774 0 : mpTabCtrlData->maTabRectangles.clear();
775 : }
776 : }
777 :
778 125 : const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
779 125 : Rectangle aRect = pItem->maRect;
780 125 : long nLeftBottom = aRect.Bottom();
781 125 : long nRightBottom = aRect.Bottom();
782 125 : bool bLeftBorder = true;
783 125 : bool bRightBorder = true;
784 : sal_uInt16 nOff;
785 125 : bool bNativeOK = false;
786 :
787 125 : sal_uInt16 nOff2 = 0;
788 125 : sal_uInt16 nOff3 = 0;
789 :
790 125 : if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
791 125 : nOff = 1;
792 : else
793 0 : nOff = 0;
794 :
795 : // if this is the active Page, we have to draw a little more
796 125 : if ( pItem->mnId == mnCurPageId )
797 : {
798 25 : nOff2 = 2;
799 25 : if( ! ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise )
800 25 : nOff3 = 1;
801 : }
802 : else
803 : {
804 100 : Point aLeftTestPos = aRect.BottomLeft();
805 100 : Point aRightTestPos = aRect.BottomRight();
806 100 : if ( aLeftTestPos.Y() == rCurRect.Bottom() )
807 : {
808 100 : aLeftTestPos.X() -= 2;
809 100 : if ( rCurRect.IsInside( aLeftTestPos ) )
810 25 : bLeftBorder = false;
811 100 : aRightTestPos.X() += 2;
812 100 : if ( rCurRect.IsInside( aRightTestPos ) )
813 0 : bRightBorder = false;
814 : }
815 : else
816 : {
817 0 : if ( rCurRect.IsInside( aLeftTestPos ) )
818 0 : nLeftBottom -= 2;
819 0 : if ( rCurRect.IsInside( aRightTestPos ) )
820 0 : nRightBottom -= 2;
821 : }
822 : }
823 :
824 125 : if( !bLayout && (bNativeOK = IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL)) )
825 : {
826 0 : Rectangle aCtrlRegion( pItem->maRect );
827 0 : ControlState nState = 0;
828 :
829 0 : if( pItem->mnId == mnCurPageId )
830 : {
831 0 : nState |= CTRL_STATE_SELECTED;
832 : // only the selected item can be focussed
833 0 : if ( HasFocus() )
834 0 : nState |= CTRL_STATE_FOCUSED;
835 : }
836 0 : if ( IsEnabled() )
837 0 : nState |= CTRL_STATE_ENABLED;
838 0 : if( IsMouseOver() && pItem->maRect.IsInside( GetPointerPosPixel() ) )
839 : {
840 0 : nState |= CTRL_STATE_ROLLOVER;
841 0 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
842 0 : it != mpTabCtrlData->maItemList.end(); ++it )
843 : {
844 0 : if( (&(*it) != pItem) && (it->maRect.IsInside( GetPointerPosPixel() ) ) )
845 : {
846 0 : nState &= ~CTRL_STATE_ROLLOVER; // avoid multiple highlighted tabs
847 0 : break;
848 : }
849 : }
850 : }
851 :
852 0 : TabitemValue tiValue;
853 0 : if(pItem->maRect.Left() < 5)
854 0 : tiValue.mnAlignment |= TABITEM_LEFTALIGNED;
855 0 : if(pItem->maRect.Right() > mnLastWidth - 5)
856 0 : tiValue.mnAlignment |= TABITEM_RIGHTALIGNED;
857 0 : if ( bFirstInGroup )
858 0 : tiValue.mnAlignment |= TABITEM_FIRST_IN_GROUP;
859 0 : if ( bLastInGroup )
860 0 : tiValue.mnAlignment |= TABITEM_LAST_IN_GROUP;
861 :
862 : bNativeOK = DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, aCtrlRegion, nState,
863 0 : tiValue, OUString() );
864 : }
865 :
866 125 : if( ! bLayout && !bNativeOK )
867 : {
868 125 : if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
869 : {
870 125 : SetLineColor( rStyleSettings.GetLightColor() );
871 125 : DrawPixel( Point( aRect.Left()+1-nOff2, aRect.Top()+1-nOff2 ) ); // diagonally indented top-left pixel
872 125 : if ( bLeftBorder )
873 : {
874 200 : DrawLine( Point( aRect.Left()-nOff2, aRect.Top()+2-nOff2 ),
875 300 : Point( aRect.Left()-nOff2, nLeftBottom-1 ) );
876 : }
877 250 : DrawLine( Point( aRect.Left()+2-nOff2, aRect.Top()-nOff2 ), // top line starting 2px from left border
878 375 : Point( aRect.Right()+nOff2-3, aRect.Top()-nOff2 ) ); // ending 3px from right border
879 :
880 125 : if ( bRightBorder )
881 : {
882 125 : SetLineColor( rStyleSettings.GetShadowColor() );
883 250 : DrawLine( Point( aRect.Right()+nOff2-2, aRect.Top()+1-nOff2 ),
884 375 : Point( aRect.Right()+nOff2-2, nRightBottom-1 ) );
885 :
886 125 : SetLineColor( rStyleSettings.GetDarkShadowColor() );
887 250 : DrawLine( Point( aRect.Right()+nOff2-1, aRect.Top()+3-nOff2 ),
888 375 : Point( aRect.Right()+nOff2-1, nRightBottom-1 ) );
889 : }
890 : }
891 : else
892 : {
893 0 : SetLineColor( Color( COL_BLACK ) );
894 0 : DrawPixel( Point( aRect.Left()+1-nOff2, aRect.Top()+1-nOff2 ) );
895 0 : DrawPixel( Point( aRect.Right()+nOff2-2, aRect.Top()+1-nOff2 ) );
896 0 : if ( bLeftBorder )
897 : {
898 0 : DrawLine( Point( aRect.Left()-nOff2, aRect.Top()+2-nOff2 ),
899 0 : Point( aRect.Left()-nOff2, nLeftBottom-1 ) );
900 : }
901 0 : DrawLine( Point( aRect.Left()+2-nOff2, aRect.Top()-nOff2 ),
902 0 : Point( aRect.Right()-3, aRect.Top()-nOff2 ) );
903 0 : if ( bRightBorder )
904 : {
905 0 : DrawLine( Point( aRect.Right()+nOff2-1, aRect.Top()+2-nOff2 ),
906 0 : Point( aRect.Right()+nOff2-1, nRightBottom-1 ) );
907 : }
908 : }
909 : }
910 :
911 125 : if( bLayout )
912 : {
913 0 : int nLine = mpControlData->mpLayoutData->m_aLineIndices.size();
914 0 : mpControlData->mpLayoutData->m_aLineIndices.push_back( mpControlData->mpLayoutData->m_aDisplayText.getLength() );
915 0 : mpTabCtrlData->maLayoutPageIdToLine[ (int)pItem->mnId ] = nLine;
916 0 : mpTabCtrlData->maLayoutLineToPageId[ nLine ] = (int)pItem->mnId;
917 0 : mpTabCtrlData->maTabRectangles.push_back( aRect );
918 : }
919 :
920 : // set font accordingly, current item is painted bold
921 : // we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
922 125 : vcl::Font aFont( GetFont() );
923 125 : aFont.SetTransparent( true );
924 125 : SetFont( aFont );
925 :
926 125 : Size aTabSize = aRect.GetSize();
927 125 : Size aImageSize( 0, 0 );
928 125 : long nTextHeight = GetTextHeight();
929 125 : long nTextWidth = GetCtrlTextWidth( pItem->maFormatText );
930 125 : if( !! pItem->maTabImage )
931 : {
932 0 : aImageSize = pItem->maTabImage.GetSizePixel();
933 0 : if( !pItem->maFormatText.isEmpty() )
934 0 : aImageSize.Width() += GetTextHeight()/4;
935 : }
936 125 : long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-nOff3;
937 125 : long nYPos = aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-nOff3;
938 125 : if( !pItem->maFormatText.isEmpty() )
939 : {
940 125 : sal_uInt16 nStyle = TEXT_DRAW_MNEMONIC;
941 125 : if( ! pItem->mbEnabled )
942 0 : nStyle |= TEXT_DRAW_DISABLE;
943 125 : DrawCtrlText( Point( nXPos + aImageSize.Width(), nYPos ),
944 : pItem->maFormatText, 0,
945 : pItem->maFormatText.getLength(), nStyle,
946 : bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : NULL,
947 : bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : NULL
948 250 : );
949 : }
950 :
951 125 : if( !! pItem->maTabImage )
952 : {
953 0 : Point aImgTL( nXPos, aRect.Top() );
954 0 : if( aImageSize.Height() < aRect.GetHeight() )
955 0 : aImgTL.Y() += (aRect.GetHeight() - aImageSize.Height())/2;
956 0 : DrawImage( aImgTL, pItem->maTabImage, pItem->mbEnabled ? 0 : IMAGE_DRAW_DISABLE );
957 125 : }
958 : }
959 :
960 0 : bool TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
961 : {
962 0 : bool nRet = false;
963 :
964 0 : if ( GetPageCount() > 1 )
965 : {
966 0 : vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
967 0 : sal_uInt16 nKeyCode = aKeyCode.GetCode();
968 :
969 0 : if ( aKeyCode.IsMod1() )
970 : {
971 0 : if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
972 : {
973 0 : if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
974 : {
975 0 : ImplActivateTabPage( false );
976 0 : nRet = true;
977 : }
978 : }
979 : else
980 : {
981 0 : if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
982 : {
983 0 : ImplActivateTabPage( true );
984 0 : nRet = true;
985 : }
986 : }
987 : }
988 : }
989 :
990 0 : return nRet;
991 : }
992 :
993 0 : IMPL_LINK_NOARG(TabControl, ImplListBoxSelectHdl)
994 : {
995 0 : SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectEntryPos() ) );
996 0 : return 0;
997 : }
998 :
999 0 : IMPL_LINK( TabControl, ImplWindowEventListener, VclSimpleEvent*, pEvent )
1000 : {
1001 0 : if ( pEvent && pEvent->ISA( VclWindowEvent ) && (pEvent->GetId() == VCLEVENT_WINDOW_KEYINPUT) )
1002 : {
1003 0 : VclWindowEvent* pWindowEvent = static_cast< VclWindowEvent* >(pEvent);
1004 : // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
1005 0 : if ( !IsWindowOrChild( pWindowEvent->GetWindow() ) )
1006 : {
1007 0 : KeyEvent* pKeyEvent = static_cast< KeyEvent* >(pWindowEvent->GetData());
1008 0 : ImplHandleKeyEvent( *pKeyEvent );
1009 : }
1010 : }
1011 0 : return 0;
1012 : }
1013 :
1014 0 : void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
1015 : {
1016 0 : if( mpTabCtrlData->mpListBox == NULL )
1017 : {
1018 0 : if( rMEvt.IsLeft() )
1019 : {
1020 0 : sal_uInt16 nPageId = GetPageId( rMEvt.GetPosPixel() );
1021 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1022 0 : if( pItem && pItem->mbEnabled )
1023 0 : SelectTabPage( nPageId );
1024 : }
1025 : }
1026 0 : }
1027 :
1028 0 : void TabControl::KeyInput( const KeyEvent& rKEvt )
1029 : {
1030 0 : if( mpTabCtrlData->mpListBox )
1031 0 : mpTabCtrlData->mpListBox->KeyInput( rKEvt );
1032 0 : else if ( GetPageCount() > 1 )
1033 : {
1034 0 : vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1035 0 : sal_uInt16 nKeyCode = aKeyCode.GetCode();
1036 :
1037 0 : if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
1038 : {
1039 0 : bool bNext = (nKeyCode == KEY_RIGHT);
1040 0 : ImplActivateTabPage( bNext );
1041 : }
1042 : }
1043 :
1044 0 : Control::KeyInput( rKEvt );
1045 0 : }
1046 :
1047 25 : void TabControl::Paint( const Rectangle& rRect )
1048 : {
1049 25 : if ( !( GetStyle() & WB_NOBORDER ) )
1050 25 : ImplPaint( rRect, false );
1051 25 : }
1052 :
1053 25 : void TabControl::ImplPaint( const Rectangle& rRect, bool bLayout )
1054 : {
1055 25 : if( ! bLayout )
1056 25 : HideFocus();
1057 :
1058 : // reformat if needed
1059 25 : Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
1060 :
1061 : // find current item
1062 25 : ImplTabItem* pCurItem = NULL;
1063 75 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1064 50 : it != mpTabCtrlData->maItemList.end(); ++it )
1065 : {
1066 25 : if ( it->mnId == mnCurPageId )
1067 : {
1068 25 : pCurItem = &(*it);
1069 25 : break;
1070 : }
1071 : }
1072 :
1073 : // Draw the TabPage border
1074 25 : const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1075 25 : Rectangle aCurRect;
1076 25 : aRect.Left() -= TAB_OFFSET;
1077 25 : aRect.Top() -= TAB_OFFSET;
1078 25 : aRect.Right() += TAB_OFFSET;
1079 25 : aRect.Bottom() += TAB_OFFSET;
1080 :
1081 : // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
1082 : // increased to avoid round corners that might be drawn by a theme
1083 : // in this case we're only interested in the top border of the tabpage because the tabitems are used
1084 : // standalone (eg impress)
1085 25 : bool bNoTabPage = false;
1086 25 : TabPage* pCurPage = pCurItem ? pCurItem->mpTabPage : NULL;
1087 25 : if( !pCurPage || !pCurPage->IsVisible() )
1088 : {
1089 25 : bNoTabPage = true;
1090 25 : aRect.Left()-=10;
1091 25 : aRect.Right()+=10;
1092 : }
1093 :
1094 25 : bool bNativeOK = false;
1095 25 : if( ! bLayout && (bNativeOK = IsNativeControlSupported( CTRL_TAB_PANE, PART_ENTIRE_CONTROL) ) )
1096 : {
1097 0 : const ImplControlValue aControlValue;
1098 :
1099 0 : ControlState nState = CTRL_STATE_ENABLED;
1100 0 : int part = PART_ENTIRE_CONTROL;
1101 0 : if ( !IsEnabled() )
1102 0 : nState &= ~CTRL_STATE_ENABLED;
1103 0 : if ( HasFocus() )
1104 0 : nState |= CTRL_STATE_FOCUSED;
1105 :
1106 0 : vcl::Region aClipRgn( GetActiveClipRegion() );
1107 0 : aClipRgn.Intersect( aRect );
1108 0 : if( !rRect.IsEmpty() )
1109 0 : aClipRgn.Intersect( rRect );
1110 :
1111 0 : if( !aClipRgn.IsEmpty() )
1112 : bNativeOK = DrawNativeControl( CTRL_TAB_PANE, part, aRect, nState,
1113 0 : aControlValue, OUString() );
1114 : }
1115 : else
1116 : {
1117 25 : long nTopOff = 1;
1118 25 : if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
1119 25 : SetLineColor( rStyleSettings.GetLightColor() );
1120 : else
1121 0 : SetLineColor( Color( COL_BLACK ) );
1122 25 : if ( pCurItem && !pCurItem->maRect.IsEmpty() )
1123 : {
1124 25 : aCurRect = pCurItem->maRect;
1125 25 : if( ! bLayout )
1126 25 : DrawLine( aRect.TopLeft(), Point( aCurRect.Left()-2, aRect.Top() ) );
1127 25 : if ( aCurRect.Right()+1 < aRect.Right() )
1128 : {
1129 0 : if( ! bLayout )
1130 0 : DrawLine( Point( aCurRect.Right(), aRect.Top() ), aRect.TopRight() );
1131 : }
1132 : else
1133 25 : nTopOff = 0;
1134 : }
1135 : else
1136 0 : if( ! bLayout )
1137 0 : DrawLine( aRect.TopLeft(), aRect.TopRight() );
1138 :
1139 25 : if( ! bLayout )
1140 : {
1141 25 : DrawLine( aRect.TopLeft(), aRect.BottomLeft() );
1142 :
1143 25 : if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
1144 : {
1145 : // if we have not tab page the bottom line of the tab page
1146 : // directly touches the tab items, so choose a color that fits seamlessly
1147 25 : if( bNoTabPage )
1148 25 : SetLineColor( rStyleSettings.GetDialogColor() );
1149 : else
1150 0 : SetLineColor( rStyleSettings.GetShadowColor() );
1151 25 : DrawLine( Point( 1, aRect.Bottom()-1 ),
1152 50 : Point( aRect.Right()-1, aRect.Bottom()-1 ) );
1153 50 : DrawLine( Point( aRect.Right()-1, aRect.Top()+nTopOff ),
1154 75 : Point( aRect.Right()-1, aRect.Bottom()-1 ) );
1155 25 : if( bNoTabPage )
1156 25 : SetLineColor( rStyleSettings.GetDialogColor() );
1157 : else
1158 0 : SetLineColor( rStyleSettings.GetDarkShadowColor() );
1159 25 : DrawLine( Point( 0, aRect.Bottom() ),
1160 50 : Point( aRect.Right(), aRect.Bottom() ) );
1161 50 : DrawLine( Point( aRect.Right(), aRect.Top()+nTopOff ),
1162 75 : Point( aRect.Right(), aRect.Bottom() ) );
1163 : }
1164 : else
1165 : {
1166 0 : DrawLine( aRect.TopRight(), aRect.BottomRight() );
1167 0 : DrawLine( aRect.BottomLeft(), aRect.BottomRight() );
1168 : }
1169 : }
1170 : }
1171 :
1172 25 : if ( !mpTabCtrlData->maItemList.empty() && mpTabCtrlData->mpListBox == NULL )
1173 : {
1174 : // Some native toolkits (GTK+) draw tabs right-to-left, with an
1175 : // overlap between adjacent tabs
1176 25 : bool bDrawTabsRTL = IsNativeControlSupported( CTRL_TAB_ITEM, PART_TABS_DRAW_RTL );
1177 25 : ImplTabItem * pFirstTab = NULL;
1178 25 : ImplTabItem * pLastTab = NULL;
1179 : size_t idx;
1180 :
1181 : // Event though there is a tab overlap with GTK+, the first tab is not
1182 : // overlapped on the left side. Other tookits ignore this option.
1183 25 : if ( bDrawTabsRTL )
1184 : {
1185 0 : pFirstTab = &mpTabCtrlData->maItemList.front();
1186 0 : pLastTab = &mpTabCtrlData->maItemList.back();
1187 0 : idx = mpTabCtrlData->maItemList.size()-1;
1188 : }
1189 : else
1190 : {
1191 25 : pLastTab = &mpTabCtrlData->maItemList.back();
1192 25 : pFirstTab = &mpTabCtrlData->maItemList.front();
1193 25 : idx = 0;
1194 : }
1195 :
1196 175 : while ( idx < mpTabCtrlData->maItemList.size() )
1197 : {
1198 125 : ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
1199 125 : if ( pItem != pCurItem )
1200 : {
1201 100 : vcl::Region aClipRgn( GetActiveClipRegion() );
1202 100 : aClipRgn.Intersect( pItem->maRect );
1203 100 : if( !rRect.IsEmpty() )
1204 100 : aClipRgn.Intersect( rRect );
1205 100 : if( bLayout || !aClipRgn.IsEmpty() )
1206 100 : ImplDrawItem( pItem, aCurRect, bLayout, (pItem==pFirstTab), (pItem==pLastTab), false );
1207 : }
1208 :
1209 125 : if ( bDrawTabsRTL )
1210 0 : idx--;
1211 : else
1212 125 : idx++;
1213 : }
1214 :
1215 25 : if ( pCurItem )
1216 : {
1217 25 : vcl::Region aClipRgn( GetActiveClipRegion() );
1218 25 : aClipRgn.Intersect( pCurItem->maRect );
1219 25 : if( !rRect.IsEmpty() )
1220 25 : aClipRgn.Intersect( rRect );
1221 25 : if( bLayout || !aClipRgn.IsEmpty() )
1222 25 : ImplDrawItem( pCurItem, aCurRect, bLayout, (pCurItem==pFirstTab), (pCurItem==pLastTab), true );
1223 : }
1224 : }
1225 :
1226 25 : if ( !bLayout && HasFocus() )
1227 0 : ImplShowFocus();
1228 :
1229 25 : if( ! bLayout )
1230 25 : mbSmallInvalidate = true;
1231 25 : }
1232 :
1233 684 : void TabControl::setAllocation(const Size &rAllocation)
1234 : {
1235 684 : ImplFreeLayoutData();
1236 :
1237 684 : if ( !IsReallyShown() )
1238 1056 : return;
1239 :
1240 312 : if( mpTabCtrlData->mpListBox )
1241 : {
1242 : // get the listbox' preferred size
1243 0 : Size aTabCtrlSize( GetSizePixel() );
1244 0 : long nPrefWidth = mpTabCtrlData->mpListBox->get_preferred_size().Width();
1245 0 : if( nPrefWidth > aTabCtrlSize.Width() )
1246 0 : nPrefWidth = aTabCtrlSize.Width();
1247 0 : Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MAP_APPFONT ) ).Height() );
1248 0 : Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
1249 0 : mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
1250 : }
1251 :
1252 312 : mbFormat = true;
1253 :
1254 : // resize/position active TabPage
1255 312 : bool bTabPage = ImplPosCurTabPage();
1256 :
1257 : // check what needs to be invalidated
1258 312 : Size aNewSize = rAllocation;
1259 312 : long nNewWidth = aNewSize.Width();
1260 5616 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1261 3744 : it != mpTabCtrlData->maItemList.end(); ++it )
1262 : {
1263 3120 : if ( !it->mbFullVisible ||
1264 1560 : (it->maRect.Right()-2 >= nNewWidth) )
1265 : {
1266 0 : mbSmallInvalidate = false;
1267 0 : break;
1268 : }
1269 : }
1270 :
1271 312 : if ( mbSmallInvalidate )
1272 : {
1273 24 : Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
1274 24 : aRect.Left() -= TAB_OFFSET+TAB_BORDER_LEFT;
1275 24 : aRect.Top() -= TAB_OFFSET+TAB_BORDER_TOP;
1276 24 : aRect.Right() += TAB_OFFSET+TAB_BORDER_RIGHT;
1277 24 : aRect.Bottom() += TAB_OFFSET+TAB_BORDER_BOTTOM;
1278 24 : if ( bTabPage )
1279 24 : Invalidate( aRect, INVALIDATE_NOCHILDREN );
1280 : else
1281 0 : Invalidate( aRect );
1282 :
1283 : }
1284 : else
1285 : {
1286 288 : if ( bTabPage )
1287 288 : Invalidate( INVALIDATE_NOCHILDREN );
1288 : else
1289 0 : Invalidate();
1290 : }
1291 :
1292 312 : mbLayoutDirty = false;
1293 : }
1294 :
1295 420 : void TabControl::SetPosSizePixel(const Point& rNewPos, const Size& rNewSize)
1296 : {
1297 420 : Window::SetPosSizePixel(rNewPos, rNewSize);
1298 : //if size changed, TabControl::Resize got called already
1299 420 : if (mbLayoutDirty)
1300 108 : setAllocation(rNewSize);
1301 420 : }
1302 :
1303 52 : void TabControl::SetSizePixel(const Size& rNewSize)
1304 : {
1305 52 : Window::SetSizePixel(rNewSize);
1306 : //if size changed, TabControl::Resize got called already
1307 52 : if (mbLayoutDirty)
1308 52 : setAllocation(rNewSize);
1309 52 : }
1310 :
1311 0 : void TabControl::SetPosPixel(const Point& rPos)
1312 : {
1313 0 : Window::SetPosPixel(rPos);
1314 0 : if (mbLayoutDirty)
1315 0 : setAllocation(GetOutputSizePixel());
1316 0 : }
1317 :
1318 524 : void TabControl::Resize()
1319 : {
1320 524 : setAllocation(Control::GetOutputSizePixel());
1321 524 : }
1322 :
1323 0 : void TabControl::GetFocus()
1324 : {
1325 0 : if( ! mpTabCtrlData->mpListBox )
1326 : {
1327 0 : ImplShowFocus();
1328 0 : SetInputContext( InputContext( GetFont() ) );
1329 : }
1330 : else
1331 : {
1332 0 : if( mpTabCtrlData->mpListBox->IsReallyVisible() )
1333 0 : mpTabCtrlData->mpListBox->GrabFocus();
1334 : }
1335 0 : Control::GetFocus();
1336 0 : }
1337 :
1338 0 : void TabControl::LoseFocus()
1339 : {
1340 0 : if( ! mpTabCtrlData->mpListBox )
1341 0 : HideFocus();
1342 0 : Control::LoseFocus();
1343 0 : }
1344 :
1345 0 : void TabControl::RequestHelp( const HelpEvent& rHEvt )
1346 : {
1347 0 : sal_uInt16 nItemId = rHEvt.KeyboardActivated() ? mnCurPageId : GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
1348 :
1349 0 : if ( nItemId )
1350 : {
1351 0 : if ( rHEvt.GetMode() & HELPMODE_BALLOON )
1352 : {
1353 0 : OUString aStr = GetHelpText( nItemId );
1354 0 : if ( !aStr.isEmpty() )
1355 : {
1356 0 : Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1357 0 : Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1358 0 : aItemRect.Left() = aPt.X();
1359 0 : aItemRect.Top() = aPt.Y();
1360 0 : aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1361 0 : aItemRect.Right() = aPt.X();
1362 0 : aItemRect.Bottom() = aPt.Y();
1363 0 : Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
1364 0 : return;
1365 0 : }
1366 : }
1367 0 : else if ( rHEvt.GetMode() & HELPMODE_EXTENDED )
1368 : {
1369 0 : OUString aHelpId( OStringToOUString( GetHelpId( nItemId ), RTL_TEXTENCODING_UTF8 ) );
1370 0 : if ( !aHelpId.isEmpty() )
1371 : {
1372 : // call Help if existing
1373 0 : Help* pHelp = Application::GetHelp();
1374 0 : if ( pHelp )
1375 0 : pHelp->Start( aHelpId, this );
1376 0 : return;
1377 0 : }
1378 : }
1379 :
1380 : // for Quick or Ballon Help, we show the text, if it is cut
1381 0 : if ( rHEvt.GetMode() & (HELPMODE_QUICK | HELPMODE_BALLOON) )
1382 : {
1383 0 : ImplTabItem* pItem = ImplGetItem( nItemId );
1384 0 : const OUString& rStr = pItem->maText;
1385 0 : if ( rStr != pItem->maFormatText )
1386 : {
1387 0 : Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1388 0 : Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1389 0 : aItemRect.Left() = aPt.X();
1390 0 : aItemRect.Top() = aPt.Y();
1391 0 : aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1392 0 : aItemRect.Right() = aPt.X();
1393 0 : aItemRect.Bottom() = aPt.Y();
1394 0 : if ( !rStr.isEmpty() )
1395 : {
1396 0 : if ( rHEvt.GetMode() & HELPMODE_BALLOON )
1397 0 : Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
1398 : else
1399 0 : Help::ShowQuickHelp( this, aItemRect, rStr );
1400 0 : return;
1401 : }
1402 : }
1403 : }
1404 :
1405 0 : if ( rHEvt.GetMode() & HELPMODE_QUICK )
1406 : {
1407 0 : ImplTabItem* pItem = ImplGetItem( nItemId );
1408 0 : const OUString& rHelpText = pItem->maHelpText;
1409 : // show tooltip if not text but image is set and helptext is available
1410 0 : if ( !rHelpText.isEmpty() && pItem->maText.isEmpty() && !!pItem->maTabImage )
1411 : {
1412 0 : Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1413 0 : Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1414 0 : aItemRect.Left() = aPt.X();
1415 0 : aItemRect.Top() = aPt.Y();
1416 0 : aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1417 0 : aItemRect.Right() = aPt.X();
1418 0 : aItemRect.Bottom() = aPt.Y();
1419 0 : Help::ShowQuickHelp( this, aItemRect, rHelpText );
1420 0 : return;
1421 : }
1422 : }
1423 : }
1424 :
1425 0 : Control::RequestHelp( rHEvt );
1426 : }
1427 :
1428 0 : void TabControl::Command( const CommandEvent& rCEvt )
1429 : {
1430 0 : if( (mpTabCtrlData->mpListBox == NULL) && (rCEvt.GetCommand() == COMMAND_CONTEXTMENU) && (GetPageCount() > 1) )
1431 : {
1432 0 : Point aMenuPos;
1433 : bool bMenu;
1434 0 : if ( rCEvt.IsMouseEvent() )
1435 : {
1436 0 : aMenuPos = rCEvt.GetMousePosPixel();
1437 0 : bMenu = GetPageId( aMenuPos ) != 0;
1438 : }
1439 : else
1440 : {
1441 0 : aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
1442 0 : bMenu = true;
1443 : }
1444 :
1445 0 : if ( bMenu )
1446 : {
1447 0 : PopupMenu aMenu;
1448 0 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1449 0 : it != mpTabCtrlData->maItemList.end(); ++it )
1450 : {
1451 0 : aMenu.InsertItem( it->mnId, it->maText, MenuItemBits::CHECKABLE );
1452 0 : if ( it->mnId == mnCurPageId )
1453 0 : aMenu.CheckItem( it->mnId );
1454 0 : aMenu.SetHelpId( it->mnId, it->maHelpId );
1455 : }
1456 :
1457 0 : sal_uInt16 nId = aMenu.Execute( this, aMenuPos );
1458 0 : if ( nId && (nId != mnCurPageId) )
1459 0 : SelectTabPage( nId );
1460 0 : return;
1461 : }
1462 : }
1463 :
1464 0 : Control::Command( rCEvt );
1465 : }
1466 :
1467 112 : void TabControl::StateChanged( StateChangedType nType )
1468 : {
1469 112 : Control::StateChanged( nType );
1470 :
1471 112 : if ( nType == StateChangedType::INITSHOW )
1472 : {
1473 52 : ImplPosCurTabPage();
1474 52 : if( mpTabCtrlData->mpListBox )
1475 0 : Resize();
1476 : }
1477 60 : else if ( nType == StateChangedType::UPDATEMODE )
1478 : {
1479 0 : if ( IsUpdateMode() )
1480 0 : Invalidate();
1481 : }
1482 60 : else if ( (nType == StateChangedType::ZOOM) ||
1483 : (nType == StateChangedType::CONTROLFONT) )
1484 : {
1485 0 : ImplInitSettings( true, false, false );
1486 0 : Invalidate();
1487 : }
1488 60 : else if ( nType == StateChangedType::CONTROLFOREGROUND )
1489 : {
1490 0 : ImplInitSettings( false, true, false );
1491 0 : Invalidate();
1492 : }
1493 60 : else if ( nType == StateChangedType::CONTROLBACKGROUND )
1494 : {
1495 0 : ImplInitSettings( false, false, true );
1496 0 : Invalidate();
1497 : }
1498 112 : }
1499 :
1500 2 : void TabControl::DataChanged( const DataChangedEvent& rDCEvt )
1501 : {
1502 2 : Control::DataChanged( rDCEvt );
1503 :
1504 6 : if ( (rDCEvt.GetType() == DATACHANGED_FONTS) ||
1505 6 : (rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) ||
1506 4 : ((rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
1507 2 : (rDCEvt.GetFlags() & SETTINGS_STYLE)) )
1508 : {
1509 2 : ImplInitSettings( true, true, true );
1510 2 : Invalidate();
1511 : }
1512 2 : }
1513 :
1514 0 : Rectangle* TabControl::ImplFindPartRect( const Point& rPt )
1515 : {
1516 0 : ImplTabItem* pFoundItem = NULL;
1517 0 : int nFound = 0;
1518 0 : for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1519 0 : it != mpTabCtrlData->maItemList.end(); ++it )
1520 : {
1521 0 : if ( it->maRect.IsInside( rPt ) )
1522 : {
1523 : // assure that only one tab is highlighted at a time
1524 0 : nFound++;
1525 0 : pFoundItem = &(*it);
1526 : }
1527 : }
1528 : // assure that only one tab is highlighted at a time
1529 0 : return nFound == 1 ? &pFoundItem->maRect : NULL;
1530 : }
1531 :
1532 0 : bool TabControl::PreNotify( NotifyEvent& rNEvt )
1533 : {
1534 0 : const MouseEvent* pMouseEvt = NULL;
1535 :
1536 0 : if( (rNEvt.GetType() == EVENT_MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != NULL )
1537 : {
1538 0 : if( !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
1539 : {
1540 : // trigger redraw if mouse over state has changed
1541 0 : if( IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL) )
1542 : {
1543 0 : Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
1544 0 : Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
1545 0 : if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
1546 : {
1547 0 : vcl::Region aClipRgn;
1548 0 : if( pLastRect )
1549 : {
1550 : // allow for slightly bigger tabitems
1551 : // as used by gtk
1552 : // TODO: query for the correct sizes
1553 0 : Rectangle aRect(*pLastRect);
1554 0 : aRect.Left()-=2;
1555 0 : aRect.Right()+=2;
1556 0 : aRect.Top()-=3;
1557 0 : aClipRgn.Union( aRect );
1558 : }
1559 0 : if( pRect )
1560 : {
1561 : // allow for slightly bigger tabitems
1562 : // as used by gtk
1563 : // TODO: query for the correct sizes
1564 0 : Rectangle aRect(*pRect);
1565 0 : aRect.Left()-=2;
1566 0 : aRect.Right()+=2;
1567 0 : aRect.Top()-=3;
1568 0 : aClipRgn.Union( aRect );
1569 : }
1570 0 : if( !aClipRgn.IsEmpty() )
1571 0 : Invalidate( aClipRgn );
1572 : }
1573 : }
1574 : }
1575 : }
1576 :
1577 0 : return Control::PreNotify(rNEvt);
1578 : }
1579 :
1580 52 : bool TabControl::Notify( NotifyEvent& rNEvt )
1581 : {
1582 52 : bool nRet = false;
1583 :
1584 52 : if ( rNEvt.GetType() == EVENT_KEYINPUT )
1585 0 : nRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );
1586 :
1587 52 : return nRet || Control::Notify( rNEvt );
1588 : }
1589 :
1590 260 : void TabControl::ActivatePage()
1591 : {
1592 260 : maActivateHdl.Call( this );
1593 260 : }
1594 :
1595 0 : bool TabControl::DeactivatePage()
1596 : {
1597 0 : return !maDeactivateHdl.IsSet() || maDeactivateHdl.Call( this );
1598 : }
1599 :
1600 52 : void TabControl::SetTabPageSizePixel( const Size& rSize )
1601 : {
1602 52 : ImplFreeLayoutData();
1603 :
1604 52 : Size aNewSize( rSize );
1605 52 : aNewSize.Width() += TAB_OFFSET*2;
1606 : Rectangle aRect = ImplGetTabRect( TAB_PAGERECT,
1607 52 : aNewSize.Width(), aNewSize.Height() );
1608 52 : aNewSize.Height() += aRect.Top()+TAB_OFFSET;
1609 52 : Window::SetOutputSizePixel( aNewSize );
1610 52 : }
1611 :
1612 0 : Size TabControl::GetTabPageSizePixel() const
1613 : {
1614 0 : Rectangle aRect = ((TabControl*)this)->ImplGetTabRect( TAB_PAGERECT );
1615 0 : return aRect.GetSize();
1616 : }
1617 :
1618 260 : void TabControl::InsertPage( sal_uInt16 nPageId, const OUString& rText,
1619 : sal_uInt16 nPos )
1620 : {
1621 : DBG_ASSERT( nPageId, "TabControl::InsertPage(): PageId == 0" );
1622 : DBG_ASSERT( GetPagePos( nPageId ) == TAB_PAGE_NOTFOUND,
1623 : "TabControl::InsertPage(): PageId already exists" );
1624 :
1625 : // insert new page item
1626 260 : ImplTabItem* pItem = NULL;
1627 260 : if( nPos == TAB_APPEND || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1628 : {
1629 260 : mpTabCtrlData->maItemList.push_back( ImplTabItem() );
1630 260 : pItem = &mpTabCtrlData->maItemList.back();
1631 260 : if( mpTabCtrlData->mpListBox )
1632 0 : mpTabCtrlData->mpListBox->InsertEntry( rText );
1633 : }
1634 : else
1635 : {
1636 : std::vector< ImplTabItem >::iterator new_it =
1637 0 : mpTabCtrlData->maItemList.insert( mpTabCtrlData->maItemList.begin() + nPos, ImplTabItem() );
1638 0 : pItem = &(*new_it);
1639 0 : if( mpTabCtrlData->mpListBox )
1640 0 : mpTabCtrlData->mpListBox->InsertEntry( rText, nPos);
1641 : }
1642 260 : if( mpTabCtrlData->mpListBox )
1643 : {
1644 0 : if( ! mnCurPageId )
1645 0 : mpTabCtrlData->mpListBox->SelectEntryPos( 0 );
1646 0 : mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1647 : }
1648 :
1649 : // set current page id
1650 260 : if ( !mnCurPageId )
1651 52 : mnCurPageId = nPageId;
1652 :
1653 : // init new page item
1654 260 : pItem->mnId = nPageId;
1655 260 : pItem->mpTabPage = NULL;
1656 260 : pItem->maText = rText;
1657 260 : pItem->mbFullVisible = false;
1658 :
1659 260 : mbFormat = true;
1660 260 : if ( IsUpdateMode() )
1661 260 : Invalidate();
1662 :
1663 260 : ImplFreeLayoutData();
1664 260 : if( mpTabCtrlData->mpListBox ) // reposition/resize listbox
1665 0 : Resize();
1666 :
1667 260 : ImplCallEventListeners( VCLEVENT_TABPAGE_INSERTED, reinterpret_cast<void*>(nPageId) );
1668 260 : }
1669 :
1670 0 : void TabControl::RemovePage( sal_uInt16 nPageId )
1671 : {
1672 0 : sal_uInt16 nPos = GetPagePos( nPageId );
1673 :
1674 : // does the item exist ?
1675 0 : if ( nPos != TAB_PAGE_NOTFOUND )
1676 : {
1677 : //remove page item
1678 0 : std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin() + nPos;
1679 0 : bool bIsCurrentPage = (it->mnId == mnCurPageId);
1680 0 : mpTabCtrlData->maItemList.erase( it );
1681 0 : if( mpTabCtrlData->mpListBox )
1682 : {
1683 0 : mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1684 0 : mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1685 : }
1686 :
1687 : // If current page is removed, than first page gets the current page
1688 0 : if ( bIsCurrentPage )
1689 : {
1690 0 : mnCurPageId = 0;
1691 :
1692 0 : if( ! mpTabCtrlData->maItemList.empty() )
1693 : {
1694 : // don't do this by simply setting mnCurPageId to pFirstItem->mnId
1695 : // this leaves a lot of stuff (such trivias as _showing_ the new current page) undone
1696 : // instead, call SetCurPageId
1697 : // without this, the next (outside) call to SetCurPageId with the id of the first page
1698 : // will result in doing nothing (as we assume that nothing changed, then), and the page
1699 : // will never be shown.
1700 : // 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com
1701 :
1702 0 : SetCurPageId( mpTabCtrlData->maItemList[0].mnId );
1703 : }
1704 : }
1705 :
1706 0 : mbFormat = true;
1707 0 : if ( IsUpdateMode() )
1708 0 : Invalidate();
1709 :
1710 0 : ImplFreeLayoutData();
1711 :
1712 0 : ImplCallEventListeners( VCLEVENT_TABPAGE_REMOVED, reinterpret_cast<void*>(nPageId) );
1713 : }
1714 0 : }
1715 :
1716 8 : void TabControl::Clear()
1717 : {
1718 : // clear item list
1719 8 : mpTabCtrlData->maItemList.clear();
1720 8 : mnCurPageId = 0;
1721 8 : if( mpTabCtrlData->mpListBox )
1722 0 : mpTabCtrlData->mpListBox->Clear();
1723 :
1724 8 : ImplFreeLayoutData();
1725 :
1726 8 : mbFormat = true;
1727 8 : if ( IsUpdateMode() )
1728 8 : Invalidate();
1729 :
1730 8 : ImplCallEventListeners( VCLEVENT_TABPAGE_REMOVEDALL );
1731 8 : }
1732 :
1733 0 : void TabControl::EnablePage( sal_uInt16 i_nPageId, bool i_bEnable )
1734 : {
1735 0 : ImplTabItem* pItem = ImplGetItem( i_nPageId );
1736 :
1737 0 : if ( pItem && pItem->mbEnabled != i_bEnable )
1738 : {
1739 0 : pItem->mbEnabled = i_bEnable;
1740 0 : mbFormat = true;
1741 0 : if( mpTabCtrlData->mpListBox )
1742 0 : mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
1743 0 : i_bEnable ? 0 : (LISTBOX_ENTRY_FLAG_DISABLE_SELECTION | LISTBOX_ENTRY_FLAG_DRAW_DISABLED) );
1744 0 : if( pItem->mnId == mnCurPageId )
1745 : {
1746 : // SetCurPageId will change to an enabled page
1747 0 : SetCurPageId( mnCurPageId );
1748 : }
1749 0 : else if ( IsUpdateMode() )
1750 0 : Invalidate();
1751 : }
1752 0 : }
1753 :
1754 872 : sal_uInt16 TabControl::GetPageCount() const
1755 : {
1756 872 : return (sal_uInt16)mpTabCtrlData->maItemList.size();
1757 : }
1758 :
1759 0 : sal_uInt16 TabControl::GetPageId( sal_uInt16 nPos ) const
1760 : {
1761 0 : if( size_t(nPos) < mpTabCtrlData->maItemList.size() )
1762 0 : return mpTabCtrlData->maItemList[ nPos ].mnId;
1763 0 : return 0;
1764 : }
1765 :
1766 777 : sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
1767 : {
1768 2331 : for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
1769 1554 : it != mpTabCtrlData->maItemList.end(); ++it )
1770 : {
1771 777 : if ( it->mnId == nPageId )
1772 777 : return (sal_uInt16)(it - mpTabCtrlData->maItemList.begin());
1773 : }
1774 :
1775 0 : return TAB_PAGE_NOTFOUND;
1776 : }
1777 :
1778 0 : sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
1779 : {
1780 0 : for( size_t i = 0; i < mpTabCtrlData->maItemList.size(); ++i )
1781 : {
1782 0 : if ( ((TabControl*)this)->ImplGetTabRect( static_cast<sal_uInt16>(i) ).IsInside( rPos ) )
1783 0 : return mpTabCtrlData->maItemList[ i ].mnId;
1784 : }
1785 :
1786 0 : return 0;
1787 : }
1788 :
1789 0 : sal_uInt16 TabControl::GetPageId( const TabPage& rPage ) const
1790 : {
1791 0 : for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
1792 0 : it != mpTabCtrlData->maItemList.end(); ++it )
1793 : {
1794 0 : if ( it->mpTabPage == &rPage )
1795 0 : return it->mnId;
1796 : }
1797 :
1798 0 : return 0;
1799 : }
1800 :
1801 0 : sal_uInt16 TabControl::GetPageId( const OString& rName ) const
1802 : {
1803 0 : for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
1804 0 : it != mpTabCtrlData->maItemList.end(); ++it )
1805 : {
1806 0 : if ( it->maTabName == rName )
1807 0 : return it->mnId;
1808 : }
1809 :
1810 0 : return 0;
1811 : }
1812 :
1813 260 : void TabControl::SetCurPageId( sal_uInt16 nPageId )
1814 : {
1815 260 : sal_uInt16 nPos = GetPagePos( nPageId );
1816 780 : while( nPos != TAB_PAGE_NOTFOUND &&
1817 260 : ! mpTabCtrlData->maItemList[nPos].mbEnabled )
1818 : {
1819 0 : nPos++;
1820 0 : if( size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1821 0 : nPos = 0;
1822 0 : if( mpTabCtrlData->maItemList[nPos].mnId == nPageId )
1823 0 : break;
1824 : }
1825 :
1826 260 : if( nPos != TAB_PAGE_NOTFOUND )
1827 : {
1828 260 : nPageId = mpTabCtrlData->maItemList[nPos].mnId;
1829 260 : if ( nPageId == mnCurPageId )
1830 : {
1831 260 : if ( mnActPageId )
1832 0 : mnActPageId = nPageId;
1833 520 : return;
1834 : }
1835 :
1836 0 : if ( mnActPageId )
1837 0 : mnActPageId = nPageId;
1838 : else
1839 : {
1840 0 : mbFormat = true;
1841 0 : sal_uInt16 nOldId = mnCurPageId;
1842 0 : mnCurPageId = nPageId;
1843 0 : ImplChangeTabPage( nPageId, nOldId );
1844 : }
1845 : }
1846 : }
1847 :
1848 832 : sal_uInt16 TabControl::GetCurPageId() const
1849 : {
1850 832 : if ( mnActPageId )
1851 0 : return mnActPageId;
1852 : else
1853 832 : return mnCurPageId;
1854 : }
1855 :
1856 0 : void TabControl::SelectTabPage( sal_uInt16 nPageId )
1857 : {
1858 0 : if ( nPageId && (nPageId != mnCurPageId) )
1859 : {
1860 0 : ImplFreeLayoutData();
1861 :
1862 0 : ImplCallEventListeners( VCLEVENT_TABPAGE_DEACTIVATE, reinterpret_cast<void*>(mnCurPageId) );
1863 0 : if ( DeactivatePage() )
1864 : {
1865 0 : mnActPageId = nPageId;
1866 0 : ActivatePage();
1867 : // Page could have been switched by the Activate handler
1868 0 : nPageId = mnActPageId;
1869 0 : mnActPageId = 0;
1870 0 : SetCurPageId( nPageId );
1871 0 : if( mpTabCtrlData->mpListBox )
1872 0 : mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
1873 0 : ImplCallEventListeners( VCLEVENT_TABPAGE_ACTIVATE, reinterpret_cast<void*>(nPageId) );
1874 : }
1875 : }
1876 0 : }
1877 :
1878 1040 : void TabControl::SetTabPage( sal_uInt16 nPageId, TabPage* pTabPage )
1879 : {
1880 1040 : ImplTabItem* pItem = ImplGetItem( nPageId );
1881 :
1882 1040 : if ( pItem && (pItem->mpTabPage != pTabPage) )
1883 : {
1884 468 : if ( pTabPage )
1885 : {
1886 : DBG_ASSERT( !pTabPage->IsVisible() || isLayoutEnabled(pTabPage),
1887 : "TabControl::SetTabPage() - Non-Layout Enabled Page is visible" );
1888 :
1889 260 : if ( IsDefaultSize() )
1890 52 : SetTabPageSizePixel( pTabPage->GetSizePixel() );
1891 :
1892 : // only set here, so that Resize does not reposition TabPage
1893 260 : pItem->mpTabPage = pTabPage;
1894 260 : queue_resize();
1895 260 : if ( pItem->mnId == mnCurPageId )
1896 52 : ImplChangeTabPage( pItem->mnId, 0 );
1897 : }
1898 : else
1899 : {
1900 208 : pItem->mpTabPage = NULL;
1901 208 : queue_resize();
1902 : }
1903 : }
1904 1040 : }
1905 :
1906 468 : TabPage* TabControl::GetTabPage( sal_uInt16 nPageId ) const
1907 : {
1908 468 : ImplTabItem* pItem = ImplGetItem( nPageId );
1909 :
1910 468 : if ( pItem )
1911 468 : return pItem->mpTabPage;
1912 : else
1913 0 : return NULL;
1914 : }
1915 :
1916 780 : void TabControl::SetPageText( sal_uInt16 nPageId, const OUString& rText )
1917 : {
1918 780 : ImplTabItem* pItem = ImplGetItem( nPageId );
1919 :
1920 780 : if ( pItem && pItem->maText != rText )
1921 : {
1922 0 : pItem->maText = rText;
1923 0 : mbFormat = true;
1924 0 : if( mpTabCtrlData->mpListBox )
1925 : {
1926 0 : sal_uInt16 nPos = GetPagePos( nPageId );
1927 0 : mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1928 0 : mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
1929 : }
1930 0 : if ( IsUpdateMode() )
1931 0 : Invalidate();
1932 0 : ImplFreeLayoutData();
1933 0 : ImplCallEventListeners( VCLEVENT_TABPAGE_PAGETEXTCHANGED, reinterpret_cast<void*>(nPageId) );
1934 : }
1935 780 : }
1936 :
1937 0 : OUString TabControl::GetPageText( sal_uInt16 nPageId ) const
1938 : {
1939 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1940 :
1941 : assert( pItem );
1942 :
1943 0 : return pItem->maText;
1944 : }
1945 :
1946 780 : void TabControl::SetHelpText( sal_uInt16 nPageId, const OUString& rText )
1947 : {
1948 780 : ImplTabItem* pItem = ImplGetItem( nPageId );
1949 :
1950 : assert( pItem );
1951 :
1952 780 : pItem->maHelpText = rText;
1953 780 : }
1954 :
1955 0 : const OUString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
1956 : {
1957 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1958 :
1959 : assert( pItem );
1960 :
1961 0 : if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
1962 : {
1963 0 : Help* pHelp = Application::GetHelp();
1964 0 : if ( pHelp )
1965 0 : pItem->maHelpText = pHelp->GetHelpText( OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this );
1966 : }
1967 0 : return pItem->maHelpText;
1968 : }
1969 :
1970 0 : void TabControl::SetHelpId( sal_uInt16 nPageId, const OString& rId ) const
1971 : {
1972 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1973 :
1974 0 : if ( pItem )
1975 0 : pItem->maHelpId = rId;
1976 0 : }
1977 :
1978 0 : OString TabControl::GetHelpId( sal_uInt16 nPageId ) const
1979 : {
1980 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1981 :
1982 0 : if (pItem)
1983 0 : return pItem->maHelpId;
1984 :
1985 0 : return OString();
1986 : }
1987 :
1988 0 : void TabControl::SetPageName( sal_uInt16 nPageId, const OString& rName ) const
1989 : {
1990 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1991 :
1992 0 : if ( pItem )
1993 0 : pItem->maTabName = rName;
1994 0 : }
1995 :
1996 0 : OString TabControl::GetPageName( sal_uInt16 nPageId ) const
1997 : {
1998 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
1999 :
2000 0 : if (pItem)
2001 0 : return pItem->maTabName;
2002 :
2003 0 : return OString();
2004 : }
2005 :
2006 0 : void TabControl::SetPageImage( sal_uInt16 i_nPageId, const Image& i_rImage )
2007 : {
2008 0 : ImplTabItem* pItem = ImplGetItem( i_nPageId );
2009 :
2010 0 : if ( pItem )
2011 : {
2012 0 : pItem->maTabImage = i_rImage;
2013 0 : mbFormat = true;
2014 0 : if ( IsUpdateMode() )
2015 0 : Invalidate();
2016 : }
2017 0 : }
2018 :
2019 0 : Rectangle TabControl::GetCharacterBounds( sal_uInt16 nPageId, long nIndex ) const
2020 : {
2021 0 : Rectangle aRet;
2022 :
2023 0 : if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
2024 0 : FillLayoutData();
2025 :
2026 0 : if( HasLayoutData() )
2027 : {
2028 0 : boost::unordered_map< int, int >::const_iterator it = mpTabCtrlData->maLayoutPageIdToLine.find( (int)nPageId );
2029 0 : if( it != mpTabCtrlData->maLayoutPageIdToLine.end() )
2030 : {
2031 0 : Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( it->second );
2032 0 : if( (aPair.B() - aPair.A()) >= nIndex )
2033 0 : aRet = mpControlData->mpLayoutData->GetCharacterBounds( aPair.A() + nIndex );
2034 : }
2035 : }
2036 :
2037 0 : return aRet;
2038 : }
2039 :
2040 0 : long TabControl::GetIndexForPoint( const Point& rPoint, sal_uInt16& rPageId ) const
2041 : {
2042 0 : long nRet = -1;
2043 :
2044 0 : if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
2045 0 : FillLayoutData();
2046 :
2047 0 : if( HasLayoutData() )
2048 : {
2049 0 : int nIndex = mpControlData->mpLayoutData->GetIndexForPoint( rPoint );
2050 0 : if( nIndex != -1 )
2051 : {
2052 : // what line (->pageid) is this index in ?
2053 0 : int nLines = mpControlData->mpLayoutData->GetLineCount();
2054 0 : int nLine = -1;
2055 0 : while( ++nLine < nLines )
2056 : {
2057 0 : Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( nLine );
2058 0 : if( aPair.A() <= nIndex && aPair.B() >= nIndex )
2059 : {
2060 0 : nRet = nIndex - aPair.A();
2061 0 : rPageId = (sal_uInt16)mpTabCtrlData->maLayoutLineToPageId[ nLine ];
2062 0 : break;
2063 : }
2064 : }
2065 : }
2066 : }
2067 :
2068 0 : return nRet;
2069 : }
2070 :
2071 0 : void TabControl::FillLayoutData() const
2072 : {
2073 0 : mpTabCtrlData->maLayoutLineToPageId.clear();
2074 0 : mpTabCtrlData->maLayoutPageIdToLine.clear();
2075 0 : const_cast<TabControl*>(this)->ImplPaint( Rectangle(), true );
2076 0 : }
2077 :
2078 0 : Rectangle TabControl::GetTabBounds( sal_uInt16 nPageId ) const
2079 : {
2080 0 : Rectangle aRet;
2081 :
2082 0 : ImplTabItem* pItem = ImplGetItem( nPageId );
2083 0 : if(pItem)
2084 0 : aRet = pItem->maRect;
2085 :
2086 0 : return aRet;
2087 : }
2088 :
2089 52 : void TabControl::SetItemsOffset( const Point& rOffs )
2090 : {
2091 52 : if( mpTabCtrlData )
2092 52 : mpTabCtrlData->maItemsOffset = rOffs;
2093 52 : }
2094 :
2095 1146 : Point TabControl::GetItemsOffset() const
2096 : {
2097 1146 : if( mpTabCtrlData )
2098 1146 : return mpTabCtrlData->maItemsOffset;
2099 : else
2100 0 : return Point();
2101 : }
2102 :
2103 0 : Size TabControl::calculateRequisition() const
2104 : {
2105 0 : Size aOptimalPageSize(0, 0);
2106 :
2107 0 : sal_uInt16 nOrigPageId = GetCurPageId();
2108 0 : for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
2109 0 : it != mpTabCtrlData->maItemList.end(); ++it )
2110 : {
2111 0 : const TabPage *pPage = it->mpTabPage;
2112 : //it's a real nuisance if the page is not inserted yet :-(
2113 : //We need to force all tabs to exist to get overall optimal size for dialog
2114 0 : if (!pPage)
2115 : {
2116 0 : TabControl *pThis = const_cast<TabControl*>(this);
2117 0 : pThis->SetCurPageId(it->mnId);
2118 0 : pThis->ActivatePage();
2119 0 : pPage = it->mpTabPage;
2120 : }
2121 :
2122 0 : if (!pPage)
2123 0 : continue;
2124 :
2125 0 : Size aPageSize(VclContainer::getLayoutRequisition(*pPage));
2126 :
2127 0 : if (aPageSize.Width() > aOptimalPageSize.Width())
2128 0 : aOptimalPageSize.Width() = aPageSize.Width();
2129 0 : if (aPageSize.Height() > aOptimalPageSize.Height())
2130 0 : aOptimalPageSize.Height() = aPageSize.Height();
2131 : }
2132 :
2133 : //fdo#61940 If we were forced to activate pages in order to on-demand
2134 : //create them to get their optimal size, then switch back to the original
2135 : //page and re-activate it
2136 0 : if (nOrigPageId != GetCurPageId())
2137 : {
2138 0 : TabControl *pThis = const_cast<TabControl*>(this);
2139 0 : pThis->SetCurPageId(nOrigPageId);
2140 0 : pThis->ActivatePage();
2141 : }
2142 :
2143 0 : long nTabLabelsBottom = 0, nTabLabelsRight = 0;
2144 0 : for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
2145 0 : it != mpTabCtrlData->maItemList.end(); ++it )
2146 : {
2147 0 : TabControl* pThis = const_cast<TabControl*>(this);
2148 :
2149 0 : sal_uInt16 nPos = it - mpTabCtrlData->maItemList.begin();
2150 0 : Rectangle aTabRect = pThis->ImplGetTabRect(nPos, aOptimalPageSize.Width(), LONG_MAX);
2151 0 : if (aTabRect.Bottom() > nTabLabelsBottom)
2152 0 : nTabLabelsBottom = aTabRect.Bottom();
2153 0 : if (aTabRect.Right() > nTabLabelsRight)
2154 0 : nTabLabelsRight = aTabRect.Right();
2155 : }
2156 :
2157 0 : Size aOptimalSize(aOptimalPageSize);
2158 0 : aOptimalSize.Height() += nTabLabelsBottom;
2159 0 : aOptimalSize.Width() = std::max(nTabLabelsRight, aOptimalSize.Width());
2160 :
2161 0 : aOptimalSize.Width() += TAB_OFFSET * 2;
2162 0 : aOptimalSize.Height() += TAB_OFFSET * 2;
2163 :
2164 0 : return aOptimalSize;
2165 : }
2166 :
2167 0 : Size TabControl::GetOptimalSize() const
2168 : {
2169 0 : return calculateRequisition();
2170 : }
2171 :
2172 628 : void TabControl::queue_resize(StateChangedType eReason)
2173 : {
2174 628 : markLayoutDirty();
2175 628 : Window::queue_resize(eReason);
2176 1861 : }
2177 :
2178 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|