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