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