blob: fa6a7cc003ac2b3fe94a86899d1b73477def7c8d [file] [log] [blame]
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -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 "editcontactview.h"
32
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040033#include <glib/gi18n.h>
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -040034#include <contactmethod.h>
35#include <personmodel.h>
36#include <numbercategorymodel.h>
37#include "utils/models.h"
38
39enum
40{
41 PERSON_SAVED,
42
43 LAST_SIGNAL
44};
45
46struct _EditContactView
47{
48 GtkGrid parent;
49};
50
51struct _EditContactViewClass
52{
53 GtkGridClass parent_class;
54};
55
56typedef struct _EditContactViewPrivate EditContactViewPrivate;
57
58struct _EditContactViewPrivate
59{
60 GtkWidget *button_save;
61 GtkWidget *combobox_addressbook;
62 GtkWidget *entry_name;
63 GtkWidget *combobox_detail;
64 GtkWidget *label_uri;
65
66 ContactMethod *cm;
67 Person *person;
68};
69
70static guint edit_contact_signals[LAST_SIGNAL] = { 0 };
71
72G_DEFINE_TYPE_WITH_PRIVATE(EditContactView, edit_contact_view, GTK_TYPE_GRID);
73
74#define EDIT_CONTACT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EDIT_CONTACT_VIEW_TYPE, EditContactViewPrivate))
75
76static void
77save_cb(EditContactView *self)
78{
79 g_return_if_fail(IS_EDIT_CONTACT_VIEW(self));
80 EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
81
82 auto name = gtk_entry_get_text(GTK_ENTRY(priv->entry_name));
83 if (!priv->person) {
84 /* make sure that the entry is not empty */
85 if (!name or strlen(name) == 0) {
86 g_warning("new contact must have a name");
87 gtk_widget_grab_focus(priv->entry_name);
88 return;
89 }
90 }
91
92 /* get the selected number category */
93 const auto& idx = gtk_combo_box_get_index(GTK_COMBO_BOX(priv->combobox_detail));
94 if (idx.isValid()) {
Guillaume Roguez5d1514b2015-10-22 15:55:31 -040095 auto category = NumberCategoryModel::instance().getCategory(idx.data().toString());
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -040096 priv->cm->setCategory(category);
97 }
98
99 if (!priv->person) {
100 /* get the selected collection */
101 GtkTreeIter iter;
102 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter);
103 auto addressbook_model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combobox_addressbook));
104 GValue value = G_VALUE_INIT;
105 gtk_tree_model_get_value(addressbook_model, &iter, 1, &value);
106 auto collection = (CollectionInterface *)g_value_get_pointer(&value);
107
108 /* create a new person */
109 priv->person = new Person(collection);
110 priv->person->setFormattedName(name);
111
112 /* associate the new person with the contact method */
113 Person::ContactMethods numbers;
114 numbers << priv->cm;
115 priv->person->setContactMethods(numbers);
116
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400117 PersonModel::instance().addNewPerson(priv->person, collection);
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400118 } else {
119 auto numbers = priv->person->phoneNumbers();
120 numbers << priv->cm;
121 priv->person->setContactMethods(numbers);
122 priv->person->save();
123 }
124
125 g_signal_emit(self, edit_contact_signals[PERSON_SAVED], 0);
126}
127
128static void
129name_changed(GtkEntry *entry_name, EditContactView *self)
130{
131 g_return_if_fail(IS_EDIT_CONTACT_VIEW(self));
132 EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
133
134 auto name = gtk_entry_get_text(entry_name);
135 /* make sure that the entry is not empty */
136 if (!name or strlen(name) == 0) {
137 gtk_widget_set_sensitive(priv->button_save, FALSE);
138 } else {
139 gtk_widget_set_sensitive(priv->button_save, TRUE);
140 }
141}
142
143static void
144edit_contact_view_init(EditContactView *self)
145{
146 gtk_widget_init_template(GTK_WIDGET(self));
147
148 EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
149
150 /* model for the combobox for the choice of addressbooks */
151 auto addressbook_model = gtk_list_store_new(
152 2, G_TYPE_STRING, G_TYPE_POINTER
153 );
154
155 gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_addressbook),
156 GTK_TREE_MODEL(addressbook_model));
157 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
158 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_addressbook),
159 renderer, FALSE);
160 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_addressbook),
161 renderer, "text", 0, NULL);
162
163 /* get all the available contact backends (addressbooks) */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400164 const auto& collections = PersonModel::instance().enabledCollections();
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400165 for (int i = 0; i < collections.size(); ++i) {
166 GtkTreeIter iter;
167 gtk_list_store_append(addressbook_model, &iter);
168 gtk_list_store_set(addressbook_model, &iter,
169 0, collections.at(i)->name().toUtf8().constData(),
170 1, collections.at(i),
171 -1);
172 }
173
174 /* set the first addressbook which is not the "vCard" to be used, unless its
175 * the only one */
176 GtkTreeIter iter_to_select;
177 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(addressbook_model), &iter_to_select)) {
178 GValue value = G_VALUE_INIT;
179 gtk_tree_model_get_value(GTK_TREE_MODEL(addressbook_model), &iter_to_select, 1, &value);
180 auto collection = (CollectionInterface *)g_value_get_pointer(&value);
181 GtkTreeIter iter = iter_to_select;
182 while ( (collection->name() == "vCard") && gtk_tree_model_iter_next(GTK_TREE_MODEL(addressbook_model), &iter)) {
183 iter_to_select = iter;
184 value = G_VALUE_INIT;
185 gtk_tree_model_get_value(GTK_TREE_MODEL(addressbook_model), &iter, 1, &value);
186 collection = (CollectionInterface *)g_value_get_pointer(&value);
187 }
188 }
189 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter_to_select);
190
191 /* model for the available details to choose from */
192 gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_detail),
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400193 (QAbstractItemModel *)&NumberCategoryModel::instance(), NULL);
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400194
195 /* set "home" as the default number category */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400196 const auto& idx = NumberCategoryModel::instance().nameToIndex(C_("Phone number category", "home"));
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400197 if (idx.isValid())
198 gtk_combo_box_set_active_index(GTK_COMBO_BOX(priv->combobox_detail), idx);
199
200 /* disable save button until name is entered */
201 gtk_widget_set_sensitive(priv->button_save, FALSE);
202
203 /* connect to the button signals */
204 g_signal_connect_swapped(priv->button_save, "clicked", G_CALLBACK(save_cb), self);
205 g_signal_connect(priv->entry_name, "changed", G_CALLBACK(name_changed), self);
206}
207
208static void
209edit_contact_view_dispose(GObject *object)
210{
211 G_OBJECT_CLASS(edit_contact_view_parent_class)->dispose(object);
212}
213
214static void
215edit_contact_view_finalize(GObject *object)
216{
217 G_OBJECT_CLASS(edit_contact_view_parent_class)->finalize(object);
218}
219
220static void
221edit_contact_view_class_init(EditContactViewClass *klass)
222{
223 G_OBJECT_CLASS(klass)->finalize = edit_contact_view_finalize;
224 G_OBJECT_CLASS(klass)->dispose = edit_contact_view_dispose;
225
226 edit_contact_signals[PERSON_SAVED] =
227 g_signal_new("person-saved",
228 G_OBJECT_CLASS_TYPE(G_OBJECT_CLASS(klass)),
229 (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
230 0, /* class offset */
231 NULL, /* accumulater */
232 NULL, /* accu data */
233 g_cclosure_marshal_VOID__VOID,
234 G_TYPE_NONE, 0);
235
236 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(klass),
237 "/cx/ring/RingGnome/editcontactview.ui");
238
239 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, button_save);
240 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, combobox_addressbook);
241 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, entry_name);
242 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, combobox_detail);
243 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, label_uri);
244}
245
246/**
247 * If a Person is specified, a view to edit the given Person will be created;
248 * if no Person is given (NULL), then a new Person object will be created when
249 * 'save' is clicked
250 */
251GtkWidget *
252edit_contact_view_new(ContactMethod *cm, Person *p)
253{
254 g_return_val_if_fail(cm, NULL);
255
256 gpointer self = g_object_new(EDIT_CONTACT_VIEW_TYPE, NULL);
257
258 EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
259
260 priv->cm = cm;
261 gtk_label_set_markup(
262 GTK_LABEL(priv->label_uri),
263 (QString("<b>") + cm->uri() + QString("</b>")).toUtf8().constData()
264 );
265
Stepan Salenikoviche371bd12015-09-09 15:45:11 -0400266 /* use the primaryName as the suggested name (usually the display name), unless it is the same
267 * as the uri */
268 if (cm->primaryName() != cm->uri())
269 gtk_entry_set_text(GTK_ENTRY(priv->entry_name), cm->primaryName().toUtf8().constData());
270
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400271 if (p) {
272 priv->person = p;
273
274 /* select the proper addressbook and prevent it from being modified */
275 if (p->collection() != NULL) {
276 auto model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combobox_addressbook));
277 GtkTreeIter iter;
278 if (gtk_tree_model_get_iter_first(model, &iter)) {
279 GValue value = G_VALUE_INIT;
280 gtk_tree_model_get_value(model, &iter, 1, &value);
281 auto collection = (CollectionInterface *)g_value_get_pointer(&value);
282 while (collection != p->collection() && gtk_tree_model_iter_next(model, &iter)) {
283 value = G_VALUE_INIT;
284 gtk_tree_model_get_value(model, &iter, 1, &value);
285 collection = (CollectionInterface *)g_value_get_pointer(&value);
286 }
287 }
288
289 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter);
290 }
291 gtk_widget_set_sensitive(priv->combobox_addressbook, FALSE);
292
293 /* set the name of the person, and prevent it from being edited */
294 gtk_entry_set_text(GTK_ENTRY(priv->entry_name), p->formattedName().toUtf8().constData());
295 g_object_set(G_OBJECT(priv->entry_name), "editable", FALSE, NULL);
296 }
297
298 return (GtkWidget *)self;
299}