xref: /haiku/src/apps/terminal/TermWindow.cpp (revision 072d3935c2497638e9c2502f574c133caeba9d3d)
1 /*
2  * Copyright 2007-2015, Haiku, Inc. All rights reserved.
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  * Authors:
10  *		Kian Duffy, myob@users.sourceforge.net
11  *		Daniel Furrer, assimil8or@users.sourceforge.net
12  *		John Scipione, jscipione@gmail.com
13  *		Simon South, simon@simonsouth.net
14  *		Siarzhuk Zharski, zharik@gmx.li
15  */
16 
17 
18 #include "TermWindow.h"
19 
20 #include <new>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <strings.h>
24 #include <time.h>
25 
26 #include <Alert.h>
27 #include <Application.h>
28 #include <Catalog.h>
29 #include <CharacterSet.h>
30 #include <CharacterSetRoster.h>
31 #include <Clipboard.h>
32 #include <Dragger.h>
33 #include <File.h>
34 #include <FindDirectory.h>
35 #include <Keymap.h>
36 #include <LayoutBuilder.h>
37 #include <LayoutUtils.h>
38 #include <Locale.h>
39 #include <Menu.h>
40 #include <MenuBar.h>
41 #include <MenuItem.h>
42 #include <ObjectList.h>
43 #include <Path.h>
44 #include <PopUpMenu.h>
45 #include <PrintJob.h>
46 #include <Rect.h>
47 #include <Roster.h>
48 #include <Screen.h>
49 #include <ScrollBar.h>
50 #include <ScrollView.h>
51 #include <String.h>
52 #include <UnicodeChar.h>
53 #include <UTF8.h>
54 
55 #include <AutoLocker.h>
56 
57 #include "ActiveProcessInfo.h"
58 #include "Arguments.h"
59 #include "AppearPrefView.h"
60 #include "FindWindow.h"
61 #include "Globals.h"
62 #include "PrefWindow.h"
63 #include "PrefHandler.h"
64 #include "SetTitleDialog.h"
65 #include "ShellParameters.h"
66 #include "TermConst.h"
67 #include "TermScrollView.h"
68 #include "TitlePlaceholderMapper.h"
69 
70 
71 const static int32 kTermViewOffset = 3;
72 
73 const static int32 kMinimumFontSize = 8;
74 const static int32 kMaximumFontSize = 36;
75 
76 // messages constants
77 static const uint32 kNewTab = 'NTab';
78 static const uint32 kCloseView = 'ClVw';
79 static const uint32 kCloseOtherViews = 'CloV';
80 static const uint32 kIncreaseFontSize = 'InFs';
81 static const uint32 kDecreaseFontSize = 'DcFs';
82 static const uint32 kSetActiveTab = 'STab';
83 static const uint32 kUpdateTitles = 'UPti';
84 static const uint32 kEditTabTitle = 'ETti';
85 static const uint32 kEditWindowTitle = 'EWti';
86 static const uint32 kTabTitleChanged = 'TTch';
87 static const uint32 kWindowTitleChanged = 'WTch';
88 static const uint32 kUpdateSwitchTerminalsMenuItem = 'Ustm';
89 
90 using namespace BPrivate ; // BCharacterSet stuff
91 
92 #undef B_TRANSLATION_CONTEXT
93 #define B_TRANSLATION_CONTEXT "Terminal TermWindow"
94 
95 // actually an arrow
96 #define UTF8_ENTER "\xe2\x86\xb5"
97 
98 
99 // #pragma mark - TermViewContainerView
100 
101 
102 class TermViewContainerView : public BView {
103 public:
104 	TermViewContainerView(TermView* termView)
105 		:
106 		BView(BRect(), "term view container", B_FOLLOW_ALL, 0),
107 		fTermView(termView)
108 	{
109 		termView->MoveTo(kTermViewOffset, kTermViewOffset);
110 		BRect frame(termView->Frame());
111 		ResizeTo(frame.right + kTermViewOffset, frame.bottom + kTermViewOffset);
112 		AddChild(termView);
113 	}
114 
115 	TermView* GetTermView() const	{ return fTermView; }
116 
117 	virtual void GetPreferredSize(float* _width, float* _height)
118 	{
119 		float width, height;
120 		fTermView->GetPreferredSize(&width, &height);
121 		*_width = width + 2 * kTermViewOffset;
122 		*_height = height + 2 * kTermViewOffset;
123 	}
124 
125 private:
126 	TermView*	fTermView;
127 };
128 
129 
130 // #pragma mark - SessionID
131 
132 
133 TermWindow::SessionID::SessionID(int32 id)
134 	:
135 	fID(id)
136 {
137 }
138 
139 
140 TermWindow::SessionID::SessionID(const BMessage& message, const char* field)
141 {
142 	if (message.FindInt32(field, &fID) != B_OK)
143 		fID = -1;
144 }
145 
146 
147 status_t
148 TermWindow::SessionID::AddToMessage(BMessage& message, const char* field) const
149 {
150 	return message.AddInt32(field, fID);
151 }
152 
153 
154 // #pragma mark - Session
155 
156 
157 struct TermWindow::Session {
158 	SessionID				id;
159 	int32					index;
160 	Title					title;
161 	TermViewContainerView*	containerView;
162 
163 	Session(SessionID id, int32 index, TermViewContainerView* containerView)
164 		:
165 		id(id),
166 		index(index),
167 		containerView(containerView)
168 	{
169 		title.title = B_TRANSLATE("Shell ");
170 		title.title << index;
171 		title.patternUserDefined = false;
172 	}
173 };
174 
175 
176 // #pragma mark - TermWindow
177 
178 
179 TermWindow::TermWindow(const BString& title, Arguments* args)
180 	:
181 	BWindow(BRect(0, 0, 0, 0), title, B_DOCUMENT_WINDOW,
182 		B_CURRENT_WORKSPACE | B_QUIT_ON_WINDOW_CLOSE),
183 	fTitleUpdateRunner(this, BMessage(kUpdateTitles), 1000000),
184 	fNextSessionID(0),
185 	fTabView(NULL),
186 	fMenuBar(NULL),
187 	fSwitchTerminalsMenuItem(NULL),
188 	fEncodingMenu(NULL),
189 	fPrintSettings(NULL),
190 	fPrefWindow(NULL),
191 	fFindPanel(NULL),
192 	fSavedFrame(0, 0, -1, -1),
193 	fSetWindowTitleDialog(NULL),
194 	fSetTabTitleDialog(NULL),
195 	fFindString(""),
196 	fFindNextMenuItem(NULL),
197 	fFindPreviousMenuItem(NULL),
198 	fFindSelection(false),
199 	fForwardSearch(false),
200 	fMatchCase(false),
201 	fMatchWord(false),
202 	fFullScreen(false)
203 {
204 	// register this terminal
205 	fTerminalRoster.Register(Team(), this);
206 	fTerminalRoster.SetListener(this);
207 	int32 id = fTerminalRoster.ID();
208 
209 	// fetch the current keymap
210 	get_key_map(&fKeymap, &fKeymapChars);
211 
212 	// apply the title settings
213 	fTitle.pattern = title;
214 	if (fTitle.pattern.Length() == 0) {
215 		fTitle.pattern = B_TRANSLATE_SYSTEM_NAME("Terminal");
216 
217 		if (id >= 0)
218 			fTitle.pattern << " " << id + 1;
219 
220 		fTitle.patternUserDefined = false;
221 	} else
222 		fTitle.patternUserDefined = true;
223 
224 	fTitle.title = fTitle.pattern;
225 	fTitle.pattern = title;
226 
227 	_TitleSettingsChanged();
228 
229 	// get the saved window position and workspaces
230 	BRect frame;
231 	uint32 workspaces;
232 	if (_LoadWindowPosition(&frame, &workspaces) == B_OK) {
233 		// make sure the window is still on screen
234 		// (for example if there was a resolution change)
235 		BRect screenFrame = BScreen(this).Frame();
236 		if (frame.Width() <= screenFrame.Width()
237 			&& frame.Height() <= screenFrame.Height())
238 			ResizeTo(frame.Width(), frame.Height());
239 
240 		MoveTo(frame.LeftTop());
241 		MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN);
242 
243 		SetWorkspaces(workspaces);
244 	} else {
245 		// use computed defaults
246 		int row = id / 16;
247 		int column = id % 16;
248 		int x = (column * 16) + (row * 64) + 50;
249 		int y = (column * 16) + 50;
250 
251 		MoveTo(x, y);
252 	}
253 
254 	// init the GUI and add a tab
255 	_InitWindow();
256 	_AddTab(args);
257 
258 	// Announce our window as no longer minimized. That's not true, since it's
259 	// still hidden at this point, but it will be shown very soon.
260 	fTerminalRoster.SetWindowInfo(false, Workspaces());
261 }
262 
263 
264 TermWindow::~TermWindow()
265 {
266 	fTerminalRoster.Unregister();
267 
268 	_FinishTitleDialog();
269 
270 	if (fPrefWindow)
271 		fPrefWindow->PostMessage(B_QUIT_REQUESTED);
272 
273 	if (fFindPanel && fFindPanel->Lock()) {
274 		fFindPanel->Quit();
275 		fFindPanel = NULL;
276 	}
277 
278 	PrefHandler::DeleteDefault();
279 
280 	for (int32 i = 0; Session* session = _SessionAt(i); i++)
281 		delete session;
282 
283 	delete fKeymap;
284 	delete[] fKeymapChars;
285 }
286 
287 
288 void
289 TermWindow::SessionChanged()
290 {
291 	_UpdateSessionTitle(fTabView->Selection());
292 }
293 
294 
295 void
296 TermWindow::_InitWindow()
297 {
298 	// make menu bar
299 	_SetupMenu();
300 
301 	// shortcuts to switch tabs
302 	for (int32 i = 0; i < 9; i++) {
303 		BMessage* message = new BMessage(kSetActiveTab);
304 		message->AddInt32("index", i);
305 		AddShortcut('1' + i, B_COMMAND_KEY, message);
306 	}
307 
308 	AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_SHIFT_KEY,
309 		new BMessage(MSG_MOVE_TAB_LEFT));
310 	AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_SHIFT_KEY,
311 		new BMessage(MSG_MOVE_TAB_RIGHT));
312 
313 	BRect textFrame = Bounds();
314 	textFrame.top = fMenuBar->Bounds().bottom + 1.0;
315 
316 	fTabView = new SmartTabView(textFrame, "tab view", B_WIDTH_FROM_LABEL);
317 	fTabView->SetListener(this);
318 	AddChild(fTabView);
319 
320 	// Make the scroll view one pixel wider than the tab view container view, so
321 	// the scroll bar will look good.
322 	fTabView->SetInsets(0, 0, -1, 0);
323 }
324 
325 
326 bool
327 TermWindow::_CanClose(int32 index)
328 {
329 	bool warnOnExit = PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT);
330 
331 	if (!warnOnExit)
332 		return true;
333 
334 	uint32 busyProcessCount = 0;
335 	BString busyProcessNames;
336 		// all names, separated by "\n\t"
337 
338 	if (index != -1) {
339 		ShellInfo shellInfo;
340 		ActiveProcessInfo info;
341 		TermView* termView = _TermViewAt(index);
342 		if (termView->GetShellInfo(shellInfo)
343 			&& termView->GetActiveProcessInfo(info)
344 			&& (info.ID() != shellInfo.ProcessID()
345 				|| !shellInfo.IsDefaultShell())) {
346 			busyProcessCount++;
347 			busyProcessNames = info.Name();
348 		}
349 	} else {
350 		for (int32 i = 0; i < fSessions.CountItems(); i++) {
351 			ShellInfo shellInfo;
352 			ActiveProcessInfo info;
353 			TermView* termView = _TermViewAt(i);
354 			if (termView->GetShellInfo(shellInfo)
355 				&& termView->GetActiveProcessInfo(info)
356 				&& (info.ID() != shellInfo.ProcessID()
357 					|| !shellInfo.IsDefaultShell())) {
358 				if (++busyProcessCount > 1)
359 					busyProcessNames << "\n\t";
360 				busyProcessNames << info.Name();
361 			}
362 		}
363 	}
364 
365 	if (busyProcessCount == 0)
366 		return true;
367 
368 	BString alertMessage;
369 	if (busyProcessCount == 1) {
370 		// Only one pending process. Select the alert text depending on whether
371 		// the terminal will be closed.
372 		alertMessage = index == -1 || fSessions.CountItems() == 1
373 			? B_TRANSLATE("The process \"%1\" is still running.\n"
374 				"If you close the Terminal, the process will be killed.")
375 			: B_TRANSLATE("The process \"%1\" is still running.\n"
376 				"If you close the tab, the process will be killed.");
377 	} else {
378 		// multiple pending processes
379 		alertMessage = B_TRANSLATE(
380 			"The following processes are still running:\n\n"
381 			"\t%1\n\n"
382 			"If you close the Terminal, the processes will be killed.");
383 	}
384 
385 	alertMessage.ReplaceFirst("%1", busyProcessNames);
386 
387 	BAlert* alert = new BAlert(B_TRANSLATE("Really close?"),
388 		alertMessage, B_TRANSLATE("Close"), B_TRANSLATE("Cancel"), NULL,
389 		B_WIDTH_AS_USUAL, B_WARNING_ALERT);
390 	alert->SetShortcut(1, B_ESCAPE);
391 	return alert->Go() == 0;
392 }
393 
394 
395 bool
396 TermWindow::QuitRequested()
397 {
398 	_FinishTitleDialog();
399 
400 	if (!_CanClose(-1))
401 		return false;
402 
403 	_SaveWindowPosition();
404 
405 	return BWindow::QuitRequested();
406 }
407 
408 
409 void
410 TermWindow::MenusBeginning()
411 {
412 	TermView* view = _ActiveTermView();
413 
414 	// Syncronize Encode Menu Pop-up menu and Preference.
415 	const BCharacterSet* charset
416 		= BCharacterSetRoster::GetCharacterSetByConversionID(view->Encoding());
417 	if (charset != NULL) {
418 		BString name(charset->GetPrintName());
419 		const char* mime = charset->GetMIMEName();
420 		if (mime)
421 			name << " (" << mime << ")";
422 
423 		BMenuItem* item = fEncodingMenu->FindItem(name);
424 		if (item != NULL)
425 			item->SetMarked(true);
426 	}
427 
428 	BFont font;
429 	view->GetTermFont(&font);
430 
431 	float size = font.Size();
432 
433 	fDecreaseFontSizeMenuItem->SetEnabled(size > kMinimumFontSize);
434 	fIncreaseFontSizeMenuItem->SetEnabled(size < kMaximumFontSize);
435 
436 	BWindow::MenusBeginning();
437 }
438 
439 
440 /* static */ void
441 TermWindow::MakeEncodingMenu(BMenu* menu)
442 {
443 	BCharacterSetRoster roster;
444 	BCharacterSet charset;
445 	while (roster.GetNextCharacterSet(&charset) == B_OK) {
446 		int encoding = M_UTF8;
447 		const char* mime = charset.GetMIMEName();
448 		if (mime == NULL || strcasecmp(mime, "UTF-8") != 0)
449 			encoding = charset.GetConversionID();
450 
451 		// filter out currently (???) not supported USC-2 and UTF-16
452 		if (encoding == B_UTF16_CONVERSION || encoding == B_UNICODE_CONVERSION)
453 			continue;
454 
455 		BString name(charset.GetPrintName());
456 		if (mime)
457 			name << " (" << mime << ")";
458 
459 		BMessage *message = new BMessage(MENU_ENCODING);
460 		if (message != NULL) {
461 			message->AddInt32("op", (int32)encoding);
462 			menu->AddItem(new BMenuItem(name, message));
463 		}
464 	}
465 
466 	menu->SetRadioMode(true);
467 }
468 
469 
470 void
471 TermWindow::_SetupMenu()
472 {
473 	fFontSizeMenu = _MakeFontSizeMenu(MSG_HALF_SIZE_CHANGED,
474 		PrefHandler::Default()->getInt32(PREF_HALF_FONT_SIZE));
475 	fIncreaseFontSizeMenuItem = new BMenuItem(B_TRANSLATE("Increase"),
476 		new BMessage(kIncreaseFontSize), '+', B_COMMAND_KEY);
477 	fDecreaseFontSizeMenuItem = new BMenuItem(B_TRANSLATE("Decrease"),
478 		new BMessage(kDecreaseFontSize), '-', B_COMMAND_KEY);
479 	fFontSizeMenu->AddSeparatorItem();
480 	fFontSizeMenu->AddItem(fIncreaseFontSizeMenuItem);
481 	fFontSizeMenu->AddItem(fDecreaseFontSizeMenuItem);
482 
483 	BMenu* windowSize = new(std::nothrow) BMenu(B_TRANSLATE("Window size"));
484 	if (windowSize != NULL) {
485 		MakeWindowSizeMenu(windowSize);
486 		windowSize->AddSeparatorItem();
487 		windowSize->AddItem(new BMenuItem(B_TRANSLATE("Full screen"),
488 			new BMessage(FULLSCREEN), B_ENTER));
489 	}
490 
491 	fEncodingMenu = new(std::nothrow) BMenu(B_TRANSLATE("Text encoding"));
492 	if (fEncodingMenu != NULL)
493 		MakeEncodingMenu(fEncodingMenu);
494 
495 	BLayoutBuilder::Menu<>(fMenuBar = new BMenuBar(Bounds(), "mbar"))
496 		// Terminal
497 		.AddMenu(B_TRANSLATE_COMMENT("Terminal", "The title for the main window"
498 				" menubar entry related to terminal sessions"))
499 			.AddItem(B_TRANSLATE("Switch Terminals"), MENU_SWITCH_TERM, B_TAB)
500 				.GetItem(fSwitchTerminalsMenuItem)
501 			.AddItem(B_TRANSLATE("New Terminal"), MENU_NEW_TERM, 'N')
502 			.AddItem(B_TRANSLATE("New tab"), kNewTab, 'T')
503 			.AddSeparator()
504 			.AddItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS), MENU_PAGE_SETUP)
505 			.AddItem(B_TRANSLATE("Print"), MENU_PRINT, 'P')
506 			.AddSeparator()
507 			.AddItem(B_TRANSLATE("Close window"), B_QUIT_REQUESTED, 'W',
508 				B_SHIFT_KEY)
509 			.AddItem(B_TRANSLATE("Close active tab"), kCloseView, 'W')
510 			.AddItem(B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q')
511 		.End()
512 
513 		// Edit
514 		.AddMenu(B_TRANSLATE("Edit"))
515 			.AddItem(B_TRANSLATE("Copy"), B_COPY, 'C')
516 			.AddItem(B_TRANSLATE("Paste"), B_PASTE, 'V')
517 			.AddSeparator()
518 			.AddItem(B_TRANSLATE("Select all"), B_SELECT_ALL, 'A')
519 			.AddItem(B_TRANSLATE("Clear all"), MENU_CLEAR_ALL, 'L')
520 			.AddSeparator()
521 			.AddItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), MENU_FIND_STRING, 'F')
522 			.AddItem(B_TRANSLATE("Find previous"), MENU_FIND_PREVIOUS, 'G',
523 					B_SHIFT_KEY)
524 				.GetItem(fFindPreviousMenuItem)
525 				.SetEnabled(false)
526 			.AddItem(B_TRANSLATE("Find next"), MENU_FIND_NEXT, 'G')
527 				.GetItem(fFindNextMenuItem)
528 				.SetEnabled(false)
529 		.End()
530 
531 		// Settings
532 		.AddMenu(B_TRANSLATE("Settings"))
533 			.AddItem(B_TRANSLATE("Window title" B_UTF8_ELLIPSIS),
534 				kEditWindowTitle)
535 			.AddItem(windowSize)
536 			.AddItem(fEncodingMenu)
537 			.AddItem(fFontSizeMenu)
538 			.AddItem(B_TRANSLATE("Save as default"), MSG_SAVE_AS_DEFAULT)
539 			.AddSeparator()
540 			.AddItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), MENU_PREF_OPEN,
541 				',')
542 		.End();
543 
544 	AddChild(fMenuBar);
545 
546 	_UpdateSwitchTerminalsMenuItem();
547 
548 #ifdef USE_DEBUG_SNAPSHOTS
549 	AddShortcut('S', B_COMMAND_KEY | B_CONTROL_KEY,
550 		new BMessage(SHORTCUT_DEBUG_SNAPSHOTS));
551 	AddShortcut('C', B_COMMAND_KEY | B_CONTROL_KEY,
552 		new BMessage(SHORTCUT_DEBUG_CAPTURE));
553 #endif
554 
555 	BKeymap keymap;
556 	keymap.SetToCurrent();
557 	BObjectList<const char> unmodified(3, true);
558 	if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified)
559 			== B_OK) {
560 		int32 count = unmodified.CountItems();
561 		for (int32 i = 0; i < count; i++) {
562 			uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i));
563 			if (!HasShortcut(key, 0)) {
564 				// Add semantic + shortcut, bug #7428
565 				AddShortcut(key, B_COMMAND_KEY,
566 					new BMessage(kIncreaseFontSize));
567 			}
568 		}
569 	}
570 	unmodified.MakeEmpty();
571 }
572 
573 
574 status_t
575 TermWindow::_GetWindowPositionFile(BFile* file, uint32 openMode)
576 {
577 	BPath path;
578 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
579 	if (status != B_OK)
580 		return status;
581 
582 	status = path.Append("Terminal");
583 	if (status != B_OK)
584 		return status;
585 
586 	status = path.Append("Windows");
587 	if (status != B_OK)
588 		return status;
589 
590 	return file->SetTo(path.Path(), openMode);
591 }
592 
593 
594 status_t
595 TermWindow::_LoadWindowPosition(BRect* frame, uint32* workspaces)
596 {
597 	status_t status;
598 	BMessage position;
599 
600 	BFile file;
601 	status = _GetWindowPositionFile(&file, B_READ_ONLY);
602 	if (status != B_OK)
603 		return status;
604 
605 	status = position.Unflatten(&file);
606 
607 	file.Unset();
608 
609 	if (status != B_OK)
610 		return status;
611 
612 	int32 id = fTerminalRoster.ID();
613 	status = position.FindRect("rect", id, frame);
614 	if (status != B_OK)
615 		return status;
616 
617 	int32 _workspaces;
618 	status = position.FindInt32("workspaces", id, &_workspaces);
619 	if (status != B_OK)
620 		return status;
621 	if (modifiers() & B_SHIFT_KEY)
622 		*workspaces = _workspaces;
623 	else
624 		*workspaces = B_CURRENT_WORKSPACE;
625 
626 	return B_OK;
627 }
628 
629 
630 status_t
631 TermWindow::_SaveWindowPosition()
632 {
633 	BFile file;
634 	BMessage originalSettings;
635 
636 	// Read the settings file if it exists and is a valid BMessage.
637 	status_t status = _GetWindowPositionFile(&file, B_READ_ONLY);
638 	if (status == B_OK) {
639 		status = originalSettings.Unflatten(&file);
640 		file.Unset();
641 
642 		if (status != B_OK)
643 			status = originalSettings.MakeEmpty();
644 
645 		if (status != B_OK)
646 			return status;
647 	}
648 
649 	// Replace the settings
650 	int32 id = fTerminalRoster.ID();
651 	BRect rect(Frame());
652 	if (originalSettings.ReplaceRect("rect", id, rect) != B_OK)
653 		originalSettings.AddRect("rect", rect);
654 
655 	int32 workspaces = Workspaces();
656 	if (originalSettings.ReplaceInt32("workspaces", id, workspaces) != B_OK)
657 		originalSettings.AddInt32("workspaces", workspaces);
658 
659 	// Resave the whole thing
660 	status = _GetWindowPositionFile (&file,
661 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
662 	if (status != B_OK)
663 		return status;
664 
665 	return originalSettings.Flatten(&file);
666 }
667 
668 
669 void
670 TermWindow::_GetPreferredFont(BFont& font)
671 {
672 	// Default to be_fixed_font
673 	font = be_fixed_font;
674 
675 	const char* family
676 		= PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY);
677 	const char* style
678 		= PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE);
679 	const char* size = PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE);
680 
681 	font.SetFamilyAndStyle(family, style);
682 	font.SetSize(atoi(size));
683 
684 	// mark the font size menu item
685 	for (int32 i = 0; i < fFontSizeMenu->CountItems(); i++) {
686 		BMenuItem* item = fFontSizeMenu->ItemAt(i);
687 		if (item == NULL)
688 			continue;
689 
690 		item->SetMarked(false);
691 		if (strcmp(item->Label(), size) == 0)
692 			item->SetMarked(true);
693 	}
694 }
695 
696 
697 void
698 TermWindow::MessageReceived(BMessage *message)
699 {
700 	int32 encodingId;
701 	bool findresult;
702 
703 	switch (message->what) {
704 		case B_KEY_MAP_LOADED:
705 			_UpdateKeymap();
706 			break;
707 
708 		case B_COPY:
709 			_ActiveTermView()->Copy(be_clipboard);
710 			break;
711 
712 		case B_PASTE:
713 			_ActiveTermView()->Paste(be_clipboard);
714 			break;
715 
716 #ifdef USE_DEBUG_SNAPSHOTS
717 		case SHORTCUT_DEBUG_SNAPSHOTS:
718 			_ActiveTermView()->MakeDebugSnapshots();
719 			break;
720 
721 		case SHORTCUT_DEBUG_CAPTURE:
722 			_ActiveTermView()->StartStopDebugCapture();
723 			break;
724 #endif
725 
726 		case B_SELECT_ALL:
727 			_ActiveTermView()->SelectAll();
728 			break;
729 
730 		case MENU_CLEAR_ALL:
731 			_ActiveTermView()->Clear();
732 			break;
733 
734 		case MENU_SWITCH_TERM:
735 			_SwitchTerminal();
736 			break;
737 
738 		case MENU_NEW_TERM:
739 		{
740 			// Set our current working directory to that of the active tab, so
741 			// that the new terminal and its shell inherit it.
742 			// Note: That's a bit lame. We should rather fork() and change the
743 			// CWD in the child, but since ATM there aren't any side effects of
744 			// changing our CWD, we save ourselves the trouble.
745 			ActiveProcessInfo activeProcessInfo;
746 			if (_ActiveTermView()->GetActiveProcessInfo(activeProcessInfo))
747 				chdir(activeProcessInfo.CurrentDirectory());
748 
749 			app_info info;
750 			be_app->GetAppInfo(&info);
751 
752 			// try launching two different ways to work around possible problems
753 			if (be_roster->Launch(&info.ref) != B_OK)
754 				be_roster->Launch(TERM_SIGNATURE);
755 			break;
756 		}
757 
758 		case MENU_PREF_OPEN:
759 			if (!fPrefWindow) {
760 				fPrefWindow = new PrefWindow(this);
761 			} else
762 				fPrefWindow->Activate();
763 			break;
764 
765 		case MSG_PREF_CLOSED:
766 			fPrefWindow = NULL;
767 			break;
768 
769 		case MSG_WINDOW_TITLE_SETTING_CHANGED:
770 		case MSG_TAB_TITLE_SETTING_CHANGED:
771 			_TitleSettingsChanged();
772 			break;
773 
774 		case MENU_FIND_STRING:
775 			if (fFindPanel == NULL) {
776 				fFindPanel = new FindWindow(this, fFindString, fFindSelection,
777 					fMatchWord, fMatchCase, fForwardSearch);
778 
779 				fFindPanel->CenterIn(Frame());
780 				_MoveWindowInScreen(fFindPanel);
781 				fFindPanel->Show();
782 			} else
783 				fFindPanel->Activate();
784 			break;
785 
786 		case MSG_FIND:
787 		{
788 			fFindPanel->PostMessage(B_QUIT_REQUESTED);
789 			message->FindBool("findselection", &fFindSelection);
790 			if (!fFindSelection)
791 				message->FindString("findstring", &fFindString);
792 			else
793 				_ActiveTermView()->GetSelection(fFindString);
794 
795 			if (fFindString.Length() == 0) {
796 				const char* errorMsg = !fFindSelection
797 					? B_TRANSLATE("No search string was entered.")
798 					: B_TRANSLATE("Nothing is selected.");
799 				BAlert* alert = new BAlert(B_TRANSLATE("Find failed"),
800 					errorMsg, B_TRANSLATE("OK"), NULL, NULL,
801 					B_WIDTH_AS_USUAL, B_WARNING_ALERT);
802 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
803 
804 				alert->Go();
805 				fFindPreviousMenuItem->SetEnabled(false);
806 				fFindNextMenuItem->SetEnabled(false);
807 				break;
808 			}
809 
810 			message->FindBool("forwardsearch", &fForwardSearch);
811 			message->FindBool("matchcase", &fMatchCase);
812 			message->FindBool("matchword", &fMatchWord);
813 			findresult = _ActiveTermView()->Find(fFindString, fForwardSearch,
814 				fMatchCase, fMatchWord);
815 
816 			if (!findresult) {
817 				BAlert* alert = new BAlert(B_TRANSLATE("Find failed"),
818 					B_TRANSLATE("Text not found."),
819 					B_TRANSLATE("OK"), NULL, NULL,
820 					B_WIDTH_AS_USUAL, B_WARNING_ALERT);
821 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
822 				alert->Go();
823 				fFindPreviousMenuItem->SetEnabled(false);
824 				fFindNextMenuItem->SetEnabled(false);
825 				break;
826 			}
827 
828 			// Enable the menu items Find Next and Find Previous
829 			fFindPreviousMenuItem->SetEnabled(true);
830 			fFindNextMenuItem->SetEnabled(true);
831 			break;
832 		}
833 
834 		case MENU_FIND_NEXT:
835 		case MENU_FIND_PREVIOUS:
836 			findresult = _ActiveTermView()->Find(fFindString,
837 				(message->what == MENU_FIND_NEXT) == fForwardSearch,
838 				fMatchCase, fMatchWord);
839 			if (!findresult) {
840 				BAlert* alert = new BAlert(B_TRANSLATE("Find failed"),
841 					B_TRANSLATE("Not found."), B_TRANSLATE("OK"),
842 					NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
843 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
844 				alert->Go();
845 			}
846 			break;
847 
848 		case MSG_FIND_CLOSED:
849 			fFindPanel = NULL;
850 			break;
851 
852 		case MENU_ENCODING:
853 			if (message->FindInt32("op", &encodingId) == B_OK)
854 				_ActiveTermView()->SetEncoding(encodingId);
855 			break;
856 
857 		case MSG_COLS_CHANGED:
858 		{
859 			int32 columns, rows;
860 			if (message->FindInt32("columns", &columns) != B_OK
861 				|| message->FindInt32("rows", &rows) != B_OK) {
862 				break;
863 			}
864 
865 			for (int32 i = 0; i < fTabView->CountTabs(); i++) {
866 				TermView* view = _TermViewAt(i);
867 				view->SetTermSize(rows, columns, true);
868 				_ResizeView(view);
869 			}
870 			break;
871 		}
872 
873 		case MSG_BLINK_CURSOR_CHANGED:
874 		{
875 			bool blinkingCursor
876 				= PrefHandler::Default()->getBool(PREF_BLINK_CURSOR);
877 
878 			for (int32 i = 0; i < fTabView->CountTabs(); i++) {
879 				TermView* view = _TermViewAt(i);
880 				view->SwitchCursorBlinking(blinkingCursor);
881 			}
882 			break;
883 		}
884 
885 		case MSG_HALF_FONT_CHANGED:
886 		case MSG_FULL_FONT_CHANGED:
887 		case MSG_ALLOW_BOLD_CHANGED:
888 		{
889 			BFont font;
890 			_GetPreferredFont(font);
891 			for (int32 i = 0; i < fTabView->CountTabs(); i++) {
892 				TermView* view = _TermViewAt(i);
893 				view->SetTermFont(&font);
894 				_ResizeView(view);
895 			}
896 			break;
897 		}
898 
899 		case MSG_HALF_SIZE_CHANGED:
900 		case MSG_FULL_SIZE_CHANGED:
901 		{
902 			const char* size = NULL;
903 			if (message->FindString("font_size", &size) != B_OK)
904 				break;
905 
906 			// mark the font size menu item
907 			for (int32 i = 0; i < fFontSizeMenu->CountItems(); i++) {
908 				BMenuItem* item = fFontSizeMenu->ItemAt(i);
909 				if (item == NULL)
910 					continue;
911 
912 				item->SetMarked(false);
913 				if (strcmp(item->Label(), size) == 0)
914 					item->SetMarked(true);
915 			}
916 
917 			BFont font;
918 			_ActiveTermView()->GetTermFont(&font);
919 			font.SetSize(atoi(size));
920 			PrefHandler::Default()->setInt32(PREF_HALF_FONT_SIZE,
921 				(int32)atoi(size));
922 			for (int32 i = 0; i < fTabView->CountTabs(); i++) {
923 				TermView* view = _TermViewAt(i);
924 				_TermViewAt(i)->SetTermFont(&font);
925 				_ResizeView(view);
926 			}
927 			break;
928 		}
929 
930 		case MSG_USE_OPTION_AS_META_CHANGED:
931 		{
932 			bool useOptionAsMetaKey
933 				= PrefHandler::Default()->getBool(PREF_USE_OPTION_AS_META);
934 
935 			for (int32 i = 0; i < fTabView->CountTabs(); i++) {
936 				TermView* view = _TermViewAt(i);
937 				view->SetUseOptionAsMetaKey(useOptionAsMetaKey);
938 			}
939 			break;
940 		}
941 
942 		case FULLSCREEN:
943 			if (!fSavedFrame.IsValid()) { // go fullscreen
944 				_ActiveTermView()->DisableResizeView();
945 				float mbHeight = fMenuBar->Bounds().Height() + 1;
946 				fSavedFrame = Frame();
947 				BScreen screen(this);
948 
949 				for (int32 i = fTabView->CountTabs() - 1; i >= 0; i--)
950 					_TermViewAt(i)->ScrollBar()->ResizeBy(0,
951 						(B_H_SCROLL_BAR_HEIGHT - 1));
952 
953 				fMenuBar->Hide();
954 				fTabView->ResizeBy(0, mbHeight);
955 				fTabView->MoveBy(0, -mbHeight);
956 				fSavedLook = Look();
957 				// done before ResizeTo to work around a Dano bug
958 				// (not erasing the decor)
959 				SetLook(B_NO_BORDER_WINDOW_LOOK);
960 				ResizeTo(screen.Frame().Width() + 1, screen.Frame().Height() + 1);
961 				MoveTo(screen.Frame().left, screen.Frame().top);
962 				SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE));
963 				fFullScreen = true;
964 			} else { // exit fullscreen
965 				_ActiveTermView()->DisableResizeView();
966 				float mbHeight = fMenuBar->Bounds().Height() + 1;
967 				fMenuBar->Show();
968 
969 				for (int32 i = fTabView->CountTabs() - 1; i >= 0; i--)
970 					_TermViewAt(i)->ScrollBar()->ResizeBy(0,
971 						-(B_H_SCROLL_BAR_HEIGHT - 1));
972 
973 				ResizeTo(fSavedFrame.Width(), fSavedFrame.Height());
974 				MoveTo(fSavedFrame.left, fSavedFrame.top);
975 				fTabView->ResizeBy(0, -mbHeight);
976 				fTabView->MoveBy(0, mbHeight);
977 				SetLook(fSavedLook);
978 				fSavedFrame = BRect(0, 0, -1, -1);
979 				SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
980 				fFullScreen = false;
981 			}
982 			break;
983 
984 		case MSG_FONT_CHANGED:
985 			PostMessage(MSG_HALF_FONT_CHANGED);
986 			break;
987 
988 		case MSG_COLOR_CHANGED:
989 		case MSG_COLOR_SCHEME_CHANGED:
990 		{
991 			for (int32 i = fTabView->CountTabs() - 1; i >= 0; i--) {
992 				TermViewContainerView* container = _TermViewContainerViewAt(i);
993 				_SetTermColors(container);
994 				container->Invalidate();
995 			}
996 			_ActiveTermView()->Invalidate();
997 			break;
998 		}
999 		case MSG_SAVE_AS_DEFAULT:
1000 		{
1001 			BPath path;
1002 			if (PrefHandler::GetDefaultPath(path) == B_OK) {
1003 				PrefHandler::Default()->SaveAsText(path.Path(),
1004 					PREFFILE_MIMETYPE);
1005 			}
1006 			break;
1007 		}
1008 
1009 		case MENU_PAGE_SETUP:
1010 			_DoPageSetup();
1011 			break;
1012 
1013 		case MENU_PRINT:
1014 			_DoPrint();
1015 			break;
1016 
1017 		case MSG_CHECK_CHILDREN:
1018 			_CheckChildren();
1019 			break;
1020 
1021 		case MSG_MOVE_TAB_LEFT:
1022 		case MSG_MOVE_TAB_RIGHT:
1023 			_NavigateTab(_IndexOfTermView(_ActiveTermView()),
1024 				message->what == MSG_MOVE_TAB_LEFT ? -1 : 1, true);
1025 			break;
1026 
1027 		case kTabTitleChanged:
1028 		{
1029 			// tab title changed message from SetTitleDialog
1030 			SessionID sessionID(*message, "session");
1031 			if (Session* session = _SessionForID(sessionID)) {
1032 				BString title;
1033 				if (message->FindString("title", &title) == B_OK) {
1034 					session->title.pattern = title;
1035 					session->title.patternUserDefined = true;
1036 				} else {
1037 					session->title.pattern.Truncate(0);
1038 					session->title.patternUserDefined = false;
1039 				}
1040 				_UpdateSessionTitle(_IndexOfSession(session));
1041 			}
1042 			break;
1043 		}
1044 
1045 		case kWindowTitleChanged:
1046 		{
1047 			// window title changed message from SetTitleDialog
1048 			BString title;
1049 			if (message->FindString("title", &title) == B_OK) {
1050 				fTitle.pattern = title;
1051 				fTitle.patternUserDefined = true;
1052 			} else {
1053 				fTitle.pattern
1054 					= PrefHandler::Default()->getString(PREF_WINDOW_TITLE);
1055 				fTitle.patternUserDefined = false;
1056 			}
1057 
1058 			_UpdateSessionTitle(fTabView->Selection());
1059 				// updates the window title as a side effect
1060 
1061 			break;
1062 		}
1063 
1064 		case kSetActiveTab:
1065 		{
1066 			int32 index;
1067 			if (message->FindInt32("index", &index) == B_OK
1068 					&& index >= 0 && index < fSessions.CountItems()) {
1069 				fTabView->Select(index);
1070 			}
1071 			break;
1072 		}
1073 
1074 		case kNewTab:
1075 			_NewTab();
1076 			break;
1077 
1078 		case kCloseView:
1079 		{
1080 			int32 index = -1;
1081 			SessionID sessionID(*message, "session");
1082 			if (sessionID.IsValid()) {
1083 				if (Session* session = _SessionForID(sessionID))
1084 					index = _IndexOfSession(session);
1085 			} else
1086 				index = _IndexOfTermView(_ActiveTermView());
1087 
1088 			if (index >= 0)
1089 				_RemoveTab(index);
1090 
1091 			break;
1092 		}
1093 
1094 		case kCloseOtherViews:
1095 		{
1096 			Session* session = _SessionForID(SessionID(*message, "session"));
1097 			if (session == NULL)
1098 				break;
1099 
1100 			int32 count = fSessions.CountItems();
1101 			for (int32 i = count - 1; i >= 0; i--) {
1102 				if (_SessionAt(i) != session)
1103 					_RemoveTab(i);
1104 			}
1105 
1106 			break;
1107 		}
1108 
1109 		case kIncreaseFontSize:
1110 		case kDecreaseFontSize:
1111 		{
1112 			BFont font;
1113 			_ActiveTermView()->GetTermFont(&font);
1114 			float size = font.Size();
1115 
1116 			if (message->what == kIncreaseFontSize) {
1117 				if (size < 12)
1118 					size += 1;
1119 				else if (size < 24)
1120 					size += 2;
1121 				else
1122 					size += 4;
1123 			} else {
1124 				if (size <= 12)
1125 					size -= 1;
1126 				else if (size <= 24)
1127 					size -= 2;
1128 				else
1129 					size -= 4;
1130 			}
1131 
1132 			// constrain the font size
1133 			if (size < kMinimumFontSize)
1134 				size = kMinimumFontSize;
1135 			if (size > kMaximumFontSize)
1136 				size = kMaximumFontSize;
1137 
1138 			// mark the font size menu item
1139 			for (int32 i = 0; i < fFontSizeMenu->CountItems(); i++) {
1140 				BMenuItem* item = fFontSizeMenu->ItemAt(i);
1141 				if (item == NULL)
1142 					continue;
1143 
1144 				item->SetMarked(false);
1145 				if (atoi(item->Label()) == size)
1146 					item->SetMarked(true);
1147 			}
1148 
1149 			font.SetSize(size);
1150 			PrefHandler::Default()->setInt32(PREF_HALF_FONT_SIZE, (int32)size);
1151 			for (int32 i = 0; i < fTabView->CountTabs(); i++) {
1152 				TermView* view = _TermViewAt(i);
1153 				_TermViewAt(i)->SetTermFont(&font);
1154 				_ResizeView(view);
1155 			}
1156 			break;
1157 		}
1158 
1159 		case kUpdateTitles:
1160 			_UpdateTitles();
1161 			break;
1162 
1163 		case kEditTabTitle:
1164 		{
1165 			SessionID sessionID(*message, "session");
1166 			if (Session* session = _SessionForID(sessionID))
1167 				_OpenSetTabTitleDialog(_IndexOfSession(session));
1168 			break;
1169 		}
1170 
1171 		case kEditWindowTitle:
1172 			_OpenSetWindowTitleDialog();
1173 			break;
1174 
1175 		case kUpdateSwitchTerminalsMenuItem:
1176 			_UpdateSwitchTerminalsMenuItem();
1177 			break;
1178 
1179 		default:
1180 			BWindow::MessageReceived(message);
1181 			break;
1182 	}
1183 }
1184 
1185 
1186 void
1187 TermWindow::WindowActivated(bool activated)
1188 {
1189 	if (activated)
1190 		_UpdateSwitchTerminalsMenuItem();
1191 }
1192 
1193 
1194 void
1195 TermWindow::_SetTermColors(TermViewContainerView* containerView)
1196 {
1197 	PrefHandler* handler = PrefHandler::Default();
1198 	rgb_color background = handler->getRGB(PREF_TEXT_BACK_COLOR);
1199 
1200 	containerView->SetViewColor(background);
1201 
1202 	TermView *termView = containerView->GetTermView();
1203 	termView->SetTextColor(handler->getRGB(PREF_TEXT_FORE_COLOR), background);
1204 
1205 	termView->SetCursorColor(handler->getRGB(PREF_CURSOR_FORE_COLOR),
1206 		handler->getRGB(PREF_CURSOR_BACK_COLOR));
1207 	termView->SetSelectColor(handler->getRGB(PREF_SELECT_FORE_COLOR),
1208 		handler->getRGB(PREF_SELECT_BACK_COLOR));
1209 }
1210 
1211 
1212 status_t
1213 TermWindow::_DoPageSetup()
1214 {
1215 	BPrintJob job("PageSetup");
1216 
1217 	// display the page configure panel
1218 	status_t status = job.ConfigPage();
1219 
1220 	// save a pointer to the settings
1221 	fPrintSettings = job.Settings();
1222 
1223 	return status;
1224 }
1225 
1226 
1227 void
1228 TermWindow::_DoPrint()
1229 {
1230 	BPrintJob job("Print");
1231 	if (fPrintSettings)
1232 		job.SetSettings(new BMessage(*fPrintSettings));
1233 
1234 	if (job.ConfigJob() != B_OK)
1235 		return;
1236 
1237 	BRect pageRect = job.PrintableRect();
1238 	BRect curPageRect = pageRect;
1239 
1240 	int pHeight = (int)pageRect.Height();
1241 	int pWidth = (int)pageRect.Width();
1242 	float w, h;
1243 	_ActiveTermView()->GetFrameSize(&w, &h);
1244 	int xPages = (int)ceil(w / pWidth);
1245 	int yPages = (int)ceil(h / pHeight);
1246 
1247 	job.BeginJob();
1248 
1249 	// loop through and draw each page, and write to spool
1250 	for (int x = 0; x < xPages; x++) {
1251 		for (int y = 0; y < yPages; y++) {
1252 			curPageRect.OffsetTo(x * pWidth, y * pHeight);
1253 			job.DrawView(_ActiveTermView(), curPageRect, B_ORIGIN);
1254 			job.SpoolPage();
1255 
1256 			if (!job.CanContinue()) {
1257 				// It is likely that the only way that the job was cancelled is
1258 				// because the user hit 'Cancel' in the page setup window, in
1259 				// which case, the user does *not* need to be told that it was
1260 				// cancelled.
1261 				// He/she will simply expect that it was done.
1262 				return;
1263 			}
1264 		}
1265 	}
1266 
1267 	job.CommitJob();
1268 }
1269 
1270 
1271 void
1272 TermWindow::_NewTab()
1273 {
1274 	ActiveProcessInfo info;
1275 	if (_ActiveTermView()->GetActiveProcessInfo(info))
1276 		_AddTab(NULL, info.CurrentDirectory());
1277 	else
1278 		_AddTab(NULL);
1279 }
1280 
1281 
1282 void
1283 TermWindow::_AddTab(Arguments* args, const BString& currentDirectory)
1284 {
1285 	int argc = 0;
1286 	const char* const* argv = NULL;
1287 	if (args != NULL)
1288 		args->GetShellArguments(argc, argv);
1289 	ShellParameters shellParameters(argc, argv, currentDirectory);
1290 
1291 	try {
1292 		TermView* view = new TermView(
1293 			PrefHandler::Default()->getInt32(PREF_ROWS),
1294 			PrefHandler::Default()->getInt32(PREF_COLS),
1295 			shellParameters,
1296 			PrefHandler::Default()->getInt32(PREF_HISTORY_SIZE));
1297 		view->SetListener(this);
1298 
1299 		TermViewContainerView* containerView = new TermViewContainerView(view);
1300 		BScrollView* scrollView = new TermScrollView("scrollView",
1301 			containerView, view, fSessions.IsEmpty());
1302 		if (!fFullScreen)
1303 			scrollView->ScrollBar(B_VERTICAL)
1304 				->ResizeBy(0, -(B_H_SCROLL_BAR_HEIGHT - 1));
1305 
1306 		if (fSessions.IsEmpty())
1307 			fTabView->SetScrollView(scrollView);
1308 
1309 		Session* session = new Session(_NewSessionID(), _NewSessionIndex(),
1310 			containerView);
1311 		fSessions.AddItem(session);
1312 
1313 		BFont font;
1314 		_GetPreferredFont(font);
1315 		view->SetTermFont(&font);
1316 
1317 		float width, height;
1318 		view->GetFontSize(&width, &height);
1319 
1320 		float minimumHeight = -1;
1321 		if (fMenuBar != NULL)
1322 			minimumHeight += fMenuBar->Bounds().Height() + 1;
1323 
1324 		if (fTabView != NULL && fTabView->CountTabs() > 0)
1325 			minimumHeight += fTabView->TabHeight() + 1;
1326 
1327 		SetSizeLimits(MIN_COLS * width - 1, MAX_COLS * width - 1,
1328 			minimumHeight + MIN_ROWS * height - 1,
1329 			minimumHeight + MAX_ROWS * height - 1);
1330 			// TODO: The size limit computation is apparently broken, since
1331 			// the terminal can be resized smaller than MIN_ROWS/MIN_COLS!
1332 
1333 		// If it's the first time we're called, setup the window
1334 		if (fTabView != NULL && fTabView->CountTabs() == 0) {
1335 			float viewWidth, viewHeight;
1336 			containerView->GetPreferredSize(&viewWidth, &viewHeight);
1337 
1338 			// Resize Window
1339 			ResizeTo(viewWidth + B_V_SCROLL_BAR_WIDTH,
1340 				viewHeight + fMenuBar->Bounds().Height() + 1);
1341 				// NOTE: Width is one pixel too small, since the scroll view
1342 				// is one pixel wider than its parent.
1343 		}
1344 
1345 		BTab* tab = new BTab;
1346 		fTabView->AddTab(scrollView, tab);
1347 		view->SetScrollBar(scrollView->ScrollBar(B_VERTICAL));
1348 		view->SetMouseClipboard(gMouseClipboard);
1349 
1350 		const BCharacterSet* charset
1351 			= BCharacterSetRoster::FindCharacterSetByName(
1352 				PrefHandler::Default()->getString(PREF_TEXT_ENCODING));
1353 		if (charset != NULL)
1354 			view->SetEncoding(charset->GetConversionID());
1355 
1356 		view->SetKeymap(fKeymap, fKeymapChars);
1357 		view->SetUseOptionAsMetaKey(
1358 			PrefHandler::Default()->getBool(PREF_USE_OPTION_AS_META));
1359 
1360 		_SetTermColors(containerView);
1361 
1362 		int32 tabIndex = fTabView->CountTabs() - 1;
1363 		fTabView->Select(tabIndex);
1364 
1365 		_UpdateSessionTitle(tabIndex);
1366 	} catch (...) {
1367 		// most probably out of memory. That's bad.
1368 		// TODO: Should cleanup, I guess
1369 
1370 		// Quit the application if we don't have a shell already
1371 		if (fTabView->CountTabs() == 0) {
1372 			fprintf(stderr, "Terminal couldn't open a shell\n");
1373 			PostMessage(B_QUIT_REQUESTED);
1374 		}
1375 	}
1376 }
1377 
1378 
1379 void
1380 TermWindow::_RemoveTab(int32 index)
1381 {
1382 	_FinishTitleDialog();
1383 		// always close to avoid confusion
1384 
1385 	if (fSessions.CountItems() > 1) {
1386 		if (!_CanClose(index))
1387 			return;
1388 		if (Session* session = (Session*)fSessions.RemoveItem(index)) {
1389 			if (fSessions.CountItems() == 1) {
1390 				fTabView->SetScrollView(dynamic_cast<BScrollView*>(
1391 					_SessionAt(0)->containerView->Parent()));
1392 			}
1393 
1394 			delete session;
1395 			delete fTabView->RemoveTab(index);
1396 		}
1397 	} else
1398 		PostMessage(B_QUIT_REQUESTED);
1399 }
1400 
1401 
1402 void
1403 TermWindow::_NavigateTab(int32 index, int32 direction, bool move)
1404 {
1405 	int32 count = fSessions.CountItems();
1406 	if (count <= 1 || index < 0 || index >= count)
1407 		return;
1408 
1409 	int32 newIndex = (index + direction + count) % count;
1410 	if (newIndex == index)
1411 		return;
1412 
1413 	if (move) {
1414 		// move the given tab to the new index
1415 		Session* session = (Session*)fSessions.RemoveItem(index);
1416 		fSessions.AddItem(session, newIndex);
1417 		fTabView->MoveTab(index, newIndex);
1418 	}
1419 
1420 	// activate the respective tab
1421 	fTabView->Select(newIndex);
1422 }
1423 
1424 
1425 TermViewContainerView*
1426 TermWindow::_ActiveTermViewContainerView() const
1427 {
1428 	return _TermViewContainerViewAt(fTabView->Selection());
1429 }
1430 
1431 
1432 TermViewContainerView*
1433 TermWindow::_TermViewContainerViewAt(int32 index) const
1434 {
1435 	if (Session* session = _SessionAt(index))
1436 		return session->containerView;
1437 	return NULL;
1438 }
1439 
1440 
1441 TermView*
1442 TermWindow::_ActiveTermView() const
1443 {
1444 	return _ActiveTermViewContainerView()->GetTermView();
1445 }
1446 
1447 
1448 TermView*
1449 TermWindow::_TermViewAt(int32 index) const
1450 {
1451 	TermViewContainerView* view = _TermViewContainerViewAt(index);
1452 	return view != NULL ? view->GetTermView() : NULL;
1453 }
1454 
1455 
1456 int32
1457 TermWindow::_IndexOfTermView(TermView* termView) const
1458 {
1459 	if (!termView)
1460 		return -1;
1461 
1462 	// find the view
1463 	int32 count = fTabView->CountTabs();
1464 	for (int32 i = count - 1; i >= 0; i--) {
1465 		if (termView == _TermViewAt(i))
1466 			return i;
1467 	}
1468 
1469 	return -1;
1470 }
1471 
1472 
1473 TermWindow::Session*
1474 TermWindow::_SessionAt(int32 index) const
1475 {
1476 	return (Session*)fSessions.ItemAt(index);
1477 }
1478 
1479 
1480 TermWindow::Session*
1481 TermWindow::_SessionForID(const SessionID& sessionID) const
1482 {
1483 	for (int32 i = 0; Session* session = _SessionAt(i); i++) {
1484 		if (session->id == sessionID)
1485 			return session;
1486 	}
1487 
1488 	return NULL;
1489 }
1490 
1491 
1492 int32
1493 TermWindow::_IndexOfSession(Session* session) const
1494 {
1495 	return fSessions.IndexOf(session);
1496 }
1497 
1498 
1499 void
1500 TermWindow::_CheckChildren()
1501 {
1502 	int32 count = fSessions.CountItems();
1503 	for (int32 i = count - 1; i >= 0; i--) {
1504 		Session* session = _SessionAt(i);
1505 		if (session->containerView->GetTermView()->CheckShellGone())
1506 			NotifyTermViewQuit(session->containerView->GetTermView(), 0);
1507 	}
1508 }
1509 
1510 
1511 void
1512 TermWindow::Zoom(BPoint leftTop, float width, float height)
1513 {
1514 	_ActiveTermView()->DisableResizeView();
1515 	BWindow::Zoom(leftTop, width, height);
1516 }
1517 
1518 
1519 void
1520 TermWindow::FrameResized(float newWidth, float newHeight)
1521 {
1522 	BWindow::FrameResized(newWidth, newHeight);
1523 
1524 	TermView* view = _ActiveTermView();
1525 	PrefHandler::Default()->setInt32(PREF_COLS, view->Columns());
1526 	PrefHandler::Default()->setInt32(PREF_ROWS, view->Rows());
1527 }
1528 
1529 
1530 void
1531 TermWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1532 {
1533 	fTerminalRoster.SetWindowInfo(IsMinimized(), Workspaces());
1534 }
1535 
1536 
1537 void
1538 TermWindow::WorkspaceActivated(int32 workspace, bool state)
1539 {
1540 	fTerminalRoster.SetWindowInfo(IsMinimized(), Workspaces());
1541 }
1542 
1543 
1544 void
1545 TermWindow::Minimize(bool minimize)
1546 {
1547 	BWindow::Minimize(minimize);
1548 	fTerminalRoster.SetWindowInfo(IsMinimized(), Workspaces());
1549 }
1550 
1551 
1552 void
1553 TermWindow::TabSelected(SmartTabView* tabView, int32 index)
1554 {
1555 	SessionChanged();
1556 }
1557 
1558 
1559 void
1560 TermWindow::TabDoubleClicked(SmartTabView* tabView, BPoint point, int32 index)
1561 {
1562 	if (index >= 0) {
1563 		// clicked on a tab -- open the title dialog
1564 		_OpenSetTabTitleDialog(index);
1565 	} else {
1566 		// not clicked on a tab -- create a new one
1567 		_NewTab();
1568 	}
1569 }
1570 
1571 
1572 void
1573 TermWindow::TabMiddleClicked(SmartTabView* tabView, BPoint point, int32 index)
1574 {
1575 	if (index >= 0)
1576 		_RemoveTab(index);
1577 }
1578 
1579 
1580 void
1581 TermWindow::TabRightClicked(SmartTabView* tabView, BPoint point, int32 index)
1582 {
1583 	if (index < 0)
1584 		return;
1585 
1586 	TermView* termView = _TermViewAt(index);
1587 	if (termView == NULL)
1588 		return;
1589 
1590 	BMessage* closeMessage = new BMessage(kCloseView);
1591 	_SessionAt(index)->id.AddToMessage(*closeMessage, "session");
1592 
1593 	BMessage* closeOthersMessage = new BMessage(kCloseOtherViews);
1594 	_SessionAt(index)->id.AddToMessage(*closeOthersMessage, "session");
1595 
1596 	BMessage* editTitleMessage = new BMessage(kEditTabTitle);
1597 	_SessionAt(index)->id.AddToMessage(*editTitleMessage, "session");
1598 
1599 	BPopUpMenu* popUpMenu = new BPopUpMenu("tab menu");
1600 	BLayoutBuilder::Menu<>(popUpMenu)
1601 		.AddItem(B_TRANSLATE("Close tab"), closeMessage)
1602 		.AddItem(B_TRANSLATE("Close other tabs"), closeOthersMessage)
1603 		.AddSeparator()
1604 		.AddItem(B_TRANSLATE("Edit tab title" B_UTF8_ELLIPSIS),
1605 			editTitleMessage)
1606 	;
1607 
1608 	popUpMenu->SetAsyncAutoDestruct(true);
1609 	popUpMenu->SetTargetForItems(BMessenger(this));
1610 
1611 	BPoint screenWhere = tabView->ConvertToScreen(point);
1612 	BRect mouseRect(screenWhere, screenWhere);
1613 	mouseRect.InsetBy(-4.0, -4.0);
1614 	popUpMenu->Go(screenWhere, true, true, mouseRect, true);
1615 }
1616 
1617 
1618 void
1619 TermWindow::NotifyTermViewQuit(TermView* view, int32 reason)
1620 {
1621 	// Since the notification can come from the view, we send a message to
1622 	// ourselves to avoid deleting the caller synchronously.
1623 	if (Session* session = _SessionAt(_IndexOfTermView(view))) {
1624 		BMessage message(kCloseView);
1625 		session->id.AddToMessage(message, "session");
1626 		message.AddInt32("reason", reason);
1627 		PostMessage(&message);
1628 	}
1629 }
1630 
1631 
1632 void
1633 TermWindow::SetTermViewTitle(TermView* view, const char* title)
1634 {
1635 	int32 index = _IndexOfTermView(view);
1636 	if (Session* session = _SessionAt(index)) {
1637 		session->title.pattern = title;
1638 		session->title.patternUserDefined = true;
1639 		_UpdateSessionTitle(index);
1640 	}
1641 }
1642 
1643 
1644 void
1645 TermWindow::TitleChanged(SetTitleDialog* dialog, const BString& title,
1646 	bool titleUserDefined)
1647 {
1648 	if (dialog == fSetTabTitleDialog) {
1649 		// tab title
1650 		BMessage message(kTabTitleChanged);
1651 		fSetTabTitleSession.AddToMessage(message, "session");
1652 		if (titleUserDefined)
1653 			message.AddString("title", title);
1654 
1655 		PostMessage(&message);
1656 	} else if (dialog == fSetWindowTitleDialog) {
1657 		// window title
1658 		BMessage message(kWindowTitleChanged);
1659 		if (titleUserDefined)
1660 			message.AddString("title", title);
1661 
1662 		PostMessage(&message);
1663 	}
1664 }
1665 
1666 
1667 void
1668 TermWindow::SetTitleDialogDone(SetTitleDialog* dialog)
1669 {
1670 	if (dialog == fSetTabTitleDialog) {
1671 		fSetTabTitleSession = SessionID();
1672 		fSetTabTitleDialog = NULL;
1673 			// assuming this is atomic
1674 	}
1675 }
1676 
1677 
1678 void
1679 TermWindow::TerminalInfosUpdated(TerminalRoster* roster)
1680 {
1681 	PostMessage(kUpdateSwitchTerminalsMenuItem);
1682 }
1683 
1684 
1685 void
1686 TermWindow::PreviousTermView(TermView* view)
1687 {
1688 	_NavigateTab(_IndexOfTermView(view), -1, false);
1689 }
1690 
1691 
1692 void
1693 TermWindow::NextTermView(TermView* view)
1694 {
1695 	_NavigateTab(_IndexOfTermView(view), 1, false);
1696 }
1697 
1698 
1699 void
1700 TermWindow::_ResizeView(TermView *view)
1701 {
1702 	float fontWidth, fontHeight;
1703 	view->GetFontSize(&fontWidth, &fontHeight);
1704 
1705 	float minimumHeight = -1;
1706 	if (fMenuBar != NULL)
1707 		minimumHeight += fMenuBar->Bounds().Height() + 1;
1708 
1709 	if (fTabView != NULL && fTabView->CountTabs() > 1)
1710 		minimumHeight += fTabView->TabHeight() + 1;
1711 
1712 	SetSizeLimits(MIN_COLS * fontWidth - 1, MAX_COLS * fontWidth - 1,
1713 		minimumHeight + MIN_ROWS * fontHeight - 1,
1714 		minimumHeight + MAX_ROWS * fontHeight - 1);
1715 
1716 	float width;
1717 	float height;
1718 	view->Parent()->GetPreferredSize(&width, &height);
1719 
1720 	width += B_V_SCROLL_BAR_WIDTH;
1721 		// NOTE: Width is one pixel too small, since the scroll view
1722 		// is one pixel wider than its parent.
1723 	if (fMenuBar != NULL)
1724 		height += fMenuBar->Bounds().Height() + 1;
1725 	if (fTabView != NULL && fTabView->CountTabs() > 1)
1726 		height += fTabView->TabHeight() + 1;
1727 
1728 	ResizeTo(width, height);
1729 	view->Invalidate();
1730 }
1731 
1732 
1733 /* static */ void
1734 TermWindow::MakeWindowSizeMenu(BMenu* menu)
1735 {
1736 	const int32 windowSizes[4][2] = {
1737 		{ 80, 25 },
1738 		{ 80, 40 },
1739 		{ 132, 25 },
1740 		{ 132, 40 }
1741 	};
1742 
1743 	const int32 sizeNum = sizeof(windowSizes) / sizeof(windowSizes[0]);
1744 	for (int32 i = 0; i < sizeNum; i++) {
1745 		char label[32];
1746 		int32 columns = windowSizes[i][0];
1747 		int32 rows = windowSizes[i][1];
1748 		snprintf(label, sizeof(label), "%" B_PRId32 "x%" B_PRId32, columns,
1749 			rows);
1750 		BMessage* message = new BMessage(MSG_COLS_CHANGED);
1751 		message->AddInt32("columns", columns);
1752 		message->AddInt32("rows", rows);
1753 		menu->AddItem(new BMenuItem(label, message));
1754 	}
1755 }
1756 
1757 
1758 /*static*/ BMenu*
1759 TermWindow::_MakeFontSizeMenu(uint32 command, uint8 defaultSize)
1760 {
1761 	BMenu* menu = new (std::nothrow) BMenu(B_TRANSLATE("Font size"));
1762 	if (menu == NULL)
1763 		return NULL;
1764 
1765 	int32 sizes[] = {
1766 		8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 0
1767 	};
1768 
1769 	bool found = false;
1770 
1771 	for (uint32 i = 0; sizes[i]; i++) {
1772 		BString string;
1773 		string << sizes[i];
1774 		BMessage* message = new BMessage(command);
1775 		message->AddString("font_size", string);
1776 		BMenuItem* item = new BMenuItem(string.String(), message);
1777 		menu->AddItem(item);
1778 		if (sizes[i] == defaultSize) {
1779 			item->SetMarked(true);
1780 			found = true;
1781 		}
1782 	}
1783 
1784 	if (!found) {
1785 		for (uint32 i = 0; sizes[i]; i++) {
1786 			if (sizes[i] > defaultSize) {
1787 				BString string;
1788 				string << defaultSize;
1789 				BMessage* message = new BMessage(command);
1790 				message->AddString("font_size", string);
1791 				BMenuItem* item = new BMenuItem(string.String(), message);
1792 				item->SetMarked(true);
1793 				menu->AddItem(item, i);
1794 				break;
1795 			}
1796 		}
1797 	}
1798 
1799 	return menu;
1800 }
1801 
1802 
1803 void
1804 TermWindow::_UpdateSwitchTerminalsMenuItem()
1805 {
1806 	fSwitchTerminalsMenuItem->SetEnabled(_FindSwitchTerminalTarget() >= 0);
1807 }
1808 
1809 
1810 void
1811 TermWindow::_TitleSettingsChanged()
1812 {
1813 	if (!fTitle.patternUserDefined)
1814 		fTitle.pattern = PrefHandler::Default()->getString(PREF_WINDOW_TITLE);
1815 
1816 	fSessionTitlePattern = PrefHandler::Default()->getString(PREF_TAB_TITLE);
1817 
1818 	_UpdateTitles();
1819 }
1820 
1821 
1822 void
1823 TermWindow::_UpdateTitles()
1824 {
1825 	int32 sessionCount = fSessions.CountItems();
1826 	for (int32 i = 0; i < sessionCount; i++)
1827 		_UpdateSessionTitle(i);
1828 }
1829 
1830 
1831 void
1832 TermWindow::_UpdateSessionTitle(int32 index)
1833 {
1834 	Session* session = _SessionAt(index);
1835 	if (session == NULL)
1836 		return;
1837 
1838 	// get the shell and active process infos
1839 	ShellInfo shellInfo;
1840 	ActiveProcessInfo activeProcessInfo;
1841 	TermView* termView = _TermViewAt(index);
1842 	if (!termView->GetShellInfo(shellInfo)
1843 		|| !termView->GetActiveProcessInfo(activeProcessInfo)) {
1844 		return;
1845 	}
1846 
1847 	// evaluate the session title pattern
1848 	BString sessionTitlePattern = session->title.patternUserDefined
1849 		? session->title.pattern : fSessionTitlePattern;
1850 	TabTitlePlaceholderMapper tabMapper(shellInfo, activeProcessInfo,
1851 		session->index);
1852 	const BString& sessionTitle = PatternEvaluator::Evaluate(
1853 		sessionTitlePattern, tabMapper);
1854 
1855 	// set the tab title
1856 	if (sessionTitle != session->title.title) {
1857 		session->title.title = sessionTitle;
1858 		fTabView->TabAt(index)->SetLabel(session->title.title);
1859 		fTabView->Invalidate();
1860 			// Invalidate the complete tab view, since other tabs might change
1861 			// their positions.
1862 	}
1863 
1864 	// If this is the active tab, also recompute the window title.
1865 	if (index != fTabView->Selection())
1866 		return;
1867 
1868 	// evaluate the window title pattern
1869 	WindowTitlePlaceholderMapper windowMapper(shellInfo, activeProcessInfo,
1870 		fTerminalRoster.CountTerminals() > 1
1871 			? fTerminalRoster.ID() + 1 : 0, sessionTitle);
1872 	const BString& windowTitle = PatternEvaluator::Evaluate(fTitle.pattern,
1873 		windowMapper);
1874 
1875 	// set the window title
1876 	if (windowTitle != fTitle.title) {
1877 		fTitle.title = windowTitle;
1878 		SetTitle(fTitle.title);
1879 	}
1880 }
1881 
1882 
1883 void
1884 TermWindow::_OpenSetTabTitleDialog(int32 index)
1885 {
1886 	// If a dialog is active, finish it.
1887 	_FinishTitleDialog();
1888 
1889 	BString toolTip = BString(B_TRANSLATE(
1890 		"The pattern specifying the current tab title. The following "
1891 			"placeholders\n"
1892 		"can be used:\n")) << kTooTipSetTabTitlePlaceholders;
1893 	fSetTabTitleDialog = new SetTitleDialog(
1894 		B_TRANSLATE("Set tab title"), B_TRANSLATE("Tab title:"),
1895 		toolTip);
1896 
1897 	Session* session = _SessionAt(index);
1898 	bool userDefined = session->title.patternUserDefined;
1899 	const BString& title = userDefined
1900 		? session->title.pattern : fSessionTitlePattern;
1901 	fSetTabTitleSession = session->id;
1902 
1903 	// place the dialog window directly under the tab, but keep it on screen
1904 	BPoint location = fTabView->ConvertToScreen(
1905 		fTabView->TabFrame(index).LeftBottom() + BPoint(0, 1));
1906 	fSetTabTitleDialog->MoveTo(location);
1907 	_MoveWindowInScreen(fSetTabTitleDialog);
1908 
1909 	fSetTabTitleDialog->Go(title, userDefined, this);
1910 }
1911 
1912 
1913 void
1914 TermWindow::_OpenSetWindowTitleDialog()
1915 {
1916 	// If a dialog is active, finish it.
1917 	_FinishTitleDialog();
1918 
1919 	BString toolTip = BString(B_TRANSLATE(
1920 		"The pattern specifying the window title. The following placeholders\n"
1921 		"can be used:\n")) << kTooTipSetTabTitlePlaceholders;
1922 	fSetWindowTitleDialog = new SetTitleDialog(B_TRANSLATE("Set window title"),
1923 		B_TRANSLATE("Window title:"), toolTip);
1924 
1925 	// center the dialog in the window frame, but keep it on screen
1926 	fSetWindowTitleDialog->CenterIn(Frame());
1927 	_MoveWindowInScreen(fSetWindowTitleDialog);
1928 
1929 	fSetWindowTitleDialog->Go(fTitle.pattern, fTitle.patternUserDefined, this);
1930 }
1931 
1932 
1933 void
1934 TermWindow::_FinishTitleDialog()
1935 {
1936 	SetTitleDialog* oldDialog = fSetTabTitleDialog;
1937 	if (oldDialog != NULL && oldDialog->Lock()) {
1938 		// might have been unset in the meantime, so recheck
1939 		if (fSetTabTitleDialog == oldDialog) {
1940 			oldDialog->Finish();
1941 				// this also unsets the variables
1942 		}
1943 		oldDialog->Unlock();
1944 		return;
1945 	}
1946 
1947 	oldDialog = fSetWindowTitleDialog;
1948 	if (oldDialog != NULL && oldDialog->Lock()) {
1949 		// might have been unset in the meantime, so recheck
1950 		if (fSetWindowTitleDialog == oldDialog) {
1951 			oldDialog->Finish();
1952 				// this also unsets the variable
1953 		}
1954 		oldDialog->Unlock();
1955 		return;
1956 	}
1957 }
1958 
1959 
1960 void
1961 TermWindow::_SwitchTerminal()
1962 {
1963 	team_id teamID = _FindSwitchTerminalTarget();
1964 	if (teamID < 0)
1965 		return;
1966 
1967 	BMessenger app(TERM_SIGNATURE, teamID);
1968 	app.SendMessage(MSG_ACTIVATE_TERM);
1969 }
1970 
1971 
1972 team_id
1973 TermWindow::_FindSwitchTerminalTarget()
1974 {
1975 	AutoLocker<TerminalRoster> rosterLocker(fTerminalRoster);
1976 
1977 	team_id myTeamID = Team();
1978 
1979 	int32 numTerms = fTerminalRoster.CountTerminals();
1980 	if (numTerms <= 1)
1981 		return -1;
1982 
1983 	// Find our position in the Terminal teams.
1984 	int32 i;
1985 
1986 	for (i = 0; i < numTerms; i++) {
1987 		if (myTeamID == fTerminalRoster.TerminalAt(i)->team)
1988 			break;
1989 	}
1990 
1991 	if (i == numTerms) {
1992 		// we didn't find ourselves -- that shouldn't happen
1993 		return -1;
1994 	}
1995 
1996 	uint32 currentWorkspace = 1L << current_workspace();
1997 
1998 	while (true) {
1999 		if (--i < 0)
2000 			i = numTerms - 1;
2001 
2002 		const TerminalRoster::Info* info = fTerminalRoster.TerminalAt(i);
2003 		if (info->team == myTeamID) {
2004 			// That's ourselves again. We've run through the complete list.
2005 			return -1;
2006 		}
2007 
2008 		if (!info->minimized && (info->workspaces & currentWorkspace) != 0)
2009 			return info->team;
2010 	}
2011 }
2012 
2013 
2014 TermWindow::SessionID
2015 TermWindow::_NewSessionID()
2016 {
2017 	return fNextSessionID++;
2018 }
2019 
2020 
2021 int32
2022 TermWindow::_NewSessionIndex()
2023 {
2024 	for (int32 id = 1; ; id++) {
2025 		bool used = false;
2026 
2027 		for (int32 i = 0;
2028 			Session* session = _SessionAt(i); i++) {
2029 			if (id == session->index) {
2030 				used = true;
2031 				break;
2032 			}
2033 		}
2034 
2035 		if (!used)
2036 			return id;
2037 	}
2038 }
2039 
2040 
2041 void
2042 TermWindow::_MoveWindowInScreen(BWindow* window)
2043 {
2044 	BRect frame = window->Frame();
2045 	BSize screenSize(BScreen(window).Frame().Size());
2046 	window->MoveTo(BLayoutUtils::MoveIntoFrame(frame, screenSize).LeftTop());
2047 }
2048 
2049 
2050 void
2051 TermWindow::_UpdateKeymap()
2052 {
2053 	delete fKeymap;
2054 	delete[] fKeymapChars;
2055 
2056 	get_key_map(&fKeymap, &fKeymapChars);
2057 
2058 	for (int32 i = 0; i < fTabView->CountTabs(); i++) {
2059 		TermView* view = _TermViewAt(i);
2060 		view->SetKeymap(fKeymap, fKeymapChars);
2061 	}
2062 }
2063