diff --git a/src/apps/terminal/Jamfile b/src/apps/terminal/Jamfile index 8575217372..1efea6d9b6 100644 --- a/src/apps/terminal/Jamfile +++ b/src/apps/terminal/Jamfile @@ -20,6 +20,7 @@ Application Terminal : PatternEvaluator.cpp PrefHandler.cpp PrefWindow.cpp + SetTitleDialog.cpp Shell.cpp ShellParameters.cpp SmartTabView.cpp diff --git a/src/apps/terminal/SetTitleDialog.cpp b/src/apps/terminal/SetTitleDialog.cpp new file mode 100644 index 0000000000..8b71c28720 --- /dev/null +++ b/src/apps/terminal/SetTitleDialog.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "SetTitleDialog.h" + +#include +#include +#include +#include + + +static const uint32 kMessageOK = 'okok'; +static const uint32 kMessageDefault = 'dflt'; +static const uint32 kMessageTitleChanged = 'chng'; + + +#undef B_TRANSLATE_CONTEXT +#define B_TRANSLATE_CONTEXT "Terminal SetTitleWindow" + + +// #pragma mark - SetTitleDialog + + +SetTitleDialog::SetTitleDialog(const char* dialogTitle, const char* label, + const char* toolTip) + : + BWindow(BRect(0, 0, 0, 0), dialogTitle, B_BORDERED_WINDOW_LOOK, + B_FLOATING_APP_WINDOW_FEEL, + B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), + fListener(NULL), + fTitle(), + fTitleUserDefined(false) +{ + BLayoutBuilder::Group<>(this, B_VERTICAL) + .SetInsets(10, 10, 10, 10) + .Add(fTitleTextControl = new BTextControl("title", label, "", NULL)) + .AddGroup(B_HORIZONTAL) + .Add(fDefaultButton = new BButton("defaultButton", + B_TRANSLATE("Use default"), new BMessage(kMessageDefault))) + .AddGlue() + .Add(fCancelButton = new BButton("cancelButton", + B_TRANSLATE("Cancel"), new BMessage(B_QUIT_REQUESTED))) + .Add(fOKButton = new BButton("okButton", B_TRANSLATE("OK"), + new BMessage(kMessageOK))); + + fTitleTextControl->SetToolTip(toolTip); + + fOKButton->MakeDefault(true); + + UpdateSizeLimits(); + // as a courtesy to our creator, who might want to place us +} + + +SetTitleDialog::~SetTitleDialog() +{ + if (fListener != NULL) { + // reset to old title + fListener->TitleChanged(this, fOldTitle, fOldTitleUserDefined); + + Listener* listener = fListener; + fListener = NULL; + listener->SetTitleDialogDone(this); + } +} + + +void +SetTitleDialog::Go(const BString& title, bool titleUserDefined, + Listener* listener) +{ + fTitle = fOldTitle = title; + fTitleUserDefined = fOldTitleUserDefined = titleUserDefined; + + fDefaultButton->SetEnabled(titleUserDefined); + + fTitleTextControl->SetText(fTitle); + fTitleTextControl->SetModificationMessage( + new BMessage(kMessageTitleChanged)); + fTitleTextControl->MakeFocus(true); + + fListener = listener; + + Show(); +} + + +void +SetTitleDialog::Finish() +{ + if (Listener* listener = fListener) { + fListener = NULL; + listener->SetTitleDialogDone(this); + } + + PostMessage(B_QUIT_REQUESTED); +} + + +void +SetTitleDialog::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMessageDefault: + if (fListener != NULL) + fListener->TitleChanged(this, BString(), false); + // We're done now, fall through... + + case kMessageOK: + Finish(); + break; + + case kMessageTitleChanged: + fTitle = fTitleTextControl->Text(); + fTitleUserDefined = true; + + fDefaultButton->SetEnabled(true); + + if (fListener != NULL) + fListener->TitleChanged(this, fTitle, fTitleUserDefined); + break; + + default: + BWindow::MessageReceived(message); + break; + } + +} + + +// #pragma mark - SetTitleDialog + + +SetTitleDialog::Listener::~Listener() +{ +} diff --git a/src/apps/terminal/SetTitleDialog.h b/src/apps/terminal/SetTitleDialog.h new file mode 100644 index 0000000000..7a255758ea --- /dev/null +++ b/src/apps/terminal/SetTitleDialog.h @@ -0,0 +1,58 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SET_TITLE_DIALOG_H +#define SET_TITLE_DIALOG_H + + +#include +#include + + +class BButton; +class BTextControl; + + +class SetTitleDialog : public BWindow { +public: + class Listener; + +public: + SetTitleDialog(const char* dialogTitle, + const char* label, const char* toolTip); + virtual ~SetTitleDialog(); + + void Go(const BString& title, bool titleUserDefined, + Listener* listener); + void Finish(); + // window must be locked + + virtual void MessageReceived(BMessage* message); + +private: + Listener* fListener; + BTextControl* fTitleTextControl; + BButton* fOKButton; + BButton* fCancelButton; + BButton* fDefaultButton; + BString fOldTitle; + BString fTitle; + bool fOldTitleUserDefined; + bool fTitleUserDefined; +}; + + +class SetTitleDialog::Listener { +public: + virtual ~Listener(); + + // hooks called in the dialog thread with the window locked + virtual void TitleChanged(SetTitleDialog* dialog, + const BString& title, + bool titleUserDefined) = 0; + virtual void SetTitleDialogDone(SetTitleDialog* dialog) = 0; +}; + + +#endif // SET_TITLE_DIALOG_H diff --git a/src/apps/terminal/TermConst.h b/src/apps/terminal/TermConst.h index 04fd34a8b4..d77dac6cbc 100644 --- a/src/apps/terminal/TermConst.h +++ b/src/apps/terminal/TermConst.h @@ -93,6 +93,8 @@ static const uint32 MSG_REPORT_MOUSE_EVENT = 'mous'; static const uint32 MSG_SAVE_WINDOW_POSITION = 'swps'; static const uint32 MSG_MOVE_TAB_LEFT = 'mvtl'; static const uint32 MSG_MOVE_TAB_RIGHT = 'mvtr'; +static const uint32 MSG_TAB_TITLE_CHANGED = 'ttch'; +static const uint32 MSG_WINDOW_TITLE_CHANGED = 'wtch'; // Preference Read/Write Keys static const char* const PREF_HALF_FONT_FAMILY = "Half Font Family"; diff --git a/src/apps/terminal/TermWindow.cpp b/src/apps/terminal/TermWindow.cpp index 1d5f3ae294..c3ff73f342 100644 --- a/src/apps/terminal/TermWindow.cpp +++ b/src/apps/terminal/TermWindow.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include "Globals.h" #include "PrefWindow.h" #include "PrefHandler.h" +#include "SetTitleDialog.h" #include "ShellParameters.h" #include "TermConst.h" #include "TermScrollView.h" @@ -163,6 +165,7 @@ TermWindow::TermWindow(BRect frame, const BString& title, fPrefWindow(NULL), fFindPanel(NULL), fSavedFrame(0, 0, -1, -1), + fSetTabTitleDialog(NULL), fFindString(""), fFindNextMenuItem(NULL), fFindPreviousMenuItem(NULL), @@ -185,6 +188,8 @@ TermWindow::TermWindow(BRect frame, const BString& title, TermWindow::~TermWindow() { + _FinishTitleDialog(); + if (fPrefWindow) fPrefWindow->PostMessage(B_QUIT_REQUESTED); @@ -279,6 +284,8 @@ TermWindow::_CanClose(int32 index) bool TermWindow::QuitRequested() { + _FinishTitleDialog(); + if (!_CanClose(-1)) return false; @@ -688,6 +695,24 @@ TermWindow::MessageReceived(BMessage *message) message->what == MSG_MOVE_TAB_LEFT ? -1 : 1, true); break; + case MSG_TAB_TITLE_CHANGED: + { + // tab title changed message from SetTitleDialog + SessionID sessionID(*message, "session"); + if (Session* session = _SessionForID(sessionID)) { + BString title; + if (message->FindString("title", &title) == B_OK) { + session->title.pattern = title; + session->title.patternUserDefined = true; + } else { + session->title.pattern.Truncate(0); + session->title.patternUserDefined = false; + } + _UpdateSessionTitle(fSessions.IndexOf(session)); + } + break; + } + case kSetActiveTab: { int32 index; @@ -942,6 +967,9 @@ TermWindow::_AddTab(Arguments* args, const BString& currentDirectory) void TermWindow::_RemoveTab(int32 index) { + _FinishTitleDialog(); + // always close to avoid confusion + if (fSessions.CountItems() > 1) { if (!_CanClose(index)) return; @@ -1032,6 +1060,18 @@ TermWindow::_IndexOfTermView(TermView* termView) const } +TermWindow::Session* +TermWindow::_SessionForID(const SessionID& sessionID) const +{ + for (int32 i = 0; Session* session = (Session*)fSessions.ItemAt(i); i++) { + if (session->id == sessionID) + return session; + } + + return NULL; +} + + void TermWindow::_CheckChildren() { @@ -1074,7 +1114,32 @@ void TermWindow::TabDoubleClicked(SmartTabView* tabView, BPoint point, int32 index) { if (index >= 0) { - // TODO: Open the change title dialog! + // If a dialog is active, finish it. + _FinishTitleDialog(); + + BString toolTip = BString(B_TRANSLATE( + "The pattern specifying the current tab title. The following " + "placeholders\n" + "can be used:\n")) << kTooTipSetTabTitlePlaceholders; + fSetTabTitleDialog = new SetTitleDialog( + B_TRANSLATE("Set tab title"), B_TRANSLATE("Tab title:"), + toolTip); + + Session* session = (Session*)fSessions.ItemAt(index); + bool userDefined = session->title.patternUserDefined; + const BString& title = userDefined + ? session->title.pattern : fSessionTitlePattern; + fSetTabTitleSession = session->id; + + // place the dialog window directly under the tab, but keep it on screen + BPoint location = tabView->ConvertToScreen( + tabView->TabFrame(index).LeftBottom() + BPoint(0, 1)); + BRect frame(fSetTabTitleDialog->Frame().OffsetToCopy(location)); + BSize screenSize(BScreen(fSetTabTitleDialog).Frame().Size()); + fSetTabTitleDialog->MoveTo( + BLayoutUtils::MoveIntoFrame(frame, screenSize).LeftTop()); + + fSetTabTitleDialog->Go(title, userDefined, this); } else { // not clicked on a tab -- create a new one _NewTab(); @@ -1139,6 +1204,33 @@ TermWindow::SetTermViewTitle(TermView* view, const char* title) } +void +TermWindow::TitleChanged(SetTitleDialog* dialog, const BString& title, + bool titleUserDefined) +{ + if (dialog != fSetTabTitleDialog) + return; + + BMessage message(MSG_TAB_TITLE_CHANGED); + fSetTabTitleSession.AddToMessage(message, "session"); + if (titleUserDefined) + message.AddString("title", title); + + PostMessage(&message); +} + + +void +TermWindow::SetTitleDialogDone(SetTitleDialog* dialog) +{ + if (dialog == fSetTabTitleDialog) { + fSetTabTitleSession = SessionID(); + fSetTabTitleDialog = NULL; + // assuming this is atomic + } +} + + void TermWindow::PreviousTermView(TermView* view) { @@ -1285,6 +1377,21 @@ TermWindow::_UpdateSessionTitle(int32 index) } +void +TermWindow::_FinishTitleDialog() +{ + SetTitleDialog* oldDialog = fSetTabTitleDialog; + if (oldDialog != NULL && oldDialog->Lock()) { + // might have been unset in the meantime, so recheck + if (fSetTabTitleDialog == oldDialog) { + oldDialog->Finish(); + // this also unsets the variables + } + oldDialog->Unlock(); + } +} + + TermWindow::SessionID TermWindow::_NewSessionID() { diff --git a/src/apps/terminal/TermWindow.h b/src/apps/terminal/TermWindow.h index 7a92312795..db41d905c7 100644 --- a/src/apps/terminal/TermWindow.h +++ b/src/apps/terminal/TermWindow.h @@ -37,6 +37,7 @@ #include #include "SmartTabView.h" +#include "SetTitleDialog.h" #include "TermView.h" @@ -50,7 +51,7 @@ class TermViewContainerView; class TermWindow : public BWindow, private SmartTabView::Listener, - private TermView::Listener { + private TermView::Listener, private SetTitleDialog::Listener { public: TermWindow(BRect frame, const BString& title, bool isUserDefinedTitle, int32 windowIndex, @@ -85,6 +86,12 @@ private: virtual void PreviousTermView(TermView* view); virtual void NextTermView(TermView* view); + // SetTitleDialog::Listener + virtual void TitleChanged(SetTitleDialog* dialog, + const BString& title, + bool titleUserDefined); + virtual void SetTitleDialogDone(SetTitleDialog* dialog); + private: struct Title { BString title; @@ -93,13 +100,18 @@ private: }; struct SessionID { - SessionID(int32 id); + SessionID(int32 id = -1); SessionID(const BMessage& message, const char* field); status_t AddToMessage(BMessage& message, const char* field) const; + bool operator==(const SessionID& other) const + { return fID == other.fID; } + bool operator!=(const SessionID& other) const + { return !(*this == other); } + private: int32 fID; }; @@ -125,17 +137,21 @@ private: bool move); bool _CanClose(int32 index); + TermViewContainerView* _ActiveTermViewContainerView() const; TermViewContainerView* _TermViewContainerViewAt(int32 index) const; TermView* _ActiveTermView() const; TermView* _TermViewAt(int32 index) const; int32 _IndexOfTermView(TermView* termView) const; + Session* _SessionForID(const SessionID& sessionID) const; + void _CheckChildren(); void _ResizeView(TermView* view); void _TitleSettingsChanged(); void _UpdateTitles(); void _UpdateSessionTitle(int32 index); + void _FinishTitleDialog(); SessionID _NewSessionID(); int32 _NewSessionIndex(); @@ -150,7 +166,6 @@ private: int32 fNextSessionID; SmartTabView* fTabView; - TermView* fTermView; BMenuBar* fMenubar; BMenu* fFilemenu; @@ -166,6 +181,9 @@ private: BRect fSavedFrame; window_look fSavedLook; + SetTitleDialog* fSetTabTitleDialog; + SessionID fSetTabTitleSession; + // Saved search parameters BString fFindString; BMenuItem* fFindNextMenuItem;