2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "cortex_ui.h"
37 #include "array_delete.h"
42 #include <ScrollBar.h>
43 #include <StringView.h>
51 __USE_CORTEX_NAMESPACE
54 #define D_ALLOC(X) //PRINT (x) // ctor/dtor
55 #define D_HOOK(X) //PRINT (x) // BView impl.
56 #define D_ACCESS(X) //PRINT (x) // Accessors
57 #define D_METHOD(x) //PRINT (x)
59 // -------------------------------------------------------- //
60 // *** internal class: _InfoTextField
63 // store the label & text for each field, and provide methods
64 // for linewrapping and drawing
66 // -------------------------------------------------------- //
71 public: // *** ctor/dtor
80 public: // *** operations
85 void updateLineWrapping(
86 bool *wrappingChanged
= 0,
87 bool *heightChanged
= 0);
89 public: // *** accessors
91 float getHeight() const;
93 float getWidth() const;
95 bool isWrapped() const;
97 private: // *** static internal methods
99 static bool canEndLine(
102 static bool mustEndLine(
105 private: // *** data members
116 // -------------------------------------------------------- //
117 // *** static member init
118 // -------------------------------------------------------- //
120 const BRect
InfoView::M_DEFAULT_FRAME
= BRect(0.0, 0.0, 250.0, 100.0);
121 const float InfoView::M_H_MARGIN
= 5.0;
122 const float InfoView::M_V_MARGIN
= 5.0;
124 // -------------------------------------------------------- //
125 // *** ctor/dtor (public)
126 // -------------------------------------------------------- //
132 : BView(M_DEFAULT_FRAME
, "InfoView", B_FOLLOW_ALL_SIDES
,
133 B_WILL_DRAW
| B_FRAME_EVENTS
),
135 m_subTitle(subTitle
),
138 D_ALLOC(("InfoView::InfoView()\n"));
141 m_icon
= new BBitmap(icon
);
143 m_fields
= new BList();
144 SetViewColor(B_TRANSPARENT_COLOR
);
147 InfoView::~InfoView() {
148 D_ALLOC(("InfoView::~InfoView()\n"));
150 // delete all the fields
152 while (m_fields
->CountItems() > 0) {
153 _InfoTextField
*field
= static_cast<_InfoTextField
*>
154 (m_fields
->RemoveItem((int32
)0));
163 // delete the icon bitmap
170 // -------------------------------------------------------- //
171 // *** BView implementation
172 // -------------------------------------------------------- //
174 void InfoView::AttachedToWindow() {
175 D_HOOK(("InfoView::AttachedToWindow()\n"));
177 // adjust the windows title
178 BString title
= m_title
;
180 Window()->SetTitle(title
.String());
182 // calculate the area occupied by title, subtitle and icon
184 be_bold_font
->GetHeight(&fh
);
185 float titleHeight
= fh
.leading
+ fh
.descent
;
186 titleHeight
+= M_V_MARGIN
* 2.0 + B_LARGE_ICON
/ 2.0;
187 be_plain_font
->GetHeight(&fh
);
188 titleHeight
+= fh
.leading
+ fh
.ascent
+ fh
.descent
;
189 BFont
font(be_bold_font
);
190 float titleWidth
= font
.StringWidth(title
.String());
191 titleWidth
+= M_H_MARGIN
+ B_LARGE_ICON
+ B_LARGE_ICON
/ 2.0;
194 GetPreferredSize(&width
, &height
);
195 Window()->ResizeTo(width
+ B_V_SCROLL_BAR_WIDTH
, height
);
196 ResizeBy(- B_V_SCROLL_BAR_WIDTH
, 0.0);
199 BRect scrollRect
= Window()->Bounds();
200 scrollRect
.left
= scrollRect
.right
- B_V_SCROLL_BAR_WIDTH
+ 1.0;
201 scrollRect
.top
-= 1.0;
202 scrollRect
.right
+= 1.0;
203 scrollRect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
- 1.0;
204 Window()->AddChild(new BScrollBar(scrollRect
, "ScrollBar", this,
205 0.0, 0.0, B_VERTICAL
));
206 ScrollBar(B_VERTICAL
)->SetRange(0.0, 0.0);
207 be_plain_font
->GetHeight(&fh
);
208 float step
= fh
.ascent
+ fh
.descent
+ fh
.leading
+ M_V_MARGIN
;
209 ScrollBar(B_VERTICAL
)->SetSteps(step
, step
* 5);
211 // set window size limits
212 float minWidth
, maxWidth
, minHeight
, maxHeight
;
213 Window()->GetSizeLimits(&minWidth
, &maxWidth
, &minHeight
, &maxHeight
);
214 Window()->SetSizeLimits(titleWidth
+ B_V_SCROLL_BAR_WIDTH
, maxWidth
,
215 titleHeight
+ B_H_SCROLL_BAR_HEIGHT
, maxHeight
);
217 // cache the bounds rect for proper redraw later on...
218 m_oldFrame
= Bounds();
223 D_HOOK(("InfoView::Draw()\n"));
226 SetDrawingMode(B_OP_COPY
);
228 r
.right
= B_LARGE_ICON
- 1.0;
229 SetLowColor(M_LIGHT_BLUE_COLOR
);
230 FillRect(r
, B_SOLID_LOW
);
231 SetHighColor(M_DARK_BLUE_COLOR
);
233 StrokeLine(r
.RightTop(), r
.RightBottom(), B_SOLID_HIGH
);
237 region
.Include(updateRect
);
239 SetLowColor(M_GRAY_COLOR
);
240 FillRegion(®ion
, B_SOLID_LOW
);
243 SetDrawingMode(B_OP_OVER
);
245 be_bold_font
->GetHeight(&fh
);
246 SetFont(be_bold_font
);
247 BPoint
p(M_H_MARGIN
+ B_LARGE_ICON
+ B_LARGE_ICON
/ 2.0,
248 M_V_MARGIN
* 2.0 + fh
.ascent
);
249 SetHighColor(M_BLACK_COLOR
);
250 DrawString(m_title
.String(), p
);
254 be_plain_font
->GetHeight(&fh
);
255 SetFont(be_plain_font
);
256 p
.y
+= fh
.ascent
+ fh
.leading
;
257 SetHighColor(M_DARK_GRAY_COLOR
);
258 DrawString(m_subTitle
.String(), p
);
261 p
.y
= 2 * M_V_MARGIN
;
263 p
.x
= B_LARGE_ICON
/ 2.0;
264 DrawBitmapAsync(m_icon
, p
);
268 be_plain_font
->GetHeight(&fh
);
270 p
.y
+= B_LARGE_ICON
+ 2 * M_V_MARGIN
+ fh
.ascent
+ 2 * fh
.leading
;
271 for (int32 i
= 0; i
< m_fields
->CountItems(); i
++) {
272 _InfoTextField
*field
= static_cast<_InfoTextField
*>(m_fields
->ItemAt(i
));
274 p
.y
+= field
->getHeight() + M_V_MARGIN
;
278 void InfoView::FrameResized(
281 D_HOOK(("InfoView::FrameResized()\n"));
283 BRect newFrame
= BRect(0.0, 0.0, width
, height
);
285 // update the each lines' line-wrapping and redraw as necessary
288 be_plain_font
->GetHeight(&fh
);
290 p
.y
+= B_LARGE_ICON
+ M_V_MARGIN
* 2.0 + fh
.ascent
+ fh
.leading
* 2.0;
291 bool heightChanged
= false;
292 for (int32 i
= 0; i
< m_fields
->CountItems(); i
++) {
293 bool wrappingChanged
= false;
294 _InfoTextField
*field
= static_cast<_InfoTextField
*>(m_fields
->ItemAt(i
));
295 field
->updateLineWrapping(&wrappingChanged
,
296 heightChanged
? 0 : &heightChanged
);
297 float fieldHeight
= field
->getHeight() + M_V_MARGIN
;
299 Invalidate(BRect(p
.x
, p
.y
, width
, p
.y
+ fieldHeight
));
301 else if (wrappingChanged
) {
302 Invalidate(BRect(p
.x
+ m_sideBarWidth
, p
.y
, width
, p
.y
+ fieldHeight
));
307 // clean up the rest of the view
309 updateRect
.left
= B_LARGE_ICON
;
310 updateRect
.top
= p
.y
< (m_oldFrame
.bottom
- M_V_MARGIN
- 15.0) ?
311 p
.y
- 15.0 : m_oldFrame
.bottom
- M_V_MARGIN
- 15.0;
312 updateRect
.right
= width
- M_H_MARGIN
;
313 updateRect
.bottom
= m_oldFrame
.bottom
< newFrame
.bottom
?
314 newFrame
.bottom
: m_oldFrame
.bottom
;
315 Invalidate(updateRect
);
318 ScrollBar(B_VERTICAL
)->SetRange(0.0, ceil(p
.y
- height
));
321 ScrollBar(B_VERTICAL
)->SetRange(0.0, 0.0);
324 // cache the new frame rect for the next time
325 m_oldFrame
= newFrame
;
329 InfoView::GetPreferredSize(
332 D_HOOK(("InfoView::GetPreferredSize()\n"));
337 // calculate the height needed to display everything, avoiding line wrapping
339 be_plain_font
->GetHeight(&fh
);
340 for (int32 i
= 0; i
< m_fields
->CountItems(); i
++) {
341 _InfoTextField
*field
= static_cast<_InfoTextField
*>(m_fields
->ItemAt(i
));
342 *height
+= fh
.ascent
+ fh
.descent
+ fh
.leading
+ M_V_MARGIN
;
343 float tfw
= field
->getWidth();
349 *width
+= B_LARGE_ICON
+ 2 * M_H_MARGIN
;
350 *height
+= B_LARGE_ICON
+ 2 * M_V_MARGIN
+ fh
.ascent
+ 2 * fh
.leading
;
351 *height
+= B_H_SCROLL_BAR_HEIGHT
;
354 // -------------------------------------------------------- //
355 // *** operations (protected)
356 // -------------------------------------------------------- //
358 void InfoView::addField(
361 D_METHOD(("InfoView::addField()\n"));
363 m_fields
->AddItem(reinterpret_cast<void *>
364 (new _InfoTextField(label
, text
, this)));
367 // -------------------------------------------------------- //
368 // *** internal class: _InfoTextField
371 // -------------------------------------------------------- //
373 _InfoTextField::_InfoTextField(
381 D_ALLOC(("_InfoTextField::_InfoTextField()\n"));
386 m_textLines
= new BList();
389 _InfoTextField::~_InfoTextField() {
390 D_ALLOC(("_InfoTextField::~_InfoTextField()\n"));
394 while (m_textLines
->CountItems() > 0) {
395 BString
*line
= static_cast<BString
*>(m_textLines
->RemoveItem((int32
)0));
405 // -------------------------------------------------------- //
406 // *** internal class: _InfoTextField
408 // *** operations (public)
409 // -------------------------------------------------------- //
411 void _InfoTextField::drawField(
413 D_METHOD(("_InfoTextField::drawField()\n"));
415 float sideBarWidth
= m_parent
->getSideBarWidth();
419 p
.x
+= sideBarWidth
- be_plain_font
->StringWidth(m_label
.String());
420 m_parent
->SetHighColor(M_DARK_GRAY_COLOR
);
421 m_parent
->SetDrawingMode(B_OP_OVER
);
422 m_parent
->SetFont(be_plain_font
);
423 m_parent
->DrawString(m_label
.String(), p
);
427 be_plain_font
->GetHeight(&fh
);
428 p
.x
= position
.x
+ sideBarWidth
;// + InfoView::M_H_MARGIN;
429 m_parent
->SetHighColor(M_BLACK_COLOR
);
430 for (int32 i
= 0; i
< m_textLines
->CountItems(); i
++) {
431 BString
*line
= static_cast<BString
*>(m_textLines
->ItemAt(i
));
432 m_parent
->DrawString(line
->String(), p
);
433 p
.y
+= fh
.ascent
+ fh
.descent
+ fh
.leading
;
437 void _InfoTextField::updateLineWrapping(
438 bool *wrappingChanged
,
441 D_METHOD(("_InfoTextField::updateLineWrapping()\n"));
443 // clear the current list of lines but remember their number and
444 // the number of characters per line (to know if something changed)
445 int32 numLines
= m_textLines
->CountItems();
446 int32
* numChars
= new int32
[numLines
];
447 array_delete
<int32
> _d(numChars
);
449 for (int32 i
= 0; i
< numLines
; i
++)
451 BString
*line
= static_cast<BString
*>(m_textLines
->ItemAt(i
));
452 numChars
[i
] = line
->CountChars();
455 m_textLines
->MakeEmpty();
457 // calculate the maximum width for a line
458 float maxWidth
= m_parent
->Bounds().Width();
459 maxWidth
-= m_parent
->getSideBarWidth() + 3 * InfoView::M_H_MARGIN
+ B_LARGE_ICON
;
460 if (maxWidth
<= be_plain_font
->StringWidth("M"))
465 // iterate through the text and split into new lines as
467 BString
*currentLine
= new BString(m_text
);
468 while (currentLine
&& (currentLine
->CountChars() > 0))
471 for (int32 i
= 0; i
< currentLine
->CountChars(); i
++)
473 if (canEndLine(currentLine
->ByteAt(i
)))
476 if (mustEndLine(currentLine
->ByteAt(i
)))
478 BString
*newLine
= new BString();
479 currentLine
->Remove(i
, 1);
480 currentLine
->MoveInto(*newLine
, i
,
481 currentLine
->CountChars() - i
);
482 m_textLines
->AddItem(reinterpret_cast<void *>(currentLine
));
483 currentLine
= newLine
;
489 if (i
== currentLine
->CountChars() - 1) // the last char in the text
491 m_textLines
->AddItem(reinterpret_cast<void *>(currentLine
));
498 currentLine
->CopyInto(buffer
, 0, i
);
499 if (be_plain_font
->StringWidth(buffer
.String()) > maxWidth
)
505 BString
*newLine
= new BString();
506 currentLine
->MoveInto(*newLine
, lastBreak
,
507 currentLine
->CountChars() - lastBreak
);
508 m_textLines
->AddItem(reinterpret_cast<void *>(currentLine
));
509 currentLine
= newLine
;
517 // report changes in the fields total height (i.e. if the number
519 if (heightChanged
&& (numLines
!= m_textLines
->CountItems()))
521 *heightChanged
= true;
524 // report changes in the wrapping (e.g. if a word slipped into the
526 else if (wrappingChanged
)
528 for (int32 i
= 0; i
< m_textLines
->CountItems(); i
++)
530 BString
*line
= static_cast<BString
*>(m_textLines
->ItemAt(i
));
531 if (line
->CountChars() != numChars
[i
])
533 *wrappingChanged
= true;
540 // -------------------------------------------------------- //
541 // *** internal class: _InfoTextField
543 // *** accessors (public)
544 // -------------------------------------------------------- //
547 _InfoTextField::getHeight() const {
548 D_ACCESS(("_InfoTextField::getHeight()\n"));
551 be_plain_font
->GetHeight(&fh
);
553 // calculate the width for an empty line (separator)
554 if (m_textLines
->CountItems() == 0)
556 return fh
.ascent
+ fh
.descent
+ fh
.leading
;
559 // calculate the total height of the field by counting the
562 float height
= fh
.ascent
+ fh
.descent
+ fh
.leading
;
563 height
*= m_textLines
->CountItems();
564 height
+= fh
.leading
;
570 _InfoTextField::getWidth() const {
571 D_ACCESS(("_InfoTextField::getWidth()\n"));
573 float width
= be_plain_font
->StringWidth(m_text
.String());
574 width
+= m_parent
->getSideBarWidth();
580 _InfoTextField::isWrapped() const {
581 D_ACCESS(("_InfoTextField::isWrapped()\n"));
583 return (m_textLines
->CountItems() > 1);
586 // -------------------------------------------------------- //
587 // *** internal class: _InfoTextField
589 // *** static internal methods (private)
590 // -------------------------------------------------------- //
592 bool _InfoTextField::canEndLine(
595 D_METHOD(("_InfoTextField::canEndLine()\n"));
597 if ((c
== B_SPACE
) || (c
== B_TAB
) || (c
== B_ENTER
)
605 bool _InfoTextField::mustEndLine(
608 D_METHOD(("_InfoTextField::mustEndLine()\n"));
617 // END -- InfoView.cpp --