1 /* 2 * Copyright 2006-2018, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Clemens Zeidler, haiku@Clemens-Zeidler.de 8 * Alexander von Gluck, kallisti5@unixzen.com 9 * Kacper Kasper, kacperkasper@gmail.com 10 */ 11 12 13 #include "PowerStatusView.h" 14 15 #include <algorithm> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include <AboutWindow.h> 22 #include <Application.h> 23 #include <Bitmap.h> 24 #include <Beep.h> 25 #include <Catalog.h> 26 #include <DataIO.h> 27 #include <Deskbar.h> 28 #include <Dragger.h> 29 #include <Drivers.h> 30 #include <File.h> 31 #include <FindDirectory.h> 32 #include <GradientLinear.h> 33 #include <MenuItem.h> 34 #include <MessageRunner.h> 35 #include <Notification.h> 36 #include <NumberFormat.h> 37 #include <Path.h> 38 #include <PopUpMenu.h> 39 #include <Resources.h> 40 #include <Roster.h> 41 #include <TextView.h> 42 #include <TranslationUtils.h> 43 44 #include "ACPIDriverInterface.h" 45 #include "APMDriverInterface.h" 46 #include "ExtendedInfoWindow.h" 47 #include "PowerStatus.h" 48 49 50 #undef B_TRANSLATION_CONTEXT 51 #define B_TRANSLATION_CONTEXT "PowerStatus" 52 53 54 extern "C" _EXPORT BView *instantiate_deskbar_item(float maxWidth, 55 float maxHeight); 56 extern const char* kDeskbarItemName; 57 58 const uint32 kMsgToggleLabel = 'tglb'; 59 const uint32 kMsgToggleTime = 'tgtm'; 60 const uint32 kMsgToggleStatusIcon = 'tgsi'; 61 const uint32 kMsgToggleExtInfo = 'texi'; 62 63 const double kLowBatteryPercentage = 0.15; 64 const double kNoteBatteryPercentage = 0.3; 65 const double kFullBatteryPercentage = 1.0; 66 67 const time_t kLowBatteryTimeLeft = 30 * 60; 68 69 70 PowerStatusView::PowerStatusView(PowerStatusDriverInterface* interface, 71 BRect frame, int32 resizingMode, int batteryID, bool inDeskbar) 72 : 73 BView(frame, kDeskbarItemName, resizingMode, 74 B_WILL_DRAW | B_TRANSPARENT_BACKGROUND | B_FULL_UPDATE_ON_RESIZE), 75 fDriverInterface(interface), 76 fBatteryID(batteryID), 77 fInDeskbar(inDeskbar) 78 { 79 _Init(); 80 } 81 82 83 PowerStatusView::PowerStatusView(BMessage* archive) 84 : 85 BView(archive), 86 fInDeskbar(false) 87 { 88 app_info info; 89 if (be_app->GetAppInfo(&info) == B_OK 90 && !strcasecmp(info.signature, kDeskbarSignature)) 91 fInDeskbar = true; 92 _Init(); 93 FromMessage(archive); 94 } 95 96 97 PowerStatusView::~PowerStatusView() 98 { 99 } 100 101 102 status_t 103 PowerStatusView::Archive(BMessage* archive, bool deep) const 104 { 105 status_t status = BView::Archive(archive, deep); 106 if (status == B_OK) 107 status = ToMessage(archive); 108 109 return status; 110 } 111 112 113 void 114 PowerStatusView::_Init() 115 { 116 fShowLabel = true; 117 fShowTime = false; 118 fShowStatusIcon = true; 119 120 fPercent = 1.0; 121 fTimeLeft = 0; 122 123 fHasNotifiedLowBattery = false; 124 125 add_system_beep_event("Battery critical"); 126 add_system_beep_event("Battery low"); 127 add_system_beep_event("Battery charged"); 128 } 129 130 131 void 132 PowerStatusView::AttachedToWindow() 133 { 134 BView::AttachedToWindow(); 135 136 SetViewColor(B_TRANSPARENT_COLOR); 137 138 if (ViewUIColor() != B_NO_COLOR) 139 SetLowUIColor(ViewUIColor()); 140 else 141 SetLowColor(ViewColor()); 142 143 Update(); 144 } 145 146 147 void 148 PowerStatusView::DetachedFromWindow() 149 { 150 } 151 152 153 void 154 PowerStatusView::MessageReceived(BMessage *message) 155 { 156 switch (message->what) { 157 case kMsgUpdate: 158 Update(); 159 break; 160 161 default: 162 BView::MessageReceived(message); 163 break; 164 } 165 } 166 167 168 void 169 PowerStatusView::_DrawBattery(BView* view, BRect rect) 170 { 171 BRect lightningRect = rect; 172 BRect pauseRect = rect; 173 float quarter = floorf((rect.Height() + 1) / 4); 174 rect.top += quarter; 175 rect.bottom -= quarter; 176 177 rect.InsetBy(2, 0); 178 179 float left = rect.left; 180 rect.left += rect.Width() / 11; 181 lightningRect.left = rect.left; 182 lightningRect.InsetBy(0.0f, 5.0f * rect.Height() / 16); 183 pauseRect.left = rect.left; 184 pauseRect.InsetBy(rect.Width() * 0.1f, rect.Height() * 0.4f); 185 186 if (view->LowColor().IsLight()) 187 view->SetHighColor(0, 0, 0); 188 else 189 view->SetHighColor(128, 128, 128); 190 191 float gap = 1; 192 if (rect.Height() > 8) { 193 gap = ceilf((rect.left - left) / 2); 194 195 // left 196 view->FillRect(BRect(rect.left, rect.top, rect.left + gap - 1, 197 rect.bottom)); 198 // right 199 view->FillRect(BRect(rect.right - gap + 1, rect.top, rect.right, 200 rect.bottom)); 201 // top 202 view->FillRect(BRect(rect.left + gap, rect.top, rect.right - gap, 203 rect.top + gap - 1)); 204 // bottom 205 view->FillRect(BRect(rect.left + gap, rect.bottom + 1 - gap, 206 rect.right - gap, rect.bottom)); 207 } else 208 view->StrokeRect(rect); 209 210 view->FillRect(BRect(left, floorf(rect.top + rect.Height() / 4) + 1, 211 rect.left - 1, floorf(rect.bottom - rect.Height() / 4))); 212 213 double percent = fPercent; 214 if (percent > 1.0) 215 percent = 1.0; 216 else if (percent < 0.0 || !fHasBattery) 217 percent = 0.0; 218 219 rect.InsetBy(gap, gap); 220 221 if (fHasBattery) { 222 // draw unfilled area 223 rgb_color unfilledColor = make_color(0x4c, 0x4c, 0x4c); 224 if (view->LowColor().IsDark()) { 225 unfilledColor.red = 256 - unfilledColor.red; 226 unfilledColor.green = 256 - unfilledColor.green; 227 unfilledColor.blue = 256 - unfilledColor.blue; 228 } 229 230 BRect unfilled = rect; 231 if (percent > 0.0) 232 unfilled.left += unfilled.Width() * percent; 233 234 view->SetHighColor(unfilledColor); 235 view->FillRect(unfilled); 236 237 if (percent > 0.0) { 238 // draw filled area 239 rgb_color fillColor; 240 if (percent <= kLowBatteryPercentage) 241 fillColor.set_to(180, 0, 0); 242 else if (percent <= kNoteBatteryPercentage) 243 fillColor.set_to(200, 140, 0); 244 else 245 fillColor.set_to(20, 180, 0); 246 247 BRect fill = rect; 248 fill.right = fill.left + fill.Width() * percent; 249 250 // draw bevel 251 rgb_color bevelLightColor = tint_color(fillColor, 0.2); 252 rgb_color bevelShadowColor = tint_color(fillColor, 1.08); 253 254 view->BeginLineArray(4); 255 view->AddLine(BPoint(fill.left, fill.bottom), 256 BPoint(fill.left, fill.top), bevelLightColor); 257 view->AddLine(BPoint(fill.left, fill.top), 258 BPoint(fill.right, fill.top), bevelLightColor); 259 view->AddLine(BPoint(fill.right, fill.top), 260 BPoint(fill.right, fill.bottom), bevelShadowColor); 261 view->AddLine(BPoint(fill.left, fill.bottom), 262 BPoint(fill.right, fill.bottom), bevelShadowColor); 263 view->EndLineArray(); 264 265 fill.InsetBy(1, 1); 266 267 // draw gradient 268 float topTint = 0.49; 269 float middleTint1 = 0.62; 270 float middleTint2 = 0.76; 271 float bottomTint = 0.90; 272 273 BGradientLinear gradient; 274 gradient.AddColor(tint_color(fillColor, topTint), 0); 275 gradient.AddColor(tint_color(fillColor, middleTint1), 132); 276 gradient.AddColor(tint_color(fillColor, middleTint2), 136); 277 gradient.AddColor(tint_color(fillColor, bottomTint), 255); 278 gradient.SetStart(fill.LeftTop()); 279 gradient.SetEnd(fill.LeftBottom()); 280 281 view->FillRect(fill, gradient); 282 } 283 } 284 285 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0) { 286 // When charging, draw a lightning symbol over the battery. 287 view->SetHighColor(255, 255, 0, 180); 288 view->SetDrawingMode(B_OP_ALPHA); 289 290 static const BPoint points[] = { 291 BPoint(3, 14), 292 BPoint(10, 6), 293 BPoint(10, 8), 294 BPoint(17, 3), 295 BPoint(9, 12), 296 BPoint(9, 10) 297 }; 298 view->FillPolygon(points, 6, lightningRect); 299 300 view->SetDrawingMode(B_OP_OVER); 301 } else if ((fBatteryInfo.state 302 & (BATTERY_CHARGING | BATTERY_DISCHARGING | BATTERY_CRITICAL_STATE)) == 0) { 303 // When a battery is not in use at all, draw a pause symbol over the battery 304 view->SetHighColor(0, 0, 0, 96); 305 view->SetDrawingMode(B_OP_ALPHA); 306 307 static const BPoint points[] = { 308 BPoint(1, 3), 309 BPoint(1, 6), 310 BPoint(8, 6), 311 BPoint(8, 3), 312 313 BPoint(14, 3), 314 BPoint(14, 6), 315 BPoint(22, 6), 316 BPoint(22, 3) 317 }; 318 view->FillPolygon(points, 8, pauseRect); 319 320 view->SetDrawingMode(B_OP_OVER); 321 } else if ((fBatteryInfo.state & BATTERY_CRITICAL_STATE) != 0) { 322 // When a battery is damaged or missing, draw an X over it 323 view->SetHighColor(200, 0, 0, 96); 324 view->SetDrawingMode(B_OP_ALPHA); 325 326 static const BPoint points[] = { 327 BPoint(1, 1), 328 BPoint(1, 2), 329 BPoint(20, 6), 330 BPoint(22, 6), 331 BPoint(22, 5), 332 BPoint(3, 1), 333 334 BPoint(20, 1), 335 BPoint(1, 5), 336 BPoint(1, 6), 337 BPoint(3, 6), 338 BPoint(22, 2), 339 BPoint(22, 1) 340 }; 341 view->FillPolygon(points, 12, lightningRect); 342 343 view->SetDrawingMode(B_OP_OVER); 344 } 345 346 view->SetHighColor(0, 0, 0); 347 } 348 349 350 void 351 PowerStatusView::Draw(BRect updateRect) 352 { 353 DrawTo(this, Bounds()); 354 } 355 356 357 void 358 PowerStatusView::DrawTo(BView* view, BRect rect) 359 { 360 bool inside = rect.Width() >= 40.0f && rect.Height() >= 40.0f; 361 362 font_height fontHeight; 363 view->GetFontHeight(&fontHeight); 364 float baseLine = ceilf(fontHeight.ascent); 365 366 char text[64]; 367 _SetLabel(text, sizeof(text)); 368 369 float textHeight = ceilf(fontHeight.descent + fontHeight.ascent); 370 float textWidth = view->StringWidth(text); 371 bool showLabel = fShowLabel && text[0]; 372 373 BRect iconRect; 374 375 if (fShowStatusIcon) { 376 iconRect = rect; 377 if (showLabel && inside == false) 378 iconRect.right -= textWidth + 2; 379 380 _DrawBattery(view, iconRect); 381 } 382 383 if (showLabel) { 384 BPoint point(0, baseLine + rect.top); 385 386 if (iconRect.IsValid()) { 387 if (inside == true) { 388 point.x = rect.left + (iconRect.Width() - textWidth) / 2 + 389 iconRect.Width() / 20; 390 point.y += (iconRect.Height() - textHeight) / 2; 391 } else { 392 point.x = rect.left + iconRect.Width() + 2; 393 point.y += (iconRect.Height() - textHeight) / 2; 394 } 395 } else { 396 point.x = rect.left + (Bounds().Width() - textWidth) / 2; 397 point.y += (Bounds().Height() - textHeight) / 2; 398 } 399 400 view->SetDrawingMode(B_OP_OVER); 401 if (fInDeskbar == false || inside == true) { 402 view->SetHighUIColor(B_CONTROL_BACKGROUND_COLOR); 403 view->DrawString(text, BPoint(point.x + 1, point.y + 1)); 404 } 405 view->SetHighUIColor(B_CONTROL_TEXT_COLOR); 406 407 view->DrawString(text, point); 408 } 409 } 410 411 412 void 413 PowerStatusView::_SetLabel(char* buffer, size_t bufferLength) 414 { 415 if (bufferLength < 1) 416 return; 417 418 buffer[0] = '\0'; 419 420 if (!fShowLabel) 421 return; 422 423 const char* open = ""; 424 const char* close = ""; 425 if ((fBatteryInfo.state & BATTERY_DISCHARGING) == 0) { 426 // surround the percentage with () if the battery is not discharging 427 open = "("; 428 close = ")"; 429 } 430 431 if (!fShowTime && fPercent >= 0) { 432 BNumberFormat numberFormat; 433 BString data; 434 435 if (numberFormat.FormatPercent(data, fPercent) != B_OK) { 436 data.SetToFormat("%" B_PRId32 "%%", int32(fPercent * 100)); 437 } 438 439 snprintf(buffer, bufferLength, "%s%s%s", open, data.String(), close); 440 } else if (fShowTime && fTimeLeft >= 0) { 441 snprintf(buffer, bufferLength, "%s%" B_PRIdTIME ":%02" B_PRIdTIME "%s", 442 open, fTimeLeft / 3600, (fTimeLeft / 60) % 60, close); 443 } 444 } 445 446 447 void 448 PowerStatusView::Update(bool force, bool notify) 449 { 450 double previousPercent = fPercent; 451 time_t previousTimeLeft = fTimeLeft; 452 bool wasCharging = (fBatteryInfo.state & BATTERY_CHARGING); 453 bool hadBattery = fHasBattery; 454 _GetBatteryInfo(fBatteryID, &fBatteryInfo); 455 fHasBattery = fBatteryInfo.full_capacity > 0 && fBatteryInfo.state != BATTERY_CRITICAL_STATE; 456 457 if (fBatteryInfo.full_capacity > 0 && fHasBattery) { 458 fPercent = (double)fBatteryInfo.capacity / fBatteryInfo.full_capacity; 459 fTimeLeft = fBatteryInfo.time_left; 460 } else { 461 fPercent = 0.0; 462 fTimeLeft = -1; 463 } 464 465 if (fHasBattery && (fPercent <= 0 || fPercent > 1.0)) { 466 // Just ignore this probe -- it obviously returned invalid values 467 fPercent = previousPercent; 468 fTimeLeft = previousTimeLeft; 469 fHasBattery = hadBattery; 470 return; 471 } 472 473 if (fInDeskbar) { 474 // make sure the tray icon is (just) large enough 475 float width = fShowStatusIcon ? Bounds().Height() : 0; 476 477 if (fShowLabel) { 478 char text[64]; 479 _SetLabel(text, sizeof(text)); 480 481 if (text[0]) 482 width += ceilf(StringWidth(text)) + 2; 483 } else { 484 char text[256]; 485 const char* open = ""; 486 const char* close = ""; 487 if ((fBatteryInfo.state & BATTERY_DISCHARGING) == 0) { 488 // surround the percentage with () if the battery is not discharging 489 open = "("; 490 close = ")"; 491 } 492 if (fHasBattery) { 493 BNumberFormat numberFormat; 494 BString data; 495 size_t length; 496 497 if (numberFormat.FormatPercent(data, fPercent) != B_OK) { 498 data.SetToFormat("%" B_PRId32 "%%", int32(fPercent * 100)); 499 } 500 501 length = snprintf(text, sizeof(text), "%s%s%s", open, data.String(), close); 502 503 if (fTimeLeft >= 0) { 504 length += snprintf(text + length, sizeof(text) - length, "\n%" B_PRIdTIME 505 ":%02" B_PRIdTIME, fTimeLeft / 3600, (fTimeLeft / 60) % 60); 506 } 507 508 const char* state = NULL; 509 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0) 510 state = B_TRANSLATE("charging"); 511 else if ((fBatteryInfo.state & BATTERY_DISCHARGING) != 0) 512 state = B_TRANSLATE("discharging"); 513 else if ((fBatteryInfo.state & BATTERY_NOT_CHARGING) != 0) 514 state = B_TRANSLATE("not charging"); 515 516 if (state != NULL) { 517 snprintf(text + length, sizeof(text) - length, "\n%s", 518 state); 519 } 520 } else 521 strcpy(text, B_TRANSLATE("no battery")); 522 SetToolTip(text); 523 } 524 if (width < 8) { 525 // make sure we're not going away completely 526 width = 8; 527 } 528 529 if (width != Bounds().Width()) { 530 ResizeTo(width, Bounds().Height()); 531 532 // inform Deskbar that it needs to realign its replicants 533 BWindow* window = Window(); 534 if (window != NULL) { 535 BView* view = window->FindView("Status"); 536 if (view != NULL) { 537 BMessenger target((BHandler*)view); 538 BMessage realignReplicants('Algn'); 539 target.SendMessage(&realignReplicants); 540 } 541 } 542 } 543 } 544 545 if (force || wasCharging != (fBatteryInfo.state & BATTERY_CHARGING) 546 || (fShowTime && fTimeLeft != previousTimeLeft) 547 || (!fShowTime && fPercent != previousPercent)) { 548 Invalidate(); 549 } 550 551 // only do low battery notices based on the aggregate virtual battery, not single batteries 552 if (fBatteryID >= 0) 553 return; 554 555 if (fPercent > kLowBatteryPercentage && fTimeLeft > kLowBatteryTimeLeft) 556 fHasNotifiedLowBattery = false; 557 558 bool justTurnedLowBattery = (previousPercent > kLowBatteryPercentage 559 && fPercent <= kLowBatteryPercentage) 560 || (fTimeLeft <= kLowBatteryTimeLeft 561 && previousTimeLeft > kLowBatteryTimeLeft); 562 563 if ((fBatteryInfo.state & BATTERY_DISCHARGING) != 0 && notify && fHasBattery 564 && !fHasNotifiedLowBattery && justTurnedLowBattery) { 565 _NotifyLowBattery(); 566 fHasNotifiedLowBattery = true; 567 } 568 569 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0 && fPercent >= kFullBatteryPercentage 570 && previousPercent < kFullBatteryPercentage) { 571 system_beep("Battery charged"); 572 } 573 } 574 575 576 void 577 PowerStatusView::FromMessage(const BMessage* archive) 578 { 579 bool value; 580 if (archive->FindBool("show label", &value) == B_OK) 581 fShowLabel = value; 582 if (archive->FindBool("show icon", &value) == B_OK) 583 fShowStatusIcon = value; 584 if (archive->FindBool("show time", &value) == B_OK) 585 fShowTime = value; 586 587 //Incase we have a bad saving and none are showed.. 588 if (!fShowLabel && !fShowStatusIcon) 589 fShowLabel = true; 590 591 int32 intValue; 592 if (archive->FindInt32("battery id", &intValue) == B_OK) 593 fBatteryID = intValue; 594 } 595 596 597 status_t 598 PowerStatusView::ToMessage(BMessage* archive) const 599 { 600 status_t status = archive->AddBool("show label", fShowLabel); 601 if (status == B_OK) 602 status = archive->AddBool("show icon", fShowStatusIcon); 603 if (status == B_OK) 604 status = archive->AddBool("show time", fShowTime); 605 if (status == B_OK) 606 status = archive->AddInt32("battery id", fBatteryID); 607 608 return status; 609 } 610 611 612 void 613 PowerStatusView::_GetBatteryInfo(int batteryID, battery_info* batteryInfo) 614 { 615 if (batteryID >= 0) { 616 fDriverInterface->GetBatteryInfo(batteryID, batteryInfo); 617 } else { 618 bool first = true; 619 memset(batteryInfo, 0, sizeof(battery_info)); 620 621 for (int i = 0; i < fDriverInterface->GetBatteryCount(); i++) { 622 battery_info info; 623 fDriverInterface->GetBatteryInfo(i, &info); 624 if (info.full_capacity <= 0) 625 continue; 626 627 if (first) { 628 *batteryInfo = info; 629 first = false; 630 } else { 631 if ((batteryInfo->state & BATTERY_CRITICAL_STATE) == 0) { 632 // don't propagate CRITICAL_STATE to the aggregate battery. 633 // one battery charging means "the system is charging" but one battery having 634 // been removed does not mean "the system has no battery" 635 batteryInfo->state |= info.state; 636 } 637 batteryInfo->capacity += info.capacity; 638 batteryInfo->full_capacity += info.full_capacity; 639 batteryInfo->current_rate += info.current_rate; 640 } 641 } 642 643 // we can't rely on just adding the individual batteries' time_lefts together: 644 // not-in-use batteries show -1 time_left as they will last infinitely long with their 645 // current (zero) level of draw, despite them being in the queue to use after the current 646 // battery is out of energy. therefore to calculate an accurate time, we have to use the 647 // current total rate of (dis)charge compared to the total remaining capacity of all 648 // batteries. 649 if (batteryInfo->current_rate == 0) { 650 // some systems briefly return current_rate of 0 as the charger is plugged/unplugged 651 batteryInfo->time_left = 0; 652 } else if ((batteryInfo->state & BATTERY_CHARGING) != 0) { 653 batteryInfo->time_left = 3600 * (batteryInfo->full_capacity - batteryInfo->capacity) 654 / batteryInfo->current_rate; 655 } else { 656 batteryInfo->time_left = 3600 * batteryInfo->capacity / batteryInfo->current_rate; 657 } 658 } 659 } 660 661 662 void 663 PowerStatusView::_NotifyLowBattery() 664 { 665 BBitmap* bitmap = NULL; 666 BResources resources; 667 resources.SetToImage((void*)&instantiate_deskbar_item); 668 669 if (resources.InitCheck() == B_OK) { 670 size_t resourceSize = 0; 671 const void* resourceData = resources.LoadResource( 672 B_VECTOR_ICON_TYPE, fHasBattery 673 ? "battery_low" : "battery_critical", &resourceSize); 674 if (resourceData != NULL) { 675 BMemoryIO memoryIO(resourceData, resourceSize); 676 bitmap = BTranslationUtils::GetBitmap(&memoryIO); 677 } 678 } 679 680 BNotification notification( 681 fHasBattery ? B_INFORMATION_NOTIFICATION : B_ERROR_NOTIFICATION); 682 683 if (fHasBattery) { 684 system_beep("Battery low"); 685 notification.SetTitle(B_TRANSLATE("Battery low")); 686 notification.SetContent(B_TRANSLATE( 687 "The battery level is getting low, please plug in the device.")); 688 } else { 689 system_beep("Battery critical"); 690 notification.SetTitle(B_TRANSLATE("Battery critical")); 691 notification.SetContent(B_TRANSLATE( 692 "The battery level is critical, please plug in the device " 693 "immediately.")); 694 } 695 696 notification.SetIcon(bitmap); 697 notification.Send(); 698 delete bitmap; 699 } 700 701 702 // #pragma mark - Replicant view 703 704 705 PowerStatusReplicant::PowerStatusReplicant(BRect frame, int32 resizingMode, 706 bool inDeskbar) 707 : 708 PowerStatusView(NULL, frame, resizingMode, -1, inDeskbar), 709 fReplicated(false) 710 { 711 _Init(); 712 _LoadSettings(); 713 714 if (!inDeskbar) { 715 // we were obviously added to a standard window - let's add a dragger 716 frame.OffsetTo(B_ORIGIN); 717 frame.top = frame.bottom - 7; 718 frame.left = frame.right - 7; 719 BDragger* dragger = new BDragger(frame, this, 720 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 721 AddChild(dragger); 722 } else 723 Update(false,false); 724 } 725 726 727 PowerStatusReplicant::PowerStatusReplicant(BMessage* archive) 728 : 729 PowerStatusView(archive), 730 fReplicated(true) 731 { 732 _Init(); 733 _LoadSettings(); 734 } 735 736 737 PowerStatusReplicant::~PowerStatusReplicant() 738 { 739 if (fMessengerExist) 740 delete fExtWindowMessenger; 741 742 if (fExtendedWindow != NULL && fExtendedWindow->Lock()) { 743 fExtendedWindow->Quit(); 744 fExtendedWindow = NULL; 745 } 746 747 fDriverInterface->StopWatching(this); 748 fDriverInterface->Disconnect(); 749 fDriverInterface->ReleaseReference(); 750 751 _SaveSettings(); 752 } 753 754 755 PowerStatusReplicant* 756 PowerStatusReplicant::Instantiate(BMessage* archive) 757 { 758 if (!validate_instantiation(archive, "PowerStatusReplicant")) 759 return NULL; 760 761 return new PowerStatusReplicant(archive); 762 } 763 764 765 status_t 766 PowerStatusReplicant::Archive(BMessage* archive, bool deep) const 767 { 768 status_t status = PowerStatusView::Archive(archive, deep); 769 if (status == B_OK) 770 status = archive->AddString("add_on", kSignature); 771 if (status == B_OK) 772 status = archive->AddString("class", "PowerStatusReplicant"); 773 774 return status; 775 } 776 777 778 void 779 PowerStatusReplicant::MessageReceived(BMessage *message) 780 { 781 switch (message->what) { 782 case kMsgToggleLabel: 783 if (fShowStatusIcon) 784 fShowLabel = !fShowLabel; 785 else 786 fShowLabel = true; 787 788 Update(true); 789 break; 790 791 case kMsgToggleTime: 792 fShowTime = !fShowTime; 793 Update(true); 794 break; 795 796 case kMsgToggleStatusIcon: 797 if (fShowLabel) 798 fShowStatusIcon = !fShowStatusIcon; 799 else 800 fShowStatusIcon = true; 801 802 Update(true); 803 break; 804 805 case kMsgToggleExtInfo: 806 _OpenExtendedWindow(); 807 break; 808 809 case B_ABOUT_REQUESTED: 810 _AboutRequested(); 811 break; 812 813 case B_QUIT_REQUESTED: 814 _Quit(); 815 break; 816 817 default: 818 PowerStatusView::MessageReceived(message); 819 break; 820 } 821 } 822 823 824 void 825 PowerStatusReplicant::MouseDown(BPoint point) 826 { 827 BMessage* msg = Window()->CurrentMessage(); 828 int32 buttons = msg->GetInt32("buttons", 0); 829 if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) { 830 BMessenger messenger(this); 831 messenger.SendMessage(kMsgToggleExtInfo); 832 } else { 833 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 834 menu->SetFont(be_plain_font); 835 836 BMenuItem* item; 837 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show text label"), 838 new BMessage(kMsgToggleLabel))); 839 if (fShowLabel) 840 item->SetMarked(true); 841 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show status icon"), 842 new BMessage(kMsgToggleStatusIcon))); 843 if (fShowStatusIcon) 844 item->SetMarked(true); 845 menu->AddItem(new BMenuItem(!fShowTime ? B_TRANSLATE("Show time") : 846 B_TRANSLATE("Show percent"), new BMessage(kMsgToggleTime))); 847 848 menu->AddSeparatorItem(); 849 menu->AddItem(new BMenuItem(B_TRANSLATE("Battery info" B_UTF8_ELLIPSIS), 850 new BMessage(kMsgToggleExtInfo))); 851 852 menu->AddSeparatorItem(); 853 menu->AddItem(new BMenuItem(B_TRANSLATE("About" B_UTF8_ELLIPSIS), 854 new BMessage(B_ABOUT_REQUESTED))); 855 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 856 new BMessage(B_QUIT_REQUESTED))); 857 menu->SetTargetForItems(this); 858 859 ConvertToScreen(&point); 860 menu->Go(point, true, false, true); 861 } 862 } 863 864 865 void 866 PowerStatusReplicant::_AboutRequested() 867 { 868 BAboutWindow* window = new BAboutWindow( 869 B_TRANSLATE_SYSTEM_NAME("PowerStatus"), kSignature); 870 871 const char* authors[] = { 872 "Axel Dörfler", 873 "Alexander von Gluck", 874 "Clemens Zeidler", 875 NULL 876 }; 877 878 window->AddCopyright(2006, "Haiku, Inc."); 879 window->AddAuthors(authors); 880 881 window->Show(); 882 } 883 884 885 void 886 PowerStatusReplicant::_Init() 887 { 888 fDriverInterface = new ACPIDriverInterface; 889 if (fDriverInterface->Connect() != B_OK) { 890 delete fDriverInterface; 891 fDriverInterface = new APMDriverInterface; 892 if (fDriverInterface->Connect() != B_OK) { 893 fprintf(stderr, "No power interface found.\n"); 894 _Quit(); 895 } 896 } 897 898 fExtendedWindow = NULL; 899 fMessengerExist = false; 900 fExtWindowMessenger = NULL; 901 902 fDriverInterface->StartWatching(this); 903 } 904 905 906 void 907 PowerStatusReplicant::_Quit() 908 { 909 if (fInDeskbar) { 910 BDeskbar deskbar; 911 deskbar.RemoveItem(kDeskbarItemName); 912 } else if (fReplicated) { 913 BDragger *dragger = dynamic_cast<BDragger*>(ChildAt(0)); 914 if (dragger != NULL) { 915 BMessenger messenger(dragger); 916 messenger.SendMessage(new BMessage(B_TRASH_TARGET)); 917 } 918 } else 919 be_app->PostMessage(B_QUIT_REQUESTED); 920 } 921 922 923 status_t 924 PowerStatusReplicant::_GetSettings(BFile& file, int mode) 925 { 926 BPath path; 927 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, 928 (mode & O_ACCMODE) != O_RDONLY); 929 if (status != B_OK) 930 return status; 931 932 path.Append("PowerStatus settings"); 933 934 return file.SetTo(path.Path(), mode); 935 } 936 937 938 void 939 PowerStatusReplicant::_LoadSettings() 940 { 941 fShowLabel = false; 942 943 BFile file; 944 if (_GetSettings(file, B_READ_ONLY) != B_OK) 945 return; 946 947 BMessage settings; 948 if (settings.Unflatten(&file) < B_OK) 949 return; 950 951 FromMessage(&settings); 952 } 953 954 955 void 956 PowerStatusReplicant::_SaveSettings() 957 { 958 BFile file; 959 if (_GetSettings(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK) 960 return; 961 962 BMessage settings('pwst'); 963 ToMessage(&settings); 964 965 ssize_t size = 0; 966 settings.Flatten(&file, &size); 967 } 968 969 970 void 971 PowerStatusReplicant::_OpenExtendedWindow() 972 { 973 if (!fExtendedWindow) { 974 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 975 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 976 fExtendedWindow->Show(); 977 return; 978 } 979 980 BMessage msg(B_SET_PROPERTY); 981 msg.AddSpecifier("Hidden", int32(0)); 982 if (fExtWindowMessenger->SendMessage(&msg) == B_BAD_PORT_ID) { 983 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 984 if (fMessengerExist) 985 delete fExtWindowMessenger; 986 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 987 fMessengerExist = true; 988 fExtendedWindow->Show(); 989 } else 990 fExtendedWindow->Activate(); 991 992 } 993 994 995 // #pragma mark - 996 997 998 extern "C" _EXPORT BView* 999 instantiate_deskbar_item(float maxWidth, float maxHeight) 1000 { 1001 return new PowerStatusReplicant(BRect(0, 0, maxHeight - 1, maxHeight - 1), 1002 B_FOLLOW_NONE, true); 1003 } 1004