blob: 6f8ef87c13f1a75925794fa6a494b0211b06f844 [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05003 * 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.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050018 */
19
20#include "ringmainwindow.h"
21
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040022// system
23#include <string.h>
24#include <memory>
25
26// GTK+ related
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040027#include <glib/gi18n.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040028
29// Qt
30#include <QtCore/QItemSelectionModel>
31#include <QtCore/QSortFilterProxyModel>
Stepan Salenikoviche9933242016-06-21 18:08:48 -040032#include <QtCore/QDateTime>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040033
34// LRC
Stepan Salenikovicha3557452015-02-20 14:14:12 -050035#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050036#include <call.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040037#include <contactmethod.h>
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040038#include <accountmodel.h>
Edric Milaret58e71072016-03-21 12:16:37 -040039#include <codecmodel.h>
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040040#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040041#include <personmodel.h>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040042#include <globalinstances.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040043#include <categorizedcontactmodel.h>
44#include <recentmodel.h>
45#include <profilemodel.h>
46#include <profile.h>
Stepan Salenikovichf53128f2016-10-07 10:32:16 -040047#include <phonedirectorymodel.h>
Stepan Salenikovich7b7be482016-11-03 16:57:48 -040048#include <availableaccountmodel.h>
Nicolas Jager0efc8432017-03-22 16:22:00 -040049#include <contactrequest.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040050
51// Ring client
52#include "models/gtkqtreemodel.h"
53#include "incomingcallview.h"
54#include "currentcallview.h"
Stepan Salenikovichf6078222016-10-03 17:31:16 -040055#include "models/gtkqtreemodel.h"
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040056#include "accountview.h"
57#include "dialogs.h"
58#include "mediasettingsview.h"
59#include "utils/drawing.h"
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040060#include "native/pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040061#include "models/activeitemproxymodel.h"
Stepan Salenikovich9816a942015-04-22 17:49:16 -040062#include "utils/calling.h"
63#include "contactsview.h"
64#include "historyview.h"
65#include "utils/models.h"
Stepan Salenikovichde896112015-05-11 16:46:33 -040066#include "generalsettingsview.h"
Stepan Salenikovich75a39172015-07-10 13:21:08 -040067#include "utils/accounts.h"
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040068#include "ringwelcomeview.h"
aviau6aeb4852016-08-18 16:01:09 -040069#include "accountmigrationview.h"
70#include "accountcreationwizard.h"
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040071#include "recentcontactsview.h"
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050072#include "chatview.h"
Nicolas Jagerb413b302016-05-06 11:41:32 -040073#include "avatarmanipulation.h"
Stepan Salenikovichba87ae22016-09-13 14:27:20 -040074#include "utils/files.h"
Nicolas Jager0efc8432017-03-22 16:22:00 -040075#include "pendingcontactrequests.h"
Nicolas Jagerd00761d2017-03-22 16:22:00 -040076#include "contactrequestcontentview.h"
Nicolas Jagerb413b302016-05-06 11:41:32 -040077
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040078static constexpr const char* CALL_VIEW_NAME = "calls";
aviau6aeb4852016-08-18 16:01:09 -040079static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
80static constexpr const char* ACCOUNT_MIGRATION_VIEW_NAME = "account-migration-view";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040081static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
82static constexpr const char* AUDIO_SETTINGS_VIEW_NAME = "audio";
83static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
84static constexpr const char* ACCOUNT_SETTINGS_VIEW_NAME = "accounts";
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040085static constexpr const char* DEFAULT_VIEW_NAME = "welcome";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040086static constexpr const char* VIEW_CONTACTS = "contacts";
87static constexpr const char* VIEW_HISTORY = "history";
88static constexpr const char* VIEW_PRESENCE = "presence";
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050089
90struct _RingMainWindow
91{
92 GtkApplicationWindow parent;
93};
94
95struct _RingMainWindowClass
96{
97 GtkApplicationWindowClass parent_class;
98};
99
100typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
101
102struct _RingMainWindowPrivate
103{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400104 GtkWidget *ring_menu;
105 GtkWidget *image_ring;
106 GtkWidget *ring_settings;
107 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400108 GtkWidget *hbox_settings;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400109 GtkWidget *scrolled_window_smartview;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400110 GtkWidget *treeview_conversations;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400111 GtkWidget *scrolled_window_contacts;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400112 GtkWidget *treeview_contacts;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400113 GtkWidget *scrolled_window_history;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400114 GtkWidget *treeview_history;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400115 GtkWidget *vbox_left_pane;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500116 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500117 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400118 GtkWidget *vbox_call_view;
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400119 GtkWidget *frame_call;
120 GtkWidget *welcome_view;
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400121 GtkWidget *button_new_conversation ;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400122 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400123 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400124 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400125 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400126 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400127 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400128 GtkWidget *radiobutton_account_settings;
aviau69081842016-10-14 14:51:54 -0400129 GtkWidget *account_creation_wizard;
130 GtkWidget *account_migration_view;
Nicolas Jager55903142017-02-01 14:43:45 -0500131 GtkWidget *spinner_lookup;
Nicolas Jager15a8b902017-03-21 07:53:06 -0400132 GtkWidget *combobox_account_selector;
Nicolas Jager0efc8432017-03-22 16:22:00 -0400133 GtkWidget *treeview_contact_requests;
134 GtkWidget *scrolled_window_contact_requests;
135 GtkWidget *contact_request_view;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400136
aviau8c0b2422016-10-31 16:06:44 -0400137 /* Pending ring usernames lookup for the search entry */
138 QMetaObject::Connection username_lookup;
139
aviau039001d2016-09-29 16:39:05 -0400140 /* The webkit_chat_container is created once, then reused for all chat views */
141 GtkWidget *webkit_chat_container;
142
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400143 QMetaObject::Connection selected_item_changed;
144 QMetaObject::Connection selected_call_over;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400145
146 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400147
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500148 /* fullscreen */
149 gboolean is_fullscreen;
Stepan Salenikovichba87ae22016-09-13 14:27:20 -0400150
151 GSettings *settings;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500152};
153
154G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
155
156#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
157
aviau039001d2016-09-29 16:39:05 -0400158static gboolean selection_changed(RingMainWindow *win);
159
160static WebKitChatContainer*
161get_webkit_chat_container(RingMainWindow *win)
162{
163 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
164 if (!priv->webkit_chat_container)
165 {
166 priv->webkit_chat_container = webkit_chat_container_new();
167
168 //We don't want it to be deleted, ever.
169 g_object_ref(priv->webkit_chat_container);
170 }
171 return WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container);
172}
173
Stepan Salenikovich69771842015-02-24 18:11:45 -0500174static void
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500175enter_full_screen(RingMainWindow *self)
176{
177 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
178 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
179
180 if (!priv->is_fullscreen) {
181 gtk_widget_hide(priv->vbox_left_pane);
182 gtk_window_fullscreen(GTK_WINDOW(self));
183 priv->is_fullscreen = TRUE;
184 }
185}
186
187static void
188leave_full_screen(RingMainWindow *self)
189{
190 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
191 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
192
193 if (priv->is_fullscreen) {
194 gtk_widget_show(priv->vbox_left_pane);
195 gtk_window_unfullscreen(GTK_WINDOW(self));
196 priv->is_fullscreen = FALSE;
197 }
198}
199
200static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400201video_double_clicked(RingMainWindow *self)
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500202{
203 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
204 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
205
206 if (priv->is_fullscreen) {
207 leave_full_screen(self);
208 } else {
209 enter_full_screen(self);
210 }
211}
212
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400213static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400214hide_view_clicked(RingMainWindow *self)
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400215{
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400216 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400217
218 /* clear selection */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400219 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
220 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
221 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
222 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contacts));
223 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
224 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_history));
Nicolas Jager0efc8432017-03-22 16:22:00 -0400225 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
226 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contact_request));
227
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400228}
229
aviau039001d2016-09-29 16:39:05 -0400230static void
231change_view(RingMainWindow *self, GtkWidget* old, QObject *object, GType type)
232{
233 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
234 leave_full_screen(self);
235 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old);
236
237 GtkWidget *new_view = nullptr;
238
239 QObject::disconnect(priv->selected_item_changed);
240 QObject::disconnect(priv->selected_call_over);
241
242 if (g_type_is_a(INCOMING_CALL_VIEW_TYPE, type)) {
243 if (auto call = qobject_cast<Call *>(object)) {
244 new_view = incoming_call_view_new(call, get_webkit_chat_container(self));
245 priv->selected_item_changed = QObject::connect(
246 call,
247 &Call::lifeCycleStateChanged,
248 [self] (Call::LifeCycleState, Call::LifeCycleState)
249 {g_idle_add((GSourceFunc)selection_changed, self);}
250 );
251 priv->selected_call_over = QObject::connect(
252 call,
253 &Call::isOver,
254 [self] ()
255 {g_idle_add((GSourceFunc)selection_changed, self);}
256 );
257 } else
258 g_warning("Trying to display a view of type IncomingCallView, but the object is not of type Call");
259 } else if (g_type_is_a(CURRENT_CALL_VIEW_TYPE, type)) {
260 if (auto call = qobject_cast<Call *>(object)) {
261 new_view = current_call_view_new(call, get_webkit_chat_container(self));
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400262 g_signal_connect_swapped(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400263 priv->selected_item_changed = QObject::connect(
264 call,
265 &Call::lifeCycleStateChanged,
266 [self] (Call::LifeCycleState, Call::LifeCycleState)
267 {g_idle_add((GSourceFunc)selection_changed, self);}
268 );
269 priv->selected_call_over = QObject::connect(
270 call,
271 &Call::isOver,
272 [self] ()
273 {g_idle_add((GSourceFunc)selection_changed, self);}
274 );
275 } else
276 g_warning("Trying to display a view of type CurrentCallView, but the object is not of type Call");
277 } else if (g_type_is_a(CHAT_VIEW_TYPE, type)) {
278 if (auto person = qobject_cast<Person *>(object)) {
279 new_view = chat_view_new_person(get_webkit_chat_container(self), person);
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400280 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400281
282 /* connect to the Person's callAdded signal, because we want to switch to the call view
283 * in this case */
284 priv->selected_item_changed = QObject::connect(
285 person,
286 &Person::callAdded,
287 [self] (Call*)
288 {g_idle_add((GSourceFunc)selection_changed, self);}
289 );
290 } else if (auto cm = qobject_cast<ContactMethod *>(object)) {
291 new_view = chat_view_new_cm(get_webkit_chat_container(self), cm);
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400292 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400293
294 /* connect to the ContactMethod's callAdded signal, because we want to switch to the
295 * call view in this case */
296 priv->selected_item_changed = QObject::connect(
297 cm,
298 &ContactMethod::callAdded,
299 [self] (Call*)
300 {g_idle_add((GSourceFunc)selection_changed, self);}
301 );
302 } else {
303 g_warning("Trying to display a veiw of type ChatView, but the object is neither a Person nor a ContactMethod");
304 }
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400305 } else if (g_type_is_a(CONTACT_REQUEST_CONTENT_VIEW_TYPE, type)) {
306 if (auto contact_request = qobject_cast<ContactRequest *>(object)) {
307 new_view = contact_request_content_view_new(contact_request);
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400308 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400309 }
aviau039001d2016-09-29 16:39:05 -0400310 } else {
311 // display the welcome view
312 new_view = priv->welcome_view;
313 }
314
315 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
316 gtk_widget_show(new_view);
317}
318
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500319/**
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400320 * This function determines which view to display in the right panel of the main window based on
321 * which item is selected in one of the 3 contact list views (Conversations, Contacts, History). The
322 * possible views ares:
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500323 * - incoming call view
324 * - current call view
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500325 * - chat view
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400326 * - contact request content view
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400327 * - welcome view (if no valid item is selected)
328 *
329 * There should never be a conflict of which item should be displayed (ie: which item is selected),
330 * as there should only ever be one selection at a time in the 3 views except in the case that the
331 * same item is selected in more than one view (see the compare_treeview_selection() function).
332 *
333 * This function could be called from a g_idle source, so it returns a boolean to remove itself from
334 * being called again. The boolean doesn't reflect success nor failure.
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500335 */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400336static gboolean
337selection_changed(RingMainWindow *win)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500338{
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400339
340 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500341 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
342
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400343 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500344
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400345 /* if we're showing the settings, then we need to make sure to remove any possible ongoing call
346 * view so that we don't have more than one VideoWidget at a time */
347 if (priv->show_settings) {
348 if (old_view != priv->welcome_view) {
349 leave_full_screen(win);
350 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_view);
351 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
352 gtk_widget_show(priv->welcome_view);
353 }
354 return G_SOURCE_REMOVE;
355 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500356
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400357 /* there should only be one item selected at a time, get which one is selected */
358 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
359 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
360 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
Nicolas Jager0efc8432017-03-22 16:22:00 -0400361 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400362
363 GtkTreeModel *model = nullptr;
364 GtkTreeIter iter;
365 QModelIndex idx;
366
367 if (gtk_tree_selection_get_selected(selection_conversations, &model, &iter)) {
Stepan Salenikovichf6078222016-10-03 17:31:16 -0400368 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400369 } else if (gtk_tree_selection_get_selected(selection_contacts, &model, &iter)) {
Stepan Salenikovichf6078222016-10-03 17:31:16 -0400370 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400371 } else if (gtk_tree_selection_get_selected(selection_history, &model, &iter)) {
Stepan Salenikovichf6078222016-10-03 17:31:16 -0400372 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Nicolas Jager0efc8432017-03-22 16:22:00 -0400373 } else if (gtk_tree_selection_get_selected(selection_contact_request, &model, &iter)) {
374 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400375 }
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500376
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500377 /* check which object type is selected */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400378 auto type = idx.data(static_cast<int>(Ring::Role::ObjectType));
379 auto object = idx.data(static_cast<int>(Ring::Role::Object));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500380
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400381 if (idx.isValid() && type.isValid() && object.isValid()) {
Stepan Salenikovich69771842015-02-24 18:11:45 -0500382
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400383 /* we prioritize the views in the following order:
384 * - call view (if there is an ongoing call associated with the item)
385 * - chat view built from Person
386 * - chat view built from ContactMethod
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400387 * - contact request content view built from ContactRequest
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400388 */
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500389
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400390 Person *person = nullptr;
391 ContactMethod *cm = nullptr;
392 Call *call = nullptr;
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400393 ContactRequest *contact_request = nullptr;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500394
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400395 /* use the RecentModel to see if there are any ongoing calls associated with the selected item */
396 switch(type.value<Ring::ObjectType>()) {
397 case Ring::ObjectType::Person:
398 {
399 person = object.value<Person *>();
400 auto recent_idx = RecentModel::instance().getIndex(person);
401 if (recent_idx.isValid()) {
402 call = RecentModel::instance().getActiveCall(recent_idx);
403 }
404 }
405 break;
406 case Ring::ObjectType::ContactMethod:
407 {
408 cm = object.value<ContactMethod *>();
409 auto recent_idx = RecentModel::instance().getIndex(cm);
410 if (recent_idx.isValid()) {
411 call = RecentModel::instance().getActiveCall(recent_idx);
412 }
413 }
414 break;
415 case Ring::ObjectType::Call:
416 {
417 /* if the call is over (eg: if it comes from the history model) then we first check
418 * if there is an ongoing call with the same ContactMethod, otherwise we need to get
419 * the associated Person or ContactMethod */
420 call = object.value<Call *>();
421 if (call->isHistory()) {
422 cm = call->peerContactMethod();
423 call = nullptr;
424 if (cm) {
425 auto recent_idx = RecentModel::instance().getIndex(cm);
426 if (recent_idx.isValid()) {
427 call = RecentModel::instance().getActiveCall(recent_idx);
428 }
429 person = cm->contact();
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500430 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500431 }
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400432 }
433 break;
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400434 case Ring::ObjectType::ContactRequest:
435 {
436 contact_request = object.value<ContactRequest *>();
437 }
438 break;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400439 case Ring::ObjectType::Media:
440 case Ring::ObjectType::Certificate:
441 case Ring::ObjectType::COUNT__:
442 // nothing to do
443 break;
444 }
445
446 /* we prioritize showing the call view */
447 if (call) {
448 auto state = call->lifeCycleState();
449
450 Call *current_call = nullptr;
451
452 switch(state) {
453 case Call::LifeCycleState::CREATION:
454 case Call::LifeCycleState::INITIALIZATION:
455 case Call::LifeCycleState::FINISHED:
456 // check if we're already displaying this call
457 if (IS_INCOMING_CALL_VIEW(old_view))
458 current_call = incoming_call_view_get_call(INCOMING_CALL_VIEW(old_view));
459 if (current_call != call)
aviau039001d2016-09-29 16:39:05 -0400460 change_view(win, old_view, call, INCOMING_CALL_VIEW_TYPE);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400461 break;
462 case Call::LifeCycleState::PROGRESS:
463 // check if we're already displaying this call
464 if (IS_CURRENT_CALL_VIEW(old_view))
465 current_call = current_call_view_get_call(CURRENT_CALL_VIEW(old_view));
aviau039001d2016-09-29 16:39:05 -0400466 if (current_call != call)
467 change_view(win, old_view, call, CURRENT_CALL_VIEW_TYPE);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400468 break;
469 case Call::LifeCycleState::COUNT__:
470 g_warning("LifeCycleState should never be COUNT");
471 break;
472 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500473
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400474 } else if (person) {
475 /* show chat view constructed from Person object */
476
477 // check if we're already displaying the chat view for this person
478 Person *current_person = nullptr;
479 if (IS_CHAT_VIEW(old_view))
480 current_person = chat_view_get_person(CHAT_VIEW(old_view));
481
aviau039001d2016-09-29 16:39:05 -0400482 if (current_person != person)
483 change_view(win, old_view, person, CHAT_VIEW_TYPE);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400484 } else if (cm) {
485 /* show chat view constructed from CM */
486
487 // check if we're already displaying the chat view for this cm
488 ContactMethod *current_cm = nullptr;
489 if (IS_CHAT_VIEW(old_view))
490 current_cm = chat_view_get_cm(CHAT_VIEW(old_view));
491
aviau039001d2016-09-29 16:39:05 -0400492 if (current_cm != cm)
493 change_view(win, old_view, cm, CHAT_VIEW_TYPE);
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400494
495 } else if (contact_request) {
496 /* show the contact request page */
497 change_view(win, old_view, contact_request, CONTACT_REQUEST_CONTENT_VIEW_TYPE);
498
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400499 } else {
500 /* not a supported object type, display the welcome view */
aviau039001d2016-09-29 16:39:05 -0400501 if (old_view != priv->welcome_view)
502 change_view(win, old_view, nullptr, RING_WELCOME_VIEW_TYPE);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500503 }
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400504 } else {
505 /* selection isn't valid, display the welcome view */
aviau039001d2016-09-29 16:39:05 -0400506 if (old_view != priv->welcome_view)
507 change_view(win, old_view, nullptr, RING_WELCOME_VIEW_TYPE);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500508 }
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500509
510 return G_SOURCE_REMOVE;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500511}
512
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500513static void
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400514activate_contact_method(RingMainWindow *self, URI uri, Account *account)
aviau8c0b2422016-10-31 16:06:44 -0400515{
516 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
517
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400518 auto cm = PhoneDirectoryModel::instance().getNumber(uri, account);
aviau8c0b2422016-10-31 16:06:44 -0400519
520 if (g_settings_get_boolean(priv->settings, "search-entry-places-call")) {
521 place_new_call(cm);
522
523 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
524 gtk_widget_child_focus(GTK_WIDGET(self), GTK_DIR_TAB_FORWARD);
525 } else {
526 // if its a new CM, bring it to the top
527 if (cm->lastUsed() == 0)
528 cm->setLastUsed(QDateTime::currentDateTime().toTime_t());
529
530 // select cm
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400531 RecentModel::instance().selectionModel()->setCurrentIndex(
532 RecentModel::instance().getIndex(cm),
533 QItemSelectionModel::ClearAndSelect
534 );
aviau8c0b2422016-10-31 16:06:44 -0400535 }
536 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
537}
538
539static void
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400540lookup_username(RingMainWindow *self, URI uri, Account *account)
541{
542 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
543
544 gtk_spinner_start(GTK_SPINNER(priv->spinner_lookup));
545 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
546
547 // we need to strip the Scheme section, or else some will contain "sip:"
548 QString querry = uri.format(URI::Section::USER_INFO|URI::Section::HOSTNAME|URI::Section::PORT);
549
550 /* disconect previous lookup */
551 QObject::disconnect(priv->username_lookup);
552
553 /* connect new lookup */
554 priv->username_lookup = QObject::connect(
555 &NameDirectory::instance(),
556 &NameDirectory::registeredNameFound,
557 [self, priv, querry] (Account* account, NameDirectory::LookupStatus status, const QString& address, const QString& name) {
558
559 g_debug("lookup results for %s is %s",
560 querry.toUtf8().constData(),
561 name.toUtf8().constData()
562 );
563
564 if ( name.compare(querry, Qt::CaseInsensitive) != 0 ) {
565 g_debug("not from the same lookup match");
566 return; // doesn't match
567 }
568
569 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
570
571 switch(status)
572 {
573 case NameDirectory::LookupStatus::SUCCESS:
574 {
575 URI result = URI("ring:" + address);
576 activate_contact_method(self, result, account);
577 break;
578 }
579 case NameDirectory::LookupStatus::INVALID_NAME:
580 {
581 //Can't be a ring username, could be something else.
582 auto dialog = gtk_message_dialog_new(
583 GTK_WINDOW(self),
584 GTK_DIALOG_DESTROY_WITH_PARENT,
585 GTK_MESSAGE_ERROR,
586 GTK_BUTTONS_CLOSE,
587 _("Invalid Ring username: %s"),
588 name.toUtf8().constData()
589 );
590
591 gtk_dialog_run (GTK_DIALOG (dialog));
592 gtk_widget_destroy (dialog);
593 break;
594 }
595 case NameDirectory::LookupStatus::ERROR:
596 {
597 auto dialog = gtk_message_dialog_new(
598 GTK_WINDOW(self),
599 GTK_DIALOG_DESTROY_WITH_PARENT,
600 GTK_MESSAGE_ERROR,
601 GTK_BUTTONS_CLOSE,
602 _("Error resolving username, nameserver is possibly unreachable")
603 );
604
605 gtk_dialog_run(GTK_DIALOG (dialog));
606 gtk_widget_destroy(dialog);
607 break;
608 }
609 case NameDirectory::LookupStatus::NOT_FOUND:
610 {
611 auto dialog = gtk_message_dialog_new(
612 GTK_WINDOW(self),
613 GTK_DIALOG_DESTROY_WITH_PARENT,
614 GTK_MESSAGE_ERROR,
615 GTK_BUTTONS_CLOSE,
616 _("Could not resolve Ring username: %s"),
617 name.toUtf8().constData()
618 );
619
620 gtk_dialog_run(GTK_DIALOG (dialog));
621 gtk_widget_destroy(dialog);
622 break;
623 }
624 }
625 gtk_spinner_stop(GTK_SPINNER(priv->spinner_lookup));
626 }
627 );
628
629 /* lookup */
630 NameDirectory::instance().lookupName(account, "", querry);
631}
632
633/**
634 * This is called when someone activates the search entry (clicks enter or on the button).
635 * At this point the list is already filtered but on clicking enter we want to either contact a new
636 * number or perform a search on the name service. Here we must decide which one the user wants.
637 * We'll use the following rules to decide:
638 * * if a user explicitly specifies the scheme ("sip:" or "ring:") then we always use a
639 * corresponding account type (use currentDefaultAccount() with the scheme type)
640 * * if a user doesn't specify the scheme, then we decide based on the user selected account; if
641 * SIP account then we assume its a SIP URI; if RING account then we check if its a ringID,
642 * otherwise we perform a name lookup.
643 *
644 * Additionally, we need to make sure that the CM found/created either has the same account set as
645 * selected by the user, no account set, or else we need to switch the user selected account to the
646 * account of the CM (eg: in the case we're switching protocols), or else the item will not be
647 * visible in the Converstations list to the user. This is accomplished via the use of
648 * currentDefaultAccount().
649 */
650static void
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -0400651search_entry_activated(RingMainWindow *self)
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500652{
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -0400653 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500654
Nicolas Jager55903142017-02-01 14:43:45 -0500655 const auto *querry = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500656
Nicolas Jager55903142017-02-01 14:43:45 -0500657 URI uri = URI(querry);
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400658
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400659 /* get account to use */
660 Account *account = nullptr;
661 if (uri.schemeType() != URI::SchemeType::NONE) {
662 /* explicit SIP, SIPS, or RING; make sure account is of right type */
663 account = AvailableAccountModel::instance().currentDefaultAccount(uri.schemeType());
664 if (!account) {
665 const gchar *type = uri.schemeType() == URI::SchemeType::RING ? "RING" : "SIP";
666 g_warning("entered %s uri, but no active %s accounts", type, type);
667 }
668 } else {
669 /* just take the user selected account */
670 account = AvailableAccountModel::instance().currentDefaultAccount();
671 }
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400672
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400673 if (!account) {
674 g_critical("could not get an account for uri: %s", uri.full().toUtf8().constData());
675 return;
676 }
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400677
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400678 /* if RING and not RingID, perform lookup */
679 if (account->protocol() == Account::Protocol::RING &&
680 uri.protocolHint() != URI::ProtocolHint::RING)
681 {
682 lookup_username(self, uri, account);
683 } else {
684 /* no lookup, simply use the URI as is */
685 activate_contact_method(self, uri, account);
686 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500687}
688
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400689static gboolean
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400690save_accounts(GtkWidget *working_dialog)
691{
692 /* save changes to accounts */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400693 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400694
695 if (working_dialog)
696 gtk_widget_destroy(working_dialog);
697
698 return G_SOURCE_REMOVE;
699}
700
Stepan Salenikovich15142182015-03-11 17:15:26 -0400701static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400702settings_clicked(RingMainWindow *win)
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400703{
704 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
705 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
706
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400707 /* check which view to show */
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500708 if (!priv->show_settings) {
709 /* show the settings */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400710 priv->show_settings = TRUE;
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500711
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500712 /* make sure we are not showing a call view so we don't have more than one clutter stage at a time */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400713 selection_changed(win);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500714
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400715 /* show settings */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400716 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400717
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400718 gtk_widget_show(priv->hbox_settings);
719
Stepan Salenikovich41118912015-05-01 11:25:46 -0400720 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400721 if (priv->last_settings_view == priv->media_settings_view)
722 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400723
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400724 /* make sure to show the profile if we're showing the general settings */
725 if (priv->last_settings_view == priv->general_settings_view)
726 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), TRUE);
727
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400728 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400729 } else {
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500730 /* hide the settings */
731 priv->show_settings = FALSE;
732
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400733 /* show working dialog in case save operation takes time */
734 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
735 gtk_window_present(GTK_WINDOW(working));
736
737 /* now save after the time it takes to transition back to the call view (400ms)
738 * the save doesn't happen before the "working" dialog is presented
739 * the timeout function should destroy the "working" dialog when done saving
740 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400741 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400742
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400743 /* show calls */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400744 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400745
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400746 gtk_widget_hide(priv->hbox_settings);
747
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500748 /* make sure video preview is stopped, in case it was started */
749 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400750 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), FALSE);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500751
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400752 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400753
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500754 /* show the view which was selected previously */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400755 selection_changed(win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400756 }
757}
758
759static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400760show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400761{
762 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
763 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
764
765 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400766 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400767 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400768 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400769 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400770 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400771 }
772}
773
774static void
775show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
776{
777 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
778 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
779
780 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400781 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
782 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400783 }
784}
785
Stepan Salenikovichde896112015-05-11 16:46:33 -0400786static void
787show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
788{
789 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
790 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
791
792 if (gtk_toggle_button_get_active(navbutton)) {
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400793 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), TRUE);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400794 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
795 priv->last_settings_view = priv->general_settings_view;
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400796 } else {
797 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), FALSE);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400798 }
799}
800
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400801static void
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400802show_combobox_account_selector(RingMainWindow *self, gboolean show)
803{
804 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
805 /* we only want to show the account selector when there is more than 1 eneabled account; so
806 * every time we want to show it, we should preform this check */
807 gtk_widget_set_visible(priv->combobox_account_selector,
808 show && AvailableAccountModel::instance().rowCount() > 1);
809}
810
811static void
aviau6aeb4852016-08-18 16:01:09 -0400812on_account_creation_completed(RingMainWindow *win)
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400813{
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500814 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400815 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
816
aviau6aeb4852016-08-18 16:01:09 -0400817 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
818
819 /* destroy the wizard */
aviau69081842016-10-14 14:51:54 -0400820 if (priv->account_creation_wizard)
aviau6aeb4852016-08-18 16:01:09 -0400821 {
aviau69081842016-10-14 14:51:54 -0400822 gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), priv->account_creation_wizard);
823 gtk_widget_destroy(priv->account_creation_wizard);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400824 }
aviau6aeb4852016-08-18 16:01:09 -0400825
826 /* show the settings button*/
827 gtk_widget_show(priv->ring_settings);
Nicolas Jager15a8b902017-03-21 07:53:06 -0400828
829 /* show the account selector */
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400830 show_combobox_account_selector(win, TRUE);
Nicolas Jager15a8b902017-03-21 07:53:06 -0400831
Stepan Salenikovich509670b2017-04-24 15:18:06 -0400832 /* select the newly created account */
833 // TODO: the new account might not always be the first one; eg: if the user has an existing
834 // SIP account
Nicolas Jager15a8b902017-03-21 07:53:06 -0400835 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), 0);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400836}
837
838static void
aviau6aeb4852016-08-18 16:01:09 -0400839show_account_creation_wizard(RingMainWindow *win)
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400840{
841 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
842
aviau61d05b32016-10-18 16:47:53 -0400843 if (!priv->account_creation_wizard)
aviau6aeb4852016-08-18 16:01:09 -0400844 {
aviau69081842016-10-14 14:51:54 -0400845 priv->account_creation_wizard = account_creation_wizard_new(false);
846 g_object_add_weak_pointer(G_OBJECT(priv->account_creation_wizard), (gpointer *)&priv->account_creation_wizard);
847 g_signal_connect_swapped(priv->account_creation_wizard, "account-creation-completed", G_CALLBACK(on_account_creation_completed), win);
Stepan Salenikovich419414c2016-06-07 10:35:07 -0400848
aviau6aeb4852016-08-18 16:01:09 -0400849 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
aviau69081842016-10-14 14:51:54 -0400850 priv->account_creation_wizard,
aviau6aeb4852016-08-18 16:01:09 -0400851 ACCOUNT_CREATION_WIZARD_VIEW_NAME);
852 }
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400853
Stepan Salenikovich64505672015-09-24 10:46:07 -0400854 /* hide settings button until account creation is complete */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400855 gtk_widget_hide(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400856 show_combobox_account_selector(win, FALSE);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400857
aviau69081842016-10-14 14:51:54 -0400858 gtk_widget_show(priv->account_creation_wizard);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400859
aviau69081842016-10-14 14:51:54 -0400860 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->account_creation_wizard);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400861}
862
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400863static void
Stepan Salenikovicha06ce192016-11-01 14:26:43 -0400864search_entry_text_changed(GtkSearchEntry *search_entry, RingMainWindow *self)
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400865{
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -0400866 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
867
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400868 /* get the text from the entry */
869 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
870
Stepan Salenikovichf53128f2016-10-07 10:32:16 -0400871 RecentModel::instance().peopleProxy()->setFilterRegExp(QRegExp(text, Qt::CaseInsensitive, QRegExp::FixedString));
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -0400872 contacts_view_set_filter_string(CONTACTS_VIEW(priv->treeview_contacts), text);
873 history_view_set_filter_string(HISTORY_VIEW(priv->treeview_history), text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400874}
875
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400876static gboolean
aviau4c87b832016-12-13 14:18:53 -0500877search_entry_key_released(G_GNUC_UNUSED GtkEntry *search_entry, GdkEventKey *key, RingMainWindow *self)
Stepan Salenikovichcac35042016-10-28 12:07:38 -0400878{
879 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
880
881 // if esc key pressed, clear the regex (keep the text, the user might not want to actually delete it)
882 if (key->keyval == GDK_KEY_Escape) {
883 RecentModel::instance().peopleProxy()->setFilterRegExp(QRegExp());
884 contacts_view_set_filter_string(CONTACTS_VIEW(priv->treeview_contacts), "");
885 history_view_set_filter_string(HISTORY_VIEW(priv->treeview_history), "");
886 return GDK_EVENT_STOP;
887 }
888
889 return GDK_EVENT_PROPAGATE;
890}
891
892static gboolean
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400893dtmf_pressed(RingMainWindow *win,
894 GdkEventKey *event,
895 G_GNUC_UNUSED gpointer user_data)
896{
897 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
898
899 /* we want to react to digit key presses, as long as a GtkEntry is not the
900 * input focus
901 */
902 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
903 if (GTK_IS_ENTRY(focus))
904 return GDK_EVENT_PROPAGATE;
905
906 /* make sure that a call is selected*/
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400907 QItemSelectionModel *selection = CallModel::instance().selectionModel();
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400908 QModelIndex idx = selection->currentIndex();
909 if (!idx.isValid())
910 return GDK_EVENT_PROPAGATE;
911
912 /* make sure that the selected call is in progress */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400913 Call *call = CallModel::instance().getCall(idx);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400914 Call::LifeCycleState state = call->lifeCycleState();
915 if (state != Call::LifeCycleState::PROGRESS)
916 return GDK_EVENT_PROPAGATE;
917
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400918 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
919 * does not result in a 'c' being played.
920 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
921 if ( event->state
922 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
923 return GDK_EVENT_PROPAGATE;
924
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400925 /* pass the character that was entered to be played by the daemon;
926 * the daemon will filter out invalid DTMF characters */
927 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
928 QString val = QString::fromUcs4(&unicode_val, 1);
929 call->playDTMF(val);
930 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400931
932 /* always propogate the key, so we don't steal accelerators/shortcuts */
933 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400934}
935
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400936/**
937 * This function determines if two different contact list views (Conversations, Contacts, History)
938 * have the same item selected. Note that the same item does not necessarily mean the same object.
939 * For example, if the history view has a Call selected and the Conversations view has a Person
940 * selected which is associated with the ContactMethod to which that Call was made, then this will
941 * evaluate to TRUE.
942 */
943static gboolean
944compare_treeview_selection(GtkTreeSelection *selection1, GtkTreeSelection *selection2)
945{
946 g_return_val_if_fail(selection1 && selection2, FALSE);
947
948 if (selection1 == selection2) return TRUE;
949
950 auto idx1 = get_index_from_selection(selection1);
951 auto type1 = idx1.data(static_cast<int>(Ring::Role::ObjectType));
952 auto object1 = idx1.data(static_cast<int>(Ring::Role::Object));
953
954 auto idx2 = get_index_from_selection(selection2);
955 auto type2 = idx2.data(static_cast<int>(Ring::Role::ObjectType));
956 auto object2 = idx2.data(static_cast<int>(Ring::Role::Object));
957
958 if (idx1.isValid() && type1.isValid() && object1.isValid() && idx2.isValid() && type2.isValid() && object2.isValid()) {
959 Call *call1 = nullptr;
960 ContactMethod *cm1 = nullptr;
961 Person *person1 = nullptr;
962
963 Call *call2 = nullptr;
964 ContactMethod *cm2 = nullptr;
965 Person *person2 = nullptr;
966
967 switch(type1.value<Ring::ObjectType>()) {
968 case Ring::ObjectType::Person:
969 person1 = object1.value<Person *>();
970 break;
971 case Ring::ObjectType::ContactMethod:
972 cm1 = object1.value<ContactMethod *>();
973 person1 = cm1->contact();
974 break;
975 case Ring::ObjectType::Call:
976 call1 = object1.value<Call *>();
977 cm1 = call1->peerContactMethod();
978 person1 = cm1->contact();
979 break;
980 case Ring::ObjectType::Media:
981 case Ring::ObjectType::Certificate:
Nicolas Jagerc74f7612017-03-28 09:48:18 -0400982 case Ring::ObjectType::ContactRequest:
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400983 case Ring::ObjectType::COUNT__:
984 // nothing to do
985 break;
986 }
987
988 switch(type2.value<Ring::ObjectType>()) {
989 case Ring::ObjectType::Person:
990 person2 = object2.value<Person *>();
991 break;
992 case Ring::ObjectType::ContactMethod:
993 cm2 = object2.value<ContactMethod *>();
994 person2 = cm2->contact();
995 break;
996 case Ring::ObjectType::Call:
997 call2 = object2.value<Call *>();
998 cm2 = call2->peerContactMethod();
999 person2 = cm2->contact();
1000 break;
1001 case Ring::ObjectType::Media:
1002 case Ring::ObjectType::Certificate:
Nicolas Jagerc74f7612017-03-28 09:48:18 -04001003 case Ring::ObjectType::ContactRequest:
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001004 case Ring::ObjectType::COUNT__:
1005 // nothing to do
1006 break;
1007 }
1008
1009 if (person1 != nullptr && person1 == person2)
1010 return TRUE;
1011 if (cm1 != nullptr && cm1 == cm2)
1012 return TRUE;
1013 if (call1 != nullptr && call1 == call2)
1014 return TRUE;
1015 }
1016
1017 return FALSE;
1018}
1019
1020static void
1021conversation_selection_changed(GtkTreeSelection *selection, RingMainWindow *self)
1022{
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001023 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1024
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001025 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1026 GtkTreeIter iter;
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001027 if (gtk_tree_selection_get_selected(selection, nullptr, &iter)) {
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001028 /* something is selected in the conversations view, clear the selection in the other views;
1029 * unless the current selection is of the same item; we don't try to match the selection in
1030 * all views since not all items exist in all 3 contact list views
1031 */
1032
1033 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
1034 if (!compare_treeview_selection(selection, selection_contacts))
1035 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contacts));
1036
1037 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
1038 if (!compare_treeview_selection(selection, selection_history))
1039 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_history));
1040 }
1041
1042 selection_changed(self);
1043}
1044
1045static void
1046contact_selection_changed(GtkTreeSelection *selection, RingMainWindow *self)
1047{
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001048 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1049
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001050 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1051 GtkTreeIter iter;
1052 if (gtk_tree_selection_get_selected(selection, nullptr, &iter)) {
1053 /* something is selected in the contacts view, clear the selection in the other views;
1054 * unless the current selection is of the same item; we don't try to match the selection in
1055 * all views since not all items exist in all 3 contact list views
1056 */
1057 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
1058 if (!compare_treeview_selection(selection, selection_conversations))
1059 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
1060
1061 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
1062 if (!compare_treeview_selection(selection, selection_history))
1063 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_history));
1064 }
1065
1066 selection_changed(self);
1067}
1068
1069static void
1070history_selection_changed(GtkTreeSelection *selection, RingMainWindow *self)
1071{
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001072 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1073
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001074 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1075 GtkTreeIter iter;
1076 if (gtk_tree_selection_get_selected(selection, nullptr, &iter)) {
1077 /* something is selected in the history view, clear the selection in the other views;
1078 * unless the current selection is of the same item; we don't try to match the selection in
1079 * all views since not all items exist in all 3 contact list views
1080 */
1081
1082 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
1083 if (!compare_treeview_selection(selection, selection_conversations))
1084 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
1085
1086 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
1087 if (!compare_treeview_selection(selection, selection_contacts))
1088 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contacts));
1089 }
1090
1091 selection_changed(self);
1092}
1093
Nicolas Jager0efc8432017-03-22 16:22:00 -04001094
1095/**
1096 * gtk callback function called when the selection in the contact request list changed
1097 */
1098static void
1099contact_request_selection_changed(GtkTreeSelection *selection, RingMainWindow *self)
1100{
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001101 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1102
Nicolas Jager0efc8432017-03-22 16:22:00 -04001103 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1104 GtkTreeIter iter;
1105
1106 if (gtk_tree_selection_get_selected(selection, nullptr, &iter)) {
1107 /* unselect previous selection in conversations list */
1108 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
1109 if (!compare_treeview_selection(selection, selection_conversations))
1110 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
1111
1112 /* unselect previous selection in contacts list */
1113 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
1114 if (!compare_treeview_selection(selection, selection_contacts))
1115 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contacts));
1116 }
1117
1118 selection_changed(self);
1119}
1120
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001121static gboolean
1122window_size_changed(GtkWidget *win, GdkEventConfigure *event, gpointer)
1123{
1124 auto *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
1125
1126 g_settings_set_int(priv->settings, "window-width", event->width);
1127 g_settings_set_int(priv->settings, "window-height", event->height);
1128
1129 return GDK_EVENT_PROPAGATE;
1130}
1131
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001132static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001133search_entry_places_call_changed(GSettings *settings, const gchar *key, RingMainWindow *self)
1134{
1135 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1136
1137 if (g_settings_get_boolean(settings, key)) {
1138 gtk_widget_set_tooltip_text(priv->button_new_conversation, C_("button next to search entry will place a new call", "place call"));
1139 } else {
1140 gtk_widget_set_tooltip_text(priv->button_new_conversation, C_("button next to search entry will open chat", "open chat"));
1141 }
1142}
1143
1144static void
aviau6aeb4852016-08-18 16:01:09 -04001145handle_account_migrations(RingMainWindow *win)
1146{
1147 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
1148
1149 /* If there is an existing migration view, remove it */
aviau69081842016-10-14 14:51:54 -04001150 if (priv->account_migration_view)
aviau6aeb4852016-08-18 16:01:09 -04001151 {
Stepan Salenikovich56530b12016-10-20 17:51:34 -04001152 gtk_widget_destroy(priv->account_migration_view);
1153 priv->account_migration_view = nullptr;
aviau6aeb4852016-08-18 16:01:09 -04001154 }
1155
1156 QList<Account*> accounts = AccountModel::instance().accountsToMigrate();
1157 if (!accounts.isEmpty())
1158 {
1159 Account* account = accounts.first();
aviau6aeb4852016-08-18 16:01:09 -04001160
aviau69081842016-10-14 14:51:54 -04001161 priv->account_migration_view = account_migration_view_new(account);
aviau69081842016-10-14 14:51:54 -04001162 g_signal_connect_swapped(priv->account_migration_view, "account-migration-completed", G_CALLBACK(handle_account_migrations), win);
1163 g_signal_connect_swapped(priv->account_migration_view, "account-migration-failed", G_CALLBACK(handle_account_migrations), win);
aviau6aeb4852016-08-18 16:01:09 -04001164
1165 gtk_widget_hide(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001166 show_combobox_account_selector(win, FALSE);
aviau69081842016-10-14 14:51:54 -04001167 gtk_widget_show(priv->account_migration_view);
aviau6aeb4852016-08-18 16:01:09 -04001168 gtk_stack_add_named(
1169 GTK_STACK(priv->stack_main_view),
aviau69081842016-10-14 14:51:54 -04001170 priv->account_migration_view,
aviau6aeb4852016-08-18 16:01:09 -04001171 ACCOUNT_MIGRATION_VIEW_NAME
1172 );
1173 gtk_stack_set_visible_child_name(
1174 GTK_STACK(priv->stack_main_view),
1175 ACCOUNT_MIGRATION_VIEW_NAME
1176 );
1177 }
1178 else
1179 {
1180 gtk_widget_show(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001181 show_combobox_account_selector(win, TRUE);
aviau6aeb4852016-08-18 16:01:09 -04001182 }
1183}
1184
Nicolas Jager5e2be732017-03-18 21:35:50 -04001185/**
1186 * set the column value by printing the alias and the state of an account in combobox_account_selector.
1187 */
1188static void
1189print_account_and_state(GtkCellLayout* cell_layout,
1190 GtkCellRenderer* cell_renderer,
1191 GtkTreeModel* tree_model,
1192 GtkTreeIter* iter,
1193 gpointer* data)
1194{
1195 (void) cell_layout; // UNUSED
1196 (void) data; // UNUSED
1197
1198 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(tree_model), iter);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001199 gchar* display_alias = nullptr;
1200
1201 if (idx.isValid()) {
Nicolas Jagera169c712017-04-14 12:02:50 -04001202 auto state = idx.data(static_cast<int>(Account::Role::RegistrationState));
1203 auto name = idx.data(static_cast<int>(Account::Role::Alias));
1204 auto account_alias = name.value<QString>().toStdString();
Nicolas Jager5e2be732017-03-18 21:35:50 -04001205
Nicolas Jagera169c712017-04-14 12:02:50 -04001206 switch (state.value<Account::RegistrationState>()) {
Nicolas Jager5e2be732017-03-18 21:35:50 -04001207 case Account::RegistrationState::READY:
1208 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001209 display_alias = g_strdup_printf("%s",account_alias.c_str());
Nicolas Jager5e2be732017-03-18 21:35:50 -04001210 }
1211 break;
1212 case Account::RegistrationState::UNREGISTERED:
1213 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001214 display_alias = g_strdup_printf("<span fgcolor=\"gray\">%s</span>", account_alias.c_str());
Nicolas Jager5e2be732017-03-18 21:35:50 -04001215 }
1216 break;
1217 case Account::RegistrationState::TRYING:
1218 case Account::RegistrationState::INITIALIZING:
1219 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001220 auto account_alias_i18n = g_markup_printf_escaped (C_("string format will be replaced by the account alias", "%s..."), account_alias.c_str());
Nicolas Jager5e2be732017-03-18 21:35:50 -04001221 display_alias = g_strdup_printf("<span fgcolor=\"gray\" font_style=\"italic\">%s</span>", account_alias_i18n);
1222 }
1223 break;
1224 case Account::RegistrationState::ERROR:
1225 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001226 auto error_string = g_markup_printf_escaped(C_("string format will be replaced by the account alias", "%s (error)"), account_alias.c_str());
Nicolas Jager5e2be732017-03-18 21:35:50 -04001227 display_alias = g_strdup_printf("<span fgcolor=\"red\">%s</span>", error_string);
1228 }
1229 case Account::RegistrationState::COUNT__:
1230 break;
1231 }
1232 }
1233
1234 g_object_set(G_OBJECT(cell_renderer), "markup", display_alias, nullptr);
1235 g_free(display_alias);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001236
1237}
1238
Nicolas Jager15a8b902017-03-21 07:53:06 -04001239static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001240ring_main_window_init(RingMainWindow *win)
1241{
1242 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
1243 gtk_widget_init_template(GTK_WIDGET(win));
1244
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001245 /* bind to window size settings */
1246 priv->settings = g_settings_new_full(get_ring_schema(), nullptr, nullptr);
1247 auto width = g_settings_get_int(priv->settings, "window-width");
1248 auto height = g_settings_get_int(priv->settings, "window-height");
1249 gtk_window_set_default_size(GTK_WINDOW(win), width, height);
1250 g_signal_connect(win, "configure-event", G_CALLBACK(window_size_changed), nullptr);
1251
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001252 /* search-entry-places-call setting */
1253 search_entry_places_call_changed(priv->settings, "search-entry-places-call", win);
1254 g_signal_connect(priv->settings, "changed::search-entry-places-call", G_CALLBACK(search_entry_places_call_changed), win);
1255
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001256 /* set window icon */
1257 GError *error = NULL;
1258 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
1259 if (icon == NULL) {
1260 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -04001261 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001262 } else
1263 gtk_window_set_icon(GTK_WINDOW(win), icon);
1264
1265 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001266 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
Stepan Salenikovichd61974b2016-03-18 16:36:27 -04001267 -1, 16, TRUE, &error);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001268 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001269 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -04001270 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001271 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001272 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001273
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001274 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001275 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
1276 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001277 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001278 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001279
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001280 /* settings icon */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -04001281 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001282
1283 /* connect settings button signal */
Stepan Salenikovichce1d0132017-04-26 14:14:37 -04001284 g_signal_connect_swapped(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001285
1286 /* add the call view to the main stack */
1287 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
1288 priv->vbox_call_view,
1289 CALL_VIEW_NAME);
1290
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001291 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001292 priv->account_settings_view = account_view_new();
1293 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
1294
Stepan Salenikovich0bd53492015-05-11 14:28:52 -04001295 priv->media_settings_view = media_settings_view_new();
1296 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->media_settings_view, MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001297
Stepan Salenikovichde896112015-05-11 16:46:33 -04001298 priv->general_settings_view = general_settings_view_new();
1299 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
1300
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001301 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -04001302 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
1303 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001304
1305 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001306 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001307 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -04001308 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001309
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001310 /* populate the notebook */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001311 priv->treeview_conversations = recent_contacts_view_new();
1312 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), priv->treeview_conversations);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -04001313
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001314 priv->treeview_contacts = contacts_view_new();
1315 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contacts), priv->treeview_contacts);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001316
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001317 priv->treeview_history = history_view_new();
1318 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_history), priv->treeview_history);
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001319
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001320 auto available_accounts_changed = [win, priv] {
1321 /* if we're hiding the settings it means we're in the migration or wizard view and we don't
1322 * want to show the combo box */
1323 if (gtk_widget_get_visible(priv->ring_settings))
1324 show_combobox_account_selector(win, TRUE);
1325 };
1326 QObject::connect(&AvailableAccountModel::instance(), &QAbstractItemModel::rowsRemoved, available_accounts_changed);
1327 QObject::connect(&AvailableAccountModel::instance(), &QAbstractItemModel::rowsInserted, available_accounts_changed);
Nicolas Jagera169c712017-04-14 12:02:50 -04001328
Nicolas Jager0efc8432017-03-22 16:22:00 -04001329 priv->treeview_contact_requests = pending_contact_requests_view_new();
1330 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contact_requests), priv->treeview_contact_requests);
1331
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04001332 /* welcome/default view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001333 priv->welcome_view = ring_welcome_view_new();
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001334 g_object_ref(priv->welcome_view); // increase ref because don't want it to be destroyed when not displayed
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001335 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
1336 gtk_widget_show(priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001337
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001338 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
1339 g_signal_connect(selection_conversations, "changed", G_CALLBACK(conversation_selection_changed), win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001340
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001341 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
1342 g_signal_connect(selection_contacts, "changed", G_CALLBACK(contact_selection_changed), win);
1343
1344 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
1345 g_signal_connect(selection_history, "changed", G_CALLBACK(history_selection_changed), win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001346
Nicolas Jager0efc8432017-03-22 16:22:00 -04001347 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
1348 g_signal_connect(selection_contact_request, "changed", G_CALLBACK(contact_request_selection_changed), win);
1349
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -04001350 g_signal_connect_swapped(priv->button_new_conversation, "clicked", G_CALLBACK(search_entry_activated), win);
1351 g_signal_connect_swapped(priv->search_entry, "activate", G_CALLBACK(search_entry_activated), win);
Stepan Salenikovicha06ce192016-11-01 14:26:43 -04001352 g_signal_connect(priv->search_entry, "search-changed", G_CALLBACK(search_entry_text_changed), win);
Stepan Salenikovichcac35042016-10-28 12:07:38 -04001353 g_signal_connect(priv->search_entry, "key-release-event", G_CALLBACK(search_entry_key_released), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001354
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001355 /* make sure the incoming call is the selected call in the CallModel */
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001356 QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001357 &CallModel::instance(),
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001358 &CallModel::incomingCall,
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -04001359 [priv](Call* call) {
Nicolas Jager85e992a2017-03-22 04:11:47 -04001360 // select the revelant account
Nicolas Jager15434f52017-04-18 10:30:27 -04001361 auto idx = call->account()->index(); // from AccountModel
1362 idx = AvailableAccountModel::instance().mapFromSource(idx); // from AvailableAccountModel
Nicolas Jagerf71f8d82017-04-17 10:22:06 -04001363
Nicolas Jager15434f52017-04-18 10:30:27 -04001364 AvailableAccountModel::instance().selectionModel()->setCurrentIndex(idx,
1365 QItemSelectionModel::ClearAndSelect);
Nicolas Jager85e992a2017-03-22 04:11:47 -04001366
Stepan Salenikovichf53128f2016-10-07 10:32:16 -04001367 // clear the regex to make sure the call is shown
1368 RecentModel::instance().peopleProxy()->setFilterRegExp(QRegExp());
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -04001369 contacts_view_set_filter_string(CONTACTS_VIEW(priv->treeview_contacts),"");
1370 history_view_set_filter_string(HISTORY_VIEW(priv->treeview_history), "");
Stepan Salenikovichf53128f2016-10-07 10:32:16 -04001371
1372 // now make sure its selected
1373 RecentModel::instance().selectionModel()->clearCurrentIndex();
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001374 CallModel::instance().selectCall(call);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001375 }
1376 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001377
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001378 /* react to digit key press events */
1379 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich48b45c12015-09-25 15:49:51 -04001380
1381 /* set the search entry placeholder text */
1382 gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
1383 C_("Please try to make the translation 50 chars or less so that it fits into the layout", "Search contacts or enter number"));
aviau6aeb4852016-08-18 16:01:09 -04001384
aviau039001d2016-09-29 16:39:05 -04001385 /* init chat webkit container so that it starts loading before the first time we need it*/
1386 get_webkit_chat_container(win);
1387
Stepan Salenikovichcbc8dcc2017-04-05 11:27:10 -04001388 if (has_ring_account()) {
1389 /* user has ring account, so show the call view right away */
1390 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
1391
1392 handle_account_migrations(win);
1393 } else {
1394 /* user has to create the ring account */
1395 show_account_creation_wizard(win);
1396 }
Nicolas Jager15a8b902017-03-21 07:53:06 -04001397
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001398 /* setup account selector */
1399 gtk_combo_box_set_qmodel(
1400 GTK_COMBO_BOX(priv->combobox_account_selector),
1401 &AvailableAccountModel::instance(),
1402 AvailableAccountModel::instance().selectionModel()
1403 );
Nicolas Jager15a8b902017-03-21 07:53:06 -04001404
Nicolas Jagera169c712017-04-14 12:02:50 -04001405 /* set visibility */
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001406 show_combobox_account_selector(win, TRUE);
Nicolas Jagera169c712017-04-14 12:02:50 -04001407
Nicolas Jager15a8b902017-03-21 07:53:06 -04001408 /* layout */
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001409 auto *renderer = gtk_cell_renderer_text_new();
Nicolas Jager15a8b902017-03-21 07:53:06 -04001410 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_account_selector), renderer, FALSE);
1411 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_account_selector), renderer, "text", 0, NULL);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001412 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(priv->combobox_account_selector),
1413 renderer,
1414 (GtkCellLayoutDataFunc)print_account_and_state,
1415 nullptr, nullptr);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001416
Stepan Salenikovichce1d0132017-04-26 14:14:37 -04001417 // we closing any view opened to avoid confusion (especially between SIP and Ring protocols).
1418 g_signal_connect_swapped(priv->combobox_account_selector, "changed", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001419}
1420
1421static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001422ring_main_window_dispose(GObject *object)
1423{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001424 RingMainWindow *self = RING_MAIN_WINDOW(object);
1425 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1426
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001427 QObject::disconnect(priv->selected_item_changed);
1428 QObject::disconnect(priv->selected_call_over);
aviau8c0b2422016-10-31 16:06:44 -04001429 QObject::disconnect(priv->username_lookup);
aviau039001d2016-09-29 16:39:05 -04001430
1431 g_clear_object(&priv->welcome_view);
1432 g_clear_object(&priv->webkit_chat_container);
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001433
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001434 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1435}
1436
1437static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001438ring_main_window_finalize(GObject *object)
1439{
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001440 RingMainWindow *self = RING_MAIN_WINDOW(object);
1441 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1442
1443 g_clear_object(&priv->settings);
1444
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001445 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001446}
1447
1448static void
1449ring_main_window_class_init(RingMainWindowClass *klass)
1450{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001451 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001452 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001453
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001454 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1455 "/cx/ring/RingGnome/ringmainwindow.ui");
1456
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001457 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001458 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_smartview);
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001459 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contacts);
1460 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_history);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001461 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1462 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1463 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1464 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001465 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001466 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001467 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001468 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001469 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, frame_call);
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001470 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_new_conversation );
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001471 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001472 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001473 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Nicolas Jager55903142017-02-01 14:43:45 -05001474 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_lookup);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001475 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_account_selector);
Nicolas Jager0efc8432017-03-22 16:22:00 -04001476 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contact_requests);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001477}
1478
1479GtkWidget *
1480ring_main_window_new (GtkApplication *app)
1481{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001482 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1483
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001484 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001485}