blob: 829626b9b604b69cca52928f49e98b6f993713a3 [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -04002 * Copyright (C) 2015 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.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040024 * terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050025 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "ringmainwindow.h"
32
33#include <gtk/gtk.h>
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040034#include <glib/gi18n.h>
Stepan Salenikovicha3557452015-02-20 14:14:12 -050035#include "models/gtkqtreemodel.h"
36#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050037#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050038#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050039#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050040#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050041#include <string.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040042#include <contactmethod.h>
Stepan Salenikovich7ff47962015-03-16 15:10:14 -040043#include <QtCore/QSortFilterProxyModel>
44#include "models/gtkqsortfiltertreemodel.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040045#include "accountview.h"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040046#include <accountmodel.h>
47#include <audio/codecmodel.h>
Stepan Salenikovichbd029582015-03-24 11:00:56 -040048#include "dialogs.h"
Stepan Salenikovich0bd53492015-05-11 14:28:52 -040049#include "mediasettingsview.h"
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040050#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040051#include <personmodel.h>
52#include "utils/drawing.h"
53#include <memory>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040054#include <globalinstances.h>
55#include "native/pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040056#include "models/activeitemproxymodel.h"
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -040057#include <numbercompletionmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040058#include "utils/calling.h"
59#include "contactsview.h"
Stepan Salenikovich9d294492015-05-14 16:34:24 -040060#include <categorizedcontactmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040061#include "historyview.h"
62#include "utils/models.h"
Stepan Salenikovichde896112015-05-11 16:46:33 -040063#include "generalsettingsview.h"
Stepan Salenikovich75a39172015-07-10 13:21:08 -040064#include "utils/accounts.h"
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040065#include "ringwelcomeview.h"
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040066#include "recentcontactsview.h"
Stepan Salenikovichc1323422016-01-06 10:54:44 -050067#include <recentmodel.h>
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050068#include "chatview.h"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040069
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040070static constexpr const char* CALL_VIEW_NAME = "calls";
71static constexpr const char* CREATE_ACCOUNT_1_VIEW_NAME = "create1";
72static constexpr const char* CREATE_ACCOUNT_2_VIEW_NAME = "create2";
73static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
74static constexpr const char* AUDIO_SETTINGS_VIEW_NAME = "audio";
75static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
76static constexpr const char* ACCOUNT_SETTINGS_VIEW_NAME = "accounts";
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040077static constexpr const char* DEFAULT_VIEW_NAME = "welcome";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040078static constexpr const char* VIEW_CONTACTS = "contacts";
79static constexpr const char* VIEW_HISTORY = "history";
80static constexpr const char* VIEW_PRESENCE = "presence";
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050081
82struct _RingMainWindow
83{
84 GtkApplicationWindow parent;
85};
86
87struct _RingMainWindowClass
88{
89 GtkApplicationWindowClass parent_class;
90};
91
92typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
93
94struct _RingMainWindowPrivate
95{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040096 GtkWidget *ring_menu;
97 GtkWidget *image_ring;
98 GtkWidget *ring_settings;
99 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400100 GtkWidget *hbox_settings;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400101 GtkWidget *scrolled_window_smartview;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400102 GtkWidget *scrolled_window_contacts;
103 GtkWidget *scrolled_window_history;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400104 GtkWidget *vbox_left_pane;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500105 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500106 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400107 GtkWidget *vbox_call_view;
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400108 GtkWidget *frame_call;
109 GtkWidget *welcome_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500110 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400111 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400112 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400113 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400114 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400115 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400116 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400117 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400118
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400119 QMetaObject::Connection selection_updated;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400120
121 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400122
123 /* account creation */
124 GtkWidget *account_creation_1;
125 GtkWidget *image_ring_logo;
126 GtkWidget *label_enter_alias;
127 GtkWidget *entry_alias;
128 GtkWidget *label_generating_account;
129 GtkWidget *spinner_generating_account;
130 GtkWidget *button_account_creation_next;
131 GtkWidget *account_creation_2;
132 GtkWidget *entry_hash;
133 GtkWidget *button_account_creation_done;
134
135 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400136
137 /* allocd qmodels */
138 ActiveItemProxyModel *q_contact_model;
139 QSortFilterProxyModel *q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400140 NumberCompletionModel *q_completion_model;
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500141
142 /* fullscreen */
143 gboolean is_fullscreen;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500144};
145
146G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
147
148#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
149
Stepan Salenikovich69771842015-02-24 18:11:45 -0500150static void
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500151enter_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_hide(priv->vbox_left_pane);
158 gtk_window_fullscreen(GTK_WINDOW(self));
159 priv->is_fullscreen = TRUE;
160 }
161}
162
163static void
164leave_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_show(priv->vbox_left_pane);
171 gtk_window_unfullscreen(GTK_WINDOW(self));
172 priv->is_fullscreen = FALSE;
173 }
174}
175
176static void
Stepan Salenikovich5e431cf2015-12-24 14:09:01 -0500177video_double_clicked(G_GNUC_UNUSED CurrentCallView *view, RingMainWindow *self)
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500178{
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 leave_full_screen(self);
184 } else {
185 enter_full_screen(self);
186 }
187}
188
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500189/**
190 * This takes the RecentModel index as the argument and displays the corresponding view:
191 * - incoming call view
192 * - current call view
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500193 * - chat view
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500194 * - welcome view (if no index is selected)
195 */
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500196static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500197selection_changed(const QModelIndex& recent_idx, RingMainWindow *win)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500198{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500199 // g_debug("selection changed");
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400200 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500201 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
202
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500203 /* if we're showing the settings, then nothing needs to be done as the call
204 view is not shown */
205 if (priv->show_settings) return;
206
Stepan Salenikovich69771842015-02-24 18:11:45 -0500207 /* get the current visible stack child */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400208 GtkWidget *old_call_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500209
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500210 /* make sure we leave full screen, since the call selection is changing */
211 leave_full_screen(win);
212
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500213 /* check which object type is selected */
214 auto type = recent_idx.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>();
215 auto object = recent_idx.data(static_cast<int>(Ring::Role::Object));
216 /* try to get the call model index, in case its a call, since we're still using the CallModel as well */
217 auto call_idx = CallModel::instance().getIndex(RecentModel::instance().getActiveCall(recent_idx));
218
219 /* we prioritize showing the call view */
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500220 if (call_idx.isValid()) {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500221 /* show the call view */
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500222 QVariant state = call_idx.data(static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500223 GtkWidget *new_call_view = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500224
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400225 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400226 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500227 case Call::LifeCycleState::INITIALIZATION:
228 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500229 new_call_view = incoming_call_view_new();
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500230 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), call_idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500231 break;
232 case Call::LifeCycleState::PROGRESS:
233 new_call_view = current_call_view_new();
Stepan Salenikovich5e431cf2015-12-24 14:09:01 -0500234 g_signal_connect(new_call_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win);
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500235 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), call_idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500236 break;
237 case Call::LifeCycleState::COUNT__:
238 g_warning("LifeCycleState should never be COUNT");
239 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500240 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500241
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400242 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
243 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_call_view);
244 gtk_widget_show(new_call_view);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500245 } else if (type == Ring::ObjectType::Person && object.isValid()) {
246 /* show chat view constructed from Person object */
247 auto new_chat_view = chat_view_new_person(object.value<Person *>());
248 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
249 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_chat_view);
250 gtk_widget_show(new_chat_view);
251 } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) {
252 /* show chat view constructed from CM */
253 auto new_chat_view = chat_view_new_cm(object.value<ContactMethod *>());
254 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
255 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_chat_view);
256 gtk_widget_show(new_chat_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500257 } else {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500258 /* nothing selected that we can display, show the welcome view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400259 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
260 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500261 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500262}
263
264static void
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500265item_changed(const QModelIndex& recent_idx, RingMainWindow *win)
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500266{
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500267 // g_debug("item changed");
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500268 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
269
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500270 /* if we're showing the settings, then nothing needs to be done as the call
271 view is not shown */
272 if (priv->show_settings) return;
273
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500274 /* check if the item that changed is the same as the one selected / displayed */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500275 auto idx_selected = RecentModel::instance().selectionModel()->currentIndex();
276
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500277 if (idx_selected != recent_idx) return;
278
279 /* we prioritize showing the call view; but if the call is over we go back to showing the chat view */
280 if(auto call = RecentModel::instance().getActiveCall(idx_selected)) {
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500281 /* check if we need to change the view */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500282 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400283 QVariant state = CallModel::instance().data(idx_selected, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500284
285 /* check what the current state is vs what is displayed */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400286 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400287 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500288 case Call::LifeCycleState::FINISHED:
289 /* go back to incoming call view;
290 * it will show that the call failed and offer to hang it up */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500291 case Call::LifeCycleState::INITIALIZATION:
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500292 {
293 /* show the incoming call view */
294 if (!IS_INCOMING_CALL_VIEW(current_view)) {
295 auto new_view = incoming_call_view_new();
296 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_view), CallModel::instance().getIndex(call));
297 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
298 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
299 gtk_widget_show(new_view);
300 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500301 }
302 break;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500303 case Call::LifeCycleState::PROGRESS:
304 {
305 /* show the current call view */
306 if (!IS_CURRENT_CALL_VIEW(current_view)) {
307 auto new_view = current_call_view_new();
308 g_signal_connect(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win);
309 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_view), CallModel::instance().getIndex(call));
310 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
311 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
312 gtk_widget_show(new_view);
313 }
314 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500315 break;
316 case Call::LifeCycleState::COUNT__:
317 g_warning("LifeCycleState should never be COUNT");
318 break;
319 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500320 } else if (idx_selected.isValid()) {
321 /* otherwise, the call is over and is already removed from the RecentModel */
322 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
323 leave_full_screen(win);
324
325 /* show the chat view */
326 if (!IS_CHAT_VIEW(current_view)) {
327 auto type = idx_selected.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>();
328 auto object = idx_selected.data(static_cast<int>(Ring::Role::Object));
329 if (type == Ring::ObjectType::Person && object.isValid()) {
330 /* show chat view constructed from Person object */
331 auto new_view = chat_view_new_person(object.value<Person *>());
332 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
333 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
334 gtk_widget_show(new_view);
335 } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) {
336 /* show chat view constructed from CM */
337 auto new_view = chat_view_new_cm(object.value<ContactMethod *>());
338 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
339 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
340 gtk_widget_show(new_view);
341 }
342 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500343 }
344}
345
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500346static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500347search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
348{
349 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
350
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400351 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500352
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400353 if (number_entered && strlen(number_entered) > 0) {
354 /* detect Ring hash */
355 gboolean is_ring_hash = FALSE;
356 if (strlen(number_entered) == 40) {
357 is_ring_hash = TRUE;
358 /* must be 40 chars long and alphanumeric */
359 for (int i = 0; i < 40 && is_ring_hash; ++i) {
360 if (!g_ascii_isalnum(number_entered[i]))
361 is_ring_hash = FALSE;
362 }
363 }
364
365 QString number = QString{number_entered};
366
367 if (is_ring_hash)
368 number = "ring:" + number;
369
370 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400371
372 Call *call = priv->q_completion_model->call();
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500373 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500374 call->performAction(Call::Action::ACCEPT);
375
376 /* make this the currently selected call */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400377 QModelIndex idx = CallModel::instance().getIndex(call);
378 CallModel::instance().selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400379
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400380 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
381 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
382 /* clear the entry */
383 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
384 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500385}
386
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400387static gboolean
388grab_focus_on_widget(GtkWidget *widget)
Stepan Salenikovich15142182015-03-11 17:15:26 -0400389{
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400390 gtk_widget_grab_focus(widget);
391 return G_SOURCE_REMOVE;
Stepan Salenikovich15142182015-03-11 17:15:26 -0400392}
393
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400394static gboolean
395save_accounts(GtkWidget *working_dialog)
396{
397 /* save changes to accounts */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400398 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400399
400 if (working_dialog)
401 gtk_widget_destroy(working_dialog);
402
403 return G_SOURCE_REMOVE;
404}
405
Stepan Salenikovich15142182015-03-11 17:15:26 -0400406static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400407settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
408{
409 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
410 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
411
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400412 /* check which view to show */
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500413 if (!priv->show_settings) {
414 /* show the settings */
415
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500416 /* make sure we are not showing a call view so we don't have more than one clutter stage at a time */
417 selection_changed(QModelIndex(), win);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500418
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400419 /* show settings */
420 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
421
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400422 gtk_widget_show(priv->hbox_settings);
423
Stepan Salenikovich41118912015-05-01 11:25:46 -0400424 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400425 if (priv->last_settings_view == priv->media_settings_view)
426 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400427
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400428 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400429 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500430
431 priv->show_settings = TRUE;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400432 } else {
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500433 /* hide the settings */
434 priv->show_settings = FALSE;
435
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400436 /* show working dialog in case save operation takes time */
437 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
438 gtk_window_present(GTK_WINDOW(working));
439
440 /* now save after the time it takes to transition back to the call view (400ms)
441 * the save doesn't happen before the "working" dialog is presented
442 * the timeout function should destroy the "working" dialog when done saving
443 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400444 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400445
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400446 /* show calls */
447 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
448
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400449 gtk_widget_hide(priv->hbox_settings);
450
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500451 /* make sure video preview is stopped, in case it was started */
452 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
453
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400454 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
455 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400456
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500457 /* show the view which was selected previously */
458 selection_changed(RecentModel::instance().selectionModel()->currentIndex(), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400459 }
460}
461
462static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400463show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400464{
465 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
466 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
467
468 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400469 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400470 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400471 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400472 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400473 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400474 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400475 }
476}
477
478static void
479show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
480{
481 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
482 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
483
484 if (gtk_toggle_button_get_active(navbutton)) {
485 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
486 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
487 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400488 }
489}
490
Stepan Salenikovichde896112015-05-11 16:46:33 -0400491static void
492show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
493{
494 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
495 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
496
497 if (gtk_toggle_button_get_active(navbutton)) {
498 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
499 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
500 priv->last_settings_view = priv->general_settings_view;
501 }
502}
503
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400504static gboolean
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400505create_ring_account(RingMainWindow *win)
506{
507 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
508 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
509
510 /* create account and set UPnP enabled, as its not by default in the daemon */
511 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400512 Account *account = AccountModel::instance().add(alias, Account::Protocol::RING);
Stepan Salenikovich75a39172015-07-10 13:21:08 -0400513 account->setDisplayName(alias); // set the display name to the same as the alias
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400514 account->setUpnpEnabled(TRUE);
515
516 /* wait for hash to be generated to show the next view */
517 priv->hash_updated = QObject::connect(
518 account,
519 &Account::changed,
520 [=] (Account *a) {
521 QString hash = a->username();
522 if (!hash.isEmpty()) {
523 /* set the hash */
524 gtk_entry_set_text(GTK_ENTRY(priv->entry_hash), hash.toUtf8().constData());
525
526 /* show the next accont creation view */
527 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
528 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_2_VIEW_NAME);
529
530 /* select the hash text */
531 gtk_widget_grab_focus(priv->entry_hash);
532 gtk_editable_select_region(GTK_EDITABLE(priv->entry_hash), 0, -1);
533 }
534 }
535 );
536
537 account->performAction(Account::EditAction::SAVE);
538
539 return G_SOURCE_REMOVE;
540}
541
542static void
543alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
544{
545 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
546
547 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
548 if (strlen(alias) > 0) {
549 /* enable "next" button */
550 gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
551 } else {
552 /* disable "next" button, as we require an alias */
553 gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
554 }
555}
556
557static void
558account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
559{
560 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
561
562 /* show/hide relevant widgets */
563 gtk_widget_hide(priv->label_enter_alias);
564 gtk_widget_hide(priv->entry_alias);
565 gtk_widget_hide(priv->button_account_creation_next);
566 gtk_widget_show(priv->label_generating_account);
567 gtk_widget_show(priv->spinner_generating_account);
568
569 /* now create account after a short timeout so that the the save doesn't
570 * happen freeze the client before the widget changes happen;
571 * the timeout function should then display the next step in account creation
572 */
573 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
574}
575
576static void
577account_creation_done_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
578{
579 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
580
581 QObject::disconnect(priv->hash_updated);
582
583 /* show the call view */
584 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
585 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
586
Stepan Salenikovich64505672015-09-24 10:46:07 -0400587 /* show the settings button*/
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400588 gtk_widget_show(priv->ring_settings);
589
590}
591
592static void
593entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
594{
595 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
596
597 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
598 if (strlen(alias) > 0)
599 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
600}
601
602static void
603show_account_creation(RingMainWindow *win)
604{
605 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
606
607 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
608 priv->account_creation_1,
609 CREATE_ACCOUNT_1_VIEW_NAME);
610
611 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
612 priv->account_creation_2,
613 CREATE_ACCOUNT_2_VIEW_NAME);
614
Stepan Salenikovich64505672015-09-24 10:46:07 -0400615 /* hide settings button until account creation is complete */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400616 gtk_widget_hide(priv->ring_settings);
617
618 /* set ring logo */
619 GError *error = NULL;
620 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
621 -1, 75, TRUE, &error);
622 if (logo_ring == NULL) {
623 g_debug("Could not load logo: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400624 g_clear_error(&error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400625 } else
626 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
627
628 /* style of alias and hash entry; give them a larger font */
629 gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
630 gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
631
632 /* connect signals */
633 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
634 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
635 g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
636 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
637 g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
638
639 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
640}
641
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400642static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400643search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
644{
645 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
646
647 /* get the text from the entry */
648 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
649
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400650 if (text && strlen(text) > 0) {
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400651 /* edit the the dialing call (or create a new one) */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400652 if (auto call = CallModel::instance().dialingCall()) {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400653 call->setDialNumber(text);
654 priv->q_completion_model->setCall(call);
655 }
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400656 } else {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400657 if (auto call = priv->q_completion_model->call()) {
658 if (call->lifeCycleState() == Call::LifeCycleState::CREATION)
659 call->performAction(Call::Action::REFUSE);
660 }
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400661 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400662}
663
664static gboolean
665completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
666 G_GNUC_UNUSED const gchar *key,
667 G_GNUC_UNUSED GtkTreeIter *iter,
668 G_GNUC_UNUSED RingMainWindow *win)
669{
670 /* the model is updated by lrc and should only every contain matching entries
671 * so always return TRUE */
672 return TRUE;
673}
674
675static QModelIndex
676get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
677 GtkTreeIter *filter_iter)
678{
679 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
680 GtkTreeIter child_iter;
681 gtk_tree_model_filter_convert_iter_to_child_iter(
682 GTK_TREE_MODEL_FILTER(filter_model),
683 &child_iter,
684 filter_iter);
685
686 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
687}
688
689static void
690autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
691 GtkCellRenderer *cell,
692 GtkTreeModel *model,
693 GtkTreeIter *iter,
694 G_GNUC_UNUSED gpointer user_data)
695{
696 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
697 if (idx.isValid()) {
698 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
699 if (photo_var.isValid()) {
700 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
701 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
702 20, 20,
703 GDK_INTERP_BILINEAR);
704
705 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
706 g_object_unref(scaled);
707 return;
708 }
709 }
710
711 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
712}
713
714static void
715autocompletion_name_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
716 GtkCellRenderer *cell,
717 GtkTreeModel *model,
718 GtkTreeIter *iter,
719 G_GNUC_UNUSED gpointer user_data)
720{
721 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
722 if (idx.isValid()) {
723 QVariant name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
Stepan Salenikovich92189cc2015-09-24 10:59:22 -0400724 gchar *text = g_strdup_printf("<span weight=\"bold\">%s</span>",
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400725 name.value<QString>().toUtf8().constData());
726
727 g_object_set(G_OBJECT(cell), "markup", text, NULL);
728 g_free(text);
729 return;
730 }
731
732 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
733}
734
735static void
736autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
737 GtkCellRenderer *cell,
738 GtkTreeModel *model,
739 GtkTreeIter *iter,
740 G_GNUC_UNUSED gpointer user_data)
741{
742 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
743 if (idx.isValid()) {
744 QVariant uri = idx.data(Qt::DisplayRole);
Stepan Salenikovich92189cc2015-09-24 10:59:22 -0400745 gchar *text = g_strdup_printf("%s", uri.value<QString>().toUtf8().constData());
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400746
747 g_object_set(G_OBJECT(cell), "markup", text, NULL);
748 g_free(text);
749 return;
750 }
751
752 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
753}
754
755static void
756autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
757 GtkCellRenderer *cell,
758 GtkTreeModel *model,
759 GtkTreeIter *iter,
760 G_GNUC_UNUSED gpointer user_data)
761{
762 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
763 if (idx.isValid()) {
764 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
Stepan Salenikovich92189cc2015-09-24 10:59:22 -0400765 gchar *text = g_strdup_printf("<span color=\"gray\">%s</span>",
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400766 alias.value<QString>().toUtf8().constData());
767
768 g_object_set(G_OBJECT(cell), "markup", text, NULL);
769 g_free(text);
770 return;
771 }
772
773 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
774}
775
776static gboolean
777select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
778 GtkTreeModel *model,
779 GtkTreeIter *iter,
780 RingMainWindow *win)
781{
782 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
783
784 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
785 if (idx.isValid()) {
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400786 priv->q_completion_model->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
787 Call *call = priv->q_completion_model->call();
788 priv->q_completion_model->callSelectedNumber();
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400789
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400790 /* make this the currently selected call */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400791 QModelIndex idx = CallModel::instance().getIndex(call);
792 CallModel::instance().selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400793
794 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
795 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400796
797 /* clear the entry */
798 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400799 } else {
800 g_warning("autocompletion selection is not a valid index!");
801 }
802 return TRUE;
803}
804
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400805static gboolean
806dtmf_pressed(RingMainWindow *win,
807 GdkEventKey *event,
808 G_GNUC_UNUSED gpointer user_data)
809{
810 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
811
812 /* we want to react to digit key presses, as long as a GtkEntry is not the
813 * input focus
814 */
815 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
816 if (GTK_IS_ENTRY(focus))
817 return GDK_EVENT_PROPAGATE;
818
819 /* make sure that a call is selected*/
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400820 QItemSelectionModel *selection = CallModel::instance().selectionModel();
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400821 QModelIndex idx = selection->currentIndex();
822 if (!idx.isValid())
823 return GDK_EVENT_PROPAGATE;
824
825 /* make sure that the selected call is in progress */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400826 Call *call = CallModel::instance().getCall(idx);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400827 Call::LifeCycleState state = call->lifeCycleState();
828 if (state != Call::LifeCycleState::PROGRESS)
829 return GDK_EVENT_PROPAGATE;
830
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400831 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
832 * does not result in a 'c' being played.
833 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
834 if ( event->state
835 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
836 return GDK_EVENT_PROPAGATE;
837
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400838 /* pass the character that was entered to be played by the daemon;
839 * the daemon will filter out invalid DTMF characters */
840 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
841 QString val = QString::fromUcs4(&unicode_val, 1);
842 call->playDTMF(val);
843 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400844
845 /* always propogate the key, so we don't steal accelerators/shortcuts */
846 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400847}
848
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400849static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500850ring_main_window_init(RingMainWindow *win)
851{
852 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
853 gtk_widget_init_template(GTK_WIDGET(win));
854
855 /* set window icon */
856 GError *error = NULL;
857 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
858 if (icon == NULL) {
859 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400860 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500861 } else
862 gtk_window_set_icon(GTK_WINDOW(win), icon);
863
864 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400865 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
866 -1, 24, TRUE, &error);
867 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500868 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400869 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500870 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400871 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500872
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400873 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500874 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
875 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400876 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500877 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500878
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400879 /* settings icon */
880 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
881
882 /* connect settings button signal */
883 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
884
885 /* add the call view to the main stack */
886 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
887 priv->vbox_call_view,
888 CALL_VIEW_NAME);
889
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400890 if (has_ring_account()) {
891 /* user has ring account, so show the call view right away */
892 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
893 } else {
894 /* user has to create the ring account */
895 show_account_creation(win);
896 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400897
898 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400899 priv->account_settings_view = account_view_new();
900 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
901
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400902 priv->media_settings_view = media_settings_view_new();
903 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 -0400904
Stepan Salenikovichde896112015-05-11 16:46:33 -0400905 priv->general_settings_view = general_settings_view_new();
906 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
907
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400908 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -0400909 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
910 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400911
912 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400913 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400914 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400915 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400916
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400917 /* populate the notebook */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400918 auto smart_view = recent_contacts_view_new();
919 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), smart_view);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400920
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400921 auto contacts_view = contacts_view_new();
922 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contacts), contacts_view);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400923
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400924 auto history_view = history_view_new();
925 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_history), history_view);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400926
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400927 /* welcome/default view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400928 priv->welcome_view = ring_welcome_view_new();
929 g_object_ref(priv->welcome_view);
930 // gtk_stack_add_named(GTK_STACK(priv->stack_call_view), welcome_view, DEFAULT_VIEW_NAME);
931 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
932 gtk_widget_show(priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500933
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500934 /* call/chat selection */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400935 QObject::connect(
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500936 RecentModel::instance().selectionModel(),
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400937 &QItemSelectionModel::currentChanged,
938 [win](const QModelIndex current, G_GNUC_UNUSED const QModelIndex & previous) {
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500939 if (auto call = RecentModel::instance().getActiveCall(current)) {
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400940 /* if the call is on hold, we want to put it off hold automatically
941 * when switching to it */
942 if (call->state() == Call::State::HOLD)
943 call << Call::Action::HOLD;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400944 }
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500945 selection_changed(current, win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400946 }
947 );
948
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500949 /* connect to dataChanged of the RecentModel to see if we need to change the view */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400950 QObject::connect(
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500951 &RecentModel::instance(),
952 &RecentModel::dataChanged,
953 [win](const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight, G_GNUC_UNUSED const QVector<int> & roles) {
954 item_changed(topLeft, win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400955 }
956 );
957
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500958 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
959 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
960
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400961 /* autocompletion */
962 priv->q_completion_model = new NumberCompletionModel();
963
964 /* autocompletion renderers */
965 GtkCellArea *completion_area = gtk_cell_area_box_new();
966
967 /* photo renderer */
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400968 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400969 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
970 renderer,
971 TRUE, /* expand */
972 TRUE, /* align */
973 TRUE); /* fixed size */
974
975 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
976 renderer,
977 (GtkCellLayoutDataFunc)autocompletion_photo_render,
978 NULL, NULL);
979
980 /* name renderer */
981 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400982 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400983 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
984 renderer,
985 TRUE, /* expand */
986 TRUE, /* align */
987 TRUE); /* fixed size */
988
989 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
990 renderer,
991 (GtkCellLayoutDataFunc)autocompletion_name_render,
992 NULL, NULL);
993
994 /* number renderer */
995 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400996 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400997 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
998 renderer,
999 TRUE, /* expand */
1000 TRUE, /* align */
1001 TRUE); /* fixed size */
1002
1003 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1004 renderer,
1005 (GtkCellLayoutDataFunc)autocompletion_number_render,
1006 NULL, NULL);
1007 /* account renderer */
1008 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -04001009 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001010 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1011 renderer,
1012 TRUE, /* expand */
1013 TRUE, /* align */
1014 TRUE); /* fixed size */
1015
1016 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1017 renderer,
1018 (GtkCellLayoutDataFunc)autocompletion_account_render,
1019 NULL, NULL);
1020
1021 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1022
1023 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1024 (QAbstractItemModel *)priv->q_completion_model,
1025 1,
1026 Qt::DisplayRole, G_TYPE_STRING);
1027
1028 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1029
1030 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1031 gtk_entry_completion_set_match_func(
1032 entry_completion,
1033 (GtkEntryCompletionMatchFunc) completion_match_func,
1034 NULL,
1035 NULL);
1036
1037 /* connect signal to when text is entered in the entry */
1038 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1039 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001040
1041 /* connect to incoming call and focus */
1042 QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001043 &CallModel::instance(),
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001044 &CallModel::incomingCall,
1045 [=](Call* call) {
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001046 CallModel::instance().selectionModel()->setCurrentIndex(
1047 CallModel::instance().getIndex(call), QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001048 }
1049 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001050
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001051 /* react to digit key press events */
1052 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich48b45c12015-09-25 15:49:51 -04001053
1054 /* set the search entry placeholder text */
1055 gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
1056 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 -05001057}
1058
1059static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001060ring_main_window_dispose(GObject *object)
1061{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001062 RingMainWindow *self = RING_MAIN_WINDOW(object);
1063 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1064
1065 QObject::disconnect(priv->selection_updated);
1066
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001067 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1068}
1069
1070static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001071ring_main_window_finalize(GObject *object)
1072{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001073 RingMainWindow *self = RING_MAIN_WINDOW(object);
1074 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1075
1076 delete priv->q_contact_model;
1077 delete priv->q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001078 delete priv->q_completion_model;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001079
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001080 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001081}
1082
1083static void
1084ring_main_window_class_init(RingMainWindowClass *klass)
1085{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001086 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001087 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001088
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001089 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1090 "/cx/ring/RingGnome/ringmainwindow.ui");
1091
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001092 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001093 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_smartview);
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001094 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contacts);
1095 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_history);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001096 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1097 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1098 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1099 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001100 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001101 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001102 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001103 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001104 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, frame_call);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001105 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001106 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001107 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001108 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001109
1110 /* account creation */
1111 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
1112 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
1113 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
1114 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
1115 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1116 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1117 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
1118
1119 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
1120 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
1121 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001122}
1123
1124GtkWidget *
1125ring_main_window_new (GtkApplication *app)
1126{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001127 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1128
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001129 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001130}