blob: 40351c1d39630c5fd02e054df8ea6a232c7bc69e [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * 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
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * 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 Salenikovicha3557452015-02-20 14:14:12 -050034#include "models/gtkqtreemodel.h"
35#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050036#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050037#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050038#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050039#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050040#include <string.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040041#include <contactmethod.h>
Stepan Salenikovich7ff47962015-03-16 15:10:14 -040042#include <QtCore/QSortFilterProxyModel>
43#include "models/gtkqsortfiltertreemodel.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040044#include "accountview.h"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040045#include <accountmodel.h>
46#include <audio/codecmodel.h>
Stepan Salenikovichbd029582015-03-24 11:00:56 -040047#include "dialogs.h"
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040048#include "videosettingsview.h"
49#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040050#include <personmodel.h>
51#include "utils/drawing.h"
52#include <memory>
53#include "delegates/pixbufdelegate.h"
54#include "models/activeitemproxymodel.h"
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -040055#include <numbercompletionmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040056#include "utils/calling.h"
57#include "contactsview.h"
58#include "historyview.h"
59#include "utils/models.h"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040060
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040061#define CALL_VIEW_NAME "calls"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040062#define CREATE_ACCOUNT_1_VIEW_NAME "create1"
63#define CREATE_ACCOUNT_2_VIEW_NAME "create2"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040064#define GENERAL_SETTINGS_VIEW_NAME "general"
65#define AUDIO_SETTINGS_VIEW_NAME "audio"
66#define VIDEO_SETTINGS_VIEW_NAME "video"
67#define ACCOUNT_SETTINGS_VIEW_NAME "accounts"
Stepan Salenikovich69771842015-02-24 18:11:45 -050068#define DEFAULT_VIEW_NAME "placeholder"
Stepan Salenikovich15142182015-03-11 17:15:26 -040069#define VIEW_CONTACTS "contacts"
70#define VIEW_HISTORY "history"
71#define VIEW_PRESENCE "presence"
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050072
73struct _RingMainWindow
74{
75 GtkApplicationWindow parent;
76};
77
78struct _RingMainWindowClass
79{
80 GtkApplicationWindowClass parent_class;
81};
82
83typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
84
85struct _RingMainWindowPrivate
86{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040087 GtkWidget *ring_menu;
88 GtkWidget *image_ring;
89 GtkWidget *ring_settings;
90 GtkWidget *image_settings;
91 GtkWidget *hbox_search;
92 GtkWidget *hbox_settings;
Stepan Salenikovich15142182015-03-11 17:15:26 -040093 GtkWidget *stack_contacts_history_presence;
94 GtkWidget *radiobutton_contacts;
95 GtkWidget *radiobutton_history;
96 GtkWidget *radiobutton_presence;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050097 GtkWidget *treeview_call;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050098 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -050099 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400100 GtkWidget *vbox_call_view;
101 GtkWidget *stack_call_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500102 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400103 GtkWidget *account_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400104 GtkWidget *video_settings_view;
105 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400106 GtkWidget *radiobutton_audio_settings;
107 GtkWidget *radiobutton_general_settings;
108 GtkWidget *radiobutton_video_settings;
109 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400110 GtkWidget *label_ring_id;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400111
112 Account *active_ring_account;
113 QMetaObject::Connection active_ring_account_updates;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400114
115 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400116
117 /* account creation */
118 GtkWidget *account_creation_1;
119 GtkWidget *image_ring_logo;
120 GtkWidget *label_enter_alias;
121 GtkWidget *entry_alias;
122 GtkWidget *label_generating_account;
123 GtkWidget *spinner_generating_account;
124 GtkWidget *button_account_creation_next;
125 GtkWidget *account_creation_2;
126 GtkWidget *entry_hash;
127 GtkWidget *button_account_creation_done;
128
129 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400130
131 /* allocd qmodels */
132 ActiveItemProxyModel *q_contact_model;
133 QSortFilterProxyModel *q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400134 NumberCompletionModel *q_completion_model;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500135};
136
137G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
138
139#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
140
Stepan Salenikovich69771842015-02-24 18:11:45 -0500141static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500142update_call_model_selection(GtkTreeSelection *selection, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500143{
Stepan Salenikovich69771842015-02-24 18:11:45 -0500144 QModelIndex current = get_index_from_selection(selection);
145 if (current.isValid())
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500146 CallModel::instance()->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500147 else
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500148 CallModel::instance()->selectionModel()->clearCurrentIndex();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500149}
150
151static void
152call_selection_changed(GtkTreeSelection *selection, gpointer win)
153{
154 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
155
156 /* get the current visible stack child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400157 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500158
159 QModelIndex idx = get_index_from_selection(selection);
160 if (idx.isValid()) {
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400161 QVariant state = CallModel::instance()->data(idx, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500162 GtkWidget *new_call_view = NULL;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500163 char* new_call_view_name = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500164
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400165 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400166 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500167 case Call::LifeCycleState::INITIALIZATION:
168 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500169 new_call_view = incoming_call_view_new();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500170 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500171 /* use the pointer of the call as a unique name */
172 new_call_view_name = g_strdup_printf("%p_incoming", (void *)CallModel::instance()->getCall(idx));
173 break;
174 case Call::LifeCycleState::PROGRESS:
175 new_call_view = current_call_view_new();
176 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx);
177 /* use the pointer of the call as a unique name */
178 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx));
179 break;
180 case Call::LifeCycleState::COUNT__:
181 g_warning("LifeCycleState should never be COUNT");
182 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500183 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500184
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400185 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
186 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500187 g_free(new_call_view_name);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500188 } else {
189 /* nothing selected in the call model, so show the default screen */
190
191 /* TODO: replace stack paceholder view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400192 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
193 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_call_view), DEFAULT_VIEW_NAME);
194 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500195
196 }
197
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500198 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400199 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500200 if (current_call_view != old_call_view && old_call_view != NULL) {
201 /* if the previous child was a call view, then remove it from
Stepan Salenikovich69771842015-02-24 18:11:45 -0500202 * the stack; removing it should destory it since there should not
203 * be any other references to it */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500204 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400205 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500206 }
207 }
208}
209
210static void
211call_state_changed(Call *call, gpointer win)
212{
213 g_debug("call state changed");
214 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
215
216 /* check if the call that changed state is the same as the selected call */
217 QModelIndex idx_selected = CallModel::instance()->selectionModel()->currentIndex();
218
219 if( idx_selected.isValid() && call == CallModel::instance()->getCall(idx_selected)) {
220 g_debug("selected call state changed");
221 /* check if we need to change the view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400222 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500223 GtkWidget *new_call_view = NULL;
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400224 QVariant state = CallModel::instance()->data(idx_selected, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500225
226 /* check what the current state is vs what is displayed */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400227 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400228 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500229 case Call::LifeCycleState::INITIALIZATION:
230 /* LifeCycleState cannot go backwards, so it should not be possible
231 * that the call is displayed as current (meaning that its in progress)
232 * but have the state 'initialization' */
233 if (IS_CURRENT_CALL_VIEW(old_call_view))
234 g_warning("call displayed as current, but is in state of initialization");
235 break;
236 case Call::LifeCycleState::PROGRESS:
237 if (IS_INCOMING_CALL_VIEW(old_call_view)) {
238 /* change from incoming to current */
239 new_call_view = current_call_view_new();
240 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx_selected);
241 /* use the pointer of the call as a unique name */
242 char* new_call_view_name = NULL;
243 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx_selected));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400244 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500245 g_free(new_call_view_name);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400246 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
247 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
248 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500249 }
250 break;
251 case Call::LifeCycleState::FINISHED:
252 /* do nothing, either call view is valid for this state */
253 break;
254 case Call::LifeCycleState::COUNT__:
255 g_warning("LifeCycleState should never be COUNT");
256 break;
257 }
258
259 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400260 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500261 if (current_call_view != old_call_view && old_call_view != NULL) {
262 /* if the previous child was a call view, then remove it from
263 * the stack; removing it should destory it since there should not
264 * be any other references to it */
265 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400266 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500267 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500268 }
269 }
270}
271
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500272static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500273search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
274{
275 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
276
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400277 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500278
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400279 if (number_entered && strlen(number_entered) > 0) {
280 /* detect Ring hash */
281 gboolean is_ring_hash = FALSE;
282 if (strlen(number_entered) == 40) {
283 is_ring_hash = TRUE;
284 /* must be 40 chars long and alphanumeric */
285 for (int i = 0; i < 40 && is_ring_hash; ++i) {
286 if (!g_ascii_isalnum(number_entered[i]))
287 is_ring_hash = FALSE;
288 }
289 }
290
291 QString number = QString{number_entered};
292
293 if (is_ring_hash)
294 number = "ring:" + number;
295
296 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500297 Call *call = CallModel::instance()->dialingCall();
298 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500299 call->performAction(Call::Action::ACCEPT);
300
301 /* make this the currently selected call */
302 QModelIndex idx = CallModel::instance()->getIndex(call);
303 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
304 }
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400305
306 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
307 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500308}
309
310static void
Stepan Salenikovich15142182015-03-11 17:15:26 -0400311navbutton_contacts_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
312{
313 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
314 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
315
316 if (gtk_toggle_button_get_active(navbutton)) {
317
318 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
319
320 if (visible) {
321 /* contacts is left of both history and presence, so always slide right to show it */
322 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
323 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
324 } else {
325 g_warning("should always have a visible child in the nav stack");
326 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
327 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
328 }
329 }
330}
331
332static void
333navbutton_presence_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
334{
335 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
336 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
337
338 if (gtk_toggle_button_get_active(navbutton)) {
339
340 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
341 if (visible) {
342 /* presence is right of both history and contacts, so always slide left to show it */
343 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
344 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
345 } else {
346 g_warning("should always have a visible child in the nav stack");
347 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
348 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
349 }
350 }
351}
352
353static void
354navbutton_history_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
355{
356 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
357 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
358
359 if (gtk_toggle_button_get_active(navbutton)) {
360
361 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
362 if (visible) {
363 if (strcmp(visible, VIEW_CONTACTS) == 0) {
364 /* history is right of contacts, so slide left to show it */
365 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
366 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
367 } else if (strcmp(visible, VIEW_PRESENCE) == 0) {
368 /* history is left of presence, so slide right to show it */
369 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
370 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
371 }
372 } else {
373 g_warning("should always have a visible child in the nav stack");
374 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
375 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
376 }
377 }
378}
379
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400380static gboolean
381save_accounts(GtkWidget *working_dialog)
382{
383 /* save changes to accounts */
384 AccountModel::instance()->save();
385 /* save changes to codecs */
386 for (int i = 0; i < AccountModel::instance()->rowCount(); i++) {
387 QModelIndex idx = AccountModel::instance()->index(i, 0);
388 AccountModel::instance()->getAccountByModelIndex(idx)->codecModel()->save();
389 }
390
391 if (working_dialog)
392 gtk_widget_destroy(working_dialog);
393
394 return G_SOURCE_REMOVE;
395}
396
Stepan Salenikovich15142182015-03-11 17:15:26 -0400397static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400398settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
399{
400 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
401 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
402
403 /* toggle show settings */
404 priv->show_settings = !priv->show_settings;
405
406 /* check which view to show */
407 if (priv->show_settings) {
408 /* show settings */
409 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
410
411 gtk_widget_hide(priv->hbox_search);
412 gtk_widget_show(priv->hbox_settings);
413
414 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400415 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400416 } else {
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400417 /* show working dialog in case save operation takes time */
418 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
419 gtk_window_present(GTK_WINDOW(working));
420
421 /* now save after the time it takes to transition back to the call view (400ms)
422 * the save doesn't happen before the "working" dialog is presented
423 * the timeout function should destroy the "working" dialog when done saving
424 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400425 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400426
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400427 /* show calls */
428 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
429
430 gtk_widget_show(priv->hbox_search);
431 gtk_widget_hide(priv->hbox_settings);
432
433 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
434 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400435
436 /* make sure video preview is stopped, in case it was started */
437 Video::PreviewManager::instance()->stopPreview();
438 }
439}
440
441static void
442show_video_settings(GtkToggleButton *navbutton, RingMainWindow *win)
443{
444 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
445 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
446
447 if (gtk_toggle_button_get_active(navbutton)) {
448 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
449 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), VIDEO_SETTINGS_VIEW_NAME);
450 priv->last_settings_view = priv->video_settings_view;
451 }
452}
453
454static void
455show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
456{
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)) {
461 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
462 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
463 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400464 }
465}
466
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400467static gboolean
468has_ring_account()
469{
470 /* check if a Ring account exists */
471 int a_count = AccountModel::instance()->rowCount();
472 for (int i = 0; i < a_count; ++i) {
473 QModelIndex idx = AccountModel::instance()->index(i, 0);
474 QVariant protocol = idx.data(static_cast<int>(Account::Role::Proto));
475 if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING)
476 return TRUE;
477 }
478
479 return FALSE;
480}
481
482static gboolean
483create_ring_account(RingMainWindow *win)
484{
485 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
486 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
487
488 /* create account and set UPnP enabled, as its not by default in the daemon */
489 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
490 Account *account = AccountModel::instance()->add(alias, Account::Protocol::RING);
491 account->setUpnpEnabled(TRUE);
492
493 /* wait for hash to be generated to show the next view */
494 priv->hash_updated = QObject::connect(
495 account,
496 &Account::changed,
497 [=] (Account *a) {
498 QString hash = a->username();
499 if (!hash.isEmpty()) {
500 /* set the hash */
501 gtk_entry_set_text(GTK_ENTRY(priv->entry_hash), hash.toUtf8().constData());
502
503 /* show the next accont creation view */
504 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
505 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_2_VIEW_NAME);
506
507 /* select the hash text */
508 gtk_widget_grab_focus(priv->entry_hash);
509 gtk_editable_select_region(GTK_EDITABLE(priv->entry_hash), 0, -1);
510 }
511 }
512 );
513
514 account->performAction(Account::EditAction::SAVE);
515
516 return G_SOURCE_REMOVE;
517}
518
519static void
520alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
521{
522 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
523
524 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
525 if (strlen(alias) > 0) {
526 /* enable "next" button */
527 gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
528 } else {
529 /* disable "next" button, as we require an alias */
530 gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
531 }
532}
533
534static void
535account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
536{
537 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
538
539 /* show/hide relevant widgets */
540 gtk_widget_hide(priv->label_enter_alias);
541 gtk_widget_hide(priv->entry_alias);
542 gtk_widget_hide(priv->button_account_creation_next);
543 gtk_widget_show(priv->label_generating_account);
544 gtk_widget_show(priv->spinner_generating_account);
545
546 /* now create account after a short timeout so that the the save doesn't
547 * happen freeze the client before the widget changes happen;
548 * the timeout function should then display the next step in account creation
549 */
550 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
551}
552
553static void
554account_creation_done_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
555{
556 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
557
558 QObject::disconnect(priv->hash_updated);
559
560 /* show the call view */
561 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
562 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
563
564 /* show the search bar and settings */
565 gtk_widget_show(priv->hbox_search);
566 gtk_widget_show(priv->ring_settings);
567
568}
569
570static void
571entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
572{
573 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
574
575 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
576 if (strlen(alias) > 0)
577 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
578}
579
580static void
581show_account_creation(RingMainWindow *win)
582{
583 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
584
585 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
586 priv->account_creation_1,
587 CREATE_ACCOUNT_1_VIEW_NAME);
588
589 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
590 priv->account_creation_2,
591 CREATE_ACCOUNT_2_VIEW_NAME);
592
593 /* hide search bar and settings until account creation is complete */
594 gtk_widget_hide(priv->hbox_search);
595 gtk_widget_hide(priv->ring_settings);
596
597 /* set ring logo */
598 GError *error = NULL;
599 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
600 -1, 75, TRUE, &error);
601 if (logo_ring == NULL) {
602 g_debug("Could not load logo: %s", error->message);
603 g_error_free(error);
604 } else
605 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
606
607 /* style of alias and hash entry; give them a larger font */
608 gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
609 gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
610
611 /* connect signals */
612 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
613 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
614 g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
615 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
616 g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
617
618 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
619}
620
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400621static void
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400622show_ring_id(RingMainWindow *win, Account *account) {
623 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
624
625 /* display the ring id, if we found a ring account */
626 if (account) {
627 if (!account->username().isEmpty()) {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400628 QString hash = "<span fgcolor=\"black\">" + account->username() + "</span>";
629 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), hash.toUtf8().constData());
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400630 } else {
631 g_warning("got ring account, but Ring id is empty");
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400632 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), "<span fgcolor=\"gray\">fetching Ring ID...</span>");
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400633 }
634 } else {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400635 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), "<span fgcolor=\"gray\">no Ring account</span>");
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400636 }
637
638}
639
640static void
641get_active_ring_account(RingMainWindow *win)
642{
643 /* get the users Ring account
644 * if multiple accounts exist, get the first one which is registered,
645 * if none, then the first one which is enabled,
646 * if none, then the first one in the list of ring accounts
647 */
648 Account *registered_account = NULL;
649 Account *enabled_account = NULL;
650 Account *ring_account = NULL;
651 int a_count = AccountModel::instance()->rowCount();
652 for (int i = 0; i < a_count && !registered_account; ++i) {
653 QModelIndex idx = AccountModel::instance()->index(i, 0);
654 Account *account = AccountModel::instance()->getAccountByModelIndex(idx);
655 if (account->protocol() == Account::Protocol::RING) {
656 /* got RING account, check if active */
657 if (account->isEnabled()) {
658 /* got enabled account, check if connected */
659 if (account->registrationState() == Account::RegistrationState::READY) {
660 /* got registered account, use this one */
661 registered_account = enabled_account = ring_account = account;
662 // g_debug("got registered account: %s", ring_account->alias().toUtf8().constData());
663 } else {
664 /* not registered, but enabled, use if its the first one */
665 if (!enabled_account) {
666 enabled_account = ring_account = account;
667 // g_debug("got enabled ring accout: %s", ring_account->alias().toUtf8().constData());
668 }
669 }
670 } else {
671 /* not enabled, but a Ring account, use if its the first one */
672 if (!ring_account) {
673 ring_account = account;
674 // g_debug("got ring account: %s", ring_account->alias().toUtf8().constData());
675 }
676 }
677 }
678 }
679
680 show_ring_id(win, ring_account);
681}
682
683static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400684search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
685{
686 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
687
688 /* get the text from the entry */
689 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
690
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400691 if (text) {
692 /* edit the the dialing call (or create a new one) */
693 Call *call = CallModel::instance()->dialingCall();
694 call->setDialNumber(text);
695 priv->q_completion_model->setCall(call);
696 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400697}
698
699static gboolean
700completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
701 G_GNUC_UNUSED const gchar *key,
702 G_GNUC_UNUSED GtkTreeIter *iter,
703 G_GNUC_UNUSED RingMainWindow *win)
704{
705 /* the model is updated by lrc and should only every contain matching entries
706 * so always return TRUE */
707 return TRUE;
708}
709
710static QModelIndex
711get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
712 GtkTreeIter *filter_iter)
713{
714 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
715 GtkTreeIter child_iter;
716 gtk_tree_model_filter_convert_iter_to_child_iter(
717 GTK_TREE_MODEL_FILTER(filter_model),
718 &child_iter,
719 filter_iter);
720
721 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
722}
723
724static void
725autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
726 GtkCellRenderer *cell,
727 GtkTreeModel *model,
728 GtkTreeIter *iter,
729 G_GNUC_UNUSED gpointer user_data)
730{
731 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
732 if (idx.isValid()) {
733 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
734 if (photo_var.isValid()) {
735 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
736 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
737 20, 20,
738 GDK_INTERP_BILINEAR);
739
740 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
741 g_object_unref(scaled);
742 return;
743 }
744 }
745
746 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
747}
748
749static void
750autocompletion_name_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
751 GtkCellRenderer *cell,
752 GtkTreeModel *model,
753 GtkTreeIter *iter,
754 G_GNUC_UNUSED gpointer user_data)
755{
756 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
757 if (idx.isValid()) {
758 QVariant name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
759 gchar *text = g_strdup_printf("<span font=\"12\" weight=\"bold\">%s</span>",
760 name.value<QString>().toUtf8().constData());
761
762 g_object_set(G_OBJECT(cell), "markup", text, NULL);
763 g_free(text);
764 return;
765 }
766
767 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
768}
769
770static void
771autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
772 GtkCellRenderer *cell,
773 GtkTreeModel *model,
774 GtkTreeIter *iter,
775 G_GNUC_UNUSED gpointer user_data)
776{
777 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
778 if (idx.isValid()) {
779 QVariant uri = idx.data(Qt::DisplayRole);
780 gchar *text = g_strdup_printf("<span font=\"12\">%s</span>",
781 uri.value<QString>().toUtf8().constData());
782
783 g_object_set(G_OBJECT(cell), "markup", text, NULL);
784 g_free(text);
785 return;
786 }
787
788 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
789}
790
791static void
792autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
793 GtkCellRenderer *cell,
794 GtkTreeModel *model,
795 GtkTreeIter *iter,
796 G_GNUC_UNUSED gpointer user_data)
797{
798 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
799 if (idx.isValid()) {
800 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
801 gchar *text = g_strdup_printf("<span font=\"12\" color=\"gray\">%s</span>",
802 alias.value<QString>().toUtf8().constData());
803
804 g_object_set(G_OBJECT(cell), "markup", text, NULL);
805 g_free(text);
806 return;
807 }
808
809 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
810}
811
812static gboolean
813select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
814 GtkTreeModel *model,
815 GtkTreeIter *iter,
816 RingMainWindow *win)
817{
818 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
819
820 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
821 if (idx.isValid()) {
822 ContactMethod *n = priv->q_completion_model->number(idx);
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400823 /* check if using a specific account */
824 QVariant var_acc = idx.data(NumberCompletionModel::Role::ACCOUNT);
825 Account *acc = var_acc.value<Account *>();
826 if (acc) {
827 g_debug("using account: %s", acc->alias().toUtf8().constData());
828 }
829 place_new_call(n, acc);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400830
831 /* clear the entry */
832 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
833
834 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
835 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400836 } else {
837 g_warning("autocompletion selection is not a valid index!");
838 }
839 return TRUE;
840}
841
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400842static gboolean
843dtmf_pressed(RingMainWindow *win,
844 GdkEventKey *event,
845 G_GNUC_UNUSED gpointer user_data)
846{
847 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
848
849 /* we want to react to digit key presses, as long as a GtkEntry is not the
850 * input focus
851 */
852 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
853 if (GTK_IS_ENTRY(focus))
854 return GDK_EVENT_PROPAGATE;
855
856 /* make sure that a call is selected*/
857 QItemSelectionModel *selection = CallModel::instance()->selectionModel();
858 QModelIndex idx = selection->currentIndex();
859 if (!idx.isValid())
860 return GDK_EVENT_PROPAGATE;
861
862 /* make sure that the selected call is in progress */
863 Call *call = CallModel::instance()->getCall(idx);
864 Call::LifeCycleState state = call->lifeCycleState();
865 if (state != Call::LifeCycleState::PROGRESS)
866 return GDK_EVENT_PROPAGATE;
867
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400868 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
869 * does not result in a 'c' being played.
870 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
871 if ( event->state
872 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
873 return GDK_EVENT_PROPAGATE;
874
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400875 /* pass the character that was entered to be played by the daemon;
876 * the daemon will filter out invalid DTMF characters */
877 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
878 QString val = QString::fromUcs4(&unicode_val, 1);
879 call->playDTMF(val);
880 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400881
882 /* always propogate the key, so we don't steal accelerators/shortcuts */
883 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400884}
885
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400886static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500887ring_main_window_init(RingMainWindow *win)
888{
889 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
890 gtk_widget_init_template(GTK_WIDGET(win));
891
892 /* set window icon */
893 GError *error = NULL;
894 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
895 if (icon == NULL) {
896 g_debug("Could not load icon: %s", error->message);
897 g_error_free(error);
898 } else
899 gtk_window_set_icon(GTK_WINDOW(win), icon);
900
901 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400902 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
903 -1, 24, TRUE, &error);
904 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500905 g_debug("Could not load icon: %s", error->message);
906 g_error_free(error);
907 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400908 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500909
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400910 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500911 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
912 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400913 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500914 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500915
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400916 /* settings icon */
917 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
918
919 /* connect settings button signal */
920 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
921
922 /* add the call view to the main stack */
923 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
924 priv->vbox_call_view,
925 CALL_VIEW_NAME);
926
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400927 if (has_ring_account()) {
928 /* user has ring account, so show the call view right away */
929 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
930 } else {
931 /* user has to create the ring account */
932 show_account_creation(win);
933 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400934
935 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400936 priv->account_settings_view = account_view_new();
937 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
938
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400939 priv->video_settings_view = video_settings_view_new();
940 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->video_settings_view, VIDEO_SETTINGS_VIEW_NAME);
941
942 /* make the setting we will show first the active one */
943 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_video_settings), TRUE);
944 priv->last_settings_view = priv->video_settings_view;
945
946 /* connect the settings button signals to switch settings views */
947 g_signal_connect(priv->radiobutton_video_settings, "toggled", G_CALLBACK(show_video_settings), win);
948 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
949
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500950 /* call model */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500951 GtkQTreeModel *call_model;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500952 GtkCellRenderer *renderer;
953 GtkTreeViewColumn *column;
954
Stepan Salenikovich69771842015-02-24 18:11:45 -0500955 call_model = gtk_q_tree_model_new(CallModel::instance(), 4,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500956 Call::Role::Name, G_TYPE_STRING,
957 Call::Role::Number, G_TYPE_STRING,
958 Call::Role::Length, G_TYPE_STRING,
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400959 Call::Role::State, G_TYPE_STRING);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500960 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_call), GTK_TREE_MODEL(call_model));
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500961
962 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400963 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500964 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400965 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500966 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
967
968 renderer = gtk_cell_renderer_text_new();
969 column = gtk_tree_view_column_new_with_attributes("Duration", renderer, "text", 2, NULL);
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400970 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500971 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
972
Stepan Salenikovich69771842015-02-24 18:11:45 -0500973 /* connect signals to and from UserActionModel to sync selection betwee
974 * the QModel and the GtkTreeView */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500975 QObject::connect(
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500976 CallModel::instance()->selectionModel(),
Stepan Salenikovich69771842015-02-24 18:11:45 -0500977 &QItemSelectionModel::currentChanged,
978 [=](const QModelIndex & current, const QModelIndex & previous) {
979 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
980
981 /* first unselect the previous */
982 if (previous.isValid()) {
983 GtkTreeIter old_iter;
984 if (gtk_q_tree_model_source_index_to_iter(call_model, previous, &old_iter)) {
985 gtk_tree_selection_unselect_iter(selection, &old_iter);
986 } else {
987 g_warning("Trying to unselect invalid GtkTreeIter");
988 }
989 }
990
991 /* select the current */
992 if (current.isValid()) {
993 GtkTreeIter new_iter;
994 if (gtk_q_tree_model_source_index_to_iter(call_model, current, &new_iter)) {
995 gtk_tree_selection_select_iter(selection, &new_iter);
996 } else {
997 g_warning("SelectionModel of CallModel changed to invalid QModelIndex?");
998 }
999 }
1000 }
1001 );
1002
1003 GtkTreeSelection *call_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001004 g_signal_connect(call_selection, "changed", G_CALLBACK(update_call_model_selection), NULL);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001005
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001006 /* connect to call state changes to update relevant view(s) */
1007 QObject::connect(
1008 CallModel::instance(),
1009 &CallModel::callStateChanged,
1010 [=](Call* call, G_GNUC_UNUSED Call::State previousState) {
1011 call_state_changed(call, win);
1012 }
1013 );
1014
Stepan Salenikovich15142182015-03-11 17:15:26 -04001015 /* contacts view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001016 GtkWidget *contacts_view = contacts_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -04001017 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001018 contacts_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001019 VIEW_CONTACTS);
1020
1021 /* history view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001022 GtkWidget *history_view = history_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -04001023 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001024 history_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001025 VIEW_HISTORY);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001026
Stepan Salenikovich15142182015-03-11 17:15:26 -04001027 /* presence view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001028 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001029 GtkWidget *treeview_presence = gtk_tree_view_new();
1030 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_presence), FALSE);
1031 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_presence);
1032 gtk_widget_show_all(scrolled_window);
1033 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
1034 scrolled_window,
1035 VIEW_PRESENCE);
1036
1037 /* connect signals to change the contacts/history/presence stack view */
1038 g_signal_connect(priv->radiobutton_contacts, "toggled", G_CALLBACK(navbutton_contacts_toggled), win);
1039 g_signal_connect(priv->radiobutton_history, "toggled", G_CALLBACK(navbutton_history_toggled), win);
1040 g_signal_connect(priv->radiobutton_presence, "toggled", G_CALLBACK(navbutton_presence_toggled), win);
1041
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001042 /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
1043 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_contacts), TRUE);
1044
Stepan Salenikovich69771842015-02-24 18:11:45 -05001045 /* TODO: replace stack paceholder view */
1046 GtkWidget *placeholder_view = gtk_tree_view_new();
1047 gtk_widget_show(placeholder_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001048 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), placeholder_view, DEFAULT_VIEW_NAME);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001049
1050 /* connect signals */
1051 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001052 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
1053 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
1054
1055 /* style of search entry */
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001056 gtk_widget_override_font(priv->search_entry, pango_font_description_from_string("15")); //"monospace 15"));
1057
1058 /* autocompletion */
1059 priv->q_completion_model = new NumberCompletionModel();
1060
1061 /* autocompletion renderers */
1062 GtkCellArea *completion_area = gtk_cell_area_box_new();
1063
1064 /* photo renderer */
1065 renderer = gtk_cell_renderer_pixbuf_new();
1066 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1067 renderer,
1068 TRUE, /* expand */
1069 TRUE, /* align */
1070 TRUE); /* fixed size */
1071
1072 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1073 renderer,
1074 (GtkCellLayoutDataFunc)autocompletion_photo_render,
1075 NULL, NULL);
1076
1077 /* name renderer */
1078 renderer = gtk_cell_renderer_text_new();
1079 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1080 renderer,
1081 TRUE, /* expand */
1082 TRUE, /* align */
1083 TRUE); /* fixed size */
1084
1085 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1086 renderer,
1087 (GtkCellLayoutDataFunc)autocompletion_name_render,
1088 NULL, NULL);
1089
1090 /* number renderer */
1091 renderer = gtk_cell_renderer_text_new();
1092 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1093 renderer,
1094 TRUE, /* expand */
1095 TRUE, /* align */
1096 TRUE); /* fixed size */
1097
1098 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1099 renderer,
1100 (GtkCellLayoutDataFunc)autocompletion_number_render,
1101 NULL, NULL);
1102 /* account renderer */
1103 renderer = gtk_cell_renderer_text_new();
1104 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1105 renderer,
1106 TRUE, /* expand */
1107 TRUE, /* align */
1108 TRUE); /* fixed size */
1109
1110 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1111 renderer,
1112 (GtkCellLayoutDataFunc)autocompletion_account_render,
1113 NULL, NULL);
1114
1115 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1116
1117 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1118 (QAbstractItemModel *)priv->q_completion_model,
1119 1,
1120 Qt::DisplayRole, G_TYPE_STRING);
1121
1122 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1123
1124 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1125 gtk_entry_completion_set_match_func(
1126 entry_completion,
1127 (GtkEntryCompletionMatchFunc) completion_match_func,
1128 NULL,
1129 NULL);
1130
1131 /* connect signal to when text is entered in the entry */
1132 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1133 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001134
1135 /* connect to incoming call and focus */
1136 QObject::connect(
1137 CallModel::instance(),
1138 &CallModel::incomingCall,
1139 [=](Call* call) {
1140 CallModel::instance()->selectionModel()->setCurrentIndex(
1141 CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
1142 }
1143 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001144
1145 /* display ring id by first getting the active ring account */
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001146 gtk_widget_override_font(priv->label_ring_id, pango_font_description_from_string("monospace"));
1147 gtk_widget_set_size_request(priv->label_ring_id, 400, 35);
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001148 get_active_ring_account(win);
1149 QObject::connect(
1150 AccountModel::instance(),
1151 &AccountModel::dataChanged,
1152 [=] () {
1153 /* check if the active ring account has changed,
1154 * eg: if it was deleted */
1155 get_active_ring_account(win);
1156 }
1157 );
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001158
1159 /* react to digit key press events */
1160 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001161}
1162
1163static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001164ring_main_window_dispose(GObject *object)
1165{
1166 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1167}
1168
1169static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001170ring_main_window_finalize(GObject *object)
1171{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001172 RingMainWindow *self = RING_MAIN_WINDOW(object);
1173 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1174
1175 delete priv->q_contact_model;
1176 delete priv->q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001177 delete priv->q_completion_model;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001178
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001179 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001180}
1181
1182static void
1183ring_main_window_class_init(RingMainWindowClass *klass)
1184{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001185 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001186 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001187
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001188 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1189 "/cx/ring/RingGnome/ringmainwindow.ui");
1190
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001191 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, treeview_call);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001192 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_contacts_history_presence);
1193 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
1194 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
1195 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001196 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1197 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1198 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1199 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
1200 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_search);
1201 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001202 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001203 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001204 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
1205 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_call_view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001206 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001207 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_audio_settings);
1208 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
1209 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_video_settings);
1210 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001211 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_ring_id);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001212
1213 /* account creation */
1214 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
1215 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
1216 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
1217 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
1218 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1219 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1220 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
1221
1222 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
1223 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
1224 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001225}
1226
1227GtkWidget *
1228ring_main_window_new (GtkApplication *app)
1229{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001230 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1231
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001232 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001233}