Bug Summary

File:src/servers/app/decorator/TabDecorator.cpp
Warning:line 468, column 29
Dereference of null pointer

Annotated Source Code

1/*
2 * Copyright 2001-2015 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Stephan Aßmus, superstippi@gmx.de
7 * DarkWyrm, bpmagic@columbus.rr.com
8 * Ryan Leavengood, leavengood@gmail.com
9 * Philippe Saint-Pierre, stpere@gmail.com
10 * John Scipione, jscipione@gmail.com
11 * Ingo Weinhold, ingo_weinhold@gmx.de
12 * Clemens Zeidler, haiku@clemens-zeidler.de
13 * Joseph Groover <looncraz@looncraz.net>
14 */
15
16
17/*! Decorator made up of tabs */
18
19
20#include "TabDecorator.h"
21
22#include <algorithm>
23#include <cmath>
24#include <new>
25#include <stdio.h>
26
27#include <Autolock.h>
28#include <Debug.h>
29#include <GradientLinear.h>
30#include <Rect.h>
31#include <View.h>
32
33#include <WindowPrivate.h>
34
35#include "BitmapDrawingEngine.h"
36#include "DesktopSettings.h"
37#include "DrawingEngine.h"
38#include "DrawState.h"
39#include "FontManager.h"
40#include "PatternHandler.h"
41
42
43//#define DEBUG_DECORATOR
44#ifdef DEBUG_DECORATOR
45# define STRACE(x); printf x
46#else
47# define STRACE(x); ;
48#endif
49
50
51static bool
52int_equal(float x, float y)
53{
54 return abs(x - y) <= 1;
55}
56
57
58static const float kBorderResizeLength = 22.0;
59static const float kResizeKnobSize = 18.0;
60
61
62// #pragma mark -
63
64
65// TODO: get rid of DesktopSettings here, and introduce private accessor
66// methods to the Decorator base class
67TabDecorator::TabDecorator(DesktopSettings& settings, BRect frame,
68 Desktop* desktop)
69 :
70 Decorator(settings, frame, desktop),
71 fOldMovingTab(0, 0, -1, -1)
72{
73 STRACE(("TabDecorator:\n"));;
74 STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n",;
75 frame.left, frame.top, frame.right, frame.bottom));;
76
77 // TODO: If the decorator was created with a frame too small, it should
78 // resize itself!
79}
80
81
82TabDecorator::~TabDecorator()
83{
84 STRACE(("TabDecorator: ~TabDecorator()\n"));;
85}
86
87
88// #pragma mark - Public methods
89
90
91/*! \brief Updates the decorator in the rectangular area \a updateRect.
92
93 Updates all areas which intersect the frame and tab.
94
95 \param updateRect The rectangular area to update.
96*/
97void
98TabDecorator::Draw(BRect updateRect)
99{
100 STRACE(("TabDecorator::Draw(BRect ";
101 "updateRect(l:%.1f, t:%.1f, r:%.1f, b:%.1f))\n",;
102 updateRect.left, updateRect.top, updateRect.right, updateRect.bottom));;
103
104 fDrawingEngine->SetDrawState(&fDrawState);
105
106 _DrawFrame(updateRect & fBorderRect);
107 _DrawTabs(updateRect & fTitleBarRect);
108}
109
110
111//! Forces a complete decorator update
112void
113TabDecorator::Draw()
114{
115 STRACE(("TabDecorator: Draw()"));;
116
117 fDrawingEngine->SetDrawState(&fDrawState);
118
119 _DrawFrame(fBorderRect);
120 _DrawTabs(fTitleBarRect);
121}
122
123
124Decorator::Region
125TabDecorator::RegionAt(BPoint where, int32& tab) const
126{
127 // Let the base class version identify hits of the buttons and the tab.
128 Region region = Decorator::RegionAt(where, tab);
129 if (region != REGION_NONE)
130 return region;
131
132 // check the resize corner
133 if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where))
134 return REGION_RIGHT_BOTTOM_CORNER;
135
136 // hit-test the borders
137 if (fLeftBorder.Contains(where))
138 return REGION_LEFT_BORDER;
139 if (fTopBorder.Contains(where))
140 return REGION_TOP_BORDER;
141
142 // Part of the bottom and right borders may be a resize-region, so we have
143 // to check explicitly, if it has been it.
144 if (fRightBorder.Contains(where))
145 region = REGION_RIGHT_BORDER;
146 else if (fBottomBorder.Contains(where))
147 region = REGION_BOTTOM_BORDER;
148 else
149 return REGION_NONE;
150
151 // check resize area
152 if ((fTopTab->flags & B_NOT_RESIZABLE) == 0
153 && (fTopTab->look == B_TITLED_WINDOW_LOOK
154 || fTopTab->look == B_FLOATING_WINDOW_LOOK
155 || fTopTab->look == B_MODAL_WINDOW_LOOK
156 || fTopTab->look == kLeftTitledWindowLook)) {
157 BRect resizeRect(BPoint(fBottomBorder.right - kBorderResizeLength,
158 fBottomBorder.bottom - kBorderResizeLength),
159 fBottomBorder.RightBottom());
160 if (resizeRect.Contains(where))
161 return REGION_RIGHT_BOTTOM_CORNER;
162 }
163
164 return region;
165}
166
167
168bool
169TabDecorator::SetRegionHighlight(Region region, uint8 highlight,
170 BRegion* dirty, int32 tabIndex)
171{
172 Decorator::Tab* tab
173 = static_cast<Decorator::Tab*>(_TabAt(tabIndex));
174 if (tab != NULL__null) {
175 tab->isHighlighted = highlight != 0;
176 // Invalidate the bitmap caches for the close/zoom button, when the
177 // highlight changes.
178 switch (region) {
179 case REGION_CLOSE_BUTTON:
180 if (highlight != RegionHighlight(region))
181 memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps));
182 break;
183 case REGION_ZOOM_BUTTON:
184 if (highlight != RegionHighlight(region))
185 memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps));
186 break;
187 default:
188 break;
189 }
190 }
191
192 return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex);
193}
194
195
196void
197TabDecorator::UpdateColors(DesktopSettings& settings)
198{
199 // Desktop is write locked, so be quick about it.
200 fFocusFrameColor = settings.UIColor(B_WINDOW_BORDER_COLOR);
201 fFocusTabColor = settings.UIColor(B_WINDOW_TAB_COLOR);
202 fFocusTabColorLight = tint_color(fFocusTabColor,
203 (B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2);
204 fFocusTabColorBevel = tint_color(fFocusTabColor, B_LIGHTEN_2_TINT);
205 fFocusTabColorShadow = tint_color(fFocusTabColor,
206 (B_DARKEN_1_TINT + B_NO_TINT) / 2);
207 fFocusTextColor = settings.UIColor(B_WINDOW_TEXT_COLOR);
208
209 fNonFocusFrameColor = settings.UIColor(B_WINDOW_INACTIVE_BORDER_COLOR);
210 fNonFocusTabColor = settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR);
211 fNonFocusTabColorLight = tint_color(fNonFocusTabColor,
212 (B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2);
213 fNonFocusTabColorBevel = tint_color(fNonFocusTabColor, B_LIGHTEN_2_TINT);
214 fNonFocusTabColorShadow = tint_color(fNonFocusTabColor,
215 (B_DARKEN_1_TINT + B_NO_TINT) / 2);
216 fNonFocusTextColor = settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR);
217}
218
219
220void
221TabDecorator::_DoLayout()
222{
223 STRACE(("TabDecorator: Do Layout\n"));;
224 // Here we determine the size of every rectangle that we use
225 // internally when we are given the size of the client rectangle.
226
227 bool hasTab = false;
228
229 switch ((int)fTopTab->look) {
2
Control jumps to 'case B_FLOATING_WINDOW_LOOK:' at line 239
230 case B_MODAL_WINDOW_LOOK:
231 fBorderWidth = 5;
232 break;
233
234 case B_TITLED_WINDOW_LOOK:
235 case B_DOCUMENT_WINDOW_LOOK:
236 hasTab = true;
237 fBorderWidth = 5;
238 break;
239 case B_FLOATING_WINDOW_LOOK:
240 case kLeftTitledWindowLook:
241 hasTab = true;
242 fBorderWidth = 3;
243 break;
3
Execution continues on line 254
244
245 case B_BORDERED_WINDOW_LOOK:
246 fBorderWidth = 1;
247 break;
248
249 default:
250 fBorderWidth = 0;
251 }
252
253 // calculate left/top/right/bottom borders
254 if (fBorderWidth > 0) {
4
Taking true branch
255 // NOTE: no overlapping, the left and right border rects
256 // don't include the corners!
257 fLeftBorder.Set(fFrame.left - fBorderWidth, fFrame.top,
258 fFrame.left - 1, fFrame.bottom);
259
260 fRightBorder.Set(fFrame.right + 1, fFrame.top ,
261 fFrame.right + fBorderWidth, fFrame.bottom);
262
263 fTopBorder.Set(fFrame.left - fBorderWidth, fFrame.top - fBorderWidth,
264 fFrame.right + fBorderWidth, fFrame.top - 1);
265
266 fBottomBorder.Set(fFrame.left - fBorderWidth, fFrame.bottom + 1,
267 fFrame.right + fBorderWidth, fFrame.bottom + fBorderWidth);
268 } else {
269 // no border
270 fLeftBorder.Set(0.0, 0.0, -1.0, -1.0);
271 fRightBorder.Set(0.0, 0.0, -1.0, -1.0);
272 fTopBorder.Set(0.0, 0.0, -1.0, -1.0);
273 fBottomBorder.Set(0.0, 0.0, -1.0, -1.0);
274 }
275
276 fBorderRect = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom());
277
278 // calculate resize rect
279 if (fBorderWidth > 1) {
5
Taking true branch
280 fResizeRect.Set(fBottomBorder.right - kResizeKnobSize,
281 fBottomBorder.bottom - kResizeKnobSize, fBottomBorder.right,
282 fBottomBorder.bottom);
283 } else {
284 // no border or one pixel border (menus and such)
285 fResizeRect.Set(0, 0, -1, -1);
286 }
287
288 if (hasTab) {
6
Taking true branch
289 _DoTabLayout();
7
Calling 'TabDecorator::_DoTabLayout'
290 return;
291 } else {
292 // no tab
293 for (int32 i = 0; i < fTabList.CountItems(); i++) {
294 Decorator::Tab* tab = fTabList.ItemAt(i);
295 tab->tabRect.Set(0.0, 0.0, -1.0, -1.0);
296 }
297 fTabsRegion.MakeEmpty();
298 fTitleBarRect.Set(0.0, 0.0, -1.0, -1.0);
299 }
300}
301
302
303void
304TabDecorator::_DoTabLayout()
305{
306 float tabOffset = 0;
307 if (fTabList.CountItems() == 1) {
8
Assuming the condition is false
9
Taking false branch
308 float tabSize;
309 tabOffset = _SingleTabOffsetAndSize(tabSize);
310 }
311
312 float sumTabWidth = 0;
313 // calculate our tab rect
314 for (int32 i = 0; i < fTabList.CountItems(); i++) {
10
Assuming the condition is false
11
Loop condition is false. Execution continues on line 394
315 Decorator::Tab* tab = _TabAt(i);
316
317 BRect& tabRect = tab->tabRect;
318 // distance from one item of the tab bar to another.
319 // In this case the text and close/zoom rects
320 tab->textOffset = _DefaultTextOffset();
321
322 font_height fontHeight;
323 fDrawState.Font().GetHeight(fontHeight);
324
325 if (tab->look != kLeftTitledWindowLook) {
326 tabRect.Set(fFrame.left - fBorderWidth,
327 fFrame.top - fBorderWidth
328 - ceilf(fontHeight.ascent + fontHeight.descent + 7.0),
329 ((fFrame.right - fFrame.left) < 35.0 ?
330 fFrame.left + 35.0 : fFrame.right) + fBorderWidth,
331 fFrame.top - fBorderWidth);
332 } else {
333 tabRect.Set(fFrame.left - fBorderWidth
334 - ceilf(fontHeight.ascent + fontHeight.descent + 5.0),
335 fFrame.top - fBorderWidth, fFrame.left - fBorderWidth,
336 fFrame.bottom + fBorderWidth);
337 }
338
339 // format tab rect for a floating window - make the rect smaller
340 if (tab->look == B_FLOATING_WINDOW_LOOK) {
341 tabRect.InsetBy(0, 2);
342 tabRect.OffsetBy(0, 2);
343 }
344
345 float offset;
346 float size;
347 float inset;
348 _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset);
349
350 // tab->minTabSize contains just the room for the buttons
351 tab->minTabSize = inset * 2 + tab->textOffset;
352 if ((tab->flags & B_NOT_CLOSABLE) == 0)
353 tab->minTabSize += offset + size;
354 if ((tab->flags & B_NOT_ZOOMABLE) == 0)
355 tab->minTabSize += offset + size;
356
357 // tab->maxTabSize contains tab->minTabSize + the width required for the
358 // title
359 tab->maxTabSize = fDrawingEngine
360 ? ceilf(fDrawingEngine->StringWidth(Title(tab), strlen(Title(tab)),
361 fDrawState.Font())) : 0.0;
362 if (tab->maxTabSize > 0.0)
363 tab->maxTabSize += tab->textOffset;
364 tab->maxTabSize += tab->minTabSize;
365
366 float tabSize = (tab->look != kLeftTitledWindowLook
367 ? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2;
368 if (tabSize < tab->minTabSize)
369 tabSize = tab->minTabSize;
370 if (tabSize > tab->maxTabSize)
371 tabSize = tab->maxTabSize;
372
373 // layout buttons and truncate text
374 if (tab->look != kLeftTitledWindowLook)
375 tabRect.right = tabRect.left + tabSize;
376 else
377 tabRect.bottom = tabRect.top + tabSize;
378
379 // make sure fTabOffset is within limits and apply it to
380 // the tabRect
381 tab->tabOffset = (uint32)tabOffset;
382 if (tab->tabLocation != 0.0 && fTabList.CountItems() == 1
383 && tab->tabOffset > (fRightBorder.right - fLeftBorder.left
384 - tabRect.Width())) {
385 tab->tabOffset = uint32(fRightBorder.right - fLeftBorder.left
386 - tabRect.Width());
387 }
388 tabRect.OffsetBy(tab->tabOffset, 0);
389 tabOffset += tabRect.Width();
390
391 sumTabWidth += tabRect.Width();
392 }
393
394 float windowWidth = fFrame.Width() + 2 * fBorderWidth;
395 if (CountTabs() > 1 && sumTabWidth > windowWidth)
12
Assuming the condition is true
13
Taking true branch
396 _DistributeTabSize(sumTabWidth - windowWidth);
14
Calling 'TabDecorator::_DistributeTabSize'
397
398 // finally, layout the buttons and text within the tab rect
399 for (int32 i = 0; i < fTabList.CountItems(); i++) {
400 Decorator::Tab* tab = fTabList.ItemAt(i);
401
402 if (i == 0)
403 fTitleBarRect = tab->tabRect;
404 else
405 fTitleBarRect = fTitleBarRect | tab->tabRect;
406
407 _LayoutTabItems(tab, tab->tabRect);
408 }
409
410 fTabsRegion = fTitleBarRect;
411}
412
413
414void
415TabDecorator::_DistributeTabSize(float delta)
416{
417 int32 tabCount = fTabList.CountItems();
418 ASSERT(tabCount > 1)(!(tabCount > 1) ? _debuggerAssert("/home/haiku/haiku/haiku/src/servers/app/decorator/TabDecorator.cpp"
,418, "tabCount > 1") : (int)0)
;
419
420 float maxTabSize = 0;
421 float secMaxTabSize = 0;
422 int32 nTabsWithMaxSize = 0;
423 for (int32 i = 0; i < tabCount; i++) {
15
Assuming 'i' is >= 'tabCount'
16
Loop condition is false. Execution continues on line 441
424 Decorator::Tab* tab = fTabList.ItemAt(i);
425 if (tab == NULL__null)
426 continue;
427
428 float tabWidth = tab->tabRect.Width();
429 if (int_equal(maxTabSize, tabWidth)) {
430 nTabsWithMaxSize++;
431 continue;
432 }
433 if (maxTabSize < tabWidth) {
434 secMaxTabSize = maxTabSize;
435 maxTabSize = tabWidth;
436 nTabsWithMaxSize = 1;
437 } else if (secMaxTabSize <= tabWidth)
438 secMaxTabSize = tabWidth;
439 }
440
441 float minus = ceilf(std::min(maxTabSize - secMaxTabSize, delta));
442 delta -= minus;
443 minus /= nTabsWithMaxSize;
444
445 Decorator::Tab* previousTab = NULL__null;
17
'previousTab' initialized to a null pointer value
446 for (int32 i = 0; i < tabCount; i++) {
18
Loop condition is false. Execution continues on line 462
447 Decorator::Tab* tab = fTabList.ItemAt(i);
448 if (tab == NULL__null)
449 continue;
450
451 if (int_equal(maxTabSize, tab->tabRect.Width()))
452 tab->tabRect.right -= minus;
453
454 if (previousTab != NULL__null) {
455 float offsetX = previousTab->tabRect.right - tab->tabRect.left;
456 tab->tabRect.OffsetBy(offsetX, 0);
457 }
458
459 previousTab = tab;
460 }
461
462 if (delta > 0) {
19
Taking false branch
463 _DistributeTabSize(delta);
464 return;
465 }
466
467 // done
468 previousTab->tabRect.right = floorf(fFrame.right + fBorderWidth);
20
Dereference of null pointer
469
470 for (int32 i = 0; i < tabCount; i++) {
471 Decorator::Tab* tab = fTabList.ItemAt(i);
472 if (tab == NULL__null)
473 continue;
474
475 tab->tabOffset = uint32(tab->tabRect.left - fLeftBorder.left);
476 }
477}
478
479
480void
481TabDecorator::_SetTitle(Decorator::Tab* tab, const char* string,
482 BRegion* updateRegion)
483{
484 // TODO: we could be much smarter about the update region
485
486 BRect rect = TabRect((int32) 0) | TabRect(CountTabs() - 1);
487 // Get a rect of all the tabs
488
489 _DoLayout();
490
491 if (updateRegion == NULL__null)
492 return;
493
494 rect = rect | TabRect(CountTabs() - 1);
495 // Update the rect to guarantee it updates all the tabs
496
497 rect.bottom++;
498 // the border will look differently when the title is adjacent
499
500 updateRegion->Include(rect);
501}
502
503
504void
505TabDecorator::_MoveBy(BPoint offset)
506{
507 STRACE(("TabDecorator: Move By (%.1f, %.1f)\n", offset.x, offset.y));;
508
509 // Move all internal rectangles the appropriate amount
510 for (int32 i = 0; i < fTabList.CountItems(); i++) {
511 Decorator::Tab* tab = fTabList.ItemAt(i);
512 tab->zoomRect.OffsetBy(offset);
513 tab->closeRect.OffsetBy(offset);
514 tab->tabRect.OffsetBy(offset);
515 }
516
517 fFrame.OffsetBy(offset);
518 fTitleBarRect.OffsetBy(offset);
519 fTabsRegion.OffsetBy(offset);
520 fResizeRect.OffsetBy(offset);
521 fBorderRect.OffsetBy(offset);
522
523 fLeftBorder.OffsetBy(offset);
524 fRightBorder.OffsetBy(offset);
525 fTopBorder.OffsetBy(offset);
526 fBottomBorder.OffsetBy(offset);
527}
528
529
530void
531TabDecorator::_ResizeBy(BPoint offset, BRegion* dirty)
532{
533 STRACE(("TabDecorator: Resize By (%.1f, %.1f)\n", offset.x, offset.y));;
534
535 // Move all internal rectangles the appropriate amount
536 fFrame.right += offset.x;
537 fFrame.bottom += offset.y;
538
539 // Handle invalidation of resize rect
540 if (dirty != NULL__null && !(fTopTab->flags & B_NOT_RESIZABLE)) {
541 BRect realResizeRect;
542 switch ((int)fTopTab->look) {
543 case B_DOCUMENT_WINDOW_LOOK:
544 realResizeRect = fResizeRect;
545 // Resize rect at old location
546 dirty->Include(realResizeRect);
547 realResizeRect.OffsetBy(offset);
548 // Resize rect at new location
549 dirty->Include(realResizeRect);
550 break;
551
552 case B_TITLED_WINDOW_LOOK:
553 case B_FLOATING_WINDOW_LOOK:
554 case B_MODAL_WINDOW_LOOK:
555 case kLeftTitledWindowLook:
556 // The bottom border resize line
557 realResizeRect.Set(fRightBorder.right - kBorderResizeLength,
558 fBottomBorder.top,
559 fRightBorder.right - kBorderResizeLength,
560 fBottomBorder.bottom - 1);
561 // Old location
562 dirty->Include(realResizeRect);
563 realResizeRect.OffsetBy(offset);
564 // New location
565 dirty->Include(realResizeRect);
566
567 // The right border resize line
568 realResizeRect.Set(fRightBorder.left,
569 fBottomBorder.bottom - kBorderResizeLength,
570 fRightBorder.right - 1,
571 fBottomBorder.bottom - kBorderResizeLength);
572 // Old location
573 dirty->Include(realResizeRect);
574 realResizeRect.OffsetBy(offset);
575 // New location
576 dirty->Include(realResizeRect);
577 break;
578
579 default:
580 break;
581 }
582 }
583
584 fResizeRect.OffsetBy(offset);
585
586 fBorderRect.right += offset.x;
587 fBorderRect.bottom += offset.y;
588
589 fLeftBorder.bottom += offset.y;
590 fTopBorder.right += offset.x;
591
592 fRightBorder.OffsetBy(offset.x, 0.0);
593 fRightBorder.bottom += offset.y;
594
595 fBottomBorder.OffsetBy(0.0, offset.y);
596 fBottomBorder.right += offset.x;
597
598 if (dirty) {
599 if (offset.x > 0.0) {
600 BRect t(fRightBorder.left - offset.x, fTopBorder.top,
601 fRightBorder.right, fTopBorder.bottom);
602 dirty->Include(t);
603 t.Set(fRightBorder.left - offset.x, fBottomBorder.top,
604 fRightBorder.right, fBottomBorder.bottom);
605 dirty->Include(t);
606 dirty->Include(fRightBorder);
607 } else if (offset.x < 0.0) {
608 dirty->Include(BRect(fRightBorder.left, fTopBorder.top,
609 fRightBorder.right, fBottomBorder.bottom));
610 }
611 if (offset.y > 0.0) {
612 BRect t(fLeftBorder.left, fLeftBorder.bottom - offset.y,
613 fLeftBorder.right, fLeftBorder.bottom);
614 dirty->Include(t);
615 t.Set(fRightBorder.left, fRightBorder.bottom - offset.y,
616 fRightBorder.right, fRightBorder.bottom);
617 dirty->Include(t);
618 dirty->Include(fBottomBorder);
619 } else if (offset.y < 0.0) {
620 dirty->Include(fBottomBorder);
621 }
622 }
623
624 // resize tab and layout tab items
625 if (fTitleBarRect.IsValid()) {
626 if (fTabList.CountItems() > 1) {
627 _DoTabLayout();
628 if (dirty != NULL__null)
629 dirty->Include(fTitleBarRect);
630 return;
631 }
632
633 Decorator::Tab* tab = _TabAt(0);
634 BRect& tabRect = tab->tabRect;
635 BRect oldTabRect(tabRect);
636
637 float tabSize;
638 float tabOffset = _SingleTabOffsetAndSize(tabSize);
639
640 float delta = tabOffset - tab->tabOffset;
641 tab->tabOffset = (uint32)tabOffset;
642 if (fTopTab->look != kLeftTitledWindowLook)
643 tabRect.OffsetBy(delta, 0.0);
644 else
645 tabRect.OffsetBy(0.0, delta);
646
647 if (tabSize < tab->minTabSize)
648 tabSize = tab->minTabSize;
649 if (tabSize > tab->maxTabSize)
650 tabSize = tab->maxTabSize;
651
652 if (fTopTab->look != kLeftTitledWindowLook
653 && tabSize != tabRect.Width()) {
654 tabRect.right = tabRect.left + tabSize;
655 } else if (fTopTab->look == kLeftTitledWindowLook
656 && tabSize != tabRect.Height()) {
657 tabRect.bottom = tabRect.top + tabSize;
658 }
659
660 if (oldTabRect != tabRect) {
661 _LayoutTabItems(tab, tabRect);
662
663 if (dirty) {
664 // NOTE: the tab rect becoming smaller only would
665 // handled be the Desktop anyways, so it is sufficient
666 // to include it into the dirty region in it's
667 // final state
668 BRect redraw(tabRect);
669 if (delta != 0.0) {
670 redraw = redraw | oldTabRect;
671 if (fTopTab->look != kLeftTitledWindowLook)
672 redraw.bottom++;
673 else
674 redraw.right++;
675 }
676 dirty->Include(redraw);
677 }
678 }
679 fTitleBarRect = tabRect;
680 fTabsRegion = fTitleBarRect;
681 }
682}
683
684
685void
686TabDecorator::_SetFocus(Decorator::Tab* tab)
687{
688 Decorator::Tab* decoratorTab = static_cast<Decorator::Tab*>(tab);
689
690 decoratorTab->buttonFocus = IsFocus(tab)
691 || ((decoratorTab->look == B_FLOATING_WINDOW_LOOK
692 || decoratorTab->look == kLeftTitledWindowLook)
693 && (decoratorTab->flags & B_AVOID_FOCUS) != 0);
694 if (CountTabs() > 1)
695 _LayoutTabItems(decoratorTab, decoratorTab->tabRect);
696}
697
698
699bool
700TabDecorator::_SetTabLocation(Decorator::Tab* _tab, float location,
701 bool isShifting, BRegion* updateRegion)
702{
703 STRACE(("TabDecorator: Set Tab Location(%.1f)\n", location));;
704
705 if (CountTabs() > 1) {
706 if (isShifting == false) {
707 _DoTabLayout();
708 if (updateRegion != NULL__null)
709 updateRegion->Include(fTitleBarRect);
710
711 fOldMovingTab = BRect(0, 0, -1, -1);
712 return true;
713 } else {
714 if (fOldMovingTab.IsValid() == false)
715 fOldMovingTab = _tab->tabRect;
716 }
717 }
718
719 Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
720 BRect& tabRect = tab->tabRect;
721 if (tabRect.IsValid() == false)
722 return false;
723
724 if (location < 0)
725 location = 0;
726
727 float maxLocation
728 = fRightBorder.right - fLeftBorder.left - tabRect.Width();
729 if (CountTabs() > 1)
730 maxLocation = fTitleBarRect.right - fLeftBorder.left - tabRect.Width();
731
732 if (location > maxLocation)
733 location = maxLocation;
734
735 float delta = floor(location - tab->tabOffset);
736 if (delta == 0.0)
737 return false;
738
739 // redraw old rect (1 pixel on the border must also be updated)
740 BRect rect(tabRect);
741 rect.bottom++;
742 if (updateRegion != NULL__null)
743 updateRegion->Include(rect);
744
745 tabRect.OffsetBy(delta, 0);
746 tab->tabOffset = (int32)location;
747 _LayoutTabItems(_tab, tabRect);
748 tab->tabLocation = maxLocation > 0.0 ? tab->tabOffset / maxLocation : 0.0;
749
750 if (fTabList.CountItems() == 1)
751 fTitleBarRect = tabRect;
752
753 _CalculateTabsRegion();
754
755 // redraw new rect as well
756 rect = tabRect;
757 rect.bottom++;
758 if (updateRegion != NULL__null)
759 updateRegion->Include(rect);
760
761 return true;
762}
763
764
765bool
766TabDecorator::_SetSettings(const BMessage& settings, BRegion* updateRegion)
767{
768 float tabLocation;
769 bool modified = false;
770 for (int32 i = 0; i < fTabList.CountItems(); i++) {
771 if (settings.FindFloat("tab location", i, &tabLocation) != B_OK((int)0))
772 return false;
773 modified |= SetTabLocation(i, tabLocation, updateRegion);
774 }
775 return modified;
776}
777
778
779bool
780TabDecorator::_AddTab(DesktopSettings& settings, int32 index,
781 BRegion* updateRegion)
782{
783 _UpdateFont(settings);
784
785 _DoLayout();
786 if (updateRegion != NULL__null)
787 updateRegion->Include(fTitleBarRect);
788 return true;
789}
790
791
792bool
793TabDecorator::_RemoveTab(int32 index, BRegion* updateRegion)
794{
795 BRect oldTitle = fTitleBarRect;
796 _DoLayout();
1
Calling 'TabDecorator::_DoLayout'
797 if (updateRegion != NULL__null) {
798 updateRegion->Include(oldTitle);
799 updateRegion->Include(fTitleBarRect);
800 }
801 return true;
802}
803
804
805bool
806TabDecorator::_MoveTab(int32 from, int32 to, bool isMoving,
807 BRegion* updateRegion)
808{
809 Decorator::Tab* toTab = _TabAt(to);
810 if (toTab == NULL__null)
811 return false;
812
813 if (from < to) {
814 fOldMovingTab.OffsetBy(toTab->tabRect.Width(), 0);
815 toTab->tabRect.OffsetBy(-fOldMovingTab.Width(), 0);
816 } else {
817 fOldMovingTab.OffsetBy(-toTab->tabRect.Width(), 0);
818 toTab->tabRect.OffsetBy(fOldMovingTab.Width(), 0);
819 }
820
821 toTab->tabOffset = uint32(toTab->tabRect.left - fLeftBorder.left);
822 _LayoutTabItems(toTab, toTab->tabRect);
823
824 _CalculateTabsRegion();
825
826 if (updateRegion != NULL__null)
827 updateRegion->Include(fTitleBarRect);
828 return true;
829}
830
831
832void
833TabDecorator::_GetFootprint(BRegion *region)
834{
835 STRACE(("TabDecorator: GetFootprint\n"));;
836
837 // This function calculates the decorator's footprint in coordinates
838 // relative to the view. This is most often used to set a Window
839 // object's visible region.
840 if (region == NULL__null)
841 return;
842
843 region->MakeEmpty();
844
845 if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
846 return;
847
848 region->Include(fTopBorder);
849 region->Include(fLeftBorder);
850 region->Include(fRightBorder);
851 region->Include(fBottomBorder);
852
853 if (fTopTab->look == B_BORDERED_WINDOW_LOOK)
854 return;
855
856 region->Include(&fTabsRegion);
857
858 if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK) {
859 // include the rectangular resize knob on the bottom right
860 float knobSize = kResizeKnobSize - fBorderWidth;
861 region->Include(BRect(fFrame.right - knobSize, fFrame.bottom - knobSize,
862 fFrame.right, fFrame.bottom));
863 }
864}
865
866
867void
868TabDecorator::_DrawButtons(Decorator::Tab* tab, const BRect& invalid)
869{
870 STRACE(("TabDecorator: _DrawButtons\n"));;
871
872 // Draw the buttons if we're supposed to
873 if (!(tab->flags & B_NOT_CLOSABLE) && invalid.Intersects(tab->closeRect))
874 _DrawClose(tab, false, tab->closeRect);
875 if (!(tab->flags & B_NOT_ZOOMABLE) && invalid.Intersects(tab->zoomRect))
876 _DrawZoom(tab, false, tab->zoomRect);
877}
878
879
880void
881TabDecorator::_UpdateFont(DesktopSettings& settings)
882{
883 ServerFont font;
884 if (fTopTab->look == B_FLOATING_WINDOW_LOOK
885 || fTopTab->look == kLeftTitledWindowLook) {
886 settings.GetDefaultPlainFont(font);
887 if (fTopTab->look == kLeftTitledWindowLook)
888 font.SetRotation(90.0f);
889 } else
890 settings.GetDefaultBoldFont(font);
891
892 font.SetFlags(B_FORCE_ANTIALIASING);
893 font.SetSpacing(B_STRING_SPACING);
894 fDrawState.SetFont(font);
895}
896
897
898void
899TabDecorator::_GetButtonSizeAndOffset(const BRect& tabRect, float* _offset,
900 float* _size, float* _inset) const
901{
902 float tabSize = fTopTab->look == kLeftTitledWindowLook ?
903 tabRect.Width() : tabRect.Height();
904
905 bool smallTab = fTopTab->look == B_FLOATING_WINDOW_LOOK
906 || fTopTab->look == kLeftTitledWindowLook;
907
908 *_offset = smallTab ? floorf(fDrawState.Font().Size() / 2.6)
909 : floorf(fDrawState.Font().Size() / 2.3);
910 *_inset = smallTab ? floorf(fDrawState.Font().Size() / 5.0)
911 : floorf(fDrawState.Font().Size() / 6.0);
912
913 // "+ 2" so that the rects are centered within the solid area
914 // (without the 2 pixels for the top border)
915 *_size = tabSize - 2 * *_offset + *_inset;
916}
917
918
919void
920TabDecorator::_LayoutTabItems(Decorator::Tab* _tab, const BRect& tabRect)
921{
922 Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
923
924 float offset;
925 float size;
926 float inset;
927 _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset);
928
929 // default textOffset
930 tab->textOffset = _DefaultTextOffset();
931
932 BRect& closeRect = tab->closeRect;
933 BRect& zoomRect = tab->zoomRect;
934
935 // calulate close rect based on the tab rectangle
936 if (tab->look != kLeftTitledWindowLook) {
937 closeRect.Set(tabRect.left + offset, tabRect.top + offset,
938 tabRect.left + offset + size, tabRect.top + offset + size);
939
940 zoomRect.Set(tabRect.right - offset - size, tabRect.top + offset,
941 tabRect.right - offset, tabRect.top + offset + size);
942
943 // hidden buttons have no width
944 if ((tab->flags & B_NOT_CLOSABLE) != 0)
945 closeRect.right = closeRect.left - offset;
946 if ((tab->flags & B_NOT_ZOOMABLE) != 0)
947 zoomRect.left = zoomRect.right + offset;
948 } else {
949 closeRect.Set(tabRect.left + offset, tabRect.top + offset,
950 tabRect.left + offset + size, tabRect.top + offset + size);
951
952 zoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size,
953 tabRect.left + size + offset, tabRect.bottom - offset);
954
955 // hidden buttons have no height
956 if ((tab->flags & B_NOT_CLOSABLE) != 0)
957 closeRect.bottom = closeRect.top - offset;
958 if ((tab->flags & B_NOT_ZOOMABLE) != 0)
959 zoomRect.top = zoomRect.bottom + offset;
960 }
961
962 // calculate room for title
963 // TODO: the +2 is there because the title often appeared
964 // truncated for no apparent reason - OTOH the title does
965 // also not appear perfectly in the middle
966 if (tab->look != kLeftTitledWindowLook)
967 size = (zoomRect.left - closeRect.right) - tab->textOffset * 2 + inset;
968 else
969 size = (zoomRect.top - closeRect.bottom) - tab->textOffset * 2 + inset;
970
971 bool stackMode = fTabList.CountItems() > 1;
972 if (stackMode && IsFocus(tab) == false) {
973 zoomRect.Set(0, 0, 0, 0);
974 size = (tab->tabRect.right - closeRect.right) - tab->textOffset * 2
975 + inset;
976 }
977 uint8 truncateMode = B_TRUNCATE_MIDDLE;
978 if (stackMode) {
979 if (tab->tabRect.Width() < 100)
980 truncateMode = B_TRUNCATE_END;
981 float titleWidth = fDrawState.Font().StringWidth(Title(tab),
982 BString(Title(tab)).Length());
983 if (size < titleWidth) {
984 float oldTextOffset = tab->textOffset;
985 tab->textOffset -= (titleWidth - size) / 2;
986 const float kMinTextOffset = 5.;
987 if (tab->textOffset < kMinTextOffset)
988 tab->textOffset = kMinTextOffset;
989 size += oldTextOffset * 2;
990 size -= tab->textOffset * 2;
991 }
992 }
993 tab->truncatedTitle = Title(tab);
994 fDrawState.Font().TruncateString(&tab->truncatedTitle, truncateMode, size);
995 tab->truncatedTitleLength = tab->truncatedTitle.Length();
996}
997
998
999float
1000TabDecorator::_DefaultTextOffset() const
1001{
1002 return (fTopTab->look == B_FLOATING_WINDOW_LOOK
1003 || fTopTab->look == kLeftTitledWindowLook) ? 10 : 18;
1004}
1005
1006
1007float
1008TabDecorator::_SingleTabOffsetAndSize(float& tabSize)
1009{
1010 float maxLocation;
1011 if (fTopTab->look != kLeftTitledWindowLook) {
1012 tabSize = fRightBorder.right - fLeftBorder.left;
1013 } else {
1014 tabSize = fBottomBorder.bottom - fTopBorder.top;
1015 }
1016 Decorator::Tab* tab = _TabAt(0);
1017 maxLocation = tabSize - tab->maxTabSize;
1018 if (maxLocation < 0)
1019 maxLocation = 0;
1020
1021 return floorf(tab->tabLocation * maxLocation);
1022}
1023
1024
1025void
1026TabDecorator::_CalculateTabsRegion()
1027{
1028 fTabsRegion.MakeEmpty();
1029 for (int32 i = 0; i < fTabList.CountItems(); i++)
1030 fTabsRegion.Include(fTabList.ItemAt(i)->tabRect);
1031}