blob: 6e49aa2d29d8a4c7caa12385cdfeb48edfd7f3fd [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 Salenikovich0bd53492015-05-11 14:28:52 -040048#include "mediasettingsview.h"
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040049#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 Salenikovichde896112015-05-11 16:46:33 -040060#include "generalsettingsview.h"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040061
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040062#define CALL_VIEW_NAME "calls"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040063#define CREATE_ACCOUNT_1_VIEW_NAME "create1"
64#define CREATE_ACCOUNT_2_VIEW_NAME "create2"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040065#define GENERAL_SETTINGS_VIEW_NAME "general"
66#define AUDIO_SETTINGS_VIEW_NAME "audio"
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -040067#define MEDIA_SETTINGS_VIEW_NAME "media"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040068#define ACCOUNT_SETTINGS_VIEW_NAME "accounts"
Stepan Salenikovich69771842015-02-24 18:11:45 -050069#define DEFAULT_VIEW_NAME "placeholder"
Stepan Salenikovich15142182015-03-11 17:15:26 -040070#define VIEW_CONTACTS "contacts"
71#define VIEW_HISTORY "history"
72#define VIEW_PRESENCE "presence"
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050073
74struct _RingMainWindow
75{
76 GtkApplicationWindow parent;
77};
78
79struct _RingMainWindowClass
80{
81 GtkApplicationWindowClass parent_class;
82};
83
84typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
85
86struct _RingMainWindowPrivate
87{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040088 GtkWidget *ring_menu;
89 GtkWidget *image_ring;
90 GtkWidget *ring_settings;
91 GtkWidget *image_settings;
92 GtkWidget *hbox_search;
93 GtkWidget *hbox_settings;
Stepan Salenikovich15142182015-03-11 17:15:26 -040094 GtkWidget *stack_contacts_history_presence;
95 GtkWidget *radiobutton_contacts;
96 GtkWidget *radiobutton_history;
97 GtkWidget *radiobutton_presence;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050098 GtkWidget *treeview_call;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050099 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500100 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400101 GtkWidget *vbox_call_view;
102 GtkWidget *stack_call_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500103 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400104 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400105 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400106 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400107 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400108 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400109 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400110 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400111 GtkWidget *label_ring_id;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400112
113 Account *active_ring_account;
114 QMetaObject::Connection active_ring_account_updates;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400115
116 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400117
118 /* account creation */
119 GtkWidget *account_creation_1;
120 GtkWidget *image_ring_logo;
121 GtkWidget *label_enter_alias;
122 GtkWidget *entry_alias;
123 GtkWidget *label_generating_account;
124 GtkWidget *spinner_generating_account;
125 GtkWidget *button_account_creation_next;
126 GtkWidget *account_creation_2;
127 GtkWidget *entry_hash;
128 GtkWidget *button_account_creation_done;
129
130 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400131
132 /* allocd qmodels */
133 ActiveItemProxyModel *q_contact_model;
134 QSortFilterProxyModel *q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400135 NumberCompletionModel *q_completion_model;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500136};
137
138G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
139
140#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
141
Stepan Salenikovich69771842015-02-24 18:11:45 -0500142static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500143update_call_model_selection(GtkTreeSelection *selection, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500144{
Stepan Salenikovich69771842015-02-24 18:11:45 -0500145 QModelIndex current = get_index_from_selection(selection);
146 if (current.isValid())
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500147 CallModel::instance()->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500148 else
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500149 CallModel::instance()->selectionModel()->clearCurrentIndex();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500150}
151
152static void
153call_selection_changed(GtkTreeSelection *selection, gpointer win)
154{
155 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
156
157 /* get the current visible stack child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400158 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500159
160 QModelIndex idx = get_index_from_selection(selection);
161 if (idx.isValid()) {
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400162 QVariant state = CallModel::instance()->data(idx, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500163 GtkWidget *new_call_view = NULL;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500164 char* new_call_view_name = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500165
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400166 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400167 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500168 case Call::LifeCycleState::INITIALIZATION:
169 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500170 new_call_view = incoming_call_view_new();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500171 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500172 /* use the pointer of the call as a unique name */
173 new_call_view_name = g_strdup_printf("%p_incoming", (void *)CallModel::instance()->getCall(idx));
174 break;
175 case Call::LifeCycleState::PROGRESS:
176 new_call_view = current_call_view_new();
177 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx);
178 /* use the pointer of the call as a unique name */
179 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx));
180 break;
181 case Call::LifeCycleState::COUNT__:
182 g_warning("LifeCycleState should never be COUNT");
183 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500184 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500185
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400186 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
187 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500188 g_free(new_call_view_name);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500189 } else {
190 /* nothing selected in the call model, so show the default screen */
191
192 /* TODO: replace stack paceholder view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400193 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
194 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_call_view), DEFAULT_VIEW_NAME);
195 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500196
197 }
198
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500199 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400200 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500201 if (current_call_view != old_call_view && old_call_view != NULL) {
202 /* if the previous child was a call view, then remove it from
Stepan Salenikovich69771842015-02-24 18:11:45 -0500203 * the stack; removing it should destory it since there should not
204 * be any other references to it */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500205 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400206 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500207 }
208 }
209}
210
211static void
212call_state_changed(Call *call, gpointer win)
213{
214 g_debug("call state changed");
215 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
216
217 /* check if the call that changed state is the same as the selected call */
218 QModelIndex idx_selected = CallModel::instance()->selectionModel()->currentIndex();
219
220 if( idx_selected.isValid() && call == CallModel::instance()->getCall(idx_selected)) {
221 g_debug("selected call state changed");
222 /* check if we need to change the view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400223 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500224 GtkWidget *new_call_view = NULL;
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400225 QVariant state = CallModel::instance()->data(idx_selected, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500226
227 /* check what the current state is vs what is displayed */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400228 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400229 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500230 case Call::LifeCycleState::INITIALIZATION:
231 /* LifeCycleState cannot go backwards, so it should not be possible
232 * that the call is displayed as current (meaning that its in progress)
233 * but have the state 'initialization' */
234 if (IS_CURRENT_CALL_VIEW(old_call_view))
235 g_warning("call displayed as current, but is in state of initialization");
236 break;
237 case Call::LifeCycleState::PROGRESS:
238 if (IS_INCOMING_CALL_VIEW(old_call_view)) {
239 /* change from incoming to current */
240 new_call_view = current_call_view_new();
241 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx_selected);
242 /* use the pointer of the call as a unique name */
243 char* new_call_view_name = NULL;
244 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx_selected));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400245 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500246 g_free(new_call_view_name);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400247 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
248 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
249 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500250 }
251 break;
252 case Call::LifeCycleState::FINISHED:
253 /* do nothing, either call view is valid for this state */
254 break;
255 case Call::LifeCycleState::COUNT__:
256 g_warning("LifeCycleState should never be COUNT");
257 break;
258 }
259
260 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400261 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500262 if (current_call_view != old_call_view && old_call_view != NULL) {
263 /* if the previous child was a call view, then remove it from
264 * the stack; removing it should destory it since there should not
265 * be any other references to it */
266 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400267 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500268 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500269 }
270 }
271}
272
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500273static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500274search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
275{
276 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
277
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400278 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500279
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400280 if (number_entered && strlen(number_entered) > 0) {
281 /* detect Ring hash */
282 gboolean is_ring_hash = FALSE;
283 if (strlen(number_entered) == 40) {
284 is_ring_hash = TRUE;
285 /* must be 40 chars long and alphanumeric */
286 for (int i = 0; i < 40 && is_ring_hash; ++i) {
287 if (!g_ascii_isalnum(number_entered[i]))
288 is_ring_hash = FALSE;
289 }
290 }
291
292 QString number = QString{number_entered};
293
294 if (is_ring_hash)
295 number = "ring:" + number;
296
297 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400298
299 Call *call = priv->q_completion_model->call();
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500300 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500301 call->performAction(Call::Action::ACCEPT);
302
303 /* make this the currently selected call */
304 QModelIndex idx = CallModel::instance()->getIndex(call);
305 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400306
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400307 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
308 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
309 /* clear the entry */
310 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
311 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500312}
313
314static void
Stepan Salenikovich15142182015-03-11 17:15:26 -0400315navbutton_contacts_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
316{
317 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
318 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
319
320 if (gtk_toggle_button_get_active(navbutton)) {
321
322 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
323
324 if (visible) {
325 /* contacts is left of both history and presence, so always slide right to show it */
326 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
327 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
328 } else {
329 g_warning("should always have a visible child in the nav stack");
330 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
331 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
332 }
333 }
334}
335
336static void
337navbutton_presence_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
338{
339 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
340 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
341
342 if (gtk_toggle_button_get_active(navbutton)) {
343
344 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
345 if (visible) {
346 /* presence is right of both history and contacts, so always slide left to show it */
347 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
348 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
349 } else {
350 g_warning("should always have a visible child in the nav stack");
351 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
352 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
353 }
354 }
355}
356
357static void
358navbutton_history_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
359{
360 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
361 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
362
363 if (gtk_toggle_button_get_active(navbutton)) {
364
365 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
366 if (visible) {
367 if (strcmp(visible, VIEW_CONTACTS) == 0) {
368 /* history is right of contacts, so slide left to show it */
369 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
370 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
371 } else if (strcmp(visible, VIEW_PRESENCE) == 0) {
372 /* history is left of presence, so slide right to show it */
373 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
374 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
375 }
376 } else {
377 g_warning("should always have a visible child in the nav stack");
378 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
379 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
380 }
381 }
382}
383
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400384static gboolean
385save_accounts(GtkWidget *working_dialog)
386{
387 /* save changes to accounts */
388 AccountModel::instance()->save();
389 /* save changes to codecs */
390 for (int i = 0; i < AccountModel::instance()->rowCount(); i++) {
391 QModelIndex idx = AccountModel::instance()->index(i, 0);
392 AccountModel::instance()->getAccountByModelIndex(idx)->codecModel()->save();
393 }
394
395 if (working_dialog)
396 gtk_widget_destroy(working_dialog);
397
398 return G_SOURCE_REMOVE;
399}
400
Stepan Salenikovich15142182015-03-11 17:15:26 -0400401static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400402settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
403{
404 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
405 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
406
407 /* toggle show settings */
408 priv->show_settings = !priv->show_settings;
409
410 /* check which view to show */
411 if (priv->show_settings) {
412 /* show settings */
413 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
414
415 gtk_widget_hide(priv->hbox_search);
416 gtk_widget_show(priv->hbox_settings);
417
Stepan Salenikovich41118912015-05-01 11:25:46 -0400418 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400419 if (priv->last_settings_view == priv->media_settings_view)
420 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400421
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400422 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400423 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400424 } else {
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400425 /* show working dialog in case save operation takes time */
426 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
427 gtk_window_present(GTK_WINDOW(working));
428
429 /* now save after the time it takes to transition back to the call view (400ms)
430 * the save doesn't happen before the "working" dialog is presented
431 * the timeout function should destroy the "working" dialog when done saving
432 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400433 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400434
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400435 /* show calls */
436 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
437
438 gtk_widget_show(priv->hbox_search);
439 gtk_widget_hide(priv->hbox_settings);
440
441 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
442 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400443
444 /* make sure video preview is stopped, in case it was started */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400445 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400446 }
447}
448
449static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400450show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400451{
452 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
453 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
454
455 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400456 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400457 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400458 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400459 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400460 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400461 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400462 }
463}
464
465static void
466show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
467{
468 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
469 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
470
471 if (gtk_toggle_button_get_active(navbutton)) {
472 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
473 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
474 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400475 }
476}
477
Stepan Salenikovichde896112015-05-11 16:46:33 -0400478static void
479show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
480{
481 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
482 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
483
484 if (gtk_toggle_button_get_active(navbutton)) {
485 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
486 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
487 priv->last_settings_view = priv->general_settings_view;
488 }
489}
490
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400491static gboolean
492has_ring_account()
493{
494 /* check if a Ring account exists */
495 int a_count = AccountModel::instance()->rowCount();
496 for (int i = 0; i < a_count; ++i) {
497 QModelIndex idx = AccountModel::instance()->index(i, 0);
498 QVariant protocol = idx.data(static_cast<int>(Account::Role::Proto));
499 if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING)
500 return TRUE;
501 }
502
503 return FALSE;
504}
505
506static gboolean
507create_ring_account(RingMainWindow *win)
508{
509 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
510 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
511
512 /* create account and set UPnP enabled, as its not by default in the daemon */
513 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
514 Account *account = AccountModel::instance()->add(alias, Account::Protocol::RING);
515 account->setUpnpEnabled(TRUE);
516
517 /* wait for hash to be generated to show the next view */
518 priv->hash_updated = QObject::connect(
519 account,
520 &Account::changed,
521 [=] (Account *a) {
522 QString hash = a->username();
523 if (!hash.isEmpty()) {
524 /* set the hash */
525 gtk_entry_set_text(GTK_ENTRY(priv->entry_hash), hash.toUtf8().constData());
526
527 /* show the next accont creation view */
528 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
529 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_2_VIEW_NAME);
530
531 /* select the hash text */
532 gtk_widget_grab_focus(priv->entry_hash);
533 gtk_editable_select_region(GTK_EDITABLE(priv->entry_hash), 0, -1);
534 }
535 }
536 );
537
538 account->performAction(Account::EditAction::SAVE);
539
540 return G_SOURCE_REMOVE;
541}
542
543static void
544alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
545{
546 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
547
548 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
549 if (strlen(alias) > 0) {
550 /* enable "next" button */
551 gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
552 } else {
553 /* disable "next" button, as we require an alias */
554 gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
555 }
556}
557
558static void
559account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
560{
561 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
562
563 /* show/hide relevant widgets */
564 gtk_widget_hide(priv->label_enter_alias);
565 gtk_widget_hide(priv->entry_alias);
566 gtk_widget_hide(priv->button_account_creation_next);
567 gtk_widget_show(priv->label_generating_account);
568 gtk_widget_show(priv->spinner_generating_account);
569
570 /* now create account after a short timeout so that the the save doesn't
571 * happen freeze the client before the widget changes happen;
572 * the timeout function should then display the next step in account creation
573 */
574 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
575}
576
577static void
578account_creation_done_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
579{
580 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
581
582 QObject::disconnect(priv->hash_updated);
583
584 /* show the call view */
585 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
586 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
587
588 /* show the search bar and settings */
589 gtk_widget_show(priv->hbox_search);
590 gtk_widget_show(priv->ring_settings);
591
592}
593
594static void
595entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
596{
597 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
598
599 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
600 if (strlen(alias) > 0)
601 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
602}
603
604static void
605show_account_creation(RingMainWindow *win)
606{
607 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
608
609 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
610 priv->account_creation_1,
611 CREATE_ACCOUNT_1_VIEW_NAME);
612
613 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
614 priv->account_creation_2,
615 CREATE_ACCOUNT_2_VIEW_NAME);
616
617 /* hide search bar and settings until account creation is complete */
618 gtk_widget_hide(priv->hbox_search);
619 gtk_widget_hide(priv->ring_settings);
620
621 /* set ring logo */
622 GError *error = NULL;
623 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
624 -1, 75, TRUE, &error);
625 if (logo_ring == NULL) {
626 g_debug("Could not load logo: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400627 g_clear_error(&error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400628 } else
629 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
630
631 /* style of alias and hash entry; give them a larger font */
632 gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
633 gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
634
635 /* connect signals */
636 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
637 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
638 g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
639 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
640 g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
641
642 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
643}
644
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400645static void
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400646show_ring_id(RingMainWindow *win, Account *account) {
647 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
648
649 /* display the ring id, if we found a ring account */
650 if (account) {
651 if (!account->username().isEmpty()) {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400652 QString hash = "<span fgcolor=\"black\">" + account->username() + "</span>";
653 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), hash.toUtf8().constData());
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400654 } else {
655 g_warning("got ring account, but Ring id is empty");
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400656 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), "<span fgcolor=\"gray\">fetching Ring ID...</span>");
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400657 }
658 } else {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400659 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), "<span fgcolor=\"gray\">no Ring account</span>");
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400660 }
661
662}
663
664static void
665get_active_ring_account(RingMainWindow *win)
666{
667 /* get the users Ring account
668 * if multiple accounts exist, get the first one which is registered,
669 * if none, then the first one which is enabled,
670 * if none, then the first one in the list of ring accounts
671 */
672 Account *registered_account = NULL;
673 Account *enabled_account = NULL;
674 Account *ring_account = NULL;
675 int a_count = AccountModel::instance()->rowCount();
676 for (int i = 0; i < a_count && !registered_account; ++i) {
677 QModelIndex idx = AccountModel::instance()->index(i, 0);
678 Account *account = AccountModel::instance()->getAccountByModelIndex(idx);
679 if (account->protocol() == Account::Protocol::RING) {
680 /* got RING account, check if active */
681 if (account->isEnabled()) {
682 /* got enabled account, check if connected */
683 if (account->registrationState() == Account::RegistrationState::READY) {
684 /* got registered account, use this one */
685 registered_account = enabled_account = ring_account = account;
686 // g_debug("got registered account: %s", ring_account->alias().toUtf8().constData());
687 } else {
688 /* not registered, but enabled, use if its the first one */
689 if (!enabled_account) {
690 enabled_account = ring_account = account;
691 // g_debug("got enabled ring accout: %s", ring_account->alias().toUtf8().constData());
692 }
693 }
694 } else {
695 /* not enabled, but a Ring account, use if its the first one */
696 if (!ring_account) {
697 ring_account = account;
698 // g_debug("got ring account: %s", ring_account->alias().toUtf8().constData());
699 }
700 }
701 }
702 }
703
704 show_ring_id(win, ring_account);
705}
706
707static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400708search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
709{
710 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
711
712 /* get the text from the entry */
713 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
714
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400715 if (text && strlen(text) > 0) {
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400716 /* edit the the dialing call (or create a new one) */
717 Call *call = CallModel::instance()->dialingCall();
718 call->setDialNumber(text);
719 priv->q_completion_model->setCall(call);
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400720 } else {
721 Call *call = priv->q_completion_model->call();
722 if (call->lifeCycleState() == Call::LifeCycleState::CREATION)
723 call->performAction(Call::Action::REFUSE);
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400724 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400725}
726
727static gboolean
728completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
729 G_GNUC_UNUSED const gchar *key,
730 G_GNUC_UNUSED GtkTreeIter *iter,
731 G_GNUC_UNUSED RingMainWindow *win)
732{
733 /* the model is updated by lrc and should only every contain matching entries
734 * so always return TRUE */
735 return TRUE;
736}
737
738static QModelIndex
739get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
740 GtkTreeIter *filter_iter)
741{
742 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
743 GtkTreeIter child_iter;
744 gtk_tree_model_filter_convert_iter_to_child_iter(
745 GTK_TREE_MODEL_FILTER(filter_model),
746 &child_iter,
747 filter_iter);
748
749 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
750}
751
752static void
753autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
754 GtkCellRenderer *cell,
755 GtkTreeModel *model,
756 GtkTreeIter *iter,
757 G_GNUC_UNUSED gpointer user_data)
758{
759 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
760 if (idx.isValid()) {
761 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
762 if (photo_var.isValid()) {
763 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
764 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
765 20, 20,
766 GDK_INTERP_BILINEAR);
767
768 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
769 g_object_unref(scaled);
770 return;
771 }
772 }
773
774 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
775}
776
777static void
778autocompletion_name_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
779 GtkCellRenderer *cell,
780 GtkTreeModel *model,
781 GtkTreeIter *iter,
782 G_GNUC_UNUSED gpointer user_data)
783{
784 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
785 if (idx.isValid()) {
786 QVariant name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
787 gchar *text = g_strdup_printf("<span font=\"12\" weight=\"bold\">%s</span>",
788 name.value<QString>().toUtf8().constData());
789
790 g_object_set(G_OBJECT(cell), "markup", text, NULL);
791 g_free(text);
792 return;
793 }
794
795 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
796}
797
798static void
799autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
800 GtkCellRenderer *cell,
801 GtkTreeModel *model,
802 GtkTreeIter *iter,
803 G_GNUC_UNUSED gpointer user_data)
804{
805 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
806 if (idx.isValid()) {
807 QVariant uri = idx.data(Qt::DisplayRole);
808 gchar *text = g_strdup_printf("<span font=\"12\">%s</span>",
809 uri.value<QString>().toUtf8().constData());
810
811 g_object_set(G_OBJECT(cell), "markup", text, NULL);
812 g_free(text);
813 return;
814 }
815
816 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
817}
818
819static void
820autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
821 GtkCellRenderer *cell,
822 GtkTreeModel *model,
823 GtkTreeIter *iter,
824 G_GNUC_UNUSED gpointer user_data)
825{
826 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
827 if (idx.isValid()) {
828 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
829 gchar *text = g_strdup_printf("<span font=\"12\" color=\"gray\">%s</span>",
830 alias.value<QString>().toUtf8().constData());
831
832 g_object_set(G_OBJECT(cell), "markup", text, NULL);
833 g_free(text);
834 return;
835 }
836
837 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
838}
839
840static gboolean
841select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
842 GtkTreeModel *model,
843 GtkTreeIter *iter,
844 RingMainWindow *win)
845{
846 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
847
848 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
849 if (idx.isValid()) {
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400850 priv->q_completion_model->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
851 Call *call = priv->q_completion_model->call();
852 priv->q_completion_model->callSelectedNumber();
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400853
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400854 /* make this the currently selected call */
855 QModelIndex idx = CallModel::instance()->getIndex(call);
856 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400857
858 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
859 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400860
861 /* clear the entry */
862 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400863 } else {
864 g_warning("autocompletion selection is not a valid index!");
865 }
866 return TRUE;
867}
868
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400869static gboolean
870dtmf_pressed(RingMainWindow *win,
871 GdkEventKey *event,
872 G_GNUC_UNUSED gpointer user_data)
873{
874 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
875
876 /* we want to react to digit key presses, as long as a GtkEntry is not the
877 * input focus
878 */
879 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
880 if (GTK_IS_ENTRY(focus))
881 return GDK_EVENT_PROPAGATE;
882
883 /* make sure that a call is selected*/
884 QItemSelectionModel *selection = CallModel::instance()->selectionModel();
885 QModelIndex idx = selection->currentIndex();
886 if (!idx.isValid())
887 return GDK_EVENT_PROPAGATE;
888
889 /* make sure that the selected call is in progress */
890 Call *call = CallModel::instance()->getCall(idx);
891 Call::LifeCycleState state = call->lifeCycleState();
892 if (state != Call::LifeCycleState::PROGRESS)
893 return GDK_EVENT_PROPAGATE;
894
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400895 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
896 * does not result in a 'c' being played.
897 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
898 if ( event->state
899 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
900 return GDK_EVENT_PROPAGATE;
901
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400902 /* pass the character that was entered to be played by the daemon;
903 * the daemon will filter out invalid DTMF characters */
904 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
905 QString val = QString::fromUcs4(&unicode_val, 1);
906 call->playDTMF(val);
907 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400908
909 /* always propogate the key, so we don't steal accelerators/shortcuts */
910 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400911}
912
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400913static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500914ring_main_window_init(RingMainWindow *win)
915{
916 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
917 gtk_widget_init_template(GTK_WIDGET(win));
918
919 /* set window icon */
920 GError *error = NULL;
921 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
922 if (icon == NULL) {
923 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400924 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500925 } else
926 gtk_window_set_icon(GTK_WINDOW(win), icon);
927
928 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400929 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
930 -1, 24, TRUE, &error);
931 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500932 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400933 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500934 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400935 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500936
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400937 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500938 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
939 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400940 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500941 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500942
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400943 /* settings icon */
944 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
945
946 /* connect settings button signal */
947 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
948
949 /* add the call view to the main stack */
950 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
951 priv->vbox_call_view,
952 CALL_VIEW_NAME);
953
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400954 if (has_ring_account()) {
955 /* user has ring account, so show the call view right away */
956 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
957 } else {
958 /* user has to create the ring account */
959 show_account_creation(win);
960 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400961
962 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400963 priv->account_settings_view = account_view_new();
964 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
965
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400966 priv->media_settings_view = media_settings_view_new();
967 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->media_settings_view, MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400968
Stepan Salenikovichde896112015-05-11 16:46:33 -0400969 priv->general_settings_view = general_settings_view_new();
970 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
971
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400972 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -0400973 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
974 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400975
976 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400977 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400978 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400979 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400980
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500981 /* call model */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500982 GtkQTreeModel *call_model;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500983 GtkCellRenderer *renderer;
984 GtkTreeViewColumn *column;
985
Stepan Salenikovich69771842015-02-24 18:11:45 -0500986 call_model = gtk_q_tree_model_new(CallModel::instance(), 4,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500987 Call::Role::Name, G_TYPE_STRING,
988 Call::Role::Number, G_TYPE_STRING,
989 Call::Role::Length, G_TYPE_STRING,
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400990 Call::Role::State, G_TYPE_STRING);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500991 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_call), GTK_TREE_MODEL(call_model));
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500992
993 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400994 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500995 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400996 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500997 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
998
999 renderer = gtk_cell_renderer_text_new();
1000 column = gtk_tree_view_column_new_with_attributes("Duration", renderer, "text", 2, NULL);
Stepan Salenikovich2efd1982015-03-22 12:10:23 -04001001 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001002 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
1003
Stepan Salenikovich69771842015-02-24 18:11:45 -05001004 /* connect signals to and from UserActionModel to sync selection betwee
1005 * the QModel and the GtkTreeView */
Stepan Salenikovich69771842015-02-24 18:11:45 -05001006 QObject::connect(
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001007 CallModel::instance()->selectionModel(),
Stepan Salenikovich69771842015-02-24 18:11:45 -05001008 &QItemSelectionModel::currentChanged,
1009 [=](const QModelIndex & current, const QModelIndex & previous) {
1010 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
1011
1012 /* first unselect the previous */
1013 if (previous.isValid()) {
1014 GtkTreeIter old_iter;
1015 if (gtk_q_tree_model_source_index_to_iter(call_model, previous, &old_iter)) {
1016 gtk_tree_selection_unselect_iter(selection, &old_iter);
1017 } else {
1018 g_warning("Trying to unselect invalid GtkTreeIter");
1019 }
1020 }
1021
1022 /* select the current */
1023 if (current.isValid()) {
1024 GtkTreeIter new_iter;
1025 if (gtk_q_tree_model_source_index_to_iter(call_model, current, &new_iter)) {
1026 gtk_tree_selection_select_iter(selection, &new_iter);
1027 } else {
1028 g_warning("SelectionModel of CallModel changed to invalid QModelIndex?");
1029 }
1030 }
1031 }
1032 );
1033
1034 GtkTreeSelection *call_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001035 g_signal_connect(call_selection, "changed", G_CALLBACK(update_call_model_selection), NULL);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001036
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001037 /* connect to call state changes to update relevant view(s) */
1038 QObject::connect(
1039 CallModel::instance(),
1040 &CallModel::callStateChanged,
1041 [=](Call* call, G_GNUC_UNUSED Call::State previousState) {
1042 call_state_changed(call, win);
1043 }
1044 );
1045
Stepan Salenikovich15142182015-03-11 17:15:26 -04001046 /* contacts view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001047 GtkWidget *contacts_view = contacts_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -04001048 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001049 contacts_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001050 VIEW_CONTACTS);
1051
1052 /* history view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001053 GtkWidget *history_view = history_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -04001054 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001055 history_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001056 VIEW_HISTORY);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001057
Stepan Salenikovich15142182015-03-11 17:15:26 -04001058 /* presence view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001059 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001060 GtkWidget *treeview_presence = gtk_tree_view_new();
1061 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_presence), FALSE);
1062 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_presence);
1063 gtk_widget_show_all(scrolled_window);
1064 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
1065 scrolled_window,
1066 VIEW_PRESENCE);
1067
1068 /* connect signals to change the contacts/history/presence stack view */
1069 g_signal_connect(priv->radiobutton_contacts, "toggled", G_CALLBACK(navbutton_contacts_toggled), win);
1070 g_signal_connect(priv->radiobutton_history, "toggled", G_CALLBACK(navbutton_history_toggled), win);
1071 g_signal_connect(priv->radiobutton_presence, "toggled", G_CALLBACK(navbutton_presence_toggled), win);
1072
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001073 /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
1074 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_contacts), TRUE);
1075
Stepan Salenikovich69771842015-02-24 18:11:45 -05001076 /* TODO: replace stack paceholder view */
1077 GtkWidget *placeholder_view = gtk_tree_view_new();
1078 gtk_widget_show(placeholder_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001079 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), placeholder_view, DEFAULT_VIEW_NAME);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001080
1081 /* connect signals */
1082 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001083 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
1084 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
1085
1086 /* style of search entry */
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001087 gtk_widget_override_font(priv->search_entry, pango_font_description_from_string("15")); //"monospace 15"));
1088
1089 /* autocompletion */
1090 priv->q_completion_model = new NumberCompletionModel();
1091
1092 /* autocompletion renderers */
1093 GtkCellArea *completion_area = gtk_cell_area_box_new();
1094
1095 /* photo renderer */
1096 renderer = gtk_cell_renderer_pixbuf_new();
1097 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1098 renderer,
1099 TRUE, /* expand */
1100 TRUE, /* align */
1101 TRUE); /* fixed size */
1102
1103 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1104 renderer,
1105 (GtkCellLayoutDataFunc)autocompletion_photo_render,
1106 NULL, NULL);
1107
1108 /* name 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_name_render,
1119 NULL, NULL);
1120
1121 /* number renderer */
1122 renderer = gtk_cell_renderer_text_new();
1123 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1124 renderer,
1125 TRUE, /* expand */
1126 TRUE, /* align */
1127 TRUE); /* fixed size */
1128
1129 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1130 renderer,
1131 (GtkCellLayoutDataFunc)autocompletion_number_render,
1132 NULL, NULL);
1133 /* account renderer */
1134 renderer = gtk_cell_renderer_text_new();
1135 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1136 renderer,
1137 TRUE, /* expand */
1138 TRUE, /* align */
1139 TRUE); /* fixed size */
1140
1141 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1142 renderer,
1143 (GtkCellLayoutDataFunc)autocompletion_account_render,
1144 NULL, NULL);
1145
1146 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1147
1148 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1149 (QAbstractItemModel *)priv->q_completion_model,
1150 1,
1151 Qt::DisplayRole, G_TYPE_STRING);
1152
1153 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1154
1155 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1156 gtk_entry_completion_set_match_func(
1157 entry_completion,
1158 (GtkEntryCompletionMatchFunc) completion_match_func,
1159 NULL,
1160 NULL);
1161
1162 /* connect signal to when text is entered in the entry */
1163 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1164 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001165
1166 /* connect to incoming call and focus */
1167 QObject::connect(
1168 CallModel::instance(),
1169 &CallModel::incomingCall,
1170 [=](Call* call) {
1171 CallModel::instance()->selectionModel()->setCurrentIndex(
1172 CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
1173 }
1174 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001175
1176 /* display ring id by first getting the active ring account */
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001177 gtk_widget_override_font(priv->label_ring_id, pango_font_description_from_string("monospace"));
1178 gtk_widget_set_size_request(priv->label_ring_id, 400, 35);
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001179 get_active_ring_account(win);
1180 QObject::connect(
1181 AccountModel::instance(),
1182 &AccountModel::dataChanged,
1183 [=] () {
1184 /* check if the active ring account has changed,
1185 * eg: if it was deleted */
1186 get_active_ring_account(win);
1187 }
1188 );
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001189
1190 /* react to digit key press events */
1191 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001192}
1193
1194static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001195ring_main_window_dispose(GObject *object)
1196{
1197 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1198}
1199
1200static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001201ring_main_window_finalize(GObject *object)
1202{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001203 RingMainWindow *self = RING_MAIN_WINDOW(object);
1204 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1205
1206 delete priv->q_contact_model;
1207 delete priv->q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001208 delete priv->q_completion_model;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001209
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001210 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001211}
1212
1213static void
1214ring_main_window_class_init(RingMainWindowClass *klass)
1215{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001216 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001217 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001218
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001219 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1220 "/cx/ring/RingGnome/ringmainwindow.ui");
1221
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001222 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, treeview_call);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001223 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_contacts_history_presence);
1224 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
1225 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
1226 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001227 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1228 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1229 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1230 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
1231 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_search);
1232 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001233 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001234 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001235 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
1236 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_call_view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001237 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001238 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001239 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001240 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001241 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_ring_id);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001242
1243 /* account creation */
1244 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
1245 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
1246 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
1247 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
1248 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1249 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1250 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
1251
1252 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
1253 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
1254 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001255}
1256
1257GtkWidget *
1258ring_main_window_new (GtkApplication *app)
1259{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001260 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1261
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001262 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001263}