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 } 322 323 view->SetHighColor(0, 0, 0); 324 } 325 326 327 void 328 PowerStatusView::Draw(BRect updateRect) 329 { 330 DrawTo(this, Bounds()); 331 } 332 333 334 void 335 PowerStatusView::DrawTo(BView* view, BRect rect) 336 { 337 bool inside = rect.Width() >= 40.0f && rect.Height() >= 40.0f; 338 339 font_height fontHeight; 340 view->GetFontHeight(&fontHeight); 341 float baseLine = ceilf(fontHeight.ascent); 342 343 char text[64]; 344 _SetLabel(text, sizeof(text)); 345 346 float textHeight = ceilf(fontHeight.descent + fontHeight.ascent); 347 float textWidth = view->StringWidth(text); 348 bool showLabel = fShowLabel && text[0]; 349 350 BRect iconRect; 351 352 if (fShowStatusIcon) { 353 iconRect = rect; 354 if (showLabel && inside == false) 355 iconRect.right -= textWidth + 2; 356 357 _DrawBattery(view, iconRect); 358 } 359 360 if (showLabel) { 361 BPoint point(0, baseLine + rect.top); 362 363 if (iconRect.IsValid()) { 364 if (inside == true) { 365 point.x = rect.left + (iconRect.Width() - textWidth) / 2 + 366 iconRect.Width() / 20; 367 point.y += (iconRect.Height() - textHeight) / 2; 368 } else { 369 point.x = rect.left + iconRect.Width() + 2; 370 point.y += (iconRect.Height() - textHeight) / 2; 371 } 372 } else { 373 point.x = rect.left + (Bounds().Width() - textWidth) / 2; 374 point.y += (Bounds().Height() - textHeight) / 2; 375 } 376 377 view->SetDrawingMode(B_OP_OVER); 378 if (fInDeskbar == false || inside == true) { 379 view->SetHighUIColor(B_CONTROL_BACKGROUND_COLOR); 380 view->DrawString(text, BPoint(point.x + 1, point.y + 1)); 381 } 382 view->SetHighUIColor(B_CONTROL_TEXT_COLOR); 383 384 view->DrawString(text, point); 385 } 386 } 387 388 389 void 390 PowerStatusView::_SetLabel(char* buffer, size_t bufferLength) 391 { 392 if (bufferLength < 1) 393 return; 394 395 buffer[0] = '\0'; 396 397 if (!fShowLabel) 398 return; 399 400 const char* open = ""; 401 const char* close = ""; 402 if ((fBatteryInfo.state & BATTERY_DISCHARGING) == 0) { 403 // surround the percentage with () if the battery is not discharging 404 open = "("; 405 close = ")"; 406 } 407 408 if (!fShowTime && fPercent >= 0) { 409 BNumberFormat numberFormat; 410 BString data; 411 412 if (numberFormat.FormatPercent(data, fPercent) != B_OK) { 413 data.SetToFormat("%" B_PRId32 "%%", int32(fPercent * 100)); 414 } 415 416 snprintf(buffer, bufferLength, "%s%s%s", open, data.String(), close); 417 } else if (fShowTime && fTimeLeft >= 0) { 418 snprintf(buffer, bufferLength, "%s%" B_PRIdTIME ":%02" B_PRIdTIME "%s", 419 open, fTimeLeft / 3600, (fTimeLeft / 60) % 60, close); 420 } 421 } 422 423 424 void 425 PowerStatusView::Update(bool force, bool notify) 426 { 427 double previousPercent = fPercent; 428 time_t previousTimeLeft = fTimeLeft; 429 bool wasCharging = (fBatteryInfo.state & BATTERY_CHARGING); 430 bool hadBattery = fHasBattery; 431 _GetBatteryInfo(fBatteryID, &fBatteryInfo); 432 fHasBattery = fBatteryInfo.full_capacity > 0; 433 434 if (fBatteryInfo.full_capacity > 0 && fHasBattery) { 435 fPercent = (double)fBatteryInfo.capacity / fBatteryInfo.full_capacity; 436 fTimeLeft = fBatteryInfo.time_left; 437 } else { 438 fPercent = 0.0; 439 fTimeLeft = -1; 440 } 441 442 if (fHasBattery && (fPercent <= 0 || fPercent > 1.0)) { 443 // Just ignore this probe -- it obviously returned invalid values 444 fPercent = previousPercent; 445 fTimeLeft = previousTimeLeft; 446 fHasBattery = hadBattery; 447 return; 448 } 449 450 if (fInDeskbar) { 451 // make sure the tray icon is (just) large enough 452 float width = fShowStatusIcon ? Bounds().Height() : 0; 453 454 if (fShowLabel) { 455 char text[64]; 456 _SetLabel(text, sizeof(text)); 457 458 if (text[0]) 459 width += ceilf(StringWidth(text)) + 2; 460 } else { 461 char text[256]; 462 const char* open = ""; 463 const char* close = ""; 464 if ((fBatteryInfo.state & BATTERY_DISCHARGING) == 0) { 465 // surround the percentage with () if the battery is not discharging 466 open = "("; 467 close = ")"; 468 } 469 if (fHasBattery) { 470 BNumberFormat numberFormat; 471 BString data; 472 size_t length; 473 474 if (numberFormat.FormatPercent(data, fPercent) != B_OK) { 475 data.SetToFormat("%" B_PRId32 "%%", int32(fPercent * 100)); 476 } 477 478 length = snprintf(text, sizeof(text), "%s%s%s", open, data.String(), close); 479 480 if (fTimeLeft >= 0) { 481 length += snprintf(text + length, sizeof(text) - length, "\n%" B_PRIdTIME 482 ":%02" B_PRIdTIME, fTimeLeft / 3600, (fTimeLeft / 60) % 60); 483 } 484 485 const char* state = NULL; 486 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0) 487 state = B_TRANSLATE("charging"); 488 else if ((fBatteryInfo.state & BATTERY_DISCHARGING) != 0) 489 state = B_TRANSLATE("discharging"); 490 else if ((fBatteryInfo.state & BATTERY_NOT_CHARGING) != 0) 491 state = B_TRANSLATE("not charging"); 492 493 if (state != NULL) { 494 snprintf(text + length, sizeof(text) - length, "\n%s", 495 state); 496 } 497 } else 498 strcpy(text, B_TRANSLATE("no battery")); 499 SetToolTip(text); 500 } 501 if (width < 8) { 502 // make sure we're not going away completely 503 width = 8; 504 } 505 506 if (width != Bounds().Width()) { 507 ResizeTo(width, Bounds().Height()); 508 509 // inform Deskbar that it needs to realign its replicants 510 BWindow* window = Window(); 511 if (window != NULL) { 512 BView* view = window->FindView("Status"); 513 if (view != NULL) { 514 BMessenger target((BHandler*)view); 515 BMessage realignReplicants('Algn'); 516 target.SendMessage(&realignReplicants); 517 } 518 } 519 } 520 } 521 522 if (force || wasCharging != (fBatteryInfo.state & BATTERY_CHARGING) 523 || (fShowTime && fTimeLeft != previousTimeLeft) 524 || (!fShowTime && fPercent != previousPercent)) { 525 Invalidate(); 526 } 527 528 // only do low battery notices based on the aggregate virtual battery, not single batteries 529 if (fBatteryID >= 0) 530 return; 531 532 if (fPercent > kLowBatteryPercentage && fTimeLeft > kLowBatteryTimeLeft) 533 fHasNotifiedLowBattery = false; 534 535 bool justTurnedLowBattery = (previousPercent > kLowBatteryPercentage 536 && fPercent <= kLowBatteryPercentage) 537 || (fTimeLeft <= kLowBatteryTimeLeft 538 && previousTimeLeft > kLowBatteryTimeLeft); 539 540 if ((fBatteryInfo.state & BATTERY_DISCHARGING) != 0 && notify && fHasBattery 541 && !fHasNotifiedLowBattery && justTurnedLowBattery) { 542 _NotifyLowBattery(); 543 fHasNotifiedLowBattery = true; 544 } 545 546 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0 && fPercent >= kFullBatteryPercentage 547 && previousPercent < kFullBatteryPercentage) { 548 system_beep("Battery charged"); 549 } 550 } 551 552 553 void 554 PowerStatusView::FromMessage(const BMessage* archive) 555 { 556 bool value; 557 if (archive->FindBool("show label", &value) == B_OK) 558 fShowLabel = value; 559 if (archive->FindBool("show icon", &value) == B_OK) 560 fShowStatusIcon = value; 561 if (archive->FindBool("show time", &value) == B_OK) 562 fShowTime = value; 563 564 //Incase we have a bad saving and none are showed.. 565 if (!fShowLabel && !fShowStatusIcon) 566 fShowLabel = true; 567 568 int32 intValue; 569 if (archive->FindInt32("battery id", &intValue) == B_OK) 570 fBatteryID = intValue; 571 } 572 573 574 status_t 575 PowerStatusView::ToMessage(BMessage* archive) const 576 { 577 status_t status = archive->AddBool("show label", fShowLabel); 578 if (status == B_OK) 579 status = archive->AddBool("show icon", fShowStatusIcon); 580 if (status == B_OK) 581 status = archive->AddBool("show time", fShowTime); 582 if (status == B_OK) 583 status = archive->AddInt32("battery id", fBatteryID); 584 585 return status; 586 } 587 588 589 void 590 PowerStatusView::_GetBatteryInfo(int batteryID, battery_info* batteryInfo) 591 { 592 if (batteryID >= 0) { 593 fDriverInterface->GetBatteryInfo(batteryID, batteryInfo); 594 } else { 595 bool first = true; 596 memset(batteryInfo, 0, sizeof(battery_info)); 597 598 for (int i = 0; i < fDriverInterface->GetBatteryCount(); i++) { 599 battery_info info; 600 fDriverInterface->GetBatteryInfo(i, &info); 601 if (info.full_capacity <= 0) 602 continue; 603 604 if (first) { 605 *batteryInfo = info; 606 first = false; 607 } else { 608 batteryInfo->state |= info.state; 609 batteryInfo->capacity += info.capacity; 610 batteryInfo->full_capacity += info.full_capacity; 611 batteryInfo->current_rate += info.current_rate; 612 } 613 } 614 615 // we can't rely on just adding the individual batteries' time_lefts together: 616 // not-in-use batteries show -1 time_left as they will last infinitely long with their 617 // current (zero) level of draw, despite them being in the queue to use after the current 618 // battery is out of energy. therefore to calculate an accurate time, we have to use the 619 // current total rate of (dis)charge compared to the total remaining capacity of all 620 // batteries. 621 if (batteryInfo->current_rate == 0) { 622 // some systems briefly return current_rate of 0 as the charger is plugged/unplugged 623 batteryInfo->time_left = 0; 624 } else if ((batteryInfo->state & BATTERY_CHARGING) != 0) { 625 batteryInfo->time_left = 3600 * (batteryInfo->full_capacity - batteryInfo->capacity) 626 / batteryInfo->current_rate; 627 } else { 628 batteryInfo->time_left = 3600 * batteryInfo->capacity / batteryInfo->current_rate; 629 } 630 } 631 } 632 633 634 void 635 PowerStatusView::_NotifyLowBattery() 636 { 637 BBitmap* bitmap = NULL; 638 BResources resources; 639 resources.SetToImage((void*)&instantiate_deskbar_item); 640 641 if (resources.InitCheck() == B_OK) { 642 size_t resourceSize = 0; 643 const void* resourceData = resources.LoadResource( 644 B_VECTOR_ICON_TYPE, fHasBattery 645 ? "battery_low" : "battery_critical", &resourceSize); 646 if (resourceData != NULL) { 647 BMemoryIO memoryIO(resourceData, resourceSize); 648 bitmap = BTranslationUtils::GetBitmap(&memoryIO); 649 } 650 } 651 652 BNotification notification( 653 fHasBattery ? B_INFORMATION_NOTIFICATION : B_ERROR_NOTIFICATION); 654 655 if (fHasBattery) { 656 system_beep("Battery low"); 657 notification.SetTitle(B_TRANSLATE("Battery low")); 658 notification.SetContent(B_TRANSLATE( 659 "The battery level is getting low, please plug in the device.")); 660 } else { 661 system_beep("Battery critical"); 662 notification.SetTitle(B_TRANSLATE("Battery critical")); 663 notification.SetContent(B_TRANSLATE( 664 "The battery level is critical, please plug in the device " 665 "immediately.")); 666 } 667 668 notification.SetIcon(bitmap); 669 notification.Send(); 670 delete bitmap; 671 } 672 673 674 // #pragma mark - Replicant view 675 676 677 PowerStatusReplicant::PowerStatusReplicant(BRect frame, int32 resizingMode, 678 bool inDeskbar) 679 : 680 PowerStatusView(NULL, frame, resizingMode, -1, inDeskbar), 681 fReplicated(false) 682 { 683 _Init(); 684 _LoadSettings(); 685 686 if (!inDeskbar) { 687 // we were obviously added to a standard window - let's add a dragger 688 frame.OffsetTo(B_ORIGIN); 689 frame.top = frame.bottom - 7; 690 frame.left = frame.right - 7; 691 BDragger* dragger = new BDragger(frame, this, 692 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 693 AddChild(dragger); 694 } else 695 Update(false,false); 696 } 697 698 699 PowerStatusReplicant::PowerStatusReplicant(BMessage* archive) 700 : 701 PowerStatusView(archive), 702 fReplicated(true) 703 { 704 _Init(); 705 _LoadSettings(); 706 } 707 708 709 PowerStatusReplicant::~PowerStatusReplicant() 710 { 711 if (fMessengerExist) 712 delete fExtWindowMessenger; 713 714 if (fExtendedWindow != NULL && fExtendedWindow->Lock()) { 715 fExtendedWindow->Quit(); 716 fExtendedWindow = NULL; 717 } 718 719 fDriverInterface->StopWatching(this); 720 fDriverInterface->Disconnect(); 721 fDriverInterface->ReleaseReference(); 722 723 _SaveSettings(); 724 } 725 726 727 PowerStatusReplicant* 728 PowerStatusReplicant::Instantiate(BMessage* archive) 729 { 730 if (!validate_instantiation(archive, "PowerStatusReplicant")) 731 return NULL; 732 733 return new PowerStatusReplicant(archive); 734 } 735 736 737 status_t 738 PowerStatusReplicant::Archive(BMessage* archive, bool deep) const 739 { 740 status_t status = PowerStatusView::Archive(archive, deep); 741 if (status == B_OK) 742 status = archive->AddString("add_on", kSignature); 743 if (status == B_OK) 744 status = archive->AddString("class", "PowerStatusReplicant"); 745 746 return status; 747 } 748 749 750 void 751 PowerStatusReplicant::MessageReceived(BMessage *message) 752 { 753 switch (message->what) { 754 case kMsgToggleLabel: 755 if (fShowStatusIcon) 756 fShowLabel = !fShowLabel; 757 else 758 fShowLabel = true; 759 760 Update(true); 761 break; 762 763 case kMsgToggleTime: 764 fShowTime = !fShowTime; 765 Update(true); 766 break; 767 768 case kMsgToggleStatusIcon: 769 if (fShowLabel) 770 fShowStatusIcon = !fShowStatusIcon; 771 else 772 fShowStatusIcon = true; 773 774 Update(true); 775 break; 776 777 case kMsgToggleExtInfo: 778 _OpenExtendedWindow(); 779 break; 780 781 case B_ABOUT_REQUESTED: 782 _AboutRequested(); 783 break; 784 785 case B_QUIT_REQUESTED: 786 _Quit(); 787 break; 788 789 default: 790 PowerStatusView::MessageReceived(message); 791 break; 792 } 793 } 794 795 796 void 797 PowerStatusReplicant::MouseDown(BPoint point) 798 { 799 BMessage* msg = Window()->CurrentMessage(); 800 int32 buttons = msg->GetInt32("buttons", 0); 801 if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) { 802 BMessenger messenger(this); 803 messenger.SendMessage(kMsgToggleExtInfo); 804 } else { 805 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 806 menu->SetFont(be_plain_font); 807 808 BMenuItem* item; 809 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show text label"), 810 new BMessage(kMsgToggleLabel))); 811 if (fShowLabel) 812 item->SetMarked(true); 813 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show status icon"), 814 new BMessage(kMsgToggleStatusIcon))); 815 if (fShowStatusIcon) 816 item->SetMarked(true); 817 menu->AddItem(new BMenuItem(!fShowTime ? B_TRANSLATE("Show time") : 818 B_TRANSLATE("Show percent"), new BMessage(kMsgToggleTime))); 819 820 menu->AddSeparatorItem(); 821 menu->AddItem(new BMenuItem(B_TRANSLATE("Battery info" B_UTF8_ELLIPSIS), 822 new BMessage(kMsgToggleExtInfo))); 823 824 menu->AddSeparatorItem(); 825 menu->AddItem(new BMenuItem(B_TRANSLATE("About" B_UTF8_ELLIPSIS), 826 new BMessage(B_ABOUT_REQUESTED))); 827 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 828 new BMessage(B_QUIT_REQUESTED))); 829 menu->SetTargetForItems(this); 830 831 ConvertToScreen(&point); 832 menu->Go(point, true, false, true); 833 } 834 } 835 836 837 void 838 PowerStatusReplicant::_AboutRequested() 839 { 840 BAboutWindow* window = new BAboutWindow( 841 B_TRANSLATE_SYSTEM_NAME("PowerStatus"), kSignature); 842 843 const char* authors[] = { 844 "Axel Dörfler", 845 "Alexander von Gluck", 846 "Clemens Zeidler", 847 NULL 848 }; 849 850 window->AddCopyright(2006, "Haiku, Inc."); 851 window->AddAuthors(authors); 852 853 window->Show(); 854 } 855 856 857 void 858 PowerStatusReplicant::_Init() 859 { 860 fDriverInterface = new ACPIDriverInterface; 861 if (fDriverInterface->Connect() != B_OK) { 862 delete fDriverInterface; 863 fDriverInterface = new APMDriverInterface; 864 if (fDriverInterface->Connect() != B_OK) { 865 fprintf(stderr, "No power interface found.\n"); 866 _Quit(); 867 } 868 } 869 870 fExtendedWindow = NULL; 871 fMessengerExist = false; 872 fExtWindowMessenger = NULL; 873 874 fDriverInterface->StartWatching(this); 875 } 876 877 878 void 879 PowerStatusReplicant::_Quit() 880 { 881 if (fInDeskbar) { 882 BDeskbar deskbar; 883 deskbar.RemoveItem(kDeskbarItemName); 884 } else if (fReplicated) { 885 BDragger *dragger = dynamic_cast<BDragger*>(ChildAt(0)); 886 if (dragger != NULL) { 887 BMessenger messenger(dragger); 888 messenger.SendMessage(new BMessage(B_TRASH_TARGET)); 889 } 890 } else 891 be_app->PostMessage(B_QUIT_REQUESTED); 892 } 893 894 895 status_t 896 PowerStatusReplicant::_GetSettings(BFile& file, int mode) 897 { 898 BPath path; 899 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, 900 (mode & O_ACCMODE) != O_RDONLY); 901 if (status != B_OK) 902 return status; 903 904 path.Append("PowerStatus settings"); 905 906 return file.SetTo(path.Path(), mode); 907 } 908 909 910 void 911 PowerStatusReplicant::_LoadSettings() 912 { 913 fShowLabel = false; 914 915 BFile file; 916 if (_GetSettings(file, B_READ_ONLY) != B_OK) 917 return; 918 919 BMessage settings; 920 if (settings.Unflatten(&file) < B_OK) 921 return; 922 923 FromMessage(&settings); 924 } 925 926 927 void 928 PowerStatusReplicant::_SaveSettings() 929 { 930 BFile file; 931 if (_GetSettings(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK) 932 return; 933 934 BMessage settings('pwst'); 935 ToMessage(&settings); 936 937 ssize_t size = 0; 938 settings.Flatten(&file, &size); 939 } 940 941 942 void 943 PowerStatusReplicant::_OpenExtendedWindow() 944 { 945 if (!fExtendedWindow) { 946 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 947 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 948 fExtendedWindow->Show(); 949 return; 950 } 951 952 BMessage msg(B_SET_PROPERTY); 953 msg.AddSpecifier("Hidden", int32(0)); 954 if (fExtWindowMessenger->SendMessage(&msg) == B_BAD_PORT_ID) { 955 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 956 if (fMessengerExist) 957 delete fExtWindowMessenger; 958 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 959 fMessengerExist = true; 960 fExtendedWindow->Show(); 961 } else 962 fExtendedWindow->Activate(); 963 964 } 965 966 967 // #pragma mark - 968 969 970 extern "C" _EXPORT BView* 971 instantiate_deskbar_item(float maxWidth, float maxHeight) 972 { 973 return new PowerStatusReplicant(BRect(0, 0, maxHeight - 1, maxHeight - 1), 974 B_FOLLOW_NONE, true); 975 } 976