ConversationListView.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
  3. * All rights reserved. Distributed under the terms of the MIT license.
  4. */
  5. #include "ConversationListView.h"
  6. #include <Catalog.h>
  7. #include <PopUpMenu.h>
  8. #include <MenuItem.h>
  9. #include <Window.h>
  10. #include "AppMessages.h"
  11. #include "ChatProtocolMessages.h"
  12. #include "Conversation.h"
  13. #include "ConversationAccountItem.h"
  14. #include "ConversationItem.h"
  15. #include "MainWindow.h"
  16. #include "ProtocolLooper.h"
  17. #include "Server.h"
  18. #include "TheApp.h"
  19. #undef B_TRANSLATION_CONTEXT
  20. #define B_TRANSLATION_CONTEXT "ConversationListView"
  21. const uint32 kOpenSelectedChat = 'CVos';
  22. const uint32 kLeaveSelectedChat = 'CVcs';
  23. static int
  24. compare_by_name(const BListItem* _item1, const BListItem* _item2)
  25. {
  26. ConversationItem* item1 = (ConversationItem*)_item1;
  27. ConversationItem* item2 = (ConversationItem*)_item2;
  28. return strcasecmp(item1->GetConversation()->GetName().String(),
  29. item2->GetConversation()->GetName().String());
  30. }
  31. static int
  32. compare_conversations(const BListItem* _item1, const BListItem* _item2)
  33. {
  34. ConversationItem* item1 = (ConversationItem*)_item1;
  35. ConversationItem* item2 = (ConversationItem*)_item2;
  36. int32 userCount1 = item1->GetConversation()->Users().CountItems();
  37. int32 userCount2 = item2->GetConversation()->Users().CountItems();
  38. // Sort by name among chats/rooms
  39. if ((userCount1 <= 2 && userCount2 <= 2)
  40. || (userCount1 > 2 && userCount2 > 2))
  41. return compare_by_name(item1, item2);
  42. // One-on-one chats should sort above rooms
  43. if (userCount1 <=2 && userCount2 > 2)
  44. return -1;
  45. if (userCount1 > 2 && userCount2 <= 2)
  46. return 1;
  47. return 0;
  48. }
  49. ConversationListView::ConversationListView(const char* name)
  50. : BOutlineListView(name)
  51. {
  52. }
  53. void
  54. ConversationListView::MessageReceived(BMessage* msg)
  55. {
  56. switch (msg->what) {
  57. case kOpenSelectedChat:
  58. {
  59. int32 selIndex = CurrentSelection();
  60. if (selIndex < 0)
  61. break;
  62. ConversationItem* citem
  63. = dynamic_cast<ConversationItem*>(ItemAt(selIndex));
  64. ConversationAccountItem* caitem
  65. = dynamic_cast<ConversationAccountItem*>(ItemAt(selIndex));
  66. if (citem != NULL)
  67. citem->GetConversation()->ShowView(false, true);
  68. else if (caitem != NULL)
  69. caitem->GetLooper()->ShowView();
  70. break;
  71. }
  72. default:
  73. BListView::MessageReceived(msg);
  74. }
  75. }
  76. void
  77. ConversationListView::MouseDown(BPoint where)
  78. {
  79. int32 selection = CurrentSelection();
  80. BOutlineListView::MouseDown(where);
  81. int32 newSel = CurrentSelection();
  82. // Don't allow deselecting anything
  83. if (newSel < 0 && selection >= 0)
  84. Select(selection);
  85. uint32 buttons = 0;
  86. Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
  87. if (!(buttons & B_SECONDARY_MOUSE_BUTTON))
  88. return;
  89. if (CurrentSelection() >= 0) {
  90. BPopUpMenu* menu = _ConversationPopUp();
  91. if (menu != NULL)
  92. menu->Go(ConvertToScreen(where), true, false);
  93. }
  94. else
  95. _BlankPopUp()->Go(ConvertToScreen(where), true, false);
  96. }
  97. void
  98. ConversationListView::SelectionChanged()
  99. {
  100. MessageReceived(new BMessage(kOpenSelectedChat));
  101. }
  102. void
  103. ConversationListView::RemoveItemSelecting(BListItem* item)
  104. {
  105. int32 selection = CurrentSelection();
  106. int32 itemIndex = IndexOf(item);
  107. RemoveItem(item);
  108. if (itemIndex == selection && CountItems() > selection)
  109. Select(selection);
  110. else if (itemIndex == selection && CountItems() >= 1)
  111. Select(0);
  112. }
  113. void
  114. ConversationListView::AddConversation(Conversation* chat)
  115. {
  116. ConversationAccountItem* superItem = _EnsureAccountItem(chat);
  117. ConversationItem* item = chat->GetListItem();
  118. if (superItem == NULL || item == NULL)
  119. return;
  120. AddUnder(item, superItem);
  121. SortItemsUnder(superItem, true, compare_conversations);
  122. }
  123. void
  124. ConversationListView::RemoveConversation(Conversation* chat)
  125. {
  126. RemoveItemSelecting(chat->GetListItem());
  127. }
  128. void
  129. ConversationListView::AddAccount(int64 instance)
  130. {
  131. Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
  132. ProtocolLooper* looper = server->GetProtocolLooper(instance);
  133. if (looper == NULL)
  134. return;
  135. AddItem(looper->GetListItem());
  136. if (CurrentSelection() < 0)
  137. Select(0);
  138. }
  139. void
  140. ConversationListView::RemoveAccount(int64 instance)
  141. {
  142. int32 selection = 0;
  143. for (int i = 0; i < CountItems(); i++) {
  144. ConversationAccountItem* item
  145. = dynamic_cast<ConversationAccountItem*>(ItemAt(i));
  146. if (item != NULL && item->GetInstance() == instance) {
  147. RemoveItemSelecting(item);
  148. break;
  149. }
  150. }
  151. if (CountItems() == 0)
  152. ((TheApp*)be_app)->GetMainWindow()->SetConversation(NULL);
  153. }
  154. void
  155. ConversationListView::SortConversation(Conversation* chat)
  156. {
  157. ConversationAccountItem* superItem = _EnsureAccountItem(chat);
  158. SortItemsUnder(superItem, true, compare_conversations);
  159. }
  160. BPopUpMenu*
  161. ConversationListView::_ConversationPopUp()
  162. {
  163. BPopUpMenu* menu = new BPopUpMenu("chatPopUp");
  164. menu->SetRadioMode(false);
  165. int32 selIndex = CurrentSelection();
  166. ConversationItem* item;
  167. if ((item = dynamic_cast<ConversationItem*>(ItemAt(selIndex))) == NULL)
  168. return NULL;
  169. Conversation* chat = item->GetConversation();
  170. ProtocolLooper* looper = chat->GetProtocolLooper();
  171. Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
  172. _AddDefaultItems(menu, chat);
  173. BObjectList<BMessage> items = looper->Protocol()->ChatPopUpItems();
  174. if (items.CountItems() > 0)
  175. menu->AddSeparatorItem();
  176. for (int i = 0; i < items.CountItems(); i++) {
  177. BMessage* itemMsg = items.ItemAt(i);
  178. BMessage* msg = new BMessage(*itemMsg);
  179. BMessage toSend;
  180. msg->FindMessage("_msg", &toSend);
  181. toSend.AddString("chat_id", chat->GetId());
  182. toSend.AddInt64("instance", looper->GetInstance());
  183. msg->ReplaceMessage("_msg", &toSend);
  184. BMenuItem* item = new BMenuItem(msg);
  185. if (msg->GetBool("x_to_protocol", true) == true)
  186. item->SetTarget(looper);
  187. else
  188. item->SetTarget(Window());
  189. menu->AddItem(item);
  190. }
  191. return menu;
  192. }
  193. #define add_flag_item(name, flag) { \
  194. msg = new BMessage(APP_ROOM_FLAG); \
  195. msg->AddString("chat_id", id); \
  196. msg->AddInt64("instance", instance); \
  197. msg->AddInt32("flag", flag); \
  198. \
  199. item = new BMenuItem(name, msg); \
  200. item->SetTarget(Window()); \
  201. \
  202. if (!(chat->DisallowedFlags() &flag)) { \
  203. if (chat->GetFlags() & flag) \
  204. item->SetMarked(true); \
  205. menu->AddItem(item); \
  206. } \
  207. }
  208. void
  209. ConversationListView::_AddDefaultItems(BPopUpMenu* menu,
  210. Conversation* chat)
  211. {
  212. if (chat == NULL || menu == NULL)
  213. return;
  214. BString id = chat->GetId();
  215. ProtocolLooper* looper = chat->GetProtocolLooper();
  216. int64 instance = looper->GetInstance();
  217. BMessage* infoMsg = new BMessage(APP_ROOM_INFO);
  218. infoMsg->AddString("chat_id", id);
  219. infoMsg->AddInt64("instance", instance);
  220. BMenuItem* joinItem = new BMenuItem(B_TRANSLATE("Room info…"), infoMsg);
  221. joinItem->SetTarget(Window());
  222. menu->AddItem(joinItem);
  223. menu->AddSeparatorItem();
  224. BMessage* msg;
  225. BMenuItem* item;
  226. add_flag_item(B_TRANSLATE("Auto-join room"), ROOM_AUTOJOIN);
  227. add_flag_item(B_TRANSLATE("Log messages"), ROOM_LOG_LOCALLY);
  228. add_flag_item(B_TRANSLATE("Notify on every message"), ROOM_NOTIFY_ALL);
  229. add_flag_item(B_TRANSLATE("Notify on direct-messages"), ROOM_NOTIFY_DM);
  230. menu->AddSeparatorItem();
  231. BMessage* leaveMsg = new BMessage(IM_MESSAGE);
  232. leaveMsg->AddInt32("im_what", IM_LEAVE_ROOM);
  233. leaveMsg->AddString("chat_id", id);
  234. leaveMsg->AddInt64("instance", instance);
  235. BMenuItem* leave = new BMenuItem(B_TRANSLATE("Leave room"), leaveMsg);
  236. leave->SetTarget(looper);
  237. menu->AddItem(leave);
  238. }
  239. BPopUpMenu*
  240. ConversationListView::_BlankPopUp()
  241. {
  242. bool enabled = false;
  243. Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
  244. if (server != NULL && server->GetAccounts().CountItems() > 0)
  245. enabled = true;
  246. BPopUpMenu* menu = new BPopUpMenu("blankPopUp");
  247. BMenuItem* newChat = new BMenuItem(B_TRANSLATE("New chat" B_UTF8_ELLIPSIS),
  248. new BMessage(APP_NEW_CHAT), 'M', B_COMMAND_KEY);
  249. newChat->SetEnabled(enabled);
  250. menu->AddItem(newChat);
  251. menu->SetTargetForItems(Window());
  252. return menu;
  253. }
  254. ConversationAccountItem*
  255. ConversationListView::_EnsureAccountItem(Conversation* chat)
  256. {
  257. ProtocolLooper* looper;
  258. if (chat == NULL || (looper = chat->GetProtocolLooper()) == NULL)
  259. return NULL;
  260. ConversationAccountItem* item = looper->GetListItem();
  261. if (HasItem(item) == false)
  262. AddItem(item);
  263. return item;
  264. }