blob: 37fdfeb70707ecd6b7adc66134ad3b16c2e9cf29 [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
26#include <api/conversationmodel.h>
27#include <api/contactmodel.h>
28#include <api/contact.h>
29
30struct _ConversationPopupMenu
31{
32 GtkMenu parent;
33};
34
35struct _ConversationPopupMenuClass
36{
37 GtkMenuClass parent_class;
38};
39
40typedef struct _ConversationPopupMenuPrivate ConversationPopupMenuPrivate;
41
42struct _ConversationPopupMenuPrivate
43{
44 GtkTreeView *treeview;
45
46 AccountContainer* accountContainer_;
47 int row_;
48};
49
50G_DEFINE_TYPE_WITH_PRIVATE(ConversationPopupMenu, conversation_popup_menu, GTK_TYPE_MENU);
51
52#define CONVERSATION_POPUP_MENU_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CONVERSATION_POPUP_MENU_TYPE, ConversationPopupMenuPrivate))
53
54static void
Sébastien Blind6787ad2017-11-27 09:40:38 -050055copy_contact_info(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
56{
57 try
58 {
59 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
60 if (conversation.participants.empty()) return;
61 auto& contact = priv->accountContainer_->info.contactModel->getContact(conversation.participants.front());
62 auto bestName = contact.registeredName.empty() ? contact.profileInfo.uri : contact.registeredName;
63 auto text = (gchar *)bestName.c_str();
64 GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
65 gtk_clipboard_set_text(clip, text, -1);
66 clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
67 gtk_clipboard_set_text(clip, text, -1);
68 }
69 catch (...)
70 {
71 g_warning("Can't get conversation at row %i", priv->row_);
72 }
73}
74
75static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -040076remove_history_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
Sébastien Blin05752142017-10-03 11:25:02 -040077{
78 try
79 {
80 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
81 priv->accountContainer_->info.conversationModel->clearHistory(conversation.uid);
82 }
83 catch (...)
84 {
85 g_warning("Can't get conversation at row %i", priv->row_);
86 }
87}
88
89static void
90remove_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
91{
92 try
93 {
94 auto conversationUid = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_).uid;
95 priv->accountContainer_->info.conversationModel->removeConversation(conversationUid);
96 }
97 catch (...)
98 {
99 g_warning("Can't get conversation at row %i", priv->row_);
100 }
101}
102
103static void
104block_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
105{
106 try
107 {
108 auto conversationUid = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_).uid;
109 priv->accountContainer_->info.conversationModel->removeConversation(conversationUid, true);
110 }
111 catch (...)
112 {
113 g_warning("Can't get conversation at row %i", priv->row_);
114 }
115}
116
117static void
118add_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
119{
120 try
121 {
122 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
123 priv->accountContainer_->info.conversationModel->makePermanent(conversation.uid);
124 }
125 catch (...)
126 {
127 g_warning("Can't get conversation at row %i", priv->row_);
128 }
129}
130
131static void
132place_call(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
133{
134 try
135 {
136 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
137 priv->accountContainer_->info.conversationModel->placeCall(conversation.uid);
138 }
139 catch (...)
140 {
141 g_warning("Can't get conversation at row %i", priv->row_);
142 }
143}
144
145/**
146 * Update the menu when the selected conversation in the treeview changes.
147 */
148static void
149update(GtkTreeSelection *selection, ConversationPopupMenu *self)
150{
151 ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
152 /* clear the current menu */
153 gtk_container_forall(GTK_CONTAINER(self), (GtkCallback)gtk_widget_destroy, nullptr);
154
155 // Retrieve conversation
156 GtkTreeIter iter;
157 GtkTreeModel *model;
158 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
159 auto path = gtk_tree_model_get_path(model, &iter);
160 auto idx = gtk_tree_path_get_indices(path);
161 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(idx[0]);
162 priv->row_ = idx[0];
Guillaume Roguezc2095922017-12-14 14:07:10 -0500163 try {
164 auto contactInfo = priv->accountContainer_->info.contactModel->getContact(conversation.participants.front());
165 if (contactInfo.profileInfo.uri.empty()) return;
Sébastien Blin05752142017-10-03 11:25:02 -0400166
Guillaume Roguezc2095922017-12-14 14:07:10 -0500167 // we always build a menu, however in some cases some or all of the conversations will be deactivated
168 // we prefer this to having an empty menu because GTK+ behaves weird in the empty menu case
169 auto place_call_conversation = gtk_menu_item_new_with_mnemonic(_("_Place call"));
170 gtk_menu_shell_append(GTK_MENU_SHELL(self), place_call_conversation);
171 g_signal_connect(place_call_conversation, "activate", G_CALLBACK(place_call), priv);
172 auto copy_name = gtk_menu_item_new_with_mnemonic(_("_Copy name"));
173 gtk_menu_shell_append(GTK_MENU_SHELL(self), copy_name);
174 g_signal_connect(copy_name, "activate", G_CALLBACK(copy_contact_info), priv);
175 if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
176 contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
177 // If we can add this conversation
178 auto add_conversation_conversation = gtk_menu_item_new_with_mnemonic(_("_Add to conversations"));
179 gtk_menu_shell_append(GTK_MENU_SHELL(self), add_conversation_conversation);
180 g_signal_connect(add_conversation_conversation, "activate", G_CALLBACK(add_conversation), priv);
181 if (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
182 auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Discard invitation"));
183 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
184 g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
185 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block invitations"));
186 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
187 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
188 }
189 } else {
190 auto rm_history_conversation = gtk_menu_item_new_with_mnemonic(_("C_lear history"));
191 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_history_conversation);
192 g_signal_connect(rm_history_conversation, "activate", G_CALLBACK(remove_history_conversation), priv);
193 auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Remove conversation"));
Sébastien Blin05752142017-10-03 11:25:02 -0400194 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
195 g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500196 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block contact"));
Sébastien Blin05752142017-10-03 11:25:02 -0400197 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
198 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
199 }
Guillaume Roguezc2095922017-12-14 14:07:10 -0500200
201 /* show all conversations */
202 gtk_widget_show_all(GTK_WIDGET(self));
203 } catch (const std::out_of_range&) {
204 // ContactModel::getContact() exception
Sébastien Blin05752142017-10-03 11:25:02 -0400205 }
Sébastien Blin05752142017-10-03 11:25:02 -0400206}
207
208static void
209conversation_popup_menu_dispose(GObject *object)
210{
211 G_OBJECT_CLASS(conversation_popup_menu_parent_class)->dispose(object);
212}
213
214static void
215conversation_popup_menu_finalize(GObject *object)
216{
217 G_OBJECT_CLASS(conversation_popup_menu_parent_class)->finalize(object);
218}
219
220static void
221conversation_popup_menu_class_init(ConversationPopupMenuClass *klass)
222{
223 G_OBJECT_CLASS(klass)->finalize = conversation_popup_menu_finalize;
224 G_OBJECT_CLASS(klass)->dispose = conversation_popup_menu_dispose;
225}
226
227
228static void
229conversation_popup_menu_init(G_GNUC_UNUSED ConversationPopupMenu *self)
230{
231 // nothing to do
232}
233
234GtkWidget *
235conversation_popup_menu_new (GtkTreeView *treeview, AccountContainer* accountContainer)
236{
237 gpointer self = g_object_new(CONVERSATION_POPUP_MENU_TYPE, NULL);
238 ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
239 priv->accountContainer_ = accountContainer;
240
241 priv->treeview = treeview;
242 GtkTreeSelection *selection = gtk_tree_view_get_selection(priv->treeview);
243
244 // build the menu for the first time
245 update(selection, CONVERSATION_POPUP_MENU(self));
246
247 return (GtkWidget *)self;
248}
249
250gboolean
251conversation_popup_menu_show(ConversationPopupMenu *self, GdkEventButton *event)
252{
253 if (!self) return GDK_EVENT_PROPAGATE;
254 if (event->type == GDK_BUTTON_PRESS
255 && event->button == GDK_BUTTON_SECONDARY) {
256 // Show popup menu. Will be updated later.
257 gtk_menu_popup(GTK_MENU(self), NULL, NULL, NULL, NULL, event->button, event->time);
258 }
259
260 return GDK_EVENT_PROPAGATE; // so that the conversation selection changes
261}