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 - 1.0; 306 v->MovePenTo(r.LeftBottom()); 307 v->DrawString(Text()); 308 309 // draw resize dragger 310 v->SetDrawingMode(B_OP_OVER); 311 r = Bounds(); 312 r.right -= 2.0; 313 r.left = r.right - 2.0; 314 r.InsetBy(0.0, 3.0); 315 r.top += 1.0; 316 for (int32 i = 0; i < r.IntegerHeight(); i += 3) { 317 BPoint p = r.LeftTop() + BPoint(0.0, i); 318 v->SetHighColor(M_MED_GRAY_COLOR); 319 v->StrokeLine(p, p, B_SOLID_HIGH); 320 p += BPoint(1.0, 1.0); 321 v->SetHighColor(M_WHITE_COLOR); 322 v->StrokeLine(p, p, B_SOLID_HIGH); 323 } 324 } 325 326 327 328 void StatusView::setMessage( 329 BString &title, 330 BString &details, 331 status_t error) { 332 D_OPERATION(("StatusView::setMessage(%s)\n", title.String())); 333 334 // get the tip manager instance and reset 335 TipManager *manager = TipManager::Instance(); 336 manager->removeAll(this); 337 338 // append error string 339 if (error) { 340 title << " (" << strerror(error) << ")"; 341 } 342 343 // truncate if necessary 344 bool truncated = false; 345 m_fullText = title; 346 if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) { 347 be_plain_font->TruncateString(&title, B_TRUNCATE_END, 348 Bounds().Width() - 25.0); 349 truncated = true; 350 } 351 BStringView::SetText(title.String()); 352 353 if (truncated || details.CountChars() > 0) { 354 BString tip = m_fullText; 355 if (details.CountChars() > 0) { 356 tip << "\n" << details; 357 } 358 manager->setTip(tip.String(), this); 359 } 360 361 if (error) { 362 beep(); 363 // set icon 364 if (m_icon) { 365 delete m_icon; 366 m_icon = 0; 367 } 368 BRect iconRect(0.0, 0.0, 7.0, 11.0); 369 m_icon = new BBitmap(iconRect, B_CMAP8); 370 m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8); 371 } 372 else { 373 // set icon 374 if (m_icon) { 375 delete m_icon; 376 m_icon = 0; 377 } 378 BRect iconRect(0.0, 0.0, 7.0, 11.0); 379 m_icon = new BBitmap(iconRect, B_CMAP8); 380 m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8); 381 } 382 m_dirty = true; 383 startFade(); 384 Invalidate(); 385 } 386 387 void StatusView::setErrorMessage( 388 BString text, 389 bool error) { 390 D_OPERATION(("StatusView::setErrorMessage(%s)\n", 391 text.String())); 392 393 // get the tip manager instance and reset 394 TipManager *manager = TipManager::Instance(); 395 manager->removeAll(this); 396 397 // truncate if necessary 398 m_fullText = text; 399 if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) { 400 be_plain_font->TruncateString(&text, B_TRUNCATE_END, 401 Bounds().Width() - 25.0); 402 manager->setTip(m_fullText.String(), this); 403 } 404 BStringView::SetText(text.String()); 405 406 if (error) { 407 beep(); 408 // set icon 409 if (m_icon) { 410 delete m_icon; 411 m_icon = 0; 412 } 413 BRect iconRect(0.0, 0.0, 7.0, 11.0); 414 m_icon = new BBitmap(iconRect, B_CMAP8); 415 m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8); 416 } 417 else { 418 // set icon 419 if (m_icon) { 420 delete m_icon; 421 m_icon = 0; 422 } 423 BRect iconRect(0.0, 0.0, 7.0, 11.0); 424 m_icon = new BBitmap(iconRect, B_CMAP8); 425 m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8); 426 } 427 m_dirty = true; 428 startFade(); 429 Invalidate(); 430 } 431 432 void StatusView::startFade() { 433 D_OPERATION(("StatusView::startFade()\n")); 434 435 m_opacity = 1.0; 436 m_decayDelay = TEXT_DECAY_DELAY; 437 if(!m_clock) { 438 m_clock = new BMessageRunner( 439 BMessenger(this), 440 new BMessage('Tick'), 441 TICK_PERIOD); 442 } 443 } 444 445 void StatusView::fadeTick() { 446 D_HOOK(("StatusView::fadeTick()\n")); 447 448 if (m_opacity > 0.0) { 449 if(m_decayDelay > 0) { 450 m_decayDelay -= TICK_PERIOD; 451 return; 452 } 453 454 float steps = static_cast<float>(TEXT_DECAY_TIME) 455 / static_cast<float>(TICK_PERIOD); 456 m_opacity -= (1.0 / steps); 457 if (m_opacity < 0.001) { 458 m_opacity = 0.0; 459 } 460 m_dirty = true; 461 Invalidate(); 462 } 463 else if (m_clock) { 464 delete m_clock; 465 m_clock = 0; 466 467 // get the tip manager instance and reset 468 TipManager *manager = TipManager::Instance(); 469 manager->removeAll(this); 470 } 471 } 472 473 void StatusView::allocBackBitmap(float width, float height) { 474 D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height)); 475 476 // sanity check 477 if(width <= 0.0 || height <= 0.0) 478 return; 479 480 if(m_backBitmap) { 481 // see if the bitmap needs to be expanded 482 BRect b = m_backBitmap->Bounds(); 483 if(b.Width() >= width && b.Height() >= height) 484 return; 485 486 // it does; clean up: 487 freeBackBitmap(); 488 } 489 490 BRect b(0.0, 0.0, width, height); 491 m_backBitmap = new BBitmap(b, B_RGB32, true); 492 if(!m_backBitmap) { 493 D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n")); 494 return; 495 } 496 497 m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW); 498 m_backBitmap->AddChild(m_backView); 499 m_dirty = true; 500 } 501 502 void StatusView::freeBackBitmap() { 503 if(m_backBitmap) { 504 delete m_backBitmap; 505 m_backBitmap = 0; 506 m_backView = 0; 507 } 508 } 509 510 511 // END -- ParameterContainerView.cpp -- 512