blob: c0240dc7bb0d833039f2a1a45d6a7b71e3fcf19a [file] [log] [blame]
Sébastien Blin05752142017-10-03 11:25:02 -04001/****************************************************************************
Guillaume Roguez77c579d2018-01-30 15:54:02 -05002 * Copyright (C) 2017-2018 Savoir-faire Linux *
Sébastien Blin05752142017-10-03 11:25:02 -04003 * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com> *
4 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Lesser General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2.1 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 ***************************************************************************/
19
20#include "conversationpopupmenu.h"
21
22// GTK+ related
23#include <glib/gi18n.h>
24
25// Lrc
Hugo Lefeuvreef608ea2018-05-04 17:24:29 -040026#include <accountmodel.h>
27#include <bannedcontactmodel.h>
28#include <contactmethod.h>
Sébastien Blin05752142017-10-03 11:25:02 -040029#include <api/conversationmodel.h>
30#include <api/contactmodel.h>
31#include <api/contact.h>
32
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040033#include "accountinfopointer.h"
34
Hugo Lefeuvreef608ea2018-05-04 17:24:29 -040035// Qt
36#include <QItemSelectionModel>
37
Sébastien Blin05752142017-10-03 11:25:02 -040038struct _ConversationPopupMenu
39{
40 GtkMenu parent;
41};
42
43struct _ConversationPopupMenuClass
44{
45 GtkMenuClass parent_class;
46};
47
48typedef struct _ConversationPopupMenuPrivate ConversationPopupMenuPrivate;
49
50struct _ConversationPopupMenuPrivate
51{
52 GtkTreeView *treeview;
53
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040054 AccountInfoPointer const *accountInfo_;
Sébastien Blin05752142017-10-03 11:25:02 -040055 int row_;
56};
57
58G_DEFINE_TYPE_WITH_PRIVATE(ConversationPopupMenu, conversation_popup_menu, GTK_TYPE_MENU);
59
60#define CONVERSATION_POPUP_MENU_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CONVERSATION_POPUP_MENU_TYPE, ConversationPopupMenuPrivate))
61
62static void
Sébastien Blind6787ad2017-11-27 09:40:38 -050063copy_contact_info(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
64{
65 try
66 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040067 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_);
Sébastien Blind6787ad2017-11-27 09:40:38 -050068 if (conversation.participants.empty()) return;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040069 auto& contact = (*priv->accountInfo_)->contactModel->getContact(conversation.participants.front());
Sébastien Blind6787ad2017-11-27 09:40:38 -050070 auto bestName = contact.registeredName.empty() ? contact.profileInfo.uri : contact.registeredName;
71 auto text = (gchar *)bestName.c_str();
72 GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
73 gtk_clipboard_set_text(clip, text, -1);
74 clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
75 gtk_clipboard_set_text(clip, text, -1);
76 }
77 catch (...)
78 {
79 g_warning("Can't get conversation at row %i", priv->row_);
80 }
81}
82
83static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -040084remove_history_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
Sébastien Blin05752142017-10-03 11:25:02 -040085{
86 try
87 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040088 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_);
89 (*priv->accountInfo_)->conversationModel->clearHistory(conversation.uid);
Sébastien Blin05752142017-10-03 11:25:02 -040090 }
91 catch (...)
92 {
93 g_warning("Can't get conversation at row %i", priv->row_);
94 }
95}
96
97static void
98remove_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
99{
100 try
101 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400102 auto conversationUid = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_).uid;
103 (*priv->accountInfo_)->conversationModel->removeConversation(conversationUid);
Sébastien Blin05752142017-10-03 11:25:02 -0400104 }
105 catch (...)
106 {
107 g_warning("Can't get conversation at row %i", priv->row_);
108 }
109}
110
111static void
Hugo Lefeuvreef608ea2018-05-04 17:24:29 -0400112unblock_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
113{
114 try
115 {
116 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_);
117 auto uri = conversation.participants[0];
118
119 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(uri);
120
121 if (!contactInfo.isBanned) {
122 g_debug("unblock_conversation: trying to unban a contact which isn't banned !");
123 return;
124 }
125
126 (*priv->accountInfo_)->contactModel->addContact(contactInfo);
127 }
128 catch (...)
129 {
130 g_warning("Can't get conversation at row %i", priv->row_);
131 }
132}
133
134static void
Sébastien Blin05752142017-10-03 11:25:02 -0400135block_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
136{
137 try
138 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400139 auto conversationUid = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_).uid;
140 (*priv->accountInfo_)->conversationModel->removeConversation(conversationUid, true);
Sébastien Blin05752142017-10-03 11:25:02 -0400141 }
142 catch (...)
143 {
144 g_warning("Can't get conversation at row %i", priv->row_);
145 }
146}
147
148static void
149add_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
150{
151 try
152 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400153 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_);
154 (*priv->accountInfo_)->conversationModel->makePermanent(conversation.uid);
Sébastien Blin05752142017-10-03 11:25:02 -0400155 }
156 catch (...)
157 {
158 g_warning("Can't get conversation at row %i", priv->row_);
159 }
160}
161
162static void
Sébastien Blin69606242018-03-06 10:30:51 -0500163place_video_call(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
Sébastien Blin05752142017-10-03 11:25:02 -0400164{
165 try
166 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400167 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_);
168 (*priv->accountInfo_)->conversationModel->placeCall(conversation.uid);
Sébastien Blin69606242018-03-06 10:30:51 -0500169 } catch (...) {
170 g_warning("Can't get conversation at row %i", priv->row_);
Sébastien Blin05752142017-10-03 11:25:02 -0400171 }
Sébastien Blin69606242018-03-06 10:30:51 -0500172}
173
174static void
175place_audio_call(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
176{
177 try
Sébastien Blin05752142017-10-03 11:25:02 -0400178 {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400179 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(priv->row_);
180 (*priv->accountInfo_)->conversationModel->placeAudioOnlyCall(conversation.uid);
Sébastien Blin69606242018-03-06 10:30:51 -0500181 } catch (...) {
Sébastien Blin05752142017-10-03 11:25:02 -0400182 g_warning("Can't get conversation at row %i", priv->row_);
183 }
184}
185
186/**
187 * Update the menu when the selected conversation in the treeview changes.
188 */
189static void
190update(GtkTreeSelection *selection, ConversationPopupMenu *self)
191{
192 ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
193 /* clear the current menu */
194 gtk_container_forall(GTK_CONTAINER(self), (GtkCallback)gtk_widget_destroy, nullptr);
195
196 // Retrieve conversation
197 GtkTreeIter iter;
198 GtkTreeModel *model;
199 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
200 auto path = gtk_tree_model_get_path(model, &iter);
201 auto idx = gtk_tree_path_get_indices(path);
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400202 auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(idx[0]);
Sébastien Blin05752142017-10-03 11:25:02 -0400203 priv->row_ = idx[0];
Guillaume Roguezc2095922017-12-14 14:07:10 -0500204 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400205 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(conversation.participants.front());
Guillaume Roguezc2095922017-12-14 14:07:10 -0500206 if (contactInfo.profileInfo.uri.empty()) return;
Sébastien Blin05752142017-10-03 11:25:02 -0400207
Guillaume Roguezc2095922017-12-14 14:07:10 -0500208 // we always build a menu, however in some cases some or all of the conversations will be deactivated
209 // we prefer this to having an empty menu because GTK+ behaves weird in the empty menu case
Sébastien Blin69606242018-03-06 10:30:51 -0500210 auto callId = conversation.confId.empty() ? conversation.callId : conversation.confId;
Hugo Lefeuvreef608ea2018-05-04 17:24:29 -0400211
Sébastien Blin69606242018-03-06 10:30:51 -0500212 // Not in call
Hugo Lefeuvreef608ea2018-05-04 17:24:29 -0400213 if (!contactInfo.isBanned) {
214 auto place_video_call_conversation = gtk_menu_item_new_with_mnemonic(_("Place _video call"));
215 gtk_menu_shell_append(GTK_MENU_SHELL(self), place_video_call_conversation);
216 g_signal_connect(place_video_call_conversation, "activate", G_CALLBACK(place_video_call), priv);
217 auto place_audio_call_conversation = gtk_menu_item_new_with_mnemonic(_("Place _audio call"));
218 gtk_menu_shell_append(GTK_MENU_SHELL(self), place_audio_call_conversation);
219 g_signal_connect(place_audio_call_conversation, "activate", G_CALLBACK(place_audio_call), priv);
220 }
221
Guillaume Roguezc2095922017-12-14 14:07:10 -0500222 if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
223 contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
224 // If we can add this conversation
225 auto add_conversation_conversation = gtk_menu_item_new_with_mnemonic(_("_Add to conversations"));
226 gtk_menu_shell_append(GTK_MENU_SHELL(self), add_conversation_conversation);
227 g_signal_connect(add_conversation_conversation, "activate", G_CALLBACK(add_conversation), priv);
228 if (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
229 auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Discard invitation"));
230 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
231 g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
232 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block invitations"));
233 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
234 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
235 }
236 } else {
237 auto rm_history_conversation = gtk_menu_item_new_with_mnemonic(_("C_lear history"));
238 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_history_conversation);
239 g_signal_connect(rm_history_conversation, "activate", G_CALLBACK(remove_history_conversation), priv);
240 auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Remove conversation"));
Sébastien Blin05752142017-10-03 11:25:02 -0400241 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
242 g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
Hugo Lefeuvreef608ea2018-05-04 17:24:29 -0400243
244 if (!contactInfo.isBanned) {
245 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block contact"));
246 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
247 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
248 } else {
249 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Unblock contact"));
250 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
251 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(unblock_conversation), priv);
252 }
Sébastien Blin05752142017-10-03 11:25:02 -0400253 }
Guillaume Roguezc2095922017-12-14 14:07:10 -0500254
Sébastien Blin69606242018-03-06 10:30:51 -0500255 auto copy_name = gtk_menu_item_new_with_mnemonic(_("_Copy name"));
256 gtk_menu_shell_append(GTK_MENU_SHELL(self), copy_name);
257 g_signal_connect(copy_name, "activate", G_CALLBACK(copy_contact_info), priv);
258
Guillaume Roguezc2095922017-12-14 14:07:10 -0500259 /* show all conversations */
260 gtk_widget_show_all(GTK_WIDGET(self));
261 } catch (const std::out_of_range&) {
262 // ContactModel::getContact() exception
Sébastien Blin05752142017-10-03 11:25:02 -0400263 }
Sébastien Blin05752142017-10-03 11:25:02 -0400264}
265
266static void
267conversation_popup_menu_dispose(GObject *object)
268{
269 G_OBJECT_CLASS(conversation_popup_menu_parent_class)->dispose(object);
270}
271
272static void
273conversation_popup_menu_finalize(GObject *object)
274{
275 G_OBJECT_CLASS(conversation_popup_menu_parent_class)->finalize(object);
276}
277
278static void
279conversation_popup_menu_class_init(ConversationPopupMenuClass *klass)
280{
281 G_OBJECT_CLASS(klass)->finalize = conversation_popup_menu_finalize;
282 G_OBJECT_CLASS(klass)->dispose = conversation_popup_menu_dispose;
283}
284
285
286static void
287conversation_popup_menu_init(G_GNUC_UNUSED ConversationPopupMenu *self)
288{
289 // nothing to do
290}
291
292GtkWidget *
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400293conversation_popup_menu_new (GtkTreeView *treeview, AccountInfoPointer const & accountInfo)
Sébastien Blin05752142017-10-03 11:25:02 -0400294{
295 gpointer self = g_object_new(CONVERSATION_POPUP_MENU_TYPE, NULL);
296 ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400297 priv->accountInfo_ = &accountInfo;
Sébastien Blin05752142017-10-03 11:25:02 -0400298
299 priv->treeview = treeview;
300 GtkTreeSelection *selection = gtk_tree_view_get_selection(priv->treeview);
301
302 // build the menu for the first time
303 update(selection, CONVERSATION_POPUP_MENU(self));
304
305 return (GtkWidget *)self;
306}
307
308gboolean
309conversation_popup_menu_show(ConversationPopupMenu *self, GdkEventButton *event)
310{
311 if (!self) return GDK_EVENT_PROPAGATE;
312 if (event->type == GDK_BUTTON_PRESS
313 && event->button == GDK_BUTTON_SECONDARY) {
314 // Show popup menu. Will be updated later.
315 gtk_menu_popup(GTK_MENU(self), NULL, NULL, NULL, NULL, event->button, event->time);
316 }
317
318 return GDK_EVENT_PROPAGATE; // so that the conversation selection changes
319}