blob: 8e7bf5fe2a0bfb2d53def8c410a5e53bdba505d2 [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"
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -040066#define MEDIA_SETTINGS_VIEW_NAME "media"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040067#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_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400107 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400108 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400109 GtkWidget *label_ring_id;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400110
111 Account *active_ring_account;
112 QMetaObject::Connection active_ring_account_updates;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400113
114 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400115
116 /* account creation */
117 GtkWidget *account_creation_1;
118 GtkWidget *image_ring_logo;
119 GtkWidget *label_enter_alias;
120 GtkWidget *entry_alias;
121 GtkWidget *label_generating_account;
122 GtkWidget *spinner_generating_account;
123 GtkWidget *button_account_creation_next;
124 GtkWidget *account_creation_2;
125 GtkWidget *entry_hash;
126 GtkWidget *button_account_creation_done;
127
128 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400129
130 /* allocd qmodels */
131 ActiveItemProxyModel *q_contact_model;
132 QSortFilterProxyModel *q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400133 NumberCompletionModel *q_completion_model;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500134};
135
136G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
137
138#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
139
Stepan Salenikovich69771842015-02-24 18:11:45 -0500140static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500141update_call_model_selection(GtkTreeSelection *selection, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500142{
Stepan Salenikovich69771842015-02-24 18:11:45 -0500143 QModelIndex current = get_index_from_selection(selection);
144 if (current.isValid())
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500145 CallModel::instance()->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500146 else
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500147 CallModel::instance()->selectionModel()->clearCurrentIndex();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500148}
149
150static void
151call_selection_changed(GtkTreeSelection *selection, gpointer win)
152{
153 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
154
155 /* get the current visible stack child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400156 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500157
158 QModelIndex idx = get_index_from_selection(selection);
159 if (idx.isValid()) {
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400160 QVariant state = CallModel::instance()->data(idx, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500161 GtkWidget *new_call_view = NULL;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500162 char* new_call_view_name = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500163
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400164 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400165 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500166 case Call::LifeCycleState::INITIALIZATION:
167 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500168 new_call_view = incoming_call_view_new();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500169 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500170 /* use the pointer of the call as a unique name */
171 new_call_view_name = g_strdup_printf("%p_incoming", (void *)CallModel::instance()->getCall(idx));
172 break;
173 case Call::LifeCycleState::PROGRESS:
174 new_call_view = current_call_view_new();
175 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx);
176 /* use the pointer of the call as a unique name */
177 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx));
178 break;
179 case Call::LifeCycleState::COUNT__:
180 g_warning("LifeCycleState should never be COUNT");
181 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500182 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500183
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400184 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
185 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500186 g_free(new_call_view_name);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500187 } else {
188 /* nothing selected in the call model, so show the default screen */
189
190 /* TODO: replace stack paceholder view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400191 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
192 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_call_view), DEFAULT_VIEW_NAME);
193 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500194
195 }
196
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500197 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400198 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500199 if (current_call_view != old_call_view && old_call_view != NULL) {
200 /* if the previous child was a call view, then remove it from
Stepan Salenikovich69771842015-02-24 18:11:45 -0500201 * the stack; removing it should destory it since there should not
202 * be any other references to it */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500203 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400204 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500205 }
206 }
207}
208
209static void
210call_state_changed(Call *call, gpointer win)
211{
212 g_debug("call state changed");
213 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
214
215 /* check if the call that changed state is the same as the selected call */
216 QModelIndex idx_selected = CallModel::instance()->selectionModel()->currentIndex();
217
218 if( idx_selected.isValid() && call == CallModel::instance()->getCall(idx_selected)) {
219 g_debug("selected call state changed");
220 /* check if we need to change the view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400221 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500222 GtkWidget *new_call_view = NULL;
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400223 QVariant state = CallModel::instance()->data(idx_selected, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500224
225 /* check what the current state is vs what is displayed */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400226 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400227 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500228 case Call::LifeCycleState::INITIALIZATION:
229 /* LifeCycleState cannot go backwards, so it should not be possible
230 * that the call is displayed as current (meaning that its in progress)
231 * but have the state 'initialization' */
232 if (IS_CURRENT_CALL_VIEW(old_call_view))
233 g_warning("call displayed as current, but is in state of initialization");
234 break;
235 case Call::LifeCycleState::PROGRESS:
236 if (IS_INCOMING_CALL_VIEW(old_call_view)) {
237 /* change from incoming to current */
238 new_call_view = current_call_view_new();
239 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx_selected);
240 /* use the pointer of the call as a unique name */
241 char* new_call_view_name = NULL;
242 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx_selected));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400243 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500244 g_free(new_call_view_name);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400245 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
246 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
247 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500248 }
249 break;
250 case Call::LifeCycleState::FINISHED:
251 /* do nothing, either call view is valid for this state */
252 break;
253 case Call::LifeCycleState::COUNT__:
254 g_warning("LifeCycleState should never be COUNT");
255 break;
256 }
257
258 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400259 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500260 if (current_call_view != old_call_view && old_call_view != NULL) {
261 /* if the previous child was a call view, then remove it from
262 * the stack; removing it should destory it since there should not
263 * be any other references to it */
264 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400265 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500266 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500267 }
268 }
269}
270
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500271static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500272search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
273{
274 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
275
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400276 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500277
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400278 if (number_entered && strlen(number_entered) > 0) {
279 /* detect Ring hash */
280 gboolean is_ring_hash = FALSE;
281 if (strlen(number_entered) == 40) {
282 is_ring_hash = TRUE;
283 /* must be 40 chars long and alphanumeric */
284 for (int i = 0; i < 40 && is_ring_hash; ++i) {
285 if (!g_ascii_isalnum(number_entered[i]))
286 is_ring_hash = FALSE;
287 }
288 }
289
290 QString number = QString{number_entered};
291
292 if (is_ring_hash)
293 number = "ring:" + number;
294
295 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500296 Call *call = CallModel::instance()->dialingCall();
297 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500298 call->performAction(Call::Action::ACCEPT);
299
300 /* make this the currently selected call */
301 QModelIndex idx = CallModel::instance()->getIndex(call);
302 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
303 }
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400304
305 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
306 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500307}
308
309static void
Stepan Salenikovich15142182015-03-11 17:15:26 -0400310navbutton_contacts_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
311{
312 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
313 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
314
315 if (gtk_toggle_button_get_active(navbutton)) {
316
317 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
318
319 if (visible) {
320 /* contacts is left of both history and presence, so always slide right to show it */
321 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
322 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
323 } else {
324 g_warning("should always have a visible child in the nav stack");
325 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
326 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
327 }
328 }
329}
330
331static void
332navbutton_presence_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
333{
334 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
335 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
336
337 if (gtk_toggle_button_get_active(navbutton)) {
338
339 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
340 if (visible) {
341 /* presence is right of both history and contacts, so always slide left to show it */
342 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
343 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
344 } else {
345 g_warning("should always have a visible child in the nav stack");
346 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
347 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
348 }
349 }
350}
351
352static void
353navbutton_history_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
354{
355 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
356 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
357
358 if (gtk_toggle_button_get_active(navbutton)) {
359
360 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
361 if (visible) {
362 if (strcmp(visible, VIEW_CONTACTS) == 0) {
363 /* history is right of contacts, so slide left to show it */
364 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
365 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
366 } else if (strcmp(visible, VIEW_PRESENCE) == 0) {
367 /* history is left of presence, so slide right to show it */
368 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
369 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
370 }
371 } else {
372 g_warning("should always have a visible child in the nav stack");
373 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
374 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
375 }
376 }
377}
378
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400379static gboolean
380save_accounts(GtkWidget *working_dialog)
381{
382 /* save changes to accounts */
383 AccountModel::instance()->save();
384 /* save changes to codecs */
385 for (int i = 0; i < AccountModel::instance()->rowCount(); i++) {
386 QModelIndex idx = AccountModel::instance()->index(i, 0);
387 AccountModel::instance()->getAccountByModelIndex(idx)->codecModel()->save();
388 }
389
390 if (working_dialog)
391 gtk_widget_destroy(working_dialog);
392
393 return G_SOURCE_REMOVE;
394}
395
Stepan Salenikovich15142182015-03-11 17:15:26 -0400396static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400397settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
398{
399 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
400 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
401
402 /* toggle show settings */
403 priv->show_settings = !priv->show_settings;
404
405 /* check which view to show */
406 if (priv->show_settings) {
407 /* show settings */
408 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
409
410 gtk_widget_hide(priv->hbox_search);
411 gtk_widget_show(priv->hbox_settings);
412
Stepan Salenikovich41118912015-05-01 11:25:46 -0400413 /* make sure to start preview if we're showing the video settings */
414 if (priv->last_settings_view == priv->video_settings_view)
415 video_settings_show_preview(VIDEO_SETTINGS_VIEW(priv->video_settings_view), TRUE);
416
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400417 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400418 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400419 } else {
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400420 /* show working dialog in case save operation takes time */
421 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
422 gtk_window_present(GTK_WINDOW(working));
423
424 /* now save after the time it takes to transition back to the call view (400ms)
425 * the save doesn't happen before the "working" dialog is presented
426 * the timeout function should destroy the "working" dialog when done saving
427 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400428 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400429
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400430 /* show calls */
431 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
432
433 gtk_widget_show(priv->hbox_search);
434 gtk_widget_hide(priv->hbox_settings);
435
436 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
437 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400438
439 /* make sure video preview is stopped, in case it was started */
Stepan Salenikovich41118912015-05-01 11:25:46 -0400440 video_settings_show_preview(VIDEO_SETTINGS_VIEW(priv->video_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400441 }
442}
443
444static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400445show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400446{
447 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
448 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
449
450 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich41118912015-05-01 11:25:46 -0400451 video_settings_show_preview(VIDEO_SETTINGS_VIEW(priv->video_settings_view), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400452 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400453 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400454 priv->last_settings_view = priv->video_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400455 } else {
456 video_settings_show_preview(VIDEO_SETTINGS_VIEW(priv->video_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400457 }
458}
459
460static void
461show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
462{
463 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
464 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
465
466 if (gtk_toggle_button_get_active(navbutton)) {
467 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
468 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
469 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400470 }
471}
472
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400473static gboolean
474has_ring_account()
475{
476 /* check if a Ring account exists */
477 int a_count = AccountModel::instance()->rowCount();
478 for (int i = 0; i < a_count; ++i) {
479 QModelIndex idx = AccountModel::instance()->index(i, 0);
480 QVariant protocol = idx.data(static_cast<int>(Account::Role::Proto));
481 if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING)
482 return TRUE;
483 }
484
485 return FALSE;
486}
487
488static gboolean
489create_ring_account(RingMainWindow *win)
490{
491 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
492 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
493
494 /* create account and set UPnP enabled, as its not by default in the daemon */
495 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
496 Account *account = AccountModel::instance()->add(alias, Account::Protocol::RING);
497 account->setUpnpEnabled(TRUE);
498
499 /* wait for hash to be generated to show the next view */
500 priv->hash_updated = QObject::connect(
501 account,
502 &Account::changed,
503 [=] (Account *a) {
504 QString hash = a->username();
505 if (!hash.isEmpty()) {
506 /* set the hash */
507 gtk_entry_set_text(GTK_ENTRY(priv->entry_hash), hash.toUtf8().constData());
508
509 /* show the next accont creation view */
510 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
511 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_2_VIEW_NAME);
512
513 /* select the hash text */
514 gtk_widget_grab_focus(priv->entry_hash);
515 gtk_editable_select_region(GTK_EDITABLE(priv->entry_hash), 0, -1);
516 }
517 }
518 );
519
520 account->performAction(Account::EditAction::SAVE);
521
522 return G_SOURCE_REMOVE;
523}
524
525static void
526alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
527{
528 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
529
530 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
531 if (strlen(alias) > 0) {
532 /* enable "next" button */
533 gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
534 } else {
535 /* disable "next" button, as we require an alias */
536 gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
537 }
538}
539
540static void
541account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
542{
543 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
544
545 /* show/hide relevant widgets */
546 gtk_widget_hide(priv->label_enter_alias);
547 gtk_widget_hide(priv->entry_alias);
548 gtk_widget_hide(priv->button_account_creation_next);
549 gtk_widget_show(priv->label_generating_account);
550 gtk_widget_show(priv->spinner_generating_account);
551
552 /* now create account after a short timeout so that the the save doesn't
553 * happen freeze the client before the widget changes happen;
554 * the timeout function should then display the next step in account creation
555 */
556 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
557}
558
559static void
560account_creation_done_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
561{
562 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
563
564 QObject::disconnect(priv->hash_updated);
565
566 /* show the call view */
567 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
568 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
569
570 /* show the search bar and settings */
571 gtk_widget_show(priv->hbox_search);
572 gtk_widget_show(priv->ring_settings);
573
574}
575
576static void
577entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
578{
579 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
580
581 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
582 if (strlen(alias) > 0)
583 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
584}
585
586static void
587show_account_creation(RingMainWindow *win)
588{
589 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
590
591 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
592 priv->account_creation_1,
593 CREATE_ACCOUNT_1_VIEW_NAME);
594
595 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
596 priv->account_creation_2,
597 CREATE_ACCOUNT_2_VIEW_NAME);
598
599 /* hide search bar and settings until account creation is complete */
600 gtk_widget_hide(priv->hbox_search);
601 gtk_widget_hide(priv->ring_settings);
602
603 /* set ring logo */
604 GError *error = NULL;
605 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
606 -1, 75, TRUE, &error);
607 if (logo_ring == NULL) {
608 g_debug("Could not load logo: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400609 g_clear_error(&error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400610 } else
611 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
612
613 /* style of alias and hash entry; give them a larger font */
614 gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
615 gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
616
617 /* connect signals */
618 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
619 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
620 g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
621 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
622 g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
623
624 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
625}
626
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400627static void
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400628show_ring_id(RingMainWindow *win, Account *account) {
629 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
630
631 /* display the ring id, if we found a ring account */
632 if (account) {
633 if (!account->username().isEmpty()) {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400634 QString hash = "<span fgcolor=\"black\">" + account->username() + "</span>";
635 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), hash.toUtf8().constData());
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400636 } else {
637 g_warning("got ring account, but Ring id is empty");
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400638 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), "<span fgcolor=\"gray\">fetching Ring ID...</span>");
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400639 }
640 } else {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400641 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), "<span fgcolor=\"gray\">no Ring account</span>");
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400642 }
643
644}
645
646static void
647get_active_ring_account(RingMainWindow *win)
648{
649 /* get the users Ring account
650 * if multiple accounts exist, get the first one which is registered,
651 * if none, then the first one which is enabled,
652 * if none, then the first one in the list of ring accounts
653 */
654 Account *registered_account = NULL;
655 Account *enabled_account = NULL;
656 Account *ring_account = NULL;
657 int a_count = AccountModel::instance()->rowCount();
658 for (int i = 0; i < a_count && !registered_account; ++i) {
659 QModelIndex idx = AccountModel::instance()->index(i, 0);
660 Account *account = AccountModel::instance()->getAccountByModelIndex(idx);
661 if (account->protocol() == Account::Protocol::RING) {
662 /* got RING account, check if active */
663 if (account->isEnabled()) {
664 /* got enabled account, check if connected */
665 if (account->registrationState() == Account::RegistrationState::READY) {
666 /* got registered account, use this one */
667 registered_account = enabled_account = ring_account = account;
668 // g_debug("got registered account: %s", ring_account->alias().toUtf8().constData());
669 } else {
670 /* not registered, but enabled, use if its the first one */
671 if (!enabled_account) {
672 enabled_account = ring_account = account;
673 // g_debug("got enabled ring accout: %s", ring_account->alias().toUtf8().constData());
674 }
675 }
676 } else {
677 /* not enabled, but a Ring account, use if its the first one */
678 if (!ring_account) {
679 ring_account = account;
680 // g_debug("got ring account: %s", ring_account->alias().toUtf8().constData());
681 }
682 }
683 }
684 }
685
686 show_ring_id(win, ring_account);
687}
688
689static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400690search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
691{
692 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
693
694 /* get the text from the entry */
695 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
696
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400697 if (text) {
698 /* edit the the dialing call (or create a new one) */
699 Call *call = CallModel::instance()->dialingCall();
700 call->setDialNumber(text);
701 priv->q_completion_model->setCall(call);
702 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400703}
704
705static gboolean
706completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
707 G_GNUC_UNUSED const gchar *key,
708 G_GNUC_UNUSED GtkTreeIter *iter,
709 G_GNUC_UNUSED RingMainWindow *win)
710{
711 /* the model is updated by lrc and should only every contain matching entries
712 * so always return TRUE */
713 return TRUE;
714}
715
716static QModelIndex
717get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
718 GtkTreeIter *filter_iter)
719{
720 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
721 GtkTreeIter child_iter;
722 gtk_tree_model_filter_convert_iter_to_child_iter(
723 GTK_TREE_MODEL_FILTER(filter_model),
724 &child_iter,
725 filter_iter);
726
727 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
728}
729
730static void
731autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
732 GtkCellRenderer *cell,
733 GtkTreeModel *model,
734 GtkTreeIter *iter,
735 G_GNUC_UNUSED gpointer user_data)
736{
737 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
738 if (idx.isValid()) {
739 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
740 if (photo_var.isValid()) {
741 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
742 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
743 20, 20,
744 GDK_INTERP_BILINEAR);
745
746 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
747 g_object_unref(scaled);
748 return;
749 }
750 }
751
752 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
753}
754
755static void
756autocompletion_name_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 name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
765 gchar *text = g_strdup_printf("<span font=\"12\" weight=\"bold\">%s</span>",
766 name.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 void
777autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
778 GtkCellRenderer *cell,
779 GtkTreeModel *model,
780 GtkTreeIter *iter,
781 G_GNUC_UNUSED gpointer user_data)
782{
783 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
784 if (idx.isValid()) {
785 QVariant uri = idx.data(Qt::DisplayRole);
786 gchar *text = g_strdup_printf("<span font=\"12\">%s</span>",
787 uri.value<QString>().toUtf8().constData());
788
789 g_object_set(G_OBJECT(cell), "markup", text, NULL);
790 g_free(text);
791 return;
792 }
793
794 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
795}
796
797static void
798autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
799 GtkCellRenderer *cell,
800 GtkTreeModel *model,
801 GtkTreeIter *iter,
802 G_GNUC_UNUSED gpointer user_data)
803{
804 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
805 if (idx.isValid()) {
806 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
807 gchar *text = g_strdup_printf("<span font=\"12\" color=\"gray\">%s</span>",
808 alias.value<QString>().toUtf8().constData());
809
810 g_object_set(G_OBJECT(cell), "markup", text, NULL);
811 g_free(text);
812 return;
813 }
814
815 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
816}
817
818static gboolean
819select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
820 GtkTreeModel *model,
821 GtkTreeIter *iter,
822 RingMainWindow *win)
823{
824 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
825
826 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
827 if (idx.isValid()) {
828 ContactMethod *n = priv->q_completion_model->number(idx);
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400829 /* check if using a specific account */
830 QVariant var_acc = idx.data(NumberCompletionModel::Role::ACCOUNT);
831 Account *acc = var_acc.value<Account *>();
832 if (acc) {
833 g_debug("using account: %s", acc->alias().toUtf8().constData());
834 }
835 place_new_call(n, acc);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400836
837 /* clear the entry */
838 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
839
840 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
841 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400842 } else {
843 g_warning("autocompletion selection is not a valid index!");
844 }
845 return TRUE;
846}
847
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400848static gboolean
849dtmf_pressed(RingMainWindow *win,
850 GdkEventKey *event,
851 G_GNUC_UNUSED gpointer user_data)
852{
853 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
854
855 /* we want to react to digit key presses, as long as a GtkEntry is not the
856 * input focus
857 */
858 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
859 if (GTK_IS_ENTRY(focus))
860 return GDK_EVENT_PROPAGATE;
861
862 /* make sure that a call is selected*/
863 QItemSelectionModel *selection = CallModel::instance()->selectionModel();
864 QModelIndex idx = selection->currentIndex();
865 if (!idx.isValid())
866 return GDK_EVENT_PROPAGATE;
867
868 /* make sure that the selected call is in progress */
869 Call *call = CallModel::instance()->getCall(idx);
870 Call::LifeCycleState state = call->lifeCycleState();
871 if (state != Call::LifeCycleState::PROGRESS)
872 return GDK_EVENT_PROPAGATE;
873
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400874 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
875 * does not result in a 'c' being played.
876 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
877 if ( event->state
878 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
879 return GDK_EVENT_PROPAGATE;
880
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400881 /* pass the character that was entered to be played by the daemon;
882 * the daemon will filter out invalid DTMF characters */
883 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
884 QString val = QString::fromUcs4(&unicode_val, 1);
885 call->playDTMF(val);
886 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400887
888 /* always propogate the key, so we don't steal accelerators/shortcuts */
889 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400890}
891
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400892static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500893ring_main_window_init(RingMainWindow *win)
894{
895 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
896 gtk_widget_init_template(GTK_WIDGET(win));
897
898 /* set window icon */
899 GError *error = NULL;
900 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
901 if (icon == NULL) {
902 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400903 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500904 } else
905 gtk_window_set_icon(GTK_WINDOW(win), icon);
906
907 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400908 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
909 -1, 24, TRUE, &error);
910 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500911 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400912 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500913 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400914 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500915
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400916 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500917 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
918 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400919 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500920 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500921
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400922 /* settings icon */
923 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
924
925 /* connect settings button signal */
926 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
927
928 /* add the call view to the main stack */
929 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
930 priv->vbox_call_view,
931 CALL_VIEW_NAME);
932
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400933 if (has_ring_account()) {
934 /* user has ring account, so show the call view right away */
935 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
936 } else {
937 /* user has to create the ring account */
938 show_account_creation(win);
939 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400940
941 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400942 priv->account_settings_view = account_view_new();
943 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
944
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400945 priv->video_settings_view = video_settings_view_new();
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400946 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->video_settings_view, MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400947
948 /* make the setting we will show first the active one */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400949 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_media_settings), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400950 priv->last_settings_view = priv->video_settings_view;
951
952 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400953 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400954 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
955
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500956 /* call model */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500957 GtkQTreeModel *call_model;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500958 GtkCellRenderer *renderer;
959 GtkTreeViewColumn *column;
960
Stepan Salenikovich69771842015-02-24 18:11:45 -0500961 call_model = gtk_q_tree_model_new(CallModel::instance(), 4,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500962 Call::Role::Name, G_TYPE_STRING,
963 Call::Role::Number, G_TYPE_STRING,
964 Call::Role::Length, G_TYPE_STRING,
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400965 Call::Role::State, G_TYPE_STRING);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500966 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_call), GTK_TREE_MODEL(call_model));
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500967
968 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400969 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500970 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400971 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500972 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
973
974 renderer = gtk_cell_renderer_text_new();
975 column = gtk_tree_view_column_new_with_attributes("Duration", renderer, "text", 2, NULL);
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400976 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500977 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
978
Stepan Salenikovich69771842015-02-24 18:11:45 -0500979 /* connect signals to and from UserActionModel to sync selection betwee
980 * the QModel and the GtkTreeView */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500981 QObject::connect(
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500982 CallModel::instance()->selectionModel(),
Stepan Salenikovich69771842015-02-24 18:11:45 -0500983 &QItemSelectionModel::currentChanged,
984 [=](const QModelIndex & current, const QModelIndex & previous) {
985 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
986
987 /* first unselect the previous */
988 if (previous.isValid()) {
989 GtkTreeIter old_iter;
990 if (gtk_q_tree_model_source_index_to_iter(call_model, previous, &old_iter)) {
991 gtk_tree_selection_unselect_iter(selection, &old_iter);
992 } else {
993 g_warning("Trying to unselect invalid GtkTreeIter");
994 }
995 }
996
997 /* select the current */
998 if (current.isValid()) {
999 GtkTreeIter new_iter;
1000 if (gtk_q_tree_model_source_index_to_iter(call_model, current, &new_iter)) {
1001 gtk_tree_selection_select_iter(selection, &new_iter);
1002 } else {
1003 g_warning("SelectionModel of CallModel changed to invalid QModelIndex?");
1004 }
1005 }
1006 }
1007 );
1008
1009 GtkTreeSelection *call_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001010 g_signal_connect(call_selection, "changed", G_CALLBACK(update_call_model_selection), NULL);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001011
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001012 /* connect to call state changes to update relevant view(s) */
1013 QObject::connect(
1014 CallModel::instance(),
1015 &CallModel::callStateChanged,
1016 [=](Call* call, G_GNUC_UNUSED Call::State previousState) {
1017 call_state_changed(call, win);
1018 }
1019 );
1020
Stepan Salenikovich15142182015-03-11 17:15:26 -04001021 /* contacts view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001022 GtkWidget *contacts_view = contacts_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 contacts_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001025 VIEW_CONTACTS);
1026
1027 /* history view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001028 GtkWidget *history_view = history_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -04001029 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001030 history_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001031 VIEW_HISTORY);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001032
Stepan Salenikovich15142182015-03-11 17:15:26 -04001033 /* presence view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001034 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001035 GtkWidget *treeview_presence = gtk_tree_view_new();
1036 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_presence), FALSE);
1037 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_presence);
1038 gtk_widget_show_all(scrolled_window);
1039 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
1040 scrolled_window,
1041 VIEW_PRESENCE);
1042
1043 /* connect signals to change the contacts/history/presence stack view */
1044 g_signal_connect(priv->radiobutton_contacts, "toggled", G_CALLBACK(navbutton_contacts_toggled), win);
1045 g_signal_connect(priv->radiobutton_history, "toggled", G_CALLBACK(navbutton_history_toggled), win);
1046 g_signal_connect(priv->radiobutton_presence, "toggled", G_CALLBACK(navbutton_presence_toggled), win);
1047
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001048 /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
1049 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_contacts), TRUE);
1050
Stepan Salenikovich69771842015-02-24 18:11:45 -05001051 /* TODO: replace stack paceholder view */
1052 GtkWidget *placeholder_view = gtk_tree_view_new();
1053 gtk_widget_show(placeholder_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001054 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), placeholder_view, DEFAULT_VIEW_NAME);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001055
1056 /* connect signals */
1057 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001058 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
1059 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
1060
1061 /* style of search entry */
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001062 gtk_widget_override_font(priv->search_entry, pango_font_description_from_string("15")); //"monospace 15"));
1063
1064 /* autocompletion */
1065 priv->q_completion_model = new NumberCompletionModel();
1066
1067 /* autocompletion renderers */
1068 GtkCellArea *completion_area = gtk_cell_area_box_new();
1069
1070 /* photo renderer */
1071 renderer = gtk_cell_renderer_pixbuf_new();
1072 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1073 renderer,
1074 TRUE, /* expand */
1075 TRUE, /* align */
1076 TRUE); /* fixed size */
1077
1078 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1079 renderer,
1080 (GtkCellLayoutDataFunc)autocompletion_photo_render,
1081 NULL, NULL);
1082
1083 /* name renderer */
1084 renderer = gtk_cell_renderer_text_new();
1085 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1086 renderer,
1087 TRUE, /* expand */
1088 TRUE, /* align */
1089 TRUE); /* fixed size */
1090
1091 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1092 renderer,
1093 (GtkCellLayoutDataFunc)autocompletion_name_render,
1094 NULL, NULL);
1095
1096 /* number renderer */
1097 renderer = gtk_cell_renderer_text_new();
1098 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1099 renderer,
1100 TRUE, /* expand */
1101 TRUE, /* align */
1102 TRUE); /* fixed size */
1103
1104 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1105 renderer,
1106 (GtkCellLayoutDataFunc)autocompletion_number_render,
1107 NULL, NULL);
1108 /* account renderer */
1109 renderer = gtk_cell_renderer_text_new();
1110 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1111 renderer,
1112 TRUE, /* expand */
1113 TRUE, /* align */
1114 TRUE); /* fixed size */
1115
1116 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1117 renderer,
1118 (GtkCellLayoutDataFunc)autocompletion_account_render,
1119 NULL, NULL);
1120
1121 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1122
1123 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1124 (QAbstractItemModel *)priv->q_completion_model,
1125 1,
1126 Qt::DisplayRole, G_TYPE_STRING);
1127
1128 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1129
1130 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1131 gtk_entry_completion_set_match_func(
1132 entry_completion,
1133 (GtkEntryCompletionMatchFunc) completion_match_func,
1134 NULL,
1135 NULL);
1136
1137 /* connect signal to when text is entered in the entry */
1138 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1139 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001140
1141 /* connect to incoming call and focus */
1142 QObject::connect(
1143 CallModel::instance(),
1144 &CallModel::incomingCall,
1145 [=](Call* call) {
1146 CallModel::instance()->selectionModel()->setCurrentIndex(
1147 CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
1148 }
1149 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001150
1151 /* display ring id by first getting the active ring account */
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001152 gtk_widget_override_font(priv->label_ring_id, pango_font_description_from_string("monospace"));
1153 gtk_widget_set_size_request(priv->label_ring_id, 400, 35);
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001154 get_active_ring_account(win);
1155 QObject::connect(
1156 AccountModel::instance(),
1157 &AccountModel::dataChanged,
1158 [=] () {
1159 /* check if the active ring account has changed,
1160 * eg: if it was deleted */
1161 get_active_ring_account(win);
1162 }
1163 );
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001164
1165 /* react to digit key press events */
1166 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001167}
1168
1169static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001170ring_main_window_dispose(GObject *object)
1171{
1172 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1173}
1174
1175static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001176ring_main_window_finalize(GObject *object)
1177{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001178 RingMainWindow *self = RING_MAIN_WINDOW(object);
1179 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1180
1181 delete priv->q_contact_model;
1182 delete priv->q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001183 delete priv->q_completion_model;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001184
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001185 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001186}
1187
1188static void
1189ring_main_window_class_init(RingMainWindowClass *klass)
1190{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001191 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001192 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001193
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001194 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1195 "/cx/ring/RingGnome/ringmainwindow.ui");
1196
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001197 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, treeview_call);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001198 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_contacts_history_presence);
1199 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
1200 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
1201 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001202 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1203 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1204 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1205 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
1206 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_search);
1207 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001208 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001209 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001210 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
1211 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_call_view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001212 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001213 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001214 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001215 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001216 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_ring_id);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001217
1218 /* account creation */
1219 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
1220 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
1221 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
1222 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
1223 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1224 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1225 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
1226
1227 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
1228 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
1229 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001230}
1231
1232GtkWidget *
1233ring_main_window_new (GtkApplication *app)
1234{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001235 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1236
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001237 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001238}