blob: b625813f3cf9eca154b571d5d855eae0721427e1 [file] [log] [blame]
Nicolas Jager0efc8432017-03-22 16:22:00 -04001/*
2 * Copyright (C) 2017 Savoir-faire Linux Inc.
3 * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20// Client
21#include "pendingcontactrequests.h"
22#include "models/gtkqtreemodel.h"
23#include "native/pixbufmanipulator.h"
Nicolas Jager8c73cd12017-05-03 14:14:03 -040024#include "utils/accounts.h"
Nicolas Jager0efc8432017-03-22 16:22:00 -040025
26// LRC
27#include <recentmodel.h>
28#include <accountmodel.h>
29#include <pendingcontactrequestmodel.h>
30#include <account.h>
Nicolas Jager8a6e10a2017-04-18 09:13:57 -040031#include <availableaccountmodel.h>
Nicolas Jager7b9f5922017-04-26 08:40:56 -040032#include <globalinstances.h>
33#include <contactrequest.h>
Nicolas Jager0efc8432017-03-22 16:22:00 -040034
Nicolas Jager7b9f5922017-04-26 08:40:56 -040035// Gtk
Nicolas Jager0efc8432017-03-22 16:22:00 -040036#include <gtk/gtk.h>
37#include <glib/gi18n.h>
38
Nicolas Jager7b9f5922017-04-26 08:40:56 -040039// Qt
40#include <QtCore/QDateTime>
41#include <QtCore/QSize>
42#include <QtCore/QLocale>
43
Nicolas Jager0efc8432017-03-22 16:22:00 -040044/**
45 * gtk structure
46 */
47struct _PendingContactRequestsView
48{
49 GtkTreeView parent;
50};
51
52/**
53 * gtk class structure
54 */
55struct _PendingContactRequestsViewClass
56{
57 GtkTreeViewClass parent_class;
58};
59
60typedef struct _PendingContactRequestsViewPrivate PendingContactRequestsViewPrivate;
61
62/**
63 * gtk private structure
64 */
65struct _PendingContactRequestsViewPrivate
66{
67 GtkWidget *treeview_pending_contact_request_list;
68 QSortFilterProxyModel* myProxy;
69};
70
71G_DEFINE_TYPE_WITH_PRIVATE(PendingContactRequestsView, pending_contact_requests_view, GTK_TYPE_TREE_VIEW);
72
73#define PENDING_CONTACT_REQUESTS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PENDING_CONTACT_REQUESTS_VIEW_TYPE, PendingContactRequestsViewPrivate))
74
75/**
Nicolas Jager7b9f5922017-04-26 08:40:56 -040076 * callback function for rendering the contact photo
77 */
78static void
79render_contact_photo(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
80 GtkCellRenderer *cell,
81 GtkTreeModel *model,
82 GtkTreeIter *iter,
83 G_GNUC_UNUSED gpointer data)
84{
85 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
86
87 std::shared_ptr<GdkPixbuf> image;
88 QVariant object = idx.data(static_cast<int>(Ring::Role::Object));
89
90 if (idx.isValid() && object.isValid()) {
91 QVariant var_photo;
92 if (auto cr = object.value<ContactRequest *>()) {
93 if (cr->peer())
94 var_photo = GlobalInstances::pixmapManipulator().contactPhoto(cr->peer(), QSize(50, 50), false);
95 }
96 if (var_photo.isValid())
97 image = var_photo.value<std::shared_ptr<GdkPixbuf>>();
98
99 g_object_set(G_OBJECT(cell), "height", 50, NULL);
100 g_object_set(G_OBJECT(cell), "width", 50, NULL);
101 g_object_set(G_OBJECT(cell), "pixbuf", image.get(), NULL);
102 }
103}
104
105/**
106 * callback function for rendering the best id of the peer and the date.
107 */
108static void
109render_name_and_info(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
110 GtkCellRenderer *cell,
111 GtkTreeModel *model,
112 GtkTreeIter *iter,
113 GtkTreeView *treeview)
114{
115 auto idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
116
117 if (not idx.isValid()) {
118 g_warning("could not get index for contact request");
119 return;
120 }
121
122 // check if this iter is selected
123 gboolean is_selected = FALSE;
124 if (GTK_IS_TREE_VIEW(treeview)) {
125 auto selection = gtk_tree_view_get_selection(treeview);
126 is_selected = gtk_tree_selection_iter_is_selected(selection, iter);
127 }
128
129 /* best id */
130 auto uri_qstring = idx.data(static_cast<int>(Qt::DisplayRole)).value<QString>();
131 auto uri_std = uri_qstring.toStdString();
132
133 /* profile name */
134 auto qt_model = idx.model();
135 auto idx_formatted_name = qt_model->index(idx.row(), static_cast<int>(PendingContactRequestModel::Columns::FORMATTED_NAME));
136 auto formatted_name_qstring = idx_formatted_name.data(static_cast<int>(Qt::DisplayRole)).value<QString>();
137 auto formatted_name_std = formatted_name_qstring.toStdString();
138
139 /* date */
140 auto idx_date = qt_model->index(idx.row(), static_cast<int>(PendingContactRequestModel::Columns::TIME));
141 auto date_q_date_time = idx_date.data(static_cast<int>(Qt::DisplayRole)).value<QDateTime>();
142 auto date_q_string = QLocale::system().toString(date_q_date_time.time(), QLocale::ShortFormat);
143 auto date_std = date_q_string.toStdString();
144
145 gchar *text = nullptr;
146
147 if(is_selected) // print in default color
148 text = g_markup_printf_escaped("<span font_weight=\"bold\">%s</span>\n%s\n"
149 "<span size=\"smaller\">%s</span>", formatted_name_std.c_str(),
150 uri_std.c_str(),
151 date_std.c_str());
152 else // use our colors
153 text = g_markup_printf_escaped("<span font_weight=\"bold\">%s</span>\n<span color=\"gray\">%s\n"
154 "<span size=\"smaller\">%s</span></span>", formatted_name_std.c_str(),
155 uri_std.c_str(),
156 date_std.c_str());
157
158 g_object_set(G_OBJECT(cell), "markup", text, nullptr);
159 g_free(text);
160}
161
162/**
Nicolas Jager8c73cd12017-05-03 14:14:03 -0400163 * bind Account::pendingContactRequestModel() to pending_contact_requests_model
164 */
165static void
166bind_models(PendingContactRequestsView *self, Account* account)
167{
168 if (not account) {
169 g_warning("invalid account, cannot bind models.");
170 return;
171 }
172
173 auto pending_contact_requests_model = gtk_q_tree_model_new(account->pendingContactRequestModel(),
174 1/*nmbr. of cols.*/,
175 0,
176 Qt::DisplayRole,
177 G_TYPE_STRING);
178
179 gtk_tree_view_set_model(GTK_TREE_VIEW(self), GTK_TREE_MODEL(pending_contact_requests_model));
180}
181
182/**
Nicolas Jager0efc8432017-03-22 16:22:00 -0400183 * gtk init function
184 */
185static void
186pending_contact_requests_view_init(PendingContactRequestsView *self)
187{
188 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self), FALSE);
189 gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(self), FALSE);
190 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(self), FALSE);
191
192 // the next signal is used to set the model in function of the selection of the account
Nicolas Jager8c73cd12017-05-03 14:14:03 -0400193 QObject::connect(
194 AvailableAccountModel::instance().selectionModel(),
195 &QItemSelectionModel::currentChanged,
196 [self] (const QModelIndex& idx) {
197 bind_models(self, idx.data(static_cast<int>(Account::Role::Object)).value<Account*>());
Guillaume Roguez22667e42017-04-21 15:47:47 -0400198 }
Nicolas Jager8c73cd12017-05-03 14:14:03 -0400199 );
Nicolas Jager0efc8432017-03-22 16:22:00 -0400200
Nicolas Jager7b9f5922017-04-26 08:40:56 -0400201 /* photo and name/contact method column */
202 GtkCellArea *area = gtk_cell_area_box_new();
203 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_area(area);
Nicolas Jager0efc8432017-03-22 16:22:00 -0400204
Nicolas Jager7b9f5922017-04-26 08:40:56 -0400205 /* photo renderer */
206 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
207 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
208
209 /* get the photo */
210 gtk_tree_view_column_set_cell_data_func(
211 column,
212 renderer,
213 (GtkTreeCellDataFunc)render_contact_photo,
214 NULL,
215 NULL);
216
217 /* name and info renderer */
218 renderer = gtk_cell_renderer_text_new();
219 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
220 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
221
222 gtk_tree_view_column_set_cell_data_func(
223 column,
224 renderer,
225 (GtkTreeCellDataFunc)render_name_and_info,
226 self,
227 NULL);
228
Nicolas Jager0efc8432017-03-22 16:22:00 -0400229 gtk_tree_view_append_column(GTK_TREE_VIEW(self), column);
230 gtk_tree_view_column_set_resizable(column, TRUE);
231 gtk_tree_view_column_set_expand(column, TRUE);
232 gtk_tree_view_expand_all(GTK_TREE_VIEW(self));
233
Nicolas Jager8c73cd12017-05-03 14:14:03 -0400234 /* init the model */
235 bind_models(self, get_active_ring_account());
236
Nicolas Jager0efc8432017-03-22 16:22:00 -0400237 gtk_widget_show_all(GTK_WIDGET(self));
238}
239
240/**
241 * gtk dispose function
242 */
243static void
244pending_contact_requests_view_dispose(GObject *object)
245{
246 G_OBJECT_CLASS(pending_contact_requests_view_parent_class)->dispose(object);
247}
248
249/**
250 * gtk finalize function
251 */
252static void
253pending_contact_requests_view_finalize(GObject *object)
254{
255 G_OBJECT_CLASS(pending_contact_requests_view_parent_class)->finalize(object);
256}
257
258/**
259 * gtk class init function
260 */
261static void
262pending_contact_requests_view_class_init(PendingContactRequestsViewClass *klass)
263{
264 G_OBJECT_CLASS(klass)->finalize = pending_contact_requests_view_finalize;
265 G_OBJECT_CLASS(klass)->dispose = pending_contact_requests_view_dispose;
266}
267
268/**
269 * gtk new function
270 */
271GtkWidget *
272pending_contact_requests_view_new()
273{
274 gpointer self = g_object_new(PENDING_CONTACT_REQUESTS_VIEW_TYPE, NULL);
275
276 return (GtkWidget *)self;
277}