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 - 10.0; 240 m_dirty = true; 241 Invalidate(r); 242 if (m_scrollBar) { 243 m_scrollBar->ResizeBy(-x, 0.0); 244 m_scrollBar->MoveBy(x, 0.0); 245 } 246 } 247 } 248 249 void StatusView::MouseUp( 250 BPoint point) { 251 D_HOOK(("StatusView::MouseUp()\n")); 252 253 m_dragging = false; 254 } 255 256 // -------------------------------------------------------- // 257 // *** internal operations 258 // -------------------------------------------------------- // 259 260 void 261 StatusView::drawInto(BView *v, BRect updateRect) 262 { 263 BRect r(Bounds()); 264 D_OPERATION(("StatusView::drawInto(%.1f, %.1f)\n", r.Width(), r.Height())); 265 266 // draw border (minus right edge, which the scrollbar draws) 267 v->SetDrawingMode(B_OP_COPY); 268 v->BeginLineArray(8); 269 v->AddLine(r.LeftTop(), r.RightTop(), M_MED_GRAY_COLOR); 270 BPoint rtop = r.RightTop(); 271 rtop.y++; 272 v->AddLine(rtop, r.RightBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT)); 273 v->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR); 274 v->AddLine(r.LeftBottom(), r.LeftTop(), M_MED_GRAY_COLOR); 275 r.InsetBy(1.0, 1.0); 276 v->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR); 277 rtop.y++; 278 rtop.x--; 279 v->AddLine(rtop, r.RightBottom(), M_GRAY_COLOR); 280 v->AddLine(r.RightBottom(), r.LeftBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT)); 281 v->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR); 282 v->EndLineArray(); 283 r.InsetBy(1.0, 1.0); 284 v->SetLowColor(M_GRAY_COLOR); 285 v->FillRect(r, B_SOLID_LOW); 286 287 r.InsetBy(2.0, 0.0); 288 v->SetDrawingMode(B_OP_ALPHA); 289 v->SetHighColor(0, 0, 0, 255 * m_opacity); 290 291 // draw icon 292 if (m_icon) { 293 v->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 294 BPoint p = r.LeftTop(); 295 p.y--; 296 v->DrawBitmap(m_icon, p); 297 } 298 299 // draw text 300 r.left += 10.0; 301 font_height fh; 302 be_plain_font->GetHeight(&fh); 303 r.bottom = Bounds().bottom - fh.descent - 1.0; 304 v->MovePenTo(r.LeftBottom()); 305 v->DrawString(Text()); 306 307 // draw resize dragger 308 v->SetDrawingMode(B_OP_OVER); 309 r = Bounds(); 310 r.right -= 2.0; 311 r.left = r.right - 2.0; 312 r.InsetBy(0.0, 3.0); 313 r.top += 1.0; 314 for (int32 i = 0; i < r.IntegerHeight(); i += 3) { 315 BPoint p = r.LeftTop() + BPoint(0.0, i); 316 v->SetHighColor(M_MED_GRAY_COLOR); 317 v->StrokeLine(p, p, B_SOLID_HIGH); 318 p += BPoint(1.0, 1.0); 319 v->SetHighColor(M_WHITE_COLOR); 320 v->StrokeLine(p, p, B_SOLID_HIGH); 321 } 322 } 323 324 325 326 void StatusView::setMessage( 327 BString &title, 328 BString &details, 329 status_t error) { 330 D_OPERATION(("StatusView::setMessage(%s)\n", title.String())); 331 332 // get the tip manager instance and reset 333 TipManager *manager = TipManager::Instance(); 334 manager->removeAll(this); 335 336 // append error string 337 if (error) { 338 title << " (" << strerror(error) << ")"; 339 } 340 341 // truncate if necessary 342 bool truncated = false; 343 m_fullText = title; 344 if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) { 345 be_plain_font->TruncateString(&title, B_TRUNCATE_END, 346 Bounds().Width() - 25.0); 347 truncated = true; 348 } 349 BStringView::SetText(title.String()); 350 351 if (truncated || details.CountChars() > 0) { 352 BString tip = m_fullText; 353 if (details.CountChars() > 0) { 354 tip << "\n" << details; 355 } 356 manager->setTip(tip.String(), this); 357 } 358 359 if (error) { 360 beep(); 361 // set icon 362 if (m_icon) { 363 delete m_icon; 364 m_icon = 0; 365 } 366 BRect iconRect(0.0, 0.0, 7.0, 11.0); 367 m_icon = new BBitmap(iconRect, B_CMAP8); 368 m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8); 369 } 370 else { 371 // set icon 372 if (m_icon) { 373 delete m_icon; 374 m_icon = 0; 375 } 376 BRect iconRect(0.0, 0.0, 7.0, 11.0); 377 m_icon = new BBitmap(iconRect, B_CMAP8); 378 m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8); 379 } 380 m_dirty = true; 381 startFade(); 382 Invalidate(); 383 } 384 385 void StatusView::setErrorMessage( 386 BString text, 387 bool error) { 388 D_OPERATION(("StatusView::setErrorMessage(%s)\n", 389 text.String())); 390 391 // get the tip manager instance and reset 392 TipManager *manager = TipManager::Instance(); 393 manager->removeAll(this); 394 395 // truncate if necessary 396 m_fullText = text; 397 if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) { 398 be_plain_font->TruncateString(&text, B_TRUNCATE_END, 399 Bounds().Width() - 25.0); 400 manager->setTip(m_fullText.String(), this); 401 } 402 BStringView::SetText(text.String()); 403 404 if (error) { 405 beep(); 406 // set icon 407 if (m_icon) { 408 delete m_icon; 409 m_icon = 0; 410 } 411 BRect iconRect(0.0, 0.0, 7.0, 11.0); 412 m_icon = new BBitmap(iconRect, B_CMAP8); 413 m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8); 414 } 415 else { 416 // set icon 417 if (m_icon) { 418 delete m_icon; 419 m_icon = 0; 420 } 421 BRect iconRect(0.0, 0.0, 7.0, 11.0); 422 m_icon = new BBitmap(iconRect, B_CMAP8); 423 m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8); 424 } 425 m_dirty = true; 426 startFade(); 427 Invalidate(); 428 } 429 430 void StatusView::startFade() { 431 D_OPERATION(("StatusView::startFade()\n")); 432 433 m_opacity = 1.0; 434 m_decayDelay = TEXT_DECAY_DELAY; 435 if(!m_clock) { 436 m_clock = new BMessageRunner( 437 BMessenger(this), 438 new BMessage('Tick'), 439 TICK_PERIOD); 440 } 441 } 442 443 void StatusView::fadeTick() { 444 D_HOOK(("StatusView::fadeTick()\n")); 445 446 if (m_opacity > 0.0) { 447 if(m_decayDelay > 0) { 448 m_decayDelay -= TICK_PERIOD; 449 return; 450 } 451 452 float steps = static_cast<float>(TEXT_DECAY_TIME) 453 / static_cast<float>(TICK_PERIOD); 454 m_opacity -= (1.0 / steps); 455 if (m_opacity < 0.001) { 456 m_opacity = 0.0; 457 } 458 m_dirty = true; 459 Invalidate(); 460 } 461 else if (m_clock) { 462 delete m_clock; 463 m_clock = 0; 464 465 // get the tip manager instance and reset 466 TipManager *manager = TipManager::Instance(); 467 manager->removeAll(this); 468 } 469 } 470 471 void StatusView::allocBackBitmap(float width, float height) { 472 D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height)); 473 474 // sanity check 475 if(width <= 0.0 || height <= 0.0) 476 return; 477 478 if(m_backBitmap) { 479 // see if the bitmap needs to be expanded 480 BRect b = m_backBitmap->Bounds(); 481 if(b.Width() >= width && b.Height() >= height) 482 return; 483 484 // it does; clean up: 485 freeBackBitmap(); 486 } 487 488 BRect b(0.0, 0.0, width, height); 489 m_backBitmap = new BBitmap(b, B_RGB32, true); 490 if(!m_backBitmap) { 491 D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n")); 492 return; 493 } 494 495 m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW); 496 m_backBitmap->AddChild(m_backView); 497 m_dirty = true; 498 } 499 500 void StatusView::freeBackBitmap() { 501 if(m_backBitmap) { 502 delete m_backBitmap; 503 m_backBitmap = 0; 504 m_backView = 0; 505 } 506 } 507 508 509 // END -- ParameterContainerView.cpp -- 510