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