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