1 /* 2 * Copyright (c) 1999-2000, Eric Moon. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer. 11 * 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. 15 * 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. 18 * 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. 29 */ 30 31 32 // StatusView.cpp 33 34 #include "StatusView.h" 35 #include "cortex_ui.h" 36 #include "RouteAppNodeManager.h" 37 #include "TipManager.h" 38 39 // Application Kit 40 #include <Message.h> 41 #include <MessageRunner.h> 42 // Interface Kit 43 #include <Bitmap.h> 44 #include <Font.h> 45 #include <ScrollBar.h> 46 #include <Window.h> 47 // Support Kit 48 #include <Beep.h> 49 50 __USE_CORTEX_NAMESPACE 51 52 #include <Debug.h> 53 #define D_ALLOC(x) //PRINT(x) 54 #define D_HOOK(x) //PRINT(x) 55 #define D_MESSAGE(x) //PRINT(x) 56 #define D_OPERATION(x) //PRINT(x) 57 58 // -------------------------------------------------------- // 59 // *** constants 60 // -------------------------------------------------------- // 61 62 const bigtime_t TICK_PERIOD = 50000; 63 const bigtime_t TEXT_DECAY_DELAY = 10 * 1000 * 1000; 64 const bigtime_t TEXT_DECAY_TIME = 3 * 1000 * 1000; 65 66 // width: 8, height:12, color_space: B_CMAP8 67 const unsigned char ERROR_ICON_BITS [] = { 68 0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff, 69 0xff,0x00,0xfa,0xfa,0x00,0xff,0xff,0xff, 70 0x00,0x3f,0x3f,0xfa,0xfa,0x00,0xff,0xff, 71 0x00,0xf9,0xf9,0x3f,0x5d,0x00,0xff,0xff, 72 0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff, 73 0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff, 74 0x00,0x00,0xf9,0x5d,0x00,0x00,0xff,0xff, 75 0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff, 76 0x00,0xf9,0x00,0x00,0x5d,0x00,0xff,0xff, 77 0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff, 78 0x00,0x00,0xf9,0x5d,0x00,0x00,0x0f,0xff, 79 0xff,0xff,0x00,0x00,0x00,0x0f,0x0f,0x0f, 80 }; 81 82 // width: 8, height:12, color_space: B_CMAP8 83 const unsigned char INFO_ICON_BITS [] = { 84 0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff, 85 0xff,0x00,0x21,0x21,0x21,0x00,0xff,0xff, 86 0xff,0x00,0x21,0x92,0x25,0x00,0xff,0xff, 87 0xff,0x00,0x21,0x25,0x25,0x00,0xff,0xff, 88 0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff, 89 0xff,0x00,0x92,0x92,0x25,0x00,0xff,0xff, 90 0xff,0x00,0x21,0x21,0x25,0x00,0xff,0xff, 91 0xff,0x00,0x21,0x21,0x25,0x00,0xff,0xff, 92 0x00,0x00,0x21,0x21,0x25,0x00,0x00,0xff, 93 0x00,0xbe,0x21,0x21,0x92,0xbe,0x25,0x00, 94 0x00,0x00,0x21,0x21,0x21,0x25,0x00,0x0f, 95 0xff,0xff,0x00,0x00,0x00,0x00,0x0f,0x0f, 96 }; 97 98 // -------------------------------------------------------- // 99 // *** ctor/dtor 100 // -------------------------------------------------------- // 101 102 StatusView::StatusView( 103 BRect frame, 104 RouteAppNodeManager *manager, 105 BScrollBar *scrollBar) 106 : BStringView(frame, "StatusView", "", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, 107 B_FRAME_EVENTS | B_WILL_DRAW), 108 m_scrollBar(scrollBar), 109 m_icon(0), 110 m_opacity(1.0), 111 m_clock(0), 112 m_dragging(false), 113 m_backBitmap(0), 114 m_backView(0), 115 m_dirty(true), 116 m_manager(manager) { 117 D_ALLOC(("StatusView::StatusView()\n")); 118 119 SetViewColor(B_TRANSPARENT_COLOR); 120 SetFont(be_plain_font); 121 122 allocBackBitmap(frame.Width(), frame.Height()); 123 } 124 125 StatusView::~StatusView() { 126 D_ALLOC(("StatusView::~ParameterContainerView()\n")); 127 128 // get the tip manager instance and reset 129 TipManager *manager = TipManager::Instance(); 130 manager->removeAll(this); 131 132 delete m_clock; 133 134 freeBackBitmap(); 135 } 136 137 // -------------------------------------------------------- // 138 // *** BScrollView impl 139 // -------------------------------------------------------- // 140 141 void StatusView::AttachedToWindow() { 142 D_HOOK(("StatusView::AttachedToWindow()\n")); 143 144 if (m_manager) { 145 m_manager->setLogTarget(BMessenger(this, Window())); 146 } 147 148 allocBackBitmap(Bounds().Width(), Bounds().Height()); 149 } 150 151 void StatusView::Draw( 152 BRect updateRect) { 153 D_HOOK(("StatusView::Draw()\n")); 154 155 if(!m_backView) { 156 drawInto(this, updateRect); 157 } else { 158 if(m_dirty) { 159 m_backBitmap->Lock(); 160 drawInto(m_backView, updateRect); 161 m_backView->Sync(); 162 m_backBitmap->Unlock(); 163 m_dirty = false; 164 } 165 166 SetDrawingMode(B_OP_COPY); 167 DrawBitmap(m_backBitmap, updateRect, updateRect); 168 } 169 } 170 171 void StatusView::FrameResized( 172 float width, 173 float height) { 174 D_HOOK(("StatusView::FrameResized()\n")); 175 176 allocBackBitmap(width, height); 177 178 // get the tip manager instance and reset 179 TipManager *manager = TipManager::Instance(); 180 manager->removeAll(this); 181 182 // re-truncate the string if necessary 183 BString text = m_fullText; 184 if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) { 185 be_plain_font->TruncateString(&text, B_TRUNCATE_END, 186 Bounds().Width() - 25.0); 187 manager->setTip(m_fullText.String(), this); 188 } 189 BStringView::SetText(text.String()); 190 191 float minWidth, maxWidth, minHeight, maxHeight; 192 Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 193 minWidth = width + 6 * B_V_SCROLL_BAR_WIDTH; 194 Window()->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 195 } 196 197 void StatusView::MessageReceived( 198 BMessage *message) { 199 D_MESSAGE(("StatusView::MessageReceived()\n")); 200 201 switch (message->what) { 202 case 'Tick': 203 fadeTick(); 204 break; 205 206 case RouteAppNodeManager::M_LOG: { 207 D_MESSAGE((" -> RouteAppNodeManager::M_LOG\n")); 208 209 BString title; 210 if (message->FindString("title", &title) != B_OK) { 211 return; 212 } 213 BString details, line; 214 for (int32 i = 0; message->FindString("line", i, &line) == B_OK; i++) { 215 if (details.CountChars() > 0) { 216 details << "\n"; 217 } 218 details << line; 219 } 220 status_t error = B_OK; 221 message->FindInt32("error", &error); 222 setMessage(title, details, error); 223 break; 224 } 225 default: { 226 BStringView::MessageReceived(message); 227 } 228 } 229 } 230 231 void StatusView::MouseDown( 232 BPoint point) { 233 D_HOOK(("StatusView::MouseDown()\n")); 234 235 int32 buttons; 236 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) { 237 buttons = B_PRIMARY_MOUSE_BUTTON; 238 } 239 240 if (buttons == B_PRIMARY_MOUSE_BUTTON) { 241 // drag rect 242 BRect dragRect(Bounds()); 243 dragRect.left = dragRect.right - 10.0; 244 if (dragRect.Contains(point)) { 245 // resize 246 m_dragging = true; 247 SetMouseEventMask(B_POINTER_EVENTS, 248 B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); 249 } 250 } 251 } 252 253 void StatusView::MouseMoved( 254 BPoint point, 255 uint32 transit, 256 const BMessage *message) { 257 D_HOOK(("StatusView::MouseMoved()\n")); 258 259 if (m_dragging) { 260 float x = point.x - (Bounds().right - 5.0); 261 if ((Bounds().Width() + x) <= 16.0) { 262 return; 263 } 264 if (m_scrollBar 265 && ((m_scrollBar->Bounds().Width() - x) <= (6 * B_V_SCROLL_BAR_WIDTH))) { 266 return; 267 } 268 ResizeBy(x, 0.0); 269 BRect r(Bounds()); 270 r.left = r.right - 5.0; 271 if (x > 0) 272 r.left -= x; 273 m_dirty = true; 274 Invalidate(r); 275 if (m_scrollBar) { 276 m_scrollBar->ResizeBy(-x, 0.0); 277 m_scrollBar->MoveBy(x, 0.0); 278 } 279 } 280 } 281 282 void StatusView::MouseUp( 283 BPoint point) { 284 D_HOOK(("StatusView::MouseUp()\n")); 285 286 m_dragging = false; 287 } 288 289 // -------------------------------------------------------- // 290 // *** internal operations 291 // -------------------------------------------------------- // 292 293 void 294 StatusView::drawInto(BView *v, BRect updateRect) 295 { 296 BRect r(Bounds()); 297 D_OPERATION(("StatusView::drawInto(%.1f, %.1f)\n", r.Width(), r.Height())); 298 299 // draw border (minus right edge, which the scrollbar draws) 300 v->SetDrawingMode(B_OP_COPY); 301 v->BeginLineArray(8); 302 v->AddLine(r.LeftTop(), r.RightTop(), M_MED_GRAY_COLOR); 303 BPoint rtop = r.RightTop(); 304 rtop.y++; 305 v->AddLine(rtop, r.RightBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT)); 306 v->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR); 307 v->AddLine(r.LeftBottom(), r.LeftTop(), M_MED_GRAY_COLOR); 308 r.InsetBy(1.0, 1.0); 309 v->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR); 310 rtop.y++; 311 rtop.x--; 312 v->AddLine(rtop, r.RightBottom(), M_GRAY_COLOR); 313 v->AddLine(r.RightBottom(), r.LeftBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT)); 314 v->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR); 315 v->EndLineArray(); 316 r.InsetBy(1.0, 1.0); 317 v->SetLowColor(M_GRAY_COLOR); 318 v->FillRect(r, B_SOLID_LOW); 319 320 r.InsetBy(2.0, 0.0); 321 v->SetDrawingMode(B_OP_ALPHA); 322 v->SetHighColor(0, 0, 0, uchar(255 * m_opacity)); 323 324 // draw icon 325 if (m_icon) { 326 v->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 327 BPoint p = r.LeftTop(); 328 p.y--; 329 v->DrawBitmap(m_icon, p); 330 } 331 332 // draw text 333 r.left += 10.0; 334 font_height fh; 335 be_plain_font->GetHeight(&fh); 336 r.bottom = Bounds().bottom - fh.descent 337 - (Bounds().Height() - fh.ascent - fh.descent) / 2; 338 v->MovePenTo(r.LeftBottom()); 339 v->DrawString(Text()); 340 341 // draw resize dragger 342 v->SetDrawingMode(B_OP_OVER); 343 r = Bounds(); 344 r.right -= 2.0; 345 r.left = r.right - 2.0; 346 r.InsetBy(0.0, 3.0); 347 r.top += 1.0; 348 for (int32 i = 0; i < r.IntegerHeight(); i += 3) { 349 BPoint p = r.LeftTop() + BPoint(0.0, i); 350 v->SetHighColor(M_MED_GRAY_COLOR); 351 v->StrokeLine(p, p, B_SOLID_HIGH); 352 p += BPoint(1.0, 1.0); 353 v->SetHighColor(M_WHITE_COLOR); 354 v->StrokeLine(p, p, B_SOLID_HIGH); 355 } 356 } 357 358 359 360 void StatusView::setMessage( 361 BString &title, 362 BString &details, 363 status_t error) { 364 D_OPERATION(("StatusView::setMessage(%s)\n", title.String())); 365 366 // get the tip manager instance and reset 367 TipManager *manager = TipManager::Instance(); 368 manager->removeAll(this); 369 370 // append error string 371 if (error) { 372 title << " (" << strerror(error) << ")"; 373 } 374 375 // truncate if necessary 376 bool truncated = false; 377 m_fullText = title; 378 if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) { 379 be_plain_font->TruncateString(&title, B_TRUNCATE_END, 380 Bounds().Width() - 25.0); 381 truncated = true; 382 } 383 BStringView::SetText(title.String()); 384 385 if (truncated || details.CountChars() > 0) { 386 BString tip = m_fullText; 387 if (details.CountChars() > 0) { 388 tip << "\n" << details; 389 } 390 manager->setTip(tip.String(), this); 391 } 392 393 if (error) { 394 beep(); 395 // set icon 396 if (m_icon) { 397 delete m_icon; 398 m_icon = 0; 399 } 400 BRect iconRect(0.0, 0.0, 7.0, 11.0); 401 m_icon = new BBitmap(iconRect, B_CMAP8); 402 m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8); 403 } 404 else { 405 // set icon 406 if (m_icon) { 407 delete m_icon; 408 m_icon = 0; 409 } 410 BRect iconRect(0.0, 0.0, 7.0, 11.0); 411 m_icon = new BBitmap(iconRect, B_CMAP8); 412 m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8); 413 } 414 m_dirty = true; 415 startFade(); 416 Invalidate(); 417 } 418 419 void StatusView::setErrorMessage( 420 BString text, 421 bool error) { 422 D_OPERATION(("StatusView::setErrorMessage(%s)\n", 423 text.String())); 424 425 // get the tip manager instance and reset 426 TipManager *manager = TipManager::Instance(); 427 manager->removeAll(this); 428 429 // truncate if necessary 430 m_fullText = text; 431 if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) { 432 be_plain_font->TruncateString(&text, B_TRUNCATE_END, 433 Bounds().Width() - 25.0); 434 manager->setTip(m_fullText.String(), this); 435 } 436 BStringView::SetText(text.String()); 437 438 if (error) { 439 beep(); 440 // set icon 441 if (m_icon) { 442 delete m_icon; 443 m_icon = 0; 444 } 445 BRect iconRect(0.0, 0.0, 7.0, 11.0); 446 m_icon = new BBitmap(iconRect, B_CMAP8); 447 m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8); 448 } 449 else { 450 // set icon 451 if (m_icon) { 452 delete m_icon; 453 m_icon = 0; 454 } 455 BRect iconRect(0.0, 0.0, 7.0, 11.0); 456 m_icon = new BBitmap(iconRect, B_CMAP8); 457 m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8); 458 } 459 m_dirty = true; 460 startFade(); 461 Invalidate(); 462 } 463 464 void StatusView::startFade() { 465 D_OPERATION(("StatusView::startFade()\n")); 466 467 m_opacity = 1.0; 468 m_decayDelay = TEXT_DECAY_DELAY; 469 if(!m_clock) { 470 m_clock = new BMessageRunner( 471 BMessenger(this), 472 new BMessage('Tick'), 473 TICK_PERIOD); 474 } 475 } 476 477 void StatusView::fadeTick() { 478 D_HOOK(("StatusView::fadeTick()\n")); 479 480 if (m_opacity > 0.0) { 481 if(m_decayDelay > 0) { 482 m_decayDelay -= TICK_PERIOD; 483 return; 484 } 485 486 float steps = static_cast<float>(TEXT_DECAY_TIME) 487 / static_cast<float>(TICK_PERIOD); 488 m_opacity -= (1.0 / steps); 489 if (m_opacity < 0.001) { 490 m_opacity = 0.0; 491 } 492 m_dirty = true; 493 Invalidate(); 494 } 495 else if (m_clock) { 496 delete m_clock; 497 m_clock = 0; 498 499 // get the tip manager instance and reset 500 TipManager *manager = TipManager::Instance(); 501 manager->removeAll(this); 502 } 503 } 504 505 void StatusView::allocBackBitmap(float width, float height) { 506 D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height)); 507 508 // sanity check 509 if(width <= 0.0 || height <= 0.0) 510 return; 511 512 if(m_backBitmap) { 513 // see if the bitmap needs to be expanded 514 BRect b = m_backBitmap->Bounds(); 515 if(b.Width() >= width && b.Height() >= height) 516 return; 517 518 // it does; clean up: 519 freeBackBitmap(); 520 } 521 522 BRect b(0.0, 0.0, width, height); 523 m_backBitmap = new BBitmap(b, B_RGB32, true); 524 if(!m_backBitmap) { 525 D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n")); 526 return; 527 } 528 529 m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW); 530 m_backBitmap->AddChild(m_backView); 531 m_dirty = true; 532 } 533 534 void StatusView::freeBackBitmap() { 535 if(m_backBitmap) { 536 delete m_backBitmap; 537 m_backBitmap = 0; 538 m_backView = 0; 539 } 540 } 541 542 543 // END -- ParameterContainerView.cpp -- 544