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