xref: /haiku/src/add-ons/input_server/devices/keyboard/TeamMonitorWindow.cpp (revision 2897df967633aab846ff4917b53e2af7d1e54eeb)
1 /*
2  * Copyright 2004-2008, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  *		Axel Doerfler, axeld@pinc-software.de
8  */
9 
10 //!	Keyboard input server addon
11 
12 #include "TeamMonitorWindow.h"
13 
14 #include <stdio.h>
15 
16 #include <Application.h>
17 #include <CardLayout.h>
18 #include <Catalog.h>
19 #include <GroupLayoutBuilder.h>
20 #include <IconView.h>
21 #include <LayoutBuilder.h>
22 #include <LocaleRoster.h>
23 #include <Message.h>
24 #include <MessageRunner.h>
25 #include <Roster.h>
26 #include <ScrollView.h>
27 #include <Screen.h>
28 #include <SpaceLayoutItem.h>
29 #include <String.h>
30 #include <StringView.h>
31 #include <TextView.h>
32 
33 #include <syscalls.h>
34 #include <tracker_private.h>
35 
36 #include "KeyboardInputDevice.h"
37 #include "TeamListItem.h"
38 
39 
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "Team monitor"
42 
43 
44 TeamMonitorWindow* gTeamMonitorWindow = NULL;
45 
46 
47 struct TeamQuitter {
48 	team_id team;
49 	thread_id thread;
50 	BLooper* window;
51 };
52 
53 
54 status_t
55 QuitTeamThreadFunction(void* data)
56 {
57 	TeamQuitter* teamQuitter = reinterpret_cast<TeamQuitter*>(data);
58 	if (teamQuitter == NULL)
59 		return B_ERROR;
60 
61 	status_t status;
62 	BMessenger messenger(NULL, teamQuitter->team, &status);
63 	if (status != B_OK)
64 		return status;
65 
66 	BMessage message(B_QUIT_REQUESTED);
67 	BMessage reply;
68 
69 	messenger.SendMessage(&message, &reply, 3000000, 3000000);
70 
71 	bool result;
72 	if (reply.what != B_REPLY
73 		|| reply.FindBool("result", &result) != B_OK
74 		|| result == false) {
75 		message.what = kMsgQuitFailed;
76 		message.AddPointer("TeamQuitter", teamQuitter);
77 		message.AddInt32("error", reply.what);
78 		if (teamQuitter->window != NULL)
79 			teamQuitter->window->PostMessage(&message);
80 		return reply.what;
81 	}
82 
83 	return B_OK;
84 }
85 
86 
87 filter_result
88 FilterLocaleChanged(BMessage* message, BHandler** target,
89 	BMessageFilter *filter)
90 {
91 	if (message->what == B_LOCALE_CHANGED && gTeamMonitorWindow != NULL)
92 		gTeamMonitorWindow->LocaleChanged();
93 
94 	return B_DISPATCH_MESSAGE;
95 }
96 
97 
98 filter_result
99 FilterKeyDown(BMessage* message, BHandler** target,
100 	BMessageFilter *filter)
101 {
102 	if (message->what == B_KEY_DOWN && gTeamMonitorWindow != NULL) {
103 		if (gTeamMonitorWindow->HandleKeyDown(message))
104 			return B_SKIP_MESSAGE;
105 	}
106 
107 	return B_DISPATCH_MESSAGE;
108 }
109 
110 
111 class TeamDescriptionView : public BView {
112 public:
113 							TeamDescriptionView();
114 	virtual					~TeamDescriptionView();
115 
116 	virtual void			MessageReceived(BMessage* message);
117 
118 			void			CtrlAltDelPressed(bool keyDown);
119 
120 			void			SetItem(TeamListItem* item);
121 			TeamListItem*	Item() { return fItem; }
122 
123 private:
124 			TeamListItem*	fItem;
125 			int32			fSeconds;
126 			BMessageRunner*	fRebootRunner;
127 			IconView*		fIconView;
128 	const	char*			fInfoString;
129 			BCardLayout*	fLayout;
130 			BTextView*		fInfoTextView;
131 
132 			BStringView*	fTeamName;
133 			BStringView*	fSysComponent;
134 			BStringView*	fQuitOverdue;
135 };
136 
137 
138 static const uint32 kMsgUpdate = 'TMup';
139 static const uint32 kMsgLaunchTerminal = 'TMlt';
140 const uint32 TM_CANCEL = 'TMca';
141 const uint32 TM_FORCE_REBOOT = 'TMfr';
142 const uint32 TM_KILL_APPLICATION = 'TMka';
143 const uint32 TM_QUIT_APPLICATION = 'TMqa';
144 const uint32 TM_RESTART_DESKTOP = 'TMrd';
145 const uint32 TM_SELECTED_TEAM = 'TMst';
146 
147 static const uint32 kMsgRebootTick = 'TMrt';
148 
149 
150 TeamMonitorWindow::TeamMonitorWindow()
151 	:
152 	BWindow(BRect(0, 0, 350, 100), B_TRANSLATE("Team monitor"),
153 		B_TITLED_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL,
154 		B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS
155 			| B_CLOSE_ON_ESCAPE | B_AUTO_UPDATE_SIZE_LIMITS,
156 		B_ALL_WORKSPACES),
157 	fQuitting(false),
158 	fUpdateRunner(NULL)
159 {
160 	BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
161 	float inset = 10;
162 	layout->SetInsets(inset, inset, inset, inset);
163 	layout->SetSpacing(inset);
164 	SetLayout(layout);
165 
166 	layout->View()->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
167 
168 	fListView = new BListView("teams");
169 	fListView->SetSelectionMessage(new BMessage(TM_SELECTED_TEAM));
170 
171 	BScrollView* scrollView = new BScrollView("scroll_teams", fListView,
172 		0, B_SUPPORTS_LAYOUT, false, true, B_FANCY_BORDER);
173 	scrollView->SetExplicitMinSize(BSize(B_SIZE_UNSET, 150));
174 
175 	fKillButton = new BButton("kill", B_TRANSLATE("Kill application"),
176 		new BMessage(TM_KILL_APPLICATION));
177 	fKillButton->SetEnabled(false);
178 
179 	fQuitButton = new BButton("quit", B_TRANSLATE("Quit application"),
180 		new BMessage(TM_QUIT_APPLICATION));
181 	fQuitButton->SetEnabled(false);
182 
183 	fDescriptionView = new TeamDescriptionView;
184 
185 	BButton* forceReboot = new BButton("force", B_TRANSLATE("Force reboot"),
186 		new BMessage(TM_FORCE_REBOOT));
187 
188 	fRestartButton = new BButton("restart", B_TRANSLATE("Restart the desktop"),
189 		new BMessage(TM_RESTART_DESKTOP));
190 
191 	fCancelButton = new BButton("cancel", B_TRANSLATE("Cancel"),
192 		new BMessage(TM_CANCEL));
193 	SetDefaultButton(fCancelButton);
194 
195 	BGroupLayoutBuilder(layout)
196 		.Add(scrollView)
197 		.AddGroup(B_HORIZONTAL)
198 			.SetInsets(0, 0, 0, 0)
199 			.Add(fKillButton)
200 			.Add(fQuitButton)
201 			.AddGlue()
202 			.End()
203 		.Add(fDescriptionView)
204 		.AddGroup(B_HORIZONTAL)
205 			.SetInsets(0, 0, 0, 0)
206 			.Add(forceReboot)
207 			.AddGlue()
208 			.Add(fRestartButton)
209 			.AddGlue(inset)
210 			.Add(fCancelButton);
211 
212 	CenterOnScreen();
213 
214 	fRestartButton->Hide();
215 
216 	AddShortcut('T', B_COMMAND_KEY | B_OPTION_KEY,
217 		new BMessage(kMsgLaunchTerminal));
218 	AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
219 
220 	gLocalizedNamePreferred
221 		= BLocaleRoster::Default()->IsFilesystemTranslationPreferred();
222 
223 	gTeamMonitorWindow = this;
224 
225 	this->AddCommonFilter(new BMessageFilter(B_ANY_DELIVERY,
226 		B_ANY_SOURCE, B_KEY_DOWN, FilterKeyDown));
227 
228 	if (be_app->Lock()) {
229 		be_app->AddCommonFilter(new BMessageFilter(B_ANY_DELIVERY,
230 			B_ANY_SOURCE, B_LOCALE_CHANGED, FilterLocaleChanged));
231 		be_app->Unlock();
232 	}
233 }
234 
235 
236 TeamMonitorWindow::~TeamMonitorWindow()
237 {
238 	while (fTeamQuitterList.ItemAt(0) != NULL) {
239 		TeamQuitter* teamQuitter = reinterpret_cast<TeamQuitter*>
240 			(fTeamQuitterList.RemoveItem((int32) 0));
241 		if (teamQuitter != NULL) {
242 			status_t status;
243 			wait_for_thread(teamQuitter->thread, &status);
244 			delete teamQuitter;
245 		}
246 	}
247 }
248 
249 
250 void
251 TeamMonitorWindow::MessageReceived(BMessage* msg)
252 {
253 	switch (msg->what) {
254 		case SYSTEM_SHUTTING_DOWN:
255 			fQuitting = true;
256 			break;
257 
258 		case kMsgUpdate:
259 			_UpdateList();
260 			break;
261 
262 		case kMsgCtrlAltDelPressed:
263 			bool keyDown;
264 			if (msg->FindBool("key down", &keyDown) != B_OK)
265 				break;
266 
267 			fDescriptionView->CtrlAltDelPressed(keyDown);
268 			break;
269 
270 		case kMsgDeselectAll:
271 			fListView->DeselectAll();
272 			break;
273 
274 		case kMsgLaunchTerminal:
275 			be_roster->Launch("application/x-vnd.Haiku-Terminal");
276 			break;
277 
278 		case TM_FORCE_REBOOT:
279 			_kern_shutdown(true);
280 			break;
281 
282 		case TM_KILL_APPLICATION:
283 		{
284 			TeamListItem* item = dynamic_cast<TeamListItem*>(fListView->ItemAt(
285 				fListView->CurrentSelection()));
286 			if (item != NULL) {
287 				kill_team(item->GetInfo()->team);
288 				_UpdateList();
289 			}
290 			break;
291 		}
292 		case TM_QUIT_APPLICATION:
293 		{
294 			TeamListItem* item = dynamic_cast<TeamListItem*>(fListView->ItemAt(
295 				fListView->CurrentSelection()));
296 			if (item != NULL) {
297 				QuitTeam(item);
298 			}
299 			break;
300 		}
301 		case kMsgQuitFailed:
302 			MarkUnquittableTeam(msg);
303 			break;
304 
305 		case TM_RESTART_DESKTOP:
306 		{
307 			if (!be_roster->IsRunning(kTrackerSignature))
308 				be_roster->Launch(kTrackerSignature);
309 			if (!be_roster->IsRunning(kDeskbarSignature))
310 				be_roster->Launch(kDeskbarSignature);
311 			fRestartButton->Hide();
312 			SetDefaultButton(fCancelButton);
313 			break;
314 		}
315 		case TM_SELECTED_TEAM:
316 		{
317 			fKillButton->SetEnabled(fListView->CurrentSelection() >= 0);
318 			TeamListItem* item = dynamic_cast<TeamListItem*>(fListView->ItemAt(
319 				fListView->CurrentSelection()));
320 			fDescriptionView->SetItem(item);
321 			fQuitButton->SetEnabled(item != NULL && item->IsApplication());
322 			break;
323 		}
324 		case TM_CANCEL:
325 			PostMessage(B_QUIT_REQUESTED);
326 			break;
327 
328 		default:
329 			BWindow::MessageReceived(msg);
330 			break;
331 	}
332 }
333 
334 
335 void
336 TeamMonitorWindow::Show()
337 {
338 	fListView->MakeFocus();
339 	BWindow::Show();
340 }
341 
342 
343 bool
344 TeamMonitorWindow::QuitRequested()
345 {
346 	Disable();
347 	return fQuitting;
348 }
349 
350 
351 void
352 TeamMonitorWindow::Enable()
353 {
354 	if (Lock()) {
355 		if (IsHidden()) {
356 			BMessage message(kMsgUpdate);
357 			fUpdateRunner = new BMessageRunner(this, &message, 1000000LL);
358 
359 			_UpdateList();
360 			Show();
361 		}
362 		Unlock();
363 	}
364 
365 	// Not sure why this is needed, but without it the layout isn't correct
366 	// when the window is first shown and the buttons at the bottom aren't
367 	// visible.
368 	InvalidateLayout();
369 }
370 
371 
372 void
373 TeamMonitorWindow::Disable()
374 {
375 	delete fUpdateRunner;
376 	fUpdateRunner = NULL;
377 	Hide();
378 	fListView->DeselectAll();
379 	for (int32 i = 0; i < fListView->CountItems(); i++) {
380 		TeamListItem* item = dynamic_cast<TeamListItem*>(fListView->ItemAt(i));
381 		if (item != NULL)
382 			item->SetRefusingToQuit(false);
383 	}
384 }
385 
386 
387 void
388 TeamMonitorWindow::LocaleChanged()
389 {
390 	BLocaleRoster::Default()->Refresh();
391 	gLocalizedNamePreferred
392 		= BLocaleRoster::Default()->IsFilesystemTranslationPreferred();
393 
394 	for (int32 i = 0; i < fListView->CountItems(); i++) {
395 		TeamListItem* item
396 			= dynamic_cast<TeamListItem*>(fListView->ItemAt(i));
397 		if (item != NULL)
398 			item->CacheLocalizedName();
399 	}
400 }
401 
402 
403 void
404 TeamMonitorWindow::QuitTeam(TeamListItem* item)
405 {
406 	if (item == NULL)
407 		return;
408 
409 	TeamQuitter* teamQuitter = new TeamQuitter;
410 	teamQuitter->team = item->GetInfo()->team;
411 	teamQuitter->window = this;
412 	teamQuitter->thread = spawn_thread(QuitTeamThreadFunction,
413 		"team quitter", B_DISPLAY_PRIORITY, teamQuitter);
414 
415 	if (teamQuitter->thread < 0) {
416 		delete teamQuitter;
417 		return;
418 	}
419 
420 	fTeamQuitterList.AddItem(teamQuitter);
421 
422 	if (resume_thread(teamQuitter->thread) != B_OK) {
423 		fTeamQuitterList.RemoveItem(teamQuitter);
424 		delete teamQuitter;
425 	}
426 }
427 
428 
429 void
430 TeamMonitorWindow::MarkUnquittableTeam(BMessage* message)
431 {
432 	if (message == NULL)
433 		return;
434 
435 	int32 reply;
436 	if (message->FindInt32("error", &reply) != B_OK)
437 		return;
438 
439 	TeamQuitter* teamQuitter;
440 	if (message->FindPointer("TeamQuitter",
441 		reinterpret_cast<void**>(&teamQuitter)) != B_OK)
442 		return;
443 
444 	for (int32 i = 0; i < fListView->CountItems(); i++) {
445 		TeamListItem* item
446 			= dynamic_cast<TeamListItem*>(fListView->ItemAt(i));
447 		if (item != NULL && item->GetInfo()->team == teamQuitter->team) {
448 			item->SetRefusingToQuit(true);
449 			fListView->Select(i);
450 			fListView->InvalidateItem(i);
451 			fDescriptionView->SetItem(item);
452 			break;
453 		}
454 	}
455 
456 	fTeamQuitterList.RemoveItem(teamQuitter);
457 	delete teamQuitter;
458 }
459 
460 
461 bool
462 TeamMonitorWindow::HandleKeyDown(BMessage* msg)
463 {
464 	uint32 rawChar = msg->FindInt32("raw_char");
465 	uint32 modifier = msg->FindInt32("modifiers");
466 
467 	// Ignore the system modifier namespace
468 	if ((modifier & (B_CONTROL_KEY | B_COMMAND_KEY))
469 			== (B_CONTROL_KEY | B_COMMAND_KEY))
470 		return false;
471 
472 	bool quit = false;
473 	bool kill = false;
474 	switch (rawChar) {
475 		case B_DELETE:
476 			if (modifier & B_SHIFT_KEY)
477 				kill = true;
478 			else
479 				quit = true;
480 			break;
481 		case 'q':
482 		case 'Q':
483 			quit = true;
484 			break;
485 		case 'k':
486 		case 'K':
487 			kill = true;
488 			break;
489 	}
490 
491 	if (quit) {
492 		PostMessage(TM_QUIT_APPLICATION);
493 		return true;
494 	} else if (kill) {
495 		PostMessage(TM_KILL_APPLICATION);
496 		return true;
497 	}
498 
499 	return false;
500 }
501 
502 
503 void
504 TeamMonitorWindow::_UpdateList()
505 {
506 	bool changed = false;
507 
508 	for (int32 i = 0; i < fListView->CountItems(); i++) {
509 		TeamListItem* item = dynamic_cast<TeamListItem*>(fListView->ItemAt(i));
510 		if (item != NULL)
511 			item->SetFound(false);
512 	}
513 
514 	int32 cookie = 0;
515 	team_info info;
516 	while (get_next_team_info(&cookie, &info) == B_OK) {
517 		if (info.team <=16)
518 			continue;
519 
520 		bool found = false;
521 		for (int32 i = 0; i < fListView->CountItems(); i++) {
522 			TeamListItem* item
523 				= dynamic_cast<TeamListItem*>(fListView->ItemAt(i));
524 			if (item != NULL && item->GetInfo()->team == info.team) {
525 				item->SetFound(true);
526 				found = true;
527 			}
528 		}
529 
530 		if (!found) {
531 			TeamListItem* item = new TeamListItem(info);
532 
533 			fListView->AddItem(item,
534 				item->IsSystemServer() ? fListView->CountItems() : 0);
535 			item->SetFound(true);
536 			changed = true;
537 		}
538 	}
539 
540 	for (int32 i = fListView->CountItems() - 1; i >= 0; i--) {
541 		TeamListItem* item = dynamic_cast<TeamListItem*>(fListView->ItemAt(i));
542 		if (item != NULL && !item->Found()) {
543 			if (item == fDescriptionView->Item()) {
544 				fDescriptionView->SetItem(NULL);
545 				fKillButton->SetEnabled(false);
546 				fQuitButton->SetEnabled(false);
547 			}
548 
549 			delete fListView->RemoveItem(i);
550 			changed = true;
551 		}
552 	}
553 
554 	if (changed)
555 		fListView->Invalidate();
556 
557 	bool desktopRunning = be_roster->IsRunning(kTrackerSignature)
558 		&& be_roster->IsRunning(kDeskbarSignature);
559 	if (!desktopRunning && fRestartButton->IsHidden()) {
560 		fRestartButton->Show();
561 		SetDefaultButton(fRestartButton);
562 		fRestartButton->Parent()->Layout(true);
563 	}
564 
565 	fRestartButton->SetEnabled(!desktopRunning);
566 }
567 
568 
569 //	#pragma mark -
570 
571 
572 TeamDescriptionView::TeamDescriptionView()
573 	:
574 	BView("description view", B_WILL_DRAW),
575 	fItem(NULL),
576 	fSeconds(4),
577 	fRebootRunner(NULL)
578 {
579 	fInfoString = B_TRANSLATE(
580 		"Select an application from the list above and click one of "
581 		"the buttons 'Kill application' and 'Quit application' "
582 		"in order to close it.\n\n"
583 		"Hold CONTROL+ALT+DELETE for %ld seconds to reboot.");
584 
585 	fTeamName = new BStringView("team name", "team name");
586 	fTeamName->SetTruncation(B_TRUNCATE_BEGINNING);
587 	fTeamName->SetExplicitSize(BSize(StringWidth("x") * 60, B_SIZE_UNSET));
588 	fSysComponent = new BStringView("system component", B_TRANSLATE(
589 		"(This team is a system component)"));
590 	fQuitOverdue = new BStringView("quit overdue", B_TRANSLATE(
591 		"If the application will not quit you may have to kill it."));
592 	fQuitOverdue->SetFont(be_bold_font);
593 
594 	fInfoTextView = new BTextView("info text");
595 	fInfoTextView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
596 	fInfoTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
597 	BGroupView* infoGroup = new BGroupView(B_VERTICAL);
598 	BGroupLayoutBuilder(infoGroup)
599 		.Add(fInfoTextView)
600 		.AddGlue();
601 
602 	fIconView = new IconView();
603 	fIconView->SetExplicitAlignment(
604 		BAlignment(B_ALIGN_HORIZONTAL_UNSET, B_ALIGN_VERTICAL_CENTER));
605 
606 	BView* teamPropertiesView = new BView("team properties", B_WILL_DRAW);
607 	teamPropertiesView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
608 	BGridLayout* layout = new BGridLayout();
609 	teamPropertiesView->SetLayout(layout);
610 
611 
612 	BLayoutBuilder::Grid<>(layout)
613 		.SetInsets(0)
614 		.SetSpacing(B_USE_ITEM_SPACING, 0)
615 		.AddGlue(0, 0)
616 		.Add(fIconView, 0, 1, 1, 3)
617 		.AddGlue(1, 0)
618 		.Add(fTeamName, 1, 2)
619 		.Add(fSysComponent, 1, 3)
620 		.Add(fQuitOverdue, 1, 4)
621 		.AddGlue(0, 5)
622 		.AddGlue(2, 1);
623 
624 	fLayout = new BCardLayout();
625 	SetLayout(fLayout);
626 	fLayout->AddView(infoGroup);
627 	fLayout->AddView(teamPropertiesView);
628 
629 	infoGroup->SetExplicitMinSize(BSize(StringWidth("x") * 70, B_SIZE_UNSET));
630 	fInfoTextView->SetExplicitSize(BSize(B_SIZE_UNSET, be_plain_font->Size() * 7.2));
631 	fInfoTextView->MakeEditable(false);
632 	fInfoTextView->MakeSelectable(false);
633 
634 	SetItem(NULL);
635 }
636 
637 
638 TeamDescriptionView::~TeamDescriptionView()
639 {
640 	delete fRebootRunner;
641 }
642 
643 
644 void
645 TeamDescriptionView::MessageReceived(BMessage* message)
646 {
647 	switch (message->what) {
648 		case kMsgRebootTick:
649 			fSeconds--;
650 			if (fSeconds == 0)
651 				Window()->PostMessage(TM_FORCE_REBOOT);
652 			else
653 				SetItem(fItem);
654 			break;
655 
656 		default:
657 			BView::MessageReceived(message);
658 	}
659 }
660 
661 
662 void
663 TeamDescriptionView::CtrlAltDelPressed(bool keyDown)
664 {
665 	if (!(keyDown ^ (fRebootRunner != NULL)))
666 		return;
667 
668 	delete fRebootRunner;
669 	fRebootRunner = NULL;
670 	fSeconds = 4;
671 
672 	if (keyDown) {
673 		Window()->PostMessage(kMsgDeselectAll);
674 		BMessage tick(kMsgRebootTick);
675 		fRebootRunner = new BMessageRunner(this, &tick, 1000000LL);
676 	}
677 
678 	SetItem(NULL);
679 }
680 
681 
682 void
683 TeamDescriptionView::SetItem(TeamListItem* item)
684 {
685 	fItem = item;
686 
687 	if (item == NULL) {
688 		int32 styleStart = 0;
689 		int32 styleEnd = 0;
690 		BString text;
691 
692 		text.SetToFormat(fInfoString, fSeconds);
693 		fInfoTextView->SetText(text);
694 		if (fRebootRunner != NULL && fSeconds < 4) {
695 			styleStart = text.FindLast('\n');
696 			styleEnd = text.Length();
697 		}
698 
699 		if (styleStart != styleEnd && fInfoTextView != NULL) {
700 			BFont font;
701 			fInfoTextView->GetFont(&font);
702 			font.SetFace(B_BOLD_FACE);
703 			fInfoTextView->SetStylable(true);
704 			fInfoTextView->SetFontAndColor(styleStart, styleEnd, &font);
705 		}
706 	} else {
707 		fTeamName->SetText(item->Path()->Path());
708 
709 		if (item->IsSystemServer()) {
710 			if (fSysComponent->IsHidden(fSysComponent))
711 				fSysComponent->Show();
712 		} else {
713 			if (!fSysComponent->IsHidden(fSysComponent))
714 				fSysComponent->Hide();
715 		}
716 
717 		if (item->IsRefusingToQuit()) {
718 			if (fQuitOverdue->IsHidden(fQuitOverdue))
719 				fQuitOverdue->Show();
720 		} else {
721 			if (!fQuitOverdue->IsHidden(fQuitOverdue))
722 				fQuitOverdue->Hide();
723 		}
724 
725 		fIconView->SetIcon(item->Path()->Path());
726 	}
727 
728 	if (fLayout == NULL)
729 		return;
730 
731 	if (item == NULL)
732 		fLayout->SetVisibleItem((int32)0);
733 	else
734 		fLayout->SetVisibleItem((int32)1);
735 
736 	Invalidate();
737 }
738 
739 
740