blob: 3ba41af1116f059f2d58488d0c0dcda089ec366a [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050018 */
19
20#include "ringmainwindow.h"
21
22#include <gtk/gtk.h>
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040023#include <glib/gi18n.h>
Stepan Salenikovicha3557452015-02-20 14:14:12 -050024#include "models/gtkqtreemodel.h"
25#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050026#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050027#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050028#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050029#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050030#include <string.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040031#include <contactmethod.h>
Stepan Salenikovich7ff47962015-03-16 15:10:14 -040032#include <QtCore/QSortFilterProxyModel>
33#include "models/gtkqsortfiltertreemodel.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040034#include "accountview.h"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040035#include <accountmodel.h>
Edric Milaret58e71072016-03-21 12:16:37 -040036#include <codecmodel.h>
Stepan Salenikovichbd029582015-03-24 11:00:56 -040037#include "dialogs.h"
Stepan Salenikovich0bd53492015-05-11 14:28:52 -040038#include "mediasettingsview.h"
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040039#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040040#include <personmodel.h>
41#include "utils/drawing.h"
42#include <memory>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040043#include <globalinstances.h>
44#include "native/pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040045#include "models/activeitemproxymodel.h"
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -040046#include <numbercompletionmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040047#include "utils/calling.h"
48#include "contactsview.h"
Stepan Salenikovich9d294492015-05-14 16:34:24 -040049#include <categorizedcontactmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040050#include "historyview.h"
51#include "utils/models.h"
Stepan Salenikovichde896112015-05-11 16:46:33 -040052#include "generalsettingsview.h"
Stepan Salenikovich75a39172015-07-10 13:21:08 -040053#include "utils/accounts.h"
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040054#include "ringwelcomeview.h"
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040055#include "recentcontactsview.h"
Stepan Salenikovichc1323422016-01-06 10:54:44 -050056#include <recentmodel.h>
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050057#include "chatview.h"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040058
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040059static constexpr const char* CALL_VIEW_NAME = "calls";
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -050060static constexpr const char* CREATE_ACCOUNT_VIEW_NAME = "wizard";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040061static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
62static constexpr const char* AUDIO_SETTINGS_VIEW_NAME = "audio";
63static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
64static constexpr const char* ACCOUNT_SETTINGS_VIEW_NAME = "accounts";
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040065static constexpr const char* DEFAULT_VIEW_NAME = "welcome";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040066static constexpr const char* VIEW_CONTACTS = "contacts";
67static constexpr const char* VIEW_HISTORY = "history";
68static constexpr const char* VIEW_PRESENCE = "presence";
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050069
70struct _RingMainWindow
71{
72 GtkApplicationWindow parent;
73};
74
75struct _RingMainWindowClass
76{
77 GtkApplicationWindowClass parent_class;
78};
79
80typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
81
82struct _RingMainWindowPrivate
83{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040084 GtkWidget *ring_menu;
85 GtkWidget *image_ring;
86 GtkWidget *ring_settings;
87 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040088 GtkWidget *hbox_settings;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040089 GtkWidget *scrolled_window_smartview;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -040090 GtkWidget *scrolled_window_contacts;
91 GtkWidget *scrolled_window_history;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -040092 GtkWidget *vbox_left_pane;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050093 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -050094 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040095 GtkWidget *vbox_call_view;
Stepan Salenikovich3034d922015-10-09 10:11:42 -040096 GtkWidget *frame_call;
97 GtkWidget *welcome_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050098 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040099 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400100 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400101 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400102 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400103 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400104 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400105 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400106
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400107 QMetaObject::Connection selection_updated;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400108
109 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400110
111 /* account creation */
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500112 GtkWidget *account_creation;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400113 GtkWidget *image_ring_logo;
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500114 GtkWidget *vbox_account_creation_entry;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400115 GtkWidget *entry_alias;
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500116 GtkWidget *label_default_name;
117 GtkWidget *label_paceholder;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400118 GtkWidget *label_generating_account;
119 GtkWidget *spinner_generating_account;
120 GtkWidget *button_account_creation_next;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400121
122 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400123
124 /* allocd qmodels */
125 ActiveItemProxyModel *q_contact_model;
126 QSortFilterProxyModel *q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400127 NumberCompletionModel *q_completion_model;
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500128
129 /* fullscreen */
130 gboolean is_fullscreen;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500131};
132
133G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
134
135#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
136
Stepan Salenikovich69771842015-02-24 18:11:45 -0500137static void
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500138enter_full_screen(RingMainWindow *self)
139{
140 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
141 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
142
143 if (!priv->is_fullscreen) {
144 gtk_widget_hide(priv->vbox_left_pane);
145 gtk_window_fullscreen(GTK_WINDOW(self));
146 priv->is_fullscreen = TRUE;
147 }
148}
149
150static void
151leave_full_screen(RingMainWindow *self)
152{
153 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
154 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
155
156 if (priv->is_fullscreen) {
157 gtk_widget_show(priv->vbox_left_pane);
158 gtk_window_unfullscreen(GTK_WINDOW(self));
159 priv->is_fullscreen = FALSE;
160 }
161}
162
163static void
Stepan Salenikovich5e431cf2015-12-24 14:09:01 -0500164video_double_clicked(G_GNUC_UNUSED CurrentCallView *view, RingMainWindow *self)
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500165{
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 leave_full_screen(self);
171 } else {
172 enter_full_screen(self);
173 }
174}
175
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400176static void
177hide_view_clicked(G_GNUC_UNUSED GtkWidget *view, RingMainWindow *self)
178{
179 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
180
181 /* clear selection */
182 RecentModel::instance().selectionModel()->clear();
183}
184
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500185/**
186 * This takes the RecentModel index as the argument and displays the corresponding view:
187 * - incoming call view
188 * - current call view
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500189 * - chat view
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500190 * - welcome view (if no index is selected)
191 */
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500192static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500193selection_changed(const QModelIndex& recent_idx, RingMainWindow *win)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500194{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500195 // g_debug("selection changed");
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400196 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500197 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
198
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500199 /* if we're showing the settings, then nothing needs to be done as the call
200 view is not shown */
201 if (priv->show_settings) return;
202
Stepan Salenikovich69771842015-02-24 18:11:45 -0500203 /* get the current visible stack child */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400204 GtkWidget *old_call_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500205
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500206 /* make sure we leave full screen, since the call selection is changing */
207 leave_full_screen(win);
208
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500209 /* check which object type is selected */
210 auto type = recent_idx.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>();
211 auto object = recent_idx.data(static_cast<int>(Ring::Role::Object));
212 /* try to get the call model index, in case its a call, since we're still using the CallModel as well */
213 auto call_idx = CallModel::instance().getIndex(RecentModel::instance().getActiveCall(recent_idx));
214
215 /* we prioritize showing the call view */
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500216 if (call_idx.isValid()) {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500217 /* show the call view */
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500218 QVariant state = call_idx.data(static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500219 GtkWidget *new_call_view = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500220
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400221 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400222 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500223 case Call::LifeCycleState::INITIALIZATION:
224 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500225 new_call_view = incoming_call_view_new();
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500226 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), call_idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500227 break;
228 case Call::LifeCycleState::PROGRESS:
229 new_call_view = current_call_view_new();
Stepan Salenikovich5e431cf2015-12-24 14:09:01 -0500230 g_signal_connect(new_call_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win);
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500231 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), call_idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500232 break;
233 case Call::LifeCycleState::COUNT__:
234 g_warning("LifeCycleState should never be COUNT");
235 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500236 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500237
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400238 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
239 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_call_view);
240 gtk_widget_show(new_call_view);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500241 } else if (type == Ring::ObjectType::Person && object.isValid()) {
242 /* show chat view constructed from Person object */
243 auto new_chat_view = chat_view_new_person(object.value<Person *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400244 g_signal_connect(new_chat_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500245 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
246 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_chat_view);
247 gtk_widget_show(new_chat_view);
248 } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) {
249 /* show chat view constructed from CM */
250 auto new_chat_view = chat_view_new_cm(object.value<ContactMethod *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400251 g_signal_connect(new_chat_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500252 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
253 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_chat_view);
254 gtk_widget_show(new_chat_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500255 } else {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500256 /* nothing selected that we can display, show the welcome view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400257 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
258 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500259 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500260}
261
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500262static gboolean
263selected_item_changed(RingMainWindow *win)
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500264{
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500265 // g_debug("item changed");
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500266 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
267
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500268 /* if we're showing the settings, then nothing needs to be done as the call
269 view is not shown */
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500270 if (priv->show_settings) return G_SOURCE_REMOVE;
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500271
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500272 auto idx_selected = RecentModel::instance().selectionModel()->currentIndex();
273
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500274 /* we prioritize showing the call view; but if the call is over we go back to showing the chat view */
275 if(auto call = RecentModel::instance().getActiveCall(idx_selected)) {
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500276 /* check if we need to change the view */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500277 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovichfe87a8d2016-01-15 10:39:09 -0500278 auto state = call->lifeCycleState();
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500279
280 /* check what the current state is vs what is displayed */
Stepan Salenikovichfe87a8d2016-01-15 10:39:09 -0500281 switch(state) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400282 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500283 case Call::LifeCycleState::FINISHED:
284 /* go back to incoming call view;
285 * it will show that the call failed and offer to hang it up */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500286 case Call::LifeCycleState::INITIALIZATION:
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500287 {
288 /* show the incoming call view */
289 if (!IS_INCOMING_CALL_VIEW(current_view)) {
290 auto new_view = incoming_call_view_new();
291 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_view), CallModel::instance().getIndex(call));
292 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
293 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
294 gtk_widget_show(new_view);
295 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500296 }
297 break;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500298 case Call::LifeCycleState::PROGRESS:
299 {
300 /* show the current call view */
301 if (!IS_CURRENT_CALL_VIEW(current_view)) {
302 auto new_view = current_call_view_new();
303 g_signal_connect(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win);
304 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_view), CallModel::instance().getIndex(call));
305 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
306 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
307 gtk_widget_show(new_view);
308 }
309 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500310 break;
311 case Call::LifeCycleState::COUNT__:
312 g_warning("LifeCycleState should never be COUNT");
313 break;
314 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500315 } else if (idx_selected.isValid()) {
316 /* otherwise, the call is over and is already removed from the RecentModel */
317 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
318 leave_full_screen(win);
319
320 /* show the chat view */
321 if (!IS_CHAT_VIEW(current_view)) {
322 auto type = idx_selected.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>();
323 auto object = idx_selected.data(static_cast<int>(Ring::Role::Object));
324 if (type == Ring::ObjectType::Person && object.isValid()) {
325 /* show chat view constructed from Person object */
326 auto new_view = chat_view_new_person(object.value<Person *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400327 g_signal_connect(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500328 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
329 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
330 gtk_widget_show(new_view);
331 } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) {
332 /* show chat view constructed from CM */
333 auto new_view = chat_view_new_cm(object.value<ContactMethod *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400334 g_signal_connect(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500335 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
336 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
337 gtk_widget_show(new_view);
338 }
339 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500340 }
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500341
342 return G_SOURCE_REMOVE;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500343}
344
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500345static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500346search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
347{
348 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
349
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400350 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500351
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400352 if (number_entered && strlen(number_entered) > 0) {
353 /* detect Ring hash */
354 gboolean is_ring_hash = FALSE;
355 if (strlen(number_entered) == 40) {
356 is_ring_hash = TRUE;
357 /* must be 40 chars long and alphanumeric */
358 for (int i = 0; i < 40 && is_ring_hash; ++i) {
359 if (!g_ascii_isalnum(number_entered[i]))
360 is_ring_hash = FALSE;
361 }
362 }
363
364 QString number = QString{number_entered};
365
366 if (is_ring_hash)
367 number = "ring:" + number;
368
369 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400370
371 Call *call = priv->q_completion_model->call();
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500372 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500373 call->performAction(Call::Action::ACCEPT);
374
375 /* make this the currently selected call */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400376 QModelIndex idx = CallModel::instance().getIndex(call);
377 CallModel::instance().selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400378
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400379 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
380 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
381 /* clear the entry */
382 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
383 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500384}
385
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400386static gboolean
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400387save_accounts(GtkWidget *working_dialog)
388{
389 /* save changes to accounts */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400390 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400391
392 if (working_dialog)
393 gtk_widget_destroy(working_dialog);
394
395 return G_SOURCE_REMOVE;
396}
397
Stepan Salenikovich15142182015-03-11 17:15:26 -0400398static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400399settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
400{
401 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
402 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
403
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400404 /* check which view to show */
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500405 if (!priv->show_settings) {
406 /* show the settings */
407
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500408 /* make sure we are not showing a call view so we don't have more than one clutter stage at a time */
409 selection_changed(QModelIndex(), win);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500410
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400411 /* show settings */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400412 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 -0400413
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400414 gtk_widget_show(priv->hbox_settings);
415
Stepan Salenikovich41118912015-05-01 11:25:46 -0400416 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400417 if (priv->last_settings_view == priv->media_settings_view)
418 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400419
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400420 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400421 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500422
423 priv->show_settings = TRUE;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400424 } else {
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500425 /* hide the settings */
426 priv->show_settings = FALSE;
427
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400428 /* show working dialog in case save operation takes time */
429 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
430 gtk_window_present(GTK_WINDOW(working));
431
432 /* now save after the time it takes to transition back to the call view (400ms)
433 * the save doesn't happen before the "working" dialog is presented
434 * the timeout function should destroy the "working" dialog when done saving
435 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400436 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400437
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400438 /* show calls */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400439 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 -0400440
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400441 gtk_widget_hide(priv->hbox_settings);
442
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500443 /* make sure video preview is stopped, in case it was started */
444 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
445
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400446 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
447 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400448
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500449 /* show the view which was selected previously */
450 selection_changed(RecentModel::instance().selectionModel()->currentIndex(), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400451 }
452}
453
454static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400455show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400456{
457 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
458 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
459
460 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400461 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400462 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400463 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400464 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400465 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400466 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400467 }
468}
469
470static void
471show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
472{
473 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
474 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
475
476 if (gtk_toggle_button_get_active(navbutton)) {
477 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
478 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
479 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400480 }
481}
482
Stepan Salenikovichde896112015-05-11 16:46:33 -0400483static void
484show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
485{
486 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
487 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
488
489 if (gtk_toggle_button_get_active(navbutton)) {
490 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
491 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
492 priv->last_settings_view = priv->general_settings_view;
493 }
494}
495
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400496static gboolean
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400497create_ring_account(RingMainWindow *win)
498{
499 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
500 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
501
502 /* create account and set UPnP enabled, as its not by default in the daemon */
503 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500504 Account *account = nullptr;
505 if (alias && strlen(alias) > 0)
506 account = AccountModel::instance().add(alias, Account::Protocol::RING);
507 else
508 account = AccountModel::instance().add(C_("The default username / account alias, if none is set by the user", "Unknown"), Account::Protocol::RING);
Stepan Salenikovich75a39172015-07-10 13:21:08 -0400509 account->setDisplayName(alias); // set the display name to the same as the alias
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400510 account->setUpnpEnabled(TRUE);
511
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500512 /* wait for hash to be generated to show the main view */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400513 priv->hash_updated = QObject::connect(
514 account,
515 &Account::changed,
516 [=] (Account *a) {
517 QString hash = a->username();
518 if (!hash.isEmpty()) {
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500519 /* show the call view */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400520 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500521 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400522
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500523 /* show the settings button*/
524 gtk_widget_show(priv->ring_settings);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400525 }
526 }
527 );
528
529 account->performAction(Account::EditAction::SAVE);
530
531 return G_SOURCE_REMOVE;
532}
533
534static void
535alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
536{
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500537 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400538 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
539
540 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500541 if (!alias || strlen(alias) == 0) {
542 gtk_widget_show(priv->label_default_name);
543 gtk_widget_hide(priv->label_paceholder);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400544 } else {
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500545 gtk_widget_hide(priv->label_default_name);
546 gtk_widget_show(priv->label_paceholder);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400547 }
548}
549
550static void
551account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
552{
553 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
554
555 /* show/hide relevant widgets */
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500556 gtk_widget_hide(priv->vbox_account_creation_entry);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400557 gtk_widget_hide(priv->button_account_creation_next);
558 gtk_widget_show(priv->label_generating_account);
559 gtk_widget_show(priv->spinner_generating_account);
560
561 /* now create account after a short timeout so that the the save doesn't
562 * happen freeze the client before the widget changes happen;
563 * the timeout function should then display the next step in account creation
564 */
565 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
566}
567
568static void
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400569entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
570{
571 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
572
573 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
574 if (strlen(alias) > 0)
575 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
576}
577
578static void
579show_account_creation(RingMainWindow *win)
580{
581 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
582
583 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500584 priv->account_creation,
585 CREATE_ACCOUNT_VIEW_NAME);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400586
Stepan Salenikovich64505672015-09-24 10:46:07 -0400587 /* hide settings button until account creation is complete */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400588 gtk_widget_hide(priv->ring_settings);
589
590 /* set ring logo */
591 GError *error = NULL;
592 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500593 -1, 50, TRUE, &error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400594 if (logo_ring == NULL) {
595 g_debug("Could not load logo: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400596 g_clear_error(&error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400597 } else
598 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
599
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500600 /* use the real name / username of the logged in user as the default */
Nicolas Jager5e604d52016-05-13 11:09:12 -0400601 const char* real_name = g_get_real_name();
602 const char* user_name = g_get_user_name();
603 g_debug("real_name = %s",real_name);
604 g_debug("user_name = %s",user_name);
605
606 /* check first if the real name was determined */
607 if (g_strcmp0 (real_name,"Unknown") != 0)
608 gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), real_name);
609 else
610 gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), user_name);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400611
612 /* connect signals */
613 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
614 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400615 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400616
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500617 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_VIEW_NAME);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400618}
619
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400620static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400621search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
622{
623 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
624
625 /* get the text from the entry */
626 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
627
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400628 if (text && strlen(text) > 0) {
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400629 /* edit the the dialing call (or create a new one) */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400630 if (auto call = CallModel::instance().dialingCall()) {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400631 call->setDialNumber(text);
632 priv->q_completion_model->setCall(call);
633 }
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400634 } else {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400635 if (auto call = priv->q_completion_model->call()) {
636 if (call->lifeCycleState() == Call::LifeCycleState::CREATION)
637 call->performAction(Call::Action::REFUSE);
638 }
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400639 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400640}
641
642static gboolean
643completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
644 G_GNUC_UNUSED const gchar *key,
645 G_GNUC_UNUSED GtkTreeIter *iter,
646 G_GNUC_UNUSED RingMainWindow *win)
647{
648 /* the model is updated by lrc and should only every contain matching entries
649 * so always return TRUE */
650 return TRUE;
651}
652
653static QModelIndex
654get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
655 GtkTreeIter *filter_iter)
656{
657 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
658 GtkTreeIter child_iter;
659 gtk_tree_model_filter_convert_iter_to_child_iter(
660 GTK_TREE_MODEL_FILTER(filter_model),
661 &child_iter,
662 filter_iter);
663
664 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
665}
666
667static void
668autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
669 GtkCellRenderer *cell,
670 GtkTreeModel *model,
671 GtkTreeIter *iter,
672 G_GNUC_UNUSED gpointer user_data)
673{
674 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
675 if (idx.isValid()) {
676 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
677 if (photo_var.isValid()) {
678 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
679 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
680 20, 20,
681 GDK_INTERP_BILINEAR);
682
683 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
684 g_object_unref(scaled);
685 return;
686 }
687 }
688
689 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
690}
691
692static void
693autocompletion_name_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
694 GtkCellRenderer *cell,
695 GtkTreeModel *model,
696 GtkTreeIter *iter,
697 G_GNUC_UNUSED gpointer user_data)
698{
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400699 gchar *text = nullptr;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400700 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
701 if (idx.isValid()) {
702 QVariant name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400703 text = g_markup_printf_escaped("<span weight=\"bold\">%s</span>",
704 name.value<QString>().toUtf8().constData());
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400705 }
706
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400707 g_object_set(G_OBJECT(cell), "markup", text, NULL);
708 g_free(text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400709}
710
711static void
712autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
713 GtkCellRenderer *cell,
714 GtkTreeModel *model,
715 GtkTreeIter *iter,
716 G_GNUC_UNUSED gpointer user_data)
717{
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400718 gchar *text = nullptr;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400719 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
720 if (idx.isValid()) {
721 QVariant uri = idx.data(Qt::DisplayRole);
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400722 text = g_markup_escape_text(uri.value<QString>().toUtf8().constData(), -1);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400723 }
724
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400725 g_object_set(G_OBJECT(cell), "markup", text, NULL);
726 g_free(text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400727}
728
729static void
730autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
731 GtkCellRenderer *cell,
732 GtkTreeModel *model,
733 GtkTreeIter *iter,
734 G_GNUC_UNUSED gpointer user_data)
735{
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400736 gchar *text = nullptr;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400737 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
738 if (idx.isValid()) {
739 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400740 text = g_markup_printf_escaped("<span color=\"gray\">%s</span>",
741 alias.value<QString>().toUtf8().constData());
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400742 }
743
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400744 g_object_set(G_OBJECT(cell), "markup", text, NULL);
745 g_free(text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400746}
747
748static gboolean
749select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
750 GtkTreeModel *model,
751 GtkTreeIter *iter,
752 RingMainWindow *win)
753{
754 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
755
756 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
757 if (idx.isValid()) {
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400758 priv->q_completion_model->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
759 Call *call = priv->q_completion_model->call();
760 priv->q_completion_model->callSelectedNumber();
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400761
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400762 /* make this the currently selected call */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400763 QModelIndex idx = CallModel::instance().getIndex(call);
764 CallModel::instance().selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400765
766 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
767 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400768
769 /* clear the entry */
770 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400771 } else {
772 g_warning("autocompletion selection is not a valid index!");
773 }
774 return TRUE;
775}
776
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400777static gboolean
778dtmf_pressed(RingMainWindow *win,
779 GdkEventKey *event,
780 G_GNUC_UNUSED gpointer user_data)
781{
782 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
783
784 /* we want to react to digit key presses, as long as a GtkEntry is not the
785 * input focus
786 */
787 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
788 if (GTK_IS_ENTRY(focus))
789 return GDK_EVENT_PROPAGATE;
790
791 /* make sure that a call is selected*/
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400792 QItemSelectionModel *selection = CallModel::instance().selectionModel();
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400793 QModelIndex idx = selection->currentIndex();
794 if (!idx.isValid())
795 return GDK_EVENT_PROPAGATE;
796
797 /* make sure that the selected call is in progress */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400798 Call *call = CallModel::instance().getCall(idx);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400799 Call::LifeCycleState state = call->lifeCycleState();
800 if (state != Call::LifeCycleState::PROGRESS)
801 return GDK_EVENT_PROPAGATE;
802
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400803 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
804 * does not result in a 'c' being played.
805 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
806 if ( event->state
807 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
808 return GDK_EVENT_PROPAGATE;
809
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400810 /* pass the character that was entered to be played by the daemon;
811 * the daemon will filter out invalid DTMF characters */
812 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
813 QString val = QString::fromUcs4(&unicode_val, 1);
814 call->playDTMF(val);
815 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400816
817 /* always propogate the key, so we don't steal accelerators/shortcuts */
818 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400819}
820
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400821static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500822ring_main_window_init(RingMainWindow *win)
823{
824 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
825 gtk_widget_init_template(GTK_WIDGET(win));
826
827 /* set window icon */
828 GError *error = NULL;
829 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
830 if (icon == NULL) {
831 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400832 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500833 } else
834 gtk_window_set_icon(GTK_WINDOW(win), icon);
835
836 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400837 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400838 -1, 16, TRUE, &error);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400839 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500840 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400841 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500842 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400843 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500844
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400845 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500846 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
847 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400848 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500849 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500850
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400851 /* settings icon */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400852 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 -0400853
854 /* connect settings button signal */
855 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
856
857 /* add the call view to the main stack */
858 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
859 priv->vbox_call_view,
860 CALL_VIEW_NAME);
861
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400862 if (has_ring_account()) {
863 /* user has ring account, so show the call view right away */
864 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
865 } else {
866 /* user has to create the ring account */
867 show_account_creation(win);
868 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400869
870 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400871 priv->account_settings_view = account_view_new();
872 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
873
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400874 priv->media_settings_view = media_settings_view_new();
875 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 -0400876
Stepan Salenikovichde896112015-05-11 16:46:33 -0400877 priv->general_settings_view = general_settings_view_new();
878 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
879
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400880 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -0400881 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
882 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400883
884 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400885 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400886 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400887 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400888
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400889 /* populate the notebook */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400890 auto smart_view = recent_contacts_view_new();
891 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), smart_view);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400892
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400893 auto contacts_view = contacts_view_new();
894 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contacts), contacts_view);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400895
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400896 auto history_view = history_view_new();
897 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_history), history_view);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400898
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400899 /* welcome/default view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400900 priv->welcome_view = ring_welcome_view_new();
901 g_object_ref(priv->welcome_view);
902 // gtk_stack_add_named(GTK_STACK(priv->stack_call_view), welcome_view, DEFAULT_VIEW_NAME);
903 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
904 gtk_widget_show(priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500905
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500906 /* call/chat selection */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400907 QObject::connect(
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500908 RecentModel::instance().selectionModel(),
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400909 &QItemSelectionModel::currentChanged,
910 [win](const QModelIndex current, G_GNUC_UNUSED const QModelIndex & previous) {
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500911 if (auto call = RecentModel::instance().getActiveCall(current)) {
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400912 /* if the call is on hold, we want to put it off hold automatically
913 * when switching to it */
914 if (call->state() == Call::State::HOLD)
915 call << Call::Action::HOLD;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400916 }
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500917 selection_changed(current, win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400918 }
919 );
920
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500921 /* connect to dataChanged of the RecentModel to see if we need to change the view */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400922 QObject::connect(
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500923 &RecentModel::instance(),
924 &RecentModel::dataChanged,
925 [win](const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight, G_GNUC_UNUSED const QVector<int> & roles) {
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500926 /* it is possible for dataChanged to be emitted inside of a dataChanged handler or
927 * some other signal; since the connection is via a lambda, Qt would cause the
928 * handler to be called directly. This is not behaviour we usually want, so we call our
929 * function via g_idle so that it gets called after the initial handler is done.
930 */
931 if (topLeft == RecentModel::instance().selectionModel()->currentIndex())
932 g_idle_add((GSourceFunc)selected_item_changed, win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400933 }
934 );
935
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500936 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
937 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
938
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400939 /* autocompletion */
940 priv->q_completion_model = new NumberCompletionModel();
941
942 /* autocompletion renderers */
943 GtkCellArea *completion_area = gtk_cell_area_box_new();
944
945 /* photo renderer */
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400946 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400947 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
948 renderer,
949 TRUE, /* expand */
950 TRUE, /* align */
951 TRUE); /* fixed size */
952
953 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
954 renderer,
955 (GtkCellLayoutDataFunc)autocompletion_photo_render,
956 NULL, NULL);
957
958 /* name renderer */
959 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400960 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400961 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
962 renderer,
963 TRUE, /* expand */
964 TRUE, /* align */
965 TRUE); /* fixed size */
966
967 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
968 renderer,
969 (GtkCellLayoutDataFunc)autocompletion_name_render,
970 NULL, NULL);
971
972 /* number renderer */
973 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400974 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400975 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
976 renderer,
977 TRUE, /* expand */
978 TRUE, /* align */
979 TRUE); /* fixed size */
980
981 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
982 renderer,
983 (GtkCellLayoutDataFunc)autocompletion_number_render,
984 NULL, NULL);
985 /* account renderer */
986 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400987 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400988 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
989 renderer,
990 TRUE, /* expand */
991 TRUE, /* align */
992 TRUE); /* fixed size */
993
994 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
995 renderer,
996 (GtkCellLayoutDataFunc)autocompletion_account_render,
997 NULL, NULL);
998
999 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1000
1001 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1002 (QAbstractItemModel *)priv->q_completion_model,
1003 1,
1004 Qt::DisplayRole, G_TYPE_STRING);
1005
1006 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1007
1008 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1009 gtk_entry_completion_set_match_func(
1010 entry_completion,
1011 (GtkEntryCompletionMatchFunc) completion_match_func,
1012 NULL,
1013 NULL);
1014
1015 /* connect signal to when text is entered in the entry */
1016 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1017 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001018
1019 /* connect to incoming call and focus */
1020 QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001021 &CallModel::instance(),
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001022 &CallModel::incomingCall,
1023 [=](Call* call) {
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001024 CallModel::instance().selectionModel()->setCurrentIndex(
1025 CallModel::instance().getIndex(call), QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001026 }
1027 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001028
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001029 /* react to digit key press events */
1030 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich48b45c12015-09-25 15:49:51 -04001031
1032 /* set the search entry placeholder text */
1033 gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
1034 C_("Please try to make the translation 50 chars or less so that it fits into the layout", "Search contacts or enter number"));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001035}
1036
1037static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001038ring_main_window_dispose(GObject *object)
1039{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001040 RingMainWindow *self = RING_MAIN_WINDOW(object);
1041 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1042
1043 QObject::disconnect(priv->selection_updated);
1044
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001045 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1046}
1047
1048static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001049ring_main_window_finalize(GObject *object)
1050{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001051 RingMainWindow *self = RING_MAIN_WINDOW(object);
1052 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1053
1054 delete priv->q_contact_model;
1055 delete priv->q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001056 delete priv->q_completion_model;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001057
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001058 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001059}
1060
1061static void
1062ring_main_window_class_init(RingMainWindowClass *klass)
1063{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001064 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001065 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001066
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001067 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1068 "/cx/ring/RingGnome/ringmainwindow.ui");
1069
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001070 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001071 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_smartview);
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001072 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contacts);
1073 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_history);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001074 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1075 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1076 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1077 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001078 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001079 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001080 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001081 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001082 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, frame_call);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001083 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001084 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001085 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001086 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001087
1088 /* account creation */
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -05001089 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001090 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -05001091 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_account_creation_entry);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001092 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -05001093 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_default_name);
1094 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_paceholder);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001095 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1096 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1097 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001098}
1099
1100GtkWidget *
1101ring_main_window_new (GtkApplication *app)
1102{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001103 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1104
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001105 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001106}