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 fOnline = true; 122 fTimeLeft = 0; 123 124 fHasNotifiedLowBattery = false; 125 126 add_system_beep_event("Battery critical"); 127 add_system_beep_event("Battery low"); 128 add_system_beep_event("Battery charged"); 129 } 130 131 132 void 133 PowerStatusView::AttachedToWindow() 134 { 135 BView::AttachedToWindow(); 136 137 SetViewColor(B_TRANSPARENT_COLOR); 138 139 if (ViewUIColor() != B_NO_COLOR) 140 SetLowUIColor(ViewUIColor()); 141 else 142 SetLowColor(ViewColor()); 143 144 Update(); 145 } 146 147 148 void 149 PowerStatusView::DetachedFromWindow() 150 { 151 } 152 153 154 void 155 PowerStatusView::MessageReceived(BMessage *message) 156 { 157 switch (message->what) { 158 case kMsgUpdate: 159 Update(); 160 break; 161 162 default: 163 BView::MessageReceived(message); 164 break; 165 } 166 } 167 168 169 void 170 PowerStatusView::_DrawBattery(BView* view, BRect rect) 171 { 172 BRect lightningRect = 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 184 if (view->LowColor().Brightness() > 100) 185 view->SetHighColor(0, 0, 0); 186 else 187 view->SetHighColor(128, 128, 128); 188 189 float gap = 1; 190 if (rect.Height() > 8) { 191 gap = ceilf((rect.left - left) / 2); 192 193 // left 194 view->FillRect(BRect(rect.left, rect.top, rect.left + gap - 1, 195 rect.bottom)); 196 // right 197 view->FillRect(BRect(rect.right - gap + 1, rect.top, rect.right, 198 rect.bottom)); 199 // top 200 view->FillRect(BRect(rect.left + gap, rect.top, rect.right - gap, 201 rect.top + gap - 1)); 202 // bottom 203 view->FillRect(BRect(rect.left + gap, rect.bottom + 1 - gap, 204 rect.right - gap, rect.bottom)); 205 } else 206 view->StrokeRect(rect); 207 208 view->FillRect(BRect(left, floorf(rect.top + rect.Height() / 4) + 1, 209 rect.left - 1, floorf(rect.bottom - rect.Height() / 4))); 210 211 double percent = fPercent; 212 if (percent > 1.0) 213 percent = 1.0; 214 else if (percent < 0.0 || !fHasBattery) 215 percent = 0.0; 216 217 rect.InsetBy(gap, gap); 218 219 if (fHasBattery) { 220 // draw unfilled area 221 rgb_color unfilledColor = make_color(0x4c, 0x4c, 0x4c); 222 if (view->LowColor().Brightness() < 128) { 223 unfilledColor.red = 256 - unfilledColor.red; 224 unfilledColor.green = 256 - unfilledColor.green; 225 unfilledColor.blue = 256 - unfilledColor.blue; 226 } 227 228 BRect unfilled = rect; 229 if (percent > 0.0) 230 unfilled.left += unfilled.Width() * percent; 231 232 view->SetHighColor(unfilledColor); 233 view->FillRect(unfilled); 234 235 if (percent > 0.0) { 236 // draw filled area 237 rgb_color fillColor; 238 if (percent <= kLowBatteryPercentage) 239 fillColor.set_to(180, 0, 0); 240 else if (percent <= kNoteBatteryPercentage) 241 fillColor.set_to(200, 140, 0); 242 else 243 fillColor.set_to(20, 180, 0); 244 245 BRect fill = rect; 246 fill.right = fill.left + fill.Width() * percent; 247 248 // draw bevel 249 rgb_color bevelLightColor = tint_color(fillColor, 0.2); 250 rgb_color bevelShadowColor = tint_color(fillColor, 1.08); 251 252 view->BeginLineArray(4); 253 view->AddLine(BPoint(fill.left, fill.bottom), 254 BPoint(fill.left, fill.top), bevelLightColor); 255 view->AddLine(BPoint(fill.left, fill.top), 256 BPoint(fill.right, fill.top), bevelLightColor); 257 view->AddLine(BPoint(fill.right, fill.top), 258 BPoint(fill.right, fill.bottom), bevelShadowColor); 259 view->AddLine(BPoint(fill.left, fill.bottom), 260 BPoint(fill.right, fill.bottom), bevelShadowColor); 261 view->EndLineArray(); 262 263 fill.InsetBy(1, 1); 264 265 // draw gradient 266 float topTint = 0.49; 267 float middleTint1 = 0.62; 268 float middleTint2 = 0.76; 269 float bottomTint = 0.90; 270 271 BGradientLinear gradient; 272 gradient.AddColor(tint_color(fillColor, topTint), 0); 273 gradient.AddColor(tint_color(fillColor, middleTint1), 132); 274 gradient.AddColor(tint_color(fillColor, middleTint2), 136); 275 gradient.AddColor(tint_color(fillColor, bottomTint), 255); 276 gradient.SetStart(fill.LeftTop()); 277 gradient.SetEnd(fill.LeftBottom()); 278 279 view->FillRect(fill, gradient); 280 } 281 } 282 283 if (fOnline) { 284 // When charging, draw a lightning symbol over the battery. 285 view->SetHighColor(255, 255, 0, 180); 286 view->SetDrawingMode(B_OP_ALPHA); 287 288 static const BPoint points[] = { 289 BPoint(3, 14), 290 BPoint(10, 6), 291 BPoint(10, 8), 292 BPoint(17, 3), 293 BPoint(9, 12), 294 BPoint(9, 10) 295 }; 296 view->FillPolygon(points, 6, lightningRect); 297 298 view->SetDrawingMode(B_OP_OVER); 299 } 300 301 view->SetHighColor(0, 0, 0); 302 } 303 304 305 void 306 PowerStatusView::Draw(BRect updateRect) 307 { 308 DrawTo(this, Bounds()); 309 } 310 311 312 void 313 PowerStatusView::DrawTo(BView* view, BRect rect) 314 { 315 bool inside = rect.Width() >= 40.0f && rect.Height() >= 40.0f; 316 317 font_height fontHeight; 318 view->GetFontHeight(&fontHeight); 319 float baseLine = ceilf(fontHeight.ascent); 320 321 char text[64]; 322 _SetLabel(text, sizeof(text)); 323 324 float textHeight = ceilf(fontHeight.descent + fontHeight.ascent); 325 float textWidth = view->StringWidth(text); 326 bool showLabel = fShowLabel && text[0]; 327 328 BRect iconRect; 329 330 if (fShowStatusIcon) { 331 iconRect = rect; 332 if (showLabel && inside == false) 333 iconRect.right -= textWidth + 2; 334 335 _DrawBattery(view, iconRect); 336 } 337 338 if (showLabel) { 339 BPoint point(0, baseLine + rect.top); 340 341 if (iconRect.IsValid()) { 342 if (inside == true) { 343 point.x = rect.left + (iconRect.Width() - textWidth) / 2 + 344 iconRect.Width() / 20; 345 point.y += (iconRect.Height() - textHeight) / 2; 346 } else { 347 point.x = rect.left + iconRect.Width() + 2; 348 point.y += (iconRect.Height() - textHeight) / 2; 349 } 350 } else { 351 point.x = rect.left + (Bounds().Width() - textWidth) / 2; 352 point.y += (Bounds().Height() - textHeight) / 2; 353 } 354 355 view->SetDrawingMode(B_OP_OVER); 356 if (fInDeskbar == false || inside == true) { 357 view->SetHighUIColor(B_CONTROL_BACKGROUND_COLOR); 358 view->DrawString(text, BPoint(point.x + 1, point.y + 1)); 359 } 360 view->SetHighUIColor(B_CONTROL_TEXT_COLOR); 361 362 view->DrawString(text, point); 363 } 364 } 365 366 367 void 368 PowerStatusView::_SetLabel(char* buffer, size_t bufferLength) 369 { 370 if (bufferLength < 1) 371 return; 372 373 buffer[0] = '\0'; 374 375 if (!fShowLabel) 376 return; 377 378 const char* open = ""; 379 const char* close = ""; 380 if (fOnline) { 381 open = "("; 382 close = ")"; 383 } 384 385 if (!fShowTime && fPercent >= 0) { 386 BNumberFormat numberFormat; 387 BString data; 388 389 if (numberFormat.FormatPercent(data, fPercent) != B_OK) { 390 data.SetToFormat("%" B_PRId32 "%%", int32(fPercent * 100)); 391 } 392 393 snprintf(buffer, bufferLength, "%s%s%s", open, data.String(), close); 394 } else if (fShowTime && fTimeLeft >= 0) { 395 snprintf(buffer, bufferLength, "%s%" B_PRIdTIME ":%02" B_PRIdTIME "%s", 396 open, fTimeLeft / 3600, (fTimeLeft / 60) % 60, close); 397 } 398 } 399 400 401 void 402 PowerStatusView::Update(bool force, bool notify) 403 { 404 double previousPercent = fPercent; 405 time_t previousTimeLeft = fTimeLeft; 406 bool wasOnline = fOnline; 407 bool hadBattery = fHasBattery; 408 _GetBatteryInfo(fBatteryID, &fBatteryInfo); 409 fHasBattery = fBatteryInfo.full_capacity > 0; 410 411 if (fBatteryInfo.full_capacity > 0 && fHasBattery) { 412 fPercent = (double)fBatteryInfo.capacity / fBatteryInfo.full_capacity; 413 fOnline = (fBatteryInfo.state & BATTERY_DISCHARGING) == 0; 414 fTimeLeft = fBatteryInfo.time_left; 415 } else { 416 fPercent = 0.0; 417 fOnline = false; 418 fTimeLeft = -1; 419 } 420 421 if (fHasBattery && (fPercent <= 0 || fPercent > 1.0)) { 422 // Just ignore this probe -- it obviously returned invalid values 423 fPercent = previousPercent; 424 fTimeLeft = previousTimeLeft; 425 fOnline = wasOnline; 426 fHasBattery = hadBattery; 427 return; 428 } 429 430 if (fInDeskbar) { 431 // make sure the tray icon is (just) large enough 432 float width = fShowStatusIcon ? Bounds().Height() : 0; 433 434 if (fShowLabel) { 435 char text[64]; 436 _SetLabel(text, sizeof(text)); 437 438 if (text[0]) 439 width += ceilf(StringWidth(text)) + 2; 440 } else { 441 char text[256]; 442 const char* open = ""; 443 const char* close = ""; 444 if (fOnline) { 445 open = "("; 446 close = ")"; 447 } 448 if (fHasBattery) { 449 BNumberFormat numberFormat; 450 BString data; 451 size_t length; 452 453 if (numberFormat.FormatPercent(data, fPercent) != B_OK) { 454 data.SetToFormat("%" B_PRId32 "%%", int32(fPercent * 100)); 455 } 456 457 length = snprintf(text, sizeof(text), "%s%s%s", open, data.String(), close); 458 459 if (fTimeLeft >= 0) { 460 length += snprintf(text + length, sizeof(text) - length, "\n%" B_PRIdTIME 461 ":%02" B_PRIdTIME, fTimeLeft / 3600, (fTimeLeft / 60) % 60); 462 } 463 464 const char* state = NULL; 465 if ((fBatteryInfo.state & BATTERY_CHARGING) != 0) 466 state = B_TRANSLATE("charging"); 467 else if ((fBatteryInfo.state & BATTERY_DISCHARGING) != 0) 468 state = B_TRANSLATE("discharging"); 469 470 if (state != NULL) { 471 snprintf(text + length, sizeof(text) - length, "\n%s", 472 state); 473 } 474 } else 475 strcpy(text, B_TRANSLATE("no battery")); 476 SetToolTip(text); 477 } 478 if (width < 8) { 479 // make sure we're not going away completely 480 width = 8; 481 } 482 483 if (width != Bounds().Width()) { 484 ResizeTo(width, Bounds().Height()); 485 486 // inform Deskbar that it needs to realign its replicants 487 BWindow* window = Window(); 488 if (window != NULL) { 489 BView* view = window->FindView("Status"); 490 if (view != NULL) { 491 BMessenger target((BHandler*)view); 492 BMessage realignReplicants('Algn'); 493 target.SendMessage(&realignReplicants); 494 } 495 } 496 } 497 } 498 499 if (force || wasOnline != fOnline 500 || (fShowTime && fTimeLeft != previousTimeLeft) 501 || (!fShowTime && fPercent != previousPercent)) { 502 Invalidate(); 503 } 504 505 if (fPercent > kLowBatteryPercentage && fTimeLeft > kLowBatteryTimeLeft) 506 fHasNotifiedLowBattery = false; 507 508 bool justTurnedLowBattery = (previousPercent > kLowBatteryPercentage 509 && fPercent <= kLowBatteryPercentage) 510 || (fTimeLeft <= kLowBatteryTimeLeft 511 && previousTimeLeft > kLowBatteryTimeLeft); 512 513 if (!fOnline && notify && fHasBattery 514 && !fHasNotifiedLowBattery && justTurnedLowBattery) { 515 _NotifyLowBattery(); 516 fHasNotifiedLowBattery = true; 517 } 518 519 if (fOnline && fPercent >= kFullBatteryPercentage 520 && previousPercent < kFullBatteryPercentage) { 521 system_beep("Battery charged"); 522 } 523 } 524 525 526 void 527 PowerStatusView::FromMessage(const BMessage* archive) 528 { 529 bool value; 530 if (archive->FindBool("show label", &value) == B_OK) 531 fShowLabel = value; 532 if (archive->FindBool("show icon", &value) == B_OK) 533 fShowStatusIcon = value; 534 if (archive->FindBool("show time", &value) == B_OK) 535 fShowTime = value; 536 537 //Incase we have a bad saving and none are showed.. 538 if (!fShowLabel && !fShowStatusIcon) 539 fShowLabel = true; 540 541 int32 intValue; 542 if (archive->FindInt32("battery id", &intValue) == B_OK) 543 fBatteryID = intValue; 544 } 545 546 547 status_t 548 PowerStatusView::ToMessage(BMessage* archive) const 549 { 550 status_t status = archive->AddBool("show label", fShowLabel); 551 if (status == B_OK) 552 status = archive->AddBool("show icon", fShowStatusIcon); 553 if (status == B_OK) 554 status = archive->AddBool("show time", fShowTime); 555 if (status == B_OK) 556 status = archive->AddInt32("battery id", fBatteryID); 557 558 return status; 559 } 560 561 562 void 563 PowerStatusView::_GetBatteryInfo(int batteryID, battery_info* batteryInfo) 564 { 565 if (batteryID >= 0) { 566 fDriverInterface->GetBatteryInfo(batteryID, batteryInfo); 567 } else { 568 bool first = true; 569 memset(batteryInfo, 0, sizeof(battery_info)); 570 571 for (int i = 0; i < fDriverInterface->GetBatteryCount(); i++) { 572 battery_info info; 573 fDriverInterface->GetBatteryInfo(i, &info); 574 575 if (info.full_capacity <= 0) 576 continue; 577 578 if (first) { 579 *batteryInfo = info; 580 first = false; 581 } else { 582 batteryInfo->state |= info.state; 583 batteryInfo->capacity += info.capacity; 584 batteryInfo->full_capacity += info.full_capacity; 585 batteryInfo->time_left += info.time_left; 586 } 587 } 588 } 589 } 590 591 592 void 593 PowerStatusView::_NotifyLowBattery() 594 { 595 BBitmap* bitmap = NULL; 596 BResources resources; 597 resources.SetToImage((void*)&instantiate_deskbar_item); 598 599 if (resources.InitCheck() == B_OK) { 600 size_t resourceSize = 0; 601 const void* resourceData = resources.LoadResource( 602 B_VECTOR_ICON_TYPE, fHasBattery 603 ? "battery_low" : "battery_critical", &resourceSize); 604 if (resourceData != NULL) { 605 BMemoryIO memoryIO(resourceData, resourceSize); 606 bitmap = BTranslationUtils::GetBitmap(&memoryIO); 607 } 608 } 609 610 BNotification notification( 611 fHasBattery ? B_INFORMATION_NOTIFICATION : B_ERROR_NOTIFICATION); 612 613 if (fHasBattery) { 614 system_beep("Battery low"); 615 notification.SetTitle(B_TRANSLATE("Battery low")); 616 notification.SetContent(B_TRANSLATE( 617 "The battery level is getting low, please plug in the device.")); 618 } else { 619 system_beep("Battery critical"); 620 notification.SetTitle(B_TRANSLATE("Battery critical")); 621 notification.SetContent(B_TRANSLATE( 622 "The battery level is critical, please plug in the device " 623 "immediately.")); 624 } 625 626 notification.SetIcon(bitmap); 627 notification.Send(); 628 delete bitmap; 629 } 630 631 632 // #pragma mark - Replicant view 633 634 635 PowerStatusReplicant::PowerStatusReplicant(BRect frame, int32 resizingMode, 636 bool inDeskbar) 637 : 638 PowerStatusView(NULL, frame, resizingMode, -1, inDeskbar), 639 fReplicated(false) 640 { 641 _Init(); 642 _LoadSettings(); 643 644 if (!inDeskbar) { 645 // we were obviously added to a standard window - let's add a dragger 646 frame.OffsetTo(B_ORIGIN); 647 frame.top = frame.bottom - 7; 648 frame.left = frame.right - 7; 649 BDragger* dragger = new BDragger(frame, this, 650 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 651 AddChild(dragger); 652 } else 653 Update(false,false); 654 } 655 656 657 PowerStatusReplicant::PowerStatusReplicant(BMessage* archive) 658 : 659 PowerStatusView(archive), 660 fReplicated(true) 661 { 662 _Init(); 663 _LoadSettings(); 664 } 665 666 667 PowerStatusReplicant::~PowerStatusReplicant() 668 { 669 if (fMessengerExist) 670 delete fExtWindowMessenger; 671 672 if (fExtendedWindow != NULL && fExtendedWindow->Lock()) { 673 fExtendedWindow->Quit(); 674 fExtendedWindow = NULL; 675 } 676 677 fDriverInterface->StopWatching(this); 678 fDriverInterface->Disconnect(); 679 fDriverInterface->ReleaseReference(); 680 681 _SaveSettings(); 682 } 683 684 685 PowerStatusReplicant* 686 PowerStatusReplicant::Instantiate(BMessage* archive) 687 { 688 if (!validate_instantiation(archive, "PowerStatusReplicant")) 689 return NULL; 690 691 return new PowerStatusReplicant(archive); 692 } 693 694 695 status_t 696 PowerStatusReplicant::Archive(BMessage* archive, bool deep) const 697 { 698 status_t status = PowerStatusView::Archive(archive, deep); 699 if (status == B_OK) 700 status = archive->AddString("add_on", kSignature); 701 if (status == B_OK) 702 status = archive->AddString("class", "PowerStatusReplicant"); 703 704 return status; 705 } 706 707 708 void 709 PowerStatusReplicant::MessageReceived(BMessage *message) 710 { 711 switch (message->what) { 712 case kMsgToggleLabel: 713 if (fShowStatusIcon) 714 fShowLabel = !fShowLabel; 715 else 716 fShowLabel = true; 717 718 Update(true); 719 break; 720 721 case kMsgToggleTime: 722 fShowTime = !fShowTime; 723 Update(true); 724 break; 725 726 case kMsgToggleStatusIcon: 727 if (fShowLabel) 728 fShowStatusIcon = !fShowStatusIcon; 729 else 730 fShowStatusIcon = true; 731 732 Update(true); 733 break; 734 735 case kMsgToggleExtInfo: 736 _OpenExtendedWindow(); 737 break; 738 739 case B_ABOUT_REQUESTED: 740 _AboutRequested(); 741 break; 742 743 case B_QUIT_REQUESTED: 744 _Quit(); 745 break; 746 747 default: 748 PowerStatusView::MessageReceived(message); 749 break; 750 } 751 } 752 753 754 void 755 PowerStatusReplicant::MouseDown(BPoint point) 756 { 757 BMessage* msg = Window()->CurrentMessage(); 758 int32 buttons = msg->GetInt32("buttons", 0); 759 if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) { 760 BMessenger messenger(this); 761 messenger.SendMessage(kMsgToggleExtInfo); 762 } else { 763 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 764 menu->SetFont(be_plain_font); 765 766 BMenuItem* item; 767 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show text label"), 768 new BMessage(kMsgToggleLabel))); 769 if (fShowLabel) 770 item->SetMarked(true); 771 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show status icon"), 772 new BMessage(kMsgToggleStatusIcon))); 773 if (fShowStatusIcon) 774 item->SetMarked(true); 775 menu->AddItem(new BMenuItem(!fShowTime ? B_TRANSLATE("Show time") : 776 B_TRANSLATE("Show percent"), new BMessage(kMsgToggleTime))); 777 778 menu->AddSeparatorItem(); 779 menu->AddItem(new BMenuItem(B_TRANSLATE("Battery info" B_UTF8_ELLIPSIS), 780 new BMessage(kMsgToggleExtInfo))); 781 782 menu->AddSeparatorItem(); 783 menu->AddItem(new BMenuItem(B_TRANSLATE("About" B_UTF8_ELLIPSIS), 784 new BMessage(B_ABOUT_REQUESTED))); 785 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 786 new BMessage(B_QUIT_REQUESTED))); 787 menu->SetTargetForItems(this); 788 789 ConvertToScreen(&point); 790 menu->Go(point, true, false, true); 791 } 792 } 793 794 795 void 796 PowerStatusReplicant::_AboutRequested() 797 { 798 BAboutWindow* window = new BAboutWindow( 799 B_TRANSLATE_SYSTEM_NAME("PowerStatus"), kSignature); 800 801 const char* authors[] = { 802 "Axel Dörfler", 803 "Alexander von Gluck", 804 "Clemens Zeidler", 805 NULL 806 }; 807 808 window->AddCopyright(2006, "Haiku, Inc."); 809 window->AddAuthors(authors); 810 811 window->Show(); 812 } 813 814 815 void 816 PowerStatusReplicant::_Init() 817 { 818 fDriverInterface = new ACPIDriverInterface; 819 if (fDriverInterface->Connect() != B_OK) { 820 delete fDriverInterface; 821 fDriverInterface = new APMDriverInterface; 822 if (fDriverInterface->Connect() != B_OK) { 823 fprintf(stderr, "No power interface found.\n"); 824 _Quit(); 825 } 826 } 827 828 fExtendedWindow = NULL; 829 fMessengerExist = false; 830 fExtWindowMessenger = NULL; 831 832 fDriverInterface->StartWatching(this); 833 } 834 835 836 void 837 PowerStatusReplicant::_Quit() 838 { 839 if (fInDeskbar) { 840 BDeskbar deskbar; 841 deskbar.RemoveItem(kDeskbarItemName); 842 } else if (fReplicated) { 843 BDragger *dragger = dynamic_cast<BDragger*>(ChildAt(0)); 844 if (dragger != NULL) { 845 BMessenger messenger(dragger); 846 messenger.SendMessage(new BMessage(B_TRASH_TARGET)); 847 } 848 } else 849 be_app->PostMessage(B_QUIT_REQUESTED); 850 } 851 852 853 status_t 854 PowerStatusReplicant::_GetSettings(BFile& file, int mode) 855 { 856 BPath path; 857 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, 858 (mode & O_ACCMODE) != O_RDONLY); 859 if (status != B_OK) 860 return status; 861 862 path.Append("PowerStatus settings"); 863 864 return file.SetTo(path.Path(), mode); 865 } 866 867 868 void 869 PowerStatusReplicant::_LoadSettings() 870 { 871 fShowLabel = false; 872 873 BFile file; 874 if (_GetSettings(file, B_READ_ONLY) != B_OK) 875 return; 876 877 BMessage settings; 878 if (settings.Unflatten(&file) < B_OK) 879 return; 880 881 FromMessage(&settings); 882 } 883 884 885 void 886 PowerStatusReplicant::_SaveSettings() 887 { 888 BFile file; 889 if (_GetSettings(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK) 890 return; 891 892 BMessage settings('pwst'); 893 ToMessage(&settings); 894 895 ssize_t size = 0; 896 settings.Flatten(&file, &size); 897 } 898 899 900 void 901 PowerStatusReplicant::_OpenExtendedWindow() 902 { 903 if (!fExtendedWindow) { 904 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 905 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 906 fExtendedWindow->Show(); 907 return; 908 } 909 910 BMessage msg(B_SET_PROPERTY); 911 msg.AddSpecifier("Hidden", int32(0)); 912 if (fExtWindowMessenger->SendMessage(&msg) == B_BAD_PORT_ID) { 913 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 914 if (fMessengerExist) 915 delete fExtWindowMessenger; 916 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 917 fMessengerExist = true; 918 fExtendedWindow->Show(); 919 } else 920 fExtendedWindow->Activate(); 921 922 } 923 924 925 // #pragma mark - 926 927 928 extern "C" _EXPORT BView* 929 instantiate_deskbar_item(float maxWidth, float maxHeight) 930 { 931 return new PowerStatusReplicant(BRect(0, 0, maxHeight - 1, maxHeight - 1), 932 B_FOLLOW_NONE, true); 933 } 934