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