blob: e9d8e2abfd9dd27e9f5379c3f68ca4a4a31f56aa [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
Guillaume Roguez2a6150d2017-07-19 18:24:47 -04002 * Copyright (C) 2015-2017 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// GTK+ related
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040024#include <glib/gi18n.h>
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040025
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040026// LRC
Sébastien Blin55bff9d2017-10-03 15:15:23 -040027#include <accountmodel.h> // Old lrc but still used
28#include <api/account.h>
29#include <api/contact.h>
30#include <api/profile.h>
31#include <api/contactmodel.h>
32#include <api/conversation.h>
33#include <api/conversationmodel.h>
34#include <api/lrc.h>
35#include <api/newaccountmodel.h>
36#include <api/newcallmodel.h>
37#include <api/behaviorcontroller.h>
38#include "accountcontainer.h"
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040039
40// Ring client
Stepan Salenikovich5fd97bc2016-08-30 09:40:38 -040041#include "accountview.h"
aviau6aeb4852016-08-18 16:01:09 -040042#include "accountmigrationview.h"
43#include "accountcreationwizard.h"
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050044#include "chatview.h"
Sébastien Blin55bff9d2017-10-03 15:15:23 -040045#include "conversationsview.h"
46#include "currentcallview.h"
47#include "dialogs.h"
48#include "generalsettingsview.h"
49#include "incomingcallview.h"
50#include "mediasettingsview.h"
51#include "models/gtkqtreemodel.h"
52#include "ringwelcomeview.h"
53#include "utils/accounts.h"
Stepan Salenikovichba87ae22016-09-13 14:27:20 -040054#include "utils/files.h"
Nicolas Jagerb413b302016-05-06 11:41:32 -040055
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040056static constexpr const char* CALL_VIEW_NAME = "calls";
aviau6aeb4852016-08-18 16:01:09 -040057static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
58static constexpr const char* ACCOUNT_MIGRATION_VIEW_NAME = "account-migration-view";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040059static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040060static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
61static constexpr const char* ACCOUNT_SETTINGS_VIEW_NAME = "accounts";
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050062
63struct _RingMainWindow
64{
65 GtkApplicationWindow parent;
66};
67
68struct _RingMainWindowClass
69{
70 GtkApplicationWindowClass parent_class;
71};
72
73typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
74
75struct _RingMainWindowPrivate
76{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040077 GtkWidget *ring_menu;
78 GtkWidget *image_ring;
79 GtkWidget *ring_settings;
80 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040081 GtkWidget *hbox_settings;
Sébastien Blin55bff9d2017-10-03 15:15:23 -040082 GtkWidget *notebook_contacts;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040083 GtkWidget *scrolled_window_smartview;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -040084 GtkWidget *treeview_conversations;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -040085 GtkWidget *vbox_left_pane;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050086 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -050087 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040088 GtkWidget *vbox_call_view;
Stepan Salenikovich3034d922015-10-09 10:11:42 -040089 GtkWidget *frame_call;
90 GtkWidget *welcome_view;
Sébastien Blin55bff9d2017-10-03 15:15:23 -040091 GtkWidget *button_new_conversation;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040092 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -040093 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -040094 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040095 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040096 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -040097 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040098 GtkWidget *radiobutton_account_settings;
aviau69081842016-10-14 14:51:54 -040099 GtkWidget *account_creation_wizard;
100 GtkWidget *account_migration_view;
Nicolas Jager15a8b902017-03-21 07:53:06 -0400101 GtkWidget *combobox_account_selector;
Nicolas Jager0efc8432017-03-22 16:22:00 -0400102 GtkWidget *treeview_contact_requests;
103 GtkWidget *scrolled_window_contact_requests;
Guillaume Roguez0a411202017-05-25 15:27:26 -0400104 GtkWidget *image_contact_requests_list;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400105
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400106 // The webkit_chat_container is created once, then reused for all chat views
aviau039001d2016-09-29 16:39:05 -0400107 GtkWidget *webkit_chat_container;
108
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400109 QMetaObject::Connection selected_item_changed;
110 QMetaObject::Connection selected_call_over;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400111
112 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400113
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500114 /* fullscreen */
115 gboolean is_fullscreen;
Stepan Salenikovichba87ae22016-09-13 14:27:20 -0400116
117 GSettings *settings;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400118
119 // Lrc containers and signals
120 std::unique_ptr<lrc::api::Lrc> lrc_;
121 AccountContainer* accountContainer_;
122 lrc::api::conversation::Info* chatViewConversation_;
123 lrc::api::profile::Type currentTypeFilter_;
124
125 QMetaObject::Connection showChatViewConnection_;
126 QMetaObject::Connection showCallViewConnection_;
127 QMetaObject::Connection showIncomingViewConnection_;
128 QMetaObject::Connection changeAccountConnection_;
129 QMetaObject::Connection newAccountConnection_;
130 QMetaObject::Connection rmAccountConnection_;
131 QMetaObject::Connection historyClearedConnection_;
132 QMetaObject::Connection modelSortedConnection_;
133 QMetaObject::Connection filterChangedConnection_;
134 QMetaObject::Connection newConversationConnection_;
135 QMetaObject::Connection conversationRemovedConnection_;
136 QMetaObject::Connection accountStatusChangedConnection_;
137
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500138};
139
140G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
141
142#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
143
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400144static void
145change_view(RingMainWindow *self, GtkWidget* old, lrc::api::conversation::Info conversation, GType type);
146static void
147ring_init_lrc(RingMainWindow *win, const std::string& accountId);
aviau039001d2016-09-29 16:39:05 -0400148
149static WebKitChatContainer*
150get_webkit_chat_container(RingMainWindow *win)
151{
152 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
153 if (!priv->webkit_chat_container)
154 {
155 priv->webkit_chat_container = webkit_chat_container_new();
156
157 //We don't want it to be deleted, ever.
158 g_object_ref(priv->webkit_chat_container);
159 }
160 return WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container);
161}
162
Stepan Salenikovich69771842015-02-24 18:11:45 -0500163static void
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500164enter_full_screen(RingMainWindow *self)
165{
166 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
167 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
168
169 if (!priv->is_fullscreen) {
170 gtk_widget_hide(priv->vbox_left_pane);
171 gtk_window_fullscreen(GTK_WINDOW(self));
172 priv->is_fullscreen = TRUE;
173 }
174}
175
176static void
177leave_full_screen(RingMainWindow *self)
178{
179 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
180 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
181
182 if (priv->is_fullscreen) {
183 gtk_widget_show(priv->vbox_left_pane);
184 gtk_window_unfullscreen(GTK_WINDOW(self));
185 priv->is_fullscreen = FALSE;
186 }
187}
188
189static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400190video_double_clicked(RingMainWindow *self)
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500191{
192 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
193 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
194
195 if (priv->is_fullscreen) {
196 leave_full_screen(self);
197 } else {
198 enter_full_screen(self);
199 }
200}
201
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400202static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400203set_pending_contact_request_tab_icon(RingMainWindow* self)
Guillaume Roguez0a411202017-05-25 15:27:26 -0400204{
Guillaume Roguez0a411202017-05-25 15:27:26 -0400205 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
Nicolas Jager2bc3fd92017-05-29 12:48:07 -0400206
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400207 if (not priv->accountContainer_ )
208 return;
209
210 auto isRingAccount = priv->accountContainer_->info.profileInfo.type == lrc::api::profile::Type::RING;
211 gtk_widget_set_visible(priv->scrolled_window_contact_requests, isRingAccount);
212
213 if (not isRingAccount)
Nicolas Jager2bc3fd92017-05-29 12:48:07 -0400214 return;
Guillaume Roguez0a411202017-05-25 15:27:26 -0400215
216 gtk_image_set_from_resource(GTK_IMAGE(priv->image_contact_requests_list),
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400217 (priv->accountContainer_->info.contactModel->hasPendingRequests())
Guillaume Roguez0a411202017-05-25 15:27:26 -0400218 ? "/cx/ring/RingGnome/contact_requests_list_with_notification"
219 : "/cx/ring/RingGnome/contact_requests_list");
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400220}
Guillaume Roguez0a411202017-05-25 15:27:26 -0400221
222static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400223hide_view_clicked(RingMainWindow *self)
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400224{
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400225 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400226 // clear selection and go to welcome page.
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400227 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
228 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
Nicolas Jager0efc8432017-03-22 16:22:00 -0400229 auto selection_contact_request = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
230 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_contact_request));
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400231 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
232 lrc::api::conversation::Info current_item;
233 if (IS_CHAT_VIEW(old_view))
234 current_item = chat_view_get_conversation(CHAT_VIEW(old_view));
235 change_view(self, old_view, current_item, RING_WELCOME_VIEW_TYPE);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400236}
237
aviau039001d2016-09-29 16:39:05 -0400238static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400239change_view(RingMainWindow *self, GtkWidget* old, lrc::api::conversation::Info conversation, GType type)
aviau039001d2016-09-29 16:39:05 -0400240{
241 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
242 leave_full_screen(self);
243 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old);
244
245 GtkWidget *new_view = nullptr;
246
247 QObject::disconnect(priv->selected_item_changed);
248 QObject::disconnect(priv->selected_call_over);
249
250 if (g_type_is_a(INCOMING_CALL_VIEW_TYPE, type)) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400251 delete priv->chatViewConversation_;
252 priv->chatViewConversation_ = new lrc::api::conversation::Info(conversation);
253 new_view = incoming_call_view_new(get_webkit_chat_container(self), priv->accountContainer_, priv->chatViewConversation_);
aviau039001d2016-09-29 16:39:05 -0400254 } else if (g_type_is_a(CURRENT_CALL_VIEW_TYPE, type)) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400255 delete priv->chatViewConversation_;
256 priv->chatViewConversation_ = new lrc::api::conversation::Info(conversation);
257 new_view = current_call_view_new(get_webkit_chat_container(self), priv->accountContainer_, priv->chatViewConversation_);
258
259 try {
260 auto contactUri = priv->chatViewConversation_->participants.front();
261 auto contactInfo = priv->accountContainer_->info.contactModel->getContact(contactUri);
Sébastien Blin366243f2017-11-03 14:14:54 -0400262 auto chat_view = current_call_view_get_chat_view(CURRENT_CALL_VIEW(new_view));
263 if (chat_view) {
264 chat_view_update_temporary(CHAT_VIEW(chat_view),
265 contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING
266 || contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY);
267 }
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400268 } catch(...) { }
269 g_signal_connect_swapped(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400270 } else if (g_type_is_a(CHAT_VIEW_TYPE, type)) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400271 delete priv->chatViewConversation_;
272 priv->chatViewConversation_ = new lrc::api::conversation::Info(conversation);
273 new_view = chat_view_new(get_webkit_chat_container(self), priv->accountContainer_, priv->chatViewConversation_);
274 g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), self);
aviau039001d2016-09-29 16:39:05 -0400275 } else {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400276 // TODO select first conversation?
aviau039001d2016-09-29 16:39:05 -0400277 new_view = priv->welcome_view;
278 }
279
280 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
281 gtk_widget_show(new_view);
282}
283
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400284static void
285show_combobox_account_selector(RingMainWindow *self, gboolean show)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500286{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400287 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
288 // we only want to show the account selector when there is more than 1 enabled
289 // account; so every time we want to show it, we should preform this check
290 auto activatedAccount = 0;
291 auto accounts = priv->lrc_->getAccountModel().getAccountList();
292 for (const auto& account : accounts) {
293 const auto& accountInfo = priv->lrc_->getAccountModel().getAccountInfo(account);
294 if (accountInfo.enabled)
295 ++activatedAccount;
296 }
297 gtk_widget_set_visible(priv->combobox_account_selector,
298 show && activatedAccount > 1);
299}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400300
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400301static void
302on_account_creation_completed(RingMainWindow *win)
303{
304 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
305 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500306
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400307 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
308
309 /* destroy the wizard */
310 if (priv->account_creation_wizard)
311 {
312 gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), priv->account_creation_wizard);
313 gtk_widget_destroy(priv->account_creation_wizard);
314 }
315
316 /* show the settings button*/
317 gtk_widget_show(priv->ring_settings);
318
319 /* show the account selector */
320 show_combobox_account_selector(win, TRUE);
321}
322
323static void
324show_account_creation_wizard(RingMainWindow *win)
325{
326 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
327
328 if (!priv->account_creation_wizard)
329 {
330 priv->account_creation_wizard = account_creation_wizard_new(false);
331 g_object_add_weak_pointer(G_OBJECT(priv->account_creation_wizard), (gpointer *)&priv->account_creation_wizard);
332 g_signal_connect_swapped(priv->account_creation_wizard, "account-creation-completed", G_CALLBACK(on_account_creation_completed), win);
333
334 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
335 priv->account_creation_wizard,
336 ACCOUNT_CREATION_WIZARD_VIEW_NAME);
337 }
338
339 /* hide settings button until account creation is complete */
340 gtk_widget_hide(priv->ring_settings);
341 show_combobox_account_selector(win, FALSE);
342
343 gtk_widget_show(priv->account_creation_wizard);
344
345 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->account_creation_wizard);
346}
347
348static void
349change_to_account(RingMainWindow *win, const std::string& accountId)
350{
351 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
352 // Go to welcome view
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400353 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400354 change_view(win, old_view, lrc::api::conversation::Info(), RING_WELCOME_VIEW_TYPE);
355 // Change combobox
356 auto accounts = priv->lrc_->getAccountModel().getAccountList();
357 auto it = std::find(accounts.begin(), accounts.end(), accountId );
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500358
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400359 if (it == accounts.end()) {
360 qDebug() << "change_to_account, cannot find " << accountId.c_str();
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400361 return;
362 }
Stepan Salenikovich7b7be482016-11-03 16:57:48 -0400363
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400364 int row = it - accounts.begin();
365
366 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), row);
367 // Show conversation panel
368 gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook_contacts), 0);
369 // Reinit LRC
370 ring_init_lrc(win, std::string(accountId));
371 // Update the welcome view
372 ring_welcome_update_view(RING_WELCOME_VIEW(priv->welcome_view), priv->accountContainer_);
373}
374
375static void
376ring_init_lrc(RingMainWindow *win, const std::string& accountId)
377{
378 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
379 // Disconnect old signals.
380 QObject::disconnect(priv->showChatViewConnection_);
381 QObject::disconnect(priv->showIncomingViewConnection_);
382 QObject::disconnect(priv->changeAccountConnection_);
383 QObject::disconnect(priv->showCallViewConnection_);
384 QObject::disconnect(priv->modelSortedConnection_);
385 QObject::disconnect(priv->historyClearedConnection_);
386 QObject::disconnect(priv->filterChangedConnection_);
387 QObject::disconnect(priv->newConversationConnection_);
388 QObject::disconnect(priv->conversationRemovedConnection_);
389
390 // Get the account selected
391 if (priv->accountContainer_) delete priv->accountContainer_;
392 if (!accountId.empty())
393 priv->accountContainer_ = new AccountContainer(priv->lrc_->getAccountModel().getAccountInfo(accountId));
394 else
395 priv->accountContainer_ = nullptr;
396
397 // Reinit tree views
398 if (priv->treeview_conversations) {
399 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_conversations));
400 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
401 gtk_widget_destroy(priv->treeview_conversations);
402 }
403 priv->treeview_conversations = conversations_view_new(priv->accountContainer_);
404 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), priv->treeview_conversations);
405
406 if (priv->treeview_contact_requests) {
407 auto selection_conversations = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_contact_requests));
408 gtk_tree_selection_unselect_all(GTK_TREE_SELECTION(selection_conversations));
409 gtk_widget_destroy(priv->treeview_contact_requests);
410 }
411 priv->treeview_contact_requests = conversations_view_new(priv->accountContainer_);
412 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contact_requests), priv->treeview_contact_requests);
413
414 if (!priv->accountContainer_) return;
415
416 // define slots (we are using this way because gtk is not a c++ lib)
417 auto slotConversationCleared = [win, priv] (const std::string& uid) {
418 // Change the view when the history is cleared.
419 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
420 lrc::api::conversation::Info current_item;
421 current_item.uid = "-1"; // Because the Searching item has an empty uid
422 if (IS_CHAT_VIEW(old_view))
423 current_item = chat_view_get_conversation(CHAT_VIEW(old_view));
424 else
425 // if incoming or call view we don't want to change the view
426 return;
427 if (current_item.uid == uid) {
428 // We are on the conversation cleared.
429 // Go to welcome view because user doesn't want this conversation
430 // TODO go to first conversation?
431 hide_view_clicked(win);
432 }
433 };
434
435 auto slotModelSorted = [win, priv] () {
436 // Synchronize selection when sorted and update pending icon
437 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
438 lrc::api::conversation::Info current_item;
439 current_item.uid = "-1";
440 if (IS_CHAT_VIEW(old_view))
441 current_item = chat_view_get_conversation(CHAT_VIEW(old_view));
442 else if (IS_CURRENT_CALL_VIEW(old_view))
443 current_item = current_call_view_get_conversation(CURRENT_CALL_VIEW(old_view));
444 else if (IS_INCOMING_CALL_VIEW(old_view))
445 current_item = incoming_call_view_get_conversation(INCOMING_CALL_VIEW(old_view));
446 conversations_view_select_conversation(CONVERSATIONS_VIEW(priv->treeview_conversations), current_item.uid);
447 set_pending_contact_request_tab_icon(win);
448 };
449
450 auto slotFilterChanged = [win, priv] () {
451 // Synchronize selection when filter changes
452 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
453 lrc::api::conversation::Info current_item;
454 current_item.uid = "-1";
455 if (IS_CHAT_VIEW(old_view))
456 current_item = chat_view_get_conversation(CHAT_VIEW(old_view));
457 else if (IS_CURRENT_CALL_VIEW(old_view))
458 current_item = current_call_view_get_conversation(CURRENT_CALL_VIEW(old_view));
459 else if (IS_INCOMING_CALL_VIEW(old_view))
460 current_item = incoming_call_view_get_conversation(INCOMING_CALL_VIEW(old_view));
461 conversations_view_select_conversation(CONVERSATIONS_VIEW(priv->treeview_conversations), current_item.uid);
462 // Get if conversation still exists.
463 auto& conversationModel = priv->accountContainer_->info.conversationModel;
464 auto conversations = conversationModel->allFilteredConversations();
465 auto isInConv = std::find_if(
466 conversations.begin(), conversations.end(),
467 [current_item](const lrc::api::conversation::Info& conversation) {
468 return current_item.uid == conversation.uid;
469 });
470 if (IS_CHAT_VIEW(old_view) && isInConv == conversations.end()) {
471 change_view(win, old_view, lrc::api::conversation::Info(), RING_WELCOME_VIEW_TYPE);
472 }
473 };
474
475 auto slotNewConversation = [win, priv] (const std::string& uid) {
476 gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook_contacts), 0);
477 priv->accountContainer_->info.conversationModel->setFilter(lrc::api::profile::Type::RING);
478 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
479 priv->accountContainer_->info.conversationModel->setFilter("");
480 // Select new conversation if contact added
481 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
482 if (IS_RING_WELCOME_VIEW(old_view) || (IS_CHAT_VIEW(old_view) && chat_view_get_temporary(CHAT_VIEW(old_view)))) {
483 priv->accountContainer_->info.conversationModel->selectConversation(uid);
484 try {
485 auto contactUri = priv->chatViewConversation_->participants.front();
486 auto contactInfo = priv->accountContainer_->info.contactModel->getContact(contactUri);
487 chat_view_update_temporary(CHAT_VIEW(gtk_bin_get_child(GTK_BIN(priv->frame_call))),
488 contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING
489 || contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY);
490 } catch(...) { }
491 }
492 };
493
494 auto slotConversationRemoved = [win, priv] (const std::string& uid) {
495 // If contact is removed, go to welcome view
496 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
497 lrc::api::conversation::Info current_item;
498 if (IS_CHAT_VIEW(old_view))
499 current_item = chat_view_get_conversation(CHAT_VIEW(old_view));
500 else if (IS_CURRENT_CALL_VIEW(old_view))
501 current_item = current_call_view_get_conversation(CURRENT_CALL_VIEW(old_view));
502 else if (IS_INCOMING_CALL_VIEW(old_view))
503 current_item = incoming_call_view_get_conversation(INCOMING_CALL_VIEW(old_view));
504 if (current_item.uid == uid)
505 change_view(win, old_view, lrc::api::conversation::Info(), RING_WELCOME_VIEW_TYPE);
506 };
507
508 auto slotShowChatView = [win, priv] (const std::string& accountId, lrc::api::conversation::Info origin) {
509 if (accountId != priv->accountContainer_->info.id)
510 change_to_account(win, accountId);
511 // Show chat view if not in call (unless if it's the same conversation)
512 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
513 lrc::api::conversation::Info current_item;
514 current_item.uid = "-1";
515 if (IS_CHAT_VIEW(old_view))
516 current_item = chat_view_get_conversation(CHAT_VIEW(old_view));
517 if (current_item.uid != origin.uid) {
518 if (origin.participants.empty()) return;
519 auto firstContactUri = origin.participants.front();
520 auto contactInfo = priv->accountContainer_->info.contactModel->getContact(firstContactUri);
521 if (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING && current_item.uid != "-1") return;
522 change_view(win, old_view, origin, CHAT_VIEW_TYPE);
523 }
524 };
525
526 auto slotShowCallView = [win, priv] (const std::string& accountId, lrc::api::conversation::Info origin) {
527 if (accountId != priv->accountContainer_->info.id)
528 change_to_account(win, accountId);
529 // Change the view if we want a different view.
530 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
531
532 lrc::api::conversation::Info current_item;
533 if (IS_CURRENT_CALL_VIEW(old_view))
534 current_item = current_call_view_get_conversation(CURRENT_CALL_VIEW(old_view));
535
536 if (current_item.uid != origin.uid)
537 change_view(win, old_view, origin, CURRENT_CALL_VIEW_TYPE);
538 };
539
540 auto slotShowIncomingCallView = [win, priv] (const std::string& accountId, lrc::api::conversation::Info origin) {
541 if (accountId != priv->accountContainer_->info.id)
542 change_to_account(win, accountId);
543 // Change the view if we want a different view.
544 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
545
546 lrc::api::conversation::Info current_item;
547 if (IS_INCOMING_CALL_VIEW(old_view))
548 current_item = incoming_call_view_get_conversation(INCOMING_CALL_VIEW(old_view));
549
550 if (current_item.uid != origin.uid)
551 change_view(win, old_view, origin, INCOMING_CALL_VIEW_TYPE);
552 };
553
554 // Connect to signals from LRC
555 priv->historyClearedConnection_ = QObject::connect(&*priv->accountContainer_->info.conversationModel,
556 &lrc::api::ConversationModel::conversationCleared,
557 slotConversationCleared);
558
559 priv->modelSortedConnection_ = QObject::connect(&*priv->accountContainer_->info.conversationModel,
560 &lrc::api::ConversationModel::modelSorted,
561 slotModelSorted);
562
563 priv->filterChangedConnection_ = QObject::connect(&*priv->accountContainer_->info.conversationModel,
564 &lrc::api::ConversationModel::filterChanged,
565 slotFilterChanged);
566
567 priv->newConversationConnection_ = QObject::connect(&*priv->accountContainer_->info.conversationModel,
568 &lrc::api::ConversationModel::newConversation,
569 slotNewConversation);
570
571 priv->conversationRemovedConnection_ = QObject::connect(&*priv->accountContainer_->info.conversationModel,
572 &lrc::api::ConversationModel::conversationRemoved,
573 slotConversationRemoved);
574
575 priv->showChatViewConnection_ = QObject::connect(&priv->lrc_->getBehaviorController(),
576 &lrc::api::BehaviorController::showChatView,
577 slotShowChatView);
578
579 priv->showCallViewConnection_ = QObject::connect(&priv->lrc_->getBehaviorController(),
580 &lrc::api::BehaviorController::showCallView,
581 slotShowCallView);
582
583 priv->showIncomingViewConnection_ = QObject::connect(&priv->lrc_->getBehaviorController(),
584 &lrc::api::BehaviorController::showIncomingCallView,
585 slotShowIncomingCallView);
586
587 const gchar *text = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
588 priv->currentTypeFilter_ = priv->accountContainer_->info.profileInfo.type;
589 priv->accountContainer_->info.conversationModel->setFilter(text);
590 priv->accountContainer_->info.conversationModel->setFilter(priv->currentTypeFilter_);
591}
592
593static void
594account_changed(RingMainWindow *self)
595{
596 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
597 auto accountComboBox = GTK_COMBO_BOX(priv->combobox_account_selector);
598 auto model = gtk_combo_box_get_model(accountComboBox);
599
600 GtkTreeIter iter;
601 if (gtk_combo_box_get_active_iter(accountComboBox, &iter)) {
602 gchar *accountId;
603 gtk_tree_model_get (model, &iter,
604 0 /* col# */, &accountId /* data */,
605 -1);
606 // Reinit view
607 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
608 change_view(self, old_view, lrc::api::conversation::Info(), RING_WELCOME_VIEW_TYPE);
609 gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook_contacts), 0);
610 // Change account
611 ring_init_lrc(self, std::string(accountId));
612 ring_welcome_update_view(RING_WELCOME_VIEW(priv->welcome_view), priv->accountContainer_);
613 g_free(accountId);
Stepan Salenikovich9a53f8c2017-04-25 16:11:33 -0400614 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500615}
616
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400617static gboolean
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400618save_accounts(GtkWidget *working_dialog)
619{
620 /* save changes to accounts */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400621 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400622
623 if (working_dialog)
624 gtk_widget_destroy(working_dialog);
625
626 return G_SOURCE_REMOVE;
627}
628
Stepan Salenikovich15142182015-03-11 17:15:26 -0400629static void
Stepan Salenikovichce1d0132017-04-26 14:14:37 -0400630settings_clicked(RingMainWindow *win)
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400631{
632 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
633 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
634
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400635 /* check which view to show */
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500636 if (!priv->show_settings) {
637 /* show the settings */
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400638 priv->show_settings = TRUE;
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500639
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400640 /* show settings */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400641 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 -0400642
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400643 gtk_widget_show(priv->hbox_settings);
644
Stepan Salenikovich41118912015-05-01 11:25:46 -0400645 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400646 if (priv->last_settings_view == priv->media_settings_view)
647 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400648
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400649 /* make sure to show the profile if we're showing the general settings */
650 if (priv->last_settings_view == priv->general_settings_view)
651 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), TRUE);
652
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400653 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400654 } else {
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500655 /* hide the settings */
656 priv->show_settings = FALSE;
657
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400658 /* show working dialog in case save operation takes time */
659 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
660 gtk_window_present(GTK_WINDOW(working));
661
662 /* now save after the time it takes to transition back to the call view (400ms)
663 * the save doesn't happen before the "working" dialog is presented
664 * the timeout function should destroy the "working" dialog when done saving
665 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400666 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400667
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400668 /* show calls */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400669 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 -0400670
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400671 gtk_widget_hide(priv->hbox_settings);
672
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500673 /* make sure video preview is stopped, in case it was started */
674 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400675 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), FALSE);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500676
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400677 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400678
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500679 /* show the view which was selected previously */
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400680 }
681}
682
683static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400684show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400685{
686 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
687 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
688
689 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400690 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400691 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400692 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400693 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400694 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400695 }
696}
697
698static void
699show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
700{
701 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
702 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
703
704 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400705 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
706 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400707 }
708}
709
Stepan Salenikovichde896112015-05-11 16:46:33 -0400710static void
711show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
712{
713 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
714 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
715
716 if (gtk_toggle_button_get_active(navbutton)) {
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400717 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), TRUE);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400718 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
719 priv->last_settings_view = priv->general_settings_view;
Nicolas Jager6f5e04a2016-05-18 15:04:59 -0400720 } else {
721 general_settings_view_show_profile(GENERAL_SETTINGS_VIEW(priv->general_settings_view), FALSE);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400722 }
723}
724
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400725static void
Stepan Salenikovicha06ce192016-11-01 14:26:43 -0400726search_entry_text_changed(GtkSearchEntry *search_entry, RingMainWindow *self)
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400727{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400728 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400729
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400730 // Filter model
731 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
732 priv->accountContainer_->info.conversationModel->setFilter(text);
733}
734
735static void
736search_entry_activated(RingMainWindow *self)
737{
738 // Select the first conversation of the list
739 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
740 auto& conversationModel = priv->accountContainer_->info.conversationModel;
741 auto conversations = conversationModel->allFilteredConversations();
742 if (!conversations.empty())
743 conversationModel->selectConversation(conversations[0].uid);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400744}
745
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400746static gboolean
aviau4c87b832016-12-13 14:18:53 -0500747search_entry_key_released(G_GNUC_UNUSED GtkEntry *search_entry, GdkEventKey *key, RingMainWindow *self)
Stepan Salenikovichcac35042016-10-28 12:07:38 -0400748{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400749 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
750
Stepan Salenikovichcac35042016-10-28 12:07:38 -0400751 // if esc key pressed, clear the regex (keep the text, the user might not want to actually delete it)
752 if (key->keyval == GDK_KEY_Escape) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400753 priv->accountContainer_->info.conversationModel->setFilter("");
Stepan Salenikovichcac35042016-10-28 12:07:38 -0400754 return GDK_EVENT_STOP;
755 }
756
757 return GDK_EVENT_PROPAGATE;
758}
759
760static gboolean
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400761dtmf_pressed(RingMainWindow *win,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400762 GdkEventKey *event,
763 G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400764{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400765 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400766 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400767 g_return_val_if_fail(priv, GDK_EVENT_PROPAGATE);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400768
769 /* we want to react to digit key presses, as long as a GtkEntry is not the
770 * input focus
771 */
772 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
773 if (GTK_IS_ENTRY(focus))
774 return GDK_EVENT_PROPAGATE;
775
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400776 if (priv->accountContainer_ &&
777 priv->accountContainer_->info.profileInfo.type != lrc::api::profile::Type::SIP)
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400778 return GDK_EVENT_PROPAGATE;
779
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400780 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
781 * does not result in a 'c' being played.
782 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
783 if ( event->state
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400784 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
785 return GDK_EVENT_PROPAGATE;
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400786
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400787 // Get current conversation
788 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
789 lrc::api::conversation::Info current_item;
790 if (IS_CURRENT_CALL_VIEW(current_view))
791 current_item = current_call_view_get_conversation(CURRENT_CALL_VIEW(current_view));
792 else
793 return GDK_EVENT_PROPAGATE;
794
795 if (current_item.callId.empty())
796 return GDK_EVENT_PROPAGATE;
797
798 // pass the character that was entered to be played by the daemon;
799 // the daemon will filter out invalid DTMF characters
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400800 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
801 QString val = QString::fromUcs4(&unicode_val, 1);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400802 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400803 priv->accountContainer_->info.callModel->playDTMF(current_item.callId, val.toStdString());
804 // always propogate the key, so we don't steal accelerators/shortcuts
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400805 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400806}
807
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400808static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400809tab_changed(G_GNUC_UNUSED GtkNotebook* notebook,
810 G_GNUC_UNUSED GtkWidget* page,
811 guint page_num,
812 RingMainWindow* self)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400813{
Nicolas Jager0efc8432017-03-22 16:22:00 -0400814 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400815 auto newType = page_num == 0 ? priv->accountContainer_->info.profileInfo.type : lrc::api::profile::Type::PENDING;
816 if (priv->currentTypeFilter_ != newType) {
817 priv->currentTypeFilter_ = newType;
818 priv->accountContainer_->info.conversationModel->setFilter(priv->currentTypeFilter_);
819 }
Nicolas Jager0efc8432017-03-22 16:22:00 -0400820}
821
Stepan Salenikovichba87ae22016-09-13 14:27:20 -0400822static gboolean
823window_size_changed(GtkWidget *win, GdkEventConfigure *event, gpointer)
824{
825 auto *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
826
827 g_settings_set_int(priv->settings, "window-width", event->width);
828 g_settings_set_int(priv->settings, "window-height", event->height);
829
830 return GDK_EVENT_PROPAGATE;
831}
832
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400833static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400834search_entry_places_call_changed(GSettings *settings, const gchar *key, RingMainWindow *self)
835{
836 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
837
838 if (g_settings_get_boolean(settings, key)) {
839 gtk_widget_set_tooltip_text(priv->button_new_conversation, C_("button next to search entry will place a new call", "place call"));
840 } else {
841 gtk_widget_set_tooltip_text(priv->button_new_conversation, C_("button next to search entry will open chat", "open chat"));
842 }
843}
844
845static void
aviau6aeb4852016-08-18 16:01:09 -0400846handle_account_migrations(RingMainWindow *win)
847{
848 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
849
850 /* If there is an existing migration view, remove it */
aviau69081842016-10-14 14:51:54 -0400851 if (priv->account_migration_view)
aviau6aeb4852016-08-18 16:01:09 -0400852 {
Stepan Salenikovich56530b12016-10-20 17:51:34 -0400853 gtk_widget_destroy(priv->account_migration_view);
854 priv->account_migration_view = nullptr;
aviau6aeb4852016-08-18 16:01:09 -0400855 }
856
857 QList<Account*> accounts = AccountModel::instance().accountsToMigrate();
858 if (!accounts.isEmpty())
859 {
860 Account* account = accounts.first();
aviau6aeb4852016-08-18 16:01:09 -0400861
aviau69081842016-10-14 14:51:54 -0400862 priv->account_migration_view = account_migration_view_new(account);
aviau69081842016-10-14 14:51:54 -0400863 g_signal_connect_swapped(priv->account_migration_view, "account-migration-completed", G_CALLBACK(handle_account_migrations), win);
864 g_signal_connect_swapped(priv->account_migration_view, "account-migration-failed", G_CALLBACK(handle_account_migrations), win);
aviau6aeb4852016-08-18 16:01:09 -0400865
866 gtk_widget_hide(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400867 show_combobox_account_selector(win, FALSE);
aviau69081842016-10-14 14:51:54 -0400868 gtk_widget_show(priv->account_migration_view);
aviau6aeb4852016-08-18 16:01:09 -0400869 gtk_stack_add_named(
870 GTK_STACK(priv->stack_main_view),
aviau69081842016-10-14 14:51:54 -0400871 priv->account_migration_view,
aviau6aeb4852016-08-18 16:01:09 -0400872 ACCOUNT_MIGRATION_VIEW_NAME
873 );
874 gtk_stack_set_visible_child_name(
875 GTK_STACK(priv->stack_main_view),
876 ACCOUNT_MIGRATION_VIEW_NAME
877 );
878 }
879 else
880 {
881 gtk_widget_show(priv->ring_settings);
Stepan Salenikovich37194a62017-04-14 16:43:27 -0400882 show_combobox_account_selector(win, TRUE);
aviau6aeb4852016-08-18 16:01:09 -0400883 }
884}
885
Nicolas Jager5e2be732017-03-18 21:35:50 -0400886/**
887 * set the column value by printing the alias and the state of an account in combobox_account_selector.
888 */
889static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400890print_account_and_state(G_GNUC_UNUSED GtkCellLayout* cell_layout,
891 GtkCellRenderer* cell,
892 GtkTreeModel* model,
Nicolas Jager5e2be732017-03-18 21:35:50 -0400893 GtkTreeIter* iter,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400894 G_GNUC_UNUSED gpointer* data)
Nicolas Jager5e2be732017-03-18 21:35:50 -0400895{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400896 gchar *alias;
897 gchar *text;
Nicolas Jager5e2be732017-03-18 21:35:50 -0400898
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400899 gtk_tree_model_get (model, iter,
900 1 /* col# */, &alias /* data */,
901 -1);
Nicolas Jager5e2be732017-03-18 21:35:50 -0400902
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400903 text = g_markup_printf_escaped(
904 "<span fgcolor=\"gray\">%s</span>",
905 alias
906 );
Nicolas Jager5e2be732017-03-18 21:35:50 -0400907
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400908 g_object_set(G_OBJECT(cell), "markup", text, NULL);
909 g_free(alias);
Nicolas Jager5e2be732017-03-18 21:35:50 -0400910}
911
Guillaume Roguez0a411202017-05-25 15:27:26 -0400912
913static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500914ring_main_window_init(RingMainWindow *win)
915{
916 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
917 gtk_widget_init_template(GTK_WIDGET(win));
918
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400919 // NOTE: When new models will be fully implemented, we need to move this
920 // in rign_client.cpp.
921 // Init LRC and the vew
922 priv->lrc_ = std::make_unique<lrc::api::Lrc>();
923 const auto accountIds = priv->lrc_->getAccountModel().getAccountList();
924 auto isInitialized = false;
925 if (not accountIds.empty()) {
926 for (const auto& accountId : accountIds) {
927 const auto& accountInfo = priv->lrc_->getAccountModel().getAccountInfo(accountId);
928 if (accountInfo.enabled) {
929 ring_init_lrc(win, accountId);
930 isInitialized = true;
931 break;
932 }
933 }
934 if (!isInitialized) {
935 priv->treeview_conversations = conversations_view_new(priv->accountContainer_);
936 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), priv->treeview_conversations);
937 priv->treeview_contact_requests = conversations_view_new(priv->accountContainer_);
938 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contact_requests), priv->treeview_contact_requests);
939 }
940 }
941
942 // Account status changed
943 auto slotAccountStatusChanged = [win, priv] (const std::string& accountId) {
944 if (not priv->accountContainer_ ) {
945 ring_init_lrc(win, accountId);
946
947 if (priv->accountContainer_)
948 ring_welcome_update_view(RING_WELCOME_VIEW(priv->welcome_view),
949 priv->accountContainer_);
950 } else {
951 auto accounts = priv->lrc_->getAccountModel().getAccountList();
952 auto store = gtk_list_store_new (2 /* # of cols */ ,
953 G_TYPE_STRING,
954 G_TYPE_STRING,
955 G_TYPE_UINT);
956 auto currentIdx = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_account_selector));
957 GtkTreeIter iter;
958 auto enabledAccounts = 0;
959 for (const auto& account : accounts) {
960 const auto& accountInfo = priv->lrc_->getAccountModel().getAccountInfo(account);
961 show_combobox_account_selector(win, TRUE);
962 if (accountId == account && !accountInfo.enabled) currentIdx = 0;
963 if (accountInfo.enabled) {
964 ++enabledAccounts;
965 gtk_list_store_append (store, &iter);
966 gtk_list_store_set (store, &iter,
967 0 /* col # */ , account.c_str() /* celldata */,
968 1 /* col # */ , accountInfo.profileInfo.alias.c_str() /* celldata */,
969 -1 /* end */);
970 }
971 }
972 // Redraw combobox
973 gtk_combo_box_set_model(
974 GTK_COMBO_BOX(priv->combobox_account_selector),
975 GTK_TREE_MODEL (store)
976 );
977 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), currentIdx);
978 // if no account. reset the view.
979 if (enabledAccounts == 0) {
980 ring_init_lrc(win, "");
981 auto old_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
982 change_view(win, old_view, lrc::api::conversation::Info(), RING_WELCOME_VIEW_TYPE);
983 ring_welcome_update_view(RING_WELCOME_VIEW(priv->welcome_view), nullptr);
984 }
985 }
986 };
987
988 priv->accountStatusChangedConnection_ = QObject::connect(&priv->lrc_->getAccountModel(),
989 &lrc::api::NewAccountModel::accountStatusChanged,
990 slotAccountStatusChanged);
991
992 // This signal must be connected if no account
993 auto slotAccountAdded = [win, priv] (const std::string& idAdded) {
994 Q_UNUSED(idAdded)
995 // New account added. go to this account
996 auto accounts = priv->lrc_->getAccountModel().getAccountList();
997 auto store = gtk_list_store_new (2 /* # of cols */ ,
998 G_TYPE_STRING,
999 G_TYPE_STRING,
1000 G_TYPE_UINT);
1001 auto currentIdx = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_account_selector));
1002 GtkTreeIter iter;
1003 for (const auto& accountId : accounts) {
1004 const auto& account = priv->lrc_->getAccountModel().getAccountInfo(accountId);
1005 if (account.enabled) {
1006 gtk_list_store_append (store, &iter);
1007 gtk_list_store_set (store, &iter,
1008 0 /* col # */ , accountId.c_str() /* celldata */,
1009 1 /* col # */ , account.profileInfo.alias.c_str() /* celldata */,
1010 -1 /* end */);
1011 }
1012 }
1013 // Redraw combobox
1014 gtk_combo_box_set_model(
1015 GTK_COMBO_BOX(priv->combobox_account_selector),
1016 GTK_TREE_MODEL (store)
1017 );
1018 // If no account selected, select the new account
1019 if (currentIdx == -1) currentIdx = 0;
1020 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), currentIdx);
1021 show_combobox_account_selector(win, TRUE);
1022 };
1023
1024 priv->newAccountConnection_ = QObject::connect(&priv->lrc_->getAccountModel(),
1025 &lrc::api::NewAccountModel::accountAdded,
1026 slotAccountAdded);
1027
1028 auto slotAccountRemoved = [win, priv] (G_GNUC_UNUSED const std::string& idRemoved) {
1029 // Account removed, change account if it's the current account selected.
1030 auto accounts = priv->lrc_->getAccountModel().getAccountList();
1031 if (accounts.empty()) {
1032 show_account_creation_wizard(win);
1033 } else {
1034 auto store = gtk_list_store_new (2 /* # of cols */ ,
1035 G_TYPE_STRING,
1036 G_TYPE_STRING,
1037 G_TYPE_UINT);
1038 GtkTreeIter iter;
1039 for (const auto& accountId : accounts) {
1040 const auto& account = priv->lrc_->getAccountModel().getAccountInfo(accountId);
1041 if (account.enabled) {
1042 gtk_list_store_append (store, &iter);
1043 gtk_list_store_set (store, &iter,
1044 0 /* col # */ , accountId.c_str() /* celldata */,
1045 1 /* col # */ , account.profileInfo.alias.c_str() /* celldata */,
1046 -1 /* end */);
1047 }
1048 }
1049
1050 gtk_combo_box_set_model(
1051 GTK_COMBO_BOX(priv->combobox_account_selector),
1052 GTK_TREE_MODEL (store)
1053 );
1054 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), 0);
1055 show_combobox_account_selector(win, TRUE);
1056 // Show conversation panel
1057 gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook_contacts), 0);
1058 // Reinit LRC
1059 ring_init_lrc(win, std::string(accounts.at(0)));
1060 // Update the welcome view
1061 ring_welcome_update_view(RING_WELCOME_VIEW(priv->welcome_view), priv->accountContainer_);
1062 }
1063 };
1064
1065 priv->rmAccountConnection_ = QObject::connect(&priv->lrc_->getAccountModel(),
1066 &lrc::api::NewAccountModel::accountRemoved,
1067 slotAccountRemoved);
1068
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001069 /* bind to window size settings */
1070 priv->settings = g_settings_new_full(get_ring_schema(), nullptr, nullptr);
1071 auto width = g_settings_get_int(priv->settings, "window-width");
1072 auto height = g_settings_get_int(priv->settings, "window-height");
1073 gtk_window_set_default_size(GTK_WINDOW(win), width, height);
1074 g_signal_connect(win, "configure-event", G_CALLBACK(window_size_changed), nullptr);
1075
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001076 /* search-entry-places-call setting */
1077 search_entry_places_call_changed(priv->settings, "search-entry-places-call", win);
1078 g_signal_connect(priv->settings, "changed::search-entry-places-call", G_CALLBACK(search_entry_places_call_changed), win);
1079
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001080 /* set window icon */
1081 GError *error = NULL;
1082 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
1083 if (icon == NULL) {
1084 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -04001085 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001086 } else
1087 gtk_window_set_icon(GTK_WINDOW(win), icon);
1088
1089 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001090 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
Stepan Salenikovichd61974b2016-03-18 16:36:27 -04001091 -1, 16, TRUE, &error);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001092 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001093 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -04001094 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001095 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001096 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001097
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001098 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001099 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
1100 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001101 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001102 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001103
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001104 /* settings icon */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -04001105 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 -04001106
1107 /* connect settings button signal */
Stepan Salenikovichce1d0132017-04-26 14:14:37 -04001108 g_signal_connect_swapped(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001109
1110 /* add the call view to the main stack */
1111 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
1112 priv->vbox_call_view,
1113 CALL_VIEW_NAME);
1114
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001115 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001116 priv->account_settings_view = account_view_new();
1117 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
1118
Stepan Salenikovich0bd53492015-05-11 14:28:52 -04001119 priv->media_settings_view = media_settings_view_new();
1120 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 -04001121
Stepan Salenikovichde896112015-05-11 16:46:33 -04001122 priv->general_settings_view = general_settings_view_new();
1123 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
1124
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001125 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -04001126 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
1127 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001128
1129 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001130 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001131 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -04001132 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -04001133
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001134 g_signal_connect(priv->notebook_contacts, "switch-page", G_CALLBACK(tab_changed), win);
Nicolas Jager0efc8432017-03-22 16:22:00 -04001135
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04001136 /* welcome/default view */
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001137 priv->welcome_view = ring_welcome_view_new(priv->accountContainer_);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001138 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 -04001139 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
1140 gtk_widget_show(priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001141
Stepan Salenikovichb0ffc342016-10-13 13:10:55 -04001142 g_signal_connect_swapped(priv->search_entry, "activate", G_CALLBACK(search_entry_activated), win);
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001143 g_signal_connect_swapped(priv->button_new_conversation, "clicked", G_CALLBACK(search_entry_activated), win);
Stepan Salenikovicha06ce192016-11-01 14:26:43 -04001144 g_signal_connect(priv->search_entry, "search-changed", G_CALLBACK(search_entry_text_changed), win);
Stepan Salenikovichcac35042016-10-28 12:07:38 -04001145 g_signal_connect(priv->search_entry, "key-release-event", G_CALLBACK(search_entry_key_released), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001146
AmarOk602babf2017-07-18 12:28:02 -04001147 auto provider = gtk_css_provider_new();
1148 gtk_css_provider_load_from_data(provider,
1149 ".search-entry-style { border: 0; border-radius: 0; } \
1150 .spinner-style { border: 0; background: white; } \
1151 .new-conversation-style { border: 0; background: white; transition: all 0.3s ease; border-radius: 0; } \
1152 .new-conversation-style:hover { background: #bae5f0; }",
1153 -1, nullptr
1154 );
1155 gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
1156 GTK_STYLE_PROVIDER(provider),
1157 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1158
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001159
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001160 /* react to digit key press events */
1161 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich48b45c12015-09-25 15:49:51 -04001162
1163 /* set the search entry placeholder text */
1164 gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
1165 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 -04001166
aviau039001d2016-09-29 16:39:05 -04001167 /* init chat webkit container so that it starts loading before the first time we need it*/
1168 get_webkit_chat_container(win);
1169
Stepan Salenikovichcbc8dcc2017-04-05 11:27:10 -04001170 if (has_ring_account()) {
1171 /* user has ring account, so show the call view right away */
1172 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
1173
1174 handle_account_migrations(win);
1175 } else {
1176 /* user has to create the ring account */
1177 show_account_creation_wizard(win);
1178 }
Nicolas Jager15a8b902017-03-21 07:53:06 -04001179
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001180 // setup account selector and select the first account
1181 auto store = gtk_list_store_new (2 /* # of cols */ ,
1182 G_TYPE_STRING,
1183 G_TYPE_STRING,
1184 G_TYPE_UINT);
1185 GtkTreeIter iter;
1186 for (const auto& accountId : accountIds) {
1187 const auto& accountInfo = priv->lrc_->getAccountModel().getAccountInfo(accountId);
1188 if (accountInfo.enabled) {
1189 gtk_list_store_append (store, &iter);
1190 gtk_list_store_set (store, &iter,
1191 0 /* col # */ , accountId.c_str() /* celldata */,
1192 1 /* col # */ , accountInfo.profileInfo.alias.c_str() /* celldata */,
1193 -1 /* end */);
1194 }
1195 }
1196
1197 gtk_combo_box_set_model(
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001198 GTK_COMBO_BOX(priv->combobox_account_selector),
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001199 GTK_TREE_MODEL (store)
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001200 );
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001201 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_account_selector), 0);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001202
Nicolas Jagera169c712017-04-14 12:02:50 -04001203 /* set visibility */
Stepan Salenikovich37194a62017-04-14 16:43:27 -04001204 show_combobox_account_selector(win, TRUE);
Nicolas Jagera169c712017-04-14 12:02:50 -04001205
Nicolas Jager15a8b902017-03-21 07:53:06 -04001206 /* layout */
Stepan Salenikovich79b5f102017-04-26 13:48:04 -04001207 auto *renderer = gtk_cell_renderer_text_new();
Nicolas Jager15a8b902017-03-21 07:53:06 -04001208 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_account_selector), renderer, FALSE);
1209 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_account_selector), renderer, "text", 0, NULL);
Nicolas Jager5e2be732017-03-18 21:35:50 -04001210 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(priv->combobox_account_selector),
1211 renderer,
1212 (GtkCellLayoutDataFunc)print_account_and_state,
1213 nullptr, nullptr);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001214
Stepan Salenikovichce1d0132017-04-26 14:14:37 -04001215 // we closing any view opened to avoid confusion (especially between SIP and Ring protocols).
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001216 g_signal_connect_swapped(priv->combobox_account_selector, "changed", G_CALLBACK(account_changed), win);
Guillaume Roguez0a411202017-05-25 15:27:26 -04001217
1218 // initialize the pending contact request icon.
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001219 set_pending_contact_request_tab_icon(win);
1220
1221 if (isInitialized) {
1222 auto& conversationModel = priv->accountContainer_->info.conversationModel;
1223 auto conversations = conversationModel->allFilteredConversations();
1224 for (const auto& conversation: conversations) {
1225 if (!conversation.callId.empty()) {
1226 priv->accountContainer_->info.conversationModel->selectConversation(conversation.uid);
1227 }
1228 }
1229 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001230}
1231
1232static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001233ring_main_window_dispose(GObject *object)
1234{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001235 RingMainWindow *self = RING_MAIN_WINDOW(object);
1236 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1237
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001238 if (priv->accountContainer_) {
1239 delete priv->accountContainer_;
1240 priv->accountContainer_ = nullptr;
1241 }
1242 if (priv->chatViewConversation_) {
1243 delete priv->chatViewConversation_;
1244 priv->chatViewConversation_ = nullptr;
1245 }
1246
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001247 QObject::disconnect(priv->selected_item_changed);
1248 QObject::disconnect(priv->selected_call_over);
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001249 QObject::disconnect(priv->showChatViewConnection_);
1250 QObject::disconnect(priv->showIncomingViewConnection_);
1251 QObject::disconnect(priv->historyClearedConnection_);
1252 QObject::disconnect(priv->modelSortedConnection_);
1253 QObject::disconnect(priv->filterChangedConnection_);
1254 QObject::disconnect(priv->newConversationConnection_);
1255 QObject::disconnect(priv->conversationRemovedConnection_);
1256 QObject::disconnect(priv->changeAccountConnection_);
1257 QObject::disconnect(priv->newAccountConnection_);
1258 QObject::disconnect(priv->rmAccountConnection_);
1259 QObject::disconnect(priv->showCallViewConnection_);
1260 QObject::disconnect(priv->modelSortedConnection_);
1261 QObject::disconnect(priv->accountStatusChangedConnection_);
aviau039001d2016-09-29 16:39:05 -04001262
1263 g_clear_object(&priv->welcome_view);
1264 g_clear_object(&priv->webkit_chat_container);
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001265
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001266 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1267}
1268
1269static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001270ring_main_window_finalize(GObject *object)
1271{
Stepan Salenikovichba87ae22016-09-13 14:27:20 -04001272 RingMainWindow *self = RING_MAIN_WINDOW(object);
1273 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1274
1275 g_clear_object(&priv->settings);
1276
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001277 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001278}
1279
1280static void
1281ring_main_window_class_init(RingMainWindowClass *klass)
1282{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001283 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001284 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001285
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001286 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1287 "/cx/ring/RingGnome/ringmainwindow.ui");
1288
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001289 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001290 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, notebook_contacts);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001291 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_smartview);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001292 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1293 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1294 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1295 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001296 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001297 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001298 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001299 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001300 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, frame_call);
Stepan Salenikoviche9933242016-06-21 18:08:48 -04001301 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_new_conversation );
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001302 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001303 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001304 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Nicolas Jager15a8b902017-03-21 07:53:06 -04001305 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_account_selector);
Nicolas Jager0efc8432017-03-22 16:22:00 -04001306 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contact_requests);
Guillaume Roguez0a411202017-05-25 15:27:26 -04001307 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_contact_requests_list);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001308}
1309
1310GtkWidget *
1311ring_main_window_new (GtkApplication *app)
1312{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001313 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001314 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001315}