repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / cortex / InfoView / InfoView.cpp
blobb1d9c77d570008793391f8d7049f95739c1771cb
1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
32 // InfoView.cpp
34 #include "InfoView.h"
35 #include "cortex_ui.h"
37 #include "array_delete.h"
39 // Interface Kit
40 #include <Bitmap.h>
41 #include <Region.h>
42 #include <ScrollBar.h>
43 #include <StringView.h>
44 #include <TextView.h>
45 #include <Window.h>
46 // Storage Kit
47 #include <Mime.h>
48 // Support Kit
49 #include <List.h>
51 __USE_CORTEX_NAMESPACE
53 #include <Debug.h>
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
62 // * PURPOSE:
63 // store the label & text for each field, and provide methods
64 // for linewrapping and drawing
66 // -------------------------------------------------------- //
68 class _InfoTextField
71 public: // *** ctor/dtor
73 _InfoTextField(
74 BString label,
75 BString text,
76 InfoView *parent);
78 ~_InfoTextField();
80 public: // *** operations
82 void drawField(
83 BPoint position);
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(
100 const char c);
102 static bool mustEndLine(
103 const char c);
105 private: // *** data members
107 BString m_label;
109 BString m_text;
111 BList *m_textLines;
113 InfoView *m_parent;
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 // -------------------------------------------------------- //
128 InfoView::InfoView(
129 BString title,
130 BString subTitle,
131 BBitmap *icon)
132 : BView(M_DEFAULT_FRAME, "InfoView", B_FOLLOW_ALL_SIDES,
133 B_WILL_DRAW | B_FRAME_EVENTS),
134 m_title(title),
135 m_subTitle(subTitle),
136 m_icon(0),
137 m_fields(0) {
138 D_ALLOC(("InfoView::InfoView()\n"));
140 if (icon) {
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
151 if (m_fields) {
152 while (m_fields->CountItems() > 0) {
153 _InfoTextField *field = static_cast<_InfoTextField *>
154 (m_fields->RemoveItem((int32)0));
155 if (field) {
156 delete field;
159 delete m_fields;
160 m_fields = 0;
163 // delete the icon bitmap
164 if (m_icon) {
165 delete m_icon;
166 m_icon = 0;
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;
179 title << " info";
180 Window()->SetTitle(title.String());
182 // calculate the area occupied by title, subtitle and icon
183 font_height fh;
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;
193 float width, height;
194 GetPreferredSize(&width, &height);
195 Window()->ResizeTo(width + B_V_SCROLL_BAR_WIDTH, height);
196 ResizeBy(- B_V_SCROLL_BAR_WIDTH, 0.0);
198 // add scroll bar
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();
221 void InfoView::Draw(
222 BRect updateRect) {
223 D_HOOK(("InfoView::Draw()\n"));
225 // Draw side bar
226 SetDrawingMode(B_OP_COPY);
227 BRect r = Bounds();
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);
232 r.right += 1.0;
233 StrokeLine(r.RightTop(), r.RightBottom(), B_SOLID_HIGH);
235 // Draw background
236 BRegion region;
237 region.Include(updateRect);
238 region.Exclude(r);
239 SetLowColor(M_GRAY_COLOR);
240 FillRegion(&region, B_SOLID_LOW);
242 // Draw title
243 SetDrawingMode(B_OP_OVER);
244 font_height fh;
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);
252 // Draw sub-title
253 p.y += fh.descent;
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);
260 // Draw icon
261 p.y = 2 * M_V_MARGIN;
262 if (m_icon) {
263 p.x = B_LARGE_ICON / 2.0;
264 DrawBitmapAsync(m_icon, p);
267 // Draw fields
268 be_plain_font->GetHeight(&fh);
269 p.x = B_LARGE_ICON;
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));
273 field->drawField(p);
274 p.y += field->getHeight() + M_V_MARGIN;
278 void InfoView::FrameResized(
279 float width,
280 float height) {
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
286 font_height fh;
287 BPoint p;
288 be_plain_font->GetHeight(&fh);
289 p.x = B_LARGE_ICON;
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;
298 if (heightChanged) {
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));
304 p.y += fieldHeight;
307 // clean up the rest of the view
308 BRect updateRect;
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);
317 if (p.y > height) {
318 ScrollBar(B_VERTICAL)->SetRange(0.0, ceil(p.y - height));
320 else {
321 ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0);
324 // cache the new frame rect for the next time
325 m_oldFrame = newFrame;
328 void
329 InfoView::GetPreferredSize(
330 float *width,
331 float *height) {
332 D_HOOK(("InfoView::GetPreferredSize()\n"));
334 *width = 0;
335 *height = 0;
337 // calculate the height needed to display everything, avoiding line wrapping
338 font_height fh;
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();
344 if (tfw > *width) {
345 *width = tfw;
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(
359 BString label,
360 BString text) {
361 D_METHOD(("InfoView::addField()\n"));
363 m_fields->AddItem(reinterpret_cast<void *>
364 (new _InfoTextField(label, text, this)));
367 // -------------------------------------------------------- //
368 // *** internal class: _InfoTextField
370 // *** ctor/dtor
371 // -------------------------------------------------------- //
373 _InfoTextField::_InfoTextField(
374 BString label,
375 BString text,
376 InfoView *parent)
377 : m_label(label),
378 m_text(text),
379 m_textLines(0),
380 m_parent(parent) {
381 D_ALLOC(("_InfoTextField::_InfoTextField()\n"));
383 if (m_label != "") {
384 m_label << ": ";
386 m_textLines = new BList();
389 _InfoTextField::~_InfoTextField() {
390 D_ALLOC(("_InfoTextField::~_InfoTextField()\n"));
392 // delete every line
393 if (m_textLines) {
394 while (m_textLines->CountItems() > 0) {
395 BString *line = static_cast<BString *>(m_textLines->RemoveItem((int32)0));
396 if (line) {
397 delete line;
400 delete m_textLines;
401 m_textLines = 0;
405 // -------------------------------------------------------- //
406 // *** internal class: _InfoTextField
408 // *** operations (public)
409 // -------------------------------------------------------- //
411 void _InfoTextField::drawField(
412 BPoint position) {
413 D_METHOD(("_InfoTextField::drawField()\n"));
415 float sideBarWidth = m_parent->getSideBarWidth();
417 // Draw label
418 BPoint p = position;
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);
425 // Draw text
426 font_height fh;
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,
439 bool *heightChanged)
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();
453 delete line;
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"))
462 return;
465 // iterate through the text and split into new lines as
466 // necessary
467 BString *currentLine = new BString(m_text);
468 while (currentLine && (currentLine->CountChars() > 0))
470 int32 lastBreak = 0;
471 for (int32 i = 0; i < currentLine->CountChars(); i++)
473 if (canEndLine(currentLine->ByteAt(i)))
475 lastBreak = i + 1;
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;
484 break;
487 else
489 if (i == currentLine->CountChars() - 1) // the last char in the text
491 m_textLines->AddItem(reinterpret_cast<void *>(currentLine));
492 currentLine = 0;
493 break;
495 else
497 BString buffer;
498 currentLine->CopyInto(buffer, 0, i);
499 if (be_plain_font->StringWidth(buffer.String()) > maxWidth)
501 if (lastBreak < 1)
503 lastBreak = i - 1;
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;
510 break;
517 // report changes in the fields total height (i.e. if the number
518 // of lines changed)
519 if (heightChanged && (numLines != m_textLines->CountItems()))
521 *heightChanged = true;
524 // report changes in the wrapping (e.g. if a word slipped into the
525 // next line)
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;
534 break;
540 // -------------------------------------------------------- //
541 // *** internal class: _InfoTextField
543 // *** accessors (public)
544 // -------------------------------------------------------- //
546 float
547 _InfoTextField::getHeight() const {
548 D_ACCESS(("_InfoTextField::getHeight()\n"));
550 font_height fh;
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
560 else
562 float height = fh.ascent + fh.descent + fh.leading;
563 height *= m_textLines->CountItems();
564 height += fh.leading;
565 return height;
569 float
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();
576 return width;
579 bool
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(
593 const char c)
595 D_METHOD(("_InfoTextField::canEndLine()\n"));
597 if ((c == B_SPACE) || (c == B_TAB) || (c == B_ENTER)
598 || ( c == '-'))
600 return true;
602 return false;
605 bool _InfoTextField::mustEndLine(
606 const char c)
608 D_METHOD(("_InfoTextField::mustEndLine()\n"));
610 if (c == B_ENTER)
612 return true;
614 return false;
617 // END -- InfoView.cpp --