blob: ccf317189116885d5aea5e80700b63eac5e2c15d [file] [log] [blame]
Sébastien Blin05752142017-10-03 11:25:02 -04001/****************************************************************************
2 * Copyright (C) 2017 Savoir-faire Linux *
3 * 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 Blin55bff9d2017-10-03 15:15:23 -040055remove_history_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
Sébastien Blin05752142017-10-03 11:25:02 -040056{
57 try
58 {
59 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
60 priv->accountContainer_->info.conversationModel->clearHistory(conversation.uid);
61 }
62 catch (...)
63 {
64 g_warning("Can't get conversation at row %i", priv->row_);
65 }
66}
67
68static void
69remove_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
70{
71 try
72 {
73 auto conversationUid = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_).uid;
74 priv->accountContainer_->info.conversationModel->removeConversation(conversationUid);
75 }
76 catch (...)
77 {
78 g_warning("Can't get conversation at row %i", priv->row_);
79 }
80}
81
82static void
83block_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
84{
85 try
86 {
87 auto conversationUid = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_).uid;
88 priv->accountContainer_->info.conversationModel->removeConversation(conversationUid, true);
89 }
90 catch (...)
91 {
92 g_warning("Can't get conversation at row %i", priv->row_);
93 }
94}
95
96static void
97add_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
98{
99 try
100 {
101 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
102 priv->accountContainer_->info.conversationModel->makePermanent(conversation.uid);
103 }
104 catch (...)
105 {
106 g_warning("Can't get conversation at row %i", priv->row_);
107 }
108}
109
110static void
111place_call(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
112{
113 try
114 {
115 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
116 priv->accountContainer_->info.conversationModel->placeCall(conversation.uid);
117 }
118 catch (...)
119 {
120 g_warning("Can't get conversation at row %i", priv->row_);
121 }
122}
123
124/**
125 * Update the menu when the selected conversation in the treeview changes.
126 */
127static void
128update(GtkTreeSelection *selection, ConversationPopupMenu *self)
129{
130 ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
131 /* clear the current menu */
132 gtk_container_forall(GTK_CONTAINER(self), (GtkCallback)gtk_widget_destroy, nullptr);
133
134 // Retrieve conversation
135 GtkTreeIter iter;
136 GtkTreeModel *model;
137 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
138 auto path = gtk_tree_model_get_path(model, &iter);
139 auto idx = gtk_tree_path_get_indices(path);
140 auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(idx[0]);
141 priv->row_ = idx[0];
142 auto contactInfo = priv->accountContainer_->info.contactModel->getContact(conversation.participants.front());
143 if (contactInfo.profileInfo.uri.empty()) return;
144
145 // we always build a menu, however in some cases some or all of the conversations will be deactivated
146 // we prefer this to having an empty menu because GTK+ behaves weird in the empty menu case
147 auto place_call_conversation = gtk_menu_item_new_with_mnemonic(_("_Place call"));
148 gtk_menu_shell_append(GTK_MENU_SHELL(self), place_call_conversation);
149 g_signal_connect(place_call_conversation, "activate", G_CALLBACK(place_call), priv);
150 if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
151 contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
152 // If we can add this conversation
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400153 auto add_conversation_conversation = gtk_menu_item_new_with_mnemonic(_("_Add to conversations"));
Sébastien Blin05752142017-10-03 11:25:02 -0400154 gtk_menu_shell_append(GTK_MENU_SHELL(self), add_conversation_conversation);
155 g_signal_connect(add_conversation_conversation, "activate", G_CALLBACK(add_conversation), priv);
156 if (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
157 auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Discard invitation"));
158 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
159 g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
160 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block invitations"));
161 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
162 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
163 }
164 } else {
165 auto rm_history_conversation = gtk_menu_item_new_with_mnemonic(_("_Clear history"));
166 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_history_conversation);
167 g_signal_connect(rm_history_conversation, "activate", G_CALLBACK(remove_history_conversation), priv);
168 auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Remove conversation"));
169 gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
170 g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
171 auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block contact"));
172 gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
173 g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
174 }
175
176 /* show all conversations */
177 gtk_widget_show_all(GTK_WIDGET(self));
178
179}
180
181static void
182conversation_popup_menu_dispose(GObject *object)
183{
184 G_OBJECT_CLASS(conversation_popup_menu_parent_class)->dispose(object);
185}
186
187static void
188conversation_popup_menu_finalize(GObject *object)
189{
190 G_OBJECT_CLASS(conversation_popup_menu_parent_class)->finalize(object);
191}
192
193static void
194conversation_popup_menu_class_init(ConversationPopupMenuClass *klass)
195{
196 G_OBJECT_CLASS(klass)->finalize = conversation_popup_menu_finalize;
197 G_OBJECT_CLASS(klass)->dispose = conversation_popup_menu_dispose;
198}
199
200
201static void
202conversation_popup_menu_init(G_GNUC_UNUSED ConversationPopupMenu *self)
203{
204 // nothing to do
205}
206
207GtkWidget *
208conversation_popup_menu_new (GtkTreeView *treeview, AccountContainer* accountContainer)
209{
210 gpointer self = g_object_new(CONVERSATION_POPUP_MENU_TYPE, NULL);
211 ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
212 priv->accountContainer_ = accountContainer;
213
214 priv->treeview = treeview;
215 GtkTreeSelection *selection = gtk_tree_view_get_selection(priv->treeview);
216
217 // build the menu for the first time
218 update(selection, CONVERSATION_POPUP_MENU(self));
219
220 return (GtkWidget *)self;
221}
222
223gboolean
224conversation_popup_menu_show(ConversationPopupMenu *self, GdkEventButton *event)
225{
226 if (!self) return GDK_EVENT_PROPAGATE;
227 if (event->type == GDK_BUTTON_PRESS
228 && event->button == GDK_BUTTON_SECONDARY) {
229 // Show popup menu. Will be updated later.
230 gtk_menu_popup(GTK_MENU(self), NULL, NULL, NULL, NULL, event->button, event->time);
231 }
232
233 return GDK_EVENT_PROPAGATE; // so that the conversation selection changes
234}