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