diff --git a/application/windows/MainWindow.cpp b/application/windows/MainWindow.cpp index fe1e53c..9633d29 100644 --- a/application/windows/MainWindow.cpp +++ b/application/windows/MainWindow.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,15 @@ MainWindow::Start() } +void +MainWindow::Show() +{ + if (fChatLayout->CountItems() == 0) + SetConversation(NULL); + BWindow::Show(); +} + + bool MainWindow::QuitRequested() { @@ -101,14 +111,20 @@ MainWindow::QuitRequested() button_index = alert->Go(); } - AppPreferences::Get()->MainWindowListWeight - = fSplitView->ItemWeight((int32)0); - AppPreferences::Get()->MainWindowChatWeight - = fSplitView->ItemWeight((int32)1); AppPreferences::Get()->MainWindowRect = Frame(); + _SaveWeights(); - if(button_index == 0) { + if (button_index == 0) { fServer->Quit(); + + // ConversationViews will be removed by Server's deletion of Conversations, + // but some special views (protocol logs, the blank ConversationView) + // must be done manually + for (int i = fChatLayout->CountItems() - 1; i >= 0; i--) + fChatLayout->RemoveItem(i); + fChatLayout->RemoveSelf(); + delete fChatLayout; + AppPreferences::Get()->Save(); ReplicantStatusView::RemoveReplicant(); be_app->PostMessage(B_QUIT_REQUESTED); @@ -282,6 +298,7 @@ MainWindow::MessageReceived(BMessage* message) } case APP_SHOW_HELP: { + // Borrowed from HaikuArchives/Calendar's _ShowHelp() BPathFinder pathFinder; BStringList paths; BPath path; @@ -394,79 +411,52 @@ MainWindow::WorkspaceActivated(int32 workspace, bool active) void MainWindow::SetConversation(Conversation* chat) { - fConversation = chat; - if (chat != NULL) { - SetConversationView(chat->GetView()); - - BString title(chat->GetName()); - title << " ― " << APP_NAME; - SetTitle(title.String()); - } - else { - SetConversationView(fBackupChatView); + if (chat == NULL) { SetTitle(APP_NAME); + SetConversationView(fBackupChatView); + return; } + + _EnsureConversationView(chat); + + bool found = false; + BLayoutItem* item = fChatList.ValueFor(chat, &found); + if (found == false) + return; + + BString title(chat->GetName()); + title << " ― " << APP_NAME; + SetTitle(title.String()); + + // Remove "Protocol" menu + BMenuItem* chatMenuItem = fMenuBar->FindItem(B_TRANSLATE("Chat")); + BMenuItem* protocolMenuItem = fMenuBar->FindItem(B_TRANSLATE("Protocol")); + if (protocolMenuItem != NULL) + fMenuBar->RemoveItem(protocolMenuItem); + + // Populate new "Protocol" menu if need be + BMenu* protocolMenu = _CreateProtocolMenu(); + if (protocolMenu != NULL) + fMenuBar->AddItem(protocolMenu, fMenuBar->IndexOf(chatMenuItem) + 1); + + _SaveWeights(); + fConversation = chat; + fChatLayout->SetVisibleItem(item); + _ApplyWeights(); } void -MainWindow::SetConversationView(ConversationView* chatView) +MainWindow::SetConversationView(ConversationView* view) { - // Save split weights - float weightChat = fRightView->ItemWeight((int32)0); - float weightSend = fRightView->ItemWeight((int32)1); - float horizChat, horizList, vertChat, vertSend; - fChatView->GetWeights(&horizChat, &horizList, &vertChat, &vertSend); + int32 index = fChatLayout->IndexOfView(view); + if (index < 0) { + BLayoutItem* item = fChatLayout->AddView(view); + fChatLayout->SetVisibleItem(item); + } else + fChatLayout->SetVisibleItem(index); - fRightView->RemoveChild(fRightView->FindView("chatView")); - fChatView = chatView; - - fRightView->AddChild(fChatView, 9); - - // Remove "Protocol" menu - BMenuItem* chatMenuItem = fMenuBar->FindItem("Protocol"); - BMenu* chatMenu; - if (chatMenuItem != NULL && (chatMenu = chatMenuItem->Submenu()) != NULL) - fMenuBar->RemoveItem(chatMenu); - - // Add and populate "Protocol" menu, if appropriate - if (fConversation != NULL) { - ProtocolLooper* looper = fConversation->GetProtocolLooper(); - BObjectList menuItems = looper->Protocol()->MenuBarItems(); - for (int i = 0; i < menuItems.CountItems(); i++) { - BMessage* itemMsg = menuItems.ItemAt(i); - BMessage* msg = new BMessage(*itemMsg); - BMessage toSend; - msg->FindMessage("_msg", &toSend); - toSend.AddString("chat_id", fConversation->GetId()); - toSend.AddInt64("instance", looper->GetInstance()); - msg->ReplaceMessage("_msg", &toSend); - - BMenuItem* item = new BMenuItem(msg); - if (item == NULL) - continue; - if (msg->GetBool("x_to_protocol", true) == true) - item->SetTarget(looper); - else - item->SetTarget(this); - chatMenu->AddItem(item); - } - } - - // Apply saved weights - fSplitView->SetItemWeight(0, AppPreferences::Get()->MainWindowListWeight, true); - fSplitView->SetItemWeight(1, AppPreferences::Get()->MainWindowChatWeight, true); - fChatView->SetWeights(horizChat, horizList, vertChat, vertSend); - if (weightChat * weightSend != 0) { - fRightView->SetItemWeight(0, weightChat, true); - fRightView->SetItemWeight(1, weightSend, true); - } - - // Save ChatView weights to settings - AppPreferences::Get()->ChatViewHorizChatWeight = horizChat; - AppPreferences::Get()->ChatViewHorizListWeight = horizList; - AppPreferences::Get()->ChatViewVertChatWeight = vertChat; - AppPreferences::Get()->ChatViewVertSendWeight = vertSend; + _ApplyWeights(); } @@ -475,6 +465,13 @@ MainWindow::RemoveConversation(Conversation* chat) { SetConversation(NULL); + bool found = false; + BLayoutItem* item = fChatList.ValueFor(chat, &found); + if (item != NULL) + item->RemoveSelf(); + if (found == true) + fChatList.RemoveItemFor(chat); + int32 index = fListView->IndexOf(chat->GetListItem()); if (index > 0) index--; @@ -506,17 +503,8 @@ MainWindow::_InitInterface() true, false, B_NO_BORDER); // Right-side of window, Chat + Textbox - fRightView = new BSplitView(B_VERTICAL, 0); + fChatLayout = new BCardLayout(); fBackupChatView = new ConversationView(); - fChatView = fBackupChatView; - - // Load weights from settings - float horizChat, horizList, vertChat, vertSend; - horizChat = AppPreferences::Get()->ChatViewHorizChatWeight; - horizList = AppPreferences::Get()->ChatViewHorizListWeight; - vertChat = AppPreferences::Get()->ChatViewVertChatWeight; - vertSend = AppPreferences::Get()->ChatViewVertSendWeight; - fChatView->SetWeights(horizChat, horizList, vertChat, vertSend); BLayoutBuilder::Group<>(this, B_VERTICAL) .Add((fMenuBar = _CreateMenuBar())) @@ -527,12 +515,11 @@ MainWindow::_InitInterface() .Add(listScroll, 1) .Add(fStatusView) .End() - .Add(fRightView, 5) + .Add(fChatLayout, 5) .End() .End() .End(); - SetConversation(NULL); _ToggleMenuItems(); } @@ -630,6 +617,40 @@ MainWindow::_RefreshAccountsMenu() } +BMenu* +MainWindow::_CreateProtocolMenu() +{ + if (fConversation == NULL) + return NULL; + + ProtocolLooper* looper = fConversation->GetProtocolLooper(); + BObjectList menuItems = looper->Protocol()->MenuBarItems(); + if (menuItems.CountItems() == 0) + return NULL; + + BMenu* menu = new BMenu(B_TRANSLATE("Protocol")); + for (int i = 0; i < menuItems.CountItems(); i++) { + BMessage* itemMsg = menuItems.ItemAt(i); + BMessage* msg = new BMessage(*itemMsg); + BMessage toSend; + msg->FindMessage("_msg", &toSend); + toSend.AddString("chat_id", fConversation->GetId()); + toSend.AddInt64("instance", looper->GetInstance()); + msg->ReplaceMessage("_msg", &toSend); + + BMenuItem* item = new BMenuItem(msg); + if (item == NULL) + continue; + if (msg->GetBool("x_to_protocol", true) == true) + item->SetTarget(looper); + else + item->SetTarget(this); + menu->AddItem(item); + } + return menu; +} + + void MainWindow::_ToggleMenuItems() { @@ -661,10 +682,12 @@ MainWindow::_ToggleMenuItems() ConversationItem* MainWindow::_EnsureConversationItem(BMessage* msg) { - ChatMap chats = fServer->Conversations(); - BString chat_id = msg->FindString("chat_id"); Conversation* chat = fServer->ConversationById(chat_id, msg->FindInt64("instance")); + + _EnsureConversationView(chat); + + // Add conversation item if necessary, return it ConversationItem* item; if (chat != NULL && (item = chat->GetListItem()) != NULL) { if (fListView->HasItem(item)) @@ -682,6 +705,62 @@ MainWindow::_EnsureConversationItem(BMessage* msg) } +void +MainWindow::_EnsureConversationView(Conversation* chat) +{ + // Add conversation's view if necessary + bool added = false; + fChatList.ValueFor(chat, &added); + + if (added == false) { + BLayoutItem* item = fChatLayout->AddView(chat->GetView()); + if (item != NULL) + fChatList.AddItem(chat, item); + } +} + + +void +MainWindow::_ApplyWeights() +{ + // Chat-list and chat-view splitter + fSplitView->SetItemWeight(0, AppPreferences::Get()->MainWindowListWeight, true); + fSplitView->SetItemWeight(1, AppPreferences::Get()->MainWindowChatWeight, true); + + // Chat-view's weights + float horizChat, horizList, vertChat, vertSend; + horizChat = AppPreferences::Get()->ChatViewHorizChatWeight; + horizList = AppPreferences::Get()->ChatViewHorizListWeight; + vertChat = AppPreferences::Get()->ChatViewVertChatWeight; + vertSend = AppPreferences::Get()->ChatViewVertSendWeight; + + BLayoutItem* item = fChatLayout->VisibleItem(); + if (item != NULL) + ((ConversationView*)item->View())->SetWeights(horizChat, horizList, vertChat, vertSend); +} + + +void +MainWindow::_SaveWeights() +{ + // Chat-list and chat-view splitter + AppPreferences::Get()->MainWindowListWeight = fSplitView->ItemWeight((int32)0); + AppPreferences::Get()->MainWindowChatWeight = fSplitView->ItemWeight((int32)1); + + // ChatView's weights + float horizChat, horizList, vertChat, vertSend; + BLayoutItem* item = fChatLayout->VisibleItem(); + if (item == NULL) + return; + ((ConversationView*)item->View())->GetWeights(&horizChat, &horizList, &vertChat, &vertSend); + + AppPreferences::Get()->ChatViewHorizChatWeight = horizChat; + AppPreferences::Get()->ChatViewHorizListWeight = horizList; + AppPreferences::Get()->ChatViewVertChatWeight = vertChat; + AppPreferences::Get()->ChatViewVertSendWeight = vertSend; +} + + bool MainWindow::_PopulateWithAccounts(BMenu* menu, ProtocolSettings* settings) { diff --git a/application/windows/MainWindow.h b/application/windows/MainWindow.h index f0e5e71..c266b8f 100644 --- a/application/windows/MainWindow.h +++ b/application/windows/MainWindow.h @@ -1,7 +1,7 @@ /* - * Copyright 2021, Jaidyn Levesque. All rights reserved. * Copyright 2009-2011, Andrea Anzani. All rights reserved. * Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved. + * Copyright 2021-2022, Jaidyn Levesque. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _MAIN_WINDOW_H @@ -11,6 +11,8 @@ #include "Server.h" +class BCardLayout; +class BLayoutItem; class BMenu; class BSplitView; class BTextView; @@ -31,6 +33,7 @@ public: MainWindow(); void Start(); + void Show(); virtual bool QuitRequested(); virtual void MessageReceived(BMessage* message); @@ -40,7 +43,7 @@ public: bool active); void SetConversation(Conversation* chat); - void SetConversationView(ConversationView* chatView); + void SetConversationView(ConversationView* view); void RemoveConversation(Conversation* chat); void SortConversation(Conversation* chat); @@ -52,11 +55,16 @@ private: BMenuBar* _CreateMenuBar(); BMenu* _CreateAccountsMenu(); void _RefreshAccountsMenu(); + BMenu* _CreateProtocolMenu(); void _ToggleMenuItems(); ConversationItem* _EnsureConversationItem(BMessage* msg); + void _EnsureConversationView(Conversation* chat); + + void _ApplyWeights(); + void _SaveWeights(); bool _PopulateWithAccounts(BMenu* menu, ProtocolSettings* settings); @@ -67,15 +75,16 @@ private: bool fWorkspaceChanged; BMenuBar* fMenuBar; + KeyMap fChatList; + // Left panel, chat list ConversationListView* fListView; StatusView* fStatusView; BSplitView* fSplitView; // Right panel, chat - BSplitView* fRightView; + BCardLayout* fChatLayout; Conversation* fConversation; - ConversationView* fChatView; ConversationView* fBackupChatView; }; diff --git a/libs/libsupport/KeyMap.h b/libs/libsupport/KeyMap.h index 7d2275b..c3afe67 100644 --- a/libs/libsupport/KeyMap.h +++ b/libs/libsupport/KeyMap.h @@ -17,6 +17,7 @@ public: void AddItem(KEY k, TYPE t); TYPE ValueFor(KEY, bool* found = NULL) const; + KEY KeyFor(TYPE, bool* found = NULL) const; TYPE RemoveItemAt(int32 position); TYPE RemoveItemFor(KEY); @@ -68,6 +69,20 @@ KeyMap::ValueFor(KEY k, bool* found) const } +template +inline KEY +KeyMap::KeyFor(TYPE v, bool* found) const +{ + *found = false; + for (int32 i = 0; i < CountItems(); i++) + if (ValueAt(i) == v) { + *found = true; + return KeyAt(i); + } + return NULL; +} + + template inline TYPE KeyMap::RemoveItemAt(int32 position)