blob: d38dfca5682940e9ab904b129f1562fb76c96c7f [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>
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04004 * Author: Guillaume Roguew <guillaume.roguez@savoirfairelinux.com>
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050019 */
20
21#include "ringmainwindow.h"
22
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040023// system
24#include <string.h>
25#include <memory>
26
27// GTK+ related
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040028#include <glib/gi18n.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040029
30// Qt
31#include <QtCore/QItemSelectionModel>
32#include <QtCore/QSortFilterProxyModel>
Stepan Salenikoviche9933242016-06-21 18:08:48 -040033#include <QtCore/QDateTime>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040034
35// LRC
Stepan Salenikovicha3557452015-02-20 14:14:12 -050036#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050037#include <call.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040038#include <contactmethod.h>
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040039#include <accountmodel.h>
Edric Milaret58e71072016-03-21 12:16:37 -040040#include <codecmodel.h>
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040041#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040042#include <personmodel.h>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040043#include <globalinstances.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040044#include <categorizedcontactmodel.h>
45#include <recentmodel.h>
46#include <profilemodel.h>
47#include <profile.h>
Stepan Salenikovichf53128f2016-10-07 10:32:16 -040048#include <phonedirectorymodel.h>
Stepan Salenikovich7b7be482016-11-03 16:57:48 -040049#include <availableaccountmodel.h>
Nicolas Jager0efc8432017-03-22 16:22:00 -040050#include <contactrequest.h>
Guillaume Roguez0a411202017-05-25 15:27:26 -040051#include <pendingcontactrequestmodel.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040052
53// Ring client
54#include "models/gtkqtreemodel.h"
55#include "incomingcallview.h"
56#include "currentcallview.h"
Stepan Salenikovichf6078222016-10-03 17:31:16 -040057#include "models/gtkqtreemodel.h"
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040058#include "accountview.h"
59#include "dialogs.h"
60#include "mediasettingsview.h"
61#include "utils/drawing.h"
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040062#include "native/pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040063#include "models/activeitemproxymodel.h"
Stepan Salenikovich9816a942015-04-22 17:49:16 -040064#include "utils/calling.h"
65#include "contactsview.h"
66#include "historyview.h"
67#include "utils/models.h"
Stepan Salenikovichde896112015-05-11 16:46:33 -040068#include "generalsettingsview.h"
Stepan Salenikovich75a39172015-07-10 13:21:08 -040069#include "utils/accounts.h"
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040070#include "ringwelcomeview.h"
aviau6aeb4852016-08-18 16:01:09 -040071#include "accountmigrationview.h"
72#include "accountcreationwizard.h"
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040073#include "recentcontactsview.h"
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050074#include "chatview.h"
Nicolas Jagerb413b302016-05-06 11:41:32 -040075#include "avatarmanipulation.h"
Stepan Salenikovichba87ae22016-09-13 14:27:20 -040076#include "utils/files.h"
Nicolas Jager0efc8432017-03-22 16:22:00 -040077#include "pendingcontactrequests.h"
Nicolas Jagerd00761d2017-03-22 16:22:00 -040078#include "contactrequestcontentview.h"
Nicolas Jagerb413b302016-05-06 11:41:32 -040079
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040080static constexpr const char* CALL_VIEW_NAME = "calls";
aviau6aeb4852016-08-18 16:01:09 -040081static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
82static constexpr const char* ACCOUNT_MIGRATION_VIEW_NAME = "account-migration-view";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040083static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
84static constexpr const char* AUDIO_SETTINGS_VIEW_NAME = "audio";
85static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
86static constexpr const char* ACCOUNT_SETTINGS_VIEW_NAME = "accounts";
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040087static constexpr const char* DEFAULT_VIEW_NAME = "welcome";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040088static constexpr const char* VIEW_CONTACTS = "contacts";
89static constexpr const char* VIEW_HISTORY = "history";
90static constexpr const char* VIEW_PRESENCE = "presence";
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050091
92struct _RingMainWindow
93{
94 GtkApplicationWindow parent;
95};
96
97struct _RingMainWindowClass
98{
99 GtkApplicationWindowClass parent_class;
100};
101
102typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
103
104struct _RingMainWindowPrivate
105{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400106 GtkWidget *ring_menu;
107 GtkWidget *image_ring;
108 GtkWidget *ring_settings;
109 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400110 GtkWidget *hbox_settings;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400111 GtkWidget *scrolled_window_smartview;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400112 GtkWidget *treeview_conversations;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400113 GtkWidget *scrolled_window_contacts;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400114 GtkWidget *treeview_contacts;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400115 GtkWidget *scrolled_window_history;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400116 GtkWidget *treeview_history;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400117 GtkWidget *vbox_left_pane;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500118 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500119 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400120 GtkWidget *vbox_call_view;
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400121 GtkWidget *frame_call;
122 GtkWidget *welcome_view;
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400123 GtkWidget *button_new_conversation ;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400124 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400125 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400126 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400127 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400128 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400129 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400130 GtkWidget *radiobutton_account_settings;
aviau69081842016-10-14 14:51:54 -0400131 GtkWidget *account_creation_wizard;
132 GtkWidget *account_migration_view;
Nicolas Jager55903142017-02-01 14:43:45 -0500133 GtkWidget *spinner_lookup;
Nicolas Jager15a8b902017-03-21 07:53:06 -0400134 GtkWidget *combobox_account_selector;
Nicolas Jager0efc8432017-03-22 16:22:00 -0400135 GtkWidget *treeview_contact_requests;
136 GtkWidget *scrolled_window_contact_requests;
137 GtkWidget *contact_request_view;
Guillaume Roguez0a411202017-05-25 15:27:26 -0400138 GtkWidget *image_contact_requests_list;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400139
aviau8c0b2422016-10-31 16:06:44 -0400140 /* Pending ring usernames lookup for the search entry */
141 QMetaObject::Connection username_lookup;
142
aviau039001d2016-09-29 16:39:05 -0400143 /* The webkit_chat_container is created once, then reused for all chat views */
144 GtkWidget *webkit_chat_container;
145
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400146 QMetaObject::Connection selected_item_changed;
147 QMetaObject::Connection selected_call_over;
Guillaume Roguez0a411202017-05-25 15:27:26 -0400148 QMetaObject::Connection account_request_added;
149 QMetaObject::Connection account_request_accepted;
150 QMetaObject::Connection account_request_discarded;
151 QMetaObject::Connection account_request_;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400152
153 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400154
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500155 /* fullscreen */
156 gboolean is_fullscreen;
Stepan Salenikovichba87ae22016-09-13 14:27:20 -0400157
158 GSettings *settings;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500159};
160
161G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
162
163#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
164
aviau039001d2016-09-29 16:39:05 -0400165static gboolean selection_changed(RingMainWindow *win);
166
167static WebKitChatContainer*
168get_webkit_chat_container(RingMainWindow *win)
169{
170 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
171 if (!priv->webkit_chat_container)
172 {
173 priv->webkit_chat_container = webkit_chat_container_new();
174
175 //We don't want it to be deleted, ever.
176 g_object_ref(priv->webkit_chat_container);
177 }
178 return WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container);
179}
180
Stepan Salenikovich69771842015-02-24 18:11:45 -0500181static void
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500182enter_full_screen(RingMainWindow *self)
183{
184 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
185 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
186
187 if (!priv->is_fullscreen) {
188 gtk_widget_hide(priv->vbox_left_pane);
189 gtk_window_fullscreen(GTK_WINDOW(self));
190 priv->is_fullscreen = TRUE;
191 }
192}
193
194static void
195leave_full_screen(RingMainWindow *self)
196{
197 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
198 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
199
200 if (priv->is_fullscreen) {
201 gtk_widget_show(priv->vbox_left_pane);
202 gtk_window_unfullscreen(GTK_WINDOW(self));
203 priv->is_fullscreen = FALSE;
204 }
205}
206
207static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400208video_double_clicked(RingMainWindow *self)
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500209{
210 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
211 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
212
213 if (priv->is_fullscreen) {
214 leave_full_screen(self);
215 } else {
216 enter_full_screen(self);
217 }
218}
219
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400220static void
Guillaume Roguez0a411202017-05-25 15:27:26 -0400221set_pending_contact_request_tab_icon(const Account* account, RingMainWindow* self)
222{
Guillaume Roguez0a411202017-05-25 15:27:26 -0400223 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
Nicolas Jager2bc3fd92017-05-29 12:48:07 -0400224 bool is_ring = account && account->protocol() == Account::Protocol::RING;
225 gtk_widget_set_visible(priv->scrolled_window_contact_requests, is_ring);
226
227 if (not is_ring)
228 return;
Guillaume Roguez0a411202017-05-25 15:27:26 -0400229
230 gtk_image_set_from_resource(GTK_IMAGE(priv->image_contact_requests_list),
231 (account->pendingContactRequestModel()->rowCount())
232 ? "/cx/ring/RingGnome/contact_requests_list_with_notification"
233 : "/cx/ring/RingGnome/contact_requests_list");
234};
235
236static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400237hide_view_clicked(RingMainWindow *self)
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400238{
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400239 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400240
241 /* clear selection */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400242 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
243 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
244 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
245 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contacts));
246 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
247 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_history));
Nicolas Jager0efc8432017-03-22 16:22:00 -0400248 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
249 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contact_request));
250
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400251}
252
aviau039001d2016-09-29 16:39:05 -0400253static void
254change_view(RingMainWindow *self, GtkWidget* old, QObject *object, GType type)
255{
256 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
257 leave_full_screen(self);
258 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old);
259
260 GtkWidget *new_view = nullptr;
261
262 QObject::disconnect(priv->selected_item_changed);
263 QObject::disconnect(priv->selected_call_over);
264
265 if (g_type_is_a(INCOMING_CALL_VIEW_TYPE, type)) {
266 if (auto call = qobject_cast<Call *>(object)) {
267 new_view = incoming_call_view_new(call, get_webkit_chat_container(self));
268 priv->selected_item_changed = QObject::connect(
269 call,
270 &Call::lifeCycleStateChanged,
271 [self] (Call::LifeCycleState, Call::LifeCycleState)
272 {g_idle_add((GSourceFunc)selection_changed, self);}
273 );
274 priv->selected_call_over = QObject::connect(
275 call,
276 &Call::isOver,
277 [self] ()
278 {g_idle_add((GSourceFunc)selection_changed, self);}
279 );
280 } else
281 g_warning("Trying to display a view of type IncomingCallView, but the object is not of type Call");
282 } else if (g_type_is_a(CURRENT_CALL_VIEW_TYPE, type)) {
283 if (auto call = qobject_cast<Call *>(object)) {
284 new_view = current_call_view_new(call, get_webkit_chat_container(self));
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400285 g_signal_connect_swapped(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400286 priv->selected_item_changed = QObject::connect(
287 call,
288 &Call::lifeCycleStateChanged,
289 [self] (Call::LifeCycleState, Call::LifeCycleState)
290 {g_idle_add((GSourceFunc)selection_changed, self);}
291 );
292 priv->selected_call_over = QObject::connect(
293 call,
294 &Call::isOver,
295 [self] ()
296 {g_idle_add((GSourceFunc)selection_changed, self);}
297 );
298 } else
299 g_warning("Trying to display a view of type CurrentCallView, but the object is not of type Call");
300 } else if (g_type_is_a(CHAT_VIEW_TYPE, type)) {
301 if (auto person = qobject_cast<Person *>(object)) {
302 new_view = chat_view_new_person(get_webkit_chat_container(self), person);
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400303 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400304
305 /* connect to the Person's callAdded signal, because we want to switch to the call view
306 * in this case */
307 priv->selected_item_changed = QObject::connect(
308 person,
309 &Person::callAdded,
310 [self] (Call*)
311 {g_idle_add((GSourceFunc)selection_changed, self);}
312 );
313 } else if (auto cm = qobject_cast<ContactMethod *>(object)) {
314 new_view = chat_view_new_cm(get_webkit_chat_container(self), cm);
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400315 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400316
317 /* connect to the ContactMethod's callAdded signal, because we want to switch to the
318 * call view in this case */
319 priv->selected_item_changed = QObject::connect(
320 cm,
321 &ContactMethod::callAdded,
322 [self] (Call*)
323 {g_idle_add((GSourceFunc)selection_changed, self);}
324 );
325 } else {
326 g_warning("Trying to display a veiw of type ChatView, but the object is neither a Person nor a ContactMethod");
327 }
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400328 } else if (g_type_is_a(CONTACT_REQUEST_CONTENT_VIEW_TYPE, type)) {
329 if (auto contact_request = qobject_cast<ContactRequest *>(object)) {
330 new_view = contact_request_content_view_new(contact_request);
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400331 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400332 }
aviau039001d2016-09-29 16:39:05 -0400333 } else {
334 // display the welcome view
335 new_view = priv->welcome_view;
336 }
337
338 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
339 gtk_widget_show(new_view);
340}
341
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500342/**
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400343 * This function determines which view to display in the right panel of the main window based on
344 * which item is selected in one of the 3 contact list views (Conversations, Contacts, History). The
345 * possible views ares:
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500346 * - incoming call view
347 * - current call view
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500348 * - chat view
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400349 * - contact request content view
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400350 * - welcome view (if no valid item is selected)
351 *
352 * There should never be a conflict of which item should be displayed (ie: which item is selected),
353 * as there should only ever be one selection at a time in the 3 views except in the case that the
354 * same item is selected in more than one view (see the compare_treeview_selection() function).
355 *
356 * This function could be called from a g_idle source, so it returns a boolean to remove itself from
357 * being called again. The boolean doesn't reflect success nor failure.
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500358 */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400359static gboolean
360selection_changed(RingMainWindow *win)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500361{
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400362
363 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500364 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
365
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400366 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500367
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400368 /* if we're showing the settings, then we need to make sure to remove any possible ongoing call
369 * view so that we don't have more than one VideoWidget at a time */
370 if (priv->show_settings) {
371 if (old_view != priv->welcome_view) {
372 leave_full_screen(win);
373 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_view);
374 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
375 gtk_widget_show(priv->welcome_view);
376 }
377 return G_SOURCE_REMOVE;
378 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500379
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400380 /* there should only be one item selected at a time, get which one is selected */
381 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
382 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
383 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
Nicolas Jager0efc8432017-03-22 16:22:00 -0400384 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400385
386 GtkTreeModel *model = nullptr;
387 GtkTreeIter iter;
388 QModelIndex idx;
389
390 if (gtk_tree_selection_get_selected(selection_conversations, &model, &iter)) {
Stepan Salenikovichf6078222016-10-03 17:31:16 -0400391 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400392 } else if (gtk_tree_selection_get_selected(selection_contacts, &model, &iter)) {
Stepan Salenikovichf6078222016-10-03 17:31:16 -0400393 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400394 } else if (gtk_tree_selection_get_selected(selection_history, &model, &iter)) {
Stepan Salenikovichf6078222016-10-03 17:31:16 -0400395 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Nicolas Jager0efc8432017-03-22 16:22:00 -0400396 } else if (gtk_tree_selection_get_selected(selection_contact_request, &model, &iter)) {
397 idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400398 }
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500399
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500400 /* check which object type is selected */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400401 auto type = idx.data(static_cast<int>(Ring::Role::ObjectType));
402 auto object = idx.data(static_cast<int>(Ring::Role::Object));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500403
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400404 if (idx.isValid() && type.isValid() && object.isValid()) {
Stepan Salenikovich69771842015-02-24 18:11:45 -0500405
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400406 /* we prioritize the views in the following order:
407 * - call view (if there is an ongoing call associated with the item)
408 * - chat view built from Person
409 * - chat view built from ContactMethod
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400410 * - contact request content view built from ContactRequest
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400411 */
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500412
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400413 Person *person = nullptr;
414 ContactMethod *cm = nullptr;
415 Call *call = nullptr;
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400416 ContactRequest *contact_request = nullptr;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500417
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400418 /* use the RecentModel to see if there are any ongoing calls associated with the selected item */
419 switch(type.value<Ring::ObjectType>()) {
420 case Ring::ObjectType::Person:
421 {
422 person = object.value<Person *>();
423 auto recent_idx = RecentModel::instance().getIndex(person);
424 if (recent_idx.isValid()) {
425 call = RecentModel::instance().getActiveCall(recent_idx);
426 }
427 }
428 break;
429 case Ring::ObjectType::ContactMethod:
430 {
431 cm = object.value<ContactMethod *>();
432 auto recent_idx = RecentModel::instance().getIndex(cm);
433 if (recent_idx.isValid()) {
434 call = RecentModel::instance().getActiveCall(recent_idx);
435 }
436 }
437 break;
438 case Ring::ObjectType::Call:
439 {
440 /* if the call is over (eg: if it comes from the history model) then we first check
441 * if there is an ongoing call with the same ContactMethod, otherwise we need to get
442 * the associated Person or ContactMethod */
443 call = object.value<Call *>();
444 if (call->isHistory()) {
445 cm = call->peerContactMethod();
446 call = nullptr;
447 if (cm) {
448 auto recent_idx = RecentModel::instance().getIndex(cm);
449 if (recent_idx.isValid()) {
450 call = RecentModel::instance().getActiveCall(recent_idx);
451 }
452 person = cm->contact();
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500453 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500454 }
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400455 }
456 break;
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400457 case Ring::ObjectType::ContactRequest:
458 {
459 contact_request = object.value<ContactRequest *>();
460 }
461 break;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400462 case Ring::ObjectType::Media:
463 case Ring::ObjectType::Certificate:
464 case Ring::ObjectType::COUNT__:
465 // nothing to do
466 break;
467 }
468
469 /* we prioritize showing the call view */
470 if (call) {
471 auto state = call->lifeCycleState();
472
473 Call *current_call = nullptr;
474
475 switch(state) {
476 case Call::LifeCycleState::CREATION:
477 case Call::LifeCycleState::INITIALIZATION:
478 case Call::LifeCycleState::FINISHED:
479 // check if we're already displaying this call
480 if (IS_INCOMING_CALL_VIEW(old_view))
481 current_call = incoming_call_view_get_call(INCOMING_CALL_VIEW(old_view));
482 if (current_call != call)
aviau039001d2016-09-29 16:39:05 -0400483 change_view(win, old_view, call, INCOMING_CALL_VIEW_TYPE);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400484 break;
485 case Call::LifeCycleState::PROGRESS:
486 // check if we're already displaying this call
487 if (IS_CURRENT_CALL_VIEW(old_view))
488 current_call = current_call_view_get_call(CURRENT_CALL_VIEW(old_view));
aviau039001d2016-09-29 16:39:05 -0400489 if (current_call != call)
490 change_view(win, old_view, call, CURRENT_CALL_VIEW_TYPE);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400491 break;
492 case Call::LifeCycleState::COUNT__:
493 g_warning("LifeCycleState should never be COUNT");
494 break;
495 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500496
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400497 } else if (person) {
498 /* show chat view constructed from Person object */
499
500 // check if we're already displaying the chat view for this person
501 Person *current_person = nullptr;
502 if (IS_CHAT_VIEW(old_view))
503 current_person = chat_view_get_person(CHAT_VIEW(old_view));
504
aviau039001d2016-09-29 16:39:05 -0400505 if (current_person != person)
506 change_view(win, old_view, person, CHAT_VIEW_TYPE);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400507 } else if (cm) {
508 /* show chat view constructed from CM */
509
510 // check if we're already displaying the chat view for this cm
511 ContactMethod *current_cm = nullptr;
512 if (IS_CHAT_VIEW(old_view))
513 current_cm = chat_view_get_cm(CHAT_VIEW(old_view));
514
aviau039001d2016-09-29 16:39:05 -0400515 if (current_cm != cm)
516 change_view(win, old_view, cm, CHAT_VIEW_TYPE);
Nicolas Jagerd00761d2017-03-22 16:22:00 -0400517
518 } else if (contact_request) {
519 /* show the contact request page */
520 change_view(win, old_view, contact_request, CONTACT_REQUEST_CONTENT_VIEW_TYPE);
521
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400522 } else {
523 /* not a supported object type, display the welcome view */
aviau039001d2016-09-29 16:39:05 -0400524 if (old_view != priv->welcome_view)
525 change_view(win, old_view, nullptr, RING_WELCOME_VIEW_TYPE);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500526 }
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400527 } else {
528 /* selection isn't valid, display the welcome view */
aviau039001d2016-09-29 16:39:05 -0400529 if (old_view != priv->welcome_view)
530 change_view(win, old_view, nullptr, RING_WELCOME_VIEW_TYPE);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500531 }
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500532
Guillaume Roguez0a411202017-05-25 15:27:26 -0400533 /* we force to check the pending contact request status */
534 set_pending_contact_request_tab_icon(get_active_ring_account(), win);
535
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500536 return G_SOURCE_REMOVE;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500537}
538
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500539static void
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400540activate_contact_method(RingMainWindow *self, URI uri, Account *account)
aviau8c0b2422016-10-31 16:06:44 -0400541{
542 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
543
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400544 auto cm = PhoneDirectoryModel::instance().getNumber(uri, account);
aviau8c0b2422016-10-31 16:06:44 -0400545
546 if (g_settings_get_boolean(priv->settings, "search-entry-places-call")) {
547 place_new_call(cm);
548
549 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
550 gtk_widget_child_focus(GTK_WIDGET(self), GTK_DIR_TAB_FORWARD);
551 } else {
552 // if its a new CM, bring it to the top
553 if (cm->lastUsed() == 0)
554 cm->setLastUsed(QDateTime::currentDateTime().toTime_t());
555
556 // select cm
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400557 RecentModel::instance().selectionModel()->setCurrentIndex(
558 RecentModel::instance().getIndex(cm),
559 QItemSelectionModel::ClearAndSelect
560 );
aviau8c0b2422016-10-31 16:06:44 -0400561 }
562 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
563}
564
565static void
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400566lookup_username(RingMainWindow *self, URI uri, Account *account)
567{
568 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
569
570 gtk_spinner_start(GTK_SPINNER(priv->spinner_lookup));
571 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
572
573 // we need to strip the Scheme section, or else some will contain "sip:"
574 QString querry = uri.format(URI::Section::USER_INFO|URI::Section::HOSTNAME|URI::Section::PORT);
575
576 /* disconect previous lookup */
577 QObject::disconnect(priv->username_lookup);
578
579 /* connect new lookup */
580 priv->username_lookup = QObject::connect(
581 &NameDirectory::instance(),
582 &NameDirectory::registeredNameFound,
583 [self, priv, querry] (Account* account, NameDirectory::LookupStatus status, const QString& address, const QString& name) {
584
585 g_debug("lookup results for %s is %s",
586 querry.toUtf8().constData(),
587 name.toUtf8().constData()
588 );
589
590 if ( name.compare(querry, Qt::CaseInsensitive) != 0 ) {
591 g_debug("not from the same lookup match");
592 return; // doesn't match
593 }
594
595 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
596
597 switch(status)
598 {
599 case NameDirectory::LookupStatus::SUCCESS:
600 {
601 URI result = URI("ring:" + address);
602 activate_contact_method(self, result, account);
603 break;
604 }
605 case NameDirectory::LookupStatus::INVALID_NAME:
606 {
607 //Can't be a ring username, could be something else.
608 auto dialog = gtk_message_dialog_new(
609 GTK_WINDOW(self),
610 GTK_DIALOG_DESTROY_WITH_PARENT,
611 GTK_MESSAGE_ERROR,
612 GTK_BUTTONS_CLOSE,
613 _("Invalid Ring username: %s"),
614 name.toUtf8().constData()
615 );
616
617 gtk_dialog_run (GTK_DIALOG (dialog));
618 gtk_widget_destroy (dialog);
619 break;
620 }
621 case NameDirectory::LookupStatus::ERROR:
622 {
623 auto dialog = gtk_message_dialog_new(
624 GTK_WINDOW(self),
625 GTK_DIALOG_DESTROY_WITH_PARENT,
626 GTK_MESSAGE_ERROR,
627 GTK_BUTTONS_CLOSE,
628 _("Error resolving username, nameserver is possibly unreachable")
629 );
630
631 gtk_dialog_run(GTK_DIALOG (dialog));
632 gtk_widget_destroy(dialog);
633 break;
634 }
635 case NameDirectory::LookupStatus::NOT_FOUND:
636 {
637 auto dialog = gtk_message_dialog_new(
638 GTK_WINDOW(self),
639 GTK_DIALOG_DESTROY_WITH_PARENT,
640 GTK_MESSAGE_ERROR,
641 GTK_BUTTONS_CLOSE,
642 _("Could not resolve Ring username: %s"),
643 name.toUtf8().constData()
644 );
645
646 gtk_dialog_run(GTK_DIALOG (dialog));
647 gtk_widget_destroy(dialog);
648 break;
649 }
650 }
651 gtk_spinner_stop(GTK_SPINNER(priv->spinner_lookup));
652 }
653 );
654
655 /* lookup */
656 NameDirectory::instance().lookupName(account, "", querry);
657}
658
659/**
660 * This is called when someone activates the search entry (clicks enter or on the button).
661 * At this point the list is already filtered but on clicking enter we want to either contact a new
662 * number or perform a search on the name service. Here we must decide which one the user wants.
663 * We'll use the following rules to decide:
664 * * if a user explicitly specifies the scheme ("sip:" or "ring:") then we always use a
665 * corresponding account type (use currentDefaultAccount() with the scheme type)
666 * * if a user doesn't specify the scheme, then we decide based on the user selected account; if
667 * SIP account then we assume its a SIP URI; if RING account then we check if its a ringID,
668 * otherwise we perform a name lookup.
669 *
670 * Additionally, we need to make sure that the CM found/created either has the same account set as
671 * selected by the user, no account set, or else we need to switch the user selected account to the
672 * account of the CM (eg: in the case we're switching protocols), or else the item will not be
673 * visible in the Converstations list to the user. This is accomplished via the use of
674 * currentDefaultAccount().
675 */
676static void
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -0400677search_entry_activated(RingMainWindow *self)
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500678{
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -0400679 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500680
Nicolas Jager55903142017-02-01 14:43:45 -0500681 const auto *querry = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500682
Nicolas Jager55903142017-02-01 14:43:45 -0500683 URI uri = URI(querry);
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400684
Stepan Salenikovich03c80d02017-05-08 08:47:56 -0400685 // nothing to do if querry is empty or just whitespace
686 if (uri.isEmpty()) return;
687
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400688 /* get account to use */
689 Account *account = nullptr;
690 if (uri.schemeType() != URI::SchemeType::NONE) {
691 /* explicit SIP, SIPS, or RING; make sure account is of right type */
692 account = AvailableAccountModel::instance().currentDefaultAccount(uri.schemeType());
693 if (!account) {
694 const gchar *type = uri.schemeType() == URI::SchemeType::RING ? "RING" : "SIP";
695 g_warning("entered %s uri, but no active %s accounts", type, type);
696 }
697 } else {
698 /* just take the user selected account */
699 account = AvailableAccountModel::instance().currentDefaultAccount();
700 }
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400701
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400702 if (!account) {
703 g_critical("could not get an account for uri: %s", uri.full().toUtf8().constData());
704 return;
705 }
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400706
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400707 /* if RING and not RingID, perform lookup */
708 if (account->protocol() == Account::Protocol::RING &&
709 uri.protocolHint() != URI::ProtocolHint::RING)
710 {
711 lookup_username(self, uri, account);
712 } else {
713 /* no lookup, simply use the URI as is */
714 activate_contact_method(self, uri, account);
715 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500716}
717
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400718static gboolean
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400719save_accounts(GtkWidget *working_dialog)
720{
721 /* save changes to accounts */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400722 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400723
724 if (working_dialog)
725 gtk_widget_destroy(working_dialog);
726
727 return G_SOURCE_REMOVE;
728}
729
Stepan Salenikovich15142182015-03-11 17:15:26 -0400730static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400731settings_clicked(RingMainWindow *win)
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400732{
733 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
734 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
735
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400736 /* check which view to show */
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500737 if (!priv->show_settings) {
738 /* show the settings */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400739 priv->show_settings = TRUE;
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500740
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500741 /* 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 -0400742 selection_changed(win);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500743
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400744 /* show settings */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400745 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 -0400746
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400747 gtk_widget_show(priv->hbox_settings);
748
Stepan Salenikovich41118912015-05-01 11:25:46 -0400749 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400750 if (priv->last_settings_view == priv->media_settings_view)
751 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400752
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400753 /* make sure to show the profile if we're showing the general settings */
754 if (priv->last_settings_view == priv->general_settings_view)
755 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), TRUE);
756
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400757 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400758 } else {
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500759 /* hide the settings */
760 priv->show_settings = FALSE;
761
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400762 /* show working dialog in case save operation takes time */
763 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
764 gtk_window_present(GTK_WINDOW(working));
765
766 /* now save after the time it takes to transition back to the call view (400ms)
767 * the save doesn't happen before the "working" dialog is presented
768 * the timeout function should destroy the "working" dialog when done saving
769 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400770 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400771
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400772 /* show calls */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400773 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 -0400774
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400775 gtk_widget_hide(priv->hbox_settings);
776
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500777 /* make sure video preview is stopped, in case it was started */
778 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400779 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), FALSE);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500780
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400781 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400782
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500783 /* show the view which was selected previously */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400784 selection_changed(win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400785 }
786}
787
788static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400789show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400790{
791 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
792 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
793
794 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400795 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400796 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400797 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400798 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400799 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400800 }
801}
802
803static void
804show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
805{
806 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
807 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
808
809 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400810 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
811 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400812 }
813}
814
Stepan Salenikovichde896112015-05-11 16:46:33 -0400815static void
816show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
817{
818 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
819 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
820
821 if (gtk_toggle_button_get_active(navbutton)) {
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400822 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), TRUE);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400823 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
824 priv->last_settings_view = priv->general_settings_view;
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400825 } else {
826 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), FALSE);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400827 }
828}
829
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400830static void
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400831show_combobox_account_selector(RingMainWindow *self, gboolean show)
832{
833 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
834 /* we only want to show the account selector when there is more than 1 eneabled account; so
835 * every time we want to show it, we should preform this check */
836 gtk_widget_set_visible(priv->combobox_account_selector,
837 show && AvailableAccountModel::instance().rowCount() > 1);
838}
839
840static void
aviau6aeb4852016-08-18 16:01:09 -0400841on_account_creation_completed(RingMainWindow *win)
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400842{
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500843 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400844 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
845
aviau6aeb4852016-08-18 16:01:09 -0400846 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
847
848 /* destroy the wizard */
aviau69081842016-10-14 14:51:54 -0400849 if (priv->account_creation_wizard)
aviau6aeb4852016-08-18 16:01:09 -0400850 {
aviau69081842016-10-14 14:51:54 -0400851 gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), priv->account_creation_wizard);
852 gtk_widget_destroy(priv->account_creation_wizard);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400853 }
aviau6aeb4852016-08-18 16:01:09 -0400854
855 /* show the settings button*/
856 gtk_widget_show(priv->ring_settings);
Nicolas Jager15a8b902017-03-21 07:53:06 -0400857
858 /* show the account selector */
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400859 show_combobox_account_selector(win, TRUE);
Nicolas Jager15a8b902017-03-21 07:53:06 -0400860
Stepan Salenikovich509670b2017-04-24 15:18:06 -0400861 /* select the newly created account */
862 // TODO: the new account might not always be the first one; eg: if the user has an existing
863 // SIP account
Nicolas Jager15a8b902017-03-21 07:53:06 -0400864 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), 0);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400865}
866
867static void
aviau6aeb4852016-08-18 16:01:09 -0400868show_account_creation_wizard(RingMainWindow *win)
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400869{
870 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
871
aviau61d05b32016-10-18 16:47:53 -0400872 if (!priv->account_creation_wizard)
aviau6aeb4852016-08-18 16:01:09 -0400873 {
aviau69081842016-10-14 14:51:54 -0400874 priv->account_creation_wizard = account_creation_wizard_new(false);
875 g_object_add_weak_pointer(G_OBJECT(priv->account_creation_wizard), (gpointer *)&priv->account_creation_wizard);
876 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 -0400877
aviau6aeb4852016-08-18 16:01:09 -0400878 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
aviau69081842016-10-14 14:51:54 -0400879 priv->account_creation_wizard,
aviau6aeb4852016-08-18 16:01:09 -0400880 ACCOUNT_CREATION_WIZARD_VIEW_NAME);
881 }
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400882
Stepan Salenikovich64505672015-09-24 10:46:07 -0400883 /* hide settings button until account creation is complete */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400884 gtk_widget_hide(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400885 show_combobox_account_selector(win, FALSE);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400886
aviau69081842016-10-14 14:51:54 -0400887 gtk_widget_show(priv->account_creation_wizard);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400888
aviau69081842016-10-14 14:51:54 -0400889 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->account_creation_wizard);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400890}
891
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400892static void
Stepan Salenikovicha06ce192016-11-01 14:26:43 -0400893search_entry_text_changed(GtkSearchEntry *search_entry, RingMainWindow *self)
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400894{
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -0400895 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
896
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400897 /* get the text from the entry */
898 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
899
Stepan Salenikovichf53128f2016-10-07 10:32:16 -0400900 RecentModel::instance().peopleProxy()->setFilterRegExp(QRegExp(text, Qt::CaseInsensitive, QRegExp::FixedString));
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -0400901 contacts_view_set_filter_string(CONTACTS_VIEW(priv->treeview_contacts), text);
902 history_view_set_filter_string(HISTORY_VIEW(priv->treeview_history), text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400903}
904
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400905static gboolean
aviau4c87b832016-12-13 14:18:53 -0500906search_entry_key_released(G_GNUC_UNUSED GtkEntry *search_entry, GdkEventKey *key, RingMainWindow *self)
Stepan Salenikovichcac35042016-10-28 12:07:38 -0400907{
908 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
909
910 // if esc key pressed, clear the regex (keep the text, the user might not want to actually delete it)
911 if (key->keyval == GDK_KEY_Escape) {
912 RecentModel::instance().peopleProxy()->setFilterRegExp(QRegExp());
913 contacts_view_set_filter_string(CONTACTS_VIEW(priv->treeview_contacts), "");
914 history_view_set_filter_string(HISTORY_VIEW(priv->treeview_history), "");
915 return GDK_EVENT_STOP;
916 }
917
918 return GDK_EVENT_PROPAGATE;
919}
920
921static gboolean
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400922dtmf_pressed(RingMainWindow *win,
923 GdkEventKey *event,
924 G_GNUC_UNUSED gpointer user_data)
925{
926 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
927
928 /* we want to react to digit key presses, as long as a GtkEntry is not the
929 * input focus
930 */
931 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
932 if (GTK_IS_ENTRY(focus))
933 return GDK_EVENT_PROPAGATE;
934
935 /* make sure that a call is selected*/
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400936 QItemSelectionModel *selection = CallModel::instance().selectionModel();
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400937 QModelIndex idx = selection->currentIndex();
938 if (!idx.isValid())
939 return GDK_EVENT_PROPAGATE;
940
941 /* make sure that the selected call is in progress */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400942 Call *call = CallModel::instance().getCall(idx);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400943 Call::LifeCycleState state = call->lifeCycleState();
944 if (state != Call::LifeCycleState::PROGRESS)
945 return GDK_EVENT_PROPAGATE;
946
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400947 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
948 * does not result in a 'c' being played.
949 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
950 if ( event->state
951 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
952 return GDK_EVENT_PROPAGATE;
953
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400954 /* pass the character that was entered to be played by the daemon;
955 * the daemon will filter out invalid DTMF characters */
956 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
957 QString val = QString::fromUcs4(&unicode_val, 1);
958 call->playDTMF(val);
959 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400960
961 /* always propogate the key, so we don't steal accelerators/shortcuts */
962 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400963}
964
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400965/**
966 * This function determines if two different contact list views (Conversations, Contacts, History)
967 * have the same item selected. Note that the same item does not necessarily mean the same object.
968 * For example, if the history view has a Call selected and the Conversations view has a Person
969 * selected which is associated with the ContactMethod to which that Call was made, then this will
970 * evaluate to TRUE.
971 */
Guillaume Roguez6d4734e2017-05-26 16:44:45 -0400972static bool
973compare_treeview_selection(GtkTreeSelection* selection1, GtkTreeSelection* selection2)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400974{
Guillaume Roguez6d4734e2017-05-26 16:44:45 -0400975 g_return_val_if_fail(selection1 && selection2, false);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400976
Guillaume Roguez6d4734e2017-05-26 16:44:45 -0400977 if (selection1 == selection2) return true;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400978
979 auto idx1 = get_index_from_selection(selection1);
980 auto type1 = idx1.data(static_cast<int>(Ring::Role::ObjectType));
981 auto object1 = idx1.data(static_cast<int>(Ring::Role::Object));
982
983 auto idx2 = get_index_from_selection(selection2);
984 auto type2 = idx2.data(static_cast<int>(Ring::Role::ObjectType));
985 auto object2 = idx2.data(static_cast<int>(Ring::Role::Object));
986
Guillaume Roguez6d4734e2017-05-26 16:44:45 -0400987 if (idx1.isValid() && type1.isValid() && object1.isValid() &&
988 idx2.isValid() && type2.isValid() && object2.isValid()) {
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400989 Call *call1 = nullptr;
990 ContactMethod *cm1 = nullptr;
991 Person *person1 = nullptr;
992
993 Call *call2 = nullptr;
994 ContactMethod *cm2 = nullptr;
995 Person *person2 = nullptr;
996
997 switch(type1.value<Ring::ObjectType>()) {
998 case Ring::ObjectType::Person:
999 person1 = object1.value<Person *>();
1000 break;
1001 case Ring::ObjectType::ContactMethod:
1002 cm1 = object1.value<ContactMethod *>();
1003 person1 = cm1->contact();
1004 break;
1005 case Ring::ObjectType::Call:
1006 call1 = object1.value<Call *>();
1007 cm1 = call1->peerContactMethod();
1008 person1 = cm1->contact();
1009 break;
1010 case Ring::ObjectType::Media:
1011 case Ring::ObjectType::Certificate:
Nicolas Jagerc74f7612017-03-28 09:48:18 -04001012 case Ring::ObjectType::ContactRequest:
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001013 case Ring::ObjectType::COUNT__:
1014 // nothing to do
1015 break;
1016 }
1017
1018 switch(type2.value<Ring::ObjectType>()) {
1019 case Ring::ObjectType::Person:
1020 person2 = object2.value<Person *>();
1021 break;
1022 case Ring::ObjectType::ContactMethod:
1023 cm2 = object2.value<ContactMethod *>();
1024 person2 = cm2->contact();
1025 break;
1026 case Ring::ObjectType::Call:
1027 call2 = object2.value<Call *>();
1028 cm2 = call2->peerContactMethod();
1029 person2 = cm2->contact();
1030 break;
1031 case Ring::ObjectType::Media:
1032 case Ring::ObjectType::Certificate:
Nicolas Jagerc74f7612017-03-28 09:48:18 -04001033 case Ring::ObjectType::ContactRequest:
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001034 case Ring::ObjectType::COUNT__:
1035 // nothing to do
1036 break;
1037 }
1038
1039 if (person1 != nullptr && person1 == person2)
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001040 return true;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001041 if (cm1 != nullptr && cm1 == cm2)
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001042 return true;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001043 if (call1 != nullptr && call1 == call2)
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001044 return true;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001045 }
1046
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001047 return false;
1048}
1049
1050inline static void
1051unselect_if_different(GtkTreeSelection *selection1, GtkTreeView* treeview)
1052{
1053 auto selection2 = gtk_tree_view_get_selection(treeview);
1054 if (!compare_treeview_selection(selection1, selection2))
1055 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection2));
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001056}
1057
1058static void
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001059sync_all_view_selection(GtkTreeSelection* selection,
1060 GtkTreeView* treeview1,
1061 GtkTreeView* treeview2,
1062 GtkTreeView* treeview3)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001063{
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001064 GtkTreeIter iter;
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001065 if (gtk_tree_selection_get_selected(selection, nullptr, &iter)) {
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001066 /* something is selected in one view, clear the selection in the other views;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001067 * unless the current selection is of the same item; we don't try to match the selection in
1068 * all views since not all items exist in all 3 contact list views
1069 */
1070
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001071 unselect_if_different(selection, treeview1);
1072 unselect_if_different(selection, treeview2);
1073 unselect_if_different(selection, treeview3);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001074 }
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001075}
1076
1077static void
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001078conversation_selection_changed(GtkTreeSelection* selection, RingMainWindow* self)
Nicolas Jager0efc8432017-03-22 16:22:00 -04001079{
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001080 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1081
Nicolas Jager0efc8432017-03-22 16:22:00 -04001082 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001083 sync_all_view_selection(selection,
1084 GTK_TREE_VIEW(priv->treeview_contacts),
1085 GTK_TREE_VIEW(priv->treeview_history),
1086 GTK_TREE_VIEW(priv->treeview_contact_requests));
Nicolas Jager0efc8432017-03-22 16:22:00 -04001087
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001088 selection_changed(self);
1089}
Nicolas Jager0efc8432017-03-22 16:22:00 -04001090
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001091static void
1092contact_selection_changed(GtkTreeSelection* selection, RingMainWindow* self)
1093{
1094 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
Nicolas Jagerffc47112017-05-26 14:05:54 -04001095
Guillaume Roguez6d4734e2017-05-26 16:44:45 -04001096 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1097 sync_all_view_selection(selection,
1098 GTK_TREE_VIEW(priv->treeview_conversations),
1099 GTK_TREE_VIEW(priv->treeview_history),
1100 GTK_TREE_VIEW(priv->treeview_contact_requests));
1101
1102 selection_changed(self);
1103}
1104
1105static void
1106history_selection_changed(GtkTreeSelection* selection, RingMainWindow* self)
1107{
1108 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1109
1110 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1111 sync_all_view_selection(selection,
1112 GTK_TREE_VIEW(priv->treeview_contacts),
1113 GTK_TREE_VIEW(priv->treeview_conversations),
1114 GTK_TREE_VIEW(priv->treeview_contact_requests));
1115
1116 selection_changed(self);
1117}
1118
1119static void
1120contact_request_selection_changed(GtkTreeSelection* selection, RingMainWindow* self)
1121{
1122 if (gtk_q_tree_model_ignore_selection_change(selection)) return;
1123
1124 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1125 sync_all_view_selection(selection,
1126 GTK_TREE_VIEW(priv->treeview_contacts),
1127 GTK_TREE_VIEW(priv->treeview_history),
1128 GTK_TREE_VIEW(priv->treeview_conversations));
Nicolas Jager0efc8432017-03-22 16:22:00 -04001129
1130 selection_changed(self);
1131}
1132
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001133static gboolean
1134window_size_changed(GtkWidget *win, GdkEventConfigure *event, gpointer)
1135{
1136 auto *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
1137
1138 g_settings_set_int(priv->settings, "window-width", event->width);
1139 g_settings_set_int(priv->settings, "window-height", event->height);
1140
1141 return GDK_EVENT_PROPAGATE;
1142}
1143
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001144static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001145search_entry_places_call_changed(GSettings *settings, const gchar *key, RingMainWindow *self)
1146{
1147 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1148
1149 if (g_settings_get_boolean(settings, key)) {
1150 gtk_widget_set_tooltip_text(priv->button_new_conversation, C_("button next to search entry will place a new call", "place call"));
1151 } else {
1152 gtk_widget_set_tooltip_text(priv->button_new_conversation, C_("button next to search entry will open chat", "open chat"));
1153 }
1154}
1155
1156static void
aviau6aeb4852016-08-18 16:01:09 -04001157handle_account_migrations(RingMainWindow *win)
1158{
1159 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
1160
1161 /* If there is an existing migration view, remove it */
aviau69081842016-10-14 14:51:54 -04001162 if (priv->account_migration_view)
aviau6aeb4852016-08-18 16:01:09 -04001163 {
Stepan Salenikovich56530b12016-10-20 17:51:34 -04001164 gtk_widget_destroy(priv->account_migration_view);
1165 priv->account_migration_view = nullptr;
aviau6aeb4852016-08-18 16:01:09 -04001166 }
1167
1168 QList<Account*> accounts = AccountModel::instance().accountsToMigrate();
1169 if (!accounts.isEmpty())
1170 {
1171 Account* account = accounts.first();
aviau6aeb4852016-08-18 16:01:09 -04001172
aviau69081842016-10-14 14:51:54 -04001173 priv->account_migration_view = account_migration_view_new(account);
aviau69081842016-10-14 14:51:54 -04001174 g_signal_connect_swapped(priv->account_migration_view, "account-migration-completed", G_CALLBACK(handle_account_migrations), win);
1175 g_signal_connect_swapped(priv->account_migration_view, "account-migration-failed", G_CALLBACK(handle_account_migrations), win);
aviau6aeb4852016-08-18 16:01:09 -04001176
1177 gtk_widget_hide(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001178 show_combobox_account_selector(win, FALSE);
aviau69081842016-10-14 14:51:54 -04001179 gtk_widget_show(priv->account_migration_view);
aviau6aeb4852016-08-18 16:01:09 -04001180 gtk_stack_add_named(
1181 GTK_STACK(priv->stack_main_view),
aviau69081842016-10-14 14:51:54 -04001182 priv->account_migration_view,
aviau6aeb4852016-08-18 16:01:09 -04001183 ACCOUNT_MIGRATION_VIEW_NAME
1184 );
1185 gtk_stack_set_visible_child_name(
1186 GTK_STACK(priv->stack_main_view),
1187 ACCOUNT_MIGRATION_VIEW_NAME
1188 );
1189 }
1190 else
1191 {
1192 gtk_widget_show(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001193 show_combobox_account_selector(win, TRUE);
aviau6aeb4852016-08-18 16:01:09 -04001194 }
1195}
1196
Nicolas Jager5e2be732017-03-18 21:35:50 -04001197/**
1198 * set the column value by printing the alias and the state of an account in combobox_account_selector.
1199 */
1200static void
1201print_account_and_state(GtkCellLayout* cell_layout,
1202 GtkCellRenderer* cell_renderer,
1203 GtkTreeModel* tree_model,
1204 GtkTreeIter* iter,
1205 gpointer* data)
1206{
1207 (void) cell_layout; // UNUSED
1208 (void) data; // UNUSED
1209
1210 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(tree_model), iter);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001211 gchar* display_alias = nullptr;
1212
1213 if (idx.isValid()) {
Nicolas Jagera169c712017-04-14 12:02:50 -04001214 auto state = idx.data(static_cast<int>(Account::Role::RegistrationState));
1215 auto name = idx.data(static_cast<int>(Account::Role::Alias));
1216 auto account_alias = name.value<QString>().toStdString();
Nicolas Jager5e2be732017-03-18 21:35:50 -04001217
Nicolas Jagera169c712017-04-14 12:02:50 -04001218 switch (state.value<Account::RegistrationState>()) {
Nicolas Jager5e2be732017-03-18 21:35:50 -04001219 case Account::RegistrationState::READY:
1220 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001221 display_alias = g_strdup_printf("%s",account_alias.c_str());
Nicolas Jager5e2be732017-03-18 21:35:50 -04001222 }
1223 break;
1224 case Account::RegistrationState::UNREGISTERED:
1225 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001226 display_alias = g_strdup_printf("<span fgcolor=\"gray\">%s</span>", account_alias.c_str());
Nicolas Jager5e2be732017-03-18 21:35:50 -04001227 }
1228 break;
1229 case Account::RegistrationState::TRYING:
1230 case Account::RegistrationState::INITIALIZING:
1231 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001232 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 -04001233 display_alias = g_strdup_printf("<span fgcolor=\"gray\" font_style=\"italic\">%s</span>", account_alias_i18n);
1234 }
1235 break;
1236 case Account::RegistrationState::ERROR:
1237 {
Nicolas Jagera169c712017-04-14 12:02:50 -04001238 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 -04001239 display_alias = g_strdup_printf("<span fgcolor=\"red\">%s</span>", error_string);
1240 }
1241 case Account::RegistrationState::COUNT__:
1242 break;
1243 }
1244 }
1245
1246 g_object_set(G_OBJECT(cell_renderer), "markup", display_alias, nullptr);
1247 g_free(display_alias);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001248
1249}
1250
Nicolas Jager15a8b902017-03-21 07:53:06 -04001251static void
Guillaume Roguez0a411202017-05-25 15:27:26 -04001252current_account_changed(RingMainWindow* self, Account* account)
1253{
1254 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1255
Guillaume Roguez0a411202017-05-25 15:27:26 -04001256 /* disconnect previous PendingContactRequestModel */
1257 QObject::disconnect(priv->account_request_added);
1258 QObject::disconnect(priv->account_request_accepted);
1259 QObject::disconnect(priv->account_request_discarded);
1260
Nicolas Jagercd54b992017-05-29 11:02:32 -04001261 set_pending_contact_request_tab_icon(account, self);
1262
Nicolas Jager2bc3fd92017-05-29 12:48:07 -04001263 bool is_ring = account && account->protocol() == Account::Protocol::RING;
1264 if (not is_ring)
1265 return;
1266
Guillaume Roguez0a411202017-05-25 15:27:26 -04001267 auto model = account->pendingContactRequestModel();
1268 auto action = [self, account](ContactRequest* r) {
1269 (void) r; // UNUSED
1270 set_pending_contact_request_tab_icon(account, self);
1271 };
1272
1273 /* connect current PendingContactRequestModel */
1274 priv->account_request_added = QObject::connect(model, &PendingContactRequestModel::requestAdded, action);
1275 priv->account_request_accepted = QObject::connect(model, &PendingContactRequestModel::requestAccepted, action);
1276 priv->account_request_discarded = QObject::connect(model, &PendingContactRequestModel::requestDiscarded, action);
1277}
1278
1279static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001280ring_main_window_init(RingMainWindow *win)
1281{
1282 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
1283 gtk_widget_init_template(GTK_WIDGET(win));
1284
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001285 /* bind to window size settings */
1286 priv->settings = g_settings_new_full(get_ring_schema(), nullptr, nullptr);
1287 auto width = g_settings_get_int(priv->settings, "window-width");
1288 auto height = g_settings_get_int(priv->settings, "window-height");
1289 gtk_window_set_default_size(GTK_WINDOW(win), width, height);
1290 g_signal_connect(win, "configure-event", G_CALLBACK(window_size_changed), nullptr);
1291
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001292 /* search-entry-places-call setting */
1293 search_entry_places_call_changed(priv->settings, "search-entry-places-call", win);
1294 g_signal_connect(priv->settings, "changed::search-entry-places-call", G_CALLBACK(search_entry_places_call_changed), win);
1295
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001296 /* set window icon */
1297 GError *error = NULL;
1298 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
1299 if (icon == NULL) {
1300 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -04001301 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001302 } else
1303 gtk_window_set_icon(GTK_WINDOW(win), icon);
1304
1305 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001306 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
Stepan Salenikovichd61974b2016-03-18 16:36:27 -04001307 -1, 16, TRUE, &error);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001308 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001309 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -04001310 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001311 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001312 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001313
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001314 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001315 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
1316 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001317 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001318 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001319
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001320 /* settings icon */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -04001321 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 -04001322
1323 /* connect settings button signal */
Stepan Salenikovichce1d0132017-04-26 14:14:37 -04001324 g_signal_connect_swapped(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001325
1326 /* add the call view to the main stack */
1327 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
1328 priv->vbox_call_view,
1329 CALL_VIEW_NAME);
1330
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001331 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001332 priv->account_settings_view = account_view_new();
1333 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
1334
Stepan Salenikovich0bd53492015-05-11 14:28:52 -04001335 priv->media_settings_view = media_settings_view_new();
1336 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 -04001337
Stepan Salenikovichde896112015-05-11 16:46:33 -04001338 priv->general_settings_view = general_settings_view_new();
1339 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
1340
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001341 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -04001342 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
1343 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001344
1345 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001346 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001347 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -04001348 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001349
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001350 /* populate the notebook */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001351 priv->treeview_conversations = recent_contacts_view_new();
1352 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), priv->treeview_conversations);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -04001353
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001354 priv->treeview_contacts = contacts_view_new();
1355 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contacts), priv->treeview_contacts);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001356
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001357 priv->treeview_history = history_view_new();
1358 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_history), priv->treeview_history);
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001359
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001360 auto available_accounts_changed = [win, priv] {
1361 /* if we're hiding the settings it means we're in the migration or wizard view and we don't
1362 * want to show the combo box */
1363 if (gtk_widget_get_visible(priv->ring_settings))
1364 show_combobox_account_selector(win, TRUE);
1365 };
Guillaume Roguez0a411202017-05-25 15:27:26 -04001366
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001367 QObject::connect(&AvailableAccountModel::instance(), &QAbstractItemModel::rowsRemoved, available_accounts_changed);
1368 QObject::connect(&AvailableAccountModel::instance(), &QAbstractItemModel::rowsInserted, available_accounts_changed);
Guillaume Roguez0a411202017-05-25 15:27:26 -04001369 QObject::connect(AvailableAccountModel::instance().selectionModel(), &QItemSelectionModel::currentChanged,
1370 [win] (const QModelIndex& current, const QModelIndex& previous) {
1371 auto account = current.data(static_cast<int>(Account::Role::Object)).value<Account*>();
1372 current_account_changed(win, account);
1373 });
Nicolas Jagera169c712017-04-14 12:02:50 -04001374
Nicolas Jager0efc8432017-03-22 16:22:00 -04001375 priv->treeview_contact_requests = pending_contact_requests_view_new();
1376 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contact_requests), priv->treeview_contact_requests);
1377
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04001378 /* welcome/default view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001379 priv->welcome_view = ring_welcome_view_new();
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001380 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 -04001381 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
1382 gtk_widget_show(priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001383
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001384 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
1385 g_signal_connect(selection_conversations, "changed", G_CALLBACK(conversation_selection_changed), win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001386
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001387 auto selection_contacts = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contacts));
1388 g_signal_connect(selection_contacts, "changed", G_CALLBACK(contact_selection_changed), win);
1389
1390 auto selection_history = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_history));
1391 g_signal_connect(selection_history, "changed", G_CALLBACK(history_selection_changed), win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001392
Nicolas Jager0efc8432017-03-22 16:22:00 -04001393 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
1394 g_signal_connect(selection_contact_request, "changed", G_CALLBACK(contact_request_selection_changed), win);
1395
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -04001396 g_signal_connect_swapped(priv->button_new_conversation, "clicked", G_CALLBACK(search_entry_activated), win);
1397 g_signal_connect_swapped(priv->search_entry, "activate", G_CALLBACK(search_entry_activated), win);
Stepan Salenikovicha06ce192016-11-01 14:26:43 -04001398 g_signal_connect(priv->search_entry, "search-changed", G_CALLBACK(search_entry_text_changed), win);
Stepan Salenikovichcac35042016-10-28 12:07:38 -04001399 g_signal_connect(priv->search_entry, "key-release-event", G_CALLBACK(search_entry_key_released), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001400
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001401 /* make sure the incoming call is the selected call in the CallModel */
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001402 QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001403 &CallModel::instance(),
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001404 &CallModel::incomingCall,
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -04001405 [priv](Call* call) {
Nicolas Jager85e992a2017-03-22 04:11:47 -04001406 // select the revelant account
Nicolas Jager15434f52017-04-18 10:30:27 -04001407 auto idx = call->account()->index(); // from AccountModel
1408 idx = AvailableAccountModel::instance().mapFromSource(idx); // from AvailableAccountModel
Nicolas Jagerf71f8d82017-04-17 10:22:06 -04001409
Nicolas Jager15434f52017-04-18 10:30:27 -04001410 AvailableAccountModel::instance().selectionModel()->setCurrentIndex(idx,
1411 QItemSelectionModel::ClearAndSelect);
Nicolas Jager85e992a2017-03-22 04:11:47 -04001412
Stepan Salenikovichf53128f2016-10-07 10:32:16 -04001413 // clear the regex to make sure the call is shown
1414 RecentModel::instance().peopleProxy()->setFilterRegExp(QRegExp());
Stepan Salenikovichdcfb5032016-10-26 18:57:56 -04001415 contacts_view_set_filter_string(CONTACTS_VIEW(priv->treeview_contacts),"");
1416 history_view_set_filter_string(HISTORY_VIEW(priv->treeview_history), "");
Stepan Salenikovichf53128f2016-10-07 10:32:16 -04001417
1418 // now make sure its selected
1419 RecentModel::instance().selectionModel()->clearCurrentIndex();
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001420 CallModel::instance().selectCall(call);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001421 }
1422 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001423
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001424 /* react to digit key press events */
1425 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich48b45c12015-09-25 15:49:51 -04001426
1427 /* set the search entry placeholder text */
1428 gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
1429 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 -04001430
aviau039001d2016-09-29 16:39:05 -04001431 /* init chat webkit container so that it starts loading before the first time we need it*/
1432 get_webkit_chat_container(win);
1433
Stepan Salenikovichcbc8dcc2017-04-05 11:27:10 -04001434 if (has_ring_account()) {
1435 /* user has ring account, so show the call view right away */
1436 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
1437
1438 handle_account_migrations(win);
1439 } else {
1440 /* user has to create the ring account */
1441 show_account_creation_wizard(win);
1442 }
Nicolas Jager15a8b902017-03-21 07:53:06 -04001443
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001444 /* setup account selector */
1445 gtk_combo_box_set_qmodel(
1446 GTK_COMBO_BOX(priv->combobox_account_selector),
1447 &AvailableAccountModel::instance(),
1448 AvailableAccountModel::instance().selectionModel()
1449 );
Nicolas Jager15a8b902017-03-21 07:53:06 -04001450
Nicolas Jagera169c712017-04-14 12:02:50 -04001451 /* set visibility */
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001452 show_combobox_account_selector(win, TRUE);
Nicolas Jagera169c712017-04-14 12:02:50 -04001453
Nicolas Jager15a8b902017-03-21 07:53:06 -04001454 /* layout */
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001455 auto *renderer = gtk_cell_renderer_text_new();
Nicolas Jager15a8b902017-03-21 07:53:06 -04001456 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_account_selector), renderer, FALSE);
1457 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_account_selector), renderer, "text", 0, NULL);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001458 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(priv->combobox_account_selector),
1459 renderer,
1460 (GtkCellLayoutDataFunc)print_account_and_state,
1461 nullptr, nullptr);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001462
Stepan Salenikovichce1d0132017-04-26 14:14:37 -04001463 // we closing any view opened to avoid confusion (especially between SIP and Ring protocols).
1464 g_signal_connect_swapped(priv->combobox_account_selector, "changed", G_CALLBACK(hide_view_clicked), win);
Guillaume Roguez0a411202017-05-25 15:27:26 -04001465
1466 // initialize the pending contact request icon.
1467 current_account_changed(win, get_active_ring_account());
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001468}
1469
1470static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001471ring_main_window_dispose(GObject *object)
1472{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001473 RingMainWindow *self = RING_MAIN_WINDOW(object);
1474 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1475
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001476 QObject::disconnect(priv->selected_item_changed);
1477 QObject::disconnect(priv->selected_call_over);
aviau8c0b2422016-10-31 16:06:44 -04001478 QObject::disconnect(priv->username_lookup);
Guillaume Roguez0a411202017-05-25 15:27:26 -04001479 QObject::disconnect(priv->account_request_added);
1480 QObject::disconnect(priv->account_request_accepted);
1481 QObject::disconnect(priv->account_request_discarded);
1482 QObject::disconnect(priv->account_request_);
aviau039001d2016-09-29 16:39:05 -04001483
1484 g_clear_object(&priv->welcome_view);
1485 g_clear_object(&priv->webkit_chat_container);
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001486
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001487 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1488}
1489
1490static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001491ring_main_window_finalize(GObject *object)
1492{
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001493 RingMainWindow *self = RING_MAIN_WINDOW(object);
1494 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1495
1496 g_clear_object(&priv->settings);
1497
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001498 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001499}
1500
1501static void
1502ring_main_window_class_init(RingMainWindowClass *klass)
1503{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001504 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001505 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001506
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001507 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1508 "/cx/ring/RingGnome/ringmainwindow.ui");
1509
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001510 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001511 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_smartview);
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001512 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contacts);
1513 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_history);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001514 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1515 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1516 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1517 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001518 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001519 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001520 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001521 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001522 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, frame_call);
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001523 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_new_conversation );
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001524 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001525 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001526 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Nicolas Jager55903142017-02-01 14:43:45 -05001527 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_lookup);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001528 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_account_selector);
Nicolas Jager0efc8432017-03-22 16:22:00 -04001529 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contact_requests);
Guillaume Roguez0a411202017-05-25 15:27:26 -04001530 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_contact_requests_list);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001531}
1532
1533GtkWidget *
1534ring_main_window_new (GtkApplication *app)
1535{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001536 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1537
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001538 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001539}