Ticket #2389: TermWindow.cpp

File TermWindow.cpp, 24.0 KB (added by hey68you, 15 years ago)
Line 
1/*
2 * Copyright 2007 Haiku, Inc.
3 * Copyright (c) 2004 Daniel Furrer <assimil8or@users.sourceforge.net>
4 * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net>
5 * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
6 *
7 * Distributed under the terms of the MIT license.
8 */
9
10#include "TermWindow.h"
11
12#include <stdio.h>
13#include <string.h>
14#include <time.h>
15
16#include <Alert.h>
17#include <Application.h>
18#include <Clipboard.h>
19#include <Dragger.h>
20#include <Menu.h>
21#include <MenuBar.h>
22#include <MenuItem.h>
23#include <Path.h>
24#include <PrintJob.h>
25#include <Roster.h>
26#include <Screen.h>
27#include <ScrollBar.h>
28#include <ScrollView.h>
29#include <String.h>
30
31#include "Arguments.h"
32#include "Coding.h"
33#include "MenuUtil.h"
34#include "FindWindow.h"
35#include "PrefWindow.h"
36#include "PrefView.h"
37#include "PrefHandler.h"
38#include "SmartTabView.h"
39#include "TermConst.h"
40#include "TermScrollView.h"
41#include "TermView.h"
42#include "SetTitleDlg.h"
43
44
45const static int32 kMaxTabs = 6;
46const static int32 kTermViewOffset = 3;
47
48// messages constants
49const static uint32 kNewTab = 'NTab';
50const static uint32 kCloseView = 'ClVw';
51const static uint32 kIncreaseFontSize = 'InFs';
52const static uint32 kDecreaseFontSize = 'DcFs';
53const static uint32 kSetActiveTab = 'STab';
54
55
56class CustomTermView : public TermView {
57public:
58 CustomTermView(int32 rows, int32 columns, int32 argc, const char **argv, int32 historySize = 1000);
59 virtual void NotifyQuit(int32 reason);
60 virtual void SetTitle(const char *title);
61};
62
63
64class TermViewContainerView : public BView {
65public:
66 TermViewContainerView(TermView* termView)
67 :
68 BView(BRect(), "term view container", B_FOLLOW_ALL, 0),
69 fTermView(termView)
70 {
71 termView->MoveTo(kTermViewOffset, kTermViewOffset);
72 BRect frame(termView->Frame());
73 ResizeTo(frame.right + kTermViewOffset, frame.bottom + kTermViewOffset);
74 AddChild(termView);
75 }
76
77 TermView* GetTermView() const { return fTermView; }
78
79 virtual void GetPreferredSize(float* _width, float* _height)
80 {
81 float width, height;
82 fTermView->GetPreferredSize(&width, &height);
83 *_width = width + 2 * kTermViewOffset;
84 *_height = height + 2 * kTermViewOffset;
85 }
86
87private:
88 TermView* fTermView;
89};
90
91
92struct TermWindow::Session {
93 int32 id;
94 BString name;
95 BString windowTitle;
96 TermViewContainerView* containerView;
97
98 Session(int32 id, TermViewContainerView* containerView)
99 :
100 id(id),
101 containerView(containerView)
102 {
103 name = "Shell ";
104 name << id;
105 }
106};
107
108
109class TermWindow::TabView : public SmartTabView {
110public:
111 TabView(TermWindow* window, BRect frame, const char *name)
112 :
113 SmartTabView(frame, name),
114 fWindow(window)
115 {
116 }
117
118 virtual void Select(int32 tab)
119 {
120 SmartTabView::Select(tab);
121 fWindow->SessionChanged();
122 }
123
124 virtual void RemoveAndDeleteTab(int32 index)
125 {
126 fWindow->_RemoveTab(index);
127 }
128
129private:
130 TermWindow* fWindow;
131};
132
133
134TermWindow::TermWindow(BRect frame, const char* title, Arguments *args)
135 :
136 BWindow(frame, title, B_DOCUMENT_WINDOW,
137 B_CURRENT_WORKSPACE | B_QUIT_ON_WINDOW_CLOSE),
138 fInitialTitle(title),
139 fTabView(NULL),
140 fMenubar(NULL),
141 fFilemenu(NULL),
142 fEditmenu(NULL),
143 fEncodingmenu(NULL),
144 fHelpmenu(NULL),
145 fWindowSizeMenu(NULL),
146 fPrintSettings(NULL),
147 fPrefWindow(NULL),
148 fFindPanel(NULL),
149 fSetTitlePanel(NULL),
150 fSavedFrame(0, 0, -1, -1),
151 fFindString(""),
152 fFindForwardMenuItem(NULL),
153 fFindBackwardMenuItem(NULL),
154 fFindSelection(false),
155 fForwardSearch(false),
156 fMatchCase(false),
157 fMatchWord(false)
158{
159 _InitWindow();
160 _AddTab(args);
161}
162
163
164TermWindow::~TermWindow()
165{
166 if (fPrefWindow)
167 fPrefWindow->PostMessage(B_QUIT_REQUESTED);
168
169 if (fFindPanel && fFindPanel->Lock()) {
170 fFindPanel->Quit();
171 fFindPanel = NULL;
172 }
173
174 PrefHandler::DeleteDefault();
175
176 for (int32 i = 0; Session* session = (Session*)fSessions.ItemAt(i); i++)
177 delete session;
178}
179
180
181void
182TermWindow::SetSessionWindowTitle(TermView* termView, const char* title)
183{
184 int32 index = _IndexOfTermView(termView);
185 if (Session* session = (Session*)fSessions.ItemAt(index)) {
186 session->windowTitle = title;
187 BTab* tab = fTabView->TabAt(index);
188 tab->SetLabel(session->windowTitle.String());
189 if (index == fTabView->Selection())
190 SetTitle(session->windowTitle.String());
191
192 /* Now refresh the tab view in order to force a re-draw of the tab label */
193 this->Lock();
194 fTabView->Invalidate();
195 this->Unlock();
196 }
197}
198
199
200void
201TermWindow::SessionChanged()
202{
203 int32 index = fTabView->Selection();
204 if (Session* session = (Session*)fSessions.ItemAt(index))
205 SetTitle(session->windowTitle.String());
206}
207
208
209void
210TermWindow::_InitWindow()
211{
212 // make menu bar
213 _SetupMenu();
214
215 AddShortcut('+', B_COMMAND_KEY, new BMessage(kIncreaseFontSize));
216 AddShortcut('-', B_COMMAND_KEY, new BMessage(kDecreaseFontSize));
217
218 // shortcuts to switch tabs
219 for (int32 i = 0; i < 9; i++) {
220 BMessage* message = new BMessage(kSetActiveTab);
221 message->AddInt32("index", i);
222 AddShortcut('1' + i, B_COMMAND_KEY, message);
223 }
224
225 BRect textFrame = Bounds();
226 textFrame.top = fMenubar->Bounds().bottom + 1.0;
227
228 fTabView = new TabView(this, textFrame, "tab view");
229 AddChild(fTabView);
230
231 // Make the scroll view one pixel wider than the tab view container view, so
232 // the scroll bar will look good.
233 fTabView->SetInsets(0, 0, -1, 0);
234}
235
236
237void
238TermWindow::MenusBeginning()
239{
240 // Syncronize Encode Menu Pop-up menu and Preference.
241 BMenuItem *item = fEncodingmenu->FindItem(EncodingAsString(_ActiveTermView()->Encoding()));
242 if (item != NULL)
243 item->SetMarked(true);
244 BWindow::MenusBeginning();
245}
246
247
248void
249TermWindow::_SetupMenu()
250{
251 PrefHandler menuText;
252
253 LoadLocaleFile(&menuText);
254
255 // Menu bar object.
256 fMenubar = new BMenuBar(Bounds(), "mbar");
257
258 // Make File Menu.
259 fFilemenu = new BMenu("Terminal");
260 fFilemenu->AddItem(new BMenuItem("Switch Terminals", new BMessage(MENU_SWITCH_TERM),'G'));
261 fFilemenu->AddItem(new BMenuItem("New Terminal" B_UTF8_ELLIPSIS, new BMessage(MENU_NEW_TERM), 'N'));
262 fFilemenu->AddItem(new BMenuItem("New Tab", new BMessage(kNewTab), 'T'));
263 fFilemenu->AddItem(new BMenuItem("Set Title" B_UTF8_ELLIPSIS, new BMessage(MENU_SET_WIN_TITLE), 'R'));
264
265 fFilemenu->AddSeparatorItem();
266 fFilemenu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS, new BMessage(MENU_PAGE_SETUP)));
267 fFilemenu->AddItem(new BMenuItem("Print", new BMessage(MENU_PRINT),'P'));
268 fFilemenu->AddSeparatorItem();
269 fFilemenu->AddItem(new BMenuItem("About Terminal" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)));
270 fFilemenu->AddSeparatorItem();
271 fFilemenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
272 fMenubar->AddItem(fFilemenu);
273
274 // Make Edit Menu.
275 fEditmenu = new BMenu ("Edit");
276 fEditmenu->AddItem (new BMenuItem ("Copy", new BMessage (B_COPY),'C'));
277 fEditmenu->AddItem (new BMenuItem ("Paste", new BMessage (B_PASTE),'V'));
278 fEditmenu->AddSeparatorItem();
279 fEditmenu->AddItem (new BMenuItem ("Select All", new BMessage (B_SELECT_ALL), 'A'));
280 fEditmenu->AddItem (new BMenuItem ("Clear All", new BMessage (MENU_CLEAR_ALL), 'L'));
281 fEditmenu->AddSeparatorItem();
282 fEditmenu->AddItem (new BMenuItem ("Find" B_UTF8_ELLIPSIS, new BMessage (MENU_FIND_STRING),'F'));
283 fFindBackwardMenuItem = new BMenuItem ("Find Backward", new BMessage (MENU_FIND_BACKWARD), '[');
284 fEditmenu->AddItem(fFindBackwardMenuItem);
285 fFindBackwardMenuItem->SetEnabled(false);
286 fFindForwardMenuItem = new BMenuItem ("Find Forward", new BMessage (MENU_FIND_FORWARD), ']');
287 fEditmenu->AddItem (fFindForwardMenuItem);
288 fFindForwardMenuItem->SetEnabled(false);
289
290 fMenubar->AddItem (fEditmenu);
291
292 // Make Help Menu.
293 fHelpmenu = new BMenu("Settings");
294 fWindowSizeMenu = new BMenu("Window Size");
295 _BuildWindowSizeMenu(fWindowSizeMenu);
296
297 fEncodingmenu = new BMenu("Text Encoding");
298 fEncodingmenu->SetRadioMode(true);
299 MakeEncodingMenu(fEncodingmenu, true);
300 fHelpmenu->AddItem(fWindowSizeMenu);
301 fHelpmenu->AddItem(fEncodingmenu);
302 fHelpmenu->AddSeparatorItem();
303 fHelpmenu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MENU_PREF_OPEN)));
304 fHelpmenu->AddSeparatorItem();
305 fHelpmenu->AddItem(new BMenuItem("Save as default", new BMessage(SAVE_AS_DEFAULT)));
306 fMenubar->AddItem(fHelpmenu);
307
308 AddChild(fMenubar);
309}
310
311
312void
313TermWindow::_GetPreferredFont(BFont &font)
314{
315 const char *family = PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY);
316
317 font.SetFamilyAndStyle(family, NULL);
318 float size = PrefHandler::Default()->getFloat(PREF_HALF_FONT_SIZE);
319 if (size < 6.0f)
320 size = 6.0f;
321 font.SetSize(size);
322 font.SetSpacing(B_FIXED_SPACING);
323}
324
325
326void
327TermWindow::MessageReceived(BMessage *message)
328{
329 int32 encodingId;
330 bool findresult;
331
332 switch (message->what) {
333 case B_COPY:
334 _ActiveTermView()->Copy(be_clipboard);
335 break;
336
337 case B_PASTE:
338 _ActiveTermView()->Paste(be_clipboard);
339 break;
340
341 case B_SELECT_ALL:
342 _ActiveTermView()->SelectAll();
343 break;
344
345 case B_ABOUT_REQUESTED:
346 be_app->PostMessage(B_ABOUT_REQUESTED);
347 break;
348
349 case MENU_CLEAR_ALL:
350 _ActiveTermView()->Clear();
351 break;
352
353 case MENU_SWITCH_TERM:
354 be_app->PostMessage(MENU_SWITCH_TERM);
355 break;
356
357 case MENU_NEW_TERM:
358 {
359 app_info info;
360 be_app->GetAppInfo(&info);
361
362 // try launching two different ways to work around possible problems
363 if (be_roster->Launch(&info.ref) != B_OK)
364 be_roster->Launch(TERM_SIGNATURE);
365 break;
366 }
367
368 case MENU_SET_WIN_TITLE:
369 this->SetTitleRequested();
370 break;
371
372 case MSG_SET_TITLE_CLOSED:
373 fSetTitlePanel = NULL;
374 break;
375
376 case MENU_PREF_OPEN:
377 if (!fPrefWindow)
378 fPrefWindow = new PrefWindow(this);
379 else
380 fPrefWindow->Activate();
381 break;
382
383 case MSG_PREF_CLOSED:
384 fPrefWindow = NULL;
385 break;
386
387 case MENU_FIND_STRING:
388 if (!fFindPanel) {
389 BRect r = Frame();
390 r.left += 20;
391 r.top += 20;
392 r.right = r.left + 260;
393 r.bottom = r.top + 190;
394 fFindPanel = new FindWindow(r, this, fFindString, fFindSelection, fMatchWord, fMatchCase, fForwardSearch);
395 }
396 else
397 fFindPanel->Activate();
398 break;
399
400 case MSG_FIND:
401 fFindPanel->PostMessage(B_QUIT_REQUESTED);
402 message->FindBool("findselection", &fFindSelection);
403 if (!fFindSelection)
404 message->FindString("findstring", &fFindString);
405 else
406 _ActiveTermView()->GetSelection(fFindString);
407
408 if (fFindString.Length() == 0) {
409 BAlert *alert = new BAlert("find failed", "No search string.", "Okay", NULL,
410 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
411 alert->Go();
412 fFindBackwardMenuItem->SetEnabled(false);
413 fFindForwardMenuItem->SetEnabled(false);
414 break;
415 }
416
417 message->FindBool("forwardsearch", &fForwardSearch);
418 message->FindBool("matchcase", &fMatchCase);
419 message->FindBool("matchword", &fMatchWord);
420 findresult = _ActiveTermView()->Find(fFindString, fForwardSearch, fMatchCase, fMatchWord);
421
422 if (!findresult) {
423 BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL,
424 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
425 alert->Go();
426 fFindBackwardMenuItem->SetEnabled(false);
427 fFindForwardMenuItem->SetEnabled(false);
428 break;
429 }
430
431 // Enable the menu items Find Forward and Find Backward
432 fFindBackwardMenuItem->SetEnabled(true);
433 fFindForwardMenuItem->SetEnabled(true);
434 break;
435
436 case MENU_FIND_FORWARD:
437 findresult = _ActiveTermView()->Find(fFindString, true, fMatchCase, fMatchWord);
438 if (!findresult) {
439 BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL,
440 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
441 alert->Go();
442 }
443 break;
444
445 case MENU_FIND_BACKWARD:
446 findresult = _ActiveTermView()->Find(fFindString, false, fMatchCase, fMatchWord);
447 if (!findresult) {
448 BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL,
449 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
450 alert->Go();
451 }
452 break;
453
454 case MSG_FIND_CLOSED:
455 fFindPanel = NULL;
456 break;
457
458 case MENU_ENCODING:
459 if (message->FindInt32("op", &encodingId) == B_OK)
460 _ActiveTermView()->SetEncoding(encodingId);
461 break;
462
463 case MSG_COLS_CHANGED:
464 {
465 int32 columns, rows;
466 message->FindInt32("columns", &columns);
467 message->FindInt32("rows", &rows);
468 PrefHandler::Default()->setInt32(PREF_COLS, columns);
469 PrefHandler::Default()->setInt32(PREF_ROWS, rows);
470
471 _ActiveTermView()->SetTermSize(rows, columns, 0);
472
473 _ResizeView(_ActiveTermView());
474
475 BPath path;
476 if (PrefHandler::GetDefaultPath(path) == B_OK)
477 PrefHandler::Default()->SaveAsText(path.Path(), PREFFILE_MIMETYPE);
478 break;
479 }
480 case MSG_HALF_FONT_CHANGED:
481 case MSG_FULL_FONT_CHANGED:
482 case MSG_HALF_SIZE_CHANGED:
483 case MSG_FULL_SIZE_CHANGED:
484 {
485 BFont font;
486 _GetPreferredFont(font);
487 _ActiveTermView()->SetTermFont(&font);
488
489 _ResizeView(_ActiveTermView());
490
491 break;
492 }
493
494 case FULLSCREEN:
495 if (!fSavedFrame.IsValid()) { // go fullscreen
496 float mbHeight = fMenubar->Bounds().Height() + 1;
497 fSavedFrame = Frame();
498 BScreen screen(this);
499 _ActiveTermView()->ScrollBar()->Hide();
500 fMenubar->Hide();
501 fTabView->ResizeBy(0, mbHeight);
502 fTabView->MoveBy(0, -mbHeight);
503 fSavedLook = Look();
504 // done before ResizeTo to work around a Dano bug (not erasing the decor)
505 SetLook(B_NO_BORDER_WINDOW_LOOK);
506 ResizeTo(screen.Frame().Width()+1, screen.Frame().Height()+1);
507 MoveTo(screen.Frame().left, screen.Frame().top);
508 } else { // exit fullscreen
509 float mbHeight = fMenubar->Bounds().Height() + 1;
510 fMenubar->Show();
511 _ActiveTermView()->ScrollBar()->Show();
512 ResizeTo(fSavedFrame.Width(), fSavedFrame.Height());
513 MoveTo(fSavedFrame.left, fSavedFrame.top);
514 fTabView->ResizeBy(0, -mbHeight);
515 fTabView->MoveBy(0, mbHeight);
516 SetLook(fSavedLook);
517 fSavedFrame = BRect(0,0,-1,-1);
518 }
519 break;
520
521 case MSG_FONT_CHANGED:
522 PostMessage(MSG_HALF_FONT_CHANGED);
523 break;
524
525 case MSG_COLOR_CHANGED:
526 {
527 _SetTermColors(_ActiveTermViewContainerView());
528 _ActiveTermView()->Invalidate();
529 break;
530 }
531
532 case SAVE_AS_DEFAULT:
533 {
534 BPath path;
535 if (PrefHandler::GetDefaultPath(path) == B_OK)
536 PrefHandler::Default()->SaveAsText(path.Path(), PREFFILE_MIMETYPE);
537 break;
538 }
539 case MENU_PAGE_SETUP:
540 _DoPageSetup();
541 break;
542
543 case MENU_PRINT:
544 _DoPrint();
545 break;
546
547 case MSG_CHECK_CHILDREN:
548 _CheckChildren();
549 break;
550
551 case MSG_PREVIOUS_TAB:
552 case MSG_NEXT_TAB:
553 {
554 TermView* termView;
555 if (message->FindPointer("termView", (void**)&termView) == B_OK) {
556 int32 count = fSessions.CountItems();
557 int32 index = _IndexOfTermView(termView);
558 if (count > 1 && index >= 0) {
559 index += message->what == MSG_PREVIOUS_TAB ? -1 : 1;
560 fTabView->Select((index + count) % count);
561 }
562 }
563 break;
564 }
565
566 case kSetActiveTab:
567 {
568 int32 index;
569 if (message->FindInt32("index", &index) == B_OK
570 && index >= 0 && index < fSessions.CountItems()) {
571 fTabView->Select(index);
572 }
573 break;
574 }
575
576 case kNewTab:
577 if (fTabView->CountTabs() < kMaxTabs)
578 _AddTab(NULL);
579 break;
580
581 case kCloseView:
582 {
583 TermView* termView;
584 if (message->FindPointer("termView", (void**)&termView) == B_OK) {
585 int32 index = _IndexOfTermView(termView);
586 if (index >= 0)
587 _RemoveTab(index);
588 }
589 break;
590 }
591
592 case kIncreaseFontSize:
593 case kDecreaseFontSize:
594 {
595 message->PrintToStream();
596 TermView *view = _ActiveTermView();
597 BFont font;
598 view->GetTermFont(&font);
599
600 float size = font.Size();
601 if (message->what == kIncreaseFontSize)
602 size += 1;
603 else
604 size -= 1;
605
606 // limit the font size
607 if (size < 6)
608 size = 6;
609 else if (size > 20)
610 size = 20;
611
612 font.SetSize(size);
613 view->SetTermFont(&font);
614 PrefHandler::Default()->setInt32(PREF_HALF_FONT_SIZE, (int32)size);
615
616 _ResizeView(view);
617 break;
618 }
619
620 default:
621 BWindow::MessageReceived(message);
622 break;
623 }
624}
625
626
627void
628TermWindow::SetTitleRequested (void)
629{
630 if (fSetTitlePanel)
631 {
632 fSetTitlePanel->Activate ();
633 }
634 else
635 {
636
637 BRect rect(0, 0, 250, 100);
638 rect.OffsetTo (this->Frame().LeftTop());
639 rect.OffsetBy (100, 100);
640 fSetTitlePanel = new SetTitleDlg (rect, this);
641 fSetTitlePanel->Show();
642 }
643
644}
645
646void
647TermWindow::WindowActivated(bool activated)
648{
649 BWindow::WindowActivated(activated);
650}
651
652
653void
654TermWindow::_SetTermColors(TermViewContainerView *containerView)
655{
656 PrefHandler* handler = PrefHandler::Default();
657 rgb_color background = handler->getRGB(PREF_TEXT_BACK_COLOR);
658
659 containerView->SetViewColor(background);
660
661 TermView *termView = containerView->GetTermView();
662 termView->SetTextColor(handler->getRGB(PREF_TEXT_FORE_COLOR), background);
663
664 termView->SetSelectColor(handler->getRGB(PREF_SELECT_FORE_COLOR),
665 handler->getRGB(PREF_SELECT_BACK_COLOR));
666
667 termView->SetCursorColor(handler->getRGB(PREF_CURSOR_FORE_COLOR),
668 handler->getRGB(PREF_CURSOR_BACK_COLOR));
669}
670
671
672status_t
673TermWindow::_DoPageSetup()
674{
675 BPrintJob job("PageSetup");
676
677 // display the page configure panel
678 status_t status = job.ConfigPage();
679
680 // save a pointer to the settings
681 fPrintSettings = job.Settings();
682
683 return status;
684}
685
686
687void
688TermWindow::_DoPrint()
689{
690 if (!fPrintSettings || (_DoPageSetup() != B_OK)) {
691 (new BAlert("Cancel", "Print cancelled.", "OK"))->Go();
692 return;
693 }
694
695 BPrintJob job("Print");
696 job.SetSettings(new BMessage(*fPrintSettings));
697
698 BRect pageRect = job.PrintableRect();
699 BRect curPageRect = pageRect;
700
701 int pHeight = (int)pageRect.Height();
702 int pWidth = (int)pageRect.Width();
703 float w,h;
704 _ActiveTermView()->GetFrameSize(&w, &h);
705 int xPages = (int)ceil(w / pWidth);
706 int yPages = (int)ceil(h / pHeight);
707
708 job.BeginJob();
709
710 // loop through and draw each page, and write to spool
711 for (int x = 0; x < xPages; x++) {
712 for (int y = 0; y < yPages; y++) {
713 curPageRect.OffsetTo(x * pWidth, y * pHeight);
714 job.DrawView(_ActiveTermView(), curPageRect, B_ORIGIN);
715 job.SpoolPage();
716
717 if (!job.CanContinue()){
718 // It is likely that the only way that the job was cancelled is
719 // because the user hit 'Cancel' in the page setup window, in which
720 // case, the user does *not* need to be told that it was cancelled.
721 // He/she will simply expect that it was done.
722 return;
723 }
724 }
725 }
726
727 job.CommitJob();
728}
729
730
731void
732TermWindow::_AddTab(Arguments *args)
733{
734 int argc = 0;
735 const char *const *argv = NULL;
736 if (args != NULL)
737 args->GetShellArguments(argc, argv);
738
739 try {
740 // Note: I don't pass the Arguments class directly to the termview,
741 // only to avoid adding it as a dependency: in other words, to keep
742 // the TermView class as agnostic as possible about the surrounding world.
743 CustomTermView *view = new CustomTermView(
744 PrefHandler::Default()->getInt32(PREF_ROWS),
745 PrefHandler::Default()->getInt32(PREF_COLS),
746 argc, (const char **)argv,
747 PrefHandler::Default()->getInt32(PREF_HISTORY_SIZE));
748
749 TermViewContainerView *containerView = new TermViewContainerView(view);
750 BScrollView *scrollView = new TermScrollView("scrollView",
751 containerView, view);
752
753 Session* session = new Session(_NewSessionID(), containerView);
754 session->windowTitle = fInitialTitle;
755 fSessions.AddItem(session);
756
757 BTab *tab = new BTab;
758 // TODO: Use a better name. For example, do like MacOsX's Terminal
759 // and update the title using the last executed command ?
760 // Or like Gnome's Terminal and use the current path ?
761 fTabView->AddTab(scrollView, tab);
762 tab->SetLabel(session->name.String());
763 view->SetScrollBar(scrollView->ScrollBar(B_VERTICAL));
764
765 view->SetEncoding(EncodingID(PrefHandler::Default()->getString(PREF_TEXT_ENCODING)));
766
767 BFont font;
768 _GetPreferredFont(font);
769 view->SetTermFont(&font);
770
771 _SetTermColors(containerView);
772
773 int width, height;
774 view->GetFontSize(&width, &height);
775
776 float minimumHeight = -1;
777 if (fMenubar)
778 minimumHeight += fMenubar->Bounds().Height() + 1;
779 if (fTabView && fTabView->CountTabs() > 1)
780 minimumHeight += fTabView->TabHeight() + 1;
781 SetSizeLimits(MIN_COLS * width - 1, MAX_COLS * width - 1,
782 minimumHeight + MIN_ROWS * height - 1,
783 minimumHeight + MAX_ROWS * height - 1);
784
785 // If it's the first time we're called, setup the window
786 if (fTabView->CountTabs() == 1) {
787 float viewWidth, viewHeight;
788 containerView->GetPreferredSize(&viewWidth, &viewHeight);
789
790 // Resize Window
791 ResizeTo(viewWidth + B_V_SCROLL_BAR_WIDTH,
792 viewHeight + fMenubar->Bounds().Height() + 1);
793 // NOTE: Width is one pixel too small, since the scroll view
794 // is one pixel wider than its parent.
795 }
796 // TODO: No fTabView->Select(tab); ?
797 fTabView->Select(fTabView->CountTabs() - 1);
798 } catch (...) {
799 // most probably out of memory. That's bad.
800 // TODO: Should cleanup, I guess
801 }
802}
803
804
805void
806TermWindow::_RemoveTab(int32 index)
807{
808 if (fSessions.CountItems() > 1) {
809 if (Session* session = (Session*)fSessions.RemoveItem(index)) {
810 delete session;
811 delete fTabView->RemoveTab(index);
812 }
813 } else
814 PostMessage(B_QUIT_REQUESTED);
815}
816
817
818TermViewContainerView*
819TermWindow::_ActiveTermViewContainerView() const
820{
821 return _TermViewContainerViewAt(fTabView->Selection());
822}
823
824
825TermViewContainerView*
826TermWindow::_TermViewContainerViewAt(int32 index) const
827{
828 if (Session* session = (Session*)fSessions.ItemAt(index))
829 return session->containerView;
830 return NULL;
831}
832
833
834TermView *
835TermWindow::_ActiveTermView() const
836{
837 return _ActiveTermViewContainerView()->GetTermView();
838}
839
840
841TermView*
842TermWindow::_TermViewAt(int32 index) const
843{
844 TermViewContainerView* view = _TermViewContainerViewAt(index);
845 return view != NULL ? view->GetTermView() : NULL;
846}
847
848
849int32
850TermWindow::_IndexOfTermView(TermView* termView) const
851{
852 if (!termView)
853 return -1;
854
855 // find the view
856 int32 count = fTabView->CountTabs();
857 for (int32 i = count - 1; i >= 0; i--) {
858 if (termView == _TermViewAt(i))
859 return i;
860 }
861
862 return -1;
863}
864
865
866void
867TermWindow::_CheckChildren()
868{
869 int32 count = fSessions.CountItems();
870 for (int32 i = count - 1; i >= 0; i--) {
871 Session* session = (Session*)fSessions.ItemAt(i);
872 session->containerView->GetTermView()->CheckShellGone();
873 }
874}
875
876
877void
878TermWindow::_ResizeView(TermView *view)
879{
880 int fontWidth, fontHeight;
881 view->GetFontSize(&fontWidth, &fontHeight);
882
883 float minimumHeight = -1;
884 if (fMenubar)
885 minimumHeight += fMenubar->Bounds().Height() + 1;
886 if (fTabView && fTabView->CountTabs() > 1)
887 minimumHeight += fTabView->TabHeight() + 1;
888
889 SetSizeLimits(MIN_COLS * fontWidth - 1, MAX_COLS * fontWidth - 1,
890 minimumHeight + MIN_ROWS * fontHeight - 1,
891 minimumHeight + MAX_ROWS * fontHeight - 1);
892
893 float width, height;
894 view->Parent()->GetPreferredSize(&width, &height);
895 width += B_V_SCROLL_BAR_WIDTH;
896 // NOTE: Width is one pixel too small, since the scroll view
897 // is one pixel wider than its parent.
898 height += fMenubar->Bounds().Height() + 1;
899
900 ResizeTo(width, height);
901
902 view->Invalidate();
903}
904
905
906void
907TermWindow::_BuildWindowSizeMenu(BMenu *menu)
908{
909 const int32 windowSizes[5][2] = {
910 { 80, 24 },
911 { 80, 25 },
912 { 80, 40 },
913 { 132, 24 },
914 { 132, 25 }
915 };
916
917 const int32 sizeNum = sizeof(windowSizes) / sizeof(windowSizes[0]);
918 for (int32 i = 0; i < sizeNum; i++) {
919 char label[32];
920 int32 columns = windowSizes[i][0];
921 int32 rows = windowSizes[i][1];
922 snprintf(label, sizeof(label), "%ldx%ld", columns, rows);
923 BMessage *message = new BMessage(MSG_COLS_CHANGED);
924 message->AddInt32("columns", columns);
925 message->AddInt32("rows", rows);
926 menu->AddItem(new BMenuItem(label, message));
927 }
928
929 menu->AddItem(new BMenuItem("Fullscreen",
930 new BMessage(FULLSCREEN), B_ENTER));
931}
932
933
934int32
935TermWindow::_NewSessionID()
936{
937 for (int32 id = 1; ; id++) {
938 bool used = false;
939
940 for (int32 i = 0;
941 Session* session = (Session*)fSessions.ItemAt(i); i++) {
942 if (id == session->id) {
943 used = true;
944 break;
945 }
946 }
947
948 if (!used)
949 return id;
950 }
951}
952
953
954// #pragma mark -
955
956
957// CustomTermView
958CustomTermView::CustomTermView(int32 rows, int32 columns, int32 argc, const char **argv, int32 historySize)
959 :
960 TermView(rows, columns, argc, argv, historySize)
961{
962}
963
964
965void
966CustomTermView::NotifyQuit(int32 reason)
967{
968 BWindow *window = Window();
969 if (window == NULL)
970 window = be_app->WindowAt(0);
971
972 // TODO: If we got this from a view in a tab not currently selected,
973 // Window() will be NULL, as the view is detached.
974 // So we send the message to the first application window
975 // This isn't so cool, but for now, a Terminal app has only one
976 // window.
977 if (window != NULL) {
978 BMessage message(kCloseView);
979 message.AddPointer("termView", this);
980 message.AddInt32("reason", reason);
981 window->PostMessage(&message);
982 }
983}
984
985
986void
987CustomTermView::SetTitle(const char *title)
988{
989 dynamic_cast<TermWindow*>(Window())->SetSessionWindowTitle(this, title);
990}
991