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