xref: /haiku/src/apps/terminal/TermWindow.cpp (revision d2e1e872611179c9cfaa43ce11bd58b1e3554e4b)
1 /*
2  * Copyright 2007 Haiku, Inc.
3  * Copyright (c) 2004 Daniel Furrer <assimil8or@users.sourceforge.net>
4  * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net>
5  * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
6  *
7  * Distributed under the terms of the MIT license.
8  */
9 
10 #include "TermWindow.h"
11 
12 #include "Arguments.h"
13 #include "Coding.h"
14 #include "MenuUtil.h"
15 #include "FindWindow.h"
16 #include "PrefWindow.h"
17 #include "PrefView.h"
18 #include "PrefHandler.h"
19 #include "SmartTabView.h"
20 #include "TermConst.h"
21 #include "TermView.h"
22 
23 #include <Alert.h>
24 #include <Application.h>
25 #include <Clipboard.h>
26 #include <Dragger.h>
27 #include <Menu.h>
28 #include <MenuBar.h>
29 #include <MenuItem.h>
30 #include <Path.h>
31 #include <PrintJob.h>
32 #include <Roster.h>
33 #include <Screen.h>
34 #include <ScrollBar.h>
35 #include <ScrollView.h>
36 #include <String.h>
37 
38 #include <stdio.h>
39 #include <string.h>
40 #include <time.h>
41 
42 
43 const static int32 kMaxTabs = 6;
44 
45 // messages constants
46 const static uint32 kNewTab = 'NTab';
47 const static uint32 kCloseView = 'ClVw';
48 const static uint32 kIncreaseFontSize = 'InFs';
49 const static uint32 kDecreaseFontSize = 'DcFs';
50 
51 
52 class CustomTermView : public TermView {
53 public:
54 	CustomTermView(int32 rows, int32 columns, int32 argc, const char **argv, int32 historySize = 1000);
55 	virtual void NotifyQuit(int32 reason);
56 	virtual void SetTitle(const char *title);
57 };
58 
59 
60 TermWindow::TermWindow(BRect frame, const char* title, Arguments *args)
61 	: BWindow(frame, title, B_DOCUMENT_WINDOW, B_CURRENT_WORKSPACE|B_QUIT_ON_WINDOW_CLOSE),
62 	fTabView(NULL),
63 	fMenubar(NULL),
64 	fFilemenu(NULL),
65 	fEditmenu(NULL),
66 	fEncodingmenu(NULL),
67 	fHelpmenu(NULL),
68 	fWindowSizeMenu(NULL),
69 	fPrintSettings(NULL),
70 	fPrefWindow(NULL),
71 	fFindPanel(NULL),
72 	fSavedFrame(0, 0, -1, -1),
73 	fFindString(""),
74 	fFindForwardMenuItem(NULL),
75 	fFindBackwardMenuItem(NULL),
76 	fFindSelection(false),
77 	fForwardSearch(false),
78 	fMatchCase(false),
79 	fMatchWord(false)
80 {
81 	_InitWindow();
82 	_AddTab(args);
83 }
84 
85 
86 TermWindow::~TermWindow()
87 {
88 	if (fPrefWindow)
89 		fPrefWindow->PostMessage(B_QUIT_REQUESTED);
90 
91 	if (fFindPanel && fFindPanel->Lock()) {
92 		fFindPanel->Quit();
93 		fFindPanel = NULL;
94 	}
95 
96 	PrefHandler::DeleteDefault();
97 }
98 
99 
100 void
101 TermWindow::_InitWindow()
102 {
103 	// make menu bar
104 	_SetupMenu();
105 
106 	AddShortcut('+', B_COMMAND_KEY, new BMessage(kIncreaseFontSize));
107 	AddShortcut('-', B_COMMAND_KEY, new BMessage(kDecreaseFontSize));
108 
109 	BRect textFrame = Bounds();
110 	textFrame.top = fMenubar->Bounds().bottom + 1.0;
111 
112 	fTabView = new SmartTabView(textFrame, "tab view");
113 	AddChild(fTabView);
114 }
115 
116 
117 void
118 TermWindow::MenusBeginning()
119 {
120 	// Syncronize Encode Menu Pop-up menu and Preference.
121 	BMenuItem *item = fEncodingmenu->FindItem(EncodingAsString(_ActiveTermView()->Encoding()));
122 	if (item != NULL)
123 		item->SetMarked(true);
124 	BWindow::MenusBeginning();
125 }
126 
127 
128 void
129 TermWindow::_SetupMenu()
130 {
131 	PrefHandler menuText;
132 
133 	LoadLocaleFile(&menuText);
134 
135 	// Menu bar object.
136 	fMenubar = new BMenuBar(Bounds(), "mbar");
137 
138 	// Make File Menu.
139 	fFilemenu = new BMenu("Terminal");
140 	fFilemenu->AddItem(new BMenuItem("Switch Terminals", new BMessage(MENU_SWITCH_TERM),'G'));
141 	fFilemenu->AddItem(new BMenuItem("New Terminal" B_UTF8_ELLIPSIS, new BMessage(MENU_NEW_TERM), 'N'));
142 	fFilemenu->AddItem(new BMenuItem("New Tab", new BMessage(kNewTab), 'T'));
143 
144 	fFilemenu->AddSeparatorItem();
145 	fFilemenu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS, new BMessage(MENU_PAGE_SETUP)));
146 	fFilemenu->AddItem(new BMenuItem("Print", new BMessage(MENU_PRINT),'P'));
147 	fFilemenu->AddSeparatorItem();
148 	fFilemenu->AddItem(new BMenuItem("About Terminal" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)));
149 	fFilemenu->AddSeparatorItem();
150 	fFilemenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
151 	fMenubar->AddItem(fFilemenu);
152 
153 	// Make Edit Menu.
154 	fEditmenu = new BMenu ("Edit");
155 	fEditmenu->AddItem (new BMenuItem ("Copy", new BMessage (B_COPY),'C'));
156 	fEditmenu->AddItem (new BMenuItem ("Paste", new BMessage (B_PASTE),'V'));
157 	fEditmenu->AddSeparatorItem();
158 	fEditmenu->AddItem (new BMenuItem ("Select All", new BMessage (B_SELECT_ALL), 'A'));
159 	fEditmenu->AddItem (new BMenuItem ("Clear All", new BMessage (MENU_CLEAR_ALL), 'L'));
160 	fEditmenu->AddSeparatorItem();
161 	fEditmenu->AddItem (new BMenuItem ("Find" B_UTF8_ELLIPSIS, new BMessage (MENU_FIND_STRING),'F'));
162 	fFindBackwardMenuItem = new BMenuItem ("Find Backward", new BMessage (MENU_FIND_BACKWARD), '[');
163 	fEditmenu->AddItem(fFindBackwardMenuItem);
164 	fFindBackwardMenuItem->SetEnabled(false);
165 	fFindForwardMenuItem = new BMenuItem ("Find Forward", new BMessage (MENU_FIND_FORWARD), ']');
166 	fEditmenu->AddItem (fFindForwardMenuItem);
167 	fFindForwardMenuItem->SetEnabled(false);
168 
169 	fMenubar->AddItem (fEditmenu);
170 
171 	// Make Help Menu.
172 	fHelpmenu = new BMenu("Settings");
173 	fWindowSizeMenu = new BMenu("Window Size");
174 	_BuildWindowSizeMenu(fWindowSizeMenu);
175 
176 	fEncodingmenu = new BMenu("Font Encoding");
177 	fEncodingmenu->SetRadioMode(true);
178 	MakeEncodingMenu(fEncodingmenu, true);
179 	fHelpmenu->AddItem(fWindowSizeMenu);
180 	fHelpmenu->AddItem(fEncodingmenu);
181 	fHelpmenu->AddSeparatorItem();
182 	fHelpmenu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MENU_PREF_OPEN)));
183 	fHelpmenu->AddSeparatorItem();
184 	fHelpmenu->AddItem(new BMenuItem("Save as default", new BMessage(SAVE_AS_DEFAULT)));
185 	fMenubar->AddItem(fHelpmenu);
186 
187 	AddChild(fMenubar);
188 }
189 
190 
191 void
192 TermWindow::_GetPreferredFont(BFont &font)
193 {
194 	const char *family = PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY);
195 
196 	font.SetFamilyAndStyle(family, NULL);
197 	float size = PrefHandler::Default()->getFloat(PREF_HALF_FONT_SIZE);
198 	if (size < 6.0f)
199 		size = 6.0f;
200 	font.SetSize(size);
201 	font.SetSpacing(B_FIXED_SPACING);
202 }
203 
204 
205 void
206 TermWindow::MessageReceived(BMessage *message)
207 {
208 	int32 encodingId;
209 	bool findresult;
210 
211 	switch (message->what) {
212 		case B_COPY:
213 			_ActiveTermView()->Copy(be_clipboard);
214 			break;
215 
216 		case B_PASTE:
217 			_ActiveTermView()->Paste(be_clipboard);
218 			break;
219 
220 		case B_SELECT_ALL:
221 			_ActiveTermView()->SelectAll();
222 			break;
223 
224 		case B_ABOUT_REQUESTED:
225 			be_app->PostMessage(B_ABOUT_REQUESTED);
226 			break;
227 
228 		case MENU_CLEAR_ALL:
229 			_ActiveTermView()->Clear();
230 			break;
231 
232 		case MENU_SWITCH_TERM:
233 			be_app->PostMessage(MENU_SWITCH_TERM);
234 			break;
235 
236 		case MENU_NEW_TERM:
237 		{
238 			app_info info;
239 			be_app->GetAppInfo(&info);
240 
241 			// try launching two different ways to work around possible problems
242 			if (be_roster->Launch(&info.ref) != B_OK)
243 				be_roster->Launch(TERM_SIGNATURE);
244 			break;
245 		}
246 
247 		case MENU_PREF_OPEN:
248 			if (!fPrefWindow)
249 				fPrefWindow = new PrefWindow(this);
250 			else
251 				fPrefWindow->Activate();
252 			break;
253 
254 		case MSG_PREF_CLOSED:
255 			fPrefWindow = NULL;
256 			break;
257 
258 		case MENU_FIND_STRING:
259 			if (!fFindPanel) {
260 				BRect r = Frame();
261 				r.left += 20;
262 				r.top += 20;
263 				r.right = r.left + 260;
264 				r.bottom = r.top + 190;
265 				fFindPanel = new FindWindow(r, this, fFindString, fFindSelection, fMatchWord, fMatchCase, fForwardSearch);
266 			}
267 			else
268 				fFindPanel->Activate();
269 			break;
270 
271 		case MSG_FIND:
272 			fFindPanel->PostMessage(B_QUIT_REQUESTED);
273 			message->FindBool("findselection", &fFindSelection);
274 			if (!fFindSelection)
275 				message->FindString("findstring", &fFindString);
276 			else
277 				_ActiveTermView()->GetSelection(fFindString);
278 
279 			if (fFindString.Length() == 0) {
280 				BAlert *alert = new BAlert("find failed", "No search string.", "Okay", NULL,
281 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
282 				alert->Go();
283 				fFindBackwardMenuItem->SetEnabled(false);
284 				fFindForwardMenuItem->SetEnabled(false);
285 				break;
286 			}
287 
288 			message->FindBool("forwardsearch", &fForwardSearch);
289 			message->FindBool("matchcase", &fMatchCase);
290 			message->FindBool("matchword", &fMatchWord);
291 			findresult = _ActiveTermView()->Find(fFindString, fForwardSearch, fMatchCase, fMatchWord);
292 
293 			if (!findresult) {
294 				BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL,
295 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
296 				alert->Go();
297 				fFindBackwardMenuItem->SetEnabled(false);
298 				fFindForwardMenuItem->SetEnabled(false);
299 				break;
300 			}
301 
302 			// Enable the menu items Find Forward and Find Backward
303 			fFindBackwardMenuItem->SetEnabled(true);
304 			fFindForwardMenuItem->SetEnabled(true);
305 			break;
306 
307 		case MENU_FIND_FORWARD:
308 			findresult = _ActiveTermView()->Find(fFindString, true, fMatchCase, fMatchWord);
309 			if (!findresult) {
310 				BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL,
311 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
312 				alert->Go();
313 			}
314 			break;
315 
316 		case MENU_FIND_BACKWARD:
317 			findresult = _ActiveTermView()->Find(fFindString, false, fMatchCase, fMatchWord);
318 			if (!findresult) {
319 				BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL,
320 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
321 				alert->Go();
322 			}
323 			break;
324 
325 		case MSG_FIND_CLOSED:
326 			fFindPanel = NULL;
327 			break;
328 
329 		case MENU_ENCODING:
330 			if (message->FindInt32("op", &encodingId) == B_OK)
331 				_ActiveTermView()->SetEncoding(encodingId);
332 			break;
333 
334 		case MSG_COLS_CHANGED:
335 		{
336 			int32 columns, rows;
337 			message->FindInt32("columns", &columns);
338 			message->FindInt32("rows", &rows);
339 			PrefHandler::Default()->setInt32(PREF_COLS, columns);
340 			PrefHandler::Default()->setInt32(PREF_ROWS, rows);
341 
342 			_ActiveTermView()->SetTermSize(rows, columns, 0);
343 
344 			_ResizeView(_ActiveTermView());
345 
346 			BPath path;
347 			if (PrefHandler::GetDefaultPath(path) == B_OK)
348 				PrefHandler::Default()->SaveAsText(path.Path(), PREFFILE_MIMETYPE);
349 			break;
350 		}
351 		case MSG_HALF_FONT_CHANGED:
352 		case MSG_FULL_FONT_CHANGED:
353 		case MSG_HALF_SIZE_CHANGED:
354 		case MSG_FULL_SIZE_CHANGED:
355 		{
356 			BFont font;
357 			_GetPreferredFont(font);
358 			_ActiveTermView()->SetTermFont(&font);
359 
360 			_ResizeView(_ActiveTermView());
361 
362 			break;
363 		}
364 
365 		case FULLSCREEN:
366 			if (!fSavedFrame.IsValid()) { // go fullscreen
367 				float mbHeight = fMenubar->Bounds().Height() + 1;
368 				fSavedFrame = Frame();
369 				BScreen screen(this);
370 				_ActiveTermView()->ScrollBar()->Hide();
371 				fMenubar->Hide();
372 				fTabView->ResizeBy(0, mbHeight);
373 				fTabView->MoveBy(0, -mbHeight);
374 				fSavedLook = Look();
375 				// done before ResizeTo to work around a Dano bug (not erasing the decor)
376 				SetLook(B_NO_BORDER_WINDOW_LOOK);
377 				ResizeTo(screen.Frame().Width()+1, screen.Frame().Height()+1);
378 				MoveTo(screen.Frame().left, screen.Frame().top);
379 			} else { // exit fullscreen
380 				float mbHeight = fMenubar->Bounds().Height() + 1;
381 				fMenubar->Show();
382 				_ActiveTermView()->ScrollBar()->Show();
383 				ResizeTo(fSavedFrame.Width(), fSavedFrame.Height());
384 				MoveTo(fSavedFrame.left, fSavedFrame.top);
385 				fTabView->ResizeBy(0, -mbHeight);
386 				fTabView->MoveBy(0, mbHeight);
387 				SetLook(fSavedLook);
388 				fSavedFrame = BRect(0,0,-1,-1);
389 			}
390 			break;
391 
392 		case MSG_FONT_CHANGED:
393 	    	PostMessage(MSG_HALF_FONT_CHANGED);
394 			break;
395 
396 		case MSG_COLOR_CHANGED:
397 			_SetTermColors(_ActiveTermView());
398 			_ActiveTermView()->Invalidate();
399 			break;
400 
401 		case SAVE_AS_DEFAULT:
402 		{
403 			BPath path;
404 			if (PrefHandler::GetDefaultPath(path) == B_OK)
405 				PrefHandler::Default()->SaveAsText(path.Path(), PREFFILE_MIMETYPE);
406 			break;
407 		}
408 		case MENU_PAGE_SETUP:
409 			_DoPageSetup();
410 			break;
411 
412 		case MENU_PRINT:
413 			_DoPrint();
414 			break;
415 
416 		case MSG_CHECK_CHILDREN:
417 			_CheckChildren();
418 			break;
419 
420 		case kNewTab:
421 			if (fTabView->CountTabs() < kMaxTabs)
422 				_AddTab(NULL);
423 			break;
424 
425 		case kCloseView:
426 		{
427 			TermView* termView;
428 			if (message->FindPointer("termView", (void**)&termView) == B_OK) {
429 				int32 index = _IndexOfTermView(termView);
430 				if (index >= 0)
431 					_RemoveTab(index);
432 			}
433 			break;
434 		}
435 
436 		case kIncreaseFontSize:
437 		case kDecreaseFontSize:
438 		{
439 			message->PrintToStream();
440 			TermView *view = _ActiveTermView();
441 			BFont font;
442 			view->GetTermFont(&font);
443 
444 			float size = font.Size();
445 			if (message->what == kIncreaseFontSize)
446 				size += 1;
447 			else
448 				size -= 1;
449 
450 			// limit the font size
451 			if (size < 6)
452 				size = 6;
453 			else if (size > 20)
454 				size = 20;
455 
456 			font.SetSize(size);
457 			view->SetTermFont(&font);
458 			PrefHandler::Default()->setInt32(PREF_HALF_FONT_SIZE, size);
459 
460 			_ResizeView(view);
461 			break;
462 		}
463 
464 		default:
465 			BWindow::MessageReceived(message);
466 			break;
467 	}
468 }
469 
470 
471 void
472 TermWindow::WindowActivated(bool activated)
473 {
474 	BWindow::WindowActivated(activated);
475 }
476 
477 
478 void
479 TermWindow::_SetTermColors(TermView *termView)
480 {
481 	termView->SetTextColor(PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR),
482 				PrefHandler::Default()->getRGB(PREF_TEXT_BACK_COLOR));
483 
484 	termView->SetSelectColor(PrefHandler::Default()->getRGB(PREF_SELECT_FORE_COLOR),
485 				PrefHandler::Default()->getRGB(PREF_SELECT_BACK_COLOR));
486 
487 	termView->SetCursorColor(PrefHandler::Default()->getRGB(PREF_CURSOR_FORE_COLOR),
488 				PrefHandler::Default()->getRGB(PREF_CURSOR_BACK_COLOR));
489 }
490 
491 
492 status_t
493 TermWindow::_DoPageSetup()
494 {
495 	BPrintJob job("PageSetup");
496 
497 	// display the page configure panel
498 	status_t status = job.ConfigPage();
499 
500 	// save a pointer to the settings
501 	fPrintSettings = job.Settings();
502 
503 	return status;
504 }
505 
506 
507 void
508 TermWindow::_DoPrint()
509 {
510 	if (!fPrintSettings || (_DoPageSetup() != B_OK)) {
511 		(new BAlert("Cancel", "Print cancelled.", "OK"))->Go();
512 		return;
513 	}
514 
515 	BPrintJob job("Print");
516 	job.SetSettings(new BMessage(*fPrintSettings));
517 
518 	BRect pageRect = job.PrintableRect();
519 	BRect curPageRect = pageRect;
520 
521 	int pHeight = (int)pageRect.Height();
522 	int pWidth = (int)pageRect.Width();
523 	float w,h;
524 	_ActiveTermView()->GetFrameSize(&w, &h);
525 	int xPages = (int)ceil(w / pWidth);
526 	int yPages = (int)ceil(h / pHeight);
527 
528 	job.BeginJob();
529 
530 	// loop through and draw each page, and write to spool
531 	for (int x = 0; x < xPages; x++) {
532 		for (int y = 0; y < yPages; y++) {
533 			curPageRect.OffsetTo(x * pWidth, y * pHeight);
534 			job.DrawView(_ActiveTermView(), curPageRect, B_ORIGIN);
535 			job.SpoolPage();
536 
537 			if (!job.CanContinue()){
538 				// It is likely that the only way that the job was cancelled is
539 			      	// because the user hit 'Cancel' in the page setup window, in which
540 			      	// case, the user does *not* need to be told that it was cancelled.
541 			      	// He/she will simply expect that it was done.
542 				// (new BAlert("Cancel", "Print job cancelled", "OK"))->Go();
543 			        return;
544 			}
545 		}
546 	}
547 
548 	job.CommitJob();
549 }
550 
551 
552 void
553 TermWindow::_AddTab(Arguments *args)
554 {
555 	int argc = 0;
556 	const char *const *argv = NULL;
557 	if (args != NULL)
558 		args->GetShellArguments(argc, argv);
559 
560 	try {
561 		// Note: I don't pass the Arguments class directly to the termview,
562 		// only to avoid adding it as a dependency: in other words, to keep
563 		// the TermView class as agnostic as possible about the surrounding world.
564 		CustomTermView *view =
565 			new CustomTermView(PrefHandler::Default()->getInt32(PREF_ROWS),
566 							PrefHandler::Default()->getInt32(PREF_COLS),
567 							argc, (const char **)argv);
568 
569 		BScrollView *scrollView = new BScrollView("scrollView", view, B_FOLLOW_ALL,
570 									B_WILL_DRAW|B_FRAME_EVENTS, false, true);
571 
572 		BTab *tab = new BTab;
573 		// TODO: Use a better name. For example, do like MacOsX's Terminal
574 		// and update the title using the last executed command ?
575 		// Or like Gnome's Terminal and use the current path ?
576 		fTabView->AddTab(scrollView, tab);
577 		tab->SetLabel("Terminal");
578 		view->SetScrollBar(scrollView->ScrollBar(B_VERTICAL));
579 
580 		// Resize the vertical scrollbar to take the window gripping handle into account
581 		// TODO: shouldn't this be done in BScrollView itself ? At least BScrollBar does that.
582 		scrollView->ScrollBar(B_VERTICAL)->ResizeBy(0, -13);
583 
584 		view->SetEncoding(EncodingID(PrefHandler::Default()->getString(PREF_TEXT_ENCODING)));
585 
586 		BFont font;
587 		_GetPreferredFont(font);
588 		view->SetTermFont(&font);
589 
590 		_SetTermColors(view);
591 
592 		int width, height;
593 		view->GetFontSize(&width, &height);
594 
595 		float minimumHeight = 0;
596 		if (fMenubar)
597 			minimumHeight += fMenubar->Bounds().Height();
598 		if (fTabView && fTabView->CountTabs() > 1)
599 			minimumHeight += fTabView->TabHeight();
600 		SetSizeLimits(MIN_COLS * width, MAX_COLS * width,
601 						minimumHeight + MIN_ROWS * height,
602 						minimumHeight + MAX_ROWS * height);
603 
604 		// If it's the first time we're called, setup the window
605 		if (fTabView->CountTabs() == 1) {
606 			float viewWidth, viewHeight;
607 			view->GetPreferredSize(&viewWidth, &viewHeight);
608 
609 			// Resize Window
610 			ResizeTo(viewWidth + B_V_SCROLL_BAR_WIDTH,
611 					viewHeight + fMenubar->Bounds().Height());
612 		}
613 	} catch (...) {
614 		// most probably out of memory. That's bad.
615 		// TODO: Should cleanup, I guess
616 	}
617 }
618 
619 
620 void
621 TermWindow::_RemoveTab(int32 index)
622 {
623 	if (fTabView->CountTabs() > 1)
624 		delete fTabView->RemoveTab(index);
625 	else
626 		PostMessage(B_QUIT_REQUESTED);
627 }
628 
629 
630 TermView *
631 TermWindow::_ActiveTermView()
632 {
633 	// TODO: BAD HACK:
634 	// We should probably use the observer api to tell
635 	// the various "tabs" when settings are changed. Fix this.
636 	return (TermView *)((BScrollView *)fTabView->ViewForTab(fTabView->Selection()))->Target();
637 }
638 
639 
640 int32
641 TermWindow::_IndexOfTermView(TermView* termView) const
642 {
643 	if (!termView)
644 		return -1;
645 
646 	// find the view
647 	int32 count = fTabView->CountTabs();
648 	for (int32 i = count - 1; i >= 0; i--) {
649 		BScrollView* scrollView
650 			= dynamic_cast<BScrollView*>(fTabView->ViewForTab(i));
651 		if (!scrollView)
652 			continue;
653 
654 		if (termView == scrollView->Target())
655 			return i;
656 	}
657 
658 	return -1;
659 }
660 
661 
662 void
663 TermWindow::_CheckChildren()
664 {
665 	// There seems to be no separate list of sessions, so we have to iterate
666 	// through the tabs.
667 	int32 count = fTabView->CountTabs();
668 	for (int32 i = count - 1; i >= 0; i--) {
669 		// get the term view
670 		BScrollView* scrollView
671 			= dynamic_cast<BScrollView*>(fTabView->ViewForTab(i));
672 		if (!scrollView)
673 			continue;
674 		TermView* termView = dynamic_cast<TermView*>(scrollView->Target());
675 		if (!termView)
676 			continue;
677 
678 		termView->CheckShellGone();
679 	}
680 }
681 
682 
683 void
684 TermWindow::_ResizeView(TermView *view)
685 {
686 	int fontWidth, fontHeight;
687 	view->GetFontSize(&fontWidth, &fontHeight);
688 
689 	float minimumHeight = 0;
690 	if (fMenubar)
691 		minimumHeight += fMenubar->Bounds().Height();
692 	if (fTabView && fTabView->CountTabs() > 1)
693 		minimumHeight += fTabView->TabHeight();
694 
695 	SetSizeLimits(MIN_COLS * fontWidth, MAX_COLS * fontWidth,
696 					minimumHeight + MIN_ROWS * fontHeight,
697 					minimumHeight + MAX_ROWS * fontHeight);
698 
699 	float width, height;
700 	view->GetPreferredSize(&width, &height);
701 	width += B_V_SCROLL_BAR_WIDTH;
702 	height += fMenubar->Bounds().Height() + 2;
703 
704 	ResizeTo(width, height);
705 
706 	view->Invalidate();
707 }
708 
709 
710 void
711 TermWindow::_BuildWindowSizeMenu(BMenu *menu)
712 {
713 	const int32 windowSizes[5][2] = {
714 		{ 80, 24 },
715 		{ 80, 25 },
716 		{ 80, 40 },
717 		{ 132, 24 },
718 		{ 132, 25 }
719 	};
720 
721 	const int32 sizeNum = sizeof(windowSizes) / sizeof(windowSizes[0]);
722 	for (int32 i = 0; i < sizeNum; i++) {
723 		char label[32];
724 		int32 columns = windowSizes[i][0];
725 		int32 rows = windowSizes[i][1];
726 		snprintf(label, sizeof(label), "%ldx%ld", columns, rows);
727 		BMessage *message = new BMessage(MSG_COLS_CHANGED);
728 		message->AddInt32("columns", columns);
729 		message->AddInt32("rows", rows);
730 		menu->AddItem(new BMenuItem(label, message));
731 	}
732 
733 	menu->AddItem(new BMenuItem("Fullscreen",
734 					new BMessage(FULLSCREEN), B_ENTER));
735 }
736 
737 
738 // CustomTermView
739 CustomTermView::CustomTermView(int32 rows, int32 columns, int32 argc, const char **argv, int32 historySize)
740 	:
741 	TermView(rows, columns, argc, argv, historySize)
742 {
743 }
744 
745 
746 void
747 CustomTermView::NotifyQuit(int32 reason)
748 {
749 	BWindow *window = Window();
750 	if (window == NULL)
751 		window = be_app->WindowAt(0);
752 
753 	// TODO: If we got this from a view in a tab not currently selected,
754 	// Window() will be NULL, as the view is detached.
755 	// So we send the message to the first application window
756 	// This isn't so cool, but for now, a Terminal app has only one
757 	// window.
758 	if (window != NULL) {
759 		BMessage message(kCloseView);
760 		message.AddPointer("termView", this);
761 		message.AddInt32("reason", reason);
762 		window->PostMessage(&message);
763 	}
764 }
765 
766 
767 void
768 CustomTermView::SetTitle(const char *title)
769 {
770 	//Window()->SetTitle(title);
771 }
772 
773