blob: 8e595fabb51fc411727ec1aa726b58993f8676d2 [file] [log] [blame]
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -04001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * Author: Stepan Salenikovich <stepan.salenikovich@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 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "frequentcontactsview.h"
32
33#include <gtk/gtk.h>
34#include "models/gtkqtreemodel.h"
35#include "utils/calling.h"
36#include <memory>
37#include "delegates/pixbufdelegate.h"
38#include <contactmethod.h>
39#include "defines.h"
40#include "utils/models.h"
Stepan Salenikovich774ab612015-06-04 17:31:37 -040041#include <phonedirectorymodel.h>
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -040042#include <call.h>
43
44#define COPY_DATA_KEY "copy_data"
45
46struct _FrequentContactsView
47{
48 GtkBox parent;
49};
50
51struct _FrequentContactsViewClass
52{
53 GtkBoxClass parent_class;
54};
55
56typedef struct _FrequentContactsViewPrivate FrequentContactsViewPrivate;
57
58struct _FrequentContactsViewPrivate
59{
60};
61
62G_DEFINE_TYPE_WITH_PRIVATE(FrequentContactsView, frequent_contacts_view, GTK_TYPE_BOX);
63
64#define FREQUENT_CONTACTS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), FREQUENT_CONTACTS_VIEW_TYPE, FrequentContactsViewPrivate))
65
66
67static void
68copy_contact_info(GtkWidget *item, G_GNUC_UNUSED gpointer user_data)
69{
70 gpointer data = g_object_get_data(G_OBJECT(item), COPY_DATA_KEY);
71 g_return_if_fail(data);
72 gchar* text = (gchar *)data;
73 GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
74 gtk_clipboard_set_text(clip, text, -1);
75}
76
77static void
78render_contact_photo(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
79 GtkCellRenderer *cell,
Stepan Salenikovich774ab612015-06-04 17:31:37 -040080 GtkTreeModel *model,
81 GtkTreeIter *iter,
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -040082 G_GNUC_UNUSED gpointer data)
83{
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -040084 /* get contact method */
Stepan Salenikovich774ab612015-06-04 17:31:37 -040085 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -040086 if (idx.isValid()) {
Stepan Salenikovich774ab612015-06-04 17:31:37 -040087 auto n = idx.data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod *>();
88 if (n) {
89 /* get photo */
90 QVariant var_p = PixbufDelegate::instance()->callPhoto(n, QSize(50, 50), false);
91 std::shared_ptr<GdkPixbuf> photo = var_p.value<std::shared_ptr<GdkPixbuf>>();
92 g_object_set(G_OBJECT(cell), "pixbuf", photo.get(), NULL);
93 return;
94 }
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -040095 }
96
97 /* otherwise, make sure its an empty pixbuf */
98 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
99}
100
101static void
102render_name_and_contact_method(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
103 GtkCellRenderer *cell,
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400104 GtkTreeModel *model,
105 GtkTreeIter *iter,
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400106 G_GNUC_UNUSED gpointer data)
107{
108 gchar *text = NULL;
109
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400110 /* get contact method */
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400111 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400112 if (idx.isValid()) {
113 /* get name and number */
114 QVariant c = idx.data(static_cast<int>(Call::Role::Name));
115 QVariant n = idx.data(static_cast<int>(Call::Role::Number));
116
117 text = g_strdup_printf("%s\n <span fgcolor=\"gray\">%s</span>",
118 c.value<QString>().toUtf8().constData(),
119 n.value<QString>().toUtf8().constData());
120 }
121
122
123 g_object_set(G_OBJECT(cell), "markup", text, NULL);
124 g_free(text);
125}
126
127static void
128activate_item(GtkTreeView *tree_view,
129 GtkTreePath *path,
130 G_GNUC_UNUSED GtkTreeViewColumn *column,
131 G_GNUC_UNUSED gpointer user_data)
132{
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400133 GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
134 GtkTreeIter iter;
135 if (gtk_tree_model_get_iter(model, &iter, path)) {
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400136 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
137 if (idx.isValid()) {
138 QVariant var_n = idx.data(static_cast<int>(Call::Role::ContactMethod));
139 if (var_n.isValid())
140 place_new_call(var_n.value<ContactMethod *>());
141 }
142 }
143}
144
145static gboolean
146popup_menu(GtkTreeView *treeview, GdkEventButton *event, G_GNUC_UNUSED gpointer user_data)
147{
148 /* build popup menu when right clicking on contact item
149 * user should be able to copy the contact's name or "number".
150 * other functionality may be added later.
151 */
152
153 /* check for right click */
154 if (event->button != BUTTON_RIGHT_CLICK || event->type != GDK_BUTTON_PRESS)
155 return FALSE;
156
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400157 GtkTreeIter iter;
158 GtkTreeModel *model;
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400159 GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400160 if (!gtk_tree_selection_get_selected(selection, &model, &iter))
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400161 return FALSE;
162
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400163 GtkWidget *menu = gtk_menu_new();
164 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
165
166 /* get name and number */
167 QVariant c = idx.data(static_cast<int>(Call::Role::Name));
168 QVariant n = idx.data(static_cast<int>(Call::Role::Number));
169
170 /* copy name */
171 gchar *name = g_strdup_printf("%s", c.value<QString>().toUtf8().constData());
172 GtkWidget *item = gtk_menu_item_new_with_mnemonic("_Copy name");
173 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
174 g_object_set_data_full(G_OBJECT(item), COPY_DATA_KEY, name, (GDestroyNotify)g_free);
175 g_signal_connect(item,
176 "activate",
177 G_CALLBACK(copy_contact_info),
178 NULL);
179
180 gchar *number = g_strdup_printf("%s", n.value<QString>().toUtf8().constData());
181 item = gtk_menu_item_new_with_mnemonic("_Copy number");
182 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
183 g_object_set_data_full(G_OBJECT(item), COPY_DATA_KEY, number, (GDestroyNotify)g_free);
184 g_signal_connect(item,
185 "activate",
186 G_CALLBACK(copy_contact_info),
187 NULL);
188
189 /* show menu */
190 gtk_widget_show_all(menu);
191 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
192
193 return TRUE; /* we handled the event */
194}
195
196static void
197frequent_contacts_view_init(FrequentContactsView *self)
198{
199 gtk_orientable_set_orientation(GTK_ORIENTABLE(self), GTK_ORIENTATION_VERTICAL);
200
201 /* frequent contacts/numbers */
202 GtkWidget *label_frequent = gtk_label_new("Frequent Contacts");
203 gtk_box_pack_start(GTK_BOX(self), label_frequent, FALSE, TRUE, 10);
204
205 GtkWidget *treeview_frequent = gtk_tree_view_new();
206 /* set can-focus to false so that the scrollwindow doesn't jump to try to
207 * contain the top of the treeview */
208 gtk_widget_set_can_focus(treeview_frequent, FALSE);
209 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_frequent), FALSE);
210 gtk_box_pack_start(GTK_BOX(self), treeview_frequent, TRUE, TRUE, 0);
211 /* no need to show the expander since it will always be expanded */
212 gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(treeview_frequent), FALSE);
213 /* disable default search, we will handle it ourselves via LRC;
214 * otherwise the search steals input focus on key presses */
215 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview_frequent), FALSE);
216
217 GtkQTreeModel *bookmark_model = gtk_q_tree_model_new(
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400218 PhoneDirectoryModel::instance()->mostPopularNumberModel(),
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400219 1,
220 Qt::DisplayRole, G_TYPE_STRING);
221
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400222 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview_frequent),
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400223 GTK_TREE_MODEL(bookmark_model));
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400224
225 /* photo and name/contact method column */
226 GtkCellArea *area = gtk_cell_area_box_new();
227 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_area(area);
228 gtk_tree_view_column_set_title(column, "Name");
229
230 /* photo renderer */
231 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
232 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
233
234 /* get the photo */
235 gtk_tree_view_column_set_cell_data_func(
236 column,
237 renderer,
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400238 (GtkTreeCellDataFunc)render_contact_photo,
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400239 NULL,
240 NULL);
241
242 /* name and contact method renderer */
243 renderer = gtk_cell_renderer_text_new();
244 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
245 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
246
247 gtk_tree_view_column_set_cell_data_func(
248 column,
249 renderer,
250 (GtkTreeCellDataFunc)render_name_and_contact_method,
251 NULL,
252 NULL);
253
254 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_frequent), column);
255 gtk_tree_view_column_set_resizable(column, TRUE);
256
257 gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview_frequent));
258
259 g_signal_connect(treeview_frequent, "button-press-event", G_CALLBACK(popup_menu), NULL);
260 g_signal_connect(treeview_frequent, "row-activated", G_CALLBACK(activate_item), NULL);
261
262 gtk_widget_show_all(GTK_WIDGET(self));
263}
264
265static void
266frequent_contacts_view_dispose(GObject *object)
267{
268 G_OBJECT_CLASS(frequent_contacts_view_parent_class)->dispose(object);
269}
270
271static void
272frequent_contacts_view_finalize(GObject *object)
273{
274 G_OBJECT_CLASS(frequent_contacts_view_parent_class)->finalize(object);
275}
276
277static void
278frequent_contacts_view_class_init(FrequentContactsViewClass *klass)
279{
280 G_OBJECT_CLASS(klass)->finalize = frequent_contacts_view_finalize;
281 G_OBJECT_CLASS(klass)->dispose = frequent_contacts_view_dispose;
282}
283
284GtkWidget *
285frequent_contacts_view_new()
286{
287 gpointer self = g_object_new(FREQUENT_CONTACTS_VIEW_TYPE, NULL);
288
289 return (GtkWidget *)self;
Stepan Salenikovich774ab612015-06-04 17:31:37 -0400290}