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().IsLight()) 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().IsDark()) { 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 else if ((fBatteryInfo.state & BATTERY_NOT_CHARGING) != 0) 470 state = B_TRANSLATE("not charging"); 471 472 if (state != NULL) { 473 snprintf(text + length, sizeof(text) - length, "\n%s", 474 state); 475 } 476 } else 477 strcpy(text, B_TRANSLATE("no battery")); 478 SetToolTip(text); 479 } 480 if (width < 8) { 481 // make sure we're not going away completely 482 width = 8; 483 } 484 485 if (width != Bounds().Width()) { 486 ResizeTo(width, Bounds().Height()); 487 488 // inform Deskbar that it needs to realign its replicants 489 BWindow* window = Window(); 490 if (window != NULL) { 491 BView* view = window->FindView("Status"); 492 if (view != NULL) { 493 BMessenger target((BHandler*)view); 494 BMessage realignReplicants('Algn'); 495 target.SendMessage(&realignReplicants); 496 } 497 } 498 } 499 } 500 501 if (force || wasOnline != fOnline 502 || (fShowTime && fTimeLeft != previousTimeLeft) 503 || (!fShowTime && fPercent != previousPercent)) { 504 Invalidate(); 505 } 506 507 if (fPercent > kLowBatteryPercentage && fTimeLeft > kLowBatteryTimeLeft) 508 fHasNotifiedLowBattery = false; 509 510 bool justTurnedLowBattery = (previousPercent > kLowBatteryPercentage 511 && fPercent <= kLowBatteryPercentage) 512 || (fTimeLeft <= kLowBatteryTimeLeft 513 && previousTimeLeft > kLowBatteryTimeLeft); 514 515 if (!fOnline && notify && fHasBattery 516 && !fHasNotifiedLowBattery && justTurnedLowBattery) { 517 _NotifyLowBattery(); 518 fHasNotifiedLowBattery = true; 519 } 520 521 if (fOnline && fPercent >= kFullBatteryPercentage 522 && previousPercent < kFullBatteryPercentage) { 523 system_beep("Battery charged"); 524 } 525 } 526 527 528 void 529 PowerStatusView::FromMessage(const BMessage* archive) 530 { 531 bool value; 532 if (archive->FindBool("show label", &value) == B_OK) 533 fShowLabel = value; 534 if (archive->FindBool("show icon", &value) == B_OK) 535 fShowStatusIcon = value; 536 if (archive->FindBool("show time", &value) == B_OK) 537 fShowTime = value; 538 539 //Incase we have a bad saving and none are showed.. 540 if (!fShowLabel && !fShowStatusIcon) 541 fShowLabel = true; 542 543 int32 intValue; 544 if (archive->FindInt32("battery id", &intValue) == B_OK) 545 fBatteryID = intValue; 546 } 547 548 549 status_t 550 PowerStatusView::ToMessage(BMessage* archive) const 551 { 552 status_t status = archive->AddBool("show label", fShowLabel); 553 if (status == B_OK) 554 status = archive->AddBool("show icon", fShowStatusIcon); 555 if (status == B_OK) 556 status = archive->AddBool("show time", fShowTime); 557 if (status == B_OK) 558 status = archive->AddInt32("battery id", fBatteryID); 559 560 return status; 561 } 562 563 564 void 565 PowerStatusView::_GetBatteryInfo(int batteryID, battery_info* batteryInfo) 566 { 567 if (batteryID >= 0) { 568 fDriverInterface->GetBatteryInfo(batteryID, batteryInfo); 569 } else { 570 bool first = true; 571 memset(batteryInfo, 0, sizeof(battery_info)); 572 573 for (int i = 0; i < fDriverInterface->GetBatteryCount(); i++) { 574 battery_info info; 575 fDriverInterface->GetBatteryInfo(i, &info); 576 577 if (info.full_capacity <= 0) 578 continue; 579 580 if (first) { 581 *batteryInfo = info; 582 first = false; 583 } else { 584 batteryInfo->state |= info.state; 585 batteryInfo->capacity += info.capacity; 586 batteryInfo->full_capacity += info.full_capacity; 587 batteryInfo->time_left += info.time_left; 588 } 589 } 590 } 591 } 592 593 594 void 595 PowerStatusView::_NotifyLowBattery() 596 { 597 BBitmap* bitmap = NULL; 598 BResources resources; 599 resources.SetToImage((void*)&instantiate_deskbar_item); 600 601 if (resources.InitCheck() == B_OK) { 602 size_t resourceSize = 0; 603 const void* resourceData = resources.LoadResource( 604 B_VECTOR_ICON_TYPE, fHasBattery 605 ? "battery_low" : "battery_critical", &resourceSize); 606 if (resourceData != NULL) { 607 BMemoryIO memoryIO(resourceData, resourceSize); 608 bitmap = BTranslationUtils::GetBitmap(&memoryIO); 609 } 610 } 611 612 BNotification notification( 613 fHasBattery ? B_INFORMATION_NOTIFICATION : B_ERROR_NOTIFICATION); 614 615 if (fHasBattery) { 616 system_beep("Battery low"); 617 notification.SetTitle(B_TRANSLATE("Battery low")); 618 notification.SetContent(B_TRANSLATE( 619 "The battery level is getting low, please plug in the device.")); 620 } else { 621 system_beep("Battery critical"); 622 notification.SetTitle(B_TRANSLATE("Battery critical")); 623 notification.SetContent(B_TRANSLATE( 624 "The battery level is critical, please plug in the device " 625 "immediately.")); 626 } 627 628 notification.SetIcon(bitmap); 629 notification.Send(); 630 delete bitmap; 631 } 632 633 634 // #pragma mark - Replicant view 635 636 637 PowerStatusReplicant::PowerStatusReplicant(BRect frame, int32 resizingMode, 638 bool inDeskbar) 639 : 640 PowerStatusView(NULL, frame, resizingMode, -1, inDeskbar), 641 fReplicated(false) 642 { 643 _Init(); 644 _LoadSettings(); 645 646 if (!inDeskbar) { 647 // we were obviously added to a standard window - let's add a dragger 648 frame.OffsetTo(B_ORIGIN); 649 frame.top = frame.bottom - 7; 650 frame.left = frame.right - 7; 651 BDragger* dragger = new BDragger(frame, this, 652 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 653 AddChild(dragger); 654 } else 655 Update(false,false); 656 } 657 658 659 PowerStatusReplicant::PowerStatusReplicant(BMessage* archive) 660 : 661 PowerStatusView(archive), 662 fReplicated(true) 663 { 664 _Init(); 665 _LoadSettings(); 666 } 667 668 669 PowerStatusReplicant::~PowerStatusReplicant() 670 { 671 if (fMessengerExist) 672 delete fExtWindowMessenger; 673 674 if (fExtendedWindow != NULL && fExtendedWindow->Lock()) { 675 fExtendedWindow->Quit(); 676 fExtendedWindow = NULL; 677 } 678 679 fDriverInterface->StopWatching(this); 680 fDriverInterface->Disconnect(); 681 fDriverInterface->ReleaseReference(); 682 683 _SaveSettings(); 684 } 685 686 687 PowerStatusReplicant* 688 PowerStatusReplicant::Instantiate(BMessage* archive) 689 { 690 if (!validate_instantiation(archive, "PowerStatusReplicant")) 691 return NULL; 692 693 return new PowerStatusReplicant(archive); 694 } 695 696 697 status_t 698 PowerStatusReplicant::Archive(BMessage* archive, bool deep) const 699 { 700 status_t status = PowerStatusView::Archive(archive, deep); 701 if (status == B_OK) 702 status = archive->AddString("add_on", kSignature); 703 if (status == B_OK) 704 status = archive->AddString("class", "PowerStatusReplicant"); 705 706 return status; 707 } 708 709 710 void 711 PowerStatusReplicant::MessageReceived(BMessage *message) 712 { 713 switch (message->what) { 714 case kMsgToggleLabel: 715 if (fShowStatusIcon) 716 fShowLabel = !fShowLabel; 717 else 718 fShowLabel = true; 719 720 Update(true); 721 break; 722 723 case kMsgToggleTime: 724 fShowTime = !fShowTime; 725 Update(true); 726 break; 727 728 case kMsgToggleStatusIcon: 729 if (fShowLabel) 730 fShowStatusIcon = !fShowStatusIcon; 731 else 732 fShowStatusIcon = true; 733 734 Update(true); 735 break; 736 737 case kMsgToggleExtInfo: 738 _OpenExtendedWindow(); 739 break; 740 741 case B_ABOUT_REQUESTED: 742 _AboutRequested(); 743 break; 744 745 case B_QUIT_REQUESTED: 746 _Quit(); 747 break; 748 749 default: 750 PowerStatusView::MessageReceived(message); 751 break; 752 } 753 } 754 755 756 void 757 PowerStatusReplicant::MouseDown(BPoint point) 758 { 759 BMessage* msg = Window()->CurrentMessage(); 760 int32 buttons = msg->GetInt32("buttons", 0); 761 if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) { 762 BMessenger messenger(this); 763 messenger.SendMessage(kMsgToggleExtInfo); 764 } else { 765 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 766 menu->SetFont(be_plain_font); 767 768 BMenuItem* item; 769 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show text label"), 770 new BMessage(kMsgToggleLabel))); 771 if (fShowLabel) 772 item->SetMarked(true); 773 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show status icon"), 774 new BMessage(kMsgToggleStatusIcon))); 775 if (fShowStatusIcon) 776 item->SetMarked(true); 777 menu->AddItem(new BMenuItem(!fShowTime ? B_TRANSLATE("Show time") : 778 B_TRANSLATE("Show percent"), new BMessage(kMsgToggleTime))); 779 780 menu->AddSeparatorItem(); 781 menu->AddItem(new BMenuItem(B_TRANSLATE("Battery info" B_UTF8_ELLIPSIS), 782 new BMessage(kMsgToggleExtInfo))); 783 784 menu->AddSeparatorItem(); 785 menu->AddItem(new BMenuItem(B_TRANSLATE("About" B_UTF8_ELLIPSIS), 786 new BMessage(B_ABOUT_REQUESTED))); 787 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 788 new BMessage(B_QUIT_REQUESTED))); 789 menu->SetTargetForItems(this); 790 791 ConvertToScreen(&point); 792 menu->Go(point, true, false, true); 793 } 794 } 795 796 797 void 798 PowerStatusReplicant::_AboutRequested() 799 { 800 BAboutWindow* window = new BAboutWindow( 801 B_TRANSLATE_SYSTEM_NAME("PowerStatus"), kSignature); 802 803 const char* authors[] = { 804 "Axel Dörfler", 805 "Alexander von Gluck", 806 "Clemens Zeidler", 807 NULL 808 }; 809 810 window->AddCopyright(2006, "Haiku, Inc."); 811 window->AddAuthors(authors); 812 813 window->Show(); 814 } 815 816 817 void 818 PowerStatusReplicant::_Init() 819 { 820 fDriverInterface = new ACPIDriverInterface; 821 if (fDriverInterface->Connect() != B_OK) { 822 delete fDriverInterface; 823 fDriverInterface = new APMDriverInterface; 824 if (fDriverInterface->Connect() != B_OK) { 825 fprintf(stderr, "No power interface found.\n"); 826 _Quit(); 827 } 828 } 829 830 fExtendedWindow = NULL; 831 fMessengerExist = false; 832 fExtWindowMessenger = NULL; 833 834 fDriverInterface->StartWatching(this); 835 } 836 837 838 void 839 PowerStatusReplicant::_Quit() 840 { 841 if (fInDeskbar) { 842 BDeskbar deskbar; 843 deskbar.RemoveItem(kDeskbarItemName); 844 } else if (fReplicated) { 845 BDragger *dragger = dynamic_cast<BDragger*>(ChildAt(0)); 846 if (dragger != NULL) { 847 BMessenger messenger(dragger); 848 messenger.SendMessage(new BMessage(B_TRASH_TARGET)); 849 } 850 } else 851 be_app->PostMessage(B_QUIT_REQUESTED); 852 } 853 854 855 status_t 856 PowerStatusReplicant::_GetSettings(BFile& file, int mode) 857 { 858 BPath path; 859 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, 860 (mode & O_ACCMODE) != O_RDONLY); 861 if (status != B_OK) 862 return status; 863 864 path.Append("PowerStatus settings"); 865 866 return file.SetTo(path.Path(), mode); 867 } 868 869 870 void 871 PowerStatusReplicant::_LoadSettings() 872 { 873 fShowLabel = false; 874 875 BFile file; 876 if (_GetSettings(file, B_READ_ONLY) != B_OK) 877 return; 878 879 BMessage settings; 880 if (settings.Unflatten(&file) < B_OK) 881 return; 882 883 FromMessage(&settings); 884 } 885 886 887 void 888 PowerStatusReplicant::_SaveSettings() 889 { 890 BFile file; 891 if (_GetSettings(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK) 892 return; 893 894 BMessage settings('pwst'); 895 ToMessage(&settings); 896 897 ssize_t size = 0; 898 settings.Flatten(&file, &size); 899 } 900 901 902 void 903 PowerStatusReplicant::_OpenExtendedWindow() 904 { 905 if (!fExtendedWindow) { 906 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 907 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 908 fExtendedWindow->Show(); 909 return; 910 } 911 912 BMessage msg(B_SET_PROPERTY); 913 msg.AddSpecifier("Hidden", int32(0)); 914 if (fExtWindowMessenger->SendMessage(&msg) == B_BAD_PORT_ID) { 915 fExtendedWindow = new ExtendedInfoWindow(fDriverInterface); 916 if (fMessengerExist) 917 delete fExtWindowMessenger; 918 fExtWindowMessenger = new BMessenger(NULL, fExtendedWindow); 919 fMessengerExist = true; 920 fExtendedWindow->Show(); 921 } else 922 fExtendedWindow->Activate(); 923 924 } 925 926 927 // #pragma mark - 928 929 930 extern "C" _EXPORT BView* 931 instantiate_deskbar_item(float maxWidth, float maxHeight) 932 { 933 return new PowerStatusReplicant(BRect(0, 0, maxHeight - 1, maxHeight - 1), 934 B_FOLLOW_NONE, true); 935 } 936